Merge remote-tracking branch 'origin/folding' into chrome

This commit is contained in:
Corey Johnson & Nathan Sobo
2012-02-28 16:07:35 -08:00
12 changed files with 322 additions and 134 deletions

View File

@@ -65,13 +65,13 @@ describe "Editor", ->
expect(editor.lines.find('pre:eq(3)').text()).toBe " var pivot = items.shift(), current, left = [], "
expect(editor.lines.find('pre:eq(4)').text()).toBe "right = [];"
editor.cursor.setScreenPosition([3, 51])
editor.cursor.setBufferPosition([3, 51])
expect(editor.cursor.position()).toEqual(editor.lines.find('pre:eq(4)').position())
editor.cursor.setScreenPosition([4, 0])
editor.cursor.setBufferPosition([4, 0])
expect(editor.cursor.position()).toEqual(editor.lines.find('pre:eq(5)').position())
editor.selection.setRange(new Range([6, 30], [6, 55]))
editor.selection.setBufferRange(new Range([6, 30], [6, 55]))
[region1, region2] = editor.selection.regions
expect(region1.position().top).toBe(editor.lines.find('.line:eq(7)').position().top)
expect(region2.position().top).toBe(editor.lines.find('.line:eq(8)').position().top)
@@ -98,6 +98,31 @@ describe "Editor", ->
$(window).trigger 'resize'
expect(editor.setMaxLineLength).not.toHaveBeenCalled()
it "allows the cursor to move down to the last line", ->
_.times editor.lastScreenRow(), -> editor.moveCursorDown()
expect(editor.getCursorScreenPosition()).toEqual [editor.lastScreenRow(), 0]
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})", ->
beforeEach ->
@@ -333,7 +358,7 @@ describe "Editor", ->
[pageX, pageY] = window.pixelPositionForPoint(editor, [4, 7])
editor.lines.trigger mousedownEvent({pageX, pageY})
expect(editor.getCursorScreenPosition()).toEqual(row: 3, column: 58)
expect(editor.getCursorBufferPosition()).toEqual(row: 3, column: 58)
describe "when soft-wrap is disabled", ->
describe "when it is a single click", ->
@@ -394,33 +419,33 @@ describe "Editor", ->
editor.trigger keydownEvent('right', shiftKey: true)
expect(selection.isEmpty()).toBeFalsy()
range = selection.getRange()
range = selection.getScreenRange()
expect(range.start).toEqual(row: 1, column: 6)
expect(range.end).toEqual(row: 1, column: 7)
editor.trigger keydownEvent('right', shiftKey: true)
range = selection.getRange()
range = selection.getScreenRange()
expect(range.start).toEqual(row: 1, column: 6)
expect(range.end).toEqual(row: 1, column: 8)
editor.trigger keydownEvent('down', shiftKey: true)
range = selection.getRange()
range = selection.getScreenRange()
expect(range.start).toEqual(row: 1, column: 6)
expect(range.end).toEqual(row: 2, column: 8)
editor.trigger keydownEvent('left', shiftKey: true)
range = selection.getRange()
range = selection.getScreenRange()
expect(range.start).toEqual(row: 1, column: 6)
expect(range.end).toEqual(row: 2, column: 7)
editor.trigger keydownEvent('up', shiftKey: true)
range = selection.getRange()
range = selection.getScreenRange()
expect(range.start).toEqual(row: 1, column: 6)
expect(range.end).toEqual(row: 1, column: 7)
describe "when the arrow keys are pressed without the shift modifier", ->
makeNonEmpty = ->
selection.setRange(new Range({row: 1, column: 2}, {row: 1, column: 5}))
selection.setBufferRange(new Range({row: 1, column: 2}, {row: 1, column: 5}))
expect(selection.isEmpty()).toBeFalsy()
it "clears the selection", ->
@@ -453,7 +478,7 @@ describe "Editor", ->
[pageX, pageY] = window.pixelPositionForPoint(editor, [5, 27])
editor.lines.trigger mousemoveEvent({pageX, pageY})
range = editor.selection.getRange()
range = editor.selection.getScreenRange()
expect(range.start).toEqual({row: 4, column: 10})
expect(range.end).toEqual({row: 5, column: 27})
expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27)
@@ -465,7 +490,7 @@ describe "Editor", ->
[pageX, pageY] = window.pixelPositionForPoint(editor, [8, 8])
editor.lines.trigger mousemoveEvent({pageX, pageY})
range = editor.selection.getRange()
range = editor.selection.getScreenRange()
expect(range.start).toEqual({row: 4, column: 10})
expect(range.end).toEqual({row: 5, column: 27})
expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27)
@@ -484,7 +509,7 @@ describe "Editor", ->
[pageX, pageY] = window.pixelPositionForPoint(editor, [5, 27])
editor.lines.trigger mousemoveEvent({pageX, pageY})
range = editor.selection.getRange()
range = editor.selection.getScreenRange()
expect(range.start).toEqual({row: 4, column: 4})
expect(range.end).toEqual({row: 5, column: 27})
expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27)
@@ -496,7 +521,7 @@ describe "Editor", ->
[pageX, pageY] = window.pixelPositionForPoint(editor, [8, 8])
editor.lines.trigger mousemoveEvent({pageX, pageY})
range = editor.selection.getRange()
range = editor.selection.getScreenRange()
expect(range.start).toEqual({row: 4, column: 4})
expect(range.end).toEqual({row: 5, column: 27})
expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27)
@@ -518,7 +543,7 @@ describe "Editor", ->
[pageX, pageY] = window.pixelPositionForPoint(editor, [5, 27])
editor.lines.trigger mousemoveEvent({pageX, pageY})
range = editor.selection.getRange()
range = editor.selection.getScreenRange()
expect(range.start).toEqual({row: 4, column: 0})
expect(range.end).toEqual({row: 5, column: 27})
expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27)
@@ -530,7 +555,7 @@ describe "Editor", ->
[pageX, pageY] = window.pixelPositionForPoint(editor, [8, 8])
editor.lines.trigger mousemoveEvent({pageX, pageY})
range = editor.selection.getRange()
range = editor.selection.getScreenRange()
expect(range.start).toEqual({row: 4, column: 0})
expect(range.end).toEqual({row: 5, column: 27})
expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27)
@@ -551,7 +576,7 @@ describe "Editor", ->
describe "when there is a selection", ->
it "replaces the selected text with the typed text", ->
editor.selection.setRange(new Range([1, 6], [2, 4]))
editor.selection.setBufferRange(new Range([1, 6], [2, 4]))
editor.hiddenInput.textInput 'q'
expect(buffer.getLine(1)).toBe ' var qif (items.length <= 1) return items;'
@@ -625,7 +650,7 @@ describe "Editor", ->
describe "when there is a selection", ->
it "deletes the selection, but not the character before it", ->
editor.selection.setRange(new Range([0,5], [0,9]))
editor.selection.setBufferRange(new Range([0,5], [0,9]))
editor.trigger keydownEvent('backspace')
expect(editor.buffer.getLine(0)).toBe 'var qsort = function () {'
@@ -644,7 +669,7 @@ describe "Editor", ->
describe "when there is a selection", ->
it "deletes the selection, but not the character following it", ->
editor.selection.setRange(new Range([1,6], [1,8]))
editor.selection.setBufferRange(new Range([1,6], [1,8]))
editor.trigger keydownEvent 'delete'
expect(buffer.getLine(1)).toBe ' var rt = function(items) {'
@@ -697,12 +722,12 @@ describe "Editor", ->
it "sets the cursor to the beginning of the file", ->
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
describe ".clipPosition(point)", ->
describe ".clipScreenPosition(point)", ->
it "selects the nearest valid position to the given point", ->
expect(editor.clipPosition(row: 1000, column: 0)).toEqual(row: buffer.lastRow(), column: buffer.getLine(buffer.lastRow()).length)
expect(editor.clipPosition(row: -5, column: 0)).toEqual(row: 0, column: 0)
expect(editor.clipPosition(row: 1, column: 10000)).toEqual(row: 1, column: buffer.getLine(1).length)
expect(editor.clipPosition(row: 1, column: -5)).toEqual(row: 1, column: 0)
expect(editor.clipScreenPosition(row: 1000, column: 0)).toEqual(row: buffer.lastRow(), column: buffer.getLine(buffer.lastRow()).length)
expect(editor.clipScreenPosition(row: -5, column: 0)).toEqual(row: 0, column: 0)
expect(editor.clipScreenPosition(row: 1, column: 10000)).toEqual(row: 1, column: buffer.getLine(1).length)
expect(editor.clipScreenPosition(row: 1, column: -5)).toEqual(row: 1, column: 0)
describe "cut, copy & paste", ->
beforeEach ->
@@ -711,14 +736,14 @@ describe "Editor", ->
describe "when a cut event is triggered", ->
it "removes the selected text from the buffer and places it on the pasteboard", ->
editor.getSelection().setRange new Range([0,4], [0,9])
editor.getSelection().setBufferRange new Range([0,4], [0,9])
editor.trigger "cut"
expect(editor.buffer.getLine(0)).toBe "var sort = function () {"
expect(atom.native.readFromPasteboard()).toBe 'quick'
describe "when a copy event is triggered", ->
it "copies selected text onto the clipboard", ->
editor.getSelection().setRange new Range([0,4], [0, 13])
editor.getSelection().setBufferRange new Range([0,4], [0, 13])
editor.trigger "copy"
expect(atom.native.readFromPasteboard()).toBe 'quicksort'
@@ -729,14 +754,30 @@ describe "Editor", ->
expect(editor.buffer.getLine(0)).toBe "var firstquicksort = function () {"
expect(editor.buffer.getLine(1)).toBe " var sort = function(items) {"
editor.getSelection().setRange new Range([1,6], [1,10])
editor.getSelection().setBufferRange new Range([1,6], [1,10])
editor.trigger "paste"
expect(editor.buffer.getLine(1)).toBe " var first = function(items) {"
describe "folding", ->
describe "when a fold-selection event is triggered", ->
it "folds the selected text and renders a placeholder for it", ->
editor.selection.setRange(new Range([4, 29], [7, 4]))
it "folds the selected text and moves the cursor to just after the placeholder", ->
editor.selection.setBufferRange(new Range([4, 29], [7, 4]))
editor.trigger 'fold-selection'
expect(editor.lines.find('.line:eq(4)').text()).toBe ' while(items.length > 0) {...}'
expect(editor.selection.isEmpty()).toBeTruthy()
expect(editor.getCursorScreenPosition()).toEqual [4, 32]
editor.setCursorScreenPosition([9, 2])
expect(editor.getCursorScreenPosition()).toEqual [9, 2]
buffer.insert([9, 4], 'x')
expect(editor.getCursorScreenPosition()).toEqual [6, 5]
expect(editor.getCursorBufferPosition()).toEqual [9, 5]
editor.setCursorScreenPosition([4, 30])
expect(editor.getCursorScreenPosition()).toEqual [4, 29]
editor.moveCursorRight()
expect(editor.getCursorScreenPosition()).toEqual [4, 32]

