mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-21 07:58:04 +03:00
Cursor moves correctly between wrapped lines
Added explicit options for controlling line wrapping, and skipping of atomic tokens to the LineWrap.clipScreenPosition. These are used when moving right to wrap to the next line.
This commit is contained in:
parent
f2f401e5a1
commit
6e46b97a5c
@ -104,6 +104,24 @@ describe "Editor", ->
|
||||
editor.moveCursorDown()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [editor.lastScreenRow(), 2]
|
||||
|
||||
it "allows the cursor to move up to a shorter soft wrapped line", ->
|
||||
editor.setCursorScreenPosition([11, 15])
|
||||
editor.moveCursorUp()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [10, 10]
|
||||
editor.moveCursorUp()
|
||||
editor.moveCursorUp()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [8, 15]
|
||||
|
||||
it "it allows the cursor to wrap when moving horizontally past the beginning / end of a wrapped line", ->
|
||||
editor.setCursorScreenPosition([11, 0])
|
||||
editor.moveCursorLeft()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [10, 10]
|
||||
|
||||
editor.moveCursorRight()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [11, 0]
|
||||
|
||||
|
||||
|
||||
|
||||
describe "cursor movement", ->
|
||||
describe ".setCursorScreenPosition({row, column})", ->
|
||||
|
@ -327,18 +327,18 @@ describe "LineFolder", ->
|
||||
expect(folder.clipScreenPosition([4, 1000])).toEqual [4, 33]
|
||||
expect(folder.clipScreenPosition([1000, 1000])).toEqual [9, 2]
|
||||
|
||||
describe "when eagerWrap is false (the default)", ->
|
||||
describe "when skipAtomicTokens is false (the default)", ->
|
||||
it "clips positions inside a placeholder to the beginning of the placeholder", ->
|
||||
expect(folder.clipScreenPosition([4, 30])).toEqual [4, 29]
|
||||
expect(folder.clipScreenPosition([4, 31])).toEqual [4, 29]
|
||||
expect(folder.clipScreenPosition([4, 32])).toEqual [4, 32]
|
||||
|
||||
describe "when eagerWrap is true", ->
|
||||
describe "when skipAtomicTokens is true", ->
|
||||
it "clips positions inside a placeholder to the end of the placeholder", ->
|
||||
expect(folder.clipScreenPosition([4, 29], true)).toEqual [4, 29]
|
||||
expect(folder.clipScreenPosition([4, 30], true)).toEqual [4, 32]
|
||||
expect(folder.clipScreenPosition([4, 31], true)).toEqual [4, 32]
|
||||
expect(folder.clipScreenPosition([4, 32], true)).toEqual [4, 32]
|
||||
expect(folder.clipScreenPosition([4, 29], skipAtomicTokens: true)).toEqual [4, 29]
|
||||
expect(folder.clipScreenPosition([4, 30], skipAtomicTokens: true)).toEqual [4, 32]
|
||||
expect(folder.clipScreenPosition([4, 31], skipAtomicTokens: true)).toEqual [4, 32]
|
||||
expect(folder.clipScreenPosition([4, 32], skipAtomicTokens: true)).toEqual [4, 32]
|
||||
|
||||
|
||||
|
||||
|
@ -237,7 +237,7 @@ describe "LineWrapper", ->
|
||||
expect(line2.endColumn).toBe 14
|
||||
expect(line2.text.length).toBe 3
|
||||
|
||||
describe ".clipScreenPosition(screenPosition, eagerWrap=false)", ->
|
||||
describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
|
||||
it "allows valid positions", ->
|
||||
expect(wrapper.clipScreenPosition([4, 5])).toEqual [4, 5]
|
||||
expect(wrapper.clipScreenPosition([4, 11])).toEqual [4, 11]
|
||||
@ -251,32 +251,42 @@ describe "LineWrapper", ->
|
||||
expect(wrapper.clipScreenPosition([1000, 0])).toEqual [15, 2]
|
||||
expect(wrapper.clipScreenPosition([1000, 1000])).toEqual [15, 2]
|
||||
|
||||
it "wraps positions at the end of soft-wrapped lines to the next screen line", ->
|
||||
expect(wrapper.clipScreenPosition([3, 51])).toEqual [4, 0]
|
||||
expect(wrapper.clipScreenPosition([3, 58])).toEqual [4, 0]
|
||||
expect(wrapper.clipScreenPosition([3, 1000])).toEqual [4, 0]
|
||||
|
||||
describe "when eagerWrap is false (the default)", ->
|
||||
it "wraps positions beyond the end of hard lines to the end of the line", ->
|
||||
describe "when wrapBeyondNewlines is false (the default)", ->
|
||||
it "wraps positions beyond the end of hard newlines to the end of the line", ->
|
||||
expect(wrapper.clipScreenPosition([1, 10000])).toEqual [1, 30]
|
||||
expect(wrapper.clipScreenPosition([4, 30])).toEqual [4, 11]
|
||||
expect(wrapper.clipScreenPosition([4, 1000])).toEqual [4, 11]
|
||||
|
||||
describe "when wrapBeyondNewlines is true", ->
|
||||
it "wraps positions past the end of hard newlines to the next line", ->
|
||||
expect(wrapper.clipScreenPosition([0, 29], wrapBeyondNewlines: true)).toEqual [0, 29]
|
||||
expect(wrapper.clipScreenPosition([0, 30], wrapBeyondNewlines: true)).toEqual [1, 0]
|
||||
expect(wrapper.clipScreenPosition([0, 1000], wrapBeyondNewlines: true)).toEqual [1, 0]
|
||||
|
||||
describe "when wrapAtSoftNewlines is false (the default)", ->
|
||||
it "wraps positions at the end of soft-wrapped lines to the character preceding the end of the line", ->
|
||||
expect(wrapper.clipScreenPosition([3, 50])).toEqual [3, 50]
|
||||
expect(wrapper.clipScreenPosition([3, 51])).toEqual [3, 50]
|
||||
expect(wrapper.clipScreenPosition([3, 58])).toEqual [3, 50]
|
||||
expect(wrapper.clipScreenPosition([3, 1000])).toEqual [3, 50]
|
||||
|
||||
describe "when wrapAtSoftNewlines is true", ->
|
||||
it "wraps positions at the end of soft-wrapped lines to the next screen line", ->
|
||||
expect(wrapper.clipScreenPosition([3, 50], wrapAtSoftNewlines: true)).toEqual [3, 50]
|
||||
expect(wrapper.clipScreenPosition([3, 51], wrapAtSoftNewlines: true)).toEqual [4, 0]
|
||||
expect(wrapper.clipScreenPosition([3, 58], wrapAtSoftNewlines: true)).toEqual [4, 0]
|
||||
expect(wrapper.clipScreenPosition([3, 1000], wrapAtSoftNewlines: true)).toEqual [4, 0]
|
||||
|
||||
describe "when skipAtomicTokens is false (the default)", ->
|
||||
it "clips screen positions in the middle of fold placeholders to the to the beginning of fold placeholders", ->
|
||||
folder.createFold(new Range([3, 55], [3, 59]))
|
||||
expect(wrapper.clipScreenPosition([4, 5])).toEqual [4, 4]
|
||||
expect(wrapper.clipScreenPosition([4, 6])).toEqual [4, 4]
|
||||
expect(wrapper.clipScreenPosition([4, 7])).toEqual [4, 7]
|
||||
|
||||
describe "when eagerWrap is true", ->
|
||||
it "wraps positions past the end of non-softwrapped lines to the next line", ->
|
||||
expect(wrapper.clipScreenPosition([0, 29], true)).toEqual [0, 29]
|
||||
expect(wrapper.clipScreenPosition([0, 30], true)).toEqual [1, 0]
|
||||
expect(wrapper.clipScreenPosition([0, 1000], true)).toEqual [1, 0]
|
||||
|
||||
describe "when skipAtomicTokens is true", ->
|
||||
it "wraps the screen positions in the middle of fold placeholders to the end of the placeholder", ->
|
||||
folder.createFold(new Range([3, 55], [3, 59]))
|
||||
expect(wrapper.clipScreenPosition([4, 4], true)).toEqual [4, 4]
|
||||
expect(wrapper.clipScreenPosition([4, 5], true)).toEqual [4, 7]
|
||||
expect(wrapper.clipScreenPosition([4, 6], true)).toEqual [4, 7]
|
||||
|
||||
expect(wrapper.clipScreenPosition([4, 4], skipAtomicTokens: true)).toEqual [4, 4]
|
||||
expect(wrapper.clipScreenPosition([4, 5], skipAtomicTokens: true)).toEqual [4, 7]
|
||||
expect(wrapper.clipScreenPosition([4, 6], skipAtomicTokens: true)).toEqual [4, 7]
|
||||
|
@ -69,15 +69,16 @@ class Cursor extends View
|
||||
|
||||
moveRight: ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
@setScreenPosition(@editor.clipScreenPosition([row, column + 1], true))
|
||||
@setScreenPosition(@editor.clipScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true))
|
||||
|
||||
moveLeft: ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
|
||||
if column > 0
|
||||
column--
|
||||
else if row > 0
|
||||
else
|
||||
row--
|
||||
column = @editor.buffer.getLine(row).length
|
||||
column = Infinity
|
||||
|
||||
@setScreenPosition({row, column})
|
||||
|
||||
|
@ -134,8 +134,8 @@ class LineFolder
|
||||
bufferPositionForScreenPosition: (screenPosition) ->
|
||||
@lineMap.bufferPositionForScreenPosition(screenPosition)
|
||||
|
||||
clipScreenPosition: (screenPosition, eagerWrap=false) ->
|
||||
@lineMap.clipScreenPosition(screenPosition, eagerWrap)
|
||||
clipScreenPosition: (screenPosition, options={}) ->
|
||||
@lineMap.clipScreenPosition(screenPosition, options)
|
||||
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
@lineMap.screenRangeForBufferRange(bufferRange)
|
||||
|
@ -141,7 +141,10 @@ class LineMap
|
||||
end = @bufferPositionForScreenPosition(screenRange.end)
|
||||
new Range(start, end)
|
||||
|
||||
clipScreenPosition: (screenPosition, eagerWrap) ->
|
||||
clipScreenPosition: (screenPosition, options) ->
|
||||
wrapBeyondNewlines = options.wrapBeyondNewlines ? false
|
||||
wrapAtSoftNewlines = options.wrapAtSoftNewlines ? false
|
||||
skipAtomicTokens = options.skipAtomicTokens ? false
|
||||
screenPosition = Point.fromObject(screenPosition)
|
||||
|
||||
screenPosition.column = Math.max(0, screenPosition.column)
|
||||
@ -150,29 +153,33 @@ class LineMap
|
||||
screenPosition.row = 0
|
||||
screenPosition.column = 0
|
||||
|
||||
maxRow = @lastScreenRow()
|
||||
if screenPosition.row > maxRow
|
||||
screenPosition.row = maxRow
|
||||
if screenPosition.row > @lastScreenRow()
|
||||
screenPosition.row = @lastScreenRow()
|
||||
screenPosition.column = Infinity
|
||||
|
||||
screenDelta = new Point
|
||||
for screenLine in @screenLines
|
||||
nextDelta = screenDelta.add(screenLine.screenDelta)
|
||||
for lineFragment in @screenLines
|
||||
nextDelta = screenDelta.add(lineFragment.screenDelta)
|
||||
break if nextDelta.isGreaterThan(screenPosition)
|
||||
screenDelta = nextDelta
|
||||
|
||||
if screenLine.isAtomic
|
||||
if eagerWrap and screenPosition.column > screenDelta.column
|
||||
screenDelta.column = screenDelta.column + screenLine.text.length
|
||||
else
|
||||
maxColumn = screenDelta.column + screenLine.text.length
|
||||
if eagerWrap and screenPosition.column > maxColumn
|
||||
screenDelta.row++
|
||||
screenDelta.column = 0
|
||||
if lineFragment.isAtomic
|
||||
if skipAtomicTokens and screenPosition.column > screenDelta.column
|
||||
return new Point(screenDelta.row, screenDelta.column + lineFragment.text.length)
|
||||
else
|
||||
screenDelta.column = Math.min(maxColumn, screenPosition.column)
|
||||
return screenDelta
|
||||
|
||||
screenDelta
|
||||
maxColumn = screenDelta.column + lineFragment.text.length
|
||||
if lineFragment.isSoftWrapped() and screenPosition.column >= maxColumn
|
||||
if wrapAtSoftNewlines
|
||||
return new Point(screenDelta.row + 1, 0)
|
||||
else
|
||||
return new Point(screenDelta.row, maxColumn - 1)
|
||||
|
||||
if screenPosition.column > maxColumn and wrapBeyondNewlines
|
||||
return new Point(screenDelta.row + 1, 0)
|
||||
|
||||
new Point(screenDelta.row, Math.min(maxColumn, screenPosition.column))
|
||||
|
||||
logLines: (start=0, end=@screenLineCount() - 1)->
|
||||
for row in [start..end]
|
||||
|
@ -93,13 +93,11 @@ class LineWrapper
|
||||
@lineFolder.bufferRangeForScreenRange(
|
||||
@lineMap.bufferRangeForScreenRange(screenRange))
|
||||
|
||||
clipScreenPosition: (screenPosition, eagerWrap=false) ->
|
||||
clipScreenPosition: (screenPosition, options={}) ->
|
||||
@lineMap.screenPositionForBufferPosition(
|
||||
@lineFolder.clipScreenPosition(
|
||||
@lineMap.bufferPositionForScreenPosition(
|
||||
@lineMap.clipScreenPosition(screenPosition, eagerWrap)
|
||||
),
|
||||
eagerWrap
|
||||
@lineMap.bufferPositionForScreenPosition(@lineMap.clipScreenPosition(screenPosition, options)),
|
||||
options
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -52,5 +52,8 @@ class ScreenLineFragment
|
||||
else
|
||||
@text.length
|
||||
|
||||
isSoftWrapped: ->
|
||||
@screenDelta.row == 1 and @bufferDelta.row == 0
|
||||
|
||||
isEqual: (other) ->
|
||||
_.isEqual(@tokens, other.tokens) and @screenDelta.isEqual(other.screenDelta) and @bufferDelta.isEqual(other.bufferDelta)
|
||||
|
Loading…
Reference in New Issue
Block a user