From 5b494b7b174be6774b786fcc8ea81eb41ab83118 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 01:19:12 +0200 Subject: [PATCH 01/23] Lazily construct placeholder lines in TokenizedBuffer --- src/tokenized-buffer.coffee | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 55d2e7e37..72a972967 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -112,14 +112,14 @@ class TokenizedBuffer extends Model throw new Error("No grammar found for path: #{path}") hasTokenForSelector: (selector) -> - for {tokens} in @tokenizedLines - for token in tokens + for tokenizedLine in @tokenizedLines when tokenizedLine? + for token in tokenizedLine.tokens return true if selector.matches(token.scopes) false retokenizeLines: -> lastRow = @buffer.getLastRow() - @tokenizedLines = @buildPlaceholderTokenizedLinesForRows(0, lastRow) + @tokenizedLines = new Array(lastRow + 1) @invalidRows = [] @invalidateRow(0) @fullyTokenized = false @@ -248,12 +248,12 @@ class TokenizedBuffer extends Model @emitter.emit 'did-change', event retokenizeWhitespaceRowsIfIndentLevelChanged: (row, increment) -> - line = @tokenizedLines[row] + line = @tokenizedLineForRow(row) if line?.isOnlyWhitespace() and @indentLevelForRow(row) isnt line.indentLevel while line?.isOnlyWhitespace() @tokenizedLines[row] = @buildTokenizedLineForRow(row, @stackForRow(row - 1), @openScopesForRow(row)) row += increment - line = @tokenizedLines[row] + line = @tokenizedLineForRow(row) row - increment @@ -313,7 +313,7 @@ class TokenizedBuffer extends Model tokenizedLines buildPlaceholderTokenizedLinesForRows: (startRow, endRow) -> - @buildPlaceholderTokenizedLineForRow(row) for row in [startRow..endRow] + @buildPlaceholderTokenizedLineForRow(row) for row in [startRow..endRow] by 1 buildPlaceholderTokenizedLineForRow: (row) -> openScopes = [@grammar.startIdForScope(@grammar.scopeName)] @@ -341,7 +341,8 @@ class TokenizedBuffer extends Model null tokenizedLineForRow: (bufferRow) -> - @tokenizedLines[bufferRow] + if 0 <= bufferRow < @tokenizedLines.length + @tokenizedLines[bufferRow] ?= @buildPlaceholderTokenizedLineForRow(bufferRow) stackForRow: (bufferRow) -> @tokenizedLines[bufferRow]?.ruleStack @@ -418,17 +419,17 @@ class TokenizedBuffer extends Model tokenForPosition: (position) -> {row, column} = Point.fromObject(position) - @tokenizedLines[row].tokenAtBufferColumn(column) + @tokenizedLineForRow(row).tokenAtBufferColumn(column) tokenStartPositionForPosition: (position) -> {row, column} = Point.fromObject(position) - column = @tokenizedLines[row].tokenStartColumnForBufferColumn(column) + column = @tokenizedLineForRow(row).tokenStartColumnForBufferColumn(column) new Point(row, column) bufferRangeForScopeAtPosition: (selector, position) -> position = Point.fromObject(position) - {openScopes, tags} = @tokenizedLines[position.row] + {openScopes, tags} = @tokenizedLineForRow(position.row) scopes = openScopes.map (tag) -> atom.grammars.scopeForId(tag) startColumn = 0 From de508db9b2c9cb609f565b38adeeaee0823c7e48 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 02:25:57 +0200 Subject: [PATCH 02/23] Implement basic large file mode for editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don’t tokenize * Don’t build metadata to support folds and soft wraps Remaining issues: * Max line length is hard coded * Foldable indicators should be disabled * Folding via API should be disallowed --- src/display-buffer.coffee | 68 +++++++++++++++++++++++++++---------- src/project.coffee | 8 ++--- src/text-editor.coffee | 4 +-- src/tokenized-buffer.coffee | 13 +++++-- 4 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index d98f93fe3..32f374776 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -24,13 +24,13 @@ class DisplayBuffer extends Model horizontalScrollMargin: 6 scopedCharacterWidthsChangeCount: 0 - constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, ignoreInvisibles}={}) -> + constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, ignoreInvisibles, @largeFileMode}={}) -> super @emitter = new Emitter @disposables = new CompositeDisposable - @tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer, ignoreInvisibles}) + @tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer, ignoreInvisibles, @largeFileMode}) @buffer = @tokenizedBuffer.buffer @charWidthsByScope = {} @markers = {} @@ -478,7 +478,10 @@ class DisplayBuffer extends Model # # Returns {TokenizedLine} tokenizedLineForScreenRow: (screenRow) -> - @screenLines[screenRow] + if @largeFileMode + @tokenizedBuffer.tokenizedLineForRow(screenRow) + else + @screenLines[screenRow] # Gets the screen lines for the given screen row range. # @@ -487,13 +490,19 @@ class DisplayBuffer extends Model # # Returns an {Array} of {TokenizedLine}s. tokenizedLinesForScreenRows: (startRow, endRow) -> - @screenLines[startRow..endRow] + if @largeFileMode + @tokenizedBuffer.tokenizedLinesForRows(startRow, endRow) + else + @screenLines[startRow..endRow] # Gets all the screen lines. # # Returns an {Array} of {TokenizedLine}s. getTokenizedLines: -> - new Array(@screenLines...) + if @largeFileMode + @tokenizedBuffer.tokenizedLinesForRows(0, @getLastRow()) + else + new Array(@screenLines...) indentLevelForLine: (line) -> @tokenizedBuffer.indentLevelForLine(line) @@ -506,8 +515,11 @@ class DisplayBuffer extends Model # # Returns an {Array} of buffer rows as {Numbers}s. bufferRowsForScreenRows: (startScreenRow, endScreenRow) -> - for screenRow in [startScreenRow..endScreenRow] - @rowMap.bufferRowRangeForScreenRow(screenRow)[0] + if @largeFileMode + [startScreenRow..endScreenRow] + else + for screenRow in [startScreenRow..endScreenRow] + @rowMap.bufferRowRangeForScreenRow(screenRow)[0] # Creates a new fold between two row numbers. # @@ -611,10 +623,16 @@ class DisplayBuffer extends Model # # Returns a {Number}. screenRowForBufferRow: (bufferRow) -> - @rowMap.screenRowRangeForBufferRow(bufferRow)[0] + if @largeFileMode + bufferRow + else + @rowMap.screenRowRangeForBufferRow(bufferRow)[0] lastScreenRowForBufferRow: (bufferRow) -> - @rowMap.screenRowRangeForBufferRow(bufferRow)[1] - 1 + if @largeFileMode + bufferRow + else + @rowMap.screenRowRangeForBufferRow(bufferRow)[1] - 1 # Given a screen row, this converts it into a buffer row. # @@ -622,7 +640,10 @@ class DisplayBuffer extends Model # # Returns a {Number}. bufferRowForScreenRow: (screenRow) -> - @rowMap.bufferRowRangeForScreenRow(screenRow)[0] + if @largeFileMode + screenRow + else + @rowMap.bufferRowRangeForScreenRow(screenRow)[0] # Given a buffer range, this converts it into a screen position. # @@ -724,7 +745,10 @@ class DisplayBuffer extends Model # # Returns a {Number}. getLineCount: -> - @screenLines.length + if @largeFileMode + @tokenizedBuffer.getLineCount() + else + @screenLines.length # Gets the number of the last screen line. # @@ -736,13 +760,19 @@ class DisplayBuffer extends Model # # Returns a {Number}. getMaxLineLength: -> - @maxLineLength + if @largeFileMode + 100 + else + @maxLineLength # Gets the row number of the longest screen line. # # Return a {} getLongestScreenRow: -> - @longestScreenRow + if @largeFileMode + 0 + else + @longestScreenRow # Given a buffer position, this converts it into a screen position. # @@ -759,7 +789,7 @@ class DisplayBuffer extends Model {row, column} = @buffer.clipPosition(bufferPosition) [startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row) for screenRow in [startScreenRow...endScreenRow] - screenLine = @screenLines[screenRow] + screenLine = @tokenizedLineForScreenRow(screenRow) unless screenLine? throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row", @@ -792,7 +822,7 @@ class DisplayBuffer extends Model bufferPositionForScreenPosition: (screenPosition, options) -> {row, column} = @clipScreenPosition(Point.fromObject(screenPosition), options) [bufferRow] = @rowMap.bufferRowRangeForScreenRow(row) - new Point(bufferRow, @screenLines[row].bufferColumnForScreenColumn(column)) + new Point(bufferRow, @tokenizedLineForScreenRow(row).bufferColumnForScreenColumn(column)) # Retrieves the grammar's token scopeDescriptor for a buffer position. # @@ -856,13 +886,13 @@ class DisplayBuffer extends Model else if column < 0 column = 0 - screenLine = @screenLines[row] + screenLine = @tokenizedLineForScreenRow(row) maxScreenColumn = screenLine.getMaxScreenColumn() if screenLine.isSoftWrapped() and column >= maxScreenColumn if wrapAtSoftNewlines row++ - column = @screenLines[row].clipScreenColumn(0) + column = @tokenizedLineForScreenRow(row).clipScreenColumn(0) else column = screenLine.clipScreenColumn(maxScreenColumn - 1) else if screenLine.isColumnInsideSoftWrapIndentation(column) @@ -870,7 +900,7 @@ class DisplayBuffer extends Model column = screenLine.clipScreenColumn(0) else row-- - column = @screenLines[row].getMaxScreenColumn() - 1 + column = @tokenizedLineForScreenRow(row).getMaxScreenColumn() - 1 else if wrapBeyondNewlines and column > maxScreenColumn and row < @getLastRow() row++ column = 0 @@ -1137,6 +1167,8 @@ class DisplayBuffer extends Model @setScrollTop(Math.min(@getScrollTop(), @getMaxScrollTop())) if delta < 0 updateScreenLines: (startBufferRow, endBufferRow, bufferDelta=0, options={}) -> + return if @largeFileMode + startBufferRow = @rowMap.bufferRowRangeForBufferRow(startBufferRow)[0] endBufferRow = @rowMap.bufferRowRangeForBufferRow(endBufferRow - 1)[1] startScreenRow = @rowMap.screenRowRangeForBufferRow(startBufferRow)[0] diff --git a/src/project.coffee b/src/project.coffee index 75cdb714f..e2eed7ac5 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -376,11 +376,6 @@ class Project extends Model # # Returns a promise that resolves to the {TextBuffer}. buildBuffer: (absoluteFilePath) -> - if fs.getSizeSync(absoluteFilePath) >= 2 * 1048576 # 2MB - error = new Error("Atom can only handle files < 2MB for now.") - error.code = 'EFILETOOLARGE' - throw error - buffer = new TextBuffer({filePath: absoluteFilePath}) @addBuffer(buffer) buffer.load() @@ -410,7 +405,8 @@ class Project extends Model buffer?.destroy() buildEditorForBuffer: (buffer, editorOptions) -> - editor = new TextEditor(_.extend({buffer, registerEditor: true}, editorOptions)) + largeFileMode = fs.getSizeSync(buffer.getPath()) >= 2 * 1048576 # 2MB + editor = new TextEditor(_.extend({buffer, largeFileMode, registerEditor: true}, editorOptions)) editor eachBuffer: (args...) -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 4aacb3dfa..9a9262e50 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -75,7 +75,7 @@ class TextEditor extends Model 'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows', toProperty: 'languageMode' - constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, buffer, registerEditor, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible}={}) -> + constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, buffer, registerEditor, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode}={}) -> super @emitter = new Emitter @@ -84,7 +84,7 @@ class TextEditor extends Model @selections = [] buffer ?= new TextBuffer - @displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrapped, ignoreInvisibles: @mini}) + @displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrapped, ignoreInvisibles: @mini, largeFileMode}) @buffer = @displayBuffer.buffer @softTabs = @usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 72a972967..5aa3482a9 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -24,7 +24,7 @@ class TokenizedBuffer extends Model visible: false configSettings: null - constructor: ({@buffer, @tabLength, @ignoreInvisibles}) -> + constructor: ({@buffer, @tabLength, @ignoreInvisibles, @largeFileMode}) -> @emitter = new Emitter @disposables = new CompositeDisposable @tokenIterator = new TokenIterator @@ -209,6 +209,8 @@ class TokenizedBuffer extends Model return invalidateRow: (row) -> + return if @largeFileMode + @invalidRows.push(row) @invalidRows.sort (a, b) -> a - b @tokenizeInBackground() @@ -230,7 +232,10 @@ class TokenizedBuffer extends Model @updateInvalidRows(start, end, delta) previousEndStack = @stackForRow(end) # used in spill detection below - newTokenizedLines = @buildTokenizedLinesForRows(start, end + delta, @stackForRow(start - 1), @openScopesForRow(start)) + if @largeFileMode + newTokenizedLines = @buildPlaceholderTokenizedLinesForRows(start, end + delta) + else + newTokenizedLines = @buildTokenizedLinesForRows(start, end + delta, @stackForRow(start - 1), @openScopesForRow(start)) _.spliceWithArray(@tokenizedLines, start, end - start + 1, newTokenizedLines) start = @retokenizeWhitespaceRowsIfIndentLevelChanged(start - 1, -1) @@ -344,6 +349,10 @@ class TokenizedBuffer extends Model if 0 <= bufferRow < @tokenizedLines.length @tokenizedLines[bufferRow] ?= @buildPlaceholderTokenizedLineForRow(bufferRow) + tokenizedLinesForRows: (startRow, endRow) -> + for row in [startRow..endRow] by 1 + @tokenizedLineForRow(row) + stackForRow: (bufferRow) -> @tokenizedLines[bufferRow]?.ruleStack From ccd739ff65b5cfd9f043bb9c0c1ced466c655554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=C5=BDu=C5=BEak?= Date: Fri, 5 Jun 2015 16:35:48 +0200 Subject: [PATCH 03/23] :arrow_up: notifications@0.51.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eabbf5978..1db19fde1 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "link": "0.30.0", "markdown-preview": "0.150.0", "metrics": "0.51.0", - "notifications": "0.50.0", + "notifications": "0.51.0", "open-on-github": "0.37.0", "package-generator": "0.39.0", "release-notes": "0.52.0", From 022d0cead3633711d9f15da5f56d64ead667e1c2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 5 Jun 2015 08:43:11 -0700 Subject: [PATCH 04/23] Re-enable tree-view and tabs sublime-tabs is installed --- .../packages/sublime-tabs/package.json | 4 ++++ spec/package-manager-spec.coffee | 19 +++++++++++++++++++ src/package-manager.coffee | 11 +++++++++++ 3 files changed, 34 insertions(+) create mode 100644 spec/fixtures/packages/sublime-tabs/package.json diff --git a/spec/fixtures/packages/sublime-tabs/package.json b/spec/fixtures/packages/sublime-tabs/package.json new file mode 100644 index 000000000..2fd01501d --- /dev/null +++ b/spec/fixtures/packages/sublime-tabs/package.json @@ -0,0 +1,4 @@ +{ + "name": "sublime-tabs", + "version": "1.0.0" +} diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index f09f43824..af9378caa 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -877,3 +877,22 @@ describe "PackageManager", -> runs -> expect(fs.isDirectorySync(autocompleteCSSPath)).toBe false expect(fs.isSymbolicLinkSync(autocompletePlusPath)).toBe true + + describe "when the deprecated sublime-tabs package is installed", -> + it "enables the tree-view and tabs package", -> + atom.config.pushAtKeyPath('core.disabledPackages', 'tree-view') + atom.config.pushAtKeyPath('core.disabledPackages', 'tabs') + + spyOn(atom.packages, 'getAvailablePackagePaths').andReturn [ + path.join(__dirname, 'fixtures', 'packages', 'sublime-tabs') + path.resolve(__dirname, '..', 'node_modules', 'tree-view') + path.resolve(__dirname, '..', 'node_modules', 'tabs') + ] + atom.packages.loadPackages() + + waitsFor -> + not atom.packages.isPackageDisabled('tree-view') and not atom.packages.isPackageDisabled('tabs') + + runs -> + expect(atom.packages.isPackageLoaded('tree-view')).toBe true + expect(atom.packages.isPackageLoaded('tabs')).toBe true diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 306e553a8..478f16dd7 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -314,6 +314,10 @@ class PackageManager @uninstallAutocompletePlus() packagePaths = @getAvailablePackagePaths() + + # TODO: remove after a few atom versions. + @migrateSublimeTabsSettings(packagePaths) + packagePaths = packagePaths.filter (packagePath) => not @isPackageDisabled(path.basename(packagePath)) packagePaths = _.uniq packagePaths, (packagePath) -> path.basename(packagePath) @loadPackage(packagePath) for packagePath in packagePaths @@ -445,6 +449,13 @@ class PackageManager @uninstallDirectory(dirToRemove) return + # TODO: remove this after a few versions + migrateSublimeTabsSettings: (packagePaths) -> + for packagePath in packagePaths when path.basename(packagePath) is 'sublime-tabs' + atom.config.removeAtKeyPath('core.disabledPackages', 'tree-view') + atom.config.removeAtKeyPath('core.disabledPackages', 'tabs') + return + uninstallDirectory: (directory) -> symlinkPromise = new Promise (resolve) -> fs.isSymbolicLink directory, (isSymLink) -> resolve(isSymLink) From 71f9c2641846eeaffbfbf9b664250d474eb12e8d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 5 Jun 2015 08:47:00 -0700 Subject: [PATCH 05/23] Only migrate config when not including deprecated APIs --- spec/package-manager-spec.coffee | 10 ++++++++++ src/package-manager.coffee | 1 + 2 files changed, 11 insertions(+) diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index af9378caa..26b675975 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -879,6 +879,16 @@ describe "PackageManager", -> expect(fs.isSymbolicLinkSync(autocompletePlusPath)).toBe true describe "when the deprecated sublime-tabs package is installed", -> + grim = require 'grim' + includeDeprecatedAPIs = null + + beforeEach -> + {includeDeprecatedAPIs} = grim + grim.includeDeprecatedAPIs = false + + afterEach -> + grim.includeDeprecatedAPIs = includeDeprecatedAPIs + it "enables the tree-view and tabs package", -> atom.config.pushAtKeyPath('core.disabledPackages', 'tree-view') atom.config.pushAtKeyPath('core.disabledPackages', 'tabs') diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 478f16dd7..aeb67ea3b 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -451,6 +451,7 @@ class PackageManager # TODO: remove this after a few versions migrateSublimeTabsSettings: (packagePaths) -> + return if Grim.includeDeprecatedAPIs for packagePath in packagePaths when path.basename(packagePath) is 'sublime-tabs' atom.config.removeAtKeyPath('core.disabledPackages', 'tree-view') atom.config.removeAtKeyPath('core.disabledPackages', 'tabs') From 786bfc7ebdd3d56457f12ae4d8588c657db2075f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=C5=BDu=C5=BEak?= Date: Fri, 5 Jun 2015 17:51:42 +0200 Subject: [PATCH 06/23] :arrow_up: notifications@0.52.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1db19fde1..e9755c850 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "link": "0.30.0", "markdown-preview": "0.150.0", "metrics": "0.51.0", - "notifications": "0.51.0", + "notifications": "0.52.0", "open-on-github": "0.37.0", "package-generator": "0.39.0", "release-notes": "0.52.0", From 2b3329673fdf6882797c65f9e1e428198e0b1346 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 5 Jun 2015 09:03:22 -0700 Subject: [PATCH 07/23] :arrow_up: settings-view@0.208 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e9755c850..20cb1a580 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "open-on-github": "0.37.0", "package-generator": "0.39.0", "release-notes": "0.52.0", - "settings-view": "0.207.0", + "settings-view": "0.208.0", "snippets": "0.93.0", "spell-check": "0.58.0", "status-bar": "0.74.0", From 53a3239379e0ae63c136f1e513e65b103705af82 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 18:17:30 +0200 Subject: [PATCH 08/23] Use buffer coordinates in TokenizedBuffer::scopeDescriptorForPosition Fixes #7073 --- src/tokenized-buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 55d2e7e37..6b25847c9 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -405,7 +405,7 @@ class TokenizedBuffer extends Model iterator = @tokenizedLines[row].getTokenIterator() while iterator.next() - if iterator.getScreenEnd() > column + if iterator.getBufferEnd() > column scopes = iterator.getScopes() break From c34838277d68bd19f0b97ecede4d86c684a111b8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 19:55:34 +0200 Subject: [PATCH 09/23] =?UTF-8?q?In=20largeFileMode,=20base=20maxLineLengt?= =?UTF-8?q?h=20on=20the=20longest=20line=20we=E2=80=99ve=20seen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is pretty hacky. If we want to compute the true longest line, we’ll need to process every line, which is what large file mode is designed to avoid. So now whenever the display buffer returns a line, it has the potential to update the longest line. This is a bit weird because retrieving a line now has a side effect. It’s even more weird because the longest line will actually be wrong for the initial state updates in the TextEditorPresenter. But as soon as the user interacts in any way the dimensions are recomputed, so it works for now. A better approach may be to set a visible region on the display buffer. But I’d like to keep this low-touch until we have a chance to revisit the design of DisplayBuffer in a bigger way. --- src/display-buffer.coffee | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 32f374776..388182c8f 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -479,7 +479,11 @@ class DisplayBuffer extends Model # Returns {TokenizedLine} tokenizedLineForScreenRow: (screenRow) -> if @largeFileMode - @tokenizedBuffer.tokenizedLineForRow(screenRow) + line = @tokenizedBuffer.tokenizedLineForRow(screenRow) + if line.text.length > @maxLineLength + @maxLineLength = line.text.length + @longestScreenRow = screenRow + line else @screenLines[screenRow] @@ -760,19 +764,13 @@ class DisplayBuffer extends Model # # Returns a {Number}. getMaxLineLength: -> - if @largeFileMode - 100 - else - @maxLineLength + @maxLineLength # Gets the row number of the longest screen line. # # Return a {} getLongestScreenRow: -> - if @largeFileMode - 0 - else - @longestScreenRow + @longestScreenRow # Given a buffer position, this converts it into a screen position. # From 0f24e369c60d5b021d46228315bac9c1eca3713d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 5 Jun 2015 11:29:36 -0700 Subject: [PATCH 10/23] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20cb1a580..879348d77 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "space-pen": "3.8.2", "stacktrace-parser": "0.1.1", "temp": "0.8.1", - "text-buffer": "6.2.0", + "text-buffer": "6.2.1", "theorist": "^1.0.2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", From 2337254afbe827b8bea2b7819800c9aa663de687 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 5 Jun 2015 12:05:05 -0700 Subject: [PATCH 11/23] Fix text-editor-component-spec failure Grouping intervals are exclusive now. It shouldn't affect anybody since it's a one-millisecond change to the meaning of grouping-interval, but it required changing some time intervals in this spec. --- spec/text-editor-component-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index b2bb09acd..e4d502ded 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2340,10 +2340,10 @@ describe "TextEditorComponent", -> editor.setText("") componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode)) - currentTime += 100 + currentTime += 99 componentNode.dispatchEvent(buildTextInputEvent(data: 'y', target: inputNode)) - currentTime += 100 + currentTime += 99 componentNode.dispatchEvent(new CustomEvent('editor:duplicate-lines', bubbles: true, cancelable: true)) currentTime += 101 From 639647d1158eb90c510811ab3cb66d199e7a2904 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 21:31:06 +0200 Subject: [PATCH 12/23] Disallow fold creation in large file mode --- src/display-buffer.coffee | 9 +++++---- src/tokenized-buffer.coffee | 7 ++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 388182c8f..2fccc4eb0 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -532,10 +532,11 @@ class DisplayBuffer extends Model # # Returns the new {Fold}. createFold: (startRow, endRow) -> - foldMarker = - @findFoldMarker({startRow, endRow}) ? - @buffer.markRange([[startRow, 0], [endRow, Infinity]], @getFoldMarkerAttributes()) - @foldForMarker(foldMarker) + unless @largeFileMode + foldMarker = + @findFoldMarker({startRow, endRow}) ? + @buffer.markRange([[startRow, 0], [endRow, Infinity]], @getFoldMarkerAttributes()) + @foldForMarker(foldMarker) isFoldedAtBufferRow: (bufferRow) -> @largestFoldContainingBufferRow(bufferRow)? diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 5aa3482a9..f051cb4be 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -263,6 +263,8 @@ class TokenizedBuffer extends Model row - increment updateFoldableStatus: (startRow, endRow) -> + return [startRow, endRow] if @largeFileMode + scanStartRow = @buffer.previousNonBlankRow(startRow) ? startRow scanStartRow-- while scanStartRow > 0 and @tokenizedLineForRow(scanStartRow).isComment() scanEndRow = @buffer.nextNonBlankRow(endRow) ? endRow @@ -278,7 +280,10 @@ class TokenizedBuffer extends Model [startRow, endRow] isFoldableAtRow: (row) -> - @isFoldableCodeAtRow(row) or @isFoldableCommentAtRow(row) + if @largeFileMode + false + else + @isFoldableCodeAtRow(row) or @isFoldableCommentAtRow(row) # Returns a {Boolean} indicating whether the given buffer row starts # a a foldable row range due to the code's indentation patterns. From efd4662de0af4776bca54ed1a79a5c7e4c8f98b2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 21:36:11 +0200 Subject: [PATCH 13/23] =?UTF-8?q?Don=E2=80=99t=20allow=20soft=20wrap=20to?= =?UTF-8?q?=20be=20enabled=20in=20large=20file=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/display-buffer.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 2fccc4eb0..8aa6e9d1b 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -445,7 +445,10 @@ class DisplayBuffer extends Model @isSoftWrapped() isSoftWrapped: -> - @softWrapped ? @configSettings.softWrap ? false + if @largeFileMode + false + else + @softWrapped ? @configSettings.softWrap ? false # Set the number of characters that fit horizontally in the editor. # From 56273e7eef6c16093ca45eebf346c9903ddb614e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 21:44:23 +0200 Subject: [PATCH 14/23] Preserve large file mode across serialization and pane splits --- src/display-buffer.coffee | 3 ++- src/tokenized-buffer.coffee | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 8aa6e9d1b..910f7b177 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -89,13 +89,14 @@ class DisplayBuffer extends Model scrollTop: @scrollTop scrollLeft: @scrollLeft tokenizedBuffer: @tokenizedBuffer.serialize() + largeFileMode: @largeFileMode deserializeParams: (params) -> params.tokenizedBuffer = TokenizedBuffer.deserialize(params.tokenizedBuffer) params copy: -> - newDisplayBuffer = new DisplayBuffer({@buffer, tabLength: @getTabLength()}) + newDisplayBuffer = new DisplayBuffer({@buffer, tabLength: @getTabLength(), @largeFileMode}) newDisplayBuffer.setScrollTop(@getScrollTop()) newDisplayBuffer.setScrollLeft(@getScrollLeft()) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index f051cb4be..6337aa3c6 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -44,6 +44,7 @@ class TokenizedBuffer extends Model bufferPath: @buffer.getPath() tabLength: @tabLength ignoreInvisibles: @ignoreInvisibles + largeFileMode: @largeFileMode deserializeParams: (params) -> params.buffer = atom.project.bufferForPathSync(params.bufferPath) From 1800a9fb1d6a6cd147a275a6efd2458606974928 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 5 Jun 2015 12:52:34 -0700 Subject: [PATCH 15/23] Prepare 0.208 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 879348d77..4e998abb4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "0.207.0", + "version": "0.208.0", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { From 48ce361bd0f81b369a5f10ce7e9a4c2e5d3c339f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 22:24:49 +0200 Subject: [PATCH 16/23] Select grammars based on first 10 lines of content, not the entire file --- src/tokenized-buffer.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 6337aa3c6..758aef501 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -67,7 +67,7 @@ class TokenizedBuffer extends Model if grammar.injectionSelector? @retokenizeLines() if @hasTokenForSelector(grammar.injectionSelector) else - newScore = grammar.getScore(@buffer.getPath(), @buffer.getText()) + newScore = grammar.getScore(@buffer.getPath(), @getGrammarSelectionContent()) @setGrammar(grammar, newScore) if newScore > @currentGrammarScore setGrammar: (grammar, score) -> @@ -75,7 +75,7 @@ class TokenizedBuffer extends Model @grammar = grammar @rootScopeDescriptor = new ScopeDescriptor(scopes: [@grammar.scopeName]) - @currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @buffer.getText()) + @currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @getGrammarSelectionContent()) @grammarUpdateDisposable?.dispose() @grammarUpdateDisposable = @grammar.onDidUpdate => @retokenizeLines() @@ -106,8 +106,11 @@ class TokenizedBuffer extends Model @emit 'grammar-changed', grammar if Grim.includeDeprecatedAPIs @emitter.emit 'did-change-grammar', grammar + getGrammarSelectionContent: -> + @buffer.getTextInRange([[0, 0], [10, 0]]) + reloadGrammar: -> - if grammar = atom.grammars.selectGrammar(@buffer.getPath(), @buffer.getText()) + if grammar = atom.grammars.selectGrammar(@buffer.getPath(), @getGrammarSelectionContent()) @setGrammar(grammar) else throw new Error("No grammar found for path: #{path}") From 2732e0dcacea193bbf54a96384320a15f4644935 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 5 Jun 2015 13:31:24 -0700 Subject: [PATCH 17/23] presenter: :fire: redundant decoration state updates --- src/text-editor-presenter.coffee | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 03cfd5a21..2acb59c27 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1134,8 +1134,6 @@ class TextEditorPresenter @shouldUpdateLineNumbersState = true else if decoration.isType('gutter') @shouldUpdateCustomGutterDecorationState = true - if decoration.isType('highlight') - @updateHighlightState(decoration) if decoration.isType('overlay') @shouldUpdateOverlaysState = true @@ -1145,14 +1143,14 @@ class TextEditorPresenter @observeDecoration(decoration) if decoration.isType('line') or decoration.isType('gutter') - @addToLineDecorationCaches(decoration, decoration.getMarker().getScreenRange()) + @shouldUpdateDecorations = true @shouldUpdateTilesState = true if decoration.isType('line') if decoration.isType('line-number') @shouldUpdateLineNumbersState = true else if decoration.isType('gutter') @shouldUpdateCustomGutterDecorationState = true else if decoration.isType('highlight') - @updateHighlightState(decoration) + @shouldUpdateDecorations = true else if decoration.isType('overlay') @shouldUpdateOverlaysState = true @@ -1357,7 +1355,6 @@ class TextEditorPresenter @shouldUpdateHiddenInputState = true @pauseCursorBlinking() @updateCursorState(cursor) - @emitDidUpdateState() startBlinkingCursors: -> From ffcebdad33bc863e88152ffa94717dfc2d669d84 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 23:14:11 +0200 Subject: [PATCH 18/23] Remove outdated test --- spec/workspace-spec.coffee | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 009f95a1c..3e94727d4 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -272,20 +272,6 @@ describe "Workspace", -> beforeEach -> atom.notifications.onDidAddNotification notificationSpy = jasmine.createSpy() - describe "when a large file is opened", -> - beforeEach -> - spyOn(fs, 'getSizeSync').andReturn 2 * 1048577 # 2MB - - it "creates a notification", -> - waitsForPromise -> - workspace.open('file1') - - runs -> - expect(notificationSpy).toHaveBeenCalled() - notification = notificationSpy.mostRecentCall.args[0] - expect(notification.getType()).toBe 'warning' - expect(notification.getMessage()).toContain '< 2MB' - describe "when a file does not exist", -> it "creates an empty buffer for the specified path", -> waitsForPromise -> From df733aa3de889976d43f9d209d15a947883018c0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 23:25:48 +0200 Subject: [PATCH 19/23] Add a basic test for opening an editor in largeFileMode if >= 2MB --- spec/workspace-spec.coffee | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 3e94727d4..4918fc65f 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -224,6 +224,17 @@ describe "Workspace", -> expect(workspace.paneContainer.root.children[0]).toBe pane1 expect(workspace.paneContainer.root.children[1]).toBe pane4 + describe "when the file is large (over 2mb)", -> + it "opens the editor with largeFileMode: true", -> + spyOn(fs, 'getSizeSync').andReturn 2 * 1048577 # 2MB + + editor = null + waitsForPromise -> + workspace.open('sample.js').then (e) -> editor = e + + runs -> + expect(editor.displayBuffer.largeFileMode).toBe true + describe "when passed a path that matches a custom opener", -> it "returns the resource returned by the custom opener", -> fooOpener = (pathToOpen, options) -> {foo: pathToOpen, options} if pathToOpen?.match(/\.foo/) From 3ac9d539ce99d09d17b4dfd2d33c140edeb54e8f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 23:40:29 +0200 Subject: [PATCH 20/23] Add a super basic test for large file mode --- spec/text-editor-spec.coffee | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 8ad1ce390..63b01a178 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -66,6 +66,25 @@ describe "TextEditor", -> expect(editor.tokenizedLineForScreenRow(0).invisibles.eol).toBe '?' + describe "when the editor is constructed with the largeFileMode option set to true", -> + it "loads the editor but doesn't tokenize", -> + editor = null + + waitsForPromise -> + atom.workspace.open('sample.js', largeFileMode: true).then (o) -> editor = o + + runs -> + buffer = editor.getBuffer() + expect(editor.tokenizedLineForScreenRow(0).text).toBe buffer.lineForRow(0) + expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1 + expect(editor.tokenizedLineForScreenRow(1).tokens.length).toBe 2 # soft tab + expect(editor.tokenizedLineForScreenRow(12).text).toBe buffer.lineForRow(12) + expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1 + expect(editor.getCursorScreenPosition()).toEqual [0, 0] + editor.insertText('hey"') + expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1 + expect(editor.tokenizedLineForScreenRow(1).tokens.length).toBe 2 # sof tab + describe "when the editor is constructed with an initialLine option", -> it "positions the cursor on the specified line", -> editor = null From 88812831ce248315f908fe048d0bd38a8d81d86e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 5 Jun 2015 23:40:56 +0200 Subject: [PATCH 21/23] Only check for max line length when lines exist --- src/display-buffer.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 910f7b177..2519fa6d6 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -483,11 +483,11 @@ class DisplayBuffer extends Model # Returns {TokenizedLine} tokenizedLineForScreenRow: (screenRow) -> if @largeFileMode - line = @tokenizedBuffer.tokenizedLineForRow(screenRow) - if line.text.length > @maxLineLength - @maxLineLength = line.text.length - @longestScreenRow = screenRow - line + if line = @tokenizedBuffer.tokenizedLineForRow(screenRow) + if line.text.length > @maxLineLength + @maxLineLength = line.text.length + @longestScreenRow = screenRow + line else @screenLines[screenRow] From 02140c718214c3a9e0160ff1f66cd7ba90042962 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 5 Jun 2015 16:26:53 -0700 Subject: [PATCH 22/23] :arrow_up: find-and-replace, text-buffer --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4e998abb4..cf19292e7 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "space-pen": "3.8.2", "stacktrace-parser": "0.1.1", "temp": "0.8.1", - "text-buffer": "6.2.1", + "text-buffer": "6.3.0", "theorist": "^1.0.2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", @@ -99,7 +99,7 @@ "dev-live-reload": "0.46.0", "encoding-selector": "0.20.0", "exception-reporting": "0.24.0", - "find-and-replace": "0.171.0", + "find-and-replace": "0.172.0", "fuzzy-finder": "0.87.0", "git-diff": "0.55.0", "go-to-line": "0.30.0", From f850750707950cf7d022e467cd5079c75cdf0f4d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 5 Jun 2015 16:44:43 -0700 Subject: [PATCH 23/23] :arrow_up: spell-check --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf19292e7..4e082676a 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "release-notes": "0.52.0", "settings-view": "0.208.0", "snippets": "0.93.0", - "spell-check": "0.58.0", + "spell-check": "0.59.0", "status-bar": "0.74.0", "styleguide": "0.44.0", "symbols-view": "0.97.0",