mirror of
https://github.com/atom/atom.git
synced 2026-02-14 08:35:11 -05:00
Merge branch 'master' into as-tiled-gutter
Conflicts: src/lines-component.coffee src/text-editor-presenter.coffee
This commit is contained in:
@@ -51,12 +51,13 @@ class AutoUpdater
|
||||
@emit 'update-not-available'
|
||||
return
|
||||
|
||||
@emit 'update-available'
|
||||
|
||||
@installUpdate (error) =>
|
||||
if error?
|
||||
@emit 'update-not-available'
|
||||
return
|
||||
|
||||
@emit 'update-available'
|
||||
@emit 'update-downloaded', {}, update.releaseNotes, update.version, new Date(), 'https://atom.io', => @quitAndInstall()
|
||||
|
||||
module.exports = new AutoUpdater()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -12,16 +12,11 @@ class HighlightsComponent
|
||||
@domNode = document.createElement('div')
|
||||
@domNode.classList.add('highlights')
|
||||
|
||||
if atom.config.get('editor.useShadowDOM')
|
||||
insertionPoint = document.createElement('content')
|
||||
insertionPoint.setAttribute('select', '.underlayer')
|
||||
@domNode.appendChild(insertionPoint)
|
||||
|
||||
getDomNode: ->
|
||||
@domNode
|
||||
|
||||
updateSync: (state) ->
|
||||
newState = state.content.highlights
|
||||
newState = state.highlights
|
||||
@oldState ?= {}
|
||||
|
||||
# remove highlights
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
CursorsComponent = require './cursors-component'
|
||||
HighlightsComponent = require './highlights-component'
|
||||
TileComponent = require './tile-component'
|
||||
TiledComponent = require './tiled-component'
|
||||
|
||||
@@ -18,9 +17,6 @@ class LinesComponent extends TiledComponent
|
||||
@cursorsComponent = new CursorsComponent(@presenter)
|
||||
@domNode.appendChild(@cursorsComponent.getDomNode())
|
||||
|
||||
@highlightsComponent = new HighlightsComponent(@presenter)
|
||||
@domNode.appendChild(@highlightsComponent.getDomNode())
|
||||
|
||||
if @useShadowDOM
|
||||
insertionPoint = document.createElement('content')
|
||||
insertionPoint.setAttribute('select', '.overlayer')
|
||||
@@ -39,11 +35,6 @@ class LinesComponent extends TiledComponent
|
||||
|
||||
if @newState.backgroundColor isnt @oldState.backgroundColor
|
||||
@domNode.style.backgroundColor = @newState.backgroundColor
|
||||
@oldState.backgroundColor = @newState.backgroundColor
|
||||
|
||||
if @newState.scrollWidth isnt @oldState.scrollWidth
|
||||
@domNode.style.width = @newState.scrollWidth + 'px'
|
||||
@oldState.scrollWidth = @newState.scrollWidth
|
||||
|
||||
afterUpdateSync: (state) ->
|
||||
if @newState.placeholderText isnt @oldState.placeholderText
|
||||
@@ -54,11 +45,18 @@ class LinesComponent extends TiledComponent
|
||||
@placeholderTextDiv.textContent = @newState.placeholderText
|
||||
@domNode.appendChild(@placeholderTextDiv)
|
||||
|
||||
@removeTileNodes() unless @oldState.indentGuidesVisible is @newState.indentGuidesVisible
|
||||
@updateTileNodes()
|
||||
|
||||
if @newState.width isnt @oldState.width
|
||||
@domNode.style.width = @newState.width + 'px'
|
||||
|
||||
@cursorsComponent.updateSync(state)
|
||||
@highlightsComponent.updateSync(state)
|
||||
|
||||
@oldState.indentGuidesVisible = @newState.indentGuidesVisible
|
||||
@oldState.scrollWidth = @newState.scrollWidth
|
||||
@oldState.width = @newState.width
|
||||
@oldState.backgroundColor = @newState.backgroundColor
|
||||
|
||||
buildComponentForTile: (id) -> new TileComponent({id, @presenter})
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -128,8 +128,8 @@ class Project extends Model
|
||||
# Prefer the following, which evaluates to a {Promise} that resolves to an
|
||||
# {Array} of {Repository} objects:
|
||||
# ```
|
||||
# Promise.all(project.getDirectories().map(
|
||||
# project.repositoryForDirectory.bind(project)))
|
||||
# Promise.all(atom.project.getDirectories().map(
|
||||
# atom.project.repositoryForDirectory.bind(atom.project)))
|
||||
# ```
|
||||
getRepositories: -> @repositories
|
||||
|
||||
@@ -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...) ->
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{Emitter, CompositeDisposable} = require 'event-kit'
|
||||
{includeDeprecatedAPIs} = require 'grim'
|
||||
|
||||
class StylesElement extends HTMLElement
|
||||
subscriptions: null
|
||||
@@ -19,7 +18,7 @@ class StylesElement extends HTMLElement
|
||||
@styleElementClonesByOriginalElement = new WeakMap
|
||||
|
||||
attachedCallback: ->
|
||||
if includeDeprecatedAPIs and @context is 'atom-text-editor'
|
||||
if @context is 'atom-text-editor'
|
||||
for styleElement in @children
|
||||
@upgradeDeprecatedSelectors(styleElement)
|
||||
@initialize()
|
||||
@@ -67,7 +66,7 @@ class StylesElement extends HTMLElement
|
||||
|
||||
@insertBefore(styleElementClone, insertBefore)
|
||||
|
||||
if includeDeprecatedAPIs and @context is 'atom-text-editor'
|
||||
if @context is 'atom-text-editor'
|
||||
@upgradeDeprecatedSelectors(styleElementClone)
|
||||
|
||||
@emitter.emit 'did-add-style-element', styleElementClone
|
||||
|
||||
@@ -24,6 +24,7 @@ class TextEditorPresenter
|
||||
|
||||
@disposables = new CompositeDisposable
|
||||
@emitter = new Emitter
|
||||
@visibleHighlights = {}
|
||||
@characterWidthsByScope = {}
|
||||
@rangesByDecorationId = {}
|
||||
@lineDecorationsByScreenRow = {}
|
||||
@@ -307,6 +308,7 @@ class TextEditorPresenter
|
||||
@state.hiddenInput.width = Math.max(width, 2)
|
||||
|
||||
updateContentState: ->
|
||||
@state.content.width = Math.max(@contentWidth + @verticalScrollbarWidth, @contentFrameWidth)
|
||||
@state.content.scrollWidth = @scrollWidth
|
||||
@state.content.scrollLeft = @scrollLeft
|
||||
@state.content.indentGuidesVisible = not @model.isMini() and @showIndentGuide
|
||||
@@ -336,6 +338,7 @@ class TextEditorPresenter
|
||||
tile.left = -@scrollLeft
|
||||
tile.height = @tileSize * @lineHeight
|
||||
tile.display = "block"
|
||||
tile.highlights ?= {}
|
||||
|
||||
gutterTile = @lineNumberGutter.tiles[startRow] ?= {}
|
||||
gutterTile.top = startRow * @lineHeight - @scrollTop
|
||||
@@ -1136,8 +1139,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,15 +1148,14 @@ class TextEditorPresenter
|
||||
@observeDecoration(decoration)
|
||||
|
||||
if decoration.isType('line') or decoration.isType('gutter')
|
||||
@addToLineDecorationCaches(decoration, decoration.getMarker().getScreenRange())
|
||||
if decoration.isType('line')
|
||||
@shouldUpdateLinesState = true
|
||||
@shouldUpdateDecorations = true
|
||||
@shouldUpdateLinesState = 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
|
||||
|
||||
@@ -1166,8 +1166,8 @@ class TextEditorPresenter
|
||||
@lineDecorationsByScreenRow = {}
|
||||
@lineNumberDecorationsByScreenRow = {}
|
||||
@customGutterDecorationsByGutterNameAndScreenRow = {}
|
||||
@visibleHighlights = {}
|
||||
|
||||
visibleHighlights = {}
|
||||
return unless 0 <= @startRow <= @endRow <= Infinity
|
||||
|
||||
for markerId, decorations of @model.decorationsForScreenRowRange(@startRow, @endRow - 1)
|
||||
@@ -1176,11 +1176,11 @@ class TextEditorPresenter
|
||||
if decoration.isType('line') or decoration.isType('gutter')
|
||||
@addToLineDecorationCaches(decoration, range)
|
||||
else if decoration.isType('highlight')
|
||||
visibleHighlights[decoration.id] = @updateHighlightState(decoration)
|
||||
@updateHighlightState(decoration)
|
||||
|
||||
for id of @state.content.highlights
|
||||
unless visibleHighlights[id]
|
||||
delete @state.content.highlights[id]
|
||||
for tileId, tileState of @state.content.tiles
|
||||
for id, highlight of tileState.highlights
|
||||
delete tileState.highlights[id] unless @visibleHighlights[tileId]?[id]?
|
||||
|
||||
return
|
||||
|
||||
@@ -1231,6 +1231,22 @@ class TextEditorPresenter
|
||||
|
||||
return
|
||||
|
||||
intersectRangeWithTile: (range, tileStartRow) ->
|
||||
intersectingStartRow = Math.max(tileStartRow, range.start.row)
|
||||
intersectingEndRow = Math.min(tileStartRow + @tileSize - 1, range.end.row)
|
||||
intersectingRange = new Range(
|
||||
new Point(intersectingStartRow, 0),
|
||||
new Point(intersectingEndRow, Infinity)
|
||||
)
|
||||
|
||||
if intersectingStartRow is range.start.row
|
||||
intersectingRange.start.column = range.start.column
|
||||
|
||||
if intersectingEndRow is range.end.row
|
||||
intersectingRange.end.column = range.end.column
|
||||
|
||||
intersectingRange
|
||||
|
||||
updateHighlightState: (decoration) ->
|
||||
return unless @startRow? and @endRow? and @lineHeight? and @hasPixelPositionRequirements()
|
||||
|
||||
@@ -1239,7 +1255,12 @@ class TextEditorPresenter
|
||||
range = marker.getScreenRange()
|
||||
|
||||
if decoration.isDestroyed() or not marker.isValid() or range.isEmpty() or not range.intersectsRowRange(@startRow, @endRow - 1)
|
||||
delete @state.content.highlights[decoration.id]
|
||||
tileStartRow = @tileForRow(range.start.row)
|
||||
tileEndRow = @tileForRow(range.end.row)
|
||||
|
||||
for tile in [tileStartRow..tileEndRow] by @tileSize
|
||||
delete @state.content.tiles[tile]?.highlights[decoration.id]
|
||||
|
||||
@emitDidUpdateState()
|
||||
return
|
||||
|
||||
@@ -1251,44 +1272,72 @@ class TextEditorPresenter
|
||||
range.end.column = 0
|
||||
|
||||
if range.isEmpty()
|
||||
delete @state.content.highlights[decoration.id]
|
||||
tileState = @state.content.tiles[@tileForRow(range.start.row)]
|
||||
delete tileState.highlights[decoration.id]
|
||||
@emitDidUpdateState()
|
||||
return
|
||||
|
||||
highlightState = @state.content.highlights[decoration.id] ?= {
|
||||
flashCount: 0
|
||||
flashDuration: null
|
||||
flashClass: null
|
||||
}
|
||||
flash = decoration.consumeNextFlash()
|
||||
|
||||
if flash = decoration.consumeNextFlash()
|
||||
highlightState.flashCount++
|
||||
highlightState.flashClass = flash.class
|
||||
highlightState.flashDuration = flash.duration
|
||||
startTile = @tileForRow(range.start.row)
|
||||
endTile = @tileForRow(range.end.row)
|
||||
|
||||
for tileStartRow in [startTile..endTile] by @tileSize
|
||||
rangeWithinTile = @intersectRangeWithTile(range, tileStartRow)
|
||||
|
||||
continue if rangeWithinTile.isEmpty()
|
||||
|
||||
tileState = @state.content.tiles[tileStartRow] ?= {highlights: {}}
|
||||
highlightState = tileState.highlights[decoration.id] ?= {
|
||||
flashCount: 0
|
||||
flashDuration: null
|
||||
flashClass: null
|
||||
}
|
||||
|
||||
if flash?
|
||||
highlightState.flashCount++
|
||||
highlightState.flashClass = flash.class
|
||||
highlightState.flashDuration = flash.duration
|
||||
|
||||
highlightState.class = properties.class
|
||||
highlightState.deprecatedRegionClass = properties.deprecatedRegionClass
|
||||
highlightState.regions = @buildHighlightRegions(rangeWithinTile)
|
||||
|
||||
for region in highlightState.regions
|
||||
@repositionRegionWithinTile(region, tileStartRow)
|
||||
|
||||
@visibleHighlights[tileStartRow] ?= {}
|
||||
@visibleHighlights[tileStartRow][decoration.id] = true
|
||||
|
||||
highlightState.class = properties.class
|
||||
highlightState.deprecatedRegionClass = properties.deprecatedRegionClass
|
||||
highlightState.regions = @buildHighlightRegions(range)
|
||||
@emitDidUpdateState()
|
||||
|
||||
true
|
||||
|
||||
repositionRegionWithinTile: (region, tileStartRow) ->
|
||||
region.top += @scrollTop - tileStartRow * @lineHeight
|
||||
region.left += @scrollLeft
|
||||
|
||||
buildHighlightRegions: (screenRange) ->
|
||||
lineHeightInPixels = @lineHeight
|
||||
startPixelPosition = @pixelPositionForScreenPosition(screenRange.start, true)
|
||||
endPixelPosition = @pixelPositionForScreenPosition(screenRange.end, true)
|
||||
startPixelPosition = @pixelPositionForScreenPosition(screenRange.start, false)
|
||||
endPixelPosition = @pixelPositionForScreenPosition(screenRange.end, false)
|
||||
spannedRows = screenRange.end.row - screenRange.start.row + 1
|
||||
|
||||
regions = []
|
||||
|
||||
if spannedRows is 1
|
||||
[
|
||||
region =
|
||||
top: startPixelPosition.top
|
||||
height: lineHeightInPixels
|
||||
left: startPixelPosition.left
|
||||
width: endPixelPosition.left - startPixelPosition.left
|
||||
]
|
||||
else
|
||||
regions = []
|
||||
|
||||
if screenRange.end.column is Infinity
|
||||
region.right = 0
|
||||
else
|
||||
region.width = endPixelPosition.left - startPixelPosition.left
|
||||
|
||||
regions.push(region)
|
||||
else
|
||||
# First row, extending from selection start to the right side of screen
|
||||
regions.push(
|
||||
top: startPixelPosition.top
|
||||
@@ -1308,14 +1357,19 @@ class TextEditorPresenter
|
||||
|
||||
# Last row, extending from left side of screen to selection end
|
||||
if screenRange.end.column > 0
|
||||
regions.push(
|
||||
region =
|
||||
top: endPixelPosition.top
|
||||
height: lineHeightInPixels
|
||||
left: 0
|
||||
width: endPixelPosition.left
|
||||
)
|
||||
|
||||
regions
|
||||
if screenRange.end.column is Infinity
|
||||
region.right = 0
|
||||
else
|
||||
region.width = endPixelPosition.left
|
||||
|
||||
regions.push(region)
|
||||
|
||||
regions
|
||||
|
||||
setOverlayDimensions: (decorationId, itemWidth, itemHeight, contentMargin) ->
|
||||
@overlayDimensions[decorationId] ?= {}
|
||||
@@ -1360,7 +1414,6 @@ class TextEditorPresenter
|
||||
@shouldUpdateHiddenInputState = true
|
||||
@pauseCursorBlinking()
|
||||
@updateCursorState(cursor)
|
||||
|
||||
@emitDidUpdateState()
|
||||
|
||||
startBlinkingCursors: ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -325,7 +325,7 @@ class TextEditor extends Model
|
||||
onWillInsertText: (callback) ->
|
||||
@emitter.on 'will-insert-text', callback
|
||||
|
||||
# Extended: Calls your `callback` adter text has been inserted.
|
||||
# Extended: Calls your `callback` after text has been inserted.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` event {Object}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
HighlightsComponent = require './highlights-component'
|
||||
TokenIterator = require './token-iterator'
|
||||
AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT}
|
||||
WrapperDiv = document.createElement('div')
|
||||
@@ -13,8 +14,6 @@ cloneObject = (object) ->
|
||||
|
||||
module.exports =
|
||||
class TileComponent
|
||||
placeholderTextDiv: null
|
||||
|
||||
constructor: ({@presenter, @id}) ->
|
||||
@tokenIterator = new TokenIterator
|
||||
@measuredLines = new Set
|
||||
@@ -26,6 +25,9 @@ class TileComponent
|
||||
@domNode.style.position = "absolute"
|
||||
@domNode.style.display = "block"
|
||||
|
||||
@highlightsComponent = new HighlightsComponent(@presenter)
|
||||
@domNode.appendChild(@highlightsComponent.getDomNode())
|
||||
|
||||
getDomNode: ->
|
||||
@domNode
|
||||
|
||||
@@ -38,6 +40,10 @@ class TileComponent
|
||||
@newTileState = @newState.tiles[@id]
|
||||
@oldTileState = @oldState.tiles[@id]
|
||||
|
||||
if @newState.backgroundColor isnt @oldState.backgroundColor
|
||||
@domNode.style.backgroundColor = @newState.backgroundColor
|
||||
@oldState.backgroundColor = @newState.backgroundColor
|
||||
|
||||
if @newTileState.display isnt @oldTileState.display
|
||||
@domNode.style.display = @newTileState.display
|
||||
@oldTileState.display = @newTileState.display
|
||||
@@ -46,6 +52,9 @@ class TileComponent
|
||||
@domNode.style.height = @newTileState.height + 'px'
|
||||
@oldTileState.height = @newTileState.height
|
||||
|
||||
if @newState.width isnt @oldState.width
|
||||
@domNode.style.width = @newState.width + 'px'
|
||||
|
||||
if @newTileState.top isnt @oldTileState.top or @newTileState.left isnt @oldTileState.left
|
||||
@domNode.style['-webkit-transform'] = "translate3d(#{@newTileState.left}px, #{@newTileState.top}px, 0px)"
|
||||
@oldTileState.top = @newTileState.top
|
||||
@@ -54,12 +63,9 @@ class TileComponent
|
||||
@removeLineNodes() unless @oldState.indentGuidesVisible is @newState.indentGuidesVisible
|
||||
@updateLineNodes()
|
||||
|
||||
if @newState.scrollWidth isnt @oldState.scrollWidth
|
||||
@domNode.style.width = @newState.scrollWidth + 'px'
|
||||
@oldState.scrollWidth = @newState.scrollWidth
|
||||
@highlightsComponent.updateSync(@newTileState)
|
||||
|
||||
@oldState.indentGuidesVisible = @newState.indentGuidesVisible
|
||||
@oldState.scrollWidth = @newState.scrollWidth
|
||||
|
||||
removeLineNodes: ->
|
||||
@removeLineNode(id) for id of @oldTileState.lines
|
||||
@@ -104,7 +110,7 @@ class TileComponent
|
||||
return
|
||||
|
||||
buildLineHTML: (id) ->
|
||||
{scrollWidth} = @newState
|
||||
{width} = @newState
|
||||
{screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel, decorationClasses} = @newTileState.lines[id]
|
||||
|
||||
classes = ''
|
||||
@@ -113,7 +119,7 @@ class TileComponent
|
||||
classes += decorationClass + ' '
|
||||
classes += 'line'
|
||||
|
||||
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px; width: #{scrollWidth}px;\" data-screen-row=\"#{screenRow}\">"
|
||||
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px; width: #{width}px;\" data-screen-row=\"#{screenRow}\">"
|
||||
|
||||
if text is ""
|
||||
lineHTML += @buildEmptyLineInnerHTML(id)
|
||||
@@ -273,8 +279,8 @@ class TileComponent
|
||||
|
||||
lineNode = @lineNodesByLineId[id]
|
||||
|
||||
if @newState.scrollWidth isnt @oldState.scrollWidth
|
||||
lineNode.style.width = @newState.scrollWidth + 'px'
|
||||
if @newState.width isnt @oldState.width
|
||||
lineNode.style.width = @newState.width + 'px'
|
||||
|
||||
newDecorationClasses = newLineState.decorationClasses
|
||||
oldDecorationClasses = oldLineState.decorationClasses
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -102,10 +102,11 @@ class TokenizedLine
|
||||
substringEnd += 1
|
||||
else
|
||||
if (screenColumn + 1) % @tabLength is 0
|
||||
@specialTokens[tokenIndex] = SoftTab
|
||||
suffix = @tags[tokenIndex] - @tabLength
|
||||
@tags.splice(tokenIndex, 1, @tabLength)
|
||||
@tags.splice(tokenIndex + 1, 0, suffix) if suffix > 0
|
||||
if suffix >= 0
|
||||
@specialTokens[tokenIndex] = SoftTab
|
||||
@tags.splice(tokenIndex, 1, @tabLength)
|
||||
@tags.splice(tokenIndex + 1, 0, suffix) if suffix > 0
|
||||
|
||||
if @invisibles?.space
|
||||
if substringEnd > substringStart
|
||||
|
||||
@@ -22,8 +22,8 @@ Task = require './task'
|
||||
# An instance of this class is available via the `atom.workspace` global.
|
||||
#
|
||||
# Interact with this object to open files, be notified of current and future
|
||||
# editors, and manipulate panes. To add panels, you'll need to use the
|
||||
# {WorkspaceView} class for now until we establish APIs at the model layer.
|
||||
# editors, and manipulate panes. To add panels, use {Workspace::addTopPanel}
|
||||
# and friends.
|
||||
#
|
||||
# * `editor` {TextEditor} the new editor
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user