View File

@@ -312,22 +312,33 @@ describe "LineFolder", ->
expect(folder.bufferPositionForScreenPosition([4, 5])).toEqual [4, 5]
expect(folder.bufferPositionForScreenPosition([4, 13])).toEqual [4, 15]
expect(folder.bufferPositionForScreenPosition([4, 18])).toEqual [4, 20]
describe ".clipScreenPosition(screenPosition)", ->
describe ".clipScreenPosition(screenPosition, eagerWrap=false)", ->
beforeEach ->
folder.createFold(new Range([4, 29], [7, 4]))
it "returns the nearest valid position based on the current screen lines", ->
expect(folder.clipScreenPosition([-1, -1])).toEqual [0, 0]
expect(folder.clipScreenPosition([0, -1])).toEqual [0, 0]
expect(folder.clipScreenPosition([-1, 5])).toEqual [0, 0]
expect(folder.clipScreenPosition([1, 10000])).toEqual [1, 30]
expect(folder.clipScreenPosition([2, 15])).toEqual [2, 15]
expect(folder.clipScreenPosition([4, 32])).toEqual [4, 32]
expect(folder.clipScreenPosition([4, 1000])).toEqual [4, 33]
expect(folder.clipScreenPosition([1000, 1000])).toEqual [10, 2]
expect(folder.clipScreenPosition([1000, 1000])).toEqual [9, 2]
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]
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 skipAtomicTokens is true", ->
it "clips positions inside a placeholder to the end of the placeholder", ->
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]

