mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-14 04:29:04 +03:00
Merge pull request #2682 from atom/bo-line-decorations
Render line decorations
This commit is contained in:
commit
633b08b9de
@ -58,7 +58,7 @@
|
||||
"temp": "0.7.0",
|
||||
"text-buffer": "^2.4.2",
|
||||
"theorist": "^1",
|
||||
"underscore-plus": "^1.4.1",
|
||||
"underscore-plus": "^1.5.0",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
@ -83,7 +83,7 @@
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.120.0",
|
||||
"fuzzy-finder": "0.55.0",
|
||||
"git-diff": "0.33.0",
|
||||
"git-diff": "0.34.0",
|
||||
"go-to-line": "0.23.0",
|
||||
"grammar-selector": "0.27.0",
|
||||
"image-view": "0.35.0",
|
||||
|
@ -245,6 +245,88 @@ describe "EditorComponent", ->
|
||||
foldedLineNode = component.lineNodeForScreenRow(4)
|
||||
expect(foldedLineNode.querySelector('.fold-marker')).toBeFalsy()
|
||||
|
||||
describe "when line decorations are attached to markers", ->
|
||||
{marker, decoration} = {}
|
||||
|
||||
lineHasClass = (screenRow, klass) ->
|
||||
component.lineNodeForScreenRow(screenRow).classList.contains(klass)
|
||||
|
||||
beforeEach ->
|
||||
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside')
|
||||
decoration = {type: 'line', class: 'someclass'}
|
||||
editor.addDecorationForMarker(marker, decoration)
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
|
||||
it "does not render off-screen lines with line number classes until they are with in the rendered row range", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureScrollView()
|
||||
|
||||
expect(component.lineNodeForScreenRow(9)).not.toBeDefined()
|
||||
|
||||
marker = editor.displayBuffer.markBufferRange([[9, 0], [9, 0]], invalidate: 'inside')
|
||||
editor.addDecorationForMarker(marker, type: 'line', class: 'fancy-class')
|
||||
editor.addDecorationForMarker(marker, type: 'gutter', class: 'nope-class')
|
||||
|
||||
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
|
||||
expect(lineHasClass(9, 'fancy-class')).toBe true
|
||||
expect(lineHasClass(9, 'nope-class')).toBe false
|
||||
|
||||
it "renders the specified decoration class on the correct lines", ->
|
||||
expect(lineHasClass(1, 'someclass')).toBe false
|
||||
expect(lineHasClass(2, 'someclass')).toBe true
|
||||
expect(lineHasClass(3, 'someclass')).toBe true
|
||||
expect(lineHasClass(4, 'someclass')).toBe false
|
||||
|
||||
it "removes line classes when a decoration's marker is invalidated", ->
|
||||
editor.getBuffer().insert([3, 2], 'n')
|
||||
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(marker.isValid()).toBe false
|
||||
expect(lineHasClass(1, 'someclass')).toBe false
|
||||
expect(lineHasClass(2, 'someclass')).toBe false
|
||||
expect(lineHasClass(3, 'someclass')).toBe false
|
||||
expect(lineHasClass(4, 'someclass')).toBe false
|
||||
|
||||
editor.getBuffer().undo()
|
||||
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(marker.isValid()).toBe true
|
||||
expect(lineHasClass(1, 'someclass')).toBe false
|
||||
expect(lineHasClass(2, 'someclass')).toBe true
|
||||
expect(lineHasClass(3, 'someclass')).toBe true
|
||||
expect(lineHasClass(4, 'someclass')).toBe false
|
||||
|
||||
it "removes the classes and unsubscribes from the marker when decoration is removed", ->
|
||||
editor.removeDecorationForMarker(marker, decoration)
|
||||
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineHasClass(1, 'someclass')).toBe false
|
||||
expect(lineHasClass(2, 'someclass')).toBe false
|
||||
expect(lineHasClass(3, 'someclass')).toBe false
|
||||
expect(lineHasClass(4, 'someclass')).toBe false
|
||||
|
||||
editor.getBuffer().insert([0, 0], '\n')
|
||||
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineHasClass(2, 'someclass')).toBe false
|
||||
expect(lineHasClass(3, 'someclass')).toBe false
|
||||
|
||||
it "removes the line number classes when the decoration's marker is destroyed", ->
|
||||
marker.destroy()
|
||||
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineHasClass(1, 'someclass')).toBe false
|
||||
expect(lineHasClass(2, 'someclass')).toBe false
|
||||
expect(lineHasClass(3, 'someclass')).toBe false
|
||||
expect(lineHasClass(4, 'someclass')).toBe false
|
||||
|
||||
describe "gutter rendering", ->
|
||||
[gutter] = []
|
||||
|
||||
@ -455,7 +537,7 @@ describe "EditorComponent", ->
|
||||
expect(lineNumberHasClass(2, 'cursor-line')).toBe true
|
||||
expect(lineNumberHasClass(3, 'cursor-line')).toBe false
|
||||
|
||||
describe "when decorations are applied to markers", ->
|
||||
describe "when gutter decorations are attached to markers", ->
|
||||
{marker, decoration} = {}
|
||||
beforeEach ->
|
||||
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside')
|
||||
|
@ -174,11 +174,11 @@ GutterComponent = React.createClass
|
||||
|
||||
if previousDecorations?
|
||||
for decoration in previousDecorations
|
||||
node.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'gutter') and not contains(decorations, decoration)
|
||||
node.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'gutter') and not _.deepContains(decorations, decoration)
|
||||
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'gutter') and not contains(previousDecorations, decoration)
|
||||
if editor.decorationMatchesType(decoration, 'gutter') and not _.deepContains(previousDecorations, decoration)
|
||||
node.classList.add(decoration.class)
|
||||
|
||||
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
|
||||
@ -211,10 +211,3 @@ GutterComponent = React.createClass
|
||||
unless width is @measuredWidth
|
||||
@measuredWidth = width
|
||||
@props.onWidthChanged?(width)
|
||||
|
||||
# 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,3 +1,4 @@
|
||||
_ = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div, span} = require 'reactionary-atom-fork'
|
||||
{debounce, isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
|
||||
@ -32,10 +33,11 @@ LinesComponent = React.createClass
|
||||
@lineNodesByLineId = {}
|
||||
@screenRowsByLineId = {}
|
||||
@lineIdsByScreenRow = {}
|
||||
@renderedDecorationsByLineId = {}
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
return true unless isEqualForProperties(newProps, @props,
|
||||
'renderedRowRange', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
|
||||
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
|
||||
'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible',
|
||||
'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount'
|
||||
)
|
||||
@ -60,7 +62,7 @@ LinesComponent = React.createClass
|
||||
@lineIdsByScreenRow = {}
|
||||
|
||||
updateLines: ->
|
||||
{editor, renderedRowRange, showIndentGuide, selectionChanged} = @props
|
||||
{editor, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
|
||||
visibleLines = editor.linesForScreenRows(startRow, endRow - 1)
|
||||
@ -81,6 +83,8 @@ LinesComponent = React.createClass
|
||||
node.removeChild(lineNode)
|
||||
|
||||
appendOrUpdateVisibleLineNodes: (visibleLines, startRow) ->
|
||||
{lineDecorations} = @props
|
||||
|
||||
newLines = null
|
||||
newLinesHTML = null
|
||||
|
||||
@ -97,6 +101,8 @@ LinesComponent = React.createClass
|
||||
@screenRowsByLineId[line.id] = screenRow
|
||||
@lineIdsByScreenRow[screenRow] = line.id
|
||||
|
||||
@renderedDecorationsByLineId[line.id] = lineDecorations[screenRow]
|
||||
|
||||
return unless newLines?
|
||||
|
||||
WrapperDiv.innerHTML = newLinesHTML
|
||||
@ -111,11 +117,18 @@ LinesComponent = React.createClass
|
||||
@lineNodesByLineId.hasOwnProperty(lineId)
|
||||
|
||||
buildLineHTML: (line, screenRow) ->
|
||||
{editor, mini, showIndentGuide, lineHeightInPixels} = @props
|
||||
{editor, mini, showIndentGuide, lineHeightInPixels, lineDecorations} = @props
|
||||
{tokens, text, lineEnding, fold, isSoftWrapped, indentLevel} = line
|
||||
|
||||
classes = ''
|
||||
if decorations = lineDecorations[screenRow]
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'line')
|
||||
classes += decoration.class + ' '
|
||||
classes += 'line'
|
||||
|
||||
top = screenRow * lineHeightInPixels
|
||||
lineHTML = "<div class=\"line\" style=\"position: absolute; top: #{top}px;\" data-screen-row=\"#{screenRow}\">"
|
||||
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px;\" data-screen-row=\"#{screenRow}\">"
|
||||
|
||||
if text is ""
|
||||
lineHTML += @buildEmptyLineInnerHTML(line)
|
||||
@ -190,9 +203,19 @@ LinesComponent = React.createClass
|
||||
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
|
||||
|
||||
updateLineNode: (line, screenRow) ->
|
||||
{editor, lineHeightInPixels, lineDecorations} = @props
|
||||
lineNode = @lineNodesByLineId[line.id]
|
||||
|
||||
if previousDecorations = @renderedDecorationsByLineId[line.id]
|
||||
for decoration in previousDecorations
|
||||
lineNode.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'line') and not _.deepContains(decorations, decoration)
|
||||
|
||||
if decorations = lineDecorations[screenRow]
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'line') and not _.deepContains(previousDecorations, decoration)
|
||||
lineNode.classList.add(decoration.class)
|
||||
|
||||
unless @screenRowsByLineId[line.id] is screenRow
|
||||
{lineHeightInPixels} = @props
|
||||
lineNode = @lineNodesByLineId[line.id]
|
||||
lineNode.style.top = screenRow * lineHeightInPixels + 'px'
|
||||
lineNode.dataset.screenRow = screenRow
|
||||
@screenRowsByLineId[line.id] = screenRow
|
||||
|
Loading…
Reference in New Issue
Block a user