Merge branch 'making-folding-better'

This commit is contained in:
Corey Johnson & Nathan Sobo 2012-07-30 13:43:47 -07:00
commit 3a5e73aa77
7 changed files with 104 additions and 92 deletions

View File

@ -160,6 +160,14 @@ describe "DisplayBuffer", ->
expect(displayBuffer.lineForRow(2).foldable).toBeTruthy()
expect(displayBuffer.lineForRow(3).foldable).toBeFalsy()
describe ".unfoldAll()", ->
it "unfolds every folded line", ->
displayBuffer.foldBufferRow(0)
displayBuffer.foldBufferRow(1)
displayBuffer.unfoldAll()
expect(Object.keys(displayBuffer.activeFolds).length).toBe 0
describe ".foldAll()", ->
it "folds every foldable line", ->
displayBuffer.foldAll()
@ -172,31 +180,52 @@ describe "DisplayBuffer", ->
expect(displayBuffer.activeFolds[4].length).toBe(1)
it "doesn't fold lines that are already folded", ->
displayBuffer.toggleFoldAtBufferRow(4)
displayBuffer.foldBufferRow(4)
displayBuffer.foldAll()
expect(Object.keys(displayBuffer.activeFolds).length).toBe(3)
expect(displayBuffer.activeFolds[0].length).toBe(1)
expect(displayBuffer.activeFolds[1].length).toBe(1)
expect(displayBuffer.activeFolds[4].length).toBe(1)
describe ".toggleFoldAtBufferRow(bufferRow)", ->
describe ".foldBufferRow(bufferRow)", ->
describe "when bufferRow can be folded", ->
it "creates/destroys a fold based on the syntactic region starting at the given row", ->
displayBuffer.toggleFoldAtBufferRow(1)
it "creates a fold based on the syntactic region starting at the given row", ->
displayBuffer.foldBufferRow(1)
fold = displayBuffer.lineForRow(1).fold
expect(fold.startRow).toBe 1
expect(fold.endRow).toBe 9
displayBuffer.toggleFoldAtBufferRow(1)
expect(displayBuffer.lineForRow(1).fold).toBeUndefined()
describe "when bufferRow can't be folded", ->
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
displayBuffer.toggleFoldAtBufferRow(8)
displayBuffer.foldBufferRow(8)
fold = displayBuffer.lineForRow(1).fold
expect(fold.startRow).toBe 1
expect(fold.endRow).toBe 9
describe "when the bufferRow is already folded", ->
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
displayBuffer.foldBufferRow(2)
expect(displayBuffer.lineForRow(1).fold).toBeDefined()
expect(displayBuffer.lineForRow(0).fold).not.toBeDefined()
displayBuffer.foldBufferRow(1)
expect(displayBuffer.lineForRow(0).fold).toBeDefined()
describe ".unfoldBufferRow(bufferRow)", ->
describe "when bufferRow can be unfolded", ->
it "destroys a fold based on the syntactic region starting at the given row", ->
displayBuffer.foldBufferRow(1)
expect(displayBuffer.lineForRow(1).fold).toBeDefined()
displayBuffer.unfoldBufferRow(1)
expect(displayBuffer.lineForRow(1).fold).toBeUndefined()
describe "when bufferRow can't be unfolded", ->
it "does not throw an error", ->
expect(displayBuffer.lineForRow(1).fold).toBeUndefined()
displayBuffer.unfoldBufferRow(1)
expect(displayBuffer.lineForRow(1).fold).toBeUndefined()
describe "primitive folding", ->
editSession2 = null

View File