View File

@@ -129,6 +129,7 @@ describe "LineWrapper", ->
it "adjusts the position to account for the fold", ->
fold = folder.createFold(new Range([4, 29], [7, 4]))
expect(wrapper.screenPositionForBufferPosition([7, 4])).toEqual [5, 32]
expect(wrapper.screenPositionForBufferPosition([8, 12])).toEqual [6, 12]
describe ".bufferPositionForScreenPosition(point)", ->
it "translates the given screen position to a buffer position, account for wrapped lines", ->
@@ -147,6 +148,7 @@ describe "LineWrapper", ->
it "adjusts the position to account for the fold", ->
fold = folder.createFold(new Range([4, 29], [7, 4]))
expect(wrapper.bufferPositionForScreenPosition([5, 32])).toEqual [7, 4]
expect(wrapper.bufferPositionForScreenPosition([6, 12])).toEqual [8, 12]
describe ".wrapScreenLine(screenLine)", ->
makeTokens = (tokenValues...) ->
@@ -235,3 +237,56 @@ describe "LineWrapper", ->
expect(line2.endColumn).toBe 14
expect(line2.text.length).toBe 3
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]
it "disallows negative positions", ->
expect(wrapper.clipScreenPosition([-1, -1])).toEqual [0, 0]
expect(wrapper.clipScreenPosition([-1, 10])).toEqual [0, 0]
expect(wrapper.clipScreenPosition([0, -1])).toEqual [0, 0]
it "disallows positions beyond the last row", ->
expect(wrapper.clipScreenPosition([1000, 0])).toEqual [15, 2]
expect(wrapper.clipScreenPosition([1000, 1000])).toEqual [15, 2]
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 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], 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]

