Merge branch 'master' into as-opaque-tiles

This commit is contained in:
Antonio Scandurra
2015-06-06 11:16:31 +02:00
12 changed files with 182 additions and 74 deletions

View File

@@ -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": {
@@ -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.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",
@@ -110,13 +110,13 @@
"link": "0.30.0",
"markdown-preview": "0.150.0",
"metrics": "0.51.0",
"notifications": "0.50.0",
"notifications": "0.52.0",
"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",
"spell-check": "0.59.0",
"status-bar": "0.74.0",
"styleguide": "0.44.0",
"symbols-view": "0.97.0",

View File

@@ -0,0 +1,4 @@
{
"name": "sublime-tabs",
"version": "1.0.0"
}

View File

@@ -877,3 +877,32 @@ describe "PackageManager", ->
runs ->
expect(fs.isDirectorySync(autocompleteCSSPath)).toBe false
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')
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

View File

@@ -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

View File

@@ -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

View File

@@ -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/)
@@ -272,20 +283,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 ->

View File

@@ -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 = {}
@@ -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())
@@ -445,7 +446,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.
#
@@ -478,7 +482,14 @@ class DisplayBuffer extends Model
#
# Returns {TokenizedLine}
tokenizedLineForScreenRow: (screenRow) ->
@screenLines[screenRow]
if @largeFileMode
if line = @tokenizedBuffer.tokenizedLineForRow(screenRow)
if line.text.length > @maxLineLength
@maxLineLength = line.text.length
@longestScreenRow = screenRow
line
else
@screenLines[screenRow]
# Gets the screen lines for the given screen row range.
#
@@ -487,13 +498,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 +523,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.
#
@@ -516,10 +536,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)?
@@ -611,10 +632,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 +649,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 +754,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.
#
@@ -759,7 +792,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 +825,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 +889,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 +903,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 +1170,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]

View File

@@ -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,14 @@ class PackageManager
@uninstallDirectory(dirToRemove)
return
# 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')
return
uninstallDirectory: (directory) ->
symlinkPromise = new Promise (resolve) ->
fs.isSymbolicLink directory, (isSymLink) -> resolve(isSymLink)

View File

@@ -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...) ->

View File

@@ -1136,8 +1136,6 @@ class TextEditorPresenter
@shouldUpdateLineNumbersState = true
else if decoration.isType('gutter')
@shouldUpdateCustomGutterDecorationState = true
if decoration.isType('highlight')
@updateHighlightState(decoration)
if decoration.isType('overlay')
@shouldUpdateOverlaysState = true
@@ -1147,14 +1145,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
@@ -1413,7 +1411,6 @@ class TextEditorPresenter
@shouldUpdateHiddenInputState = true
@pauseCursorBlinking()
@updateCursorState(cursor)
@emitDidUpdateState()
startBlinkingCursors: ->

View File

@@ -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

View File

@@ -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
@@ -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)
@@ -66,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) ->
@@ -74,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()
@@ -105,21 +106,24 @@ 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}")
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
@@ -209,6 +213,8 @@ class TokenizedBuffer extends Model
return
invalidateRow: (row) ->
return if @largeFileMode
@invalidRows.push(row)
@invalidRows.sort (a, b) -> a - b
@tokenizeInBackground()
@@ -230,7 +236,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)
@@ -248,16 +257,18 @@ 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
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
@@ -273,7 +284,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.
@@ -313,7 +327,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 +355,12 @@ class TokenizedBuffer extends Model
null
tokenizedLineForRow: (bufferRow) ->
@tokenizedLines[bufferRow]
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
@@ -405,7 +424,7 @@ class TokenizedBuffer extends Model
iterator = @tokenizedLines[row].getTokenIterator()
while iterator.next()
if iterator.getScreenEnd() > column
if iterator.getBufferEnd() > column
scopes = iterator.getScopes()
break
@@ -418,17 +437,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