@ -825,7 +825,7 @@ describe "EditSession", ->
describe "when the cursor is on the first column of a line below a fold", ->
it "absorbs the current line into the fold", ->
editSession.setCursorScreenPosition([4,0])
editSession.toggleFold()
editSession.foldCurrentRow()
editSession.setCursorScreenPosition([5,0])
editSession.backspace()
@ -835,7 +835,7 @@ describe "EditSession", ->
describe "when the cursor is in the middle of a line below a fold", ->
it "backspaces as normal", ->
editSession.setCursorScreenPosition([4,0])
editSession.toggleFold()
editSession.foldCurrentRow()
editSession.setCursorScreenPosition([5,5])
editSession.backspace()
@ -845,7 +845,7 @@ describe "EditSession", ->
describe "when the cursor is on a folded screen line", ->
it "deletes all of the folded lines along with the fold", ->
editSession.setCursorBufferPosition([3, 0])
editSession.toggleFold()
editSession.foldCurrentRow()
editSession.backspace()
expect(buffer.lineForRow(1)).toBe ""
expect(buffer.lineForRow(2)).toBe " return sort(Array.apply(this, arguments));"
@ -912,7 +912,7 @@ describe "EditSession", ->
describe "when the selection ends on a folded line", ->
it "destroys the fold", ->
editSession.setSelectedBufferRange([[3,0], [4,0]])
editSession.toggleFoldAtBufferRow(4)
editSession.foldBufferRow(4)
editSession.backspace()
expect(buffer.lineForRow(3)).toBe " return sort(left).concat(pivot).concat(sort(right));"
@ -972,7 +972,7 @@ describe "EditSession", ->
describe "when the cursor is on the end of a line above a fold", ->
it "only deletes the lines inside the fold", ->
editSession.toggleFoldAtBufferRow(4)
editSession.foldBufferRow(4)
editSession.setCursorScreenPosition([3, Infinity])
cursorPositionBefore = editSession.getCursorScreenPosition()
@ -984,7 +984,7 @@ describe "EditSession", ->
describe "when the cursor is in the middle a line above a fold", ->
it "deletes as normal", ->
editSession.toggleFoldAtBufferRow(4)
editSession.foldBufferRow(4)
editSession.setCursorScreenPosition([3, 4])
cursorPositionBefore = editSession.getCursorScreenPosition()
@ -1371,41 +1371,10 @@ describe "EditSession", ->
describe "folding", ->
describe "structural folding", ->
describe "when a toggle-fold event is triggered", ->
it "creates/destroys a structual fold based on cursor position", ->
editSession.setCursorBufferPosition([1,0])
editSession.toggleFold()
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
editSession.toggleFold()
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
it "creates/destroys the largest fold containing the cursor position", ->
editSession.foldAll()
editSession.setCursorBufferPosition([5,1])
editSession.toggleFold()
expect(editSession.lineForScreenRow(0).fold).toBeUndefined()
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
editSession.toggleFold()
expect(editSession.lineForScreenRow(0).fold).toBeUndefined()
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
expect(editSession.lineForScreenRow(4).fold).toBeDefined()
describe "when a fold-all event is triggered", ->
it "creates folds on every line that can be folded", ->
editSession.setCursorBufferPosition([5,13])
editSession.foldAll()
expect(editSession.lineForScreenRow(0).fold).toBeDefined()
expect(editSession.lineForScreenRow(1)).toBeUndefined()
it "maintains cursor buffer position when a fold is created/destroyed", ->
editSession.setCursorBufferPosition([5,5])
editSession.foldAll()
expect(editSession.getCursorBufferPosition()).toEqual([5,5])
it "maintains cursor buffer position when a fold is created/destroyed", ->
editSession.setCursorBufferPosition([5,5])
editSession.foldAll()
expect(editSession.getCursorBufferPosition()).toEqual([5,5])
describe "anchors", ->
[anchor, destroyHandler] = []

View File