View File

@@ -12,34 +12,34 @@ describe "Selection", ->
editor.setBuffer(buffer)
selection = editor.selection
describe ".setRange(range)", ->
describe ".setBufferRange(range)", ->
it "places the anchor at the start of the range and the cursor at the end", ->
range = new Range({row: 2, column: 7}, {row: 3, column: 18})
selection.setRange(range)
selection.setBufferRange(range)
expect(selection.anchor.getScreenPosition()).toEqual range.start
expect(selection.cursor.getScreenPosition()).toEqual range.end
describe ".delete()", ->
describe "when nothing is selected", ->
it "deletes nothing", ->
selection.setRange new Range([0,3], [0,3])
selection.setBufferRange new Range([0,3], [0,3])
selection.delete()
expect(editor.buffer.getLine(0)).toBe "var quicksort = function () {"
describe "when one line is selected", ->
it "deletes selected text", ->
selection.setRange new Range([0,4], [0,14])
selection.setBufferRange new Range([0,4], [0,14])
selection.delete()
expect(editor.buffer.getLine(0)).toBe "var = function () {"
endOfLine = editor.buffer.getLine(0).length
selection.setRange new Range([0,0], [0, endOfLine])
selection.setBufferRange new Range([0,0], [0, endOfLine])
selection.delete()
expect(editor.buffer.getLine(0)).toBe ""
describe "when multiple lines are selected", ->
it "deletes selected text", ->
selection.setRange new Range([0,1], [2,39])
selection.setBufferRange new Range([0,1], [2,39])
selection.delete()
expect(editor.buffer.getLine(0)).toBe "v;"
@@ -53,7 +53,7 @@ describe "Selection", ->
describe "when the selection is within a single line", ->
it "covers the selection's range with a single region", ->
selection.setRange(new Range({row: 2, column: 7}, {row: 2, column: 25}))
selection.setBufferRange(new Range({row: 2, column: 7}, {row: 2, column: 25}))
expect(selection.regions.length).toBe 1
region = selection.regions[0]
@@ -64,7 +64,7 @@ describe "Selection", ->
describe "when the selection spans 2 lines", ->
it "covers the selection's range with 2 regions", ->
selection.setRange(new Range({row: 2, column: 7}, {row: 3, column: 25}))
selection.setBufferRange(new Range({row: 2, column: 7}, {row: 3, column: 25}))
expect(selection.regions.length).toBe 2
@@ -82,7 +82,7 @@ describe "Selection", ->
describe "when the selection spans more than 2 lines", ->
it "covers the selection's range with 3 regions", ->
selection.setRange(new Range({row: 2, column: 7}, {row: 6, column: 25}))
selection.setBufferRange(new Range({row: 2, column: 7}, {row: 6, column: 25}))
expect(selection.regions.length).toBe 3
@@ -110,7 +110,7 @@ describe "Selection", ->
expect(region3.width()).toBe(25 * charWidth)
it "clears previously drawn regions before creating new ones", ->
selection.setRange(new Range({row: 2, column: 7}, {row: 4, column: 25}))
selection.setBufferRange(new Range({row: 2, column: 7}, {row: 4, column: 25}))
expect(selection.regions.length).toBe 3
expect(selection.find('.selection').length).toBe 3
@@ -124,19 +124,19 @@ describe "Selection", ->
expect(atom.native.readFromPasteboard()).toBe 'first'
it "removes selected text from the buffer and places it on the clipboard", ->
selection.setRange new Range([0,4], [0,13])
selection.setBufferRange new Range([0,4], [0,13])
selection.cut()
expect(atom.native.readFromPasteboard()).toBe 'quicksort'
expect(editor.buffer.getLine(0)).toBe "var = function () {"
expect(selection.isEmpty()).toBeTruthy()
selection.setRange new Range([1,6], [3,8])
selection.setBufferRange new Range([1,6], [3,8])
selection.cut()
expect(atom.native.readFromPasteboard()).toBe "sort = function(items) {\n if (items.length <= 1) return items;\n var "
expect(editor.buffer.getLine(1)).toBe " var pivot = items.shift(), current, left = [], right = [];"
it "places nothing on the clipboard when there is no selection", ->
selection.setRange new Range([0,4], [0,4])
selection.setBufferRange new Range([0,4], [0,4])
selection.copy()
expect(atom.native.readFromPasteboard()).toBe 'first'
@@ -146,16 +146,16 @@ describe "Selection", ->
expect(atom.native.readFromPasteboard()).toBe 'first'
it "places selected text on the clipboard", ->
selection.setRange new Range([0,4], [0,13])
selection.setBufferRange new Range([0,4], [0,13])
selection.copy()
expect(atom.native.readFromPasteboard()).toBe 'quicksort'
selection.setRange new Range([0,4], [3,13])
selection.setBufferRange new Range([0,4], [3,13])
selection.copy()
expect(atom.native.readFromPasteboard()).toBe "quicksort = function () {\n var sort = function(items) {\n if (items.length <= 1) return items;\n var pivot"
it "places nothing on the clipboard when there is no selection", ->
selection.setRange new Range([0,4], [0,4])
selection.setBufferRange new Range([0,4], [0,4])
selection.copy()
expect(atom.native.readFromPasteboard()).toBe 'first'

