From 298d50b61e33f2f724e30d7026936c0df6804e02 Mon Sep 17 00:00:00 2001 From: Jon Rohan Date: Tue, 29 Jan 2013 14:38:01 -0500 Subject: [PATCH 01/27] changing the space invisible to 00b7 'middot' --- src/app/editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index d4a1a4093..d0041c1bd 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -312,7 +312,7 @@ class Editor extends View setInvisibles: (@invisibles={}) -> _.defaults @invisibles, eol: '\u00ac' - space: '\u2022' + space: '\u00b7' tab: '\u00bb' cr: '\u00a4' @resetDisplay() From ad7e4b63c00eca7478b24222b415e55258945f2e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 08:36:24 -0800 Subject: [PATCH 02/27] Bind ctrl-meta-up to move line(s) up Refs #134 --- spec/app/editor-spec.coffee | 90 +++++++++++++++++++++++++++++++++++++ src/app/edit-session.coffee | 33 ++++++++++++++ src/app/editor.coffee | 2 + src/app/keymaps/editor.cson | 1 + src/app/line-map.coffee | 2 +- src/app/point.coffee | 6 +++ 6 files changed, 133 insertions(+), 1 deletion(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 25dc557dc..06e0b95b5 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2260,3 +2260,93 @@ describe "Editor", -> it "copies the absolute path to the editor's file to the pasteboard", -> editor.trigger 'editor:copy-path' expect(pasteboard.read()[0]).toBe editor.getPath() + + describe "when editor:move-line-up is triggered", -> + describe "when there is no selection", -> + it "moves the line where the cursor is up", -> + editor.setCursorBufferPosition([1,0]) + editor.trigger 'editor:move-line-up' + expect(buffer.lineForRow(0)).toBe ' var sort = function(items) {' + expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' + + it "moves the cursor to the new row and the same column", -> + editor.setCursorBufferPosition([1,2]) + editor.trigger 'editor:move-line-up' + expect(editor.getCursorBufferPosition()).toEqual [0,2] + + describe "where there is a selection", -> + describe "when the selection falls inside the line", -> + it "maintains the selection", -> + editor.setSelectedBufferRange([[1, 2], [1, 5]]) + expect(editor.getSelectedText()).toBe 'var' + editor.trigger 'editor:move-line-up' + expect(editor.getSelectedBufferRange()).toEqual [[0, 2], [0, 5]] + expect(editor.getSelectedText()).toBe 'var' + + describe "where there are multiple lines selected", -> + it "moves the selected lines up", -> + editor.setSelectedBufferRange([[2, 0], [3, Infinity]]) + editor.trigger 'editor:move-line-up' + expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {' + expect(buffer.lineForRow(1)).toBe ' if (items.length <= 1) return items;' + expect(buffer.lineForRow(2)).toBe ' var pivot = items.shift(), current, left = [], right = [];' + expect(buffer.lineForRow(3)).toBe ' var sort = function(items) {' + + it "maintains the selection", -> + editor.setSelectedBufferRange([[2, 0], [3, 62]]) + editor.trigger 'editor:move-line-up' + expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [2, 62]] + + describe "when the last line is selected", -> + it "moves the selected line up", -> + editor.setSelectedBufferRange([[12, 0], [12, Infinity]]) + editor.trigger 'editor:move-line-up' + expect(buffer.lineForRow(11)).toBe '};' + expect(buffer.lineForRow(12)).toBe ' return sort(Array.apply(this, arguments));' + + describe "when the last two lines are selected", -> + it "moves the selected lines up", -> + editor.setSelectedBufferRange([[11, 0], [12, Infinity]]) + editor.trigger 'editor:move-line-up' + expect(buffer.lineForRow(10)).toBe ' return sort(Array.apply(this, arguments));' + expect(buffer.lineForRow(11)).toBe '};' + expect(buffer.lineForRow(12)).toBe '' + + describe "when the cursor is on the first line", -> + it "does not move the line", -> + editor.setCursorBufferPosition([0,0]) + originalText = editor.getText() + editor.trigger 'editor:move-line-up' + expect(editor.getText()).toBe originalText + + describe "when the cursor is on the trailing newline", -> + it "does not move the line", -> + editor.moveCursorToBottom() + editor.insertNewline() + editor.moveCursorToBottom() + originalText = editor.getText() + editor.trigger 'editor:move-line-up' + expect(editor.getText()).toBe originalText + + describe "when the cursor is on a folded line", -> + it "moves all lines in the fold up and preserves the fold", -> + editor.setCursorBufferPosition([4, 0]) + editor.foldCurrentRow() + editor.trigger 'editor:move-line-up' + expect(buffer.lineForRow(3)).toBe ' while(items.length > 0) {' + expect(buffer.lineForRow(7)).toBe ' var pivot = items.shift(), current, left = [], right = [];' + expect(editor.getSelectedBufferRange()).toEqual [[3, 0], [3, 0]] + expect(editor.isFoldedAtScreenRow(3)).toBeTruthy() + + describe "when a fold is selected", -> + it "moves the selected lines up and preserves the fold", -> + editor.setCursorBufferPosition([4, 0]) + editor.foldCurrentRow() + editor.setCursorBufferPosition([3, 4]) + editor.selectDown() + expect(editor.isFoldedAtScreenRow(4)).toBeTruthy() + editor.trigger 'editor:move-line-up' + expect(buffer.lineForRow(2)).toBe ' var pivot = items.shift(), current, left = [], right = [];' + expect(buffer.lineForRow(3)).toBe ' while(items.length > 0) {' + expect(editor.getSelectedBufferRange()).toEqual [[2, 4], [3,0]] + expect(editor.isFoldedAtScreenRow(3)).toBeTruthy() diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 74fe4a2a3..fcedbd28f 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -334,6 +334,39 @@ class EditSession toggleLineCommentsForBufferRows: (start, end) -> @languageMode.toggleLineCommentsForBufferRows(start, end) + moveLineUp: -> + selection = @getSelectedBufferRange() + return if selection.start.row is 0 + lastRow = @buffer.getLastRow() + return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' + + @transact => + for row in [selection.start.row..selection.end.row] + screenPosition = @screenPositionForBufferPosition([row, 0]) + isRowFolded = @isFoldedAtScreenRow(screenPosition.row) + if isRowFolded + bufferRange = @bufferRangeForScreenRange([[screenPosition.row, 0], [screenPosition.row + 1, 0]]) + startRow = bufferRange.start.row + endRow = bufferRange.end.row - 1 + else + startRow = row + endRow = row + + endPosition = Point.min([endRow + 1, 0], @buffer.getEofPosition()) + lines = @buffer.getTextInRange([[startRow, 0], endPosition]) + if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row) + lines = "#{lines}\n" + @buffer.deleteRows(startRow, endRow) + @buffer.insert([startRow - 1, 0], lines) + @foldBufferRow(startRow - 1) if isRowFolded + + newStartPosition = [selection.start.row - 1, selection.start.column] + if selection.isEmpty() + @setCursorBufferPosition(newStartPosition) + else + newEndPosition = [selection.end.row - 1, selection.end.column] + @setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true) + mutateSelectedText: (fn) -> @transact => fn(selection) for selection in @getSelections() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index d0041c1bd..2afa97e4d 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -183,6 +183,7 @@ class Editor extends View 'editor:close-all-edit-sessions': @destroyAllEditSessions 'editor:select-grammar': @selectGrammar 'editor:copy-path': @copyPathToPasteboard + 'editor:move-line-up': @moveLineUp documentation = {} for name, method of editorBindings @@ -204,6 +205,7 @@ class Editor extends View moveCursorToBeginningOfLine: -> @activeEditSession.moveCursorToBeginningOfLine() moveCursorToFirstCharacterOfLine: -> @activeEditSession.moveCursorToFirstCharacterOfLine() moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine() + moveLineUp: -> @activeEditSession.moveLineUp() setCursorScreenPosition: (position) -> @activeEditSession.setCursorScreenPosition(position) getCursorScreenPosition: -> @activeEditSession.getCursorScreenPosition() getCursorScreenRow: -> @activeEditSession.getCursorScreenRow() diff --git a/src/app/keymaps/editor.cson b/src/app/keymaps/editor.cson index 06f0dd571..4cb724be2 100644 --- a/src/app/keymaps/editor.cson +++ b/src/app/keymaps/editor.cson @@ -38,3 +38,4 @@ 'meta-P': 'editor:close-all-edit-sessions' 'meta-L': 'editor:select-grammar' 'ctrl-C': 'editor:copy-path' + 'ctrl-meta-up': 'editor:move-line-up' diff --git a/src/app/line-map.coffee b/src/app/line-map.coffee index eae630887..1a5a74147 100644 --- a/src/app/line-map.coffee +++ b/src/app/line-map.coffee @@ -133,6 +133,7 @@ class LineMap new Range(start, end) bufferRangeForScreenRange: (screenRange) -> + screenRange = Range.fromObject(screenRange) start = @bufferPositionForScreenPosition(screenRange.start) end = @bufferPositionForScreenPosition(screenRange.end) new Range(start, end) @@ -141,4 +142,3 @@ class LineMap for row in [start..end] line = @lineForScreenRow(row).text console.log row, line, line.length - diff --git a/src/app/point.coffee b/src/app/point.coffee index a216cad0b..0b07e3ed7 100644 --- a/src/app/point.coffee +++ b/src/app/point.coffee @@ -11,6 +11,12 @@ class Point new Point(row, column) + @min: (point1, point2) -> + if @fromObject(point1).isLessThanOrEqual(@fromObject(point2)) + point1 + else + point2 + constructor: (@row=0, @column=0) -> copy: -> From 66df8603292edff2efa2cfe4ab556d83d700716b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 14:37:55 -0800 Subject: [PATCH 03/27] :lipstick: --- src/app/buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index 266b30ba2..95d0819b1 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -117,7 +117,7 @@ class Buffer getTextInRange: (range) -> range = Range.fromObject(range) if range.start.row == range.end.row - return @lines[range.start.row][range.start.column...range.end.column] + return @lineForRow(range.start.row)[range.start.column...range.end.column] multipleLines = [] multipleLines.push @lineForRow(range.start.row)[range.start.column..] # first line From 348a0a3d9aed4074e0dab347a135f0e36ee14146 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 14:38:28 -0800 Subject: [PATCH 04/27] Return normalized point --- src/app/point.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/point.coffee b/src/app/point.coffee index 0b07e3ed7..b740027f6 100644 --- a/src/app/point.coffee +++ b/src/app/point.coffee @@ -12,7 +12,9 @@ class Point new Point(row, column) @min: (point1, point2) -> - if @fromObject(point1).isLessThanOrEqual(@fromObject(point2)) + point1 = @fromObject(point1) + point2 = @fromObject(point2) + if point1.isLessThanOrEqual(point2) point1 else point2 From 096566ab2ac51698954a5d41ae4a25b5d7662070 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 14:55:55 -0800 Subject: [PATCH 05/27] Bind ctrl-meta-down to move line(s) down Refs #134 --- spec/app/editor-spec.coffee | 89 ++++++++++++++++++++++++++++++++++++- src/app/edit-session.coffee | 58 ++++++++++++++++++++---- src/app/editor.coffee | 2 + src/app/keymaps/editor.cson | 1 + 4 files changed, 140 insertions(+), 10 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 06e0b95b5..b6dcab74f 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2338,7 +2338,7 @@ describe "Editor", -> expect(editor.getSelectedBufferRange()).toEqual [[3, 0], [3, 0]] expect(editor.isFoldedAtScreenRow(3)).toBeTruthy() - describe "when a fold is selected", -> + describe "when the selection contains a folded and unfolded line", -> it "moves the selected lines up and preserves the fold", -> editor.setCursorBufferPosition([4, 0]) editor.foldCurrentRow() @@ -2348,5 +2348,90 @@ describe "Editor", -> editor.trigger 'editor:move-line-up' expect(buffer.lineForRow(2)).toBe ' var pivot = items.shift(), current, left = [], right = [];' expect(buffer.lineForRow(3)).toBe ' while(items.length > 0) {' - expect(editor.getSelectedBufferRange()).toEqual [[2, 4], [3,0]] + expect(editor.getSelectedBufferRange()).toEqual [[2, 4], [3, 0]] expect(editor.isFoldedAtScreenRow(3)).toBeTruthy() + + describe "when editor:move-line-down is triggered", -> + describe "when there is no selection", -> + it "moves the line where the cursor is down", -> + editor.setCursorBufferPosition([0, 0]) + editor.trigger 'editor:move-line-down' + expect(buffer.lineForRow(0)).toBe ' var sort = function(items) {' + expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' + + it "moves the cursor to the new row and the same column", -> + editor.setCursorBufferPosition([0, 2]) + editor.trigger 'editor:move-line-down' + expect(editor.getCursorBufferPosition()).toEqual [1, 2] + + describe "when the cursor is on the last line", -> + it "does not move the line", -> + editor.moveCursorToBottom() + editor.trigger 'editor:move-line-down' + expect(buffer.lineForRow(12)).toBe '};' + expect(editor.getSelectedBufferRange()).toEqual [[12, 2], [12, 2]] + + describe "when the cursor is on the second to last line", -> + it "moves the line down", -> + editor.setCursorBufferPosition([11, 0]) + editor.trigger 'editor:move-line-down' + expect(buffer.lineForRow(11)).toBe '};' + expect(buffer.lineForRow(12)).toBe ' return sort(Array.apply(this, arguments));' + expect(buffer.lineForRow(13)).toBeUndefined() + + describe "when the cursor is on the second to last line and the last line is empty", -> + it "does not move the line", -> + editor.moveCursorToBottom() + editor.insertNewline() + editor.setCursorBufferPosition([12, 2]) + editor.trigger 'editor:move-line-down' + expect(buffer.lineForRow(12)).toBe '};' + expect(buffer.lineForRow(13)).toBe '' + expect(editor.getSelectedBufferRange()).toEqual [[12, 2], [12, 2]] + + describe "where there is a selection", -> + describe "when the selection falls inside the line", -> + it "maintains the selection", -> + editor.setSelectedBufferRange([[1, 2], [1, 5]]) + expect(editor.getSelectedText()).toBe 'var' + editor.trigger 'editor:move-line-down' + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [2, 5]] + expect(editor.getSelectedText()).toBe 'var' + + describe "where there are multiple lines selected", -> + it "moves the selected lines down", -> + editor.setSelectedBufferRange([[2, 0], [3, Infinity]]) + editor.trigger 'editor:move-line-down' + expect(buffer.lineForRow(2)).toBe ' while(items.length > 0) {' + expect(buffer.lineForRow(3)).toBe ' if (items.length <= 1) return items;' + expect(buffer.lineForRow(4)).toBe ' var pivot = items.shift(), current, left = [], right = [];' + expect(buffer.lineForRow(5)).toBe ' current = items.shift();' + + it "maintains the selection", -> + editor.setSelectedBufferRange([[2, 0], [3, 62]]) + editor.trigger 'editor:move-line-down' + expect(editor.getSelectedBufferRange()).toEqual [[3, 0], [4, 62]] + + describe "when the cursor is on a folded line", -> + it "moves all lines in the fold down and preserves the fold", -> + editor.setCursorBufferPosition([4, 0]) + editor.foldCurrentRow() + editor.trigger 'editor:move-line-down' + expect(buffer.lineForRow(4)).toBe ' return sort(left).concat(pivot).concat(sort(right));' + expect(buffer.lineForRow(5)).toBe ' while(items.length > 0) {' + expect(editor.getSelectedBufferRange()).toEqual [[5, 0], [5, 0]] + expect(editor.isFoldedAtScreenRow(5)).toBeTruthy() + + describe "when the selection contains a folded and unfolded line", -> + it "moves the selected lines down and preserves the fold", -> + editor.setCursorBufferPosition([4, 0]) + editor.foldCurrentRow() + editor.setCursorBufferPosition([3, 4]) + editor.selectDown() + expect(editor.isFoldedAtScreenRow(4)).toBeTruthy() + editor.trigger 'editor:move-line-down' + expect(buffer.lineForRow(3)).toBe ' return sort(left).concat(pivot).concat(sort(right));' + expect(buffer.lineForRow(4)).toBe ' var pivot = items.shift(), current, left = [], right = [];' + expect(buffer.lineForRow(5)).toBe ' while(items.length > 0) {' + expect(editor.getSelectedBufferRange()).toEqual [[4, 4], [5, 0]] + expect(editor.isFoldedAtScreenRow(5)).toBeTruthy() diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index fcedbd28f..52c319015 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -341,24 +341,26 @@ class EditSession return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' @transact => + foldedRows = [] for row in [selection.start.row..selection.end.row] - screenPosition = @screenPositionForBufferPosition([row, 0]) - isRowFolded = @isFoldedAtScreenRow(screenPosition.row) - if isRowFolded - bufferRange = @bufferRangeForScreenRange([[screenPosition.row, 0], [screenPosition.row + 1, 0]]) + screenRow = @screenPositionForBufferPosition([row]).row + if @isFoldedAtScreenRow(screenRow) + bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]]) startRow = bufferRange.start.row endRow = bufferRange.end.row - 1 + foldedRows.push(endRow - 1) else startRow = row endRow = row - endPosition = Point.min([endRow + 1, 0], @buffer.getEofPosition()) - lines = @buffer.getTextInRange([[startRow, 0], endPosition]) + endPosition = Point.min([endRow + 1], @buffer.getEofPosition()) + lines = @buffer.getTextInRange([[startRow], endPosition]) if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row) lines = "#{lines}\n" @buffer.deleteRows(startRow, endRow) - @buffer.insert([startRow - 1, 0], lines) - @foldBufferRow(startRow - 1) if isRowFolded + @buffer.insert([startRow - 1], lines) + + @foldBufferRow(foldedRow) for foldedRow in foldedRows newStartPosition = [selection.start.row - 1, selection.start.column] if selection.isEmpty() @@ -367,6 +369,46 @@ class EditSession newEndPosition = [selection.end.row - 1, selection.end.column] @setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true) + moveLineDown: -> + selection = @getSelectedBufferRange() + lastRow = @buffer.getLastRow() + return if selection.end.row is lastRow + return if selection.end.row is lastRow - 1 and @buffer.getLastLine() is '' + + @transact => + foldedRows = [] + for row in [selection.end.row..selection.start.row] + screenRow = @screenPositionForBufferPosition([row]).row + if @isFoldedAtScreenRow(screenRow) + bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]]) + startRow = bufferRange.start.row + endRow = bufferRange.end.row - 1 + foldedRows.push(endRow + 1) + else + startRow = row + endRow = row + + if endRow + 1 is lastRow + endPosition = [endRow, @buffer.lineLengthForRow(endRow)] + else + endPosition = [endRow + 1] + lines = @buffer.getTextInRange([[startRow], endPosition]) + @buffer.deleteRows(startRow, endRow) + insertPosition = Point.min([startRow + 1], @buffer.getEofPosition()) + if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0 + lines = "\n#{lines}" + @buffer.insert(insertPosition, lines) + + @foldBufferRow(foldedRow) for foldedRow in foldedRows + + newStartPosition = [selection.start.row + 1, selection.start.column] + if selection.isEmpty() + @setCursorBufferPosition(newStartPosition) + else + newEndPosition = [selection.end.row + 1, selection.end.column] + @setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true) + + mutateSelectedText: (fn) -> @transact => fn(selection) for selection in @getSelections() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 2afa97e4d..0fb628d4d 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -184,6 +184,7 @@ class Editor extends View 'editor:select-grammar': @selectGrammar 'editor:copy-path': @copyPathToPasteboard 'editor:move-line-up': @moveLineUp + 'editor:move-line-down': @moveLineDown documentation = {} for name, method of editorBindings @@ -206,6 +207,7 @@ class Editor extends View moveCursorToFirstCharacterOfLine: -> @activeEditSession.moveCursorToFirstCharacterOfLine() moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine() moveLineUp: -> @activeEditSession.moveLineUp() + moveLineDown: -> @activeEditSession.moveLineDown() setCursorScreenPosition: (position) -> @activeEditSession.setCursorScreenPosition(position) getCursorScreenPosition: -> @activeEditSession.getCursorScreenPosition() getCursorScreenRow: -> @activeEditSession.getCursorScreenRow() diff --git a/src/app/keymaps/editor.cson b/src/app/keymaps/editor.cson index 4cb724be2..4c977b698 100644 --- a/src/app/keymaps/editor.cson +++ b/src/app/keymaps/editor.cson @@ -39,3 +39,4 @@ 'meta-L': 'editor:select-grammar' 'ctrl-C': 'editor:copy-path' 'ctrl-meta-up': 'editor:move-line-up' + 'ctrl-meta-down': 'editor:move-line-down' From 367927faa136f5b863e4d341e4357a8c28108819 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 15:08:21 -0800 Subject: [PATCH 06/27] Remove unneeded empty selection logic --- src/app/edit-session.coffee | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 52c319015..fbac2d16f 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -363,11 +363,8 @@ class EditSession @foldBufferRow(foldedRow) for foldedRow in foldedRows newStartPosition = [selection.start.row - 1, selection.start.column] - if selection.isEmpty() - @setCursorBufferPosition(newStartPosition) - else - newEndPosition = [selection.end.row - 1, selection.end.column] - @setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true) + newEndPosition = [selection.end.row - 1, selection.end.column] + @setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true) moveLineDown: -> selection = @getSelectedBufferRange() @@ -402,11 +399,8 @@ class EditSession @foldBufferRow(foldedRow) for foldedRow in foldedRows newStartPosition = [selection.start.row + 1, selection.start.column] - if selection.isEmpty() - @setCursorBufferPosition(newStartPosition) - else - newEndPosition = [selection.end.row + 1, selection.end.column] - @setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true) + newEndPosition = [selection.end.row + 1, selection.end.column] + @setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true) mutateSelectedText: (fn) -> From 1a04fa31d15a33563230a2fda3cdc01635fd7ac0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 15:34:41 -0800 Subject: [PATCH 07/27] Clip range specified to Buffer.getTextInRange() --- spec/app/buffer-spec.coffee | 8 ++++++++ src/app/buffer.coffee | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/spec/app/buffer-spec.coffee b/spec/app/buffer-spec.coffee index ea10907cf..b13c2b7c2 100644 --- a/spec/app/buffer-spec.coffee +++ b/spec/app/buffer-spec.coffee @@ -465,6 +465,14 @@ describe 'Buffer', -> range = [[2,10], [4,10]] expect(buffer.getTextInRange(range)).toBe "ems.length <= 1) return items;\n var pivot = items.shift(), current, left = [], right = [];\n while(" + describe "when the range starts before the start of the buffer", -> + it "clips the range to the start of the buffer", -> + expect(buffer.getTextInRange([[-Infinity, -Infinity], [0, Infinity]])).toBe buffer.lineForRow(0) + + describe "when the range ends after the end of the buffer", -> + it "clips the range to the end of the buffer", -> + expect(buffer.getTextInRange([[12], [13, Infinity]])).toBe buffer.lineForRow(12) + describe ".scanInRange(range, regex, fn)", -> describe "when given a regex with a ignore case flag", -> it "does a case-insensitive search", -> diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index 95d0819b1..640e21fae 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -115,7 +115,7 @@ class Buffer new Range([0, 0], [@getLastRow(), @getLastLine().length]) getTextInRange: (range) -> - range = Range.fromObject(range) + range = @clipRange(Range.fromObject(range)) if range.start.row == range.end.row return @lineForRow(range.start.row)[range.start.column...range.end.column] @@ -220,6 +220,10 @@ class Buffer new Point(row, column) + clipRange: (range) -> + range = Range.fromObject(range) + new Range(@clipPosition(range.start), @clipPosition(range.end)) + prefixAndSuffixForRange: (range) -> prefix: @lines[range.start.row][0...range.start.column] suffix: @lines[range.end.row][range.end.column..] From 4bfa5dd7a0c0c24bae0cdaf50a72b2f876b835df Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 15:40:34 -0800 Subject: [PATCH 08/27] Remove unneeded Range.fromObject call --- src/app/buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index 640e21fae..9e0cd94ab 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -115,7 +115,7 @@ class Buffer new Range([0, 0], [@getLastRow(), @getLastLine().length]) getTextInRange: (range) -> - range = @clipRange(Range.fromObject(range)) + range = @clipRange(range) if range.start.row == range.end.row return @lineForRow(range.start.row)[range.start.column...range.end.column] From 1303e58a87401b255807b3fd7413722d9b42e683 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 16:09:55 -0800 Subject: [PATCH 09/27] Don't move trailing newline for multiline selections --- spec/app/editor-spec.coffee | 18 ++++++++++++++++++ src/app/edit-session.coffee | 10 ++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index b6dcab74f..9dcffb4cf 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2351,6 +2351,15 @@ describe "Editor", -> expect(editor.getSelectedBufferRange()).toEqual [[2, 4], [3, 0]] expect(editor.isFoldedAtScreenRow(3)).toBeTruthy() + describe "when an entire line is selected including the newline", -> + it "moves the selected line up", -> + editor.setCursorBufferPosition([1]) + editor.selectToEndOfLine() + editor.selectRight() + editor.trigger 'editor:move-line-up' + expect(buffer.lineForRow(0)).toBe ' var sort = function(items) {' + expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' + describe "when editor:move-line-down is triggered", -> describe "when there is no selection", -> it "moves the line where the cursor is down", -> @@ -2435,3 +2444,12 @@ describe "Editor", -> expect(buffer.lineForRow(5)).toBe ' while(items.length > 0) {' expect(editor.getSelectedBufferRange()).toEqual [[4, 4], [5, 0]] expect(editor.isFoldedAtScreenRow(5)).toBeTruthy() + + describe "when an entire line is selected including the newline", -> + it "moves the selected line down", -> + editor.setCursorBufferPosition([1]) + editor.selectToEndOfLine() + editor.selectRight() + editor.trigger 'editor:move-line-down' + expect(buffer.lineForRow(1)).toBe ' if (items.length <= 1) return items;' + expect(buffer.lineForRow(2)).toBe ' var sort = function(items) {' diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index fbac2d16f..12adae4d6 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -342,7 +342,10 @@ class EditSession @transact => foldedRows = [] - for row in [selection.start.row..selection.end.row] + rows = [selection.start.row..selection.end.row] + if selection.start.row isnt selection.end.row and selection.end.column is 0 + rows.pop() unless @isFoldedAtScreenRow(@screenPositionForBufferPosition(selection.end).row) + for row in rows screenRow = @screenPositionForBufferPosition([row]).row if @isFoldedAtScreenRow(screenRow) bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]]) @@ -374,7 +377,10 @@ class EditSession @transact => foldedRows = [] - for row in [selection.end.row..selection.start.row] + rows = [selection.end.row..selection.start.row] + if selection.start.row isnt selection.end.row and selection.end.column is 0 + rows.shift() unless @isFoldedAtScreenRow(@screenPositionForBufferPosition(selection.end).row) + for row in rows screenRow = @screenPositionForBufferPosition([row]).row if @isFoldedAtScreenRow(screenRow) bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]]) From 1d2fa089e5cc8e4b054cf1a41cc0f8b90e8af14b Mon Sep 17 00:00:00 2001 From: Corey Johnson & Kevin Sawicki Date: Mon, 28 Jan 2013 12:00:12 -0800 Subject: [PATCH 10/27] editor.wordRegex is now a config option. --- src/app/cursor.coffee | 5 ++--- src/app/editor.coffee | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index 4599705f3..efc80fbf4 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -9,7 +9,6 @@ class Cursor screenPosition: null bufferPosition: null goalColumn: null - wordRegex: /(\w+)|([^\w\n]+)/g visible: true needsAutoscroll: false @@ -150,7 +149,7 @@ class Cursor previousLinesRange = [[previousNonBlankRow, 0], currentBufferPosition] beginningOfWordPosition = currentBufferPosition - @editSession.backwardsScanInRange (options.wordRegex || @wordRegex), previousLinesRange, (match, matchRange, { stop }) => + @editSession.backwardsScanInRange (options.wordRegex ? config.get("editor.wordRegex")), previousLinesRange, (match, matchRange, { stop }) => if matchRange.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious beginningOfWordPosition = matchRange.start stop() @@ -162,7 +161,7 @@ class Cursor range = [currentBufferPosition, @editSession.getEofBufferPosition()] endOfWordPosition = null - @editSession.scanInRange (options.wordRegex || @wordRegex), range, (match, matchRange, { stop }) => + @editSession.scanInRange (options.wordRegex ? config.get("editor.wordRegex")), range, (match, matchRange, { stop }) => endOfWordPosition = matchRange.end if not allowNext and matchRange.start.isGreaterThan(currentBufferPosition) endOfWordPosition = currentBufferPosition diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 0fb628d4d..55600c0fa 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -19,6 +19,7 @@ class Editor extends View autosave: false autoIndent: true autoIndentOnPaste: false + wordRegex: /(\w+)|([^\w\n]+)/g @content: (params) -> @div class: @classes(params), tabindex: -1, => From b66efbe3e7137d18d29460ff41735c77c04e9a2c Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 29 Jan 2013 11:08:17 -0800 Subject: [PATCH 11/27] cursor.getBeginningOfCurrentWordBufferPosition behaves like vim --- spec/app/edit-session-spec.coffee | 10 +++++----- src/app/cursor.coffee | 6 +++++- src/app/editor.coffee | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index 6f72cd8bc..a16554f84 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -16,7 +16,7 @@ describe "EditSession", -> afterEach -> fixturesProject.destroy() - describe "cursor", -> + fdescribe "cursor", -> describe ".getCursor()", -> it "returns the most recently created cursor", -> editSession.addCursorAtScreenPosition([1, 0]) @@ -276,17 +276,17 @@ describe "EditSession", -> editSession.moveCursorToBeginningOfWord() expect(cursor1.getBufferPosition()).toEqual [0, 4] - expect(cursor2.getBufferPosition()).toEqual [1, 10] + expect(cursor2.getBufferPosition()).toEqual [1, 11] expect(cursor3.getBufferPosition()).toEqual [2, 39] it "does not fail at position [0, 0]", -> editSession.setCursorBufferPosition([0, 0]) editSession.moveCursorToBeginningOfWord() - it "works when the preceding line is blank", -> - editSession.setCursorBufferPosition([10, 0]) + it "works when the previous line is blank", -> + editSession.setCursorBufferPosition([11, 0]) editSession.moveCursorToBeginningOfWord() - expect(editSession.getCursorBufferPosition()).toEqual [9, 0] + expect(editSession.getCursorBufferPosition()).toEqual [10, 0] describe ".moveCursorToEndOfWord()", -> it "moves the cursor to the end of the word", -> diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index efc80fbf4..cc4c13c30 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -149,10 +149,14 @@ class Cursor previousLinesRange = [[previousNonBlankRow, 0], currentBufferPosition] beginningOfWordPosition = currentBufferPosition - @editSession.backwardsScanInRange (options.wordRegex ? config.get("editor.wordRegex")), previousLinesRange, (match, matchRange, { stop }) => + + wordSeparators = config.get("editor.wordSeparators") + wordSeparatorsRegex = new RegExp("^[\t ]*\n|[^\\s#{_.escapeRegExp(wordSeparators)}]+|[#{_.escapeRegExp(wordSeparators)}]+", "m") + @editSession.backwardsScanInRange (options.wordRegex ? wordSeparatorsRegex), previousLinesRange, (match, matchRange, { stop }) => if matchRange.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious beginningOfWordPosition = matchRange.start stop() + beginningOfWordPosition getEndOfCurrentWordBufferPosition: (options = {}) -> diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 55600c0fa..affeefc68 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -20,6 +20,7 @@ class Editor extends View autoIndent: true autoIndentOnPaste: false wordRegex: /(\w+)|([^\w\n]+)/g + wordSeparators: "./\()\"’-:,.;<>~!@#$%^&*|+=[]{}`~?" @content: (params) -> @div class: @classes(params), tabindex: -1, => From dac92ca6e748cec3d8bcddf361c16fa3a6fca852 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 29 Jan 2013 12:15:43 -0800 Subject: [PATCH 12/27] Make cursor.moveCursorToBeginningOfWord behave like vim --- spec/app/edit-session-spec.coffee | 28 ++++++++++++++++++++++------ src/app/cursor.coffee | 15 ++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index a16554f84..a6c67536f 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -16,7 +16,7 @@ describe "EditSession", -> afterEach -> fixturesProject.destroy() - fdescribe "cursor", -> + describe "cursor", -> describe ".getCursor()", -> it "returns the most recently created cursor", -> editSession.addCursorAtScreenPosition([1, 0]) @@ -266,7 +266,7 @@ describe "EditSession", -> editSession.moveCursorToFirstCharacterOfLine() expect(editSession.getCursorBufferPosition()).toEqual [10, 0] - describe ".moveCursorToBeginningOfWord()", -> + fdescribe ".moveCursorToBeginningOfWord()", -> it "moves the cursor to the beginning of the word", -> editSession.setCursorBufferPosition [0, 8] editSession.addCursorAtBufferPosition [1, 12] @@ -283,12 +283,17 @@ describe "EditSession", -> editSession.setCursorBufferPosition([0, 0]) editSession.moveCursorToBeginningOfWord() - it "works when the previous line is blank", -> + it "treats lines with only whitespace as a word", -> editSession.setCursorBufferPosition([11, 0]) editSession.moveCursorToBeginningOfWord() expect(editSession.getCursorBufferPosition()).toEqual [10, 0] - describe ".moveCursorToEndOfWord()", -> + it "works when the current line is blank", -> + editSession.setCursorBufferPosition([10, 0]) + editSession.moveCursorToBeginningOfWord() + expect(editSession.getCursorBufferPosition()).toEqual [9, 2] + + fdescribe ".moveCursorToEndOfWord()", -> it "moves the cursor to the end of the word", -> editSession.setCursorBufferPosition [0, 6] editSession.addCursorAtBufferPosition [1, 10] @@ -298,8 +303,8 @@ describe "EditSession", -> editSession.moveCursorToEndOfWord() expect(cursor1.getBufferPosition()).toEqual [0, 13] - expect(cursor2.getBufferPosition()).toEqual [1, 13] - expect(cursor3.getBufferPosition()).toEqual [3, 4] + expect(cursor2.getBufferPosition()).toEqual [1, 12] + expect(cursor3.getBufferPosition()).toEqual [3, 7] it "does not blow up when there is no next word", -> editSession.setCursorBufferPosition [Infinity, Infinity] @@ -307,6 +312,17 @@ describe "EditSession", -> editSession.moveCursorToEndOfWord() expect(editSession.getCursorBufferPosition()).toEqual endPosition + it "treats lines with only whitespace as a word", -> + editSession.setCursorBufferPosition([9, 4]) + editSession.moveCursorToEndOfWord() + expect(editSession.getCursorBufferPosition()).toEqual [10, 0] + + it "works when the current line is blank", -> + editSession.setCursorBufferPosition([10, 0]) + editSession.moveCursorToEndOfWord() + expect(editSession.getCursorBufferPosition()).toEqual [11, 8] + + describe ".getCurrentParagraphBufferRange()", -> it "returns the buffer range of the current paragraph, delimited by blank lines or the beginning / end of the file", -> buffer.setText """ diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index cc4c13c30..46ff27d5b 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -142,6 +142,10 @@ class Cursor if position = @getEndOfCurrentWordBufferPosition() @setBufferPosition(position) + wordSeparatorsRegExp: -> + wordSeparators = config.get("editor.wordSeparators") + new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(wordSeparators)}]+|[#{_.escapeRegExp(wordSeparators)}]+", "mg") + getBeginningOfCurrentWordBufferPosition: (options = {}) -> allowPrevious = options.allowPrevious ? true currentBufferPosition = @getBufferPosition() @@ -150,12 +154,10 @@ class Cursor beginningOfWordPosition = currentBufferPosition - wordSeparators = config.get("editor.wordSeparators") - wordSeparatorsRegex = new RegExp("^[\t ]*\n|[^\\s#{_.escapeRegExp(wordSeparators)}]+|[#{_.escapeRegExp(wordSeparators)}]+", "m") - @editSession.backwardsScanInRange (options.wordRegex ? wordSeparatorsRegex), previousLinesRange, (match, matchRange, { stop }) => + @editSession.backwardsScanInRange (options.wordRegex ? @wordSeparatorsRegExp()), previousLinesRange, (match, matchRange, { stop }) => if matchRange.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious beginningOfWordPosition = matchRange.start - stop() + stop() unless beginningOfWordPosition.isEqual(currentBufferPosition) beginningOfWordPosition @@ -165,8 +167,11 @@ class Cursor range = [currentBufferPosition, @editSession.getEofBufferPosition()] endOfWordPosition = null - @editSession.scanInRange (options.wordRegex ? config.get("editor.wordRegex")), range, (match, matchRange, { stop }) => + @editSession.scanInRange (options.wordRegex ? @wordSeparatorsRegExp()), + range, (match, matchRange, { stop }) => endOfWordPosition = matchRange.end + return if endOfWordPosition.isEqual(currentBufferPosition) + if not allowNext and matchRange.start.isGreaterThan(currentBufferPosition) endOfWordPosition = currentBufferPosition stop() From 8973a66cfe6d34cf6407c7a353464be499d6e523 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 29 Jan 2013 16:33:06 -0800 Subject: [PATCH 13/27] selection.selectWord will consider whitespace a word --- src/app/cursor.coffee | 5 +++++ src/app/selection.coffee | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index 46ff27d5b..3f39bb572 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -58,6 +58,11 @@ class Cursor isLastCursor: -> this == @editSession.getCursor() + isSurroundedByWhitespace: -> + {row, column} = @getBufferPosition() + range = [[row, column + 1], [row, Math.max(0, column - 1)]] + /^\s+$/.test @editSession.getTextInBufferRange(range) + autoscrolled: -> @needsAutoscroll = false diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 8123d1157..abc702103 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -96,7 +96,10 @@ class Selection @screenRangeChanged() selectWord: -> - @setBufferRange(@cursor.getCurrentWordBufferRange()) + options = {} + options.wordRegex = /[\t ]*/ if @cursor.isSurroundedByWhitespace() + + @setBufferRange(@cursor.getCurrentWordBufferRange(options)) @wordwise = true @initialScreenRange = @getScreenRange() From 46aefc75abf6205051519e02e0ee78695e763d27 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 29 Jan 2013 16:45:24 -0800 Subject: [PATCH 14/27] Make EditSession specs match vim style word behavior --- spec/app/edit-session-spec.coffee | 75 ++++++++++++++++--------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index a6c67536f..327f6ab76 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -266,7 +266,7 @@ describe "EditSession", -> editSession.moveCursorToFirstCharacterOfLine() expect(editSession.getCursorBufferPosition()).toEqual [10, 0] - fdescribe ".moveCursorToBeginningOfWord()", -> + describe ".moveCursorToBeginningOfWord()", -> it "moves the cursor to the beginning of the word", -> editSession.setCursorBufferPosition [0, 8] editSession.addCursorAtBufferPosition [1, 12] @@ -293,7 +293,7 @@ describe "EditSession", -> editSession.moveCursorToBeginningOfWord() expect(editSession.getCursorBufferPosition()).toEqual [9, 2] - fdescribe ".moveCursorToEndOfWord()", -> + describe ".moveCursorToEndOfWord()", -> it "moves the cursor to the end of the word", -> editSession.setCursorBufferPosition [0, 6] editSession.addCursorAtBufferPosition [1, 10] @@ -534,13 +534,13 @@ describe "EditSession", -> expect(editSession.getCursors().length).toBe 2 [cursor1, cursor2] = editSession.getCursors() expect(cursor1.getBufferPosition()).toEqual [0,4] - expect(cursor2.getBufferPosition()).toEqual [3,44] + expect(cursor2.getBufferPosition()).toEqual [3,47] expect(editSession.getSelections().length).toBe 2 [selection1, selection2] = editSession.getSelections() expect(selection1.getBufferRange()).toEqual [[0,4], [0,13]] expect(selection1.isReversed()).toBeTruthy() - expect(selection2.getBufferRange()).toEqual [[3,44], [3,49]] + expect(selection2.getBufferRange()).toEqual [[3,47], [3,49]] expect(selection2.isReversed()).toBeTruthy() describe ".selectToEndOfWord()", -> @@ -553,40 +553,44 @@ describe "EditSession", -> expect(editSession.getCursors().length).toBe 2 [cursor1, cursor2] = editSession.getCursors() expect(cursor1.getBufferPosition()).toEqual [0,13] - expect(cursor2.getBufferPosition()).toEqual [3,51] + expect(cursor2.getBufferPosition()).toEqual [3,50] expect(editSession.getSelections().length).toBe 2 [selection1, selection2] = editSession.getSelections() expect(selection1.getBufferRange()).toEqual [[0,4], [0,13]] expect(selection1.isReversed()).toBeFalsy() - expect(selection2.getBufferRange()).toEqual [[3,48], [3,51]] + expect(selection2.getBufferRange()).toEqual [[3,48], [3,50]] expect(selection2.isReversed()).toBeFalsy() describe ".selectWord()", -> - describe "when the cursor is inside a word", -> - it "selects the entire word", -> - editSession.setCursorScreenPosition([0, 8]) - editSession.selectWord() - expect(editSession.getSelectedText()).toBe 'quicksort' + describe "when the cursor is inside a word", -> + it "selects the entire word", -> + editSession.setCursorScreenPosition([0, 8]) + editSession.selectWord() + expect(editSession.getSelectedText()).toBe 'quicksort' - describe "when the cursor is between two words", -> - it "selects both words", -> - editSession.setCursorScreenPosition([0, 4]) - editSession.selectWord() - expect(editSession.getSelectedText()).toBe ' quicksort' + describe "when the cursor is between two words", -> + it "selects the nearest word", -> + editSession.setCursorScreenPosition([0, 4]) + editSession.selectWord() + expect(editSession.getSelectedText()).toBe 'quicksort' - describe "when the cursor is inside a region of whitespace", -> - it "selects the whitespace region", -> - editSession.setCursorScreenPosition([5, 2]) - editSession.selectWord() - expect(editSession.getSelectedBufferRange()).toEqual [[5, 0], [5, 6]] + describe "when the cursor is inside a region of whitespace", -> + it "selects the whitespace region", -> + editSession.setCursorScreenPosition([5, 2]) + editSession.selectWord() + expect(editSession.getSelectedBufferRange()).toEqual [[5, 0], [5, 6]] - describe "when the cursor is at the end of the text", -> - it "select the previous word", -> - editSession.buffer.append 'word' - editSession.moveCursorToBottom() - editSession.selectWord() - expect(editSession.getSelectedBufferRange()).toEqual [[12, 2], [12, 6]] + editSession.setCursorScreenPosition([5, 0]) + editSession.selectWord() + expect(editSession.getSelectedBufferRange()).toEqual [[5, 0], [5, 6]] + + describe "when the cursor is at the end of the text", -> + it "select the previous word", -> + editSession.buffer.append 'word' + editSession.moveCursorToBottom() + editSession.selectWord() + expect(editSession.getSelectedBufferRange()).toEqual [[12, 2], [12, 6]] describe ".setSelectedBufferRanges(ranges)", -> it "clears existing selections and creates selections for each of the given ranges", -> @@ -1126,25 +1130,26 @@ describe "EditSession", -> describe "when no text is selected", -> it "deletes all text between the cursor and the beginning of the word", -> editSession.setCursorBufferPosition([1, 24]) - editSession.addCursorAtBufferPosition([2, 5]) + editSession.addCursorAtBufferPosition([3, 5]) [cursor1, cursor2] = editSession.getCursors() editSession.backspaceToBeginningOfWord() expect(buffer.lineForRow(1)).toBe ' var sort = function(ems) {' - expect(buffer.lineForRow(2)).toBe ' f (items.length <= 1) return items;' + expect(buffer.lineForRow(3)).toBe ' ar pivot = items.shift(), current, left = [], right = [];' expect(cursor1.getBufferPosition()).toEqual [1, 22] - expect(cursor2.getBufferPosition()).toEqual [2, 4] + expect(cursor2.getBufferPosition()).toEqual [3, 4] editSession.backspaceToBeginningOfWord() expect(buffer.lineForRow(1)).toBe ' var sort = functionems) {' - expect(buffer.lineForRow(2)).toBe 'f (items.length <= 1) return items;' + expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return itemsar pivot = items.shift(), current, left = [], right = [];' expect(cursor1.getBufferPosition()).toEqual [1, 21] - expect(cursor2.getBufferPosition()).toEqual [2, 0] + expect(cursor2.getBufferPosition()).toEqual [2, 39] editSession.backspaceToBeginningOfWord() - expect(buffer.lineForRow(1)).toBe ' var sort = emsf (items.length <= 1) return items;' + expect(buffer.lineForRow(1)).toBe ' var sort = ems) {' + expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return ar pivot = items.shift(), current, left = [], right = [];' expect(cursor1.getBufferPosition()).toEqual [1, 13] - expect(cursor2.getBufferPosition()).toEqual [1, 16] + expect(cursor2.getBufferPosition()).toEqual [2, 34] describe "when text is selected", -> it "deletes only selected text", -> @@ -1312,7 +1317,7 @@ describe "EditSession", -> expect(cursor2.getBufferPosition()).toEqual [2, 5] editSession.deleteToEndOfWord() - expect(buffer.lineForRow(1)).toBe ' var sort = function(it' + expect(buffer.lineForRow(1)).toBe ' var sort = function(it {' expect(buffer.lineForRow(2)).toBe ' iitems.length <= 1) return items;' expect(cursor1.getBufferPosition()).toEqual [1, 24] expect(cursor2.getBufferPosition()).toEqual [2, 5] From 167b9c28fa7355c715dc19fdd360e64787e9b369 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 29 Jan 2013 17:06:39 -0800 Subject: [PATCH 15/27] Rename wordSeparators to nonWordCharacters --- src/app/cursor.coffee | 10 +++++----- src/app/editor.coffee | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index 3f39bb572..aaffe22d6 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -147,9 +147,9 @@ class Cursor if position = @getEndOfCurrentWordBufferPosition() @setBufferPosition(position) - wordSeparatorsRegExp: -> - wordSeparators = config.get("editor.wordSeparators") - new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(wordSeparators)}]+|[#{_.escapeRegExp(wordSeparators)}]+", "mg") + wordRegExp: -> + nonWordCharacters = config.get("editor.nonWordCharacters") + new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "mg") getBeginningOfCurrentWordBufferPosition: (options = {}) -> allowPrevious = options.allowPrevious ? true @@ -159,7 +159,7 @@ class Cursor beginningOfWordPosition = currentBufferPosition - @editSession.backwardsScanInRange (options.wordRegex ? @wordSeparatorsRegExp()), previousLinesRange, (match, matchRange, { stop }) => + @editSession.backwardsScanInRange (options.wordRegex ? @wordRegExp()), previousLinesRange, (match, matchRange, { stop }) => if matchRange.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious beginningOfWordPosition = matchRange.start stop() unless beginningOfWordPosition.isEqual(currentBufferPosition) @@ -172,7 +172,7 @@ class Cursor range = [currentBufferPosition, @editSession.getEofBufferPosition()] endOfWordPosition = null - @editSession.scanInRange (options.wordRegex ? @wordSeparatorsRegExp()), + @editSession.scanInRange (options.wordRegex ? @wordRegExp()), range, (match, matchRange, { stop }) => endOfWordPosition = matchRange.end return if endOfWordPosition.isEqual(currentBufferPosition) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index affeefc68..bdd25ba67 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -19,8 +19,7 @@ class Editor extends View autosave: false autoIndent: true autoIndentOnPaste: false - wordRegex: /(\w+)|([^\w\n]+)/g - wordSeparators: "./\()\"’-:,.;<>~!@#$%^&*|+=[]{}`~?" + nonWordCharacters: "./\()\"’-:,.;<>~!@#$%^&*|+=[]{}`~?" @content: (params) -> @div class: @classes(params), tabindex: -1, => From c71f58a65231c3622ee7e5a1ea8529e35901efb2 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 29 Jan 2013 17:08:07 -0800 Subject: [PATCH 16/27] :lipstick: --- src/app/cursor.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index aaffe22d6..c3e0a0591 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -55,6 +55,10 @@ class Cursor isVisible: -> @visible + wordRegExp: -> + nonWordCharacters = config.get("editor.nonWordCharacters") + new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "mg") + isLastCursor: -> this == @editSession.getCursor() @@ -147,10 +151,6 @@ class Cursor if position = @getEndOfCurrentWordBufferPosition() @setBufferPosition(position) - wordRegExp: -> - nonWordCharacters = config.get("editor.nonWordCharacters") - new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "mg") - getBeginningOfCurrentWordBufferPosition: (options = {}) -> allowPrevious = options.allowPrevious ? true currentBufferPosition = @getBufferPosition() From c32836ad2c1b9e4e3e7b8f318aaa15d535de28e4 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 29 Jan 2013 17:10:27 -0800 Subject: [PATCH 17/27] _ and - both considered non word characters. Fixes #82 Maybe @defunkt wanted the reverse though (consider _ and - word characters)? Either way, it's a config option you can change now. --- src/app/editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index bdd25ba67..9a6710d8e 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -19,7 +19,7 @@ class Editor extends View autosave: false autoIndent: true autoIndentOnPaste: false - nonWordCharacters: "./\()\"’-:,.;<>~!@#$%^&*|+=[]{}`~?" + nonWordCharacters: "./\()\"’-_:,.;<>~!@#$%^&*|+=[]{}`~?" @content: (params) -> @div class: @classes(params), tabindex: -1, => From a34b9296e9c38da457f95245fea450caa6eb1444 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 29 Jan 2013 17:14:10 -0800 Subject: [PATCH 18/27] Escape \ in editor.nonWordCharachters --- src/app/editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 9a6710d8e..7d4ee0382 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -19,7 +19,7 @@ class Editor extends View autosave: false autoIndent: true autoIndentOnPaste: false - nonWordCharacters: "./\()\"’-_:,.;<>~!@#$%^&*|+=[]{}`~?" + nonWordCharacters: "./\\()\"’-_:,.;<>~!@#$%^&*|+=[]{}`~?" @content: (params) -> @div class: @classes(params), tabindex: -1, => From 05314ae2150298fca37b63c2691d9f534a1f4706 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 17:16:33 -0800 Subject: [PATCH 19/27] Call delete() from deleteRows() --- src/app/buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index 9e0cd94ab..db7de2e75 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -194,7 +194,7 @@ class Buffer startPoint = [start, 0] endPoint = [end + 1, 0] - @change(new Range(startPoint, endPoint), '') + @delete(new Range(startPoint, endPoint)) append: (text) -> @insert(@getEofPosition(), text) From 7a186b1ab64e6f0e7838e2687fe3373ef3c6361d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 29 Jan 2013 18:21:38 -0800 Subject: [PATCH 20/27] Add python bundle as submodule --- .gitmodules | 3 +++ vendor/packages/python.tmbundle | 1 + 2 files changed, 4 insertions(+) create mode 160000 vendor/packages/python.tmbundle diff --git a/.gitmodules b/.gitmodules index caa40d8c9..6495d34a2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,6 @@ [submodule "vendor/packages/property-list.tmbundle"] path = vendor/packages/property-list.tmbundle url = https://github.com/textmate/property-list.tmbundle.git +[submodule "vendor/packages/python.tmbundle"] + path = vendor/packages/python.tmbundle + url = https://github.com/textmate/python.tmbundle diff --git a/vendor/packages/python.tmbundle b/vendor/packages/python.tmbundle new file mode 160000 index 000000000..3675c22ae --- /dev/null +++ b/vendor/packages/python.tmbundle @@ -0,0 +1 @@ +Subproject commit 3675c22ae891419b27a80c58001831d01e73d431 From 1a8986aae2f5d0035252e4761879ee76b6bd1913 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 30 Jan 2013 08:02:19 -0800 Subject: [PATCH 21/27] Fix start/end for range --- src/app/cursor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index c3e0a0591..bf6e058ae 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -64,7 +64,7 @@ class Cursor isSurroundedByWhitespace: -> {row, column} = @getBufferPosition() - range = [[row, column + 1], [row, Math.max(0, column - 1)]] + range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]] /^\s+$/.test @editSession.getTextInBufferRange(range) autoscrolled: -> From 086c7ef98712ae824f3296a5192edf95d1ac7ace Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 30 Jan 2013 08:15:28 -0800 Subject: [PATCH 22/27] multiline regex not needed --- src/app/cursor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index bf6e058ae..04d13ee37 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -57,7 +57,7 @@ class Cursor wordRegExp: -> nonWordCharacters = config.get("editor.nonWordCharacters") - new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "mg") + new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "g") isLastCursor: -> this == @editSession.getCursor() From f698d7e9dcff5c9aebef3064d7d9236ccdcd3c17 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 30 Jan 2013 08:20:11 -0800 Subject: [PATCH 23/27] :lipstick: --- src/app/cursor.coffee | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index 04d13ee37..659e91e43 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -155,16 +155,16 @@ class Cursor allowPrevious = options.allowPrevious ? true currentBufferPosition = @getBufferPosition() previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row) - previousLinesRange = [[previousNonBlankRow, 0], currentBufferPosition] + range = [[previousNonBlankRow, 0], currentBufferPosition] - beginningOfWordPosition = currentBufferPosition - - @editSession.backwardsScanInRange (options.wordRegex ? @wordRegExp()), previousLinesRange, (match, matchRange, { stop }) => + beginningOfWordPosition = null + @editSession.backwardsScanInRange (options.wordRegex ? @wordRegExp()), range, (match, matchRange, { stop }) => if matchRange.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious beginningOfWordPosition = matchRange.start - stop() unless beginningOfWordPosition.isEqual(currentBufferPosition) + if not beginningOfWordPosition?.isEqual(currentBufferPosition) + stop() - beginningOfWordPosition + beginningOfWordPosition or currentBufferPosition getEndOfCurrentWordBufferPosition: (options = {}) -> allowNext = options.allowNext ? true @@ -172,14 +172,13 @@ class Cursor range = [currentBufferPosition, @editSession.getEofBufferPosition()] endOfWordPosition = null - @editSession.scanInRange (options.wordRegex ? @wordRegExp()), - range, (match, matchRange, { stop }) => + @editSession.scanInRange (options.wordRegex ? @wordRegExp()), range, (match, matchRange, { stop }) => endOfWordPosition = matchRange.end - return if endOfWordPosition.isEqual(currentBufferPosition) - - if not allowNext and matchRange.start.isGreaterThan(currentBufferPosition) + if matchRange.start.isGreaterThan(currentBufferPosition) and not allowNext endOfWordPosition = currentBufferPosition - stop() + if not endOfWordPosition.isEqual(currentBufferPosition) + stop() + endOfWordPosition or currentBufferPosition getCurrentWordBufferRange: (options={}) -> From 97fa9d522a05b00aa3299196015859af10ec9626 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 30 Jan 2013 08:39:32 -0800 Subject: [PATCH 24/27] end/beginning word implementations are now more similar --- spec/app/edit-session-spec.coffee | 7 ++++++- src/app/cursor.coffee | 7 +++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index 327f6ab76..c77ddd15c 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -570,11 +570,16 @@ describe "EditSession", -> expect(editSession.getSelectedText()).toBe 'quicksort' describe "when the cursor is between two words", -> - it "selects the nearest word", -> + it "selects the word the cursor is on", -> editSession.setCursorScreenPosition([0, 4]) editSession.selectWord() expect(editSession.getSelectedText()).toBe 'quicksort' + editSession.setCursorScreenPosition([0, 3]) + editSession.selectWord() + expect(editSession.getSelectedText()).toBe 'var' + + describe "when the cursor is inside a region of whitespace", -> it "selects the whitespace region", -> editSession.setCursorScreenPosition([5, 2]) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index 659e91e43..8ac44739e 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -173,10 +173,9 @@ class Cursor endOfWordPosition = null @editSession.scanInRange (options.wordRegex ? @wordRegExp()), range, (match, matchRange, { stop }) => - endOfWordPosition = matchRange.end - if matchRange.start.isGreaterThan(currentBufferPosition) and not allowNext - endOfWordPosition = currentBufferPosition - if not endOfWordPosition.isEqual(currentBufferPosition) + if matchRange.start.isLessThanOrEqual(currentBufferPosition) or allowNext + endOfWordPosition = matchRange.end + if not endOfWordPosition?.isEqual(currentBufferPosition) stop() endOfWordPosition or currentBufferPosition From 4db876aed130f62497dafc0f96782f5123f8e56f Mon Sep 17 00:00:00 2001 From: Corey Johnson & Kevin Sawicki Date: Wed, 30 Jan 2013 09:08:12 -0800 Subject: [PATCH 25/27] Deleting before fold no longer unfolds after undo Use same row delta computation for updating start and end rows in folds. --- spec/app/display-buffer-spec.coffee | 14 ++++++++++++++ src/app/fold.coffee | 30 ++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/spec/app/display-buffer-spec.coffee b/spec/app/display-buffer-spec.coffee index cd7ca484c..7444a3859 100644 --- a/spec/app/display-buffer-spec.coffee +++ b/spec/app/display-buffer-spec.coffee @@ -495,6 +495,20 @@ describe "DisplayBuffer", -> expect(displayBuffer.lineForRow(8).text).toMatch /^9-+/ expect(displayBuffer.lineForRow(10).fold).toBeDefined() + describe "when the line being deleted preceeds a fold", -> + describe "when the command is undone", -> + it "restores the line and preserves the fold", -> + editSession.setCursorBufferPosition([4]) + editSession.foldCurrentRow() + expect(editSession.isFoldedAtScreenRow(4)).toBeTruthy() + editSession.setCursorBufferPosition([3]) + editSession.deleteLine() + expect(editSession.isFoldedAtScreenRow(3)).toBeTruthy() + expect(buffer.lineForRow(3)).toBe ' while(items.length > 0) {' + editSession.undo() + expect(editSession.isFoldedAtScreenRow(4)).toBeTruthy() + expect(buffer.lineForRow(3)).toBe ' var pivot = items.shift(), current, left = [], right = [];' + describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", -> beforeEach -> displayBuffer.setSoftWrapColumn(50) diff --git a/src/app/fold.coffee b/src/app/fold.coffee index 57bea3cfa..50e5d34ba 100644 --- a/src/app/fold.coffee +++ b/src/app/fold.coffee @@ -36,8 +36,8 @@ class Fold @displayBuffer.unregisterFold(@startRow, this) return - @updateStartRow(event) - @updateEndRow(event) + @startRow += @getRowDelta(event, @startRow) + @endRow += @getRowDelta(event, @endRow) if @startRow != oldStartRow @displayBuffer.unregisterFold(oldStartRow, this) @@ -49,26 +49,12 @@ class Fold isContainedByFold: (fold) -> @isContainedByRange(fold.getBufferRange()) - updateStartRow: (event) -> + getRowDelta: (event, row) -> { newRange, oldRange } = event - if oldRange.end.row < @startRow - delta = newRange.end.row - oldRange.end.row - else if newRange.end.row < @startRow - delta = newRange.end.row - @startRow + if oldRange.end.row <= row + newRange.end.row - oldRange.end.row + else if newRange.end.row < row + newRange.end.row - row else - delta = 0 - - @startRow += delta - - updateEndRow: (event) -> - { newRange, oldRange } = event - - if oldRange.end.row <= @endRow - delta = newRange.end.row - oldRange.end.row - else if newRange.end.row <= @endRow - delta = newRange.end.row - @endRow - else - delta = 0 - - @endRow += delta + 0 From 8dbcefa9327defa03f2f7202fd7fbfc5bba14aa7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 30 Jan 2013 11:48:58 -0800 Subject: [PATCH 26/27] Style color of folded line numbers --- spec/app/editor-spec.coffee | 5 +++++ src/app/edit-session.coffee | 4 ++++ src/app/editor.coffee | 1 + src/app/gutter.coffee | 9 ++++++--- themes/Atom - Dark/editor.css | 11 ++++++++++- themes/Atom - Light/editor.css | 11 ++++++++++- 6 files changed, 36 insertions(+), 5 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 9dcffb4cf..753aa7c85 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1730,6 +1730,11 @@ describe "Editor", -> fold.destroy() expect(editor.gutter.find('.line-number').length).toBe 13 + it "styles folded line numbers", -> + editor.createFold(3, 5) + expect(editor.gutter.find('.line-number.fold').length).toBe 1 + expect(editor.gutter.find('.line-number.fold:eq(0)').text()).toBe '4' + describe "when the scrollView is scrolled to the right", -> it "adds a drop shadow to the gutter", -> editor.attachToDom() diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 12adae4d6..f0be412a5 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -307,6 +307,10 @@ class EditSession fold.destroy() @setCursorBufferPosition([fold.startRow, 0]) + isFoldedAtBufferRow: (bufferRow) -> + screenRow = @screenPositionForBufferPosition([bufferRow]).row + @isFoldedAtScreenRow(screenRow) + isFoldedAtScreenRow: (screenRow) -> @lineForScreenRow(screenRow)?.fold? diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 7d4ee0382..6daf0f276 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -276,6 +276,7 @@ class Editor extends View destroyFold: (foldId) -> @activeEditSession.destroyFold(foldId) destroyFoldsContainingBufferRow: (bufferRow) -> @activeEditSession.destroyFoldsContainingBufferRow(bufferRow) isFoldedAtScreenRow: (screenRow) -> @activeEditSession.isFoldedAtScreenRow(screenRow) + isFoldedAtBufferRow: (bufferRow) -> @activeEditSession.isFoldedAtBufferRow(bufferRow) lineForScreenRow: (screenRow) -> @activeEditSession.lineForScreenRow(screenRow) linesForScreenRows: (start, end) -> @activeEditSession.linesForScreenRows(start, end) diff --git a/src/app/gutter.coffee b/src/app/gutter.coffee index e1e2f8a82..9f7de8f7a 100644 --- a/src/app/gutter.coffee +++ b/src/app/gutter.coffee @@ -58,16 +58,19 @@ class Gutter extends View @renderLineNumbers(renderFrom, renderTo) if performUpdate renderLineNumbers: (startScreenRow, endScreenRow) -> - rows = @editor().bufferRowsForScreenRows(startScreenRow, endScreenRow) + editor = @editor() + rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) - cursorScreenRow = @editor().getCursorScreenPosition().row + cursorScreenRow = editor.getCursorScreenPosition().row @lineNumbers[0].innerHTML = $$$ -> for row in rows if row == lastScreenRow rowValue = '•' else rowValue = row + 1 - @div {class: 'line-number'}, rowValue + classes = ['line-number'] + classes.push('fold') if editor.isFoldedAtBufferRow(row) + @div rowValue, class: classes.join(' ') lastScreenRow = row @calculateWidth() diff --git a/themes/Atom - Dark/editor.css b/themes/Atom - Dark/editor.css index a9a8429d7..64d73ff3b 100644 --- a/themes/Atom - Dark/editor.css +++ b/themes/Atom - Dark/editor.css @@ -43,10 +43,19 @@ -webkit-animation-iteration-count: 1; } -.editor .fold { +.editor .line.fold { background-color: #444; } +.editor .gutter .line-number.fold { + color: #FBA0E3; + opacity: .75; +} + +.editor .gutter .line-number.fold.cursor-line { + opacity: 1; +} + .editor .fold.selected { background-color: #244; } diff --git a/themes/Atom - Light/editor.css b/themes/Atom - Light/editor.css index 9326721b9..ae5837c62 100644 --- a/themes/Atom - Light/editor.css +++ b/themes/Atom - Light/editor.css @@ -46,10 +46,19 @@ -webkit-animation-iteration-count: 1; } -.editor .fold { +.editor .line.fold { background-color: #444; } +.editor .gutter .line-number.fold { + color: #FBA0E3; + opacity: .75; +} + +.editor .gutter .line-number.fold.cursor-line { + opacity: 1; +} + .editor .fold.selected { background-color: #244; } From f90d29262cb786e4a963bb68cb46ae65d6172dd4 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 30 Jan 2013 11:49:09 -0800 Subject: [PATCH 27/27] Use octicon as folding indicator --- spec/app/editor-spec.coffee | 5 ++++- src/app/editor.coffee | 4 ++-- themes/Atom - Dark/editor.css | 19 +++++++++++-------- themes/Atom - Light/editor.css | 19 +++++++++++-------- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 753aa7c85..433a890e7 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1903,16 +1903,18 @@ describe "Editor", -> editor.attachToDom() describe "when a fold-selection event is triggered", -> - it "folds the lines covered by the selection into a single line with a fold class", -> + it "folds the lines covered by the selection into a single line with a fold class and marker", -> editor.getSelection().setBufferRange(new Range([4, 29], [7, 4])) editor.trigger 'editor:fold-selection' expect(editor.renderedLines.find('.line:eq(4)')).toHaveClass('fold') + expect(editor.renderedLines.find('.line:eq(4) > .fold-marker')).toExist() expect(editor.renderedLines.find('.line:eq(5)').text()).toBe '8' expect(editor.getSelection().isEmpty()).toBeTruthy() expect(editor.getCursorScreenPosition()).toEqual [5, 0] + describe "when a fold placeholder line is clicked", -> it "removes the associated fold and places the cursor at its beginning", -> editor.setCursorBufferPosition([3,0]) @@ -1921,6 +1923,7 @@ describe "Editor", -> editor.find('.fold.line').mousedown() expect(editor.find('.fold')).not.toExist() + expect(editor.find('.fold-marker')).not.toExist() expect(editor.renderedLines.find('.line:eq(4)').text()).toMatch /4-+/ expect(editor.renderedLines.find('.line:eq(5)').text()).toMatch /5/ diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 6daf0f276..79fc4fba0 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -1059,8 +1059,6 @@ class Editor extends View if fold = screenLine.fold lineAttributes = { class: 'fold line', 'fold-id': fold.id } - if @activeEditSession.selectionIntersectsBufferRange(fold.getBufferRange()) - lineAttributes.class += ' selected' else lineAttributes = { class: 'line' } @@ -1093,6 +1091,8 @@ class Editor extends View if invisibles.eol line.push("") + line.push("") if fold + line.push('') line.join('') diff --git a/themes/Atom - Dark/editor.css b/themes/Atom - Dark/editor.css index 64d73ff3b..d15b4a7e0 100644 --- a/themes/Atom - Dark/editor.css +++ b/themes/Atom - Dark/editor.css @@ -43,21 +43,24 @@ -webkit-animation-iteration-count: 1; } -.editor .line.fold { - background-color: #444; -} - .editor .gutter .line-number.fold { - color: #FBA0E3; - opacity: .75; + color: #fba0e3; + opacity: .8; } .editor .gutter .line-number.fold.cursor-line { opacity: 1; } -.editor .fold.selected { - background-color: #244; +.editor .fold-marker:before { + content: '\f25e'; + font-family: 'Octicons Regular'; + display: inline-block; + margin-left: .5em; + margin-top: .1em; + line-height: .8em; + -webkit-font-smoothing: antialiased; + color: #fba0e3; } .editor .invisible { diff --git a/themes/Atom - Light/editor.css b/themes/Atom - Light/editor.css index ae5837c62..14e16fbc5 100644 --- a/themes/Atom - Light/editor.css +++ b/themes/Atom - Light/editor.css @@ -46,21 +46,24 @@ -webkit-animation-iteration-count: 1; } -.editor .line.fold { - background-color: #444; -} - .editor .gutter .line-number.fold { - color: #FBA0E3; - opacity: .75; + color: #fba0e3; + opacity: .8; } .editor .gutter .line-number.fold.cursor-line { opacity: 1; } -.editor .fold.selected { - background-color: #244; +.editor .fold-marker:before { + content: '\f25e'; + font-family: 'Octicons Regular'; + display: inline-block; + margin-left: .5em; + margin-top: .1em; + line-height: .8em; + -webkit-font-smoothing: antialiased; + color: #fba0e3; } .editor .invisible {