mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
WIP: Reworking auto-indent/outdent logic
This commit is contained in:
@@ -656,60 +656,78 @@ describe "EditSession", ->
|
||||
editSession.insertText('holy cow')
|
||||
expect(editSession.lineForScreenRow(2).fold).toBeUndefined()
|
||||
|
||||
xdescribe "when auto-indent is enabled", ->
|
||||
fdescribe "when auto-indent is enabled", ->
|
||||
beforeEach ->
|
||||
editSession.setAutoIndent(true)
|
||||
|
||||
describe "when editing a non-wrapped line", ->
|
||||
describe "when a newline is inserted", ->
|
||||
it "auto-indents the new line for each cursor", ->
|
||||
editSession.setCursorScreenPosition([1, 30])
|
||||
editSession.addCursorAtScreenPosition([4, 29])
|
||||
editSession.insertText("\n")
|
||||
expect(editSession.buffer.lineForRow(2)).toEqual(" ")
|
||||
expect(editSession.buffer.lineForRow(6)).toEqual(" ")
|
||||
describe "when a single newline is inserted", ->
|
||||
describe "when the newline is inserted on a line that starts a new level of indentation", ->
|
||||
it "auto-indents the new line to one additional level of indentation beyond the preceding line", ->
|
||||
editSession.setCursorBufferPosition([1, Infinity])
|
||||
editSession.insertText('\n')
|
||||
expect(buffer.indentationForRow(2)).toBe buffer.indentationForRow(1) + 2
|
||||
|
||||
describe "when text beginning with a newline is inserted", ->
|
||||
it "indents cursor based on the indentation of previous buffer line", ->
|
||||
editSession.setCursorBufferPosition([4, 29])
|
||||
editSession.insertText("\nvar thisIsCool")
|
||||
expect(buffer.lineForRow(5)).toEqual(" var thisIsCool")
|
||||
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", ->
|
||||
editSession.setCursorBufferPosition([5, 13])
|
||||
editSession.insertText('\n')
|
||||
expect(buffer.indentationForRow(6)).toBe buffer.indentationForRow(5)
|
||||
|
||||
describe "when text that closes a scope entered", ->
|
||||
it "outdents the text", ->
|
||||
editSession.setCursorBufferPosition([1, 30])
|
||||
editSession.insertText("\n")
|
||||
expect(editSession.buffer.lineForRow(2)).toEqual(" ")
|
||||
editSession.insertText("}")
|
||||
expect(buffer.lineForRow(2)).toEqual(" }")
|
||||
expect(editSession.getCursorBufferPosition().column).toBe 3
|
||||
describe "when text with newlines is inserted", ->
|
||||
describe "when the portion of the line preceding the inserted text is blank", ->
|
||||
it "auto-increases the indentation of the first line, then fully auto-indents the subsequent lines", ->
|
||||
editSession.setCursorBufferPosition([5, 2])
|
||||
editSession.insertText """
|
||||
if (true) {
|
||||
console.log("It's true!")
|
||||
}\n
|
||||
"""
|
||||
|
||||
describe "when the line is already indented beyond the suggested depth", ->
|
||||
describe "when text without a newline is inserted", ->
|
||||
it "does not modify the line's indentation level", ->
|
||||
expect(buffer.indentationForRow(5)).toBe buffer.indentationForRow(4) + 2
|
||||
expect(buffer.indentationForRow(6)).toBe buffer.indentationForRow(5) + 2
|
||||
expect(buffer.indentationForRow(7)).toBe buffer.indentationForRow(4) + 2
|
||||
expect(buffer.indentationForRow(8)).toBe buffer.indentationForRow(4) + 2
|
||||
|
||||
describe "when text with a newline is inserted", ->
|
||||
it "only modifies the indentation level of subsequent lines, but not the current line", ->
|
||||
describe "when the portion of the line preceding the inserted text is non-blank", ->
|
||||
it "fully auto-indents lines subsequent to the first inserted line", ->
|
||||
buffer.delete([[5, 0], [5, 2]])
|
||||
editSession.setCursorBufferPosition([5, Infinity])
|
||||
editSession.insertText """
|
||||
if (true) {
|
||||
console.log("It's true!")
|
||||
}
|
||||
"""
|
||||
expect(buffer.indentationForRow(5)).toBe 4
|
||||
expect(buffer.indentationForRow(6)).toBe 6
|
||||
expect(buffer.indentationForRow(7)).toBe 4
|
||||
|
||||
describe "when text without newlines is inserted", ->
|
||||
describe "when the current line matches an auto-outdent pattern", ->
|
||||
describe "when the preceding line matches an auto-indent pattern", ->
|
||||
it "auto-decreases the indentation of the line to match that of the preceding line", ->
|
||||
editSession.setCursorBufferPosition([2, 4])
|
||||
editSession.insertText '\n'
|
||||
editSession.setCursorBufferPosition([2, 4])
|
||||
expect(buffer.indentationForRow(2)).toBe buffer.indentationForRow(1) + 2
|
||||
editSession.insertText ' }'
|
||||
buffer.logLines()
|
||||
expect(buffer.indentationForRow(2)).toBe buffer.indentationForRow(1)
|
||||
|
||||
describe "when editing a wrapped line", ->
|
||||
beforeEach ->
|
||||
editSession.setSoftWrapColumn(50)
|
||||
describe "when the preceding does not match an outo-indent pattern", ->
|
||||
ffit "auto-decreases the indentation of the line to be one level below that of the preceding line", ->
|
||||
editSession.setCursorBufferPosition([3, Infinity])
|
||||
editSession.insertText '\n'
|
||||
expect(buffer.indentationForRow(4)).toBe buffer.indentationForRow(3)
|
||||
editSession.insertText ' }'
|
||||
buffer.logLines()
|
||||
expect(buffer.indentationForRow(4)).toBe buffer.indentationForRow(3) - 2
|
||||
|
||||
describe "when newline is inserted", ->
|
||||
it "indents cursor based on the indentation of previous buffer line", ->
|
||||
editSession.setCursorBufferPosition([4, 29])
|
||||
editSession.insertText("\n")
|
||||
expect(editSession.buffer.lineForRow(5)).toEqual(" ")
|
||||
|
||||
describe "when text that closes a scope is entered", ->
|
||||
it "outdents the text", ->
|
||||
editSession.setCursorBufferPosition([4, 29])
|
||||
editSession.insertText("\n")
|
||||
expect(editSession.buffer.lineForRow(5)).toEqual(" ")
|
||||
editSession.insertText("}")
|
||||
expect(editSession.buffer.lineForRow(5)).toEqual(" }")
|
||||
expect(editSession.getCursorBufferPosition().column).toBe 5
|
||||
describe "when the current line does not match an auto-outdent pattern", ->
|
||||
it "leaves the line unchanged", ->
|
||||
editSession.setCursorBufferPosition([2, 4])
|
||||
expect(buffer.indentationForRow(2)).toBe buffer.indentationForRow(1) + 2
|
||||
editSession.insertText 'foo'
|
||||
expect(buffer.indentationForRow(2)).toBe buffer.indentationForRow(1) + 2
|
||||
|
||||
describe ".insertNewline()", ->
|
||||
describe "when there is a single cursor", ->
|
||||
|
||||
@@ -329,17 +329,27 @@ class Buffer
|
||||
isRowBlank: (row) ->
|
||||
not /\S/.test @lineForRow(row)
|
||||
|
||||
nextNonBlankRow: (row) ->
|
||||
lastRow = @getLastRow()
|
||||
if row < lastRow
|
||||
for row in [(row + 1)..lastRow]
|
||||
return row unless @isRowBlank(row)
|
||||
previousNonBlankRow: (startRow) ->
|
||||
startRow = Math.min(startRow, @getLastRow())
|
||||
for row in [(startRow - 1)..0]
|
||||
return row unless @isRowBlank(row)
|
||||
null
|
||||
|
||||
nextNonBlankRow: (startRow) ->
|
||||
lastRow = @getLastRow()
|
||||
if startRow < lastRow
|
||||
for row in [(startRow + 1)..lastRow]
|
||||
return row unless @isRowBlank(row)
|
||||
null
|
||||
|
||||
indentationForRow: (row) ->
|
||||
@lineForRow(row).match(/^\s*/)?[0].length
|
||||
|
||||
setIndentationForRow: (bufferRow, newLevel) ->
|
||||
currentLevel = @indentationForRow(bufferRow)
|
||||
indentString = [0...newLevel].map(-> ' ').join('')
|
||||
@change([[bufferRow, 0], [bufferRow, currentLevel]], indentString)
|
||||
|
||||
logLines: (start=0, end=@getLastRow())->
|
||||
for row in [start..end]
|
||||
line = @lineForRow(row)
|
||||
|
||||
@@ -247,17 +247,17 @@ class EditSession
|
||||
largestFoldStartingAtScreenRow: (screenRow) ->
|
||||
@displayBuffer.largestFoldStartingAtScreenRow(screenRow)
|
||||
|
||||
indentationForRow: (row) ->
|
||||
@languageMode.indentationForRow(row)
|
||||
autoIndentBufferRows: (startRow, endRow) ->
|
||||
@languageMode.autoIndentBufferRows(startRow, endRow)
|
||||
|
||||
autoIndentRows: (startRow, endRow) ->
|
||||
@autoIndentRow(row) for row in [startRow..endRow]
|
||||
autoIndentBufferRow: (bufferRow) ->
|
||||
@languageMode.autoIndentBufferRow(bufferRow)
|
||||
|
||||
autoIndentRow: (row) ->
|
||||
actualIndentation = @lineForBufferRow(row).match(/^\s*/)[0]
|
||||
desiredIndentation = @indentationForRow(row)
|
||||
if actualIndentation != desiredIndentation
|
||||
@buffer.change([[row, 0], [row, actualIndentation.length]], desiredIndentation)
|
||||
autoIncreaseIndentForBufferRow: (bufferRow) ->
|
||||
@languageMode.autoIncreaseIndentForBufferRow(bufferRow)
|
||||
|
||||
autoDecreaseIndentForRow: (bufferRow) ->
|
||||
@languageMode.autoDecreaseIndentForBufferRow(bufferRow)
|
||||
|
||||
toggleLineCommentsInRange: (range) ->
|
||||
@languageMode.toggleLineCommentsInRange(range)
|
||||
|
||||
@@ -13,7 +13,8 @@ class LanguageMode
|
||||
"'": "'"
|
||||
|
||||
constructor: (@editSession) ->
|
||||
@grammar = TextMateBundle.grammarForFileName(@editSession.buffer.getBaseName())
|
||||
@buffer = @editSession.buffer
|
||||
@grammar = TextMateBundle.grammarForFileName(@buffer.getBaseName())
|
||||
|
||||
_.adviseBefore @editSession, 'insertText', (text) =>
|
||||
return true if @editSession.hasMultipleCursors()
|
||||
@@ -84,25 +85,43 @@ class LanguageMode
|
||||
|
||||
[bufferRow, foldEndRow]
|
||||
|
||||
indentationForRow: (row) ->
|
||||
for precedingRow in [row - 1..-1]
|
||||
return if precedingRow < 0
|
||||
precedingLine = @editSession.buffer.lineForRow(precedingRow)
|
||||
break if /\S/.test(precedingLine)
|
||||
|
||||
autoIndentBufferRows: (startRow, endRow) ->
|
||||
@autoIndentBufferRow(row) for row in [startRow..endRow]
|
||||
|
||||
autoIndentBufferRow: (bufferRow) ->
|
||||
@autoIncreaseIndentForBufferRow(bufferRow)
|
||||
@autoDecreaseIndentForBufferRow(bufferRow)
|
||||
|
||||
autoIncreaseIndentForBufferRow: (bufferRow) ->
|
||||
precedingRow = @buffer.previousNonBlankRow(bufferRow)
|
||||
return unless precedingRow?
|
||||
|
||||
precedingLine = @editSession.lineForBufferRow(precedingRow)
|
||||
scopes = @tokenizedBuffer.scopesForPosition([precedingRow, Infinity])
|
||||
indentation = precedingLine.match(/^\s*/)[0]
|
||||
increaseIndentPattern = TextMateBundle.getPreferenceInScope(scopes[0], 'increaseIndentPattern')
|
||||
decreaseIndentPattern = TextMateBundle.getPreferenceInScope(scopes[0], 'decreaseIndentPattern')
|
||||
increaseIndentPattern = new OnigRegExp(TextMateBundle.getPreferenceInScope(scopes[0], 'increaseIndentPattern'))
|
||||
|
||||
if new OnigRegExp(increaseIndentPattern).search(precedingLine)
|
||||
indentation += @editSession.tabText
|
||||
currentIndentation = @buffer.indentationForRow(bufferRow)
|
||||
desiredIndentation = @buffer.indentationForRow(precedingRow)
|
||||
desiredIndentation += @editSession.tabText.length if increaseIndentPattern.test(precedingLine)
|
||||
if desiredIndentation > currentIndentation
|
||||
@buffer.setIndentationForRow(bufferRow, desiredIndentation)
|
||||
|
||||
line = @editSession.buffer.lineForRow(row)
|
||||
if new OnigRegExp(decreaseIndentPattern).search(line)
|
||||
indentation = indentation.replace(@editSession.tabText, "")
|
||||
autoDecreaseIndentForBufferRow: (bufferRow) ->
|
||||
scopes = @tokenizedBuffer.scopesForPosition([bufferRow, 0])
|
||||
increaseIndentPattern = new OnigRegExp(TextMateBundle.getPreferenceInScope(scopes[0], 'increaseIndentPattern'))
|
||||
decreaseIndentPattern = new OnigRegExp(TextMateBundle.getPreferenceInScope(scopes[0], 'decreaseIndentPattern'))
|
||||
line = @buffer.lineForRow(bufferRow)
|
||||
return unless decreaseIndentPattern.test(line)
|
||||
|
||||
indentation
|
||||
currentIndentation = @buffer.indentationForRow(bufferRow)
|
||||
precedingRow = @buffer.previousNonBlankRow(bufferRow)
|
||||
precedingLine = @buffer.lineForRow(precedingRow)
|
||||
|
||||
desiredIndentation = @buffer.indentationForRow(precedingRow)
|
||||
desiredIndentation -= @editSession.tabText.length unless increaseIndentPattern.test(precedingLine)
|
||||
if desiredIndentation < currentIndentation
|
||||
@buffer.setIndentationForRow(bufferRow, desiredIndentation)
|
||||
|
||||
getLineTokens: (line, stack) ->
|
||||
{tokens, stack} = @grammar.getLineTokens(line, stack)
|
||||
|
||||
@@ -74,3 +74,6 @@ class Range
|
||||
else
|
||||
columns = @end.column
|
||||
new Point(rows, columns)
|
||||
|
||||
getRowCount: ->
|
||||
@end.row - @start.row + 1
|
||||
|
||||
@@ -134,7 +134,18 @@ class Selection
|
||||
@cursor.setBufferPosition(newBufferRange.end, skipAtomicTokens: true) if wasReversed
|
||||
|
||||
if @editSession.autoIndent
|
||||
@editSession.autoIndentRows(newBufferRange.start.row, newBufferRange.end.row)
|
||||
if /\n/.test(text)
|
||||
firstLinePrefix = @editSession.getTextInBufferRange([[newBufferRange.start.row, 0], newBufferRange.start])
|
||||
if /^\s*$/.test(firstLinePrefix)
|
||||
@editSession.autoIncreaseIndentForBufferRow(newBufferRange.start.row)
|
||||
if newBufferRange.getRowCount() > 1
|
||||
@editSession.autoIndentBufferRows(newBufferRange.start.row + 1, newBufferRange.end.row)
|
||||
else
|
||||
@editSession.autoIncreaseIndentForBufferRow(newBufferRange.start.row + 1)
|
||||
if newBufferRange.getRowCount() > 2
|
||||
@editSession.autoIndentBufferRows(newBufferRange.start.row + 2, newBufferRange.end.row)
|
||||
else
|
||||
@editSession.autoDecreaseIndentForRow(newBufferRange.start.row)
|
||||
|
||||
backspace: ->
|
||||
if @isEmpty() and not @editSession.isFoldedAtScreenRow(@cursor.getScreenRow())
|
||||
|
||||
Reference in New Issue
Block a user