View File

@@ -13,11 +13,11 @@ class Cursor extends View
@one 'attach', => @updateAppearance()
bufferChanged: (e) ->
@setScreenPosition(e.newRange.end)
@setBufferPosition(e.newRange.end)
setScreenPosition: (point) ->
point = Point.fromObject(point)
@$position = @editor.clipPosition(point)
setScreenPosition: (position) ->
position = Point.fromObject(position)
@screenPosition = @editor.clipScreenPosition(position)
@goalColumn = null
@updateAppearance()
@trigger 'cursor:position-changed'
@@ -26,7 +26,13 @@ class Cursor extends View
window.clearTimeout(@idleTimeout) if @idleTimeout
@idleTimeout = window.setTimeout (=> @addClass 'idle'), 200
getScreenPosition: -> _.clone(@$position)
setBufferPosition: (bufferPosition) ->
@setScreenPosition(@editor.screenPositionForBufferPosition(bufferPosition))
getBufferPosition: ->
@editor.bufferPositionForScreenPosition(@getScreenPosition())
getScreenPosition: -> _.clone(@screenPosition)
getColumn: ->
@getScreenPosition().column
@@ -44,21 +50,13 @@ class Cursor extends View
moveUp: ->
{ row, column } = @getScreenPosition()
column = @goalColumn if @goalColumn?
if row > 0
@setScreenPosition({row: row - 1, column: column})
else
@moveToLineStart()
@setScreenPosition({row: row - 1, column: column})
@goalColumn = column
moveDown: ->
{ row, column } = @getScreenPosition()
column = @goalColumn if @goalColumn?
if row < @editor.buffer.numLines() - 1
@setScreenPosition({row: row + 1, column: column})
else
@moveToLineEnd()
@setScreenPosition({row: row + 1, column: column})
@goalColumn = column
moveToLineEnd: ->
@@ -71,20 +69,16 @@ class Cursor extends View
moveRight: ->
{ row, column } = @getScreenPosition()
if column < @editor.buffer.getLine(row).length
column++
else if row < @editor.buffer.numLines() - 1
row++
column = 0
@setScreenPosition({row, column})
@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})
@@ -111,7 +105,7 @@ class Cursor extends View
@setScreenPosition [row, column + offset]
updateAppearance: ->
position = @editor.pixelPositionFromPoint(@getScreenPosition())
position = @editor.pixelPositionForScreenPosition(@getScreenPosition())
@css(position)
@autoScrollVertically(position)
@autoScrollHorizontally(position)

View File

