mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
Correctly translate buffer positions to screen positions when the buffer has tab chars
This commit is contained in:
@@ -1661,6 +1661,11 @@ describe "Editor", ->
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 1]
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, editor.tabText.length]
|
||||
|
||||
editor.trigger 'tab'
|
||||
expect(buffer.lineForRow(0)).toMatch(/^\t\t/)
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, editor.tabText.length * 2]
|
||||
|
||||
describe "undo/redo", ->
|
||||
it "undoes/redoes the last change", ->
|
||||
buffer.insert [0, 0], "foo"
|
||||
|
||||
@@ -3,11 +3,12 @@ Buffer = require 'buffer'
|
||||
Highlighter = require 'highlighter'
|
||||
|
||||
describe "screenLineFragment", ->
|
||||
[screenLine, highlighter] = []
|
||||
[buffer, tabText, screenLine, highlighter] = []
|
||||
|
||||
beforeEach ->
|
||||
tabText = ' '
|
||||
buffer = new Buffer(require.resolve 'fixtures/sample.js')
|
||||
highlighter = new Highlighter(buffer)
|
||||
highlighter = new Highlighter(buffer, tabText)
|
||||
screenLine = highlighter.lineForScreenRow(3)
|
||||
|
||||
describe ".splitAt(column)", ->
|
||||
@@ -75,7 +76,36 @@ describe "screenLineFragment", ->
|
||||
expect(concatenated.screenDelta).toEqual [2, 0]
|
||||
expect(concatenated.bufferDelta).toEqual [2, 0]
|
||||
|
||||
describe ".translateColumn(sourceDeltaType, targetDeltaType, sourceColumn, skipAtomicTokens: false)", ->
|
||||
beforeEach ->
|
||||
buffer.insert([0, 13], '\t')
|
||||
buffer.insert([0, 0], '\t\t')
|
||||
screenLine = highlighter.lineForScreenRow(0)
|
||||
|
||||
describe "when translating from buffer to screen coordinates", ->
|
||||
it "accounts for tab characters being wider on screen", ->
|
||||
expect(screenLine.translateColumn('bufferDelta', 'screenDelta', 0)).toBe 0
|
||||
expect(screenLine.translateColumn('bufferDelta', 'screenDelta', 1)).toBe 2
|
||||
expect(screenLine.translateColumn('bufferDelta', 'screenDelta', 2)).toBe 4
|
||||
expect(screenLine.translateColumn('bufferDelta', 'screenDelta', 3)).toBe 5
|
||||
expect(screenLine.translateColumn('bufferDelta', 'screenDelta', 15)).toBe 17
|
||||
expect(screenLine.translateColumn('bufferDelta', 'screenDelta', 16)).toBe 19
|
||||
|
||||
describe "when translating from screen coordinates to buffer coordinates", ->
|
||||
describe "when skipAtomicTokens is false (the default)", ->
|
||||
it "clips positions in the middle of tab tokens to the beginning", ->
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 0)).toBe 0
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 1)).toBe 0
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 2)).toBe 1
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 3)).toBe 1
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 4)).toBe 2
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 5)).toBe 3
|
||||
|
||||
describe "when skipAtomicTokens is true", ->
|
||||
it "clips positions in the middle of tab tokens to the end", ->
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 0, skipAtomicTokens: true)).toBe 0
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 1, skipAtomicTokens: true)).toBe 1
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 2, skipAtomicTokens: true)).toBe 1
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 3, skipAtomicTokens: true)).toBe 2
|
||||
expect(screenLine.translateColumn('screenDelta', 'bufferDelta', 5, skipAtomicTokens: true)).toBe 3
|
||||
|
||||
|
||||
@@ -136,7 +136,8 @@ class LineMap
|
||||
targetDelta.column = 0
|
||||
else
|
||||
additionalColumns = sourcePosition.column - sourceDelta.column
|
||||
targetDelta.column += lastLineFragment.clipColumn(additionalColumns, { skipAtomicTokens })
|
||||
additionalColumns = lastLineFragment.translateColumn(sourceDeltaType, targetDeltaType, additionalColumns, { skipAtomicTokens })
|
||||
targetDelta.column += additionalColumns
|
||||
|
||||
targetDelta
|
||||
|
||||
|
||||
@@ -44,24 +44,30 @@ class ScreenLineFragment
|
||||
bufferDelta = @bufferDelta.add(other.bufferDelta)
|
||||
new ScreenLineFragment(tokens, text, screenDelta, bufferDelta, {state: other.state})
|
||||
|
||||
clipColumn: (column, { skipAtomicTokens }) ->
|
||||
translateColumn: (sourceDeltaType, targetDeltaType, sourceColumn, options={}) ->
|
||||
{ skipAtomicTokens } = options
|
||||
textLength = @text.length
|
||||
column = Math.min(column, textLength)
|
||||
sourceColumn = Math.min(sourceColumn, textLength)
|
||||
|
||||
currentColumn = 0
|
||||
currentSourceColumn = 0
|
||||
currentTargetColumn = 0
|
||||
for token in @tokens
|
||||
tokenStartColumn = currentColumn
|
||||
tokenEndColumn = tokenStartColumn + token.value.length
|
||||
break if tokenEndColumn > column
|
||||
currentColumn = tokenEndColumn
|
||||
tokenStartTargetColumn = currentTargetColumn
|
||||
tokenStartSourceColumn = currentSourceColumn
|
||||
tokenEndSourceColumn = currentSourceColumn + token[sourceDeltaType]
|
||||
tokenEndTargetColumn = currentTargetColumn + token[targetDeltaType]
|
||||
break if tokenEndSourceColumn > sourceColumn
|
||||
currentSourceColumn = tokenEndSourceColumn
|
||||
currentTargetColumn = tokenEndTargetColumn
|
||||
|
||||
if token?.isAtomic
|
||||
if skipAtomicTokens and column > tokenStartColumn
|
||||
tokenEndColumn
|
||||
if skipAtomicTokens and sourceColumn > tokenStartSourceColumn
|
||||
tokenEndTargetColumn
|
||||
else
|
||||
tokenStartColumn
|
||||
tokenStartTargetColumn
|
||||
else
|
||||
column
|
||||
remainingColumns = sourceColumn - currentSourceColumn
|
||||
currentTargetColumn + remainingColumns
|
||||
|
||||
isSoftWrapped: ->
|
||||
@screenDelta.row == 1 and @bufferDelta.row == 0
|
||||
|
||||
@@ -4,7 +4,9 @@ class Token
|
||||
type: null
|
||||
isAtomic: null
|
||||
|
||||
constructor: ({@value, @type, @isAtomic}) ->
|
||||
constructor: ({@value, @type, @isAtomic, @bufferDelta}) ->
|
||||
@screenDelta = @value.length
|
||||
@bufferDelta ?= @screenDelta
|
||||
|
||||
isEqual: (other) ->
|
||||
@value == other.value and @type == other.type and !!@isAtomic == !!other.isAtomic
|
||||
@@ -22,4 +24,4 @@ class Token
|
||||
new Token(value: substring, type: @type)
|
||||
|
||||
buildTabToken: (tabText) ->
|
||||
new Token(value: tabText, type: @type, isAtomic: true)
|
||||
new Token(value: tabText, type: @type, bufferDelta: 1, isAtomic: true)
|
||||
|
||||
Reference in New Issue
Block a user