diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index ca1e9b595..1d317b1c0 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -1011,145 +1011,6 @@ describe "EditSession", -> editSession.insertText('holy cow') expect(editSession.lineForScreenRow(2).fold).toBeUndefined() - xdescribe "when the `normalizeIndent` option is true", -> - describe "when the inserted text contains no newlines", -> - it "does not adjust the indentation level of the text", -> - editSession.setCursorBufferPosition([5, 2]) - editSession.insertText("foo", normalizeIndent: true) - expect(editSession.lineForBufferRow(5)).toBe " foo current = items.shift();" - - describe "when the inserted text contains newlines", -> - text = null - beforeEach -> - editSession.setCursorBufferPosition([2, Infinity]) - text = [ - " while (true) {" - " foo();" - " }" - " bar();" - ].join('\n') - - removeLeadingWhitespace = (text) -> text.replace(/^\s*/, '') - - describe "when the cursor is preceded only by whitespace", -> - describe "when auto-indent is enabled", -> - describe "when the cursor's current column is less than the suggested indent level", -> - describe "when the indentBasis is inferred from the first line", -> - it "indents all lines relative to the suggested indent", -> - editSession.insertText('\n xx', autoIndent: true) - editSession.setCursorBufferPosition([3, 1]) - editSession.insertText(text, normalizeIndent: true, autoIndent: true) - - expect(editSession.lineForBufferRow(3)).toBe " while (true) {" - expect(editSession.lineForBufferRow(4)).toBe " foo();" - expect(editSession.lineForBufferRow(5)).toBe " }" - expect(editSession.lineForBufferRow(6)).toBe " bar();xx" - - describe "when an indentBasis is provided", -> - it "indents all lines relative to the suggested indent", -> - editSession.insertText('\n xx') - editSession.setCursorBufferPosition([3, 1]) - editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2, autoIndent: true) - - expect(editSession.lineForBufferRow(3)).toBe " while (true) {" - expect(editSession.lineForBufferRow(4)).toBe " foo();" - expect(editSession.lineForBufferRow(5)).toBe " }" - expect(editSession.lineForBufferRow(6)).toBe " bar();xx" - - describe "when inserting on a line that has mixed tabs and whitespace in hard tabs mode (regression)", -> - it "correctly indents the inserted text", -> - editSession.softTabs = false - buffer.setText """ - not indented - \tmixed indented - """ - - editSession.setCursorBufferPosition([1, 0]) - editSession.insertText(text, normalizeIndent: true, autoIndent: true) - - expect(editSession.lineForBufferRow(1)).toBe "\t\t\twhile (true) {" - expect(editSession.lineForBufferRow(2)).toBe "\t\t\t\tfoo();" - expect(editSession.lineForBufferRow(3)).toBe "\t\t\t}" - expect(editSession.lineForBufferRow(4)).toBe "\t\tbar(); \tmixed indented" - - describe "when inserting on a fractionally-indented line in hard tabs mode (regression)", -> - it "correctly indents the inserted text", -> - editSession.softTabs = false - buffer.setText """ - not indented - fractional indentation - """ - - editSession.setCursorBufferPosition([1, 0]) - editSession.insertText(text, normalizeIndent: true, autoIndent: true) - - expect(editSession.lineForBufferRow(1)).toBe "\t\twhile (true) {" - expect(editSession.lineForBufferRow(2)).toBe "\t\t\tfoo();" - expect(editSession.lineForBufferRow(3)).toBe "\t\t}" - expect(editSession.lineForBufferRow(4)).toBe "\tbar(); fractional indentation" - - describe "when the cursor's current column is greater than the suggested indent level", -> - describe "when the indentBasis is inferred from the first line", -> - it "preserves the current indent level, indenting all lines relative to it", -> - editSession.insertText('\n ') - editSession.insertText(text, normalizeIndent: true) - - expect(editSession.lineForBufferRow(3)).toBe " while (true) {" - expect(editSession.lineForBufferRow(4)).toBe " foo();" - expect(editSession.lineForBufferRow(5)).toBe " }" - expect(editSession.lineForBufferRow(6)).toBe " bar();" - - describe "when an indentBasis is provided", -> - it "preserves the current indent level, indenting all lines relative to it", -> - editSession.insertText('\n ') - editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2) - - expect(editSession.lineForBufferRow(3)).toBe " while (true) {" - expect(editSession.lineForBufferRow(4)).toBe " foo();" - expect(editSession.lineForBufferRow(5)).toBe " }" - expect(editSession.lineForBufferRow(6)).toBe " bar();" - - describe "if auto-indent is disabled", -> - describe "when the indentBasis is inferred from the first line", -> - it "always normalizes indented lines to the cursor's current indentation level", -> - editSession.insertText('\n ') - editSession.insertText(text, normalizeIndent: true) - - expect(editSession.lineForBufferRow(3)).toBe " while (true) {" - expect(editSession.lineForBufferRow(4)).toBe " foo();" - expect(editSession.lineForBufferRow(5)).toBe " }" - expect(editSession.lineForBufferRow(6)).toBe "bar();" - - describe "when an indentBasis is provided", -> - it "always normalizes indented lines to the cursor's current indentation level", -> - editSession.insertText('\n ') - editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2) - - expect(editSession.lineForBufferRow(3)).toBe " while (true) {" - expect(editSession.lineForBufferRow(4)).toBe " foo();" - expect(editSession.lineForBufferRow(5)).toBe " }" - - describe "when the cursor is preceded by non-whitespace characters", -> - describe "when the indentBasis is inferred from the first line", -> - it "normalizes the indentation level of all lines based on the level of the existing first line", -> - editSession.buffer.delete([[2, 0], [2, 2]]) - editSession.insertText(text, normalizeIndent:true) - - expect(editSession.lineForBufferRow(2)).toBe " if (items.length <= 1) return items;while (true) {" - expect(editSession.lineForBufferRow(3)).toBe " foo();" - expect(editSession.lineForBufferRow(4)).toBe " }" - expect(editSession.lineForBufferRow(5)).toBe "bar();" - - describe "when an indentBasis is provided", -> - it "normalizes the indentation level of all lines based on the level of the existing first line", -> - editSession.buffer.delete([[2, 0], [2, 2]]) - editSession.insertText(removeLeadingWhitespace(text), normalizeIndent:true, indentBasis: 2) - - expect(editSession.lineForBufferRow(2)).toBe " if (items.length <= 1) return items;while (true) {" - expect(editSession.lineForBufferRow(3)).toBe " foo();" - expect(editSession.lineForBufferRow(4)).toBe " }" - expect(editSession.lineForBufferRow(5)).toBe "bar();" - describe ".insertNewline()", -> describe "when there is a single cursor", -> describe "when the cursor is at the beginning of a line", -> @@ -2332,11 +2193,13 @@ describe "EditSession", -> expect(editSession.lineForScreenRow(0).tokens.length).toBeGreaterThan 1 describe "auto-indent", -> - copyText = (text) -> + copyText = (text, {startColumn}={}) -> + startColumn ?= 0 editSession.setCursorBufferPosition([0, 0]) editSession.insertText(text) numberOfNewlines = text.match(/\n/g)?.length - editSession.getSelection().setBufferRange([[0,0], [numberOfNewlines,0]]) + endColumn = text.match(/[^\n]*$/)[0]?.length + editSession.getSelection().setBufferRange([[0,startColumn], [numberOfNewlines,endColumn]]) editSession.cutSelectedText() describe "editor.autoIndent", -> @@ -2393,7 +2256,7 @@ describe "EditSession", -> editSession.setCursorBufferPosition([1, Infinity]) editSession.insertText('\n ') expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1 - editSession.insertText('}', autoDecreaseIndent: true) + editSession.insertText('}') expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) describe "when the preceding line doesn't match an increase indent pattern", -> @@ -2401,12 +2264,12 @@ describe "EditSession", -> editSession.setCursorBufferPosition([3, Infinity]) editSession.insertText('\n ') expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3) - editSession.insertText('}', autoDecreaseIndent: true) + editSession.insertText('}') expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3) - 1 it "doesn't break when decreasing the indentation on a row that has no indentation", -> editSession.setCursorBufferPosition([12, Infinity]) - editSession.insertText("\n}; # too many closing brackets!", autoDecreaseIndent: true) + editSession.insertText("\n}; # too many closing brackets!") expect(editSession.lineForBufferRow(13)).toBe "}; # too many closing brackets!" describe "when inserted text does not match a decrease indent pattern", -> @@ -2414,14 +2277,14 @@ describe "EditSession", -> editSession.setCursorBufferPosition([12, 0]) editSession.insertText(' ') expect(editSession.lineForBufferRow(12)).toBe ' };' - editSession.insertText('\t\t', autoDecreaseIndent: true) + editSession.insertText('\t\t') expect(editSession.lineForBufferRow(12)).toBe ' \t\t};' describe "when the current line does not match a decrease indent pattern", -> it "leaves the line unchanged", -> editSession.setCursorBufferPosition([2, 4]) expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1 - editSession.insertText('foo', autoIndent: true) + editSession.insertText('foo') expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1 describe "editor.autoIndentOnPaste", -> @@ -2460,6 +2323,57 @@ describe "EditSession", -> editSession.pasteText() expect(editSession.lineForBufferRow(10)).toBe " var number" + describe "editor.normalizePastedText", -> + describe "when the inserted text contains no newlines", -> + it "does not adjust the indentation level of the text", -> + editSession.setCursorBufferPosition([5, 2]) + editSession.insertText("foo", indentBasis: 5) + expect(editSession.lineForBufferRow(5)).toBe " foo current = items.shift();" + + describe "when the inserted text contains newlines", -> + it "does not normalize the indentation level of the text when editor.autoIndentOnPaste is true", -> + copyText(" function() {\nvar cool = 1;\n }\n") + config.set('editor.autoIndentOnPaste', true) + editSession.setCursorBufferPosition([5, 2]) + editSession.pasteText() + expect(editSession.lineForBufferRow(5)).toBe " function() {" + expect(editSession.lineForBufferRow(6)).toBe " var cool = 1;" + expect(editSession.lineForBufferRow(7)).toBe " }" + + describe "when copied text includes whitespace on first line", -> + describe "when cursor is preceded whitespace and followed non-whitespace", -> + it "normalizes indented lines to the cursor's current indentation level", -> + copyText(" while (true) {\n foo();\n }\n", {startColumn: 4}) + editSession.setCursorBufferPosition([3, 4]) + editSession.pasteText() + + expect(editSession.lineForBufferRow(3)).toBe " while (true) {" + expect(editSession.lineForBufferRow(4)).toBe " foo();" + expect(editSession.lineForBufferRow(5)).toBe " }" + expect(editSession.lineForBufferRow(6)).toBe "var pivot = items.shift(), current, left = [], right = [];" + + describe "when cursor is preceded whitespace and followed by whitespace", -> + it "normalizes indented lines to the cursor's current indentation level", -> + copyText(" while (true) {\n foo();\n }\n", {startColumn: 0}) + editSession.setCursorBufferPosition([3, 4]) + editSession.pasteText() + + expect(editSession.lineForBufferRow(3)).toBe " while (true) {" + expect(editSession.lineForBufferRow(4)).toBe " foo();" + expect(editSession.lineForBufferRow(5)).toBe " }" + expect(editSession.lineForBufferRow(6)).toBe "var pivot = items.shift(), current, left = [], right = [];" + + describe "when the cursor is preceded by non-whitespace characters", -> + it "normalizes the indentation level of all lines based on the level of the existing first line", -> + copyText(" while (true) {\n foo();\n }\n", {startColumn: 0}) + editSession.setCursorBufferPosition([1, Infinity]) + editSession.pasteText() + + expect(editSession.lineForBufferRow(1)).toBe " var sort = function(items) { while (true) {" + expect(editSession.lineForBufferRow(2)).toBe " foo();" + expect(editSession.lineForBufferRow(3)).toBe " }" + expect(editSession.lineForBufferRow(4)).toBe "" + it "autoIndentSelectedRows auto-indents the selection", -> editSession.setCursorBufferPosition([2, 0]) editSession.insertText("function() {\ninside=true\n}\n i=1\n") diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 9800d3223..eb90f15db 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -263,10 +263,12 @@ class Selection oldBufferRange = @getBufferRange() @editSession.destroyFoldsContainingBufferRow(oldBufferRange.end.row) wasReversed = @isReversed() - @clear() @cursor.needsAutoscroll = @cursor.isLastCursor() + if options.indentBasis? and not options.autoIndent + text = @normalizeIndents(text, options.indentBasis) + newBufferRange = @editSession.buffer.change(oldBufferRange, text) if options.select @setBufferRange(newBufferRange, reverse: wasReversed) @@ -282,6 +284,32 @@ class Selection newBufferRange + normalizeIndents: (text, indentBasis) -> + textPrecedingCursor = @cursor.getCurrentBufferLine()[0...@cursor.getBufferColumn()] + isCursorInsideExistingLine = /\S/.test(textPrecedingCursor) + + lines = text.split('\n') + firstLineIndentLevel = @editSession.indentLevelForLine(lines[0]) + if isCursorInsideExistingLine + minimumIndentLevel = @editSession.indentationForBufferRow(@cursor.getBufferRow()) + else + minimumIndentLevel = @cursor.getIndentLevel() + firstLineIndentLevel + normalizedLines = [] + + for line, i in lines + if i == 0 + indentLevel = firstLineIndentLevel + else if /$^/.test line # remove all indentation from empty lines + indentLevel = 0 + else + lineIndentLevel = @editSession.indentLevelForLine(lines[i]) + indentLevel = minimumIndentLevel + (lineIndentLevel - indentBasis) + + normalizedLines.push(@setIndentationForLine(line, indentLevel)) + + console.log normalizedLines + normalizedLines.join('\n') + # Indents the selection. # # options - A hash with one key, `autoIndent`. If `true`, the indentation is @@ -307,9 +335,8 @@ class Selection for row in [start..end] @editSession.buffer.insert([row, 0], @editSession.getTabText()) unless @editSession.buffer.lineLengthForRow(row) == 0 - adjustIndentationForLine: (line, delta) -> - currentIndentLevel = @editSession.indentLevelForLine(line) - desiredIndentLevel = Math.max(0, currentIndentLevel + delta) + setIndentationForLine: (line, indentLevel) -> + desiredIndentLevel = Math.max(0, indentLevel) desiredIndentString = @editSession.buildIndentString(desiredIndentLevel) line.replace(/^[\t ]*/, desiredIndentString)