Indent works with hard tabs

This commit is contained in:
Corey Johnson & Nathan Sobo
2012-10-25 17:34:13 -07:00
parent 29351ee5fc
commit 0aa9f8de57
7 changed files with 112 additions and 57 deletions

View File

@@ -671,7 +671,7 @@ describe "EditSession", ->
it "auto-indents the new line to one additional level of indentation beyond the preceding line", ->
editSession.setCursorBufferPosition([1, Infinity])
editSession.insertText('\n', autoIndent: true)
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 2
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
describe "when the newline is inserted on a normal line", ->
it "auto-indents the new line to the same level of indentation as the preceding line", ->
@@ -686,7 +686,7 @@ describe "EditSession", ->
editSession.setCursorBufferPosition([2, 4])
editSession.insertText('\n', autoIndent: true)
editSession.setCursorBufferPosition([2, 4])
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 2
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
editSession.insertText(' }', autoIndent: true)
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1)
@@ -696,14 +696,14 @@ describe "EditSession", ->
editSession.insertText('\n', autoIndent: true)
expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3)
editSession.insertText(' }', autoIndent: true)
expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3) - 2
expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3) - 1
describe "when the current line does not match an auto-outdent pattern", ->
it "leaves the line unchanged", ->
editSession.setCursorBufferPosition([2, 4])
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 2
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
editSession.insertText('foo', autoIndent: true)
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 2
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
describe "when the `normalizeIndent` option is true", ->
describe "when the inserted text contains no newlines", ->
@@ -746,7 +746,7 @@ describe "EditSession", ->
it "indents all lines relative to the suggested indent", ->
editSession.insertText('\n xx')
editSession.setCursorBufferPosition([3, 1])
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 4)
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
@@ -767,7 +767,7 @@ describe "EditSession", ->
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: 4)
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
@@ -791,7 +791,7 @@ describe "EditSession", ->
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: 4)
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
@@ -813,7 +813,7 @@ describe "EditSession", ->
it "normalizes the indentation level of all lines based on the level of the existing first line", ->
editSession.setAutoIndent(true)
editSession.buffer.delete([[2, 0], [2, 2]])
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent:true, indentBasis: 4)
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();"
@@ -1214,6 +1214,9 @@ describe "EditSession", ->
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
describe ".indent()", ->
convertToHardTabs = ->
buffer.setText(buffer.getText().replace(/[ ]{2}/g, "\t"))
describe "when the selection is empty", ->
describe "when autoIndent is disabled", ->
describe "if 'softTabs' is true (the default)", ->
@@ -1223,7 +1226,37 @@ describe "EditSession", ->
editSession.indent()
expect(buffer.lineForRow(0)).toMatch(tabRegex)
describe "if 'softTabs' is false", ->
it "insert a \t into the buffer", ->
editSession.softTabs = false
expect(buffer.lineForRow(0)).not.toMatch(/^\t/)
editSession.indent()
expect(buffer.lineForRow(0)).toMatch(/^\t/)
describe "when autoIndent is enabled", ->
describe "when the cursor's column is less than the suggested level of indentation", ->
describe "when 'softTabs' is true (the default)", ->
it "inserts enough whitespace to bring the line to the suggested level of indentaion", ->
buffer.insert([5, 0], " \n")
editSession.tabLength = 2
editSession.setCursorBufferPosition [5, 2]
editSession.setAutoIndent(true)
editSession.indent()
expect(buffer.lineForRow(5)).toMatch /^\s+$/
expect(buffer.lineForRow(5).length).toBe 6
expect(editSession.getCursorBufferPosition()).toEqual [5, 6]
describe "when 'softTabs' is false", ->
it "inserts enough tabs to bring the line to the suggested level of indentaion", ->
convertToHardTabs()
editSession.softTabs = false
buffer.insert([5, 0], "\t\n")
editSession.setCursorBufferPosition [5, 1]
editSession.setAutoIndent(true)
editSession.indent()
expect(buffer.lineForRow(5)).toMatch /^\t\t\t$/
expect(editSession.getCursorBufferPosition()).toEqual [5, 3]
describe "when the cursor's column is greater than the suggested level of indentation", ->
describe "when 'softTabs' is true (the default)", ->
it "inserts 'tabLength' spaces into the buffer", ->
@@ -1236,17 +1269,16 @@ describe "EditSession", ->
expect(buffer.lineForRow(7).length).toBe 8
expect(editSession.getCursorBufferPosition()).toEqual [7, 8]
describe "when the cursor's column is less than the suggested level of indentation", ->
describe "when 'softTabs' is true (the default)", ->
it "inserts enough whitespace to bring the line to the suggested level of indentaion", ->
buffer.insert([5, 0], " \n")
editSession.tabLength = 2
editSession.setCursorBufferPosition [5, 2]
describe "when 'softTabs' is false", ->
it "inserts \t into the buffer", ->
convertToHardTabs()
editSession.softTabs = false
buffer.insert([7, 0], "\t\t\t\n")
editSession.setCursorBufferPosition [7, 3]
editSession.setAutoIndent(true)
editSession.indent()
expect(buffer.lineForRow(5)).toMatch /^\s+$/
expect(buffer.lineForRow(5).length).toBe 6
expect(editSession.getCursorBufferPosition()).toEqual [5, 6]
expect(buffer.lineForRow(7)).toMatch /^\t\t\t\t$/
expect(editSession.getCursorBufferPosition()).toEqual [7, 4]
describe "when the selection is not empty", ->
it "indents the selected lines", ->

