From e0cc48f17e2c4ccbdbb6b4e18eca7fba2de1b88b Mon Sep 17 00:00:00 2001 From: Hubot Date: Thu, 28 Jul 2016 13:29:20 -0500 Subject: [PATCH 01/21] 1.11.0-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 58174153d..f3b6c86f9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.10.0-dev", + "version": "1.11.0-dev", "description": "A hackable text editor for the 21st Century.", "main": "./src/main-process/main.js", "repository": { From ebd68515d2e036d3fd935b7a83284620c151cd25 Mon Sep 17 00:00:00 2001 From: Hubot Date: Thu, 28 Jul 2016 13:29:20 -0500 Subject: [PATCH 02/21] 1.10.0-beta0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 58174153d..c78267a40 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.10.0-dev", + "version": "1.10.0-beta0", "description": "A hackable text editor for the 21st Century.", "main": "./src/main-process/main.js", "repository": { From e249b26a3876a6efb12a6cf85d14c14cca20dacc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Jul 2016 15:34:38 -0600 Subject: [PATCH 03/21] Fix regex to allow multi-digit minor versions in build script Signed-off-by: Antonio Scandurra --- build/Gruntfile.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 5cf88175e..451752829 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -320,7 +320,7 @@ getDefaultChannelAndReleaseBranch = (version) -> else channel = 'stable' - minorVersion = version.match(/^\d\.\d/)[0] + minorVersion = version.match(/^\d+\.\d+/)[0] releaseBranch = "#{minorVersion}-releases" [channel, releaseBranch] From 002c776f1088e61ed17f943e326c4d978b7cf844 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Jul 2016 15:34:38 -0600 Subject: [PATCH 04/21] Fix regex to allow multi-digit minor versions in build script Signed-off-by: Antonio Scandurra --- build/Gruntfile.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 5cf88175e..451752829 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -320,7 +320,7 @@ getDefaultChannelAndReleaseBranch = (version) -> else channel = 'stable' - minorVersion = version.match(/^\d\.\d/)[0] + minorVersion = version.match(/^\d+\.\d+/)[0] releaseBranch = "#{minorVersion}-releases" [channel, releaseBranch] From 45b93e6df839db0abb91eb505bb3659fae19e6fa Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Thu, 28 Jul 2016 19:53:36 -0700 Subject: [PATCH 05/21] :memo: Add JS style guide to the Contributing guide --- CONTRIBUTING.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4f7d539ca..837961e6a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,7 @@ These are just guidelines, not rules, use your best judgment and feel free to pr [Styleguides](#styleguides) * [Git Commit Messages](#git-commit-messages) + * [JavaScript Styleguide](#javascript-styleguide) * [CoffeeScript Styleguide](#coffeescript-styleguide) * [Specs Styleguide](#specs-styleguide) * [Documentation Styleguide](#documentation-styleguide) @@ -280,6 +281,25 @@ If you want to read about using Atom or developing packages in Atom, the [Atom F * :arrow_down: `:arrow_down:` when downgrading dependencies * :shirt: `:shirt:` when removing linter warnings +### JavaScript Styleguide + +All JavaScript must adhere to [JavaScript Standard Style](http://standardjs.com/). + +* Prefer `Object.assign()` to the object spread operator (`{...anotherObj}`) +* Inline `export`s with expressions + ```js + // Use this: + export default class ClassName { + + } + + // Instead of: + class ClassName { + + } + export default ClassName + ``` + ### CoffeeScript Styleguide * Set parameter defaults without spaces around the equal sign From fd94acc2e832c7d01fe6c9ab9b5441cbd77f0af3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 29 Jul 2016 17:12:44 +0200 Subject: [PATCH 06/21] Delete RowMap and its specs After transitioning to `DisplayLayer` we don't need this object anymore. --- spec/row-map-spec.coffee | 104 --------------------------------- src/row-map.coffee | 120 --------------------------------------- 2 files changed, 224 deletions(-) delete mode 100644 spec/row-map-spec.coffee delete mode 100644 src/row-map.coffee diff --git a/spec/row-map-spec.coffee b/spec/row-map-spec.coffee deleted file mode 100644 index e89ac1259..000000000 --- a/spec/row-map-spec.coffee +++ /dev/null @@ -1,104 +0,0 @@ -RowMap = require '../src/row-map' - -describe "RowMap", -> - map = null - - beforeEach -> - map = new RowMap - - describe "::screenRowRangeForBufferRow(bufferRow)", -> - it "returns the range of screen rows corresponding to the given buffer row", -> - map.spliceRegions(0, 0, [ - {bufferRows: 5, screenRows: 5} - {bufferRows: 1, screenRows: 5} - {bufferRows: 5, screenRows: 5} - {bufferRows: 5, screenRows: 1} - ]) - - expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1] - expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 10] - expect(map.screenRowRangeForBufferRow(6)).toEqual [10, 11] - expect(map.screenRowRangeForBufferRow(11)).toEqual [15, 16] - expect(map.screenRowRangeForBufferRow(12)).toEqual [15, 16] - expect(map.screenRowRangeForBufferRow(16)).toEqual [16, 17] - - describe "::bufferRowRangeForScreenRow(screenRow)", -> - it "returns the range of buffer rows corresponding to the given screen row", -> - map.spliceRegions(0, 0, [ - {bufferRows: 5, screenRows: 5} - {bufferRows: 1, screenRows: 5} - {bufferRows: 5, screenRows: 5} - {bufferRows: 5, screenRows: 1} - ]) - - expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1] - expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 6] - expect(map.bufferRowRangeForScreenRow(6)).toEqual [5, 6] - expect(map.bufferRowRangeForScreenRow(10)).toEqual [6, 7] - expect(map.bufferRowRangeForScreenRow(14)).toEqual [10, 11] - expect(map.bufferRowRangeForScreenRow(15)).toEqual [11, 16] - expect(map.bufferRowRangeForScreenRow(16)).toEqual [16, 17] - - describe "::spliceRegions(startBufferRow, bufferRowCount, regions)", -> - it "can insert regions when empty", -> - regions = [ - {bufferRows: 5, screenRows: 5} - {bufferRows: 1, screenRows: 5} - {bufferRows: 5, screenRows: 5} - {bufferRows: 5, screenRows: 1} - ] - map.spliceRegions(0, 0, regions) - expect(map.getRegions()).toEqual regions - - it "can insert wrapped lines into rectangular regions", -> - map.spliceRegions(0, 0, [{bufferRows: 10, screenRows: 10}]) - map.spliceRegions(5, 0, [{bufferRows: 1, screenRows: 3}]) - expect(map.getRegions()).toEqual [ - {bufferRows: 5, screenRows: 5} - {bufferRows: 1, screenRows: 3} - {bufferRows: 5, screenRows: 5} - ] - - it "can splice wrapped lines into rectangular regions", -> - map.spliceRegions(0, 0, [{bufferRows: 10, screenRows: 10}]) - map.spliceRegions(5, 1, [{bufferRows: 1, screenRows: 3}]) - expect(map.getRegions()).toEqual [ - {bufferRows: 5, screenRows: 5} - {bufferRows: 1, screenRows: 3} - {bufferRows: 4, screenRows: 4} - ] - - it "can splice folded lines into rectangular regions", -> - map.spliceRegions(0, 0, [{bufferRows: 10, screenRows: 10}]) - map.spliceRegions(5, 3, [{bufferRows: 3, screenRows: 1}]) - expect(map.getRegions()).toEqual [ - {bufferRows: 5, screenRows: 5} - {bufferRows: 3, screenRows: 1} - {bufferRows: 2, screenRows: 2} - ] - - it "can replace folded regions with a folded region that surrounds them", -> - map.spliceRegions(0, 0, [ - {bufferRows: 3, screenRows: 3} - {bufferRows: 3, screenRows: 1} - {bufferRows: 1, screenRows: 1} - {bufferRows: 3, screenRows: 1} - {bufferRows: 3, screenRows: 3} - ]) - map.spliceRegions(2, 8, [{bufferRows: 8, screenRows: 1}]) - expect(map.getRegions()).toEqual [ - {bufferRows: 2, screenRows: 2} - {bufferRows: 8, screenRows: 1} - {bufferRows: 3, screenRows: 3} - ] - - it "merges adjacent rectangular regions", -> - map.spliceRegions(0, 0, [ - {bufferRows: 3, screenRows: 3} - {bufferRows: 3, screenRows: 1} - {bufferRows: 1, screenRows: 1} - {bufferRows: 3, screenRows: 1} - {bufferRows: 3, screenRows: 3} - ]) - - map.spliceRegions(3, 7, [{bufferRows: 5, screenRows: 5}]) diff --git a/src/row-map.coffee b/src/row-map.coffee deleted file mode 100644 index 5510c1421..000000000 --- a/src/row-map.coffee +++ /dev/null @@ -1,120 +0,0 @@ -{spliceWithArray} = require 'underscore-plus' - -# Used by the display buffer to map screen rows to buffer rows and vice-versa. -# This mapping may not be 1:1 due to folds and soft-wraps. This object maintains -# an array of regions, which contain `bufferRows` and `screenRows` fields. -# -# Rectangular Regions: -# If a region has the same number of buffer rows and screen rows, it is referred -# to as "rectangular", and represents one or more non-soft-wrapped, non-folded -# lines. -# -# Trapezoidal Regions: -# If a region has one buffer row and more than one screen row, it represents a -# soft-wrapped line. If a region has one screen row and more than one buffer -# row, it represents folded lines -module.exports = -class RowMap - constructor: -> - @regions = [] - - # Public: Returns a copy of all the regions in the map - getRegions: -> - @regions.slice() - - # Public: Returns an end-row-exclusive range of screen rows corresponding to - # the given buffer row. If the buffer row is soft-wrapped, the range may span - # multiple screen rows. Otherwise it will span a single screen row. - screenRowRangeForBufferRow: (targetBufferRow) -> - {region, bufferRows, screenRows} = @traverseToBufferRow(targetBufferRow) - - if region? and region.bufferRows isnt region.screenRows - [screenRows, screenRows + region.screenRows] - else - screenRows += targetBufferRow - bufferRows - [screenRows, screenRows + 1] - - # Public: Returns an end-row-exclusive range of buffer rows corresponding to - # the given screen row. If the screen row is the first line of a folded range - # of buffer rows, the range may span multiple buffer rows. Otherwise it will - # span a single buffer row. - bufferRowRangeForScreenRow: (targetScreenRow) -> - {region, screenRows, bufferRows} = @traverseToScreenRow(targetScreenRow) - if region? and region.bufferRows isnt region.screenRows - [bufferRows, bufferRows + region.bufferRows] - else - bufferRows += targetScreenRow - screenRows - [bufferRows, bufferRows + 1] - - # Public: If the given buffer row is part of a folded row range, returns that - # row range. Otherwise returns a range spanning only the given buffer row. - bufferRowRangeForBufferRow: (targetBufferRow) -> - {region, bufferRows} = @traverseToBufferRow(targetBufferRow) - if region? and region.bufferRows isnt region.screenRows - [bufferRows, bufferRows + region.bufferRows] - else - [targetBufferRow, targetBufferRow + 1] - - # Public: Given a starting buffer row, the number of buffer rows to replace, - # and an array of regions of shape {bufferRows: n, screenRows: m}, splices - # the regions at the appropriate location in the map. This method is used by - # display buffer to keep the map updated when the underlying buffer changes. - spliceRegions: (startBufferRow, bufferRowCount, regions) -> - endBufferRow = startBufferRow + bufferRowCount - {index, bufferRows} = @traverseToBufferRow(startBufferRow) - precedingRows = startBufferRow - bufferRows - - count = 0 - while region = @regions[index + count] - count++ - bufferRows += region.bufferRows - if bufferRows >= endBufferRow - followingRows = bufferRows - endBufferRow - break - - if precedingRows > 0 - regions.unshift({bufferRows: precedingRows, screenRows: precedingRows}) - - if followingRows > 0 - regions.push({bufferRows: followingRows, screenRows: followingRows}) - - spliceWithArray(@regions, index, count, regions) - @mergeAdjacentRectangularRegions(index - 1, index + regions.length) - - traverseToBufferRow: (targetBufferRow) -> - bufferRows = 0 - screenRows = 0 - for region, index in @regions - if (bufferRows + region.bufferRows) > targetBufferRow - return {region, index, screenRows, bufferRows} - bufferRows += region.bufferRows - screenRows += region.screenRows - {index, screenRows, bufferRows} - - traverseToScreenRow: (targetScreenRow) -> - bufferRows = 0 - screenRows = 0 - for region, index in @regions - if (screenRows + region.screenRows) > targetScreenRow - return {region, index, screenRows, bufferRows} - bufferRows += region.bufferRows - screenRows += region.screenRows - {index, screenRows, bufferRows} - - mergeAdjacentRectangularRegions: (startIndex, endIndex) -> - for index in [endIndex..startIndex] - if 0 < index < @regions.length - leftRegion = @regions[index - 1] - rightRegion = @regions[index] - leftIsRectangular = leftRegion.bufferRows is leftRegion.screenRows - rightIsRectangular = rightRegion.bufferRows is rightRegion.screenRows - if leftIsRectangular and rightIsRectangular - @regions.splice index - 1, 2, - bufferRows: leftRegion.bufferRows + rightRegion.bufferRows - screenRows: leftRegion.screenRows + rightRegion.screenRows - return - - # Public: Returns an array of strings describing the map's regions. - inspect: -> - for {bufferRows, screenRows} in @regions - "#{bufferRows}:#{screenRows}" From 6d4102a09883cbfaedc0e6a9441eafd591d55526 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 29 Jul 2016 17:15:06 +0200 Subject: [PATCH 07/21] Delete random-editor-spec.coffee We are already extensively testing the same specs on text-buffer. --- spec/random-editor-spec.coffee | 91 ---------------------------------- 1 file changed, 91 deletions(-) delete mode 100644 spec/random-editor-spec.coffee diff --git a/spec/random-editor-spec.coffee b/spec/random-editor-spec.coffee deleted file mode 100644 index 699e60b21..000000000 --- a/spec/random-editor-spec.coffee +++ /dev/null @@ -1,91 +0,0 @@ -{times, random} = require 'underscore-plus' -randomWords = require 'random-words' -TextBuffer = require 'text-buffer' -TextEditor = require '../src/text-editor' - -describe "TextEditor", -> - [editor, tokenizedBuffer, buffer, steps] = [] - - softWrapColumn = 80 - - beforeEach -> - atom.config.set('editor.softWrapAtPreferredLineLength', true) - atom.config.set('editor.preferredLineLength', softWrapColumn) - - it "properly renders soft-wrapped lines when randomly mutated", -> - times 10, (i) -> - buffer = new TextBuffer - editor = atom.workspace.buildTextEditor({buffer}) - editor.setEditorWidthInChars(80) - tokenizedBuffer = editor.tokenizedBuffer - steps = [] - - times 30, -> - randomlyMutateEditor() - verifyLines() - - verifyLines = -> - {bufferRows, screenLines} = getReferenceScreenLines() - for referenceBufferRow, screenRow in bufferRows - referenceScreenLine = screenLines[screenRow] - actualBufferRow = editor.bufferRowForScreenRow(screenRow) - unless actualBufferRow is referenceBufferRow - logLines() - throw new Error("Invalid buffer row #{actualBufferRow} for screen row #{screenRow}", ) - - actualScreenLine = editor.lineTextForScreenRow(screenRow) - unless actualScreenLine is referenceScreenLine - logLines() - throw new Error("Invalid line text at screen row #{screenRow}") - - logLines = -> - console.log "==== screen lines ====" - editor.logScreenLines() - console.log "==== reference lines ====" - {bufferRows, screenLines} = getReferenceScreenLines() - for bufferRow, screenRow in bufferRows - console.log screenRow, bufferRow, screenLines[screenRow].text - console.log "==== steps to reproduce this failure: ===" - for step in steps - console.log 'editor.' + step[0] + '('+ step[1..].map((a) -> JSON.stringify(a)).join(', ') + ')' - - randomlyMutateEditor = -> - if Math.random() < .2 - softWrapped = not editor.isSoftWrapped() - steps.push(['setSoftWrapped', softWrapped]) - editor.setSoftWrapped(softWrapped) - else - range = getRandomRange() - text = getRandomText() - steps.push(['setTextInBufferRange', range, text]) - editor.setTextInBufferRange(range, text) - - getRandomRange = -> - startRow = random(0, buffer.getLastRow()) - startColumn = random(0, buffer.lineForRow(startRow).length) - endRow = random(startRow, buffer.getLastRow()) - endColumn = random(0, buffer.lineForRow(endRow).length) - [[startRow, startColumn], [endRow, endColumn]] - - getRandomText = -> - text = [] - max = buffer.getText().split(/\s/).length * 0.75 - - times random(5, max), -> - if Math.random() < .1 - text += '\n' - else - text += " " if /\w$/.test(text) - text += randomWords(exactly: 1) - text - - getReferenceScreenLines = -> - referenceEditor = atom.workspace.buildTextEditor() - referenceEditor.setEditorWidthInChars(80) - referenceEditor.setText(editor.getText()) - referenceEditor.setSoftWrapped(editor.isSoftWrapped()) - - screenLines = [0..referenceEditor.getLastScreenRow()].map (row) -> referenceEditor.lineTextForScreenRow(row) - bufferRows = referenceEditor.bufferRowsForScreenRows(0, referenceEditor.getLastScreenRow()) - - {screenLines, bufferRows} From 1fd51ee2710f59b26c50929d16c3f74cf3c9e9c3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 29 Jul 2016 15:11:37 -0600 Subject: [PATCH 08/21] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3b6c86f9..211ab39be 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "sinon": "1.17.4", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.2.2", + "text-buffer": "9.2.3", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From acc5052978c82701be0a5d6c180e61ceb1c8b0d3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 29 Jul 2016 15:11:37 -0600 Subject: [PATCH 09/21] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c78267a40..fe0649740 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "sinon": "1.17.4", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.2.2", + "text-buffer": "9.2.3", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From 82efce08ed9f329c2dfe380f896171a6d51310af Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Fri, 29 Jul 2016 14:48:33 -0700 Subject: [PATCH 10/21] Fix WinShell closure/for issue, improve error handling --- src/main-process/win-shell.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main-process/win-shell.coffee b/src/main-process/win-shell.coffee index 39e9f4ed0..973859597 100644 --- a/src/main-process/win-shell.coffee +++ b/src/main-process/win-shell.coffee @@ -13,11 +13,11 @@ class ShellOption isRegistered: (callback) => new Registry({hive: 'HKCU', key: "#{@key}\\#{@parts[0].key}"}) .get @parts[0].name, (err, val) => - callback(not err? and val.value is @parts[0].value) + callback(not err? and val? and val.value is @parts[0].value) register: (callback) => doneCount = @parts.length - for part in @parts + @parts.forEach (part) => reg = new Registry({hive: 'HKCU', key: if part.key? then "#{@key}\\#{part.key}" else @key}) reg.create( -> reg.set part.name, Registry.REG_SZ, part.value, -> callback() if --doneCount is 0) @@ -31,7 +31,7 @@ class ShellOption update: (callback) => new Registry({hive: 'HKCU', key: "#{@key}\\#{@parts[0].key}"}) .get @parts[0].name, (err, val) => - if err? or not val.value.includes '\\' + exeName + if err? or not val? or val.value.includes '\\' + exeName callback(err) else @register callback From f3caa671092f2460eea7bc5a148628ce343dbe80 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Fri, 29 Jul 2016 15:51:45 -0700 Subject: [PATCH 11/21] Ensure beta is detected from path --- src/main-process/win-shell.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main-process/win-shell.coffee b/src/main-process/win-shell.coffee index 973859597..2bb993e9c 100644 --- a/src/main-process/win-shell.coffee +++ b/src/main-process/win-shell.coffee @@ -3,7 +3,8 @@ Path = require 'path' exeName = Path.basename(process.execPath) appPath = "\"#{process.execPath}\"" -appName = exeName.replace('atom', 'Atom').replace('beta', 'Beta').replace('.exe', '') +isBeta = appPath.includes(' Beta') +appName = exeName.replace('atom', (if isBeta then 'Atom Beta' else 'Atom' )).replace('.exe', '') class ShellOption constructor: (key, parts) -> From ec3207e6c0c069815465d1b3f567f8dde0939d62 Mon Sep 17 00:00:00 2001 From: simurai Date: Sun, 31 Jul 2016 14:26:13 +0200 Subject: [PATCH 12/21] :arrow_up: settings-view@v0.241.1 https://github.com/atom/settings-view/pull/822 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 211ab39be..50a266d56 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "notifications": "0.65.0", "open-on-github": "1.2.0", "package-generator": "1.0.0", - "settings-view": "0.241.0", + "settings-view": "0.241.1", "snippets": "1.0.2", "spell-check": "0.67.1", "status-bar": "1.4.0", From 475ad6b4e6dfa2cd42425386d8e63a5d9526419b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 1 Aug 2016 16:14:20 -0600 Subject: [PATCH 13/21] :arrow_up: about --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 50a266d56..1704f02f4 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "one-light-syntax": "1.3.0", "solarized-dark-syntax": "1.0.2", "solarized-light-syntax": "1.0.2", - "about": "1.5.3", + "about": "1.6.0", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.2", From 3fd275a7f65781cc6eb55b0f3f2c48656aad80ed Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 1 Aug 2016 15:52:59 -0700 Subject: [PATCH 14/21] Convert Color to JS --- src/color.coffee | 89 ------------------------------- src/color.js | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 89 deletions(-) delete mode 100644 src/color.coffee create mode 100644 src/color.js diff --git a/src/color.coffee b/src/color.coffee deleted file mode 100644 index b413b9e2c..000000000 --- a/src/color.coffee +++ /dev/null @@ -1,89 +0,0 @@ -_ = require 'underscore-plus' -ParsedColor = null - -# Essential: A simple color class returned from {Config::get} when the value -# at the key path is of type 'color'. -module.exports = -class Color - # Essential: Parse a {String} or {Object} into a {Color}. - # - # * `value` A {String} such as `'white'`, `#ff00ff`, or - # `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`, `blue`, - # and `alpha` properties. - # - # Returns a {Color} or `null` if it cannot be parsed. - @parse: (value) -> - return null if _.isArray(value) or _.isFunction(value) - return null unless _.isObject(value) or _.isString(value) - - ParsedColor ?= require 'color' - - try - parsedColor = new ParsedColor(value) - catch error - return null - - new Color(parsedColor.red(), parsedColor.green(), parsedColor.blue(), parsedColor.alpha()) - - constructor: (red, green, blue, alpha) -> - Object.defineProperties this, - red: - set: (newRed) -> red = parseColor(newRed) - get: -> red - enumerable: true - configurable: false - green: - set: (newGreen) -> green = parseColor(newGreen) - get: -> green - enumerable: true - configurable: false - blue: - set: (newBlue) -> blue = parseColor(newBlue) - get: -> blue - enumerable: true - configurable: false - alpha: - set: (newAlpha) -> alpha = parseAlpha(newAlpha) - get: -> alpha - enumerable: true - configurable: false - - @red = red - @green = green - @blue = blue - @alpha = alpha - - # Essential: Returns a {String} in the form `'#abcdef'`. - toHexString: -> - "##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}" - - # Essential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`. - toRGBAString: -> - "rgba(#{@red}, #{@green}, #{@blue}, #{@alpha})" - - isEqual: (color) -> - return true if this is color - color = Color.parse(color) unless color instanceof Color - return false unless color? - color.red is @red and color.blue is @blue and color.green is @green and color.alpha is @alpha - - clone: -> new Color(@red, @green, @blue, @alpha) - -parseColor = (color) -> - color = parseInt(color) - color = 0 if isNaN(color) - color = Math.max(color, 0) - color = Math.min(color, 255) - color - -parseAlpha = (alpha) -> - alpha = parseFloat(alpha) - alpha = 1 if isNaN(alpha) - alpha = Math.max(alpha, 0) - alpha = Math.min(alpha, 1) - alpha - -numberToHexString = (number) -> - hex = number.toString(16) - hex = "0#{hex}" if number < 16 - hex diff --git a/src/color.js b/src/color.js new file mode 100644 index 000000000..9db9e9b16 --- /dev/null +++ b/src/color.js @@ -0,0 +1,134 @@ +/** @babel */ + +let ParsedColor = null + +// Essential: A simple color class returned from {Config::get} when the value +// at the key path is of type 'color'. +export default class Color { + // Essential: Parse a {String} or {Object} into a {Color}. + // + // * `value` A {String} such as `'white'`, `#ff00ff`, or + // `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`, `blue`, + // and `alpha` properties. + // + // Returns a {Color} or `null` if it cannot be parsed. + static parse (value) { + switch (typeof value) { + case 'string': + break + case 'object': + if (Array.isArray(value)) { return null } + break + default: + return null + } + + if (!ParsedColor) { + ParsedColor = require('color') + } + + try { + var parsedColor = new ParsedColor(value) + } catch (error) { + return null + } + + return new Color(parsedColor.red(), parsedColor.green(), parsedColor.blue(), parsedColor.alpha()) + } + + constructor (red, green, blue, alpha) { + this.red = red + this.green = green + this.blue = blue + this.alpha = alpha + } + + set red (red) { + this._red = parseColor(red) + } + + set green (green) { + this._green = parseColor(green) + } + + set blue (blue) { + this._blue = parseColor(blue) + } + + set alpha (alpha) { + this._alpha = parseAlpha(alpha) + } + + get red () { + return this._red + } + + get green () { + return this._green + } + + get blue () { + return this._blue + } + + get alpha () { + return this._alpha + } + + // Essential: Returns a {String} in the form `'#abcdef'`. + toHexString () { + return `#${numberToHexString(this.red)}${numberToHexString(this.green)}${numberToHexString(this.blue)}` + } + + // Essential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`. + toRGBAString () { + return `rgba(${this.red}, ${this.green}, ${this.blue}, ${this.alpha})` + } + + isEqual (color) { + if (this === color) { + return true + } + + if (!(color instanceof Color)) { + color = Color.parse(color) + } + + if (color == null) { + return false + } + + return color.red === this.red && color.blue === this.blue && color.green === this.green && color.alpha === this.alpha + } + + clone () { + return new Color(this.red, this.green, this.blue, this.alpha) + } +} + +function parseColor (colorString) { + const color = parseInt(colorString, 10) + if (isNaN(color)) { + return 0 + } else { + return Math.min(Math.max(color, 0), 255) + } +} + +function parseAlpha (alphaString) { + const alpha = parseFloat(alphaString) + if (isNaN(alpha)) { + return 1 + } else { + return Math.min(Math.max(alpha, 0), 1) + } +} + +function numberToHexString (number) { + const hex = number.toString(16) + if (number < 16) { + return `0${hex}` + } else { + return hex + } +} From 2500d59bcef79eed44c179ce37c5c4a7e61d39d8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 1 Aug 2016 15:57:18 -0700 Subject: [PATCH 15/21] Generate API docs from js sources --- build/package.json | 1 + build/tasks/docs-task.coffee | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/build/package.json b/build/package.json index 533f49417..97f93e7ab 100644 --- a/build/package.json +++ b/build/package.json @@ -30,6 +30,7 @@ "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", "grunt-standard": "^2.0.0", + "joanna": "0.0.3", "legal-eagle": "~0.13.0", "minidump": "~0.9", "npm": "3.10.5", diff --git a/build/tasks/docs-task.coffee b/build/tasks/docs-task.coffee index 75e21de8a..5b05688a0 100644 --- a/build/tasks/docs-task.coffee +++ b/build/tasks/docs-task.coffee @@ -4,6 +4,7 @@ fs = require 'fs-plus' _ = require 'underscore-plus' donna = require 'donna' +joanna = require 'joanna' tello = require 'tello' module.exports = (grunt) -> @@ -30,8 +31,16 @@ module.exports = (grunt) -> grunt.registerTask 'build-docs', 'Builds the API docs in src', -> docsOutputDir = grunt.config.get('docsOutputDir') - metadata = donna.generateMetadata(['.']) - api = tello.digest(metadata) + [coffeeMetadata] = donna.generateMetadata(['.']) + jsMetadata = joanna('.') + + metadata = { + repository: coffeeMetadata.repository, + version: coffeeMetadata.version, + files: Object.assign(coffeeMetadata.files, jsMetadata.files) + } + + api = tello.digest([metadata]) _.extend(api.classes, getClassesToInclude()) api.classes = sortClasses(api.classes) From 61bbfae420431b9124f5982e9e33dcc640503aa5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 1 Aug 2016 16:20:26 -0700 Subject: [PATCH 16/21] :arrow_up: joanna --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index 97f93e7ab..8a41ac980 100644 --- a/build/package.json +++ b/build/package.json @@ -30,7 +30,7 @@ "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", "grunt-standard": "^2.0.0", - "joanna": "0.0.3", + "joanna": "0.0.5", "legal-eagle": "~0.13.0", "minidump": "~0.9", "npm": "3.10.5", From 2e04bd6ea9dbc0513f4f65be4f8894e97609dcf9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 1 Aug 2016 16:21:11 -0700 Subject: [PATCH 17/21] Convert window.coffee and clipboard.coffee to javascript --- src/clipboard.coffee | 61 -------------------------------------- src/clipboard.js | 70 ++++++++++++++++++++++++++++++++++++++++++++ src/window.coffee | 27 ----------------- src/window.js | 30 +++++++++++++++++++ 4 files changed, 100 insertions(+), 88 deletions(-) delete mode 100644 src/clipboard.coffee create mode 100644 src/clipboard.js delete mode 100644 src/window.coffee create mode 100644 src/window.js diff --git a/src/clipboard.coffee b/src/clipboard.coffee deleted file mode 100644 index 9511bdda9..000000000 --- a/src/clipboard.coffee +++ /dev/null @@ -1,61 +0,0 @@ -crypto = require 'crypto' -clipboard = require './safe-clipboard' - -# Extended: Represents the clipboard used for copying and pasting in Atom. -# -# An instance of this class is always available as the `atom.clipboard` global. -# -# ## Examples -# -# ```coffee -# atom.clipboard.write('hello') -# -# console.log(atom.clipboard.read()) # 'hello' -# ``` -module.exports = -class Clipboard - constructor: -> - @reset() - - reset: -> - @metadata = null - @signatureForMetadata = null - - # Creates an `md5` hash of some text. - # - # * `text` A {String} to hash. - # - # Returns a hashed {String}. - md5: (text) -> - crypto.createHash('md5').update(text, 'utf8').digest('hex') - - # Public: Write the given text to the clipboard. - # - # The metadata associated with the text is available by calling - # {::readWithMetadata}. - # - # * `text` The {String} to store. - # * `metadata` (optional) The additional info to associate with the text. - write: (text, metadata) -> - @signatureForMetadata = @md5(text) - @metadata = metadata - clipboard.writeText(text) - - # Public: Read the text from the clipboard. - # - # Returns a {String}. - read: -> - clipboard.readText() - - # Public: Read the text from the clipboard and return both the text and the - # associated metadata. - # - # Returns an {Object} with the following keys: - # * `text` The {String} clipboard text. - # * `metadata` The metadata stored by an earlier call to {::write}. - readWithMetadata: -> - text = @read() - if @signatureForMetadata is @md5(text) - {text, @metadata} - else - {text} diff --git a/src/clipboard.js b/src/clipboard.js new file mode 100644 index 000000000..34f6b1f83 --- /dev/null +++ b/src/clipboard.js @@ -0,0 +1,70 @@ +/** @babel */ + +import crypto from 'crypto' +import clipboard from './safe-clipboard' + +// Extended: Represents the clipboard used for copying and pasting in Atom. +// +// An instance of this class is always available as the `atom.clipboard` global. +// +// ## Examples +// +// ```coffee +// atom.clipboard.write('hello') +// +// console.log(atom.clipboard.read()) # 'hello' +// ``` +export default class Clipboard { + constructor () { + this.reset() + } + + reset () { + this.metadata = null + this.signatureForMetadata = null + } + + // Creates an `md5` hash of some text. + // + // * `text` A {String} to hash. + // + // Returns a hashed {String}. + md5 (text) { + return crypto.createHash('md5').update(text, 'utf8').digest('hex') + } + + // Public: Write the given text to the clipboard. + // + // The metadata associated with the text is available by calling + // {::readWithMetadata}. + // + // * `text` The {String} to store. + // * `metadata` (optional) The additional info to associate with the text. + write (text, metadata) { + this.signatureForMetadata = this.md5(text) + this.metadata = metadata + clipboard.writeText(text) + } + + // Public: Read the text from the clipboard. + // + // Returns a {String}. + read () { + return clipboard.readText() + } + + // Public: Read the text from the clipboard and return both the text and the + // associated metadata. + // + // Returns an {Object} with the following keys: + // * `text` The {String} clipboard text. + // * `metadata` The metadata stored by an earlier call to {::write}. + readWithMetadata () { + let text = this.read() + if (this.signatureForMetadata === this.md5(text)) { + return {text, metadata: this.metadata} + } else { + return {text} + } + } +} diff --git a/src/window.coffee b/src/window.coffee deleted file mode 100644 index 9554218ca..000000000 --- a/src/window.coffee +++ /dev/null @@ -1,27 +0,0 @@ -# Public: Measure how long a function takes to run. -# -# description - A {String} description that will be logged to the console when -# the function completes. -# fn - A {Function} to measure the duration of. -# -# Returns the value returned by the given function. -window.measure = (description, fn) -> - start = Date.now() - value = fn() - result = Date.now() - start - console.log description, result - value - -# Public: Create a dev tools profile for a function. -# -# description - A {String} description that will be available in the Profiles -# tab of the dev tools. -# fn - A {Function} to profile. -# -# Returns the value returned by the given function. -window.profile = (description, fn) -> - measure description, -> - console.profile(description) - value = fn() - console.profileEnd(description) - value diff --git a/src/window.js b/src/window.js new file mode 100644 index 000000000..c4f28ba96 --- /dev/null +++ b/src/window.js @@ -0,0 +1,30 @@ +// Public: Measure how long a function takes to run. +// +// description - A {String} description that will be logged to the console when +// the function completes. +// fn - A {Function} to measure the duration of. +// +// Returns the value returned by the given function. +window.measure = function (description, fn) { + let start = Date.now() + let value = fn() + let result = Date.now() - start + console.log(description, result) + return value +} + +// Public: Create a dev tools profile for a function. +// +// description - A {String} description that will be available in the Profiles +// tab of the dev tools. +// fn - A {Function} to profile. +// +// Returns the value returned by the given function. +window.profile = function (description, fn) { + window.measure(description, function () { + console.profile(description) + let value = fn() + console.profileEnd(description) + return value + }) +} From b358047b96ba6733c8139399008c51dc75e5e25a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 1 Aug 2016 16:33:42 -0700 Subject: [PATCH 18/21] Convert DeserializerManager to JS --- src/deserializer-manager.coffee | 68 ---------------------- src/deserializer-manager.js | 100 ++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 68 deletions(-) delete mode 100644 src/deserializer-manager.coffee create mode 100644 src/deserializer-manager.js diff --git a/src/deserializer-manager.coffee b/src/deserializer-manager.coffee deleted file mode 100644 index 3c73a0b02..000000000 --- a/src/deserializer-manager.coffee +++ /dev/null @@ -1,68 +0,0 @@ -{Disposable} = require 'event-kit' - -# Extended: Manages the deserializers used for serialized state -# -# An instance of this class is always available as the `atom.deserializers` -# global. -# -# ## Examples -# -# ```coffee -# class MyPackageView extends View -# atom.deserializers.add(this) -# -# @deserialize: (state) -> -# new MyPackageView(state) -# -# constructor: (@state) -> -# -# serialize: -> -# @state -# ``` -module.exports = -class DeserializerManager - constructor: (@atomEnvironment) -> - @deserializers = {} - - # Public: Register the given class(es) as deserializers. - # - # * `deserializers` One or more deserializers to register. A deserializer can - # be any object with a `.name` property and a `.deserialize()` method. A - # common approach is to register a *constructor* as the deserializer for its - # instances by adding a `.deserialize()` class method. When your method is - # called, it will be passed serialized state as the first argument and the - # {Atom} environment object as the second argument, which is useful if you - # wish to avoid referencing the `atom` global. - add: (deserializers...) -> - @deserializers[deserializer.name] = deserializer for deserializer in deserializers - new Disposable => - delete @deserializers[deserializer.name] for deserializer in deserializers - return - - getDeserializerCount: -> - Object.keys(@deserializers).length - - # Public: Deserialize the state and params. - # - # * `state` The state {Object} to deserialize. - deserialize: (state) -> - return unless state? - - if deserializer = @get(state) - stateVersion = state.get?('version') ? state.version - return if deserializer.version? and deserializer.version isnt stateVersion - deserializer.deserialize(state, @atomEnvironment) - else - console.warn "No deserializer found for", state - - # Get the deserializer for the state. - # - # * `state` The state {Object} being deserialized. - get: (state) -> - return unless state? - - name = state.get?('deserializer') ? state.deserializer - @deserializers[name] - - clear: -> - @deserializers = {} diff --git a/src/deserializer-manager.js b/src/deserializer-manager.js new file mode 100644 index 000000000..f5f2e6429 --- /dev/null +++ b/src/deserializer-manager.js @@ -0,0 +1,100 @@ +/** @babel */ + +import {Disposable} from 'event-kit' + +// Extended: Manages the deserializers used for serialized state +// +// An instance of this class is always available as the `atom.deserializers` +// global. +// +// ## Examples +// +// ```coffee +// class MyPackageView extends View +// atom.deserializers.add(this) +// +// @deserialize: (state) -> +// new MyPackageView(state) +// +// constructor: (@state) -> +// +// serialize: -> +// @state +// ``` +export default class DeserializerManager { + constructor (atomEnvironment) { + this.atomEnvironment = atomEnvironment + this.deserializers = {} + } + + // Public: Register the given class(es) as deserializers. + // + // * `deserializers` One or more deserializers to register. A deserializer can + // be any object with a `.name` property and a `.deserialize()` method. A + // common approach is to register a *constructor* as the deserializer for its + // instances by adding a `.deserialize()` class method. When your method is + // called, it will be passed serialized state as the first argument and the + // {Atom} environment object as the second argument, which is useful if you + // wish to avoid referencing the `atom` global. + add (...deserializers) { + for (let i = 0; i < deserializers.length; i++) { + let deserializer = deserializers[i] + this.deserializers[deserializer.name] = deserializer + } + + return new Disposable(() => { + for (let j = 0; j < deserializers.length; j++) { + let deserializer = deserializers[j] + delete this.deserializers[deserializer.name] + } + }) + } + + getDeserializerCount () { + return Object.keys(this.deserializers).length + } + + // Public: Deserialize the state and params. + // + // * `state` The state {Object} to deserialize. + deserialize (state) { + if (state == null) { + return + } + + const deserializer = this.get(state) + if (deserializer) { + let stateVersion = ( + (typeof state.get === 'function') && state.get('version') || + state.version + ) + + if ((deserializer.version != null) && deserializer.version !== stateVersion) { + return + } + return deserializer.deserialize(state, this.atomEnvironment) + } else { + return console.warn('No deserializer found for', state) + } + } + + // Get the deserializer for the state. + // + // * `state` The state {Object} being deserialized. + get (state) { + if (state == null) { + return + } + + let stateDeserializer = ( + (typeof state.get === 'function') && state.get('deserializer') || + state.deserializer + ) + + return this.deserializers[stateDeserializer] + } + + clear () { + this.deserializers = {} + } +} From ebcffb1c8b95e0384a650e5fb2c859e3e07aadd7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 2 Aug 2016 11:18:11 -0700 Subject: [PATCH 19/21] Run joanna with an explicit list of paths --- build/package.json | 2 +- build/tasks/docs-task.coffee | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/package.json b/build/package.json index 8a41ac980..98a806134 100644 --- a/build/package.json +++ b/build/package.json @@ -30,7 +30,7 @@ "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", "grunt-standard": "^2.0.0", - "joanna": "0.0.5", + "joanna": "0.0.6", "legal-eagle": "~0.13.0", "minidump": "~0.9", "npm": "3.10.5", diff --git a/build/tasks/docs-task.coffee b/build/tasks/docs-task.coffee index 5b05688a0..1e80eb2ce 100644 --- a/build/tasks/docs-task.coffee +++ b/build/tasks/docs-task.coffee @@ -6,6 +6,7 @@ _ = require 'underscore-plus' donna = require 'donna' joanna = require 'joanna' tello = require 'tello' +glob = require 'glob' module.exports = (grunt) -> getClassesToInclude = -> @@ -32,7 +33,7 @@ module.exports = (grunt) -> docsOutputDir = grunt.config.get('docsOutputDir') [coffeeMetadata] = donna.generateMetadata(['.']) - jsMetadata = joanna('.') + jsMetadata = joanna(glob.sync('src/*.js')) metadata = { repository: coffeeMetadata.repository, From 2b5f1bda469527ffe89ca80e7d7181146dc26dbf Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 2 Aug 2016 12:19:45 -0600 Subject: [PATCH 20/21] Always seek to specified position in TokenizedBufferIterator Fixes #10350 Signed-off-by: Antonio Scandurra --- spec/tokenized-buffer-iterator-spec.js | 204 ++++++++++++++++--------- spec/tokenized-buffer-spec.coffee | 2 +- src/tokenized-buffer-iterator.coffee | 25 +-- 3 files changed, 145 insertions(+), 86 deletions(-) diff --git a/spec/tokenized-buffer-iterator-spec.js b/spec/tokenized-buffer-iterator-spec.js index 8d0e458f4..93aaf21e0 100644 --- a/spec/tokenized-buffer-iterator-spec.js +++ b/spec/tokenized-buffer-iterator-spec.js @@ -4,100 +4,152 @@ import TokenizedBufferIterator from '../src/tokenized-buffer-iterator' import {Point} from 'text-buffer' describe('TokenizedBufferIterator', () => { - it('reports two boundaries at the same position when tags close, open, then close again without a non-negative integer separating them (regression)', () => { - const tokenizedBuffer = { - tokenizedLineForRow () { - return { - tags: [-1, -2, -1, -2], - text: '', - openScopes: [] - } - } - } - - const grammarRegistry = { - scopeForId () { - return 'foo' - } - } - - const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry) - - iterator.seek(Point(0, 0)) - expect(iterator.getPosition()).toEqual(Point(0, 0)) - expect(iterator.getCloseTags()).toEqual([]) - expect(iterator.getOpenTags()).toEqual(['foo']) - - iterator.moveToSuccessor() - expect(iterator.getPosition()).toEqual(Point(0, 0)) - expect(iterator.getCloseTags()).toEqual(['foo']) - expect(iterator.getOpenTags()).toEqual(['foo']) - - iterator.moveToSuccessor() - expect(iterator.getCloseTags()).toEqual(['foo']) - expect(iterator.getOpenTags()).toEqual([]) - }) - - it("reports a boundary at line end if the next line's open scopes don't match the containing tags for the current line", () => { - const tokenizedBuffer = { - tokenizedLineForRow (row) { - if (row === 0) { + describe('seek(position)', function () { + it('seeks to the leftmost tag boundary at the given position, returning the containing tags', function () { + const tokenizedBuffer = { + tokenizedLineForRow (row) { return { - tags: [-1, 3, -2, -3], - text: 'bar', + tags: [-1, -2, -3, -4, -5, 3, -3, -4, -6], + text: 'foo', openScopes: [] } - } else if (row === 1) { + } + } + + const grammarRegistry = { + scopeForId (id) { return { - tags: [3], - text: 'baz', - openScopes: [-1] - } - } else if (row === 2) { + '-1': 'foo', '-2': 'foo', + '-3': 'bar', '-4': 'bar', + '-5': 'baz', '-6': 'baz' + }[id] + } + } + + const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry) + + expect(iterator.seek(Point(0, 0))).toEqual([]) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['foo']) + + iterator.moveToSuccessor() + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual(['bar']) + + expect(iterator.seek(Point(0, 1))).toEqual(['baz']) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual([]) + + iterator.moveToSuccessor() + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['bar']) + + expect(iterator.seek(Point(0, 3))).toEqual(['baz']) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['bar']) + + iterator.moveToSuccessor() + expect(iterator.getCloseTags()).toEqual(['bar', 'baz']) + expect(iterator.getOpenTags()).toEqual([]) + }) + }) + + describe('moveToSuccessor()', function () { + it('reports two boundaries at the same position when tags close, open, then close again without a non-negative integer separating them (regression)', () => { + const tokenizedBuffer = { + tokenizedLineForRow () { return { - tags: [-2], + tags: [-1, -2, -1, -2], text: '', - openScopes: [-1] + openScopes: [] } } } - } - const grammarRegistry = { - scopeForId (id) { - if (id === -2 || id === -1) { + const grammarRegistry = { + scopeForId () { return 'foo' - } else if (id === -3) { - return 'qux' } } - } - const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry) + const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry) - iterator.seek(Point(0, 0)) - expect(iterator.getPosition()).toEqual(Point(0, 0)) - expect(iterator.getCloseTags()).toEqual([]) - expect(iterator.getOpenTags()).toEqual(['foo']) + iterator.seek(Point(0, 0)) + expect(iterator.getPosition()).toEqual(Point(0, 0)) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['foo']) - iterator.moveToSuccessor() - expect(iterator.getPosition()).toEqual(Point(0, 3)) - expect(iterator.getCloseTags()).toEqual(['foo']) - expect(iterator.getOpenTags()).toEqual(['qux']) + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(0, 0)) + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual(['foo']) - iterator.moveToSuccessor() - expect(iterator.getPosition()).toEqual(Point(0, 3)) - expect(iterator.getCloseTags()).toEqual(['qux']) - expect(iterator.getOpenTags()).toEqual([]) + iterator.moveToSuccessor() + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual([]) + }) - iterator.moveToSuccessor() - expect(iterator.getPosition()).toEqual(Point(1, 0)) - expect(iterator.getCloseTags()).toEqual([]) - expect(iterator.getOpenTags()).toEqual(['foo']) + it("reports a boundary at line end if the next line's open scopes don't match the containing tags for the current line", () => { + const tokenizedBuffer = { + tokenizedLineForRow (row) { + if (row === 0) { + return { + tags: [-1, 3, -2, -3], + text: 'bar', + openScopes: [] + } + } else if (row === 1) { + return { + tags: [3], + text: 'baz', + openScopes: [-1] + } + } else if (row === 2) { + return { + tags: [-2], + text: '', + openScopes: [-1] + } + } + } + } - iterator.moveToSuccessor() - expect(iterator.getPosition()).toEqual(Point(2, 0)) - expect(iterator.getCloseTags()).toEqual(['foo']) - expect(iterator.getOpenTags()).toEqual([]) + const grammarRegistry = { + scopeForId (id) { + if (id === -2 || id === -1) { + return 'foo' + } else if (id === -3) { + return 'qux' + } + } + } + + const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry) + + iterator.seek(Point(0, 0)) + expect(iterator.getPosition()).toEqual(Point(0, 0)) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['foo']) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(0, 3)) + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual(['qux']) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(0, 3)) + expect(iterator.getCloseTags()).toEqual(['qux']) + expect(iterator.getOpenTags()).toEqual([]) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(1, 0)) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['foo']) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(2, 0)) + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual([]) + }) }) }) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index d11c93213..6c437d953 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -816,7 +816,7 @@ describe "TokenizedBuffer", -> expect(iterator.seek(Point(0, 8))).toEqual(["source.js"]) expect(iterator.getPosition()).toEqual(Point(0, 8)) expect(iterator.seek(Point(1, 0))).toEqual(["source.js", "comment.block.js"]) - expect(iterator.getPosition()).toEqual(Point(1, 5)) + expect(iterator.getPosition()).toEqual(Point(1, 0)) expect(iterator.seek(Point(1, 18))).toEqual(["source.js", "constant.numeric.decimal.js"]) expect(iterator.getPosition()).toEqual(Point(1, 18)) diff --git a/src/tokenized-buffer-iterator.coffee b/src/tokenized-buffer-iterator.coffee index 780156e42..591943a48 100644 --- a/src/tokenized-buffer-iterator.coffee +++ b/src/tokenized-buffer-iterator.coffee @@ -18,24 +18,31 @@ class TokenizedBufferIterator @currentLineLength = currentLine.text.length @containingTags = @currentLineOpenTags.map (id) => @grammarRegistry.scopeForId(id) currentColumn = 0 + for tag, index in @currentTags if tag >= 0 - if currentColumn >= position.column and @isAtTagBoundary() + if currentColumn is position.column @tagIndex = index break else currentColumn += tag @containingTags.pop() while @closeTags.shift() - @containingTags.push(tag) while tag = @openTags.shift() - else - scopeName = @grammarRegistry.scopeForId(tag) - if tag % 2 is 0 - if @openTags.length > 0 + @containingTags.push(openTag) while openTag = @openTags.shift() + if currentColumn > position.column @tagIndex = index break - else - @closeTags.push(scopeName) - else + else + scopeName = @grammarRegistry.scopeForId(tag) + if tag % 2 is 0 # close tag + if @openTags.length > 0 + if currentColumn is position.column + @tagIndex = index + break + else + @containingTags.pop() while @closeTags.shift() + @containingTags.push(openTag) while openTag = @openTags.shift() + @closeTags.push(scopeName) + else # open tag @openTags.push(scopeName) @tagIndex ?= @currentTags.length From 9a72c7e65e543d7125300b294358af2f80616509 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 2 Aug 2016 12:14:00 -0700 Subject: [PATCH 21/21] Fix exception when package requires an incompatible native module --- spec/package-spec.coffee | 19 +++++++++++++++---- src/package.coffee | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/spec/package-spec.coffee b/spec/package-spec.coffee index 92218e749..57d7a661f 100644 --- a/spec/package-spec.coffee +++ b/spec/package-spec.coffee @@ -24,14 +24,14 @@ describe "Package", -> mockLocalStorage() it "does not activate it", -> - packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module') + packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-incompatible-native-module') pack = buildPackage(packagePath) expect(pack.isCompatible()).toBe false expect(pack.incompatibleModules[0].name).toBe 'native-module' expect(pack.incompatibleModules[0].path).toBe path.join(packagePath, 'node_modules', 'native-module') it "utilizes _atomModuleCache if present to determine the package's native dependencies", -> - packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-ignored-incompatible-native-module') + packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-ignored-incompatible-native-module') pack = buildPackage(packagePath) expect(pack.getNativeModuleDependencyPaths().length).toBe(1) # doesn't see the incompatible module expect(pack.isCompatible()).toBe true @@ -41,8 +41,7 @@ describe "Package", -> expect(pack.isCompatible()).toBe false it "caches the incompatible native modules in local storage", -> - packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module') - + packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-incompatible-native-module') expect(buildPackage(packagePath).isCompatible()).toBe false expect(global.localStorage.getItem.callCount).toBe 1 expect(global.localStorage.setItem.callCount).toBe 1 @@ -51,6 +50,18 @@ describe "Package", -> expect(global.localStorage.getItem.callCount).toBe 2 expect(global.localStorage.setItem.callCount).toBe 1 + it "logs an error to the console describing the problem", -> + packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-incompatible-native-module') + + spyOn(console, 'warn') + spyOn(atom.notifications, 'addFatalError') + + buildPackage(packagePath).activateNow() + + expect(atom.notifications.addFatalError).not.toHaveBeenCalled() + expect(console.warn.callCount).toBe(1) + expect(console.warn.mostRecentCall.args[0]).toContain('it requires one or more incompatible native modules (native-module)') + describe "::rebuild()", -> beforeEach -> mockLocalStorage() diff --git a/src/package.coffee b/src/package.coffee index 1bb8a939c..94763f961 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -427,7 +427,7 @@ class Package return @mainModule if @mainModuleRequired unless @isCompatible() console.warn """ - Failed to require the main module of '#{@name}' because it requires one or more incompatible native modules (#{_.map(@incompatibleModules, 'name').join(', ')}). + Failed to require the main module of '#{@name}' because it requires one or more incompatible native modules (#{_.pluck(@incompatibleModules, 'name').join(', ')}). Run `apm rebuild` in the package directory and restart Atom to resolve. """ return