Render lines inline instead of with separate components

Esse commit está contido em:
Nathan Sobo
2014-05-23 15:30:08 -06:00
commit 0eb44e06d4
+89 -98
Ver Arquivo
@@ -34,20 +34,98 @@ LinesComponent = React.createClass
lineComponents =
for line, i in editor.linesForScreenRows(startRow, endRow - 1)
screenRow = startRow + i
LineComponent({key: line.id, line, screenRow, lineHeightInPixels, showIndentGuide, mini, invisibles})
@renderLine(line, startRow + i)
if mouseWheelScreenRow? and not (startRow <= mouseWheelScreenRow < endRow)
line = editor.lineForScreenRow(mouseWheelScreenRow)
lineComponents.push(LineComponent({
key: line.id, line, screenRow: mouseWheelScreenRow, screenRowOverride: endRow,
lineHeightInPixels, showIndentGuide, mini, invisibles
}))
lineComponents.push(@renderLine(editor.lineForScreenRow(mouseWheelScreenRow), mouseWheelScreenRow, endRow))
lineComponents
renderLine: (line, screenRow, screenRowOverride) ->
{lineHeightInPixels} = @props
style =
position: "absolute"
top: (screenRowOverride ? screenRow) * lineHeightInPixels
@lineInnerHTMLByLineId[line.id] ?= @buildLineInnerHTML(line)
innerHTML = @lineInnerHTMLByLineId[line.id]
div key: line.id, className: "line", style: style, 'data-screen-row': screenRow, dangerouslySetInnerHTML: {__html: innerHTML}
buildLineInnerHTML: (line) ->
if line.text is ""
@buildEmptyLineInnerHTML(line)
else
@buildNonEmptyLineInnerHTML(line)
buildEmptyLineInnerHTML: (line) ->
{showIndentGuide} = @props
{indentLevel, tabLength} = line
if showIndentGuide and indentLevel > 0
indentSpan = "<span class='indent-guide'>#{multiplyString(' ', tabLength)}</span>"
multiplyString(indentSpan, indentLevel + 1)
else
"&nbsp;"
buildNonEmptyLineInnerHTML: (line) ->
{invisibles, mini, showIndentGuide, invisibles} = @props
{tokens, text} = line
innerHTML = ""
scopeStack = []
firstTrailingWhitespacePosition = text.search(/\s*$/)
lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0
for token in tokens
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)
innerHTML
buildEndOfLineHTML: (line) ->
{invisibles, mini} = @props
return '' if 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 = ""
# Find a common prefix
for scope, i in desiredScopes
break unless scopeStack[i]?.scope is desiredScopes[i]
# Pop scopes until we're at the common prefx
until scopeStack.length is i
html += @popScope(scopeStack)
# Push onto common prefix until scopeStack equals desiredScopes
for j in [i...desiredScopes.length]
html += @pushScope(scopeStack, desiredScopes[j])
html
popScope: (scopeStack) ->
scopeStack.pop()
"</span>"
pushScope: (scopeStack, scope) ->
scopeStack.push(scope)
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
componentWillMount: ->
@measuredLines = new WeakSet
@lineInnerHTMLByLineId = {}
componentDidMount: ->
@measureLineHeightInPixelsAndCharWidth()
@@ -66,6 +144,10 @@ LinesComponent = React.createClass
false
componentWillUpdate: (newProps) ->
unless isEqualForProperties(newProps, @props, 'invisibles', 'showIndentGuide')
@lineInnerHTMLByLineId = {}
componentDidUpdate: (prevProps) ->
@measureLineHeightInPixelsAndCharWidthIfNeeded(prevProps)
@clearScopedCharWidths() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily')
@@ -147,94 +229,3 @@ LinesComponent = React.createClass
clearScopedCharWidths: ->
@measuredLines.clear()
@props.editor.clearScopedCharWidths()
LineComponent = React.createClass
displayName: "LineComponent"
render: ->
{screenRow, screenRowOverride, lineHeightInPixels} = @props
style =
position: "absolute"
top: (screenRowOverride ? screenRow) * lineHeightInPixels
div className: "line", style: style, 'data-screen-row': screenRow, dangerouslySetInnerHTML: {__html: @buildInnerHTML()}
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'screenRow', 'lineHeightInPixels', 'showIndentGuide', 'invisibles')
componentWillUpdate: (newProps) ->
unless isEqualForProperties(newProps, @props, 'showIndentGuide', 'invisibles')
@innerHTML = null
buildInnerHTML: ->
{line} = @props
if line.text is ""
@innerHTML ?= @buildEmptyInnerHTML()
else
@innerHTML ?= @buildNonEmptyInnerHTML()
buildEmptyInnerHTML: ->
{line, showIndentGuide} = @props
{indentLevel, tabLength} = line
if showIndentGuide and indentLevel > 0
indentSpan = "<span class='indent-guide'>#{multiplyString(' ', tabLength)}</span>"
multiplyString(indentSpan, indentLevel + 1)
else
"&nbsp;"
buildNonEmptyInnerHTML: ->
{line, invisibles, mini, showIndentGuide, invisibles} = @props
{tokens, text} = line
innerHTML = ""
scopeStack = []
firstTrailingWhitespacePosition = text.search(/\s*$/)
lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0
for token in tokens
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, mini} = @props
return '' if 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 = ""
# Find a common prefix
for scope, i in desiredScopes
break unless scopeStack[i]?.scope is desiredScopes[i]
# Pop scopes until we're at the common prefx
until scopeStack.length is i
html += @popScope(scopeStack)
# Push onto common prefix until scopeStack equals desiredScopes
for j in [i...desiredScopes.length]
html += @pushScope(scopeStack, desiredScopes[j])
html
popScope: (scopeStack) ->
scopeStack.pop()
"</span>"
pushScope: (scopeStack, scope) ->
scopeStack.push(scope)
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"