mirror of
https://github.com/atom/atom.git
synced 2026-01-25 23:08:18 -05:00
Move auto-indent code to TokenizedBuffer, 🔥 LanguageMode
This commit is contained in:
@@ -1,123 +0,0 @@
|
||||
{Range, Point} = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
{OnigRegExp} = require 'oniguruma'
|
||||
ScopeDescriptor = require './scope-descriptor'
|
||||
NullGrammar = require './null-grammar'
|
||||
|
||||
module.exports =
|
||||
class LanguageMode
|
||||
# Sets up a `LanguageMode` for the given {TextEditor}.
|
||||
#
|
||||
# editor - The {TextEditor} to associate with
|
||||
constructor: (@editor) ->
|
||||
{@buffer} = @editor
|
||||
@regexesByPattern = {}
|
||||
|
||||
# Given a buffer row, this returns a suggested indentation level.
|
||||
#
|
||||
# The indentation level provided is based on the current {LanguageMode}.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
#
|
||||
# Returns a {Number}.
|
||||
suggestedIndentForBufferRow: (bufferRow, options) ->
|
||||
line = @buffer.lineForRow(bufferRow)
|
||||
tokenizedLine = @editor.tokenizedBuffer.tokenizedLineForRow(bufferRow)
|
||||
@suggestedIndentForTokenizedLineAtBufferRow(bufferRow, line, tokenizedLine, options)
|
||||
|
||||
suggestedIndentForLineAtBufferRow: (bufferRow, line, options) ->
|
||||
tokenizedLine = @editor.tokenizedBuffer.buildTokenizedLineForRowWithText(bufferRow, line)
|
||||
@suggestedIndentForTokenizedLineAtBufferRow(bufferRow, line, tokenizedLine, options)
|
||||
|
||||
suggestedIndentForTokenizedLineAtBufferRow: (bufferRow, line, tokenizedLine, options) ->
|
||||
iterator = tokenizedLine.getTokenIterator()
|
||||
iterator.next()
|
||||
scopeDescriptor = new ScopeDescriptor(scopes: iterator.getScopes())
|
||||
|
||||
increaseIndentRegex = @increaseIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
decreaseIndentRegex = @decreaseIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
decreaseNextIndentRegex = @decreaseNextIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
|
||||
if options?.skipBlankLines ? true
|
||||
precedingRow = @buffer.previousNonBlankRow(bufferRow)
|
||||
return 0 unless precedingRow?
|
||||
else
|
||||
precedingRow = bufferRow - 1
|
||||
return 0 if precedingRow < 0
|
||||
|
||||
desiredIndentLevel = @editor.indentationForBufferRow(precedingRow)
|
||||
return desiredIndentLevel unless increaseIndentRegex
|
||||
|
||||
unless @editor.isBufferRowCommented(precedingRow)
|
||||
precedingLine = @buffer.lineForRow(precedingRow)
|
||||
desiredIndentLevel += 1 if increaseIndentRegex?.testSync(precedingLine)
|
||||
desiredIndentLevel -= 1 if decreaseNextIndentRegex?.testSync(precedingLine)
|
||||
|
||||
unless @buffer.isRowBlank(precedingRow)
|
||||
desiredIndentLevel -= 1 if decreaseIndentRegex?.testSync(line)
|
||||
|
||||
Math.max(desiredIndentLevel, 0)
|
||||
|
||||
# Calculate a minimum indent level for a range of lines excluding empty lines.
|
||||
#
|
||||
# startRow - The row {Number} to start at
|
||||
# endRow - The row {Number} to end at
|
||||
#
|
||||
# Returns a {Number} of the indent level of the block of lines.
|
||||
|
||||
# Indents all the rows between two buffer row numbers.
|
||||
#
|
||||
# startRow - The row {Number} to start at
|
||||
# endRow - The row {Number} to end at
|
||||
autoIndentBufferRows: (startRow, endRow) ->
|
||||
@autoIndentBufferRow(row) for row in [startRow..endRow] by 1
|
||||
return
|
||||
|
||||
# Given a buffer row, this indents it.
|
||||
#
|
||||
# bufferRow - The row {Number}.
|
||||
# options - An options {Object} to pass through to {TextEditor::setIndentationForBufferRow}.
|
||||
autoIndentBufferRow: (bufferRow, options) ->
|
||||
indentLevel = @suggestedIndentForBufferRow(bufferRow, options)
|
||||
@editor.setIndentationForBufferRow(bufferRow, indentLevel, options)
|
||||
|
||||
# Given a buffer row, this decreases the indentation.
|
||||
#
|
||||
# bufferRow - The row {Number}
|
||||
autoDecreaseIndentForBufferRow: (bufferRow) ->
|
||||
scopeDescriptor = @editor.scopeDescriptorForBufferPosition([bufferRow, 0])
|
||||
return unless decreaseIndentRegex = @decreaseIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
|
||||
line = @buffer.lineForRow(bufferRow)
|
||||
return unless decreaseIndentRegex.testSync(line)
|
||||
|
||||
currentIndentLevel = @editor.indentationForBufferRow(bufferRow)
|
||||
return if currentIndentLevel is 0
|
||||
|
||||
precedingRow = @buffer.previousNonBlankRow(bufferRow)
|
||||
return unless precedingRow?
|
||||
|
||||
precedingLine = @buffer.lineForRow(precedingRow)
|
||||
desiredIndentLevel = @editor.indentationForBufferRow(precedingRow)
|
||||
|
||||
if increaseIndentRegex = @increaseIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
desiredIndentLevel -= 1 unless increaseIndentRegex.testSync(precedingLine)
|
||||
|
||||
if decreaseNextIndentRegex = @decreaseNextIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
desiredIndentLevel -= 1 if decreaseNextIndentRegex.testSync(precedingLine)
|
||||
|
||||
if desiredIndentLevel >= 0 and desiredIndentLevel < currentIndentLevel
|
||||
@editor.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
|
||||
|
||||
cacheRegex: (pattern) ->
|
||||
if pattern
|
||||
@regexesByPattern[pattern] ?= new OnigRegExp(pattern)
|
||||
|
||||
increaseIndentRegexForScopeDescriptor: (scopeDescriptor) ->
|
||||
@cacheRegex(@editor.getIncreaseIndentPattern(scopeDescriptor))
|
||||
|
||||
decreaseIndentRegexForScopeDescriptor: (scopeDescriptor) ->
|
||||
@cacheRegex(@editor.getDecreaseIndentPattern(scopeDescriptor))
|
||||
|
||||
decreaseNextIndentRegexForScopeDescriptor: (scopeDescriptor) ->
|
||||
@cacheRegex(@editor.getDecreaseNextIndentPattern(scopeDescriptor))
|
||||
@@ -381,7 +381,7 @@ class Selection extends Model
|
||||
if options.autoIndent and textIsAutoIndentable and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0
|
||||
autoIndentFirstLine = true
|
||||
firstLine = precedingText + firstInsertedLine
|
||||
desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine)
|
||||
desiredIndentLevel = @editor.tokenizedBuffer.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine)
|
||||
indentAdjustment = desiredIndentLevel - @editor.indentLevelForLine(firstLine)
|
||||
@adjustIndent(remainingLines, indentAdjustment)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ Grim = require 'grim'
|
||||
{CompositeDisposable, Disposable, Emitter} = require 'event-kit'
|
||||
{OnigRegExp} = require 'oniguruma'
|
||||
{Point, Range} = TextBuffer = require 'text-buffer'
|
||||
LanguageMode = require './language-mode'
|
||||
DecorationManager = require './decoration-manager'
|
||||
TokenizedBuffer = require './tokenized-buffer'
|
||||
Cursor = require './cursor'
|
||||
@@ -80,7 +79,6 @@ class TextEditor extends Model
|
||||
serializationVersion: 1
|
||||
|
||||
buffer: null
|
||||
languageMode: null
|
||||
cursors: null
|
||||
showCursorOnSelection: null
|
||||
selections: null
|
||||
@@ -245,8 +243,6 @@ class TextEditor extends Model
|
||||
initialColumn = Math.max(parseInt(initialColumn) or 0, 0)
|
||||
@addCursorAtBufferPosition([initialLine, initialColumn])
|
||||
|
||||
@languageMode = new LanguageMode(this)
|
||||
|
||||
@gutterContainer = new GutterContainer(this)
|
||||
@lineNumberGutter = @gutterContainer.addGutter
|
||||
name: 'line-number'
|
||||
@@ -3085,7 +3081,8 @@ class TextEditor extends Model
|
||||
else
|
||||
endColumn = @lineTextForBufferRow(bufferRow).match(/^\s*/)[0].length
|
||||
newIndentString = @buildIndentString(newLevel)
|
||||
@buffer.setTextInRange([[bufferRow, 0], [bufferRow, endColumn]], newIndentString)
|
||||
if newIndentString.length isnt endColumn
|
||||
@buffer.setTextInRange([[bufferRow, 0], [bufferRow, endColumn]], newIndentString)
|
||||
|
||||
# Extended: Indent rows intersecting selections by one level.
|
||||
indentSelectedRows: ->
|
||||
@@ -3626,18 +3623,6 @@ class TextEditor extends Model
|
||||
getCommentStrings: (scopes) ->
|
||||
@scopedSettingsDelegate?.getCommentStrings?(scopes)
|
||||
|
||||
getIncreaseIndentPattern: (scopes) ->
|
||||
@scopedSettingsDelegate?.getIncreaseIndentPattern?(scopes)
|
||||
|
||||
getDecreaseIndentPattern: (scopes) ->
|
||||
@scopedSettingsDelegate?.getDecreaseIndentPattern?(scopes)
|
||||
|
||||
getDecreaseNextIndentPattern: (scopes) ->
|
||||
@scopedSettingsDelegate?.getDecreaseNextIndentPattern?(scopes)
|
||||
|
||||
getFoldEndPattern: (scopes) ->
|
||||
@scopedSettingsDelegate?.getFoldEndPattern?(scopes)
|
||||
|
||||
###
|
||||
Section: Event Handlers
|
||||
###
|
||||
@@ -3873,15 +3858,32 @@ class TextEditor extends Model
|
||||
Section: Language Mode Delegated Methods
|
||||
###
|
||||
|
||||
suggestedIndentForBufferRow: (bufferRow, options) -> @languageMode.suggestedIndentForBufferRow(bufferRow, options)
|
||||
suggestedIndentForBufferRow: (bufferRow, options) -> @tokenizedBuffer.suggestedIndentForBufferRow(bufferRow, options)
|
||||
|
||||
autoIndentBufferRow: (bufferRow, options) -> @languageMode.autoIndentBufferRow(bufferRow, options)
|
||||
# Given a buffer row, indent it.
|
||||
#
|
||||
# * bufferRow - The row {Number}.
|
||||
# * options - An options {Object} to pass through to {TextEditor::setIndentationForBufferRow}.
|
||||
autoIndentBufferRow: (bufferRow, options) ->
|
||||
indentLevel = @suggestedIndentForBufferRow(bufferRow, options)
|
||||
@setIndentationForBufferRow(bufferRow, indentLevel, options)
|
||||
|
||||
autoIndentBufferRows: (startRow, endRow) -> @languageMode.autoIndentBufferRows(startRow, endRow)
|
||||
# Indents all the rows between two buffer row numbers.
|
||||
#
|
||||
# * startRow - The row {Number} to start at
|
||||
# * endRow - The row {Number} to end at
|
||||
autoIndentBufferRows: (startRow, endRow) ->
|
||||
row = startRow
|
||||
while row <= endRow
|
||||
@autoIndentBufferRow(row)
|
||||
row++
|
||||
return
|
||||
|
||||
autoDecreaseIndentForBufferRow: (bufferRow) -> @languageMode.autoDecreaseIndentForBufferRow(bufferRow)
|
||||
autoDecreaseIndentForBufferRow: (bufferRow) ->
|
||||
indentLevel = @tokenizedBuffer.suggestedIndentForEditedBufferRow(bufferRow)
|
||||
@setIndentationForBufferRow(bufferRow, indentLevel)
|
||||
|
||||
toggleLineCommentForBufferRow: (row) -> @languageMode.toggleLineCommentsForBufferRow(row)
|
||||
toggleLineCommentForBufferRow: (row) -> @toggleLineCommentsForBufferRows(row, row)
|
||||
|
||||
toggleLineCommentsForBufferRows: (start, end) ->
|
||||
scope = @scopeDescriptorForBufferPosition([start, 0])
|
||||
|
||||
@@ -57,6 +57,105 @@ class TokenizedBuffer {
|
||||
return !this.alive
|
||||
}
|
||||
|
||||
/*
|
||||
Section - auto-indent
|
||||
*/
|
||||
|
||||
// Get the suggested indentation level for an existing line in the buffer.
|
||||
//
|
||||
// * bufferRow - A {Number} indicating the buffer row
|
||||
//
|
||||
// Returns a {Number}.
|
||||
suggestedIndentForBufferRow (bufferRow, options) {
|
||||
const line = this.buffer.lineForRow(bufferRow)
|
||||
const tokenizedLine = this.tokenizedLineForRow(bufferRow)
|
||||
return this._suggestedIndentForTokenizedLineAtBufferRow(bufferRow, line, tokenizedLine, options)
|
||||
}
|
||||
|
||||
// Get the suggested indentation level for a given line of text, if it were inserted at the given
|
||||
// row in the buffer.
|
||||
//
|
||||
// * bufferRow - A {Number} indicating the buffer row
|
||||
//
|
||||
// Returns a {Number}.
|
||||
suggestedIndentForLineAtBufferRow (bufferRow, line, options) {
|
||||
const tokenizedLine = this.buildTokenizedLineForRowWithText(bufferRow, line)
|
||||
return this._suggestedIndentForTokenizedLineAtBufferRow(bufferRow, line, tokenizedLine, options)
|
||||
}
|
||||
|
||||
// Get the suggested indentation level for a line in the buffer on which the user is currently
|
||||
// typing. This may return a different result from {::suggestedIndentForBufferRow} in order
|
||||
// to avoid unexpected changes in indentation.
|
||||
//
|
||||
// * bufferRow - The row {Number}
|
||||
//
|
||||
// Returns a {Number}.
|
||||
suggestedIndentForEditedBufferRow (bufferRow) {
|
||||
const line = this.buffer.lineForRow(bufferRow)
|
||||
const currentIndentLevel = this.indentLevelForLine(line)
|
||||
if (currentIndentLevel === 0) return currentIndentLevel
|
||||
|
||||
const scopeDescriptor = this.scopeDescriptorForPosition([bufferRow, 0])
|
||||
const decreaseIndentRegex = this.decreaseIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
if (!decreaseIndentRegex) return currentIndentLevel
|
||||
|
||||
if (!decreaseIndentRegex.testSync(line)) return currentIndentLevel
|
||||
|
||||
const precedingRow = this.buffer.previousNonBlankRow(bufferRow)
|
||||
if (precedingRow == null) return currentIndentLevel
|
||||
|
||||
const precedingLine = this.buffer.lineForRow(precedingRow)
|
||||
let desiredIndentLevel = this.indentLevelForLine(precedingLine)
|
||||
|
||||
const increaseIndentRegex = this.increaseIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
if (increaseIndentRegex) {
|
||||
if (!increaseIndentRegex.testSync(precedingLine)) desiredIndentLevel -= 1
|
||||
}
|
||||
|
||||
const decreaseNextIndentRegex = this.decreaseNextIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
if (decreaseNextIndentRegex) {
|
||||
if (decreaseNextIndentRegex.testSync(precedingLine)) desiredIndentLevel -= 1
|
||||
}
|
||||
|
||||
if (desiredIndentLevel < 0) return 0
|
||||
if (desiredIndentLevel > currentIndentLevel) return currentIndentLevel
|
||||
return desiredIndentLevel
|
||||
}
|
||||
|
||||
_suggestedIndentForTokenizedLineAtBufferRow (bufferRow, line, tokenizedLine, options) {
|
||||
const iterator = tokenizedLine.getTokenIterator()
|
||||
iterator.next()
|
||||
const scopeDescriptor = new ScopeDescriptor({scopes: iterator.getScopes()})
|
||||
|
||||
const increaseIndentRegex = this.increaseIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
const decreaseIndentRegex = this.decreaseIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
const decreaseNextIndentRegex = this.decreaseNextIndentRegexForScopeDescriptor(scopeDescriptor)
|
||||
|
||||
let precedingRow
|
||||
if (!options || options.skipBlankLines !== false) {
|
||||
precedingRow = this.buffer.previousNonBlankRow(bufferRow)
|
||||
if (precedingRow == null) return 0
|
||||
} else {
|
||||
precedingRow = bufferRow - 1
|
||||
if (precedingRow < 0) return 0
|
||||
}
|
||||
|
||||
const precedingLine = this.buffer.lineForRow(precedingRow)
|
||||
let desiredIndentLevel = this.indentLevelForLine(precedingLine)
|
||||
if (!increaseIndentRegex) return desiredIndentLevel
|
||||
|
||||
if (!this.isRowCommented(precedingRow)) {
|
||||
if (increaseIndentRegex && increaseIndentRegex.testSync(precedingLine)) desiredIndentLevel += 1
|
||||
if (decreaseNextIndentRegex && decreaseNextIndentRegex.testSync(precedingLine)) desiredIndentLevel -= 1
|
||||
}
|
||||
|
||||
if (!this.buffer.isRowBlank(precedingRow)) {
|
||||
if (decreaseIndentRegex && decreaseIndentRegex.testSync(line)) desiredIndentLevel -= 1
|
||||
}
|
||||
|
||||
return Math.max(desiredIndentLevel, 0)
|
||||
}
|
||||
|
||||
buildIterator () {
|
||||
return new TokenizedBufferIterator(this)
|
||||
}
|
||||
@@ -595,6 +694,24 @@ class TokenizedBuffer {
|
||||
return foldEndRow
|
||||
}
|
||||
|
||||
increaseIndentRegexForScopeDescriptor (scopeDescriptor) {
|
||||
if (this.scopedSettingsDelegate) {
|
||||
return this.regexForPattern(this.scopedSettingsDelegate.getIncreaseIndentPattern(scopeDescriptor))
|
||||
}
|
||||
}
|
||||
|
||||
decreaseIndentRegexForScopeDescriptor (scopeDescriptor) {
|
||||
if (this.scopedSettingsDelegate) {
|
||||
return this.regexForPattern(this.scopedSettingsDelegate.getDecreaseIndentPattern(scopeDescriptor))
|
||||
}
|
||||
}
|
||||
|
||||
decreaseNextIndentRegexForScopeDescriptor (scopeDescriptor) {
|
||||
if (this.scopedSettingsDelegate) {
|
||||
return this.regexForPattern(this.scopedSettingsDelegate.getDecreaseNextIndentPattern(scopeDescriptor))
|
||||
}
|
||||
}
|
||||
|
||||
foldEndRegexForScopeDescriptor (scopes) {
|
||||
if (this.scopedSettingsDelegate) {
|
||||
return this.regexForPattern(this.scopedSettingsDelegate.getFoldEndPattern(scopes))
|
||||
|
||||
Reference in New Issue
Block a user