mirror of
https://github.com/atom/atom.git
synced 2026-02-06 04:34:55 -05:00
Merge remote-tracking branch 'origin/master' into cj-add-react-editor-shims
Conflicts: src/gutter-component.coffee
This commit is contained in:
@@ -111,6 +111,34 @@ class DisplayBufferMarker
|
||||
setTailBufferPosition: (bufferPosition) ->
|
||||
@bufferMarker.setTailPosition(bufferPosition)
|
||||
|
||||
# Retrieves the screen position of the marker's start. This will always be
|
||||
# less than or equal to the result of {DisplayBufferMarker::getEndScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getStartScreenPosition: ->
|
||||
@displayBuffer.screenPositionForBufferPosition(@getStartBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Retrieves the buffer position of the marker's start. This will always be
|
||||
# less than or equal to the result of {DisplayBufferMarker::getEndBufferPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getStartBufferPosition: ->
|
||||
@bufferMarker.getStartPosition()
|
||||
|
||||
# Retrieves the screen position of the marker's end. This will always be
|
||||
# greater than or equal to the result of {DisplayBufferMarker::getStartScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getEndScreenPosition: ->
|
||||
@displayBuffer.screenPositionForBufferPosition(@getEndBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Retrieves the buffer position of the marker's end. This will always be
|
||||
# greater than or equal to the result of {DisplayBufferMarker::getStartBufferPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getEndBufferPosition: ->
|
||||
@bufferMarker.getEndPosition()
|
||||
|
||||
# Sets the marker's tail to the same position as the marker's head.
|
||||
#
|
||||
# This only works if there isn't already a tail position.
|
||||
|
||||
@@ -43,6 +43,8 @@ class DisplayBuffer extends Model
|
||||
@charWidthsByScope = {}
|
||||
@markers = {}
|
||||
@foldsByMarkerId = {}
|
||||
@decorations = {}
|
||||
@decorationMarkerSubscriptions = {}
|
||||
@updateAllScreenLines()
|
||||
@createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
|
||||
@subscribe @tokenizedBuffer, 'grammar-changed', (grammar) => @emit 'grammar-changed', grammar
|
||||
@@ -716,6 +718,97 @@ class DisplayBuffer extends Model
|
||||
rangeForAllLines: ->
|
||||
new Range([0, 0], @clipScreenPosition([Infinity, Infinity]))
|
||||
|
||||
decorationsForBufferRow: (bufferRow, decorationType) ->
|
||||
decorations = @decorations[bufferRow] ? []
|
||||
decorations = (dec for dec in decorations when not dec.type? or dec.type is decorationType) if decorationType?
|
||||
decorations
|
||||
|
||||
decorationsForBufferRowRange: (startBufferRow, endBufferRow, decorationType) ->
|
||||
decorations = {}
|
||||
for bufferRow in [startBufferRow..endBufferRow]
|
||||
decorations[bufferRow] = @decorationsForBufferRow(bufferRow, decorationType)
|
||||
decorations
|
||||
|
||||
addDecorationToBufferRow: (bufferRow, decoration) ->
|
||||
@decorations[bufferRow] ?= []
|
||||
for current in @decorations[bufferRow]
|
||||
return if _.isEqual(current, decoration)
|
||||
@decorations[bufferRow].push(decoration)
|
||||
@emit 'decoration-changed', {bufferRow, decoration, action: 'add'}
|
||||
|
||||
removeDecorationFromBufferRow: (bufferRow, decorationPattern) ->
|
||||
return unless decorations = @decorations[bufferRow]
|
||||
|
||||
removed = []
|
||||
i = decorations.length - 1
|
||||
while i >= 0
|
||||
if @decorationMatchesPattern(decorations[i], decorationPattern)
|
||||
removed.push decorations[i]
|
||||
decorations.splice(i, 1)
|
||||
i--
|
||||
|
||||
delete @decorations[bufferRow] unless @decorations[bufferRow]?
|
||||
|
||||
for decoration in removed
|
||||
@emit 'decoration-changed', {bufferRow, decoration, action: 'remove'}
|
||||
|
||||
removed
|
||||
|
||||
addDecorationToBufferRowRange: (startBufferRow, endBufferRow, decoration) ->
|
||||
for bufferRow in [startBufferRow..endBufferRow]
|
||||
@addDecorationToBufferRow(bufferRow, decoration)
|
||||
return
|
||||
|
||||
removeDecorationFromBufferRowRange: (startBufferRow, endBufferRow, decoration) ->
|
||||
for bufferRow in [startBufferRow..endBufferRow]
|
||||
@removeDecorationFromBufferRow(bufferRow, decoration)
|
||||
return
|
||||
|
||||
decorationMatchesPattern: (decoration, decorationPattern) ->
|
||||
return false unless decoration? and decorationPattern?
|
||||
for key, value of decorationPattern
|
||||
return false if decoration[key] != value
|
||||
true
|
||||
|
||||
addDecorationForMarker: (marker, decoration) ->
|
||||
startRow = marker.getStartBufferPosition().row
|
||||
endRow = marker.getEndBufferPosition().row
|
||||
@addDecorationToBufferRowRange(startRow, endRow, decoration)
|
||||
|
||||
changedSubscription = @subscribe marker, 'changed', (e) =>
|
||||
oldStartRow = e.oldHeadBufferPosition.row
|
||||
oldEndRow = e.oldTailBufferPosition.row
|
||||
newStartRow = e.newHeadBufferPosition.row
|
||||
newEndRow = e.newTailBufferPosition.row
|
||||
|
||||
# swap so head is always <= than tail
|
||||
[oldEndRow, oldStartRow] = [oldStartRow, oldEndRow] if oldStartRow > oldEndRow
|
||||
[newEndRow, newStartRow] = [newStartRow, newEndRow] if newStartRow > newEndRow
|
||||
|
||||
@removeDecorationFromBufferRowRange(oldStartRow, oldEndRow, decoration)
|
||||
@addDecorationToBufferRowRange(newStartRow, newEndRow, decoration) if e.isValid
|
||||
|
||||
destroyedSubscription = @subscribe marker, 'destroyed', (e) =>
|
||||
@removeDecorationForMarker(marker, decoration)
|
||||
|
||||
@decorationMarkerSubscriptions[marker.id] ?= []
|
||||
@decorationMarkerSubscriptions[marker.id].push {decoration, changedSubscription, destroyedSubscription}
|
||||
|
||||
removeDecorationForMarker: (marker, decorationPattern) ->
|
||||
return unless @decorationMarkerSubscriptions[marker.id]?
|
||||
|
||||
startRow = marker.getStartBufferPosition().row
|
||||
endRow = marker.getEndBufferPosition().row
|
||||
@removeDecorationFromBufferRowRange(startRow, endRow, decorationPattern)
|
||||
|
||||
for subscription in _.clone(@decorationMarkerSubscriptions[marker.id])
|
||||
if @decorationMatchesPattern(subscription.decoration, decorationPattern)
|
||||
subscription.changedSubscription.off()
|
||||
subscription.destroyedSubscription.off()
|
||||
@decorationMarkerSubscriptions[marker.id] = _.without(@decorationMarkerSubscriptions[marker.id], subscription)
|
||||
|
||||
return
|
||||
|
||||
# Retrieves a {DisplayBufferMarker} based on its id.
|
||||
#
|
||||
# id - A {Number} representing a marker id
|
||||
@@ -959,6 +1052,8 @@ class DisplayBuffer extends Model
|
||||
@emit 'marker-created', @getMarker(marker.id)
|
||||
|
||||
createFoldForMarker: (marker) ->
|
||||
bufferMarker = new DisplayBufferMarker({bufferMarker: marker, displayBuffer: this})
|
||||
@addDecorationForMarker(bufferMarker, type: 'gutter', class: 'folded')
|
||||
new Fold(this, marker)
|
||||
|
||||
foldForMarker: (marker) ->
|
||||
|
||||
@@ -43,12 +43,14 @@ EditorComponent = React.createClass
|
||||
{editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
|
||||
maxLineNumberDigits = editor.getScreenLineCount().toString().length
|
||||
invisibles = if showInvisibles then @state.invisibles else {}
|
||||
hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty()
|
||||
|
||||
if @isMounted()
|
||||
renderedRowRange = @getRenderedRowRange()
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
cursorScreenRanges = @getCursorScreenRanges(renderedRowRange)
|
||||
selectionScreenRanges = @getSelectionScreenRanges(renderedRowRange)
|
||||
decorations = @getGutterDecorations(renderedRowRange)
|
||||
scrollHeight = editor.getScrollHeight()
|
||||
scrollWidth = editor.getScrollWidth()
|
||||
scrollTop = editor.getScrollTop()
|
||||
@@ -67,11 +69,13 @@ EditorComponent = React.createClass
|
||||
|
||||
className = 'editor-contents editor-colors'
|
||||
className += ' is-focused' if focused
|
||||
className += ' has-selection' if hasSelection
|
||||
|
||||
div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1,
|
||||
GutterComponent {
|
||||
ref: 'gutter', editor, renderedRowRange, maxLineNumberDigits, scrollTop,
|
||||
scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow
|
||||
scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow,
|
||||
decorations
|
||||
}
|
||||
|
||||
div ref: 'scrollView', className: 'scroll-view', onMouseDown: @onMouseDown,
|
||||
@@ -227,6 +231,18 @@ EditorComponent = React.createClass
|
||||
|
||||
selectionScreenRanges
|
||||
|
||||
getGutterDecorations: (renderedRowRange) ->
|
||||
{editor} = @props
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
|
||||
bufferRows = editor.bufferRowsForScreenRows(renderedStartRow, renderedEndRow - 1)
|
||||
|
||||
decorations = {}
|
||||
for bufferRow in bufferRows
|
||||
decorations[bufferRow] = editor.decorationsForBufferRow(bufferRow, 'gutter')
|
||||
decorations[bufferRow].push {class: 'foldable'} if editor.isFoldableAtBufferRow(bufferRow)
|
||||
decorations
|
||||
|
||||
observeEditor: ->
|
||||
{editor} = @props
|
||||
@subscribe editor, 'batched-updates-started', @onBatchedUpdatesStarted
|
||||
@@ -235,6 +251,7 @@ EditorComponent = React.createClass
|
||||
@subscribe editor, 'cursors-moved', @onCursorsMoved
|
||||
@subscribe editor, 'selection-removed selection-screen-range-changed', @onSelectionChanged
|
||||
@subscribe editor, 'selection-added', @onSelectionAdded
|
||||
@subscribe editor, 'decoration-changed', @onDecorationChanged
|
||||
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
|
||||
@subscribe editor.$scrollLeft.changes, @requestUpdate
|
||||
@subscribe editor.$height.changes, @requestUpdate
|
||||
@@ -503,6 +520,8 @@ EditorComponent = React.createClass
|
||||
@onStoppedScrollingAfterDelay()
|
||||
|
||||
onStoppedScrolling: ->
|
||||
return unless @isMounted()
|
||||
|
||||
@scrollingVertically = false
|
||||
@mouseWheelScreenRow = null
|
||||
@requestUpdate()
|
||||
@@ -513,6 +532,11 @@ EditorComponent = React.createClass
|
||||
@cursorsMoved = true
|
||||
@requestUpdate()
|
||||
|
||||
onDecorationChanged: ->
|
||||
@decorationChangedImmediate ?= setImmediate =>
|
||||
@requestUpdate()
|
||||
@decorationChangedImmediate = null
|
||||
|
||||
selectToMousePositionUntilMouseUp: (event) ->
|
||||
{editor} = @props
|
||||
dragging = false
|
||||
|
||||
@@ -214,6 +214,7 @@ class Editor extends Model
|
||||
@subscribe @displayBuffer, 'grammar-changed', => @handleGrammarChange()
|
||||
@subscribe @displayBuffer, 'tokenized', => @handleTokenization()
|
||||
@subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args...
|
||||
@subscribe @displayBuffer, "decoration-changed", (e) => @emit 'decoration-changed', e
|
||||
|
||||
getViewClass: ->
|
||||
if atom.config.get('core.useReactEditor')
|
||||
@@ -1057,6 +1058,106 @@ class Editor extends Model
|
||||
selection.insertText(fn(text))
|
||||
selection.setBufferRange(range)
|
||||
|
||||
# Public: Get all the decorations for a buffer row.
|
||||
#
|
||||
# bufferRow - the {int} buffer row
|
||||
# decorationType - the {String} decoration type to filter by eg. 'gutter'
|
||||
#
|
||||
# Returns an {Array} of decorations in the form `[{type: 'gutter', class: 'someclass'}, ...]`
|
||||
# Returns an empty array when no decorations are found
|
||||
decorationsForBufferRow: (bufferRow, decorationType) ->
|
||||
@displayBuffer.decorationsForBufferRow(bufferRow, decorationType)
|
||||
|
||||
# Public: Get all the decorations for a range of buffer rows (inclusive)
|
||||
#
|
||||
# startBufferRow - the {int} start of the buffer row range
|
||||
# endBufferRow - the {int} end of the buffer row range (inclusive)
|
||||
# decorationType - the {String} decoration type to filter by eg. 'gutter'
|
||||
#
|
||||
# Returns an {Object} of decorations in the form `{23: [{type: 'gutter', class: 'someclass'}, ...], 24: [...]}`
|
||||
# Returns an {Object} with keyed with all buffer rows in the range containing empty {Array}s when no decorations are found
|
||||
decorationsForBufferRowRange: (startBufferRow, endBufferRow, decorationType) ->
|
||||
@displayBuffer.decorationsForBufferRowRange(startBufferRow, endBufferRow, decorationType)
|
||||
|
||||
# Public: Adds a decoration to a buffer row. For example, use to mark a gutter
|
||||
# line number with a class by using the form `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# bufferRow - the {int} buffer row
|
||||
# decoration - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns nothing
|
||||
addDecorationToBufferRow: (bufferRow, decoration) ->
|
||||
@displayBuffer.addDecorationToBufferRow(bufferRow, decoration)
|
||||
|
||||
# Public: Removes a decoration from a buffer row.
|
||||
#
|
||||
# ```coffee
|
||||
# editor.removeDecorationFromBufferRow(2, {type: 'gutter', class: 'linter-error'})
|
||||
# ```
|
||||
#
|
||||
# All decorations matching a pattern will be removed. For example, you might
|
||||
# have decorations with a namespace like this attached to a row:
|
||||
#
|
||||
# ```coffee
|
||||
# [
|
||||
# {type: 'gutter', namespace: 'myns', class: 'something'},
|
||||
# {type: 'gutter', namespace: 'myns', class: 'something-else'}
|
||||
# ]
|
||||
# ```
|
||||
#
|
||||
# You can remove both with:
|
||||
#
|
||||
# ```coffee
|
||||
# editor.removeDecorationFromBufferRow(2, {namespace: 'myns'})
|
||||
# ```
|
||||
#
|
||||
# bufferRow - the {int} buffer row
|
||||
# decorationPattern - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns an {Array} of the removed decorations
|
||||
removeDecorationFromBufferRow: (bufferRow, decorationPattern) ->
|
||||
@displayBuffer.removeDecorationFromBufferRow(bufferRow, decorationPattern)
|
||||
|
||||
# Public: Adds a decoration to line numbers in a buffer row range
|
||||
#
|
||||
# startBufferRow - the {int} start of the buffer row range
|
||||
# endBufferRow - the {int} end of the buffer row range (inclusive)
|
||||
# decoration - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns nothing
|
||||
addDecorationToBufferRowRange: (startBufferRow, endBufferRow, decoration) ->
|
||||
@displayBuffer.addDecorationToBufferRowRange(startBufferRow, endBufferRow, decoration)
|
||||
|
||||
# Public: Removes a decoration from line numbers in a buffer row range
|
||||
#
|
||||
# startBufferRow - the {int} start of the buffer row range
|
||||
# endBufferRow - the {int} end of the buffer row range (inclusive)
|
||||
# decoration - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns nothing
|
||||
removeDecorationFromBufferRowRange: (startBufferRow, endBufferRow, decoration) ->
|
||||
@displayBuffer.removeDecorationFromBufferRowRange(startBufferRow, endBufferRow, decoration)
|
||||
|
||||
# Public: Adds a decoration that tracks a {Marker}. When the marker moves,
|
||||
# is invalidated, or is destroyed, the decoration will be updated to reflect the marker's state.
|
||||
#
|
||||
# marker - the {Marker} you want this decoration to follow
|
||||
# decoration - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns nothing
|
||||
addDecorationForMarker: (marker, decoration) ->
|
||||
@displayBuffer.addDecorationForMarker(marker, decoration)
|
||||
|
||||
# Public: Removes all decorations associated with a {Marker} that match a
|
||||
# `decorationPattern` and stop tracking the {Marker}.
|
||||
#
|
||||
# marker - the {Marker} to detach from
|
||||
# decorationPattern - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns nothing
|
||||
removeDecorationForMarker: (marker, decorationPattern) ->
|
||||
@displayBuffer.removeDecorationForMarker(marker, decorationPattern)
|
||||
|
||||
# Public: Get the {DisplayBufferMarker} for the given marker id.
|
||||
getMarker: (id) ->
|
||||
@displayBuffer.getMarker(id)
|
||||
@@ -1162,6 +1263,7 @@ class Editor extends Model
|
||||
addCursor: (marker) ->
|
||||
cursor = new Cursor(editor: this, marker: marker)
|
||||
@cursors.push(cursor)
|
||||
@addDecorationForMarker(marker, {class: 'cursor-line'})
|
||||
@emit 'cursor-added', cursor
|
||||
cursor
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
_ = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
|
||||
@@ -15,7 +16,7 @@ GutterComponent = React.createClass
|
||||
render: ->
|
||||
{scrollHeight, scrollTop} = @props
|
||||
|
||||
div className: 'gutter',
|
||||
div className: 'gutter', onClick: @onClick,
|
||||
div className: 'line-numbers', ref: 'lineNumbers', style:
|
||||
height: scrollHeight
|
||||
WebkitTransform: "translate3d(0px, #{-scrollTop}px, 0px)"
|
||||
@@ -24,6 +25,7 @@ GutterComponent = React.createClass
|
||||
@lineNumberNodesById = {}
|
||||
@lineNumberIdsByScreenRow = {}
|
||||
@screenRowsByLineNumberId = {}
|
||||
@previousDecorations = {}
|
||||
|
||||
componentDidMount: ->
|
||||
@appendDummyLineNumber()
|
||||
@@ -36,10 +38,12 @@ GutterComponent = React.createClass
|
||||
'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow'
|
||||
)
|
||||
|
||||
{renderedRowRange, pendingChanges} = newProps
|
||||
{renderedRowRange, pendingChanges, decorations} = newProps
|
||||
for change in pendingChanges when Math.abs(change.screenDelta) > 0 or Math.abs(change.bufferDelta) > 0
|
||||
return true unless change.end <= renderedRowRange.start or renderedRowRange.end <= change.start
|
||||
|
||||
return true unless _.isEqual(@previousDecorations, decorations)
|
||||
|
||||
false
|
||||
|
||||
componentDidUpdate: (oldProps) ->
|
||||
@@ -70,7 +74,7 @@ GutterComponent = React.createClass
|
||||
@removeLineNumberNodes(lineNumberIdsToPreserve)
|
||||
|
||||
appendOrUpdateVisibleLineNumberNodes: ->
|
||||
{editor, renderedRowRange, scrollTop, maxLineNumberDigits} = @props
|
||||
{editor, renderedRowRange, scrollTop, maxLineNumberDigits, decorations} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
|
||||
newLineNumberIds = null
|
||||
@@ -91,12 +95,12 @@ GutterComponent = React.createClass
|
||||
visibleLineNumberIds.add(id)
|
||||
|
||||
if @hasLineNumberNode(id)
|
||||
@updateLineNumberNode(id, screenRow)
|
||||
@updateLineNumberNode(id, bufferRow, screenRow, wrapCount > 0, decorations[bufferRow])
|
||||
else
|
||||
newLineNumberIds ?= []
|
||||
newLineNumbersHTML ?= ""
|
||||
newLineNumberIds.push(id)
|
||||
newLineNumbersHTML += @buildLineNumberHTML(bufferRow, wrapCount > 0, maxLineNumberDigits, screenRow)
|
||||
newLineNumbersHTML += @buildLineNumberHTML(bufferRow, wrapCount > 0, maxLineNumberDigits, screenRow, decorations[bufferRow])
|
||||
@screenRowsByLineNumberId[id] = screenRow
|
||||
@lineNumberIdsByScreenRow[screenRow] = id
|
||||
|
||||
@@ -110,6 +114,7 @@ GutterComponent = React.createClass
|
||||
@lineNumberNodesById[lineNumberId] = lineNumberNode
|
||||
node.appendChild(lineNumberNode)
|
||||
|
||||
@previousDecorations = decorations
|
||||
visibleLineNumberIds
|
||||
|
||||
removeLineNumberNodes: (lineNumberIdsToPreserve) ->
|
||||
@@ -123,7 +128,7 @@ GutterComponent = React.createClass
|
||||
delete @screenRowsByLineNumberId[lineNumberId]
|
||||
node.removeChild(lineNumberNode)
|
||||
|
||||
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow) ->
|
||||
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow, decorations) ->
|
||||
if screenRow?
|
||||
{lineHeightInPixels} = @props
|
||||
style = "position: absolute; top: #{screenRow * lineHeightInPixels}px;"
|
||||
@@ -131,7 +136,13 @@ GutterComponent = React.createClass
|
||||
style = "visibility: hidden;"
|
||||
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits)
|
||||
|
||||
"<div class=\"line-number line-number-#{bufferRow}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
|
||||
classes = ''
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
classes += decoration.class + ' ' if not softWrapped or softWrapped and decoration.softWrap
|
||||
classes += "line-number line-number-#{bufferRow}"
|
||||
|
||||
"<div class=\"#{classes}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
|
||||
|
||||
buildLineNumberInnerHTML: (bufferRow, softWrapped, maxLineNumberDigits) ->
|
||||
if softWrapped
|
||||
@@ -143,11 +154,23 @@ GutterComponent = React.createClass
|
||||
iconHTML = '<div class="icon-right"></div>'
|
||||
padding + lineNumber + iconHTML
|
||||
|
||||
updateLineNumberNode: (lineNumberId, screenRow) ->
|
||||
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped, decorations) ->
|
||||
node = @lineNumberNodesById[lineNumberId]
|
||||
previousDecorations = @previousDecorations[bufferRow]
|
||||
|
||||
if previousDecorations?
|
||||
for decoration in previousDecorations
|
||||
node.classList.remove(decoration.class) if not contains(decorations, decoration)
|
||||
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
if not contains(previousDecorations, decoration) and (not softWrapped or softWrapped and decoration.softWrap)
|
||||
node.classList.add(decoration.class)
|
||||
|
||||
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
|
||||
{lineHeightInPixels} = @props
|
||||
@lineNumberNodesById[lineNumberId].style.top = screenRow * lineHeightInPixels + 'px'
|
||||
@lineNumberNodesById[lineNumberId].dataset.screenRow = screenRow
|
||||
node.style.top = screenRow * lineHeightInPixels + 'px'
|
||||
node.dataset.screenRow = screenRow
|
||||
@screenRowsByLineNumberId[lineNumberId] = screenRow
|
||||
@lineNumberIdsByScreenRow[screenRow] = lineNumberId
|
||||
|
||||
@@ -156,3 +179,22 @@ GutterComponent = React.createClass
|
||||
|
||||
lineNumberNodeForScreenRow: (screenRow) ->
|
||||
@lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]]
|
||||
|
||||
onClick: (event) ->
|
||||
{editor} = @props
|
||||
{target} = event
|
||||
lineNumber = target.parentNode
|
||||
|
||||
if target.classList.contains('icon-right') and lineNumber.classList.contains('foldable')
|
||||
bufferRow = parseInt(lineNumber.getAttribute('data-buffer-row'))
|
||||
if lineNumber.classList.contains('folded')
|
||||
editor.unfoldBufferRow(bufferRow)
|
||||
else
|
||||
editor.foldBufferRow(bufferRow)
|
||||
|
||||
# Created because underscore uses === not _.isEqual, which we need
|
||||
contains = (array, target) ->
|
||||
return false unless array?
|
||||
for object in array
|
||||
return true if _.isEqual(object, target)
|
||||
false
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{View, $} = require 'space-pen'
|
||||
Grim = require 'Grim'
|
||||
React = require 'react-atom-fork'
|
||||
EditorComponent = require './editor-component'
|
||||
{defaults} = require 'underscore-plus'
|
||||
@@ -16,6 +17,8 @@ class ReactEditorView extends View
|
||||
|
||||
getEditor: -> @editor
|
||||
|
||||
getModel: -> @editor
|
||||
|
||||
Object.defineProperty @::, 'lineHeight', get: -> @editor.getLineHeightInPixels()
|
||||
Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
|
||||
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
|
||||
@@ -40,12 +43,14 @@ class ReactEditorView extends View
|
||||
|
||||
@gutter = $(node).find('.gutter')
|
||||
@gutter.removeClassFromAllLines = (klass) =>
|
||||
Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::removeDecorationFromBufferRow()` and related functions'
|
||||
@gutter.find('.line-number').removeClass(klass)
|
||||
|
||||
@gutter.getLineNumberElement = (bufferRow) =>
|
||||
@gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
|
||||
@gutter.addClassToLine = (bufferRow, klass) =>
|
||||
Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::addDecorationToBufferRow()` and related functions'
|
||||
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
lines.addClass(klass)
|
||||
lines.length > 0
|
||||
|
||||
Reference in New Issue
Block a user