From c61510ce99ff7fc40c8c34dc3d213a730c1b55fe Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 5 Mar 2012 19:15:03 -0700 Subject: [PATCH] Ensure line wrapping interacts cleanly with folding --- spec/atom/renderer-spec.coffee | 16 +++++++++++++-- src/atom/renderer.coffee | 36 ++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/spec/atom/renderer-spec.coffee b/spec/atom/renderer-spec.coffee index f81d3a12f..f8e42e0ad 100644 --- a/spec/atom/renderer-spec.coffee +++ b/spec/atom/renderer-spec.coffee @@ -42,8 +42,20 @@ describe "Renderer", -> expect(renderer.lineForRow(4).text).toBe 'right = [];' describe "when there is a fold placeholder straddling the max length boundary", -> + it "wraps the line before the fold placeholder", -> + renderer.createFold([[3, 49], [6, 1]]) - fdescribe "folding", -> + expect(renderer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = []' + expect(renderer.lineForRow(4).text).toBe '... current < pivot ? left.push(current) : ' + expect(renderer.lineForRow(5).text).toBe 'right.push(current);' + expect(renderer.lineForRow(6).text).toBe ' }' + + renderer.createFold([[6, 56], [8, 15]]) + expect(renderer.lineForRow(6).text).toBe 'right.push(...(left).concat(pivot).concat(sort(rig' + expect(renderer.lineForRow(7).text).toBe 'ht));' + expect(renderer.lineForRow(8).text).toBe ' };' + + describe "folding", -> describe "when folds are created and destroyed", -> describe "when a fold spans multiple lines", -> it "replaces the lines spanned by the fold with a single line containing a placeholder", -> @@ -112,7 +124,7 @@ describe "Renderer", -> expect(line5.text).toBe ' current = items.shift();' expect(renderer.lineForRow(8).text).toBe ' r... sort(left).concat(pivot).concat(sort(right));' - fit "allows the outer fold to start at the same location as the inner fold", -> + it "allows the outer fold to start at the same location as the inner fold", -> renderer.createFold([[4, 29], [7, 4]]) renderer.createFold([[4, 29], [9, 2]]) expect(renderer.lineForRow(4).text).toBe " while(items.length > 0) {...};" diff --git a/src/atom/renderer.coffee b/src/atom/renderer.coffee index 0cd8b68d2..223fe5922 100644 --- a/src/atom/renderer.coffee +++ b/src/atom/renderer.coffee @@ -43,11 +43,16 @@ class Renderer fold = new Fold(this, bufferRange) @registerFold(bufferRange.start.row, fold) - oldScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(bufferRange)) + oldScreenRange = + @expandScreenRangeToLineEnds( + @screenRangeForBufferRange( + @expandBufferRangeToLineEnds(bufferRange))) + + lines = @buildLineForBufferRow(bufferRange.start.row) @lineMap.replaceOutputRows( oldScreenRange.start.row, oldScreenRange.end.row, - @buildLineForBufferRow(bufferRange.start.row)) + lines) newScreenRange = @expandScreenRangeToLineEnds( new Range(_.clone(oldScreenRange.start), _.clone(oldScreenRange.start))) @@ -84,20 +89,23 @@ class Renderer @buildLinesForBufferRows(bufferRow, bufferRow) buildLinesForBufferRows: (startRow, endRow) -> - buildLinesForBufferRows = (startRow, endRow, startColumn) => + buildLinesForBufferRows = (startRow, endRow, startColumn, currentScreenLineLength=0) => return [] if startRow > endRow and not startColumn? startColumn ?= 0 line = @highlighter.lineForRow(startRow).splitAt(startColumn)[1] - wrapColumn = @findWrapColumn(line.text) + wrapColumn = @findWrapColumn(line.text, @maxLineLength - currentScreenLineLength) for fold in @foldsForBufferRow(startRow) if fold.start.column >= startColumn - break if fold.start.column > wrapColumn - foldPlaceholderLength + if fold.start.column > wrapColumn - foldPlaceholderLength + wrapColumn = Math.min(wrapColumn, fold.start.column) + break prefix = line.splitAt(fold.start.column - startColumn)[0] placeholder = @buildFoldPlaceholder(fold) - suffix = buildLinesForBufferRows(fold.end.row, endRow, fold.end.column) + currentScreenLineLength = currentScreenLineLength + (prefix?.text.length ? 0) + foldPlaceholderLength + suffix = buildLinesForBufferRows(fold.end.row, endRow, fold.end.column, currentScreenLineLength) return _.compact _.flatten [prefix, placeholder, suffix] if wrapColumn @@ -112,19 +120,19 @@ class Renderer foldStartRowForBufferRow: (bufferRow) -> @bufferRowForScreenRow(@screenRowForBufferRow(bufferRow)) - findWrapColumn: (line) -> - return unless line.length > @maxLineLength + findWrapColumn: (line, maxLineLength) -> + return unless line.length > maxLineLength - if /\s/.test(line[@maxLineLength]) + if /\s/.test(line[maxLineLength]) # search forward for the start of a word past the boundary - for column in [@maxLineLength..line.length] + for column in [maxLineLength..line.length] return column if /\S/.test(line[column]) return line.length else # search backward for the start of the word on the boundary - for column in [@maxLineLength..0] + for column in [maxLineLength..0] return column + 1 if /\s/.test(line[column]) - return @maxLineLength + return maxLineLength registerFold: (bufferRow, fold) -> @activeFolds[bufferRow] ?= [] @@ -148,4 +156,8 @@ class Renderer { start, end } = screenRange new Range([start.row, 0], [end.row, @lineMap.lineForOutputRow(end.row).text.length]) + expandBufferRangeToLineEnds: (bufferRange) -> + { start, end } = bufferRange + new Range([start.row, 0], [end.row, @lineMap.lineForInputRow(end.row).text.length]) + _.extend Renderer.prototype, EventEmitter