diff --git a/spec/app/cursor-spec.coffee b/spec/app/cursor-spec.coffee index 2dcf91af8..7c403981c 100644 --- a/spec/app/cursor-spec.coffee +++ b/spec/app/cursor-spec.coffee @@ -41,47 +41,4 @@ describe "Cursor", -> expect(cursor.isOnEOL()).toBeFalsy() cursor.setScreenPosition([1,30]) - expect(cursor.isOnEOL()).toBeTruthy() - - describe "vertical auto scroll", -> - beforeEach -> - editor.attachToDom() - editor.focus() - editor.vScrollMargin = 2 - - it "only attempts to scroll when a cursor is visible", -> - setEditorWidthInChars(editor, 20) - setEditorHeightInChars(editor, 10) - editor.setCursorBufferPosition([11,0]) - editor.addCursorAtBufferPosition([0,0]) - editor.addCursorAtBufferPosition([6,50]) - window.advanceClock() - - offscreenScrollHandler = spyOn(editor.getCursors()[0], 'autoScrollVertically') - onscreenScrollHandler = spyOn(editor.getCursors()[1], 'autoScrollVertically') - anotherOffscreenScrollHandler = spyOn(editor.getCursors()[2], 'autoScrollVertically') - - editor.moveCursorRight() - window.advanceClock() - expect(offscreenScrollHandler).not.toHaveBeenCalled() - expect(onscreenScrollHandler).toHaveBeenCalled() - expect(anotherOffscreenScrollHandler).not.toHaveBeenCalled() - - it "only attempts to scroll once when multiple cursors are visible", -> - setEditorWidthInChars(editor, 20) - setEditorHeightInChars(editor, 10) - editor.setCursorBufferPosition([11,0]) - editor.addCursorAtBufferPosition([0,0]) - editor.addCursorAtBufferPosition([6,0]) - window.advanceClock() - - offscreenScrollHandler = spyOn(editor.getCursors()[0], 'autoScrollVertically') - onscreenScrollHandler = spyOn(editor.getCursors()[1], 'autoScrollVertically') - anotherOnscreenScrollHandler = spyOn(editor.getCursors()[2], 'autoScrollVertically') - - editor.moveCursorRight() - window.advanceClock() - expect(offscreenScrollHandler).not.toHaveBeenCalled() - expect(onscreenScrollHandler).toHaveBeenCalled() - expect(anotherOnscreenScrollHandler).not.toHaveBeenCalled() - + expect(cursor.isOnEOL()).toBeTruthy() \ No newline at end of file diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 8fc17791a..ffbebd862 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -228,52 +228,6 @@ describe "Editor", -> expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) describe "vertical movement", -> - describe "auto-scrolling", -> - beforeEach -> - editor.attachToDom() - editor.focus() - editor.vScrollMargin = 3 - - it "scrolls the buffer with the specified scroll margin when cursor approaches the end of the screen", -> - editor.height(editor.lineHeight * 10) - - _.times 6, -> editor.moveCursorDown() - window.advanceClock() - expect(editor.scroller.scrollTop()).toBe(0) - - editor.moveCursorDown() - window.advanceClock() - expect(editor.scroller.scrollTop()).toBe(editor.lineHeight) - - editor.moveCursorDown() - window.advanceClock() - expect(editor.scroller.scrollTop()).toBe(editor.lineHeight * 2) - - _.times 3, -> editor.moveCursorUp() - window.advanceClock() - expect(editor.scroller.scrollTop()).toBe(editor.lineHeight * 2) - - editor.moveCursorUp() - window.advanceClock() - expect(editor.scroller.scrollTop()).toBe(editor.lineHeight) - - editor.moveCursorUp() - window.advanceClock() - expect(editor.scroller.scrollTop()).toBe(0) - - it "reduces scroll margins when there isn't enough height to maintain them and scroll smoothly", -> - setEditorHeightInChars(editor, 5) - - _.times 3, -> - editor.moveCursorDown() - window.advanceClock() - - expect(editor.scroller.scrollTop()).toBe(editor.lineHeight) - - editor.moveCursorUp() - window.advanceClock() - expect(editor.scroller.scrollTop()).toBe(0) - describe "goal column retention", -> lineLengths = null @@ -353,86 +307,6 @@ describe "Editor", -> editor.moveCursorUp() expect(editor.getCursorScreenPosition().column).toBe 0 - describe "horizontal movement", -> - describe "auto-scrolling", -> - charWidth = null - beforeEach -> - editor.attachToDom() - {charWidth} = editor - editor.hScrollMargin = 5 - - it "scrolls horizontally to keep the cursor on screen", -> - setEditorWidthInChars(editor, 30) - - # moving right - editor.setCursorScreenPosition([2, 24]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe 0 - - editor.setCursorScreenPosition([2, 25]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe charWidth - - editor.setCursorScreenPosition([2, 28]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe charWidth * 4 - - # moving left - editor.setCursorScreenPosition([2, 9]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe charWidth * 4 - - editor.setCursorScreenPosition([2, 8]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe charWidth * 3 - - editor.setCursorScreenPosition([2, 5]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe 0 - - it "reduces scroll margins when there isn't enough width to maintain them and scroll smoothly", -> - editor.hScrollMargin = 6 - setEditorWidthInChars(editor, 7) - - editor.setCursorScreenPosition([2, 3]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe(0) - - editor.setCursorScreenPosition([2, 4]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe(charWidth) - - editor.setCursorScreenPosition([2, 3]) - window.advanceClock() - expect(editor.scroller.scrollLeft()).toBe(0) - - describe "when soft-wrap is on", -> - beforeEach -> - editor.setSoftWrap(true) - - it "does not scroll the buffer horizontally", -> - editor.width(charWidth * 30) - - # moving right - editor.setCursorScreenPosition([2, 24]) - expect(editor.scroller.scrollLeft()).toBe 0 - - editor.setCursorScreenPosition([2, 25]) - expect(editor.scroller.scrollLeft()).toBe 0 - - editor.setCursorScreenPosition([2, 28]) - expect(editor.scroller.scrollLeft()).toBe 0 - - # moving left - editor.setCursorScreenPosition([2, 9]) - expect(editor.scroller.scrollLeft()).toBe 0 - - editor.setCursorScreenPosition([2, 8]) - expect(editor.scroller.scrollLeft()).toBe 0 - - editor.setCursorScreenPosition([2, 5]) - expect(editor.scroller.scrollLeft()).toBe 0 - describe "when left is pressed on the first column", -> describe "when there is a previous line", -> it "wraps to the end of the previous line", -> @@ -667,6 +541,169 @@ describe "Editor", -> editor.lines.trigger 'mouseup' expect(editor.getSelectedText()).toBe " if (items.length <= 1) return items;" + describe "scrolling", -> + describe "vertical scrolling", -> + beforeEach -> + editor.attachToDom() + editor.focus() + editor.vScrollMargin = 3 + + it "scrolls the buffer with the specified scroll margin when cursor approaches the end of the screen", -> + editor.height(editor.lineHeight * 10) + + _.times 6, -> editor.moveCursorDown() + window.advanceClock() + expect(editor.scroller.scrollTop()).toBe(0) + + editor.moveCursorDown() + window.advanceClock() + expect(editor.scroller.scrollTop()).toBe(editor.lineHeight) + + editor.moveCursorDown() + window.advanceClock() + expect(editor.scroller.scrollTop()).toBe(editor.lineHeight * 2) + + _.times 3, -> editor.moveCursorUp() + window.advanceClock() + expect(editor.scroller.scrollTop()).toBe(editor.lineHeight * 2) + + editor.moveCursorUp() + window.advanceClock() + expect(editor.scroller.scrollTop()).toBe(editor.lineHeight) + + editor.moveCursorUp() + window.advanceClock() + expect(editor.scroller.scrollTop()).toBe(0) + + it "reduces scroll margins when there isn't enough height to maintain them and scroll smoothly", -> + setEditorHeightInChars(editor, 5) + + _.times 3, -> + editor.moveCursorDown() + window.advanceClock() + + expect(editor.scroller.scrollTop()).toBe(editor.lineHeight) + + editor.moveCursorUp() + window.advanceClock() + expect(editor.scroller.scrollTop()).toBe(0) + + describe "horizontal scrolling", -> + charWidth = null + beforeEach -> + editor.attachToDom() + {charWidth} = editor + editor.hScrollMargin = 5 + + it "scrolls horizontally to keep the cursor on screen", -> + setEditorWidthInChars(editor, 30) + + # moving right + editor.setCursorScreenPosition([2, 24]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe 0 + + editor.setCursorScreenPosition([2, 25]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe charWidth + + editor.setCursorScreenPosition([2, 28]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe charWidth * 4 + + # moving left + editor.setCursorScreenPosition([2, 9]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe charWidth * 4 + + editor.setCursorScreenPosition([2, 8]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe charWidth * 3 + + editor.setCursorScreenPosition([2, 5]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe 0 + + it "reduces scroll margins when there isn't enough width to maintain them and scroll smoothly", -> + editor.hScrollMargin = 6 + setEditorWidthInChars(editor, 7) + + editor.setCursorScreenPosition([2, 3]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe(0) + + editor.setCursorScreenPosition([2, 4]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe(charWidth) + + editor.setCursorScreenPosition([2, 3]) + window.advanceClock() + expect(editor.scroller.scrollLeft()).toBe(0) + + describe "when soft-wrap is on", -> + beforeEach -> + editor.setSoftWrap(true) + + it "does not scroll the buffer horizontally", -> + editor.width(charWidth * 30) + + # moving right + editor.setCursorScreenPosition([2, 24]) + expect(editor.scroller.scrollLeft()).toBe 0 + + editor.setCursorScreenPosition([2, 25]) + expect(editor.scroller.scrollLeft()).toBe 0 + + editor.setCursorScreenPosition([2, 28]) + expect(editor.scroller.scrollLeft()).toBe 0 + + # moving left + editor.setCursorScreenPosition([2, 9]) + expect(editor.scroller.scrollLeft()).toBe 0 + + editor.setCursorScreenPosition([2, 8]) + expect(editor.scroller.scrollLeft()).toBe 0 + + editor.setCursorScreenPosition([2, 5]) + expect(editor.scroller.scrollLeft()).toBe 0 + + describe "when there are multiple cursor", -> + beforeEach -> + editor.attachToDom() + editor.focus() + editor.vScrollMargin = 2 + + it "only attempts to scroll when a cursor is visible", -> + setEditorWidthInChars(editor, 20) + setEditorHeightInChars(editor, 10) + editor.setCursorBufferPosition([11,0]) + editor.addCursorAtBufferPosition([0,0]) + editor.addCursorAtBufferPosition([6,50]) + window.advanceClock() + + scrollHandler = spyOn(editor, 'scrollVertically') + + editor.moveCursorRight() + window.advanceClock() + position = editor.pixelPositionForScreenPosition([0,1]) + expect(scrollHandler).toHaveBeenCalledWith(position) + + it "only attempts to scroll once when multiple cursors are visible", -> + setEditorWidthInChars(editor, 20) + setEditorHeightInChars(editor, 10) + editor.setCursorBufferPosition([11,0]) + editor.addCursorAtBufferPosition([0,0]) + editor.addCursorAtBufferPosition([6,0]) + window.advanceClock() + + scrollHandler = spyOn(editor, 'scrollVertically') + + editor.moveCursorRight() + window.advanceClock() + + position = editor.pixelPositionForScreenPosition([0,1]) + expect(scrollHandler).toHaveBeenCalledWith(position) + describe "auto indent/outdent", -> beforeEach -> editor.autoIndent = true @@ -1936,4 +1973,4 @@ describe "Editor", -> expect(editor.screenPositionInBounds([0,0])).toBeTruthy() expect(editor.screenPositionInBounds([10,20])).toBeTruthy() expect(editor.screenPositionInBounds([10,21])).toBeFalsy() - expect(editor.screenPositionInBounds([11,21])).toBeFalsy() \ No newline at end of file + expect(editor.screenPositionInBounds([11,21])).toBeFalsy() diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index c7e54aa60..df0353841 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -137,43 +137,4 @@ class Cursor extends View @css(position) if @editor.getCursors().length == 1 or @editor.screenPositionInBounds(screenPosition) - @autoScroll(position) - - autoScroll: (position) -> - return if @editor._autoScrolling - - @editor._autoScrolling = true - _.defer => - @editor._autoScrolling = false - @autoScrollVertically(position) - @autoScrollHorizontally(position) - - autoScrollVertically: (position) -> - linesInView = @editor.scroller.height() / @editor.lineHeight - maxScrollMargin = Math.floor((linesInView - 1) / 2) - scrollMargin = Math.min(@editor.vScrollMargin, maxScrollMargin) - margin = scrollMargin * @height() - desiredTop = position.top - margin - desiredBottom = position.top + @height() + margin - - if desiredBottom > @editor.scroller.scrollBottom() - @editor.scroller.scrollBottom(desiredBottom) - else if desiredTop < @editor.scroller.scrollTop() - @editor.scroller.scrollTop(desiredTop) - - autoScrollHorizontally: (position) -> - return if @editor.softWrap - - charWidth = @editor.charWidth - charsInView = @editor.scroller.width() / charWidth - maxScrollMargin = Math.floor((charsInView - 1) / 2) - scrollMargin = Math.min(@editor.hScrollMargin, maxScrollMargin) - margin = scrollMargin * charWidth - desiredRight = position.left + charWidth + margin - desiredLeft = position.left - margin - - if desiredRight > @editor.scroller.scrollRight() - @editor.scroller.scrollRight(desiredRight) - else if desiredLeft < @editor.scroller.scrollLeft() - @editor.scroller.scrollLeft(desiredLeft) - + @editor.scrollTo(position) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 6a9e6d2e4..045209419 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -37,6 +37,7 @@ class Editor extends View autoIndent: null lineCache: null isFocused: false + isScrolling: false initialize: ({buffer}) -> requireStylesheet 'editor.css' @@ -328,8 +329,9 @@ class Editor extends View clipScreenPosition: (screenPosition, options={}) -> @renderer.clipScreenPosition(screenPosition, options) - pixelPositionForScreenPosition: ({row, column}) -> - { top: row * @lineHeight, left: column * @charWidth } + pixelPositionForScreenPosition: (position) -> + position = Point.fromObject(position) + { top: position.row * @lineHeight, left: position.column * @charWidth } screenPositionFromPixelPosition: ({top, left}) -> screenPosition = new Point(Math.floor(top / @lineHeight), Math.floor(left / @charWidth)) @@ -478,5 +480,41 @@ class Editor extends View getCurrentMode: -> @buffer.getMode() + scrollTo: (position) -> + return if @isScrolling + @isScrolling = true + _.defer => + @isScrolling = false + @scrollVertically(position) + @scrollHorizontally(position) + + scrollVertically: (position) -> + linesInView = @scroller.height() / @lineHeight + maxScrollMargin = Math.floor((linesInView - 1) / 2) + scrollMargin = Math.min(@vScrollMargin, maxScrollMargin) + margin = scrollMargin * @lineHeight + desiredTop = position.top - margin + desiredBottom = position.top + @lineHeight + margin + + if desiredBottom > @scroller.scrollBottom() + @scroller.scrollBottom(desiredBottom) + else if desiredTop < @scroller.scrollTop() + @scroller.scrollTop(desiredTop) + + scrollHorizontally: (position) -> + return if @softWrap + + charsInView = @scroller.width() / @charWidth + maxScrollMargin = Math.floor((charsInView - 1) / 2) + scrollMargin = Math.min(@hScrollMargin, maxScrollMargin) + margin = scrollMargin * @charWidth + desiredRight = position.left + @charWidth + margin + desiredLeft = position.left - margin + + if desiredRight > @scroller.scrollRight() + @scroller.scrollRight(desiredRight) + else if desiredLeft < @scroller.scrollLeft() + @scroller.scrollLeft(desiredLeft) + logLines: -> @renderer.logLines()