mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Merge pull request #2317 from atom/cj-add-invisibles-to-react-editor
Add invisibles to react editor
This commit is contained in:
@@ -90,6 +90,51 @@ describe "EditorComponent", ->
|
||||
expect(component.lineNodeForScreenRow(3).offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels
|
||||
|
||||
describe "when showInvisibles is enabled", ->
|
||||
invisibles = null
|
||||
|
||||
beforeEach ->
|
||||
invisibles =
|
||||
eol: 'E'
|
||||
space: 'S'
|
||||
tab: 'T'
|
||||
cr: 'C'
|
||||
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
atom.config.set("editor.invisibles", invisibles)
|
||||
|
||||
it "re-renders the lines when the showInvisibles config option changes", ->
|
||||
editor.setText " a line with tabs\tand spaces "
|
||||
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
atom.config.set("editor.showInvisibles", false)
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces "
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
it "displays spaces, tabs, and newlines as visible characters", ->
|
||||
editor.setText " a line with tabs\tand spaces "
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
it "displays newlines as their own token outside of the other tokens' scopes", ->
|
||||
editor.setText "var"
|
||||
expect(component.lineNodeForScreenRow(0).innerHTML).toBe "<span class=\"source js\"><span class=\"storage modifier js\">var</span></span><span class=\"invisible-character\">#{invisibles.eol}</span>"
|
||||
|
||||
it "displays trailing carriage returns using a visible, non-empty value", ->
|
||||
editor.setText "a line that ends with a carriage return\r\n"
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "a line that ends with a carriage return#{invisibles.cr}#{invisibles.eol}"
|
||||
|
||||
describe "when soft wrapping is enabled", ->
|
||||
beforeEach ->
|
||||
editor.setText "a line that wraps "
|
||||
editor.setSoftWrap(true)
|
||||
node.style.width = 15 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
it "doesn't show end of line invisibles at the end of wrapped lines", ->
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "a line that "
|
||||
expect(component.lineNodeForScreenRow(1).textContent).toBe "wraps#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
describe "when indent guides are enabled", ->
|
||||
beforeEach ->
|
||||
component.setShowIndentGuide(true)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
React = require 'react'
|
||||
{div, span} = require 'reactionary'
|
||||
{debounce} = require 'underscore-plus'
|
||||
{debounce, defaults} = require 'underscore-plus'
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
|
||||
GutterComponent = require './gutter-component'
|
||||
@@ -31,9 +31,10 @@ EditorComponent = React.createClass
|
||||
mouseWheelScreenRow: null
|
||||
|
||||
render: ->
|
||||
{focused, fontSize, lineHeight, fontFamily, showIndentGuide} = @state
|
||||
{focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles} = @state
|
||||
{editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
|
||||
maxLineNumberDigits = editor.getScreenLineCount().toString().length
|
||||
invisibles = if showInvisibles then @state.invisibles else {}
|
||||
|
||||
if @isMounted()
|
||||
renderedRowRange = @getRenderedRowRange()
|
||||
@@ -62,7 +63,8 @@ EditorComponent = React.createClass
|
||||
lineHeight: lineHeightInPixels, renderedRowRange, @pendingChanges,
|
||||
scrollTop, scrollLeft, scrollHeight, scrollWidth, @scrollingVertically,
|
||||
@cursorsMoved, @selectionChanged, @selectionAdded, cursorBlinkPeriod,
|
||||
cursorBlinkResumeDelay, @onInputFocused, @onInputBlurred, @mouseWheelScreenRow
|
||||
cursorBlinkResumeDelay, @onInputFocused, @onInputBlurred, @mouseWheelScreenRow,
|
||||
invisibles
|
||||
}
|
||||
|
||||
ScrollbarComponent
|
||||
@@ -101,7 +103,7 @@ EditorComponent = React.createClass
|
||||
{editor, lineOverdrawMargin} = @props
|
||||
[visibleStartRow, visibleEndRow] = editor.getVisibleRowRange()
|
||||
renderedStartRow = Math.max(0, visibleStartRow - lineOverdrawMargin)
|
||||
renderedEndRow = Math.min(editor.getLineCount(), visibleEndRow + lineOverdrawMargin)
|
||||
renderedEndRow = Math.min(editor.getScreenLineCount(), visibleEndRow + lineOverdrawMargin)
|
||||
[renderedStartRow, renderedEndRow]
|
||||
|
||||
getInitialState: -> {}
|
||||
@@ -269,6 +271,8 @@ EditorComponent = React.createClass
|
||||
@subscribe atom.config.observe 'editor.fontFamily', @setFontFamily
|
||||
@subscribe atom.config.observe 'editor.fontSize', @setFontSize
|
||||
@subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide
|
||||
@subscribe atom.config.observe 'editor.invisibles', @setInvisibles
|
||||
@subscribe atom.config.observe 'editor.showInvisibles', @setShowInvisibles
|
||||
|
||||
measureScrollbars: ->
|
||||
@measuringScrollbars = false
|
||||
@@ -292,6 +296,25 @@ EditorComponent = React.createClass
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
@setState({showIndentGuide})
|
||||
|
||||
# Public: Defines which characters are invisible.
|
||||
#
|
||||
# invisibles - An {Object} defining the invisible characters:
|
||||
# :eol - The end of line invisible {String} (default: `\u00ac`).
|
||||
# :space - The space invisible {String} (default: `\u00b7`).
|
||||
# :tab - The tab invisible {String} (default: `\u00bb`).
|
||||
# :cr - The carriage return invisible {String} (default: `\u00a4`).
|
||||
setInvisibles: (invisibles={}) ->
|
||||
defaults invisibles,
|
||||
eol: '\u00ac'
|
||||
space: '\u00b7'
|
||||
tab: '\u00bb'
|
||||
cr: '\u00a4'
|
||||
|
||||
@setState({invisibles})
|
||||
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
@setState({showInvisibles})
|
||||
|
||||
onFocus: ->
|
||||
@refs.scrollView.focus()
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ EditorScrollViewComponent = React.createClass
|
||||
overflowChangedWhilePaused: false
|
||||
|
||||
render: ->
|
||||
{editor, fontSize, fontFamily, lineHeight, showIndentGuide} = @props
|
||||
{editor, fontSize, fontFamily, lineHeight, showIndentGuide, invisibles} = @props
|
||||
{renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollHeight, scrollWidth, scrollingVertically, mouseWheelScreenRow} = @props
|
||||
{selectionChanged, selectionAdded, cursorBlinkPeriod, cursorBlinkResumeDelay, cursorsMoved, onInputFocused, onInputBlurred} = @props
|
||||
|
||||
@@ -37,7 +37,7 @@ EditorScrollViewComponent = React.createClass
|
||||
LinesComponent {
|
||||
ref: 'lines', editor, fontSize, fontFamily, lineHeight, showIndentGuide,
|
||||
renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollingVertically,
|
||||
selectionChanged, scrollHeight, scrollWidth, mouseWheelScreenRow
|
||||
selectionChanged, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles
|
||||
}
|
||||
|
||||
componentDidMount: ->
|
||||
|
||||
@@ -35,7 +35,7 @@ LinesComponent = React.createClass
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
return true if newProps.selectionChanged
|
||||
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically')
|
||||
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles')
|
||||
|
||||
{renderedRowRange, pendingChanges} = newProps
|
||||
for change in pendingChanges
|
||||
@@ -46,7 +46,7 @@ LinesComponent = React.createClass
|
||||
componentDidUpdate: (prevProps) ->
|
||||
@measureLineHeightAndCharWidth() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily', 'lineHeight')
|
||||
@clearScreenRowCaches() unless prevProps.lineHeight is @props.lineHeight
|
||||
@removeLineNodes() unless prevProps.showIndentGuide is @props.showIndentGuide
|
||||
@removeLineNodes() unless isEqualForProperties(prevProps, @props, 'showIndentGuide', 'invisibles')
|
||||
@updateLines()
|
||||
@clearScopedCharWidths() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily')
|
||||
@measureCharactersInNewLines() unless @props.scrollingVertically
|
||||
@@ -132,7 +132,7 @@ LinesComponent = React.createClass
|
||||
" "
|
||||
|
||||
buildLineInnerHTML: (line) ->
|
||||
{invisibles, mini, showIndentGuide} = @props
|
||||
{invisibles, mini, showIndentGuide, invisibles} = @props
|
||||
{tokens, text} = line
|
||||
innerHTML = ""
|
||||
|
||||
@@ -143,9 +143,22 @@ LinesComponent = React.createClass
|
||||
innerHTML += @updateScopeStack(scopeStack, token.scopes)
|
||||
hasIndentGuide = not mini and showIndentGuide and token.hasLeadingWhitespace or (token.hasTrailingWhitespace and lineIsWhitespaceOnly)
|
||||
innerHTML += token.getValueAsHtml({invisibles, hasIndentGuide})
|
||||
|
||||
innerHTML += @popScope(scopeStack) while scopeStack.length > 0
|
||||
innerHTML += @buildEndOfLineHTML(line, invisibles)
|
||||
innerHTML
|
||||
|
||||
buildEndOfLineHTML: (line, invisibles) ->
|
||||
return '' if @props.mini or line.isSoftWrapped()
|
||||
|
||||
html = ''
|
||||
if invisibles.cr? and line.lineEnding is '\r\n'
|
||||
html += "<span class='invisible-character'>#{invisibles.cr}</span>"
|
||||
if invisibles.eol?
|
||||
html += "<span class='invisible-character'>#{invisibles.eol}</span>"
|
||||
|
||||
html
|
||||
|
||||
updateScopeStack: (scopeStack, desiredScopes) ->
|
||||
html = ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user