From 052f9580f2b30ebe9be881ec8a53b26fbdb2e34a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 11 Aug 2014 14:39:04 -0600 Subject: [PATCH] Render correct classes on leading/trailing whitespace spans --- spec/editor-component-spec.coffee | 6 +++++- src/token.coffee | 23 ++++++++++++----------- src/tokenized-line.coffee | 18 ++++++++++-------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 603892b19..578ee4ed8 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -221,11 +221,15 @@ describe "EditorComponent", -> 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", -> + it "displays leading/trailing spaces, tabs, and newlines as visible characters", -> editor.setText " a line with tabs\tand spaces " nextAnimationFrame() expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab}and spaces#{invisibles.space}#{invisibles.eol}" + leafNodes = getLeafNodes(component.lineNodeForScreenRow(0)) + expect(leafNodes[0].classList.contains('invisible-character')).toBe true + expect(leafNodes[leafNodes.length - 1].classList.contains('invisible-character')).toBe true + it "displays newlines as their own token outside of the other tokens' scopes", -> editor.setText "var" nextAnimationFrame() diff --git a/src/token.coffee b/src/token.coffee index 9d455a965..d0f3ea27e 100644 --- a/src/token.coffee +++ b/src/token.coffee @@ -2,11 +2,7 @@ _ = require 'underscore-plus' textUtils = require './text-utils' WhitespaceRegexesByTabLength = {} -LeadingSpaceRegex = /^[ ]+/ -TrailingSpaceRegex = /[ ]+$/ EscapeRegex = /[&"'<>]/g -CharacterRegex = /./g -StartCharacterRegex = /^./ StartDotRegex = /^\.?/ WhitespaceRegex = /\S/ @@ -22,6 +18,8 @@ class Token isHardTab: null hasLeadingWhitespace: false hasTrailingWhitespace: false + firstNonWhitespaceIndex: null + firstTrailingWhitespaceIndex: null constructor: ({@value, @scopes, @isAtomic, @bufferDelta, @isHardTab}) -> @screenDelta = @value.length @@ -150,24 +148,27 @@ class Token leadingHtml = '' trailingHtml = '' - if @hasLeadingWhitespace and match = LeadingSpaceRegex.exec(@value) + if @hasLeadingWhitespace + leadingWhitespace = @value.substring(0, @firstNonWhitespaceIndex) + classes = 'leading-whitespace' classes += ' indent-guide' if hasIndentGuide classes += ' invisible-character' if invisibles.space - leadingHtml = "#{match[0]}" + leadingHtml = "#{leadingWhitespace}" + startIndex = @firstNonWhitespaceIndex - startIndex = match[0].length + if @hasTrailingWhitespace + tokenIsOnlyWhitespace = @firstTrailingWhitespaceIndex is 0 + trailingWhitespace = @value.substring(@firstTrailingWhitespaceIndex) - if @hasTrailingWhitespace and match = TrailingSpaceRegex.exec(@value) - tokenIsOnlyWhitespace = match[0].length is @value.length classes = 'trailing-whitespace' classes += ' indent-guide' if hasIndentGuide and not @hasLeadingWhitespace and tokenIsOnlyWhitespace classes += ' invisible-character' if invisibles.space - trailingHtml = "#{match[0]}" + trailingHtml = "#{trailingWhitespace}" - endIndex = match.index + endIndex = @firstTrailingWhitespaceIndex html = leadingHtml if @value.length > MaxTokenLength diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 8efcf51fb..d6a2616e3 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -138,15 +138,17 @@ class TokenizedLine outputTokens markLeadingAndTrailingWhitespaceTokens: -> - firstNonWhitespacePosition = @text.search(NonWhitespaceRegex) - firstTrailingWhitespacePosition = @text.search(TrailingWhitespaceRegex) - lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0 - position = 0 - for token, i in @tokens - token.hasLeadingWhitespace = position < firstNonWhitespacePosition + firstNonWhitespaceIndex = @text.search(NonWhitespaceRegex) + firstTrailingWhitespaceIndex = @text.search(TrailingWhitespaceRegex) + lineIsWhitespaceOnly = firstTrailingWhitespaceIndex is 0 + index = 0 + for token in @tokens + if token.hasLeadingWhitespace = index < firstNonWhitespaceIndex + token.firstNonWhitespaceIndex = firstNonWhitespaceIndex - index # Only the *last* segment of a soft-wrapped line can have trailing whitespace - token.hasTrailingWhitespace = @lineEnding? and (position + token.value.length > firstTrailingWhitespacePosition) - position += token.value.length + if token.hasTrailingWhitespace = @lineEnding? and (index + token.value.length > firstTrailingWhitespaceIndex) + token.firstTrailingWhitespaceIndex = Math.max(0, firstTrailingWhitespaceIndex - index) + index += token.value.length substituteInvisibleCharacters: -> invisibles = @invisibles