From 328aa3ba6b094c5af70d06ddedee485b903326ba Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 26 May 2012 13:05:25 -0700 Subject: [PATCH] =?UTF-8?q?WIP:=20Redoing=20overdraw=20code=E2=80=A6=20it'?= =?UTF-8?q?s=20broken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/app/editor-spec.coffee | 75 +++++++++++++----------- src/app/editor.coffee | 110 ++++++++++++++++++++++-------------- 2 files changed, 111 insertions(+), 74 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 7d3ccc3a2..47c028c78 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -8,7 +8,7 @@ $ = require 'jquery' _ = require 'underscore' fs = require 'fs' -describe "Editor", -> +fdescribe "Editor", -> [rootView, buffer, editor, cachedLineHeight] = [] getLineHeight = -> @@ -30,6 +30,7 @@ describe "Editor", -> this.height(getLineHeight() * heightInLines) $('#jasmine-content').append(this) + editor.lineOverdraw = 2 editor.autoIndent = false editor.enableKeymap() editor.isFocused = true @@ -72,7 +73,6 @@ describe "Editor", -> rootView.remove() newEditor.attachToDom() expect(newEditor.scrollTop()).toBe 1.5 * editor.lineHeight - expect(newEditor.visibleLines.css('padding-top')).toBe "#{editor.lineHeight}px" expect(newEditor.scrollView.scrollLeft()).toBe 44 describe ".setBuffer(buffer)", -> @@ -339,7 +339,7 @@ describe "Editor", -> editor.simulateDomAttachment() expect(openHandler).not.toHaveBeenCalled() - describe "text rendering", -> + ffdescribe "text rendering", -> describe "when all lines in the buffer are visible on screen", -> beforeEach -> editor.attachToDom() @@ -465,10 +465,10 @@ describe "Editor", -> describe "when the editor is attached and some lines at the end of the buffer are not visible on screen", -> beforeEach -> - editor.lineOverdraw = 2 editor.attachToDom(heightInLines: 5.5) + console.log "--------------------" - fit "only renders the visible lines plus the overdrawn lines, setting the padding-bottom of the lines element to account for the missing lines", -> + it "only renders the visible lines plus the overdrawn lines, setting the padding-bottom of the lines element to account for the missing lines", -> expect(editor.visibleLines.find('.line').length).toBe 8 expectedPaddingBottom = (buffer.numLines() - 8) * editor.lineHeight expect(editor.visibleLines.css('padding-bottom')).toBe "#{expectedPaddingBottom}px" @@ -477,7 +477,7 @@ describe "Editor", -> describe "when scrolling vertically", -> describe "whes scrolling less than the editor's height", -> - fit "draws new lines and removes old lines when the last visible line will exceed the last rendered line", -> + it "draws new lines and removes old lines when the last visible line will exceed the last rendered line", -> editor.scrollTop(editor.lineHeight * 1.5) expect(editor.visibleLines.find('.line').length).toBe 8 expect(editor.visibleLines.find('.line:first').text()).toBe buffer.lineForRow(0) @@ -504,7 +504,7 @@ describe "Editor", -> expect(editor.visibleLines.find('.line:last').text()).toBe buffer.lineForRow(7) describe "when scrolling more than the editors height", -> - fit "removes lines that are offscreen and not in range of the overdraw and builds lines that become visible", -> + it "removes lines that are offscreen and not in range of the overdraw and builds lines that become visible", -> editor.scrollTop(editor.scrollView.prop('scrollHeight') - editor.scrollView.height()) expect(editor.visibleLines.find('.line').length).toBe 8 expect(editor.visibleLines.find('.line:first').text()).toBe buffer.lineForRow(5) @@ -516,58 +516,69 @@ describe "Editor", -> expect(editor.visibleLines.find('.line:first').text()).toBe buffer.lineForRow(0) expect(editor.visibleLines.find('.line:last').text()).toBe buffer.lineForRow(7) - fit "adjusts the vertical padding of the lines element to account for non-rendered lines", -> - editor.scrollTop(editor.lineHeight * 2.5) - # first visible row is 2, first rendered is 0 - expect(editor.visibleLines.css('padding-top')).toBe "0px" - expectedPaddingBottom = (buffer.numLines() - 8) * editor.lineHeight + it "adjusts the vertical padding of the lines element to account for non-rendered lines", -> + editor.scrollTop(editor.lineHeight * 3) + firstVisibleBufferRow = 3 + expectedPaddingTop = (firstVisibleBufferRow - editor.lineOverdraw) * editor.lineHeight + expect(editor.visibleLines.css('padding-top')).toBe "#{expectedPaddingTop}px" + + lastVisibleBufferRow = Math.ceil(3 + 5.5) # scroll top in lines + height in lines + lastOverdrawnRow = lastVisibleBufferRow + editor.lineOverdraw + expectedPaddingBottom = ((buffer.numLines() - lastOverdrawnRow) * editor.lineHeight) expect(editor.visibleLines.css('padding-bottom')).toBe "#{expectedPaddingBottom}px" - editor.scrollTop(editor.scrollView.prop('scrollHeight') - editor.scrollView.height()) + editor.scrollToBottom() # scrolled to bottom, first visible row is 5 and first rendered row is 3 - expect(editor.visibleLines.css('padding-top')).toBe "#{3 * editor.lineHeight}px" + firstVisibleBufferRow = Math.floor(buffer.numLines() - 5.5) + firstOverdrawnBufferRow = firstVisibleBufferRow - editor.lineOverdraw + expectedPaddingTop = firstOverdrawnBufferRow * editor.lineHeight + expect(editor.visibleLines.css('padding-top')).toBe "#{expectedPaddingTop}px" expect(editor.visibleLines.css('padding-bottom')).toBe "0px" it "renders additional lines when the editor is resized", -> setEditorHeightInLines(editor, 10) $(window).trigger 'resize' - expect(editor.visibleLines.find('.line').length).toBe 10 + expect(editor.visibleLines.find('.line').length).toBe 12 expect(editor.visibleLines.find('.line:first').text()).toBe buffer.lineForRow(0) - expect(editor.visibleLines.find('.line:last').text()).toBe buffer.lineForRow(9) + expect(editor.visibleLines.find('.line:last').text()).toBe buffer.lineForRow(11) - it "renders correctly when scrolling after text added to buffer", -> - editor.attachToDom(heightInLines: 5.5) + it "renders correctly when scrolling after text is added to the buffer", -> editor.insertText("1\n") _.times 4, -> editor.moveCursorDown() - expect(editor.visibleLines.find('.line:eq(0)').text()).toBe editor.buffer.lineForRow(2) - expect(editor.visibleLines.find('.line:eq(5)').text()).toBe editor.buffer.lineForRow(7) + expect(editor.visibleLines.find('.line:eq(2)').text()).toBe editor.buffer.lineForRow(2) + expect(editor.visibleLines.find('.line:eq(7)').text()).toBe editor.buffer.lineForRow(7) it "renders correctly when scrolling after text is removed from buffer", -> - editor.attachToDom(heightInLines: 5.5) + console.log "lastRenderedScreenRow before delete", editor.lastRenderedScreenRow editor.buffer.delete([[0,0],[1,0]]) expect(editor.visibleLines.find('.line:eq(0)').text()).toBe editor.buffer.lineForRow(0) expect(editor.visibleLines.find('.line:eq(5)').text()).toBe editor.buffer.lineForRow(5) - _.times 4, -> editor.moveCursorDown() - expect(editor.visibleLines.find('.line:eq(0)').text()).toBe editor.buffer.lineForRow(1) - expect(editor.visibleLines.find('.line:eq(5)').text()).toBe editor.buffer.lineForRow(6) + console.log "lastRenderedScreenRow after delete", editor.lastRenderedScreenRow + + editor.scrollTop(3 * editor.lineHeight) + expect(editor.visibleLines.find('.line:first').text()).toBe editor.buffer.lineForRow(1) + expect(editor.visibleLines.find('.line:last').text()).toBe editor.buffer.lineForRow(10) describe "when lines are added", -> beforeEach -> - editor.attachToDom() - setEditorHeightInLines(editor, 5) + editor.attachToDom(heightInLines: 5) spyOn(editor, "scrollTo") describe "when the change the precedes the first rendered row", -> - it "inserts and removes rendered lines to account for upstream change", -> - editor.scrollBottom(editor.scrollView.prop('scrollHeight')) - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) + fffit "inserts and removes rendered lines to account for upstream change", -> + console.log "-------------------" + editor.scrollToBottom() + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(6) expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) buffer.change([[1,0], [3,0]], "1\n2\n3\n") - expect(editor.visibleLines.find(".line").length).toBe 5 - expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8) - expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12) + expect(editor.visibleLines.find(".line").length).toBe 7 + expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(7) + expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(13) + + editor.visibleLines.find('.line').each -> console.log $(this).text() + describe "when the change straddles the first rendered row", -> it "doesn't render rows that were not previously rendered", -> diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 17482245a..4b3ea5846 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -225,7 +225,7 @@ class Editor extends View else @gutter.addClass('drop-shadow') - $(window).on "resize", => + $(window).on "resize.editor#{@id}", => @updateVisibleLines() afterAttach: (onDom) -> @@ -236,11 +236,16 @@ class Editor extends View @calculateDimensions() @setMaxLineLength() if @softWrap @prepareForVerticalScrolling() + + # this also renders the visible lines @setScrollPositionFromActiveEditSession() - @renderVisibleLines() - # TODO: The redundant assignment of scrollLeft below is needed because the lines weren't render - # rendered when we called setScrollPositionFromActiveEditSession above. Remove this when we fix - # that problem by setting the width of the lines container based on the max line width + + # TODO: The redundant assignment of scrollLeft below is needed because the + # lines weren't rendered when we called + # setScrollPositionFromActiveEditSession above. Remove this when we fix that + # problem by setting the width of the lines container based on the max line + # length + @scrollView.scrollLeft(@getActiveEditSession().scrollLeft ? 0) @hiddenInput.width(@charWidth) @focus() if @isFocused @@ -303,54 +308,80 @@ class Editor extends View renderFrom = Math.max(0, firstVisibleScreenRow - @lineOverdraw) renderTo = Math.min(@getLastScreenRow(), lastVisibleScreenRow + @lineOverdraw) - + console.log "Rendering lines %d-%d", renderFrom, renderTo if firstVisibleScreenRow < @firstRenderedScreenRow - newLinesEndRow = Math.min(@firstRenderedScreenRow - 1, lastVisibleScreenRow) - lineElements = @buildLineElements(renderFrom, newLinesEndRow) - console.log "inserting", renderFrom, "to", newLinesEndRow - @insertLineElements(renderFrom, lineElements) - @firstRenderedScreenRow = renderFrom - adjustPaddingTop = true - - if renderTo < @lastRenderedScreenRow - console.log "removing", renderTo + 1, "to", @lastRenderedScreenRow - @removeLineElements(renderTo + 1, @lastRenderedScreenRow) - adjustPaddingBottom = true - + @removeLineElements(Math.max(@firstRenderedScreenRow, renderTo), @lastRenderedScreenRow) @lastRenderedScreenRow = renderTo + @firstRenderedScreenRow = renderFrom + newLines = @buildLineElements(renderFrom, Math.min(@firstRenderedScreenRow, renderTo)) + @insertLineElements(renderFrom, newLines) + adjustPadding = true if lastVisibleScreenRow > @lastRenderedScreenRow - newLinesStartRow = Math.max(@lastRenderedScreenRow + 1, firstVisibleScreenRow) - lineElements = @buildLineElements(newLinesStartRow, renderTo) - console.log "inserting", newLinesStartRow, "to", renderTo - @insertLineElements(newLinesStartRow, lineElements) - @lastRenderedScreenRow = renderTo - adjustPaddingBottom = true - - if 0 <= @firstRenderedScreenRow < renderFrom - console.log "removing", @firstRenderedScreenRow, "to", renderFrom - 1 - @removeLineElements(@firstRenderedScreenRow, renderFrom - 1) - adjustPaddingTop = true - + @removeLineElements(@firstRenderedScreenRow, Math.min(@lastRenderedScreenRow, renderFrom)) @firstRenderedScreenRow = renderFrom + @lastRenderedScreenRow = renderTo + startRowOfNewLines = Math.max(@lastRenderedScreenRow, renderFrom) + newLines = @buildLineElements(startRowOfNewLines, renderTo) + @insertLineElements(startRowOfNewLines, newLines) + adjustPadding = true - if adjustPaddingTop + if adjustPadding paddingTop = @firstRenderedScreenRow * @lineHeight @visibleLines.css('padding-top', paddingTop) @gutter.lineNumbers.css('padding-top', paddingTop) - # @firstRenderedScreenRow = firstVisibleScreenRow - if adjustPaddingBottom paddingBottom = (@getLastScreenRow() - @lastRenderedScreenRow) * @lineHeight @visibleLines.css('padding-bottom', paddingBottom) @gutter.lineNumbers.css('padding-bottom', paddingBottom) + # if firstVisibleScreenRow < @firstRenderedScreenRow + # newLinesEndRow = Math.min(@firstRenderedScreenRow - 1, lastVisibleScreenRow) + # lineElements = @buildLineElements(renderFrom, newLinesEndRow) + # console.log "inserting", renderFrom, "to", newLinesEndRow + # @insertLineElements(renderFrom, lineElements) + # @firstRenderedScreenRow = renderFrom + # adjustPaddingTop = true + + # if renderTo < @lastRenderedScreenRow + # console.log "removing", renderTo + 1, "to", @lastRenderedScreenRow + # @removeLineElements(renderTo + 1, @lastRenderedScreenRow) + # adjustPaddingBottom = true + + # @lastRenderedScreenRow = renderTo + + # if lastVisibleScreenRow > @lastRenderedScreenRow + # newLinesStartRow = Math.max(@lastRenderedScreenRow + 1, renderFrom) + # lineElements = @buildLineElements(newLinesStartRow, renderTo) + # console.log "inserting", newLinesStartRow, "to", renderTo + # @insertLineElements(newLinesStartRow, lineElements) + # @lastRenderedScreenRow = renderTo + # adjustPaddingBottom = true + + # if 0 <= @firstRenderedScreenRow < renderFrom + # console.log "removing", @firstRenderedScreenRow, "to", renderFrom - 1 + # @removeLineElements(@firstRenderedScreenRow, renderFrom - 1) + # adjustPaddingTop = true + + # @firstRenderedScreenRow = renderFrom + + # if adjustPaddingTop + # paddingTop = @firstRenderedScreenRow * @lineHeight + # @visibleLines.css('padding-top', paddingTop) + # @gutter.lineNumbers.css('padding-top', paddingTop) + + # if adjustPaddingBottom + # paddingBottom = (@getLastScreenRow() - @lastRenderedScreenRow) * @lineHeight + # @visibleLines.css('padding-bottom', paddingBottom) + # @gutter.lineNumbers.css('padding-bottom', paddingBottom) + getFirstVisibleScreenRow: -> Math.floor(@scrollTop() / @lineHeight) getLastVisibleScreenRow: -> - Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1 + maxVisibleScreenRow = Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1 + Math.min(maxVisibleScreenRow, @getLastScreenRow()) highlightSelectedFolds: -> screenLines = @screenLinesForRows(@firstRenderedScreenRow, @lastRenderedScreenRow) @@ -441,7 +472,6 @@ class Editor extends View editSession = @getActiveEditSession() @scrollTop(editSession.scrollTop ? 0) @scrollView.scrollLeft(editSession.scrollLeft ? 0) - @verticalScrollbar.trigger 'scroll' saveCurrentEditSession: -> @editSessions[@activeEditSessionIndex] = @@ -481,19 +511,14 @@ class Editor extends View newScreenRange.start.row = Math.max(newScreenRange.start.row, @firstRenderedScreenRow) oldScreenRange.start.row = Math.max(oldScreenRange.start.row, @firstRenderedScreenRow) - newScreenRange.end.row = Math.min(newScreenRange.end.row, @lastRenderedScreenRow) - oldScreenRange.end.row = Math.min(oldScreenRange.end.row, @lastRenderedScreenRow) lineElements = @buildLineElements(newScreenRange.start.row, newScreenRange.end.row) @replaceLineElements(oldScreenRange.start.row, oldScreenRange.end.row, lineElements) rowDelta = newScreenRange.end.row - oldScreenRange.end.row - if rowDelta > 0 - @removeLineElements(@lastRenderedScreenRow + 1, @lastRenderedScreenRow + rowDelta) - else if rowDelta < 0 - @lastRenderedScreenRow += rowDelta - @updateVisibleLines() + @lastRenderedScreenRow += rowDelta + @updateVisibleLines() if rowDelta < 0 buildLineElements: (startRow, endRow) -> charWidth = @charWidth @@ -763,6 +788,7 @@ class Editor extends View @trigger 'before-remove' @unsubscribeFromBuffer() + $(window).off ".editor#{@id}" rootView = @rootView() rootView?.off ".editor#{@id}" if @pane() then @pane().remove() else super