@ -1377,8 +1377,8 @@ describe "Editor", ->
it "renders lines properly", ->
editor.lineOverdraw = 1
editor.attachToDom(heightInLines: 5)
editor.activeEditSession.toggleFoldAtBufferRow(4)
editor.activeEditSession.toggleFoldAtBufferRow(0)
editor.activeEditSession.foldBufferRow(4)
editor.activeEditSession.foldBufferRow(0)
expect(editor.renderedLines.find('.line').length).toBe 1
expect(editor.renderedLines.find('.line').text()).toBe buffer.lineForRow(0)
@ -1569,8 +1569,8 @@ describe "Editor", ->
describe "when a fold placeholder line is clicked", ->
it "removes the associated fold and places the cursor at its beginning", ->
editor.getSelection().setBufferRange(new Range([3, 0], [9, 0]))
editor.trigger 'fold-selection'
editor.setCursorBufferPosition([3,0])
editor.trigger 'fold-current-row'
editor.find('.fold.line').mousedown()
@ -1580,13 +1580,13 @@ describe "Editor", ->
expect(editor.getCursorBufferPosition()).toEqual [3, 0]
describe "when the unfold event is triggered when the cursor is on a fold placeholder line", ->
describe "when the unfold-current-row event is triggered when the cursor is on a fold placeholder line", ->
it "removes the associated fold and places the cursor at its beginning", ->
editor.getSelection().setBufferRange(new Range([3, 0], [9, 0]))
editor.trigger 'fold-selection'
editor.setCursorBufferPosition([3,0])
editor.trigger 'fold-current-row'
editor.setCursorBufferPosition([3,0])
editor.trigger 'unfold'
editor.trigger 'unfold-current-row'
expect(editor.find('.fold')).not.toExist()
expect(editor.renderedLines.find('.line:eq(4)').text()).toMatch /4-+/

View File

@ -59,26 +59,23 @@ class DisplayBuffer
@createFold(startRow, endRow)
toggleFoldAtBufferRow: (bufferRow) ->
unfoldAll: ->
for row in [@buffer.getLastRow()..0]
@activeFolds[row]?.forEach (fold) => @destroyFold(fold)
foldBufferRow: (bufferRow) ->
for currentRow in [bufferRow..0]
[startRow, endRow] = @languageMode.rowRangeForFoldAtBufferRow(currentRow) ? []
continue unless startRow? and startRow <= bufferRow <= endRow
fold = @largestFoldStartingAtBufferRow(startRow)
continue if fold
if fold = @largestFoldStartingAtBufferRow(startRow)
fold.destroy()
else
@createFold(startRow, endRow)
@createFold(startRow, endRow)
break
return
isFoldContainedByActiveFold: (fold) ->
for row, folds of @activeFolds
for otherFold in folds
return otherFold if fold != otherFold and fold.isContainedByFold(otherFold)
foldFor: (startRow, endRow) ->
_.find @activeFolds[startRow] ? [], (fold) ->
fold.startRow == startRow and fold.endRow == endRow
unfoldBufferRow: (bufferRow) ->
@largestFoldContainingBufferRow(bufferRow)?.destroy()
createFold: (startRow, endRow) ->
return fold if fold = @foldFor(startRow, endRow)
@ -97,6 +94,15 @@ class DisplayBuffer
fold
isFoldContainedByActiveFold: (fold) ->
for row, folds of @activeFolds
for otherFold in folds
return otherFold if fold != otherFold and fold.isContainedByFold(otherFold)
foldFor: (startRow, endRow) ->
_.find @activeFolds[startRow] ? [], (fold) ->
fold.startRow == startRow and fold.endRow == endRow
destroyFold: (fold) ->
@unregisterFold(fold.startRow, fold)
@ -124,6 +130,7 @@ class DisplayBuffer
folds = @activeFolds[bufferRow]
_.remove(folds, fold)
delete @foldsById[fold.id]
delete @activeFolds[bufferRow] if folds.length == 0
largestFoldStartingAtBufferRow: (bufferRow) ->
return unless folds = @activeFolds[bufferRow]

View File