View File

@@ -183,9 +183,9 @@ describe "LanguageMode", ->
describe "suggestedIndentForBufferRow", ->
it "returns the suggested indentation based on auto-indent/outdent rules", ->
expect(languageMode.suggestedIndentForBufferRow(0)).toBe 0
expect(languageMode.suggestedIndentForBufferRow(1)).toBe 2
expect(languageMode.suggestedIndentForBufferRow(2)).toBe 4
expect(languageMode.suggestedIndentForBufferRow(9)).toBe 2
expect(languageMode.suggestedIndentForBufferRow(1)).toBe 1
expect(languageMode.suggestedIndentForBufferRow(2)).toBe 2
expect(languageMode.suggestedIndentForBufferRow(9)).toBe 1
describe "coffeescript", ->

View File

@@ -162,6 +162,12 @@ class Cursor
isAtBeginningOfLine: ->
@getBufferPosition().column == 0
getIndentLevel: ->
if @editSession.softTabs
@getBufferColumn() / @editSession.tabLength
else
@getBufferColumn()
isAtEndOfLine: ->
@getBufferPosition().isEqual(@getCurrentLineBufferRange().end)

View File

@@ -114,12 +114,25 @@ class EditSession
@buffer.clipPosition(bufferPosition)
indentationForBufferRow: (bufferRow) ->
@lineForBufferRow(bufferRow).match(/^\s*/)?[0].length
@indentLevelForLine(@lineForBufferRow(bufferRow))
setIndentationForBufferRow: (bufferRow, newLevel) ->
currentLevel = @indentationForBufferRow(bufferRow)
indentString = [0...newLevel].map(-> ' ').join('')
@buffer.change([[bufferRow, 0], [bufferRow, currentLevel]], indentString)
currentIndentString = @buildIndentString(currentLevel)
newIndentString = @buildIndentString(newLevel)
@buffer.change([[bufferRow, 0], [bufferRow, currentIndentString.length]], newIndentString)
indentLevelForLine: (line) ->
if @softTabs
line.match(/^\s*/)?[0].length / @tabLength
else
line.match(/^\t*/)?[0].length
buildIndentString: (number) ->
if @softTabs
_.multiplyString(" ", number * @tabLength)
else
_.multiplyString("\t", number)
getFileExtension: -> @buffer.getExtension()
getPath: -> @buffer.getPath()

View File