@@ -95,7 +95,7 @@ class Editor extends View
clickCount = e.originalEvent.detail
if clickCount == 1
@setCursorScreenPosition @pointFromMouseEvent(e)
@setCursorScreenPosition @screenPositionFromMouseEvent(e)
else if clickCount == 2
@selection.selectWord()
else if clickCount >= 3
@@ -107,7 +107,7 @@ class Editor extends View
@insertText(e.originalEvent.data)
@on 'cursor:position-changed', =>
@hiddenInput.css(@pixelPositionFromPoint(@cursor.getScreenPosition()))
@hiddenInput.css(@pixelPositionForScreenPosition(@cursor.getScreenPosition()))
@one 'attach', =>
@calculateDimensions()
@@ -116,7 +116,7 @@ class Editor extends View
@focus()
selectTextOnMouseMovement: ->
moveHandler = (e) => @selectToPosition(@pointFromMouseEvent(e))
moveHandler = (e) => @selectToScreenPosition(@screenPositionFromMouseEvent(e))
@on 'mousemove', moveHandler
$(document).one 'mouseup', => @off 'mousemove', moveHandler
@@ -132,9 +132,21 @@ class Editor extends View
renderLines: ->
@lines.empty()
for screenLine in @lineWrapper.getLines()
for screenLine in @getScreenLines()
@lines.append @buildLineElement(screenLine)
getScreenLines: ->
@lineWrapper.getLines()
linesForScreenRows: (start, end) ->
@lineWrapper.linesForScreenRows(start, end)
screenLineCount: ->
@lineWrapper.lineCount()
lastScreenRow: ->
@screenLineCount() - 1
setBuffer: (@buffer) ->
@highlighter = new Highlighter(@buffer)
@lineFolder = new LineFolder(@highlighter)
@@ -146,9 +158,12 @@ class Editor extends View
@buffer.on 'change', (e) =>
@cursor.bufferChanged(e)
@lineFolder.on 'fold', (range) =>
@setCursorBufferPosition(range.end)
@lineWrapper.on 'change', (e) =>
{ oldRange, newRange } = e
screenLines = @lineWrapper.linesForScreenRows(newRange.start.row, newRange.end.row)
screenLines = @linesForScreenRows(newRange.start.row, newRange.end.row)
if newRange.end.row > oldRange.end.row
# update, then insert elements
for row in [newRange.start.row..newRange.end.row]
@@ -202,27 +217,30 @@ class Editor extends View
else
$(window).off 'resize', @_setMaxLineLength
clipPosition: ({row, column}) ->
if row > @buffer.lastRow()
row = @buffer.lastRow()
column = @buffer.getLine(row).length
else
row = Math.min(Math.max(0, row), @buffer.numLines() - 1)
column = Math.min(Math.max(0, column), @buffer.getLine(row).length)
clipScreenPosition: (screenPosition, eagerWrap=false) ->
@lineWrapper.clipScreenPosition(screenPosition, eagerWrap)
new Point(row, column)
pixelPositionFromPoint: (position) ->
{ row, column } = @lineWrapper.screenPositionForBufferPosition(position)
pixelPositionForScreenPosition: ({row, column}) ->
{ top: row * @lineHeight, left: column * @charWidth }
pointFromPixelPosition: ({top, left}) ->
screenPositionFromPixelPosition: ({top, left}) ->
screenPosition = new Point(Math.floor(top / @lineHeight), Math.floor(left / @charWidth))
@lineWrapper.bufferPositionForScreenPosition screenPosition
pointFromMouseEvent: (e) ->
screenPositionForBufferPosition: (position) ->
@lineWrapper.screenPositionForBufferPosition(position)
bufferPositionForScreenPosition: (position) ->
@lineWrapper.bufferPositionForScreenPosition(position)
screenRangeForBufferRange: (range) ->
@lineWrapper.screenRangeForBufferRange(range)
bufferRangeForScreenRange: (range) ->
@lineWrapper.bufferRangeForScreenRange(range)
screenPositionFromMouseEvent: (e) ->
{ pageX, pageY } = e
@pointFromPixelPosition
@screenPositionFromPixelPosition
top: pageY - @lines.offset().top
left: pageX - @lines.offset().left
@@ -254,8 +272,10 @@ class Editor extends View
moveCursorDown: -> @cursor.moveDown()
moveCursorRight: -> @cursor.moveRight()
moveCursorLeft: -> @cursor.moveLeft()
setCursorScreenPosition: (point) -> @cursor.setScreenPosition(point)
setCursorScreenPosition: (position) -> @cursor.setScreenPosition(position)
getCursorScreenPosition: -> @cursor.getScreenPosition()
setCursorBufferPosition: (position) -> @cursor.setBufferPosition(position)
getCursorBufferPosition: -> @cursor.getBufferPosition()
setCursorRow: (row) -> @cursor.setRow(row)
getCursorRow: -> @cursor.getRow()
setCursorColumn: (column) -> @cursor.setColumn(column)
@@ -265,8 +285,10 @@ class Editor extends View
selectLeft: -> @selection.selectLeft()
selectUp: -> @selection.selectUp()
selectDown: -> @selection.selectDown()
selectToPosition: (position) ->
@selection.selectToPosition(position)
selectToScreenPosition: (position) ->
@selection.selectToScreenPosition(position)
selectToBufferPosition: (position) ->
@selection.selectToBufferPosition(position)
insertText: (text) -> @selection.insertText(text)
insertNewline: -> @selection.insertNewline()

View File