@ -201,18 +201,28 @@ class EditSession
redo: ->
@buffer.redo(this)
foldSelection: ->
selection.fold() for selection in @getSelections()
foldAll: ->
@displayBuffer.foldAll()
toggleFold: ->
bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
@toggleFoldAtBufferRow(bufferRow)
unfoldAll: ->
@displayBuffer.unfoldAll()
toggleFoldAtBufferRow: (bufferRow) ->
@displayBuffer.toggleFoldAtBufferRow(bufferRow)
foldCurrentRow: ->
bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
@foldBufferRow(bufferRow)
foldBufferRow: (bufferRow) ->
@displayBuffer.foldBufferRow(bufferRow)
unfoldCurrentRow: ->
bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
@unfoldBufferRow(bufferRow)
unfoldBufferRow: (bufferRow) ->
@displayBuffer.unfoldBufferRow(bufferRow)
foldSelection: ->
selection.fold() for selection in @getSelections()
createFold: (startRow, endRow) ->
@displayBuffer.createFold(startRow, endRow)
@ -224,9 +234,6 @@ class EditSession
for row in [bufferRange.start.row..bufferRange.end.row]
@destroyFoldsContainingBufferRow(row)
unfoldCurrentRow: ->
@largestFoldStartingAtBufferRow(@getLastCursor().getBufferRow())?.destroy()
destroyFold: (foldId) ->
fold = @displayBuffer.foldsById[foldId]
fold.destroy()
@ -235,9 +242,6 @@ class EditSession
isFoldedAtScreenRow: (screenRow) ->
@lineForScreenRow(screenRow).fold?
largestFoldStartingAtBufferRow: (bufferRow) ->
@displayBuffer.largestFoldStartingAtBufferRow(bufferRow)
largestFoldContainingBufferRow: (bufferRow) ->
@displayBuffer.largestFoldContainingBufferRow(bufferRow)

View File

@ -139,9 +139,10 @@ class Editor extends View
'newline-below': @insertNewlineBelow
'toggle-soft-wrap': @toggleSoftWrap
'fold-all': @foldAll
'toggle-fold': @toggleFold
'unfold-all': @unfoldAll
'fold-current-row': @foldCurrentRow
'unfold-current-row': @unfoldCurrentRow
'fold-selection': @foldSelection
'unfold': => @unfoldCurrentRow()
'split-left': @splitLeft
'split-right': @splitRight
'split-up': @splitUp
@ -220,13 +221,14 @@ class Editor extends View
undo: -> @activeEditSession.undo()
redo: -> @activeEditSession.redo()
createFold: (startRow, endRow) -> @activeEditSession.createFold(startRow, endRow)
foldCurrentRow: -> @activeEditSession.foldCurrentRow()
unfoldCurrentRow: -> @activeEditSession.unfoldCurrentRow()
foldAll: -> @activeEditSession.foldAll()
unfoldAll: -> @activeEditSession.unfoldAll()
foldSelection: -> @activeEditSession.foldSelection()
destroyFold: (foldId) -> @activeEditSession.destroyFold(foldId)
destroyFoldsContainingBufferRow: (bufferRow) -> @activeEditSession.destroyFoldsContainingBufferRow(bufferRow)
toggleFold: -> @activeEditSession.toggleFold()
isFoldedAtScreenRow: (screenRow) -> @activeEditSession.isFoldedAtScreenRow(screenRow)
unfoldCurrentRow: -> @activeEditSession.unfoldCurrentRow()
lineForScreenRow: (screenRow) -> @activeEditSession.lineForScreenRow(screenRow)
linesForScreenRows: (start, end) -> @activeEditSession.linesForScreenRows(start, end)

View File

@ -17,10 +17,11 @@ window.keymap.bindKeys '.editor',
'meta-z': 'undo'
'meta-Z': 'redo'
'alt-meta-w': 'toggle-soft-wrap'
'ctrl-9': 'toggle-fold'
'ctrl-(': 'fold-all'
'ctrl-[': 'fold-current-row'
'ctrl-]': 'unfold-current-row'
'ctrl-{': 'fold-all'
'ctrl-}': 'unfold-all'
'alt-meta-ctrl-f': 'fold-selection'
'alt-meta-u': 'unfold'
'alt-meta-left': 'split-left'
'alt-meta-right': 'split-right'
'alt-meta-up': 'split-up'