From 84ba78c55db5fd14bc69ed26c63856cf9d7073a3 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 5 Apr 2012 15:22:57 -0600 Subject: [PATCH] Tab characters render as atomic tokens containing spaces --- spec/app/highlighter-spec.coffee | 17 +++++++++++++++++ spec/app/renderer-spec.coffee | 12 ++++++++++++ spec/fixtures/sample-with-tabs.coffee | 2 +- src/app/highlighter.coffee | 8 ++++++-- src/app/screen-line-fragment.coffee | 16 +++++++++------- src/app/token.coffee | 10 ++++++++++ 6 files changed, 55 insertions(+), 10 deletions(-) diff --git a/spec/app/highlighter-spec.coffee b/spec/app/highlighter-spec.coffee index dd2fc23e1..c30f74859 100644 --- a/spec/app/highlighter-spec.coffee +++ b/spec/app/highlighter-spec.coffee @@ -137,3 +137,20 @@ describe "Highlighter", -> [event] = changeHandler.argsForCall[0] expect(event.oldRange).toEqual new Range([2, 0], [5, buffer.lineForRow(7).length]) expect(event.newRange).toEqual new Range([2, 0], [7, buffer.lineForRow(7).length]) + + describe "when the buffer contains tab characters", -> + beforeEach -> + buffer = new Buffer(require.resolve('fixtures/sample-with-tabs.coffee')) + highlighter = new Highlighter(buffer) + + it "always renders each tab as its own atomic token containing atom.tabText", -> + screenLine0 = highlighter.lineForScreenRow(0) + expect(screenLine0.text).toBe "# Econ 101#{atom.tabText}" + { tokens } = screenLine0 + expect(tokens.length).toBe 2 + expect(tokens[0].value).toBe "# Econ 101" + expect(tokens[1].value).toBe atom.tabText + expect(tokens[1].type).toBe tokens[0].type + expect(tokens[1].isAtomic).toBeTruthy() + + expect(highlighter.lineForScreenRow(2).text).toBe "#{atom.tabText} buy()#{atom.tabText}while supply > demand" diff --git a/spec/app/renderer-spec.coffee b/spec/app/renderer-spec.coffee index fc8120f64..742dabf9a 100644 --- a/spec/app/renderer-spec.coffee +++ b/spec/app/renderer-spec.coffee @@ -604,6 +604,12 @@ describe "Renderer", -> expect(renderer.clipScreenPosition([4, 6])).toEqual [4, 4] expect(renderer.clipScreenPosition([4, 7])).toEqual [4, 7] + it "clips screen positions in the middle of atomic tab characters to the beginning of the character", -> + buffer.insert([0, 0], '\t') + expect(renderer.clipScreenPosition([0, 0])).toEqual [0, 0] + expect(renderer.clipScreenPosition([0, 1])).toEqual [0, 0] + expect(renderer.clipScreenPosition([0, atom.tabText.length])).toEqual [0, atom.tabText.length] + describe "when skipAtomicTokens is true", -> it "wraps the screen positions in the middle of fold placeholders to the end of the placeholder", -> renderer.createFold([[3, 55], [3, 59]]) @@ -611,6 +617,12 @@ describe "Renderer", -> expect(renderer.clipScreenPosition([4, 5], skipAtomicTokens: true)).toEqual [4, 7] expect(renderer.clipScreenPosition([4, 6], skipAtomicTokens: true)).toEqual [4, 7] + it "clips screen positions in the middle of atomic tab characters to the beginning of the character", -> + buffer.insert([0, 0], '\t') + expect(renderer.clipScreenPosition([0, 0], skipAtomicTokens: true)).toEqual [0, 0] + expect(renderer.clipScreenPosition([0, 1], skipAtomicTokens: true)).toEqual [0, atom.tabText.length] + expect(renderer.clipScreenPosition([0, atom.tabText.length], skipAtomicTokens: true)).toEqual [0, atom.tabText.length] + describe ".bufferRowsForScreenRows()", -> it "returns the buffer rows corresponding to each screen row in the given range", -> renderer.setMaxLineLength(50) diff --git a/spec/fixtures/sample-with-tabs.coffee b/spec/fixtures/sample-with-tabs.coffee index 4cdb1507e..1b937ea33 100644 --- a/spec/fixtures/sample-with-tabs.coffee +++ b/spec/fixtures/sample-with-tabs.coffee @@ -1,4 +1,4 @@ -# Econ 101 +# Econ 101 if this.studyingEconomics buy() while supply > demand sell() until supply > demand diff --git a/src/app/highlighter.coffee b/src/app/highlighter.coffee index b460a5cea..66eb26748 100644 --- a/src/app/highlighter.coffee +++ b/src/app/highlighter.coffee @@ -56,8 +56,12 @@ class Highlighter tokenizer = @buffer.getMode().getTokenizer() line = @buffer.lineForRow(row) {tokens, state} = tokenizer.getLineTokens(line, state) - tokens = tokens.map (tokenProperties) -> new Token(tokenProperties) - new ScreenLineFragment(tokens, line, [1, 0], [1, 0], { state }) + tokenObjects = [] + for tokenProperties in tokens + token = new Token(tokenProperties) + tokenObjects.push(token.breakOutTabCharacters()...) + text = _.pluck(tokenObjects, 'value').join('') + new ScreenLineFragment(tokenObjects, text, [1, 0], [1, 0], { state }) lineForScreenRow: (row) -> @screenLines[row] diff --git a/src/app/screen-line-fragment.coffee b/src/app/screen-line-fragment.coffee index 1cf2a41f2..586b7a0e6 100644 --- a/src/app/screen-line-fragment.coffee +++ b/src/app/screen-line-fragment.coffee @@ -45,19 +45,21 @@ class ScreenLineFragment new ScreenLineFragment(tokens, text, screenDelta, bufferDelta, {state: other.state}) clipColumn: (column, { skipAtomicTokens }) -> - column = Math.min(column, @text.length) + textLength = @text.length + column = Math.min(column, textLength) currentColumn = 0 for token in @tokens - nextColumn = token.value.length + currentColumn - break if nextColumn >= column - currentColumn = nextColumn + tokenStartColumn = currentColumn + tokenEndColumn = tokenStartColumn + token.value.length + break if tokenEndColumn > column + currentColumn = tokenEndColumn if token?.isAtomic - if skipAtomicTokens and column > currentColumn - nextColumn + if skipAtomicTokens and column > tokenStartColumn + tokenEndColumn else - currentColumn + tokenStartColumn else column diff --git a/src/app/token.coffee b/src/app/token.coffee index 7710670dd..e52a63cb6 100644 --- a/src/app/token.coffee +++ b/src/app/token.coffee @@ -13,3 +13,13 @@ class Token value1 = @value.substring(0, splitIndex) value2 = @value.substring(splitIndex) [new Token(value: value1, type: @type), new Token(value: value2, type: @type)] + + breakOutTabCharacters: -> + for substring in @value.match(/([^\t]+|\t)/g) + if substring == '\t' + @buildTabToken() + else + new Token(value: substring, type: @type) + + buildTabToken: -> + new Token(value: atom.tabText, type: @type, isAtomic: true)