@@ -21,9 +21,7 @@ class LineFolder
@lineMap.insertAtBufferRow(0, @highlighter.screenLines)
logLines: (start=0, end=@lastRow())->
for row in [start..end]
line = @lineForScreenRow(row).text
console.log row, line, line.length
@lineMap.logLines(start, end)
createFold: (bufferRange) ->
fold = new Fold(this, bufferRange)
@@ -116,7 +114,7 @@ class LineFolder
@lineMap.lineForScreenRow(screenRow)
getLines: ->
@lineMap.getScreenLines()
@lineMap.screenLinesForRows(0, @lastRow())
lineCount: ->
@lineMap.screenLineCount()
@@ -136,12 +134,15 @@ class LineFolder
bufferPositionForScreenPosition: (screenPosition) ->
@lineMap.bufferPositionForScreenPosition(screenPosition)
clipScreenPosition: (screenPosition) ->
@lineMap.clipScreenPosition(screenPosition)
clipScreenPosition: (screenPosition, options={}) ->
@lineMap.clipScreenPosition(screenPosition, options)
screenRangeForBufferRange: (bufferRange) ->
@lineMap.screenRangeForBufferRange(bufferRange)
bufferRangeForScreenRange: (screenRange) ->
@lineMap.bufferRangeForScreenRange(screenRange)
expandScreenRangeToLineEnds: (screenRange) ->
{ start, end } = screenRange
new Range([start.row, 0], [end.row, @lineMap.lineForScreenRow(end.row).text.length])

View File

@@ -50,14 +50,10 @@ class LineMap
replaceScreenRows: (start, end, screenLines) ->
@spliceAtScreenRow(start, end - start + 1, screenLines)
getScreenLines: ->
return @screenLines
lineForScreenRow: (row) ->
@linesForScreenRows(row, row)[0]
linesForScreenRows: (startRow, endRow) ->
lastLine = null
lines = []
delta = new Point
@@ -67,11 +63,13 @@ class LineMap
if pendingFragment
pendingFragment = pendingFragment.concat(fragment)
else
pendingFragment = fragment
pendingFragment = _.clone(fragment)
if pendingFragment.screenDelta.row > 0
pendingFragment.bufferDelta = new Point(1, 0)
lines.push pendingFragment
pendingFragment = null
delta = delta.add(fragment.screenDelta)
lines
lineForBufferRow: (row) ->
@@ -99,6 +97,9 @@ class LineMap
delta = delta.add(screenLine.screenDelta)
delta.row
lastScreenRow: ->
@screenLineCount() - 1
screenPositionForBufferPosition: (bufferPosition, eagerWrap=true) ->
bufferPosition = Point.fromObject(bufferPosition)
bufferDelta = new Point
@@ -135,18 +136,52 @@ class LineMap
end = @screenPositionForBufferPosition(bufferRange.end)
new Range(start, end)
clipScreenPosition: (screenPosition) ->
bufferRangeForScreenRange: (screenRange) ->
start = @bufferPositionForScreenPosition(screenRange.start)
end = @bufferPositionForScreenPosition(screenRange.end)
new Range(start, end)
clipScreenPosition: (screenPosition, options) ->
wrapBeyondNewlines = options.wrapBeyondNewlines ? false
wrapAtSoftNewlines = options.wrapAtSoftNewlines ? false
skipAtomicTokens = options.skipAtomicTokens ? false
screenPosition = Point.fromObject(screenPosition)
screenPosition = new Point(Math.max(0, screenPosition.row), Math.max(0, screenPosition.column))
screenPosition.column = Math.max(0, screenPosition.column)
if screenPosition.row < 0
screenPosition.row = 0
screenPosition.column = 0
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
maxColumn = screenDelta.column + screenLine.lengthForClipping()
screenDelta.column = Math.min(maxColumn, screenPosition.column)
if lineFragment.isAtomic
if skipAtomicTokens and screenPosition.column > screenDelta.column
return new Point(screenDelta.row, screenDelta.column + lineFragment.text.length)
else
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]
line = @lineForScreenRow(row).text
console.log row, line, line.length

View File