@@ -83,13 +83,13 @@ class LanguageMode
rowRangeForFoldAtBufferRow: (bufferRow) ->
return null unless @doesBufferRowStartFold(bufferRow)
startIndentation = @editSession.indentationForBufferRow(bufferRow)
startIndentLevel = @editSession.indentationForBufferRow(bufferRow)
scopes = @tokenizedBuffer.scopesForPosition([bufferRow, 0])
for row in [(bufferRow + 1)..@editSession.getLastBufferRow()]
continue if @editSession.isBufferRowBlank(row)
indentation = @editSession.indentationForBufferRow(row)
if indentation <= startIndentation
includeRowInFold = indentation == startIndentation and TextMateBundle.foldEndRegexForScope(@grammar, scopes[0]).search(@editSession.lineForBufferRow(row))
if indentation <= startIndentLevel
includeRowInFold = indentation == startIndentLevel and TextMateBundle.foldEndRegexForScope(@grammar, scopes[0]).search(@editSession.lineForBufferRow(row))
foldEndRow = row if includeRowInFold
break
@@ -98,23 +98,23 @@ class LanguageMode
[bufferRow, foldEndRow]
suggestedIndentForBufferRow: (bufferRow) ->
currentIndentation = @editSession.indentationForBufferRow(bufferRow)
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
scopes = @tokenizedBuffer.scopesForPosition([bufferRow, 0])
return currentIndentation unless increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
return currentIndentLevel unless increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
currentLine = @buffer.lineForRow(bufferRow)
precedingRow = @buffer.previousNonBlankRow(bufferRow)
return currentIndentation unless precedingRow?
return currentIndentLevel unless precedingRow?
precedingLine = @buffer.lineForRow(precedingRow)
desiredIndentation = @editSession.indentationForBufferRow(precedingRow)
desiredIndentation += @editSession.tabLength if increaseIndentPattern.test(precedingLine)
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
desiredIndentLevel += 1 if increaseIndentPattern.test(precedingLine)
return desiredIndentation unless decreaseIndentPattern = TextMateBundle.outdentRegexForScope(scopes[0])
desiredIndentation -= @editSession.tabLength if decreaseIndentPattern.test(currentLine)
return desiredIndentLevel unless decreaseIndentPattern = TextMateBundle.outdentRegexForScope(scopes[0])
desiredIndentLevel -= 1 if decreaseIndentPattern.test(currentLine)
Math.max(desiredIndentation, currentIndentation)
Math.max(desiredIndentLevel, currentIndentLevel)
autoIndentBufferRows: (startRow, endRow) ->
@autoIndentBufferRow(row) for row in [startRow..endRow]
@@ -132,11 +132,11 @@ class LanguageMode
increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
return unless increaseIndentPattern
currentIndentation = @editSession.indentationForBufferRow(bufferRow)
desiredIndentation = @editSession.indentationForBufferRow(precedingRow)
desiredIndentation += @editSession.tabLength if increaseIndentPattern.test(precedingLine)
if desiredIndentation > currentIndentation
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentation)
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
desiredIndentLevel += 1 if increaseIndentPattern.test(precedingLine)
if desiredIndentLevel > currentIndentLevel
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
autoDecreaseIndentForBufferRow: (bufferRow) ->
scopes = @tokenizedBuffer.scopesForPosition([bufferRow, 0])
@@ -147,14 +147,14 @@ class LanguageMode
line = @buffer.lineForRow(bufferRow)
return unless decreaseIndentPattern.test(line)
currentIndentation = @editSession.indentationForBufferRow(bufferRow)
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
precedingRow = @buffer.previousNonBlankRow(bufferRow)
precedingLine = @buffer.lineForRow(precedingRow)
desiredIndentation = @editSession.indentationForBufferRow(precedingRow)
desiredIndentation -= @editSession.tabLength unless increaseIndentPattern.test(precedingLine)
if desiredIndentation < currentIndentation
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentation)
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
desiredIndentLevel -= 1 unless increaseIndentPattern.test(precedingLine)
if desiredIndentLevel < currentIndentLevel
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
getLineTokens: (line, stack) ->
{tokens, stack} = @grammar.getLineTokens(line, stack)

View File

@@ -153,9 +153,10 @@ class Selection
if @isEmpty()
desiredIndent = @editSession.suggestedIndentForBufferRow(row)
delta = desiredIndent - column
delta = desiredIndent - @cursor.getIndentLevel()
if @editSession.autoIndent and delta > 0
@insertText(new Array(delta + 1).join(' '))
@insertText(@editSession.buildIndentString(delta))
else
if @editSession.softTabs
@insertText(@editSession.getTabText())
@@ -170,7 +171,7 @@ class Selection
currentBufferRow = @cursor.getBufferRow()
currentBufferColumn = @cursor.getBufferColumn()
lines = text.split('\n')
currentBasis = options.indentBasis ? lines[0].match(/\s*/)[0].length
currentBasis = options.indentBasis ? @editSession.indentLevelForLine(lines[0])
lines[0] = lines[0].replace(/^\s*/, '') # strip leading space from first line
normalizedLines = []
@@ -183,14 +184,14 @@ class Selection
else if @editSession.autoIndent
desiredBasis = @editSession.suggestedIndentForBufferRow(currentBufferRow)
else
desiredBasis = currentBufferColumn
desiredBasis = @cursor.getIndentLevel()
for line, i in lines
if i == 0
if insideExistingLine
delta = 0
else
delta = desiredBasis - currentBufferColumn
delta = desiredBasis - @cursor.getIndentLevel()
else
delta = desiredBasis - currentBasis
@@ -199,12 +200,12 @@ class Selection
normalizedLines.join('\n')
adjustIndentationForLine: (line, delta) ->
if delta > 0
new Array(delta + 1).join(' ') + line
else if delta < 0
line.replace(new RegExp("^ {0,#{Math.abs(delta)}}"), '')
else
line
currentIndentLevel = @editSession.indentLevelForLine(line)
currentIndentString = @editSession.buildIndentString(currentIndentLevel)
desiredIndentLevel = Math.max(0, currentIndentLevel + delta)
desiredIndentString = @editSession.buildIndentString(desiredIndentLevel)
line.replace(new RegExp("^#{currentIndentString}"), desiredIndentString)
backspace: ->
if @isEmpty() and not @editSession.isFoldedAtScreenRow(@cursor.getScreenRow())

View File

@@ -68,4 +68,7 @@ _.mixin
for key, value of hash
inverted[value] ?= []
inverted[value].push(key)
inverted
inverted
multiplyString: (string, n) ->
new Array(1 + n).join(string)