diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index df7c13383..1703a24d1 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -124,9 +124,42 @@ describe "DisplayBuffer", -> expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[1].isHardTab).toBeTruthy() describe "when a line is wrapped", -> - it "correctly tokenizes soft wrap indentation tokens", -> - expect(displayBuffer.tokenizedLineForScreenRow(4).tokens[0].isSoftWrapIndentation).toBeTruthy() - expect(displayBuffer.tokenizedLineForScreenRow(4).tokens[1].isSoftWrapIndentation).toBeTruthy() + it "breaks soft-wrap indentation into a token for each indentation level to support indent guides", -> + tokenizedLine = displayBuffer.tokenizedLineForScreenRow(4) + + expect(tokenizedLine.tokens[0].value).toBe(" ") + expect(tokenizedLine.tokens[0].isSoftWrapIndentation).toBeTruthy() + + expect(tokenizedLine.tokens[1].value).toBe(" ") + expect(tokenizedLine.tokens[1].isSoftWrapIndentation).toBeTruthy() + + expect(tokenizedLine.tokens[2].isSoftWrapIndentation).toBeFalsy() + + describe "when editor.softWrapHangingIndent is set", -> + beforeEach -> + atom.config.set('editor.softWrapHangingIndent', 3) + + it "further indents wrapped lines", -> + expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe " return " + expect(displayBuffer.tokenizedLineForScreenRow(11).text).toBe " sort(left).concat(pivot).concat(sort(right)" + expect(displayBuffer.tokenizedLineForScreenRow(12).text).toBe " );" + + it "includes hanging indent when breaking soft-wrap indentation into tokens", -> + tokenizedLine = displayBuffer.tokenizedLineForScreenRow(4) + + expect(tokenizedLine.tokens[0].value).toBe(" ") + expect(tokenizedLine.tokens[0].isSoftWrapIndentation).toBeTruthy() + + expect(tokenizedLine.tokens[1].value).toBe(" ") + expect(tokenizedLine.tokens[1].isSoftWrapIndentation).toBeTruthy() + + expect(tokenizedLine.tokens[2].value).toBe(" ") # hanging indent + expect(tokenizedLine.tokens[2].isSoftWrapIndentation).toBeTruthy() + + expect(tokenizedLine.tokens[3].value).toBe(" ") # odd space + expect(tokenizedLine.tokens[3].isSoftWrapIndentation).toBeTruthy() + + expect(tokenizedLine.tokens[4].isSoftWrapIndentation).toBeFalsy() describe "when the buffer changes", -> describe "when buffer lines are updated", -> diff --git a/src/config-schema.coffee b/src/config-schema.coffee index 1f205ecbf..c2944911b 100644 --- a/src/config-schema.coffee +++ b/src/config-schema.coffee @@ -149,6 +149,10 @@ module.exports = softWrapAtPreferredLineLength: type: 'boolean' default: false + softWrapHangingIndent: + type: 'integer' + default: 0 + minimum: 0 scrollSensitivity: type: 'integer' default: 40 diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 5659d38a0..8e8bd8dcd 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -69,12 +69,17 @@ class DisplayBuffer extends Model scrollPastEnd: atom.config.get('editor.scrollPastEnd', scope: scopeDescriptor) softWrap: atom.config.get('editor.softWrap', scope: scopeDescriptor) softWrapAtPreferredLineLength: atom.config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor) + softWrapHangingIndent: atom.config.get('editor.softWrapHangingIndent', scope: scopeDescriptor) preferredLineLength: atom.config.get('editor.preferredLineLength', scope: scopeDescriptor) subscriptions.add atom.config.onDidChange 'editor.softWrap', scope: scopeDescriptor, ({newValue}) => @configSettings.softWrap = newValue @updateWrappedScreenLines() + subscriptions.add atom.config.onDidChange 'editor.softWrapHangingIndent', scope: scopeDescriptor, ({newValue}) => + @configSettings.softWrapHangingIndent = newValue + @updateWrappedScreenLines() + subscriptions.add atom.config.onDidChange 'editor.softWrapAtPreferredLineLength', scope: scopeDescriptor, ({newValue}) => @configSettings.softWrapAtPreferredLineLength = newValue @updateWrappedScreenLines() if @isSoftWrapped() @@ -1157,7 +1162,10 @@ class DisplayBuffer extends Model softWraps = 0 if @isSoftWrapped() while wrapScreenColumn = tokenizedLine.findWrapColumn(@getSoftWrapColumn()) - [wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt(wrapScreenColumn) + [wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt( + wrapScreenColumn, + @configSettings.softWrapHangingIndent + ) break if wrappedLine.hasOnlySoftWrapIndentation() screenLines.push(wrappedLine) softWraps++ diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 3560c35a1..2a807d84a 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -111,25 +111,18 @@ class TokenizedLine return maxColumn - # Calculates how many trailing spaces in this line's indentation cannot fit in a single tab. - # - # Returns a {Number} representing the odd indentation spaces in this line. - getOddIndentationSpaces: -> - oddIndentLevel = @indentLevel - Math.floor(@indentLevel) - Math.round(@tabLength * oddIndentLevel) + buildSoftWrapIndentationTokens: (token, hangingIndent) -> + totalIndentSpaces = (@indentLevel * @tabLength) + hangingIndent + indentTokens = [] + while totalIndentSpaces > 0 + tokenLength = Math.min(@tabLength, totalIndentSpaces) + indentToken = token.buildSoftWrapIndentationToken(tokenLength) + indentTokens.push(indentToken) + totalIndentSpaces -= tokenLength - buildSoftWrapIndentationTokens: (token) -> - indentTokens = [0...Math.floor(@indentLevel)].map => - token.buildSoftWrapIndentationToken(@tabLength) + indentTokens - if @getOddIndentationSpaces() - indentTokens.concat( - token.buildSoftWrapIndentationToken @getOddIndentationSpaces() - ) - else - indentTokens - - softWrapAt: (column) -> + softWrapAt: (column, hangingIndent) -> return [new TokenizedLine([], '', [0, 0], [0, 0]), this] if column == 0 rightTokens = new Array(@tokens...) @@ -142,7 +135,7 @@ class TokenizedLine leftTextLength += nextToken.value.length leftTokens.push nextToken - indentationTokens = @buildSoftWrapIndentationTokens(leftTokens[0]) + indentationTokens = @buildSoftWrapIndentationTokens(leftTokens[0], hangingIndent) leftFragment = new TokenizedLine( tokens: leftTokens