mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2025-01-02 20:07:25 +03:00
Handle invisible character rendering when building HTML for lines.
Not during creation of tokens.
This commit is contained in:
parent
dd5a10e82e
commit
b33bbbfc0d
@ -1472,6 +1472,16 @@ describe "Editor", ->
|
||||
expect(editor.renderedLines.find('.line:eq(14)').text()).toBe 'B'
|
||||
expect(editor.renderedLines.find('.line:eq(15)')).not.toExist()
|
||||
|
||||
describe "when editSession.showInvisibles is true", ->
|
||||
beforeEach ->
|
||||
project.setShowInvisibles(true)
|
||||
rootView.open()
|
||||
editor.attachToDom(5)
|
||||
|
||||
it "displays spaces as •, tabs as ▸ and newlines as ¬", ->
|
||||
editor.setText " a line with tabs\tand spaces "
|
||||
expect(editor.find('.line').text()).toBe "•a line with tabs▸ and spaces•¬"
|
||||
|
||||
describe "gutter rendering", ->
|
||||
beforeEach ->
|
||||
editor.attachToDom(heightInLines: 5.5)
|
||||
@ -1827,13 +1837,3 @@ describe "Editor", ->
|
||||
editor.pageUp()
|
||||
expect(editor.getCursor().getScreenPosition().row).toBe(0)
|
||||
expect(editor.getFirstVisibleScreenRow()).toBe(0)
|
||||
|
||||
describe "when editSession.showInvisibles is true", ->
|
||||
beforeEach ->
|
||||
project.setShowInvisibles(true)
|
||||
rootView.open()
|
||||
editor.attachToDom(5)
|
||||
|
||||
it "displays spaces as •, tabs as ▸ and newlines as ¬", ->
|
||||
editor.setText " a line with tabs\tand spaces "
|
||||
expect(editor.find('.line').text()).toBe "•a•line•with•tabs▸ and•spaces•¬"
|
||||
|
@ -1,61 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
Token = require 'token'
|
||||
Buffer = require 'buffer'
|
||||
TokenizedBuffer = require 'tokenized-buffer'
|
||||
|
||||
describe "Token", ->
|
||||
[editSession, token] = []
|
||||
|
||||
beforeEach ->
|
||||
editSession = fixturesProject.buildEditSessionForPath('sample.js', { tabLength: 2 })
|
||||
{ tokenizedBuffer } = editSession
|
||||
screenLine = tokenizedBuffer.lineForScreenRow(3)
|
||||
token = _.last(screenLine.tokens)
|
||||
|
||||
afterEach ->
|
||||
editSession.destroy()
|
||||
|
||||
describe ".breakOutWhitespaceCharacters(tabLength, showInvisbles)", ->
|
||||
describe "when showInvisbles is false", ->
|
||||
it "replaces spaces and tabs with their own tokens", ->
|
||||
value = " spaces tabs\t\t\t "
|
||||
scopes = ["whatever"]
|
||||
isAtomic = false
|
||||
bufferDelta = value.length
|
||||
token = new Token({value, scopes, isAtomic, bufferDelta})
|
||||
tokens = token.breakOutWhitespaceCharacters(4, false)
|
||||
|
||||
expect(tokens.length).toBe(8)
|
||||
expect(tokens[0].value).toBe(" ")
|
||||
expect(tokens[0].scopes).not.toContain("invisible")
|
||||
expect(tokens[1].value).toBe("spaces")
|
||||
expect(tokens[1].scopes).not.toContain("invisible")
|
||||
expect(tokens[2].value).toBe(" ")
|
||||
expect(tokens[3].value).toBe("tabs")
|
||||
expect(tokens[4].value).toBe(" ")
|
||||
expect(tokens[4].scopes).not.toContain("invisible")
|
||||
expect(tokens[5].value).toBe(" ")
|
||||
expect(tokens[6].value).toBe(" ")
|
||||
expect(tokens[7].value).toBe(" ")
|
||||
|
||||
describe "when showInvisbles is true", ->
|
||||
it "replaces spaces and tabs with their own tokens", ->
|
||||
value = " spaces tabs\t\t\t "
|
||||
scopes = ["whatever"]
|
||||
isAtomic = false
|
||||
bufferDelta = value.length
|
||||
token = new Token({value, scopes, isAtomic, bufferDelta})
|
||||
tokens = token.breakOutWhitespaceCharacters(4, true)
|
||||
|
||||
expect(tokens.length).toBe(8)
|
||||
expect(tokens[0].value).toBe("•")
|
||||
expect(tokens[0].scopes).toContain("invisible")
|
||||
expect(tokens[1].value).toBe("spaces")
|
||||
expect(tokens[1].scopes).not.toContain("invisible")
|
||||
expect(tokens[2].value).toBe("•••")
|
||||
expect(tokens[3].value).toBe("tabs")
|
||||
expect(tokens[4].value).toBe("▸ ")
|
||||
expect(tokens[4].scopes).toContain("invisible")
|
||||
expect(tokens[5].value).toBe("▸ ")
|
||||
expect(tokens[6].value).toBe("▸ ")
|
||||
expect(tokens[7].value).toBe("••")
|
@ -81,12 +81,12 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
# previous line 3 should be combined with input to form line 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[8]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
# lines below deleted regions should be shifted upward
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[3]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[1]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[1]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@ -125,7 +125,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
# previous line 3 is pushed down to become line 5
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[5]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@ -165,12 +165,11 @@ describe "TokenizedBuffer", ->
|
||||
screenLine0 = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(screenLine0.text).toBe "# Econ 101#{editSession2.getTabText()}"
|
||||
{ tokens } = screenLine0
|
||||
expect(tokens.length).toBe 6
|
||||
expect(tokens.length).toBe 3
|
||||
expect(tokens[0].value).toBe "#"
|
||||
expect(tokens[2].value).toBe "Econ"
|
||||
expect(tokens[4].value).toBe "101"
|
||||
expect(tokens[5].value).toBe editSession2.getTabText()
|
||||
expect(tokens[5].scopes).toEqual tokens[1].scopes
|
||||
expect(tokens[5].isAtomic).toBeTruthy()
|
||||
expect(tokens[1].value).toBe " Econ 101"
|
||||
expect(tokens[2].value).toBe editSession2.getTabText()
|
||||
expect(tokens[2].scopes).toEqual tokens[1].scopes
|
||||
expect(tokens[2].isAtomic).toBeTruthy()
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "#{editSession2.getTabText()} buy()#{editSession2.getTabText()}while supply > demand"
|
||||
|
@ -876,9 +876,18 @@ class Editor extends View
|
||||
if screenLine.text == ''
|
||||
line.push(" ") unless @activeEditSession.showInvisibles
|
||||
else
|
||||
firstNonWhitespacePosition = screenLine.text.search(/\S/)
|
||||
firstTrailingWhitespacePosition = screenLine.text.search(/\s*$/)
|
||||
position = 0
|
||||
for token in screenLine.tokens
|
||||
updateScopeStack(token.scopes)
|
||||
line.push(token.escapeValue(@activeEditSession.showInvisibles))
|
||||
line.push(token.getValueAsHtml(
|
||||
showInvisibles: @activeEditSession.showInvisibles
|
||||
hasLeadingWhitespace: position < firstNonWhitespacePosition
|
||||
hasTrailingWhitespace: position + token.value.length > firstTrailingWhitespacePosition
|
||||
))
|
||||
|
||||
position += token.value.length
|
||||
|
||||
line.push("<span class='invisible'>¬</span>") if @activeEditSession.showInvisibles
|
||||
line.push('</pre>')
|
||||
|
@ -5,6 +5,7 @@ class Token
|
||||
value: null
|
||||
scopes: null
|
||||
isAtomic: null
|
||||
isTab: null
|
||||
|
||||
constructor: ({@value, @scopes, @isAtomic, @bufferDelta, @fold, @isTab}) ->
|
||||
@screenDelta = @value.length
|
||||
@ -21,29 +22,41 @@ class Token
|
||||
value2 = @value.substring(splitIndex)
|
||||
[new Token(value: value1, scopes: @scopes), new Token(value: value2, scopes: @scopes)]
|
||||
|
||||
breakOutWhitespaceCharacters: (tabLength, showInvisibles) ->
|
||||
return [this] unless /\t| /.test(@value)
|
||||
|
||||
for substring in @value.match(/([^\t ]+|(\t| +))/g)
|
||||
scopesForInvisibles = if showInvisibles then @scopes.concat("invisible") else @scopes
|
||||
breakOutTabCharacters: (tabLength, showInvisibles) ->
|
||||
return [this] unless /\t/.test(@value)
|
||||
|
||||
for substring in @value.match(/[^\t]+|\t/g)
|
||||
if substring == "\t"
|
||||
value = new Array(tabLength + 1).join(" ")
|
||||
value = "▸" + value[1..] if showInvisibles
|
||||
new Token(value: value, scopes: scopesForInvisibles, bufferDelta: 1, isAtomic: true)
|
||||
else if /^ +$/.test(substring)
|
||||
value = if showInvisibles then substring.replace(/[ ]/g, "•") else substring
|
||||
new Token(value: value, scopes: scopesForInvisibles)
|
||||
@buildTabToken(tabLength)
|
||||
else
|
||||
new Token(value: substring, scopes: @scopes)
|
||||
|
||||
buildTabToken: (tabLength) ->
|
||||
tabText = new Array(tabLength + 1).join(" ")
|
||||
new Token(
|
||||
value: new Array(tabLength + 1).join(" ")
|
||||
scopes: @scopes
|
||||
bufferDelta: 1
|
||||
isAtomic: true
|
||||
isTab: true
|
||||
)
|
||||
|
||||
escapeValue: (showInvisibles)->
|
||||
@value
|
||||
getValueAsHtml: ({showInvisibles, hasLeadingWhitespace, hasTrailingWhitespace})->
|
||||
html = @value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
|
||||
if showInvisibles
|
||||
if @isTab
|
||||
html = html.replace(/^./, "<span class='invisible'>▸</span>")
|
||||
else
|
||||
if hasLeadingWhitespace
|
||||
html = html.replace /^[ ]+/, (match) ->
|
||||
"<span class='invisible'>#{match.replace(/./g, '•')}</span>"
|
||||
if hasTrailingWhitespace
|
||||
html = html.replace /[ ]+$/, (match) ->
|
||||
"<span class='invisible'>#{match.replace(/./g, '•')}</span>"
|
||||
|
||||
html
|
||||
|
@ -67,7 +67,7 @@ class TokenizedBuffer
|
||||
tokenObjects = []
|
||||
for tokenProperties in tokens
|
||||
token = new Token(tokenProperties)
|
||||
tokenObjects.push(token.breakOutWhitespaceCharacters(@tabLength, @showInvisibles)...)
|
||||
tokenObjects.push(token.breakOutTabCharacters(@tabLength, @showInvisibles)...)
|
||||
text = _.pluck(tokenObjects, 'value').join('')
|
||||
new ScreenLine(tokenObjects, text, [1, 0], [1, 0], { stack })
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user