@@ -76,9 +76,6 @@ class LineWrapper
return column + 1 if /\s/.test(line[column])
return @maxLength
screenRangeForBufferRange: (bufferRange) ->
@lineMap.screenRangeForBufferRange(bufferRange)
screenPositionForBufferPosition: (bufferPosition, eagerWrap=true) ->
@lineMap.screenPositionForBufferPosition(
@lineFolder.screenPositionForBufferPosition(bufferPosition),
@@ -88,6 +85,22 @@ class LineWrapper
@lineFolder.bufferPositionForScreenPosition(
@lineMap.bufferPositionForScreenPosition(screenPosition))
screenRangeForBufferRange: (bufferRange) ->
@lineMap.screenRangeForBufferRange(
@lineFolder.screenRangeForBufferRange(bufferRange))
bufferRangeForScreenRange: (screenRange) ->
@lineFolder.bufferRangeForScreenRange(
@lineMap.bufferRangeForScreenRange(screenRange))
clipScreenPosition: (screenPosition, options={}) ->
@lineMap.screenPositionForBufferPosition(
@lineFolder.clipScreenPosition(
@lineMap.bufferPositionForScreenPosition(@lineMap.clipScreenPosition(screenPosition, options)),
options
)
)
lineForScreenRow: (screenRow) ->
@linesForScreenRows(screenRow, screenRow)[0]
@@ -95,9 +108,15 @@ class LineWrapper
@lineMap.linesForScreenRows(startRow, endRow)
getLines: ->
@linesForScreenRows(0, @lineCount() - 1)
@linesForScreenRows(0, @lastRow())
lineCount: ->
@lineMap.screenLineCount()
lastRow: ->
@lineCount() - 1
logLines: (start=0, end=@lineCount() - 1)->
@lineMap.logLines(start, end)
_.extend(LineWrapper.prototype, EventEmitter)

View File

@@ -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)

View File

@@ -60,35 +60,38 @@ class Selection extends View
region.remove() for region in @regions
@regions = []
getRange: ->
getScreenRange: ->
if @anchor
new Range(@anchor.getScreenPosition(), @cursor.getScreenPosition())
else
new Range(@cursor.getScreenPosition(), @cursor.getScreenPosition())
setRange: (range) ->
setScreenRange: (range) ->
@cursor.setScreenPosition(range.start)
@modifySelection =>
@cursor.setScreenPosition(range.end)
getScreenRange: ->
@editor.lineWrapper.screenRangeForBufferRange(@getRange())
getBufferRange: ->
@editor.bufferRangeForScreenRange(@getScreenRange())
setBufferRange: (bufferRange) ->
@setScreenRange(@editor.screenRangeForBufferRange(bufferRange))
getText: ->
@editor.buffer.getTextInRange @getRange()
@editor.buffer.getTextInRange @getBufferRange()
insertText: (text) ->
@editor.buffer.change(@getRange(), text)
@editor.buffer.change(@getBufferRange(), text)
insertNewline: ->
@insertText('\n')
delete: ->
range = @getRange()
range = @getBufferRange()
@editor.buffer.change(range, '') unless range.isEmpty()
isEmpty: ->
@getRange().isEmpty()
@getBufferRange().isEmpty()
modifySelection: (fn) ->
@placeAnchor()
@@ -114,11 +117,11 @@ class Selection extends View
endOffset = regex.exec(rightSide)?[0]?.length or 0
range = new Range([row, column + startOffset], [row, column + endOffset])
@setRange range
@setBufferRange range
selectLine: (row) ->
rowLength = @editor.buffer.getLine(row).length
@setRange new Range([row, 0], [row, rowLength])
@setBufferRange new Range([row, 0], [row, rowLength])
selectRight: ->
@modifySelection =>
@@ -140,10 +143,14 @@ class Selection extends View
@modifySelection =>
@cursor.moveLeftUntilMatch(regex)
selectToPosition: (position) ->
selectToScreenPosition: (position) ->
@modifySelection =>
@cursor.setScreenPosition(position)
selectToBufferPosition: (position) ->
@modifySelection =>
@cursor.setBufferPosition(position)
moveCursorToLineEnd: ->
@cursor.moveToLineEnd()
@@ -156,8 +163,8 @@ class Selection extends View
copy: ->
return if @isEmpty()
text = @editor.buffer.getTextInRange @getRange()
text = @editor.buffer.getTextInRange(@getBufferRange())
atom.native.writeToPasteboard text
fold: ->
@editor.lineFolder.createFold(@getRange())
@editor.lineFolder.createFold(@getBufferRange())

View File

@@ -13,7 +13,7 @@ class MoveLeft extends Motion
select: ->
position = @editor.getCursorScreenPosition()
position.column-- if position.column > 0
@editor.selectToPosition position
@editor.selectToBufferPosition(position)
class MoveRight extends Motion
execute: ->
@@ -42,7 +42,7 @@ class MoveToNextWord extends Motion
@editor.setCursorScreenPosition(@nextWordPosition())
select: ->
@editor.selectToPosition(@nextWordPosition())
@editor.selectToBufferPosition(@nextWordPosition())
nextWordPosition: ->
regex = getWordRegex()