mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
Introduce LanguageMode wrapper for Ace modes as a foundation of our own modes
LanguageMode is the central point for all language-specific behavior associated with an EditSession. There is one LanguageMode instance per EditSession. LanguageMode has access to the EditSession and its TokenizedBuffer, and in reverse the EditSession, DisplayBuffer, and TokenizedBuffer also make use of LanguageMode. This is a bit incestuous, but I think it's okay because you can think of LanguageMode as a swappable strategy object that governs language-specific aspects of that constellation of objects.
This commit is contained in:
@@ -2,16 +2,16 @@ DisplayBuffer = require 'display-buffer'
|
||||
Buffer = require 'buffer'
|
||||
|
||||
describe "DisplayBuffer", ->
|
||||
[displayBuffer, buffer, changeHandler, tabText] = []
|
||||
[editSession, displayBuffer, buffer, changeHandler, tabText] = []
|
||||
beforeEach ->
|
||||
tabText = ' '
|
||||
buffer = new Buffer(require.resolve 'fixtures/sample.js')
|
||||
displayBuffer = new DisplayBuffer(buffer, {tabText})
|
||||
editSession = fixturesProject.buildEditSessionForPath('sample.js', { tabText })
|
||||
{ buffer, displayBuffer } = editSession
|
||||
changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer.on 'change', changeHandler
|
||||
|
||||
afterEach ->
|
||||
buffer.destroy()
|
||||
editSession.destroy()
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
it "renders line numbers correctly", ->
|
||||
@@ -198,12 +198,16 @@ describe "DisplayBuffer", ->
|
||||
expect(fold.endRow).toBe 9
|
||||
|
||||
describe "primitive folding", ->
|
||||
editSession2 = null
|
||||
|
||||
beforeEach ->
|
||||
buffer.destroy()
|
||||
buffer = new Buffer(require.resolve 'fixtures/two-hundred.txt')
|
||||
displayBuffer = new DisplayBuffer(buffer, {tabText})
|
||||
editSession2 = fixturesProject.buildEditSessionForPath('two-hundred.txt')
|
||||
{ buffer, displayBuffer } = editSession2
|
||||
displayBuffer.on 'change', changeHandler
|
||||
|
||||
afterEach ->
|
||||
editSession2.destroy()
|
||||
|
||||
describe "when folds are created and destroyed", ->
|
||||
describe "when a fold spans multiple lines", ->
|
||||
it "replaces the lines spanned by the fold with a placeholder that references the fold object", ->
|
||||
|
||||
78
spec/app/language-mode-spec.coffee
Normal file
78
spec/app/language-mode-spec.coffee
Normal file
@@ -0,0 +1,78 @@
|
||||
Project = require 'project'
|
||||
Buffer = require 'buffer'
|
||||
EditSession = require 'edit-session'
|
||||
|
||||
describe "LanguageMode", ->
|
||||
[editSession, buffer, languageMode] = []
|
||||
|
||||
afterEach ->
|
||||
editSession.destroy()
|
||||
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
editSession = fixturesProject.buildEditSessionForPath('sample.js', autoIndent: false)
|
||||
{ buffer, languageMode } = editSession
|
||||
|
||||
describe ".toggleLineCommentsInRange(range)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsInRange([[4, 5], [7, 8]])
|
||||
expect(buffer.lineForRow(4)).toBe "// while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe "// current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
|
||||
languageMode.toggleLineCommentsInRange([[4, 5], [5, 8]])
|
||||
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".isBufferRowFoldable(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(languageMode.isBufferRowFoldable(0)).toBeTruthy()
|
||||
expect(languageMode.isBufferRowFoldable(1)).toBeTruthy()
|
||||
expect(languageMode.isBufferRowFoldable(2)).toBeFalsy()
|
||||
expect(languageMode.isBufferRowFoldable(3)).toBeFalsy()
|
||||
|
||||
describe ".rowRangeForFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(1)).toEqual [1, 9]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
editSession = fixturesProject.buildEditSessionForPath('coffee.coffee', autoIndent: false)
|
||||
{ buffer, languageMode } = editSession
|
||||
|
||||
describe ".toggleLineCommentsInRange(range)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsInRange([[4, 5], [7, 8]])
|
||||
expect(buffer.lineForRow(4)).toBe " #pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " #left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " #right = []"
|
||||
expect(buffer.lineForRow(7)).toBe "#"
|
||||
|
||||
languageMode.toggleLineCommentsInRange([[4, 5], [5, 8]])
|
||||
expect(buffer.lineForRow(4)).toBe " pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " #right = []"
|
||||
expect(buffer.lineForRow(7)).toBe "#"
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".isBufferRowFoldable(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(languageMode.isBufferRowFoldable(0)).toBeTruthy()
|
||||
expect(languageMode.isBufferRowFoldable(1)).toBeTruthy()
|
||||
expect(languageMode.isBufferRowFoldable(2)).toBeFalsy()
|
||||
expect(languageMode.isBufferRowFoldable(3)).toBeFalsy()
|
||||
expect(languageMode.isBufferRowFoldable(19)).toBeTruthy()
|
||||
|
||||
describe ".rowRangeForFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(0)).toEqual [0, 20]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(1)).toEqual [1, 17]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(19)).toEqual [19, 20]
|
||||
@@ -5,17 +5,17 @@ TokenizedBuffer = require 'tokenized-buffer'
|
||||
Point = require 'point'
|
||||
|
||||
describe "LineMap", ->
|
||||
[tokenizedBuffer, map] = []
|
||||
[editSession, tokenizedBuffer, map] = []
|
||||
[line0, line1, line2, line3, line4] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = new Buffer(require.resolve 'fixtures/sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
editSession = fixturesProject.buildEditSessionForPath('sample.js')
|
||||
{ buffer, tokenizedBuffer } = editSession
|
||||
map = new LineMap
|
||||
[line0, line1, line2, line3, line4] = tokenizedBuffer.linesForScreenRows(0, 4)
|
||||
|
||||
afterEach ->
|
||||
tokenizedBuffer.buffer.destroy()
|
||||
editSession.destroy()
|
||||
|
||||
describe ".insertAtBufferRow(row, lineFragments)", ->
|
||||
it "inserts the given line fragments before the specified buffer row", ->
|
||||
|
||||
@@ -3,16 +3,16 @@ Buffer = require 'buffer'
|
||||
TokenizedBuffer = require 'tokenized-buffer'
|
||||
|
||||
describe "ScreenLine", ->
|
||||
[buffer, tabText, screenLine, tokenizedBuffer] = []
|
||||
[editSession, buffer, tabText, screenLine, tokenizedBuffer] = []
|
||||
|
||||
beforeEach ->
|
||||
tabText = ' '
|
||||
buffer = new Buffer(require.resolve 'fixtures/sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer, tabText)
|
||||
editSession = fixturesProject.buildEditSessionForPath('sample.js')
|
||||
{ buffer, tokenizedBuffer } = editSession
|
||||
screenLine = tokenizedBuffer.lineForScreenRow(3)
|
||||
|
||||
afterEach ->
|
||||
buffer.destroy()
|
||||
editSession.destroy()
|
||||
|
||||
describe ".splitAt(column)", ->
|
||||
it "breaks the line fragment into two fragments", ->
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
TokenizedBuffer = require 'tokenized-buffer'
|
||||
LanguageMode = require 'language-mode'
|
||||
Buffer = require 'buffer'
|
||||
Range = require 'range'
|
||||
|
||||
describe "TokenizedBuffer", ->
|
||||
[tokenizedBuffer, buffer] = []
|
||||
[editSession, tokenizedBuffer, buffer] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = new Buffer(require.resolve('fixtures/sample.js'))
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer, ' ')
|
||||
editSession = fixturesProject.buildEditSessionForPath('sample.js', autoIndent: false)
|
||||
{ tokenizedBuffer, buffer } = editSession
|
||||
|
||||
afterEach ->
|
||||
buffer.destroy()
|
||||
editSession.destroy()
|
||||
|
||||
describe ".findClosingBracket(startBufferPosition)", ->
|
||||
it "returns the position of the matching bracket, skipping any nested brackets", ->
|
||||
@@ -20,81 +21,6 @@ describe "TokenizedBuffer", ->
|
||||
it "returns the position of the matching bracket, skipping any nested brackets", ->
|
||||
expect(tokenizedBuffer.findOpeningBracket([9, 2])).toEqual [1, 29]
|
||||
|
||||
describe ".toggleLineCommentsInRange(range)", ->
|
||||
describe "javascript", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
tokenizedBuffer.toggleLineCommentsInRange([[4, 5], [7, 8]])
|
||||
expect(buffer.lineForRow(4)).toBe "// while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe "// current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
|
||||
tokenizedBuffer.toggleLineCommentsInRange([[4, 5], [5, 8]])
|
||||
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
|
||||
describe "coffeescript", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
buffer.destroy()
|
||||
buffer = new Buffer(require.resolve('fixtures/coffee.coffee'))
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer, ' ')
|
||||
|
||||
tokenizedBuffer.toggleLineCommentsInRange([[4, 5], [7, 8]])
|
||||
expect(buffer.lineForRow(4)).toBe " #pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " #left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " #right = []"
|
||||
expect(buffer.lineForRow(7)).toBe "#"
|
||||
|
||||
tokenizedBuffer.toggleLineCommentsInRange([[4, 5], [5, 8]])
|
||||
expect(buffer.lineForRow(4)).toBe " pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " #right = []"
|
||||
expect(buffer.lineForRow(7)).toBe "#"
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
buffer.destroy()
|
||||
buffer = new Buffer(require.resolve 'fixtures/sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
|
||||
describe ".isBufferRowFoldable(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(0)).toBeTruthy()
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(1)).toBeTruthy()
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(2)).toBeFalsy()
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(3)).toBeFalsy()
|
||||
|
||||
describe ".rowRangeForFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(tokenizedBuffer.rowRangeForFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
expect(tokenizedBuffer.rowRangeForFoldAtBufferRow(1)).toEqual [1, 9]
|
||||
expect(tokenizedBuffer.rowRangeForFoldAtBufferRow(2)).toBeNull()
|
||||
expect(tokenizedBuffer.rowRangeForFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
buffer.destroy()
|
||||
buffer = new Buffer(require.resolve 'fixtures/coffee.coffee')
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
|
||||
describe ".isBufferRowFoldable(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(0)).toBeTruthy()
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(1)).toBeTruthy()
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(2)).toBeFalsy()
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(3)).toBeFalsy()
|
||||
expect(tokenizedBuffer.isBufferRowFoldable(19)).toBeTruthy()
|
||||
|
||||
describe ".rowRangeForFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(tokenizedBuffer.rowRangeForFoldAtBufferRow(0)).toEqual [0, 20]
|
||||
expect(tokenizedBuffer.rowRangeForFoldAtBufferRow(1)).toEqual [1, 17]
|
||||
expect(tokenizedBuffer.rowRangeForFoldAtBufferRow(2)).toBeNull()
|
||||
expect(tokenizedBuffer.rowRangeForFoldAtBufferRow(19)).toEqual [19, 20]
|
||||
|
||||
describe "tokenization", ->
|
||||
it "tokenizes all the lines in the buffer on construction", ->
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual(type: 'keyword.definition', value: 'var')
|
||||
@@ -225,13 +151,15 @@ describe "TokenizedBuffer", ->
|
||||
expect(event.newRange).toEqual new Range([2, 0], [7, buffer.lineForRow(7).length])
|
||||
|
||||
describe "when the buffer contains tab characters", ->
|
||||
tabText = null
|
||||
tabText = ' '
|
||||
editSession2 = null
|
||||
|
||||
beforeEach ->
|
||||
tabText = ' '
|
||||
buffer.destroy()
|
||||
buffer = new Buffer(require.resolve('fixtures/sample-with-tabs.coffee'))
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer, tabText)
|
||||
editSession2 = fixturesProject.buildEditSessionForPath('sample-with-tabs.coffee', { tabText })
|
||||
{ buffer, tokenizedBuffer } = editSession2
|
||||
|
||||
afterEach ->
|
||||
editSession2.destroy()
|
||||
|
||||
it "always renders each tab as its own atomic token containing tabText", ->
|
||||
screenLine0 = tokenizedBuffer.lineForScreenRow(0)
|
||||
|
||||
@@ -4,8 +4,8 @@ module.exports =
|
||||
class AceAdaptor
|
||||
foldWidgets: {}
|
||||
|
||||
constructor: (@tokenizedBuffer) ->
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
constructor: (@editSession) ->
|
||||
@buffer = @editSession.buffer
|
||||
|
||||
getLine: (bufferRow) ->
|
||||
@buffer.lineForRow(bufferRow)
|
||||
@@ -14,7 +14,7 @@ class AceAdaptor
|
||||
@buffer.getLineCount()
|
||||
|
||||
$findClosingBracket: (bracketType, bufferPosition) ->
|
||||
@tokenizedBuffer.findClosingBracket([bufferPosition.row, bufferPosition.column - 1])
|
||||
@editSession.tokenizedBuffer.findClosingBracket([bufferPosition.row, bufferPosition.column - 1])
|
||||
|
||||
indentRows: (startRow, endRow, indentString) ->
|
||||
for row in [startRow..endRow]
|
||||
@@ -25,4 +25,4 @@ class AceAdaptor
|
||||
@buffer.change(range, text)
|
||||
|
||||
findMatchingBracket: ({row, column}) ->
|
||||
@tokenizedBuffer.findOpeningBracket([row, column])
|
||||
@editSession.tokenizedBuffer.findOpeningBracket([row, column])
|
||||
|
||||
@@ -12,6 +12,7 @@ module.exports =
|
||||
class DisplayBuffer
|
||||
@idCounter: 1
|
||||
lineMap: null
|
||||
languageMode: null
|
||||
tokenizedBuffer: null
|
||||
activeFolds: null
|
||||
foldsById: null
|
||||
@@ -19,7 +20,9 @@ class DisplayBuffer
|
||||
|
||||
constructor: (@buffer, options={}) ->
|
||||
@id = @constructor.idCounter++
|
||||
@tokenizedBuffer = new TokenizedBuffer(@buffer, options.tabText ? ' ')
|
||||
options.tabText ?= ' '
|
||||
@languageMode = options.languageMode
|
||||
@tokenizedBuffer = new TokenizedBuffer(@buffer, options)
|
||||
@softWrapColumn = options.softWrapColumn ? Infinity
|
||||
@activeFolds = {}
|
||||
@foldsById = {}
|
||||
@@ -51,14 +54,14 @@ class DisplayBuffer
|
||||
|
||||
foldAll: ->
|
||||
for currentRow in [0..@buffer.getLastRow()]
|
||||
[startRow, endRow] = @tokenizedBuffer.rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
[startRow, endRow] = @languageMode.rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow?
|
||||
|
||||
@createFold(startRow, endRow)
|
||||
|
||||
toggleFoldAtBufferRow: (bufferRow) ->
|
||||
for currentRow in [bufferRow..0]
|
||||
[startRow, endRow] = @tokenizedBuffer.rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
[startRow, endRow] = @languageMode.rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow? and startRow <= bufferRow <= endRow
|
||||
|
||||
if fold = @largestFoldStartingAtBufferRow(startRow)
|
||||
@@ -209,7 +212,7 @@ class DisplayBuffer
|
||||
startBufferColumn = 0
|
||||
while currentBufferRow <= endBufferRow
|
||||
screenLine = @tokenizedBuffer.lineForScreenRow(currentBufferRow)
|
||||
screenLine.foldable = @tokenizedBuffer.isBufferRowFoldable(currentBufferRow)
|
||||
screenLine.foldable = @languageMode.isBufferRowFoldable(currentBufferRow)
|
||||
|
||||
if fold = @largestFoldStartingAtBufferRow(currentBufferRow)
|
||||
screenLine = screenLine.copy()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Point = require 'point'
|
||||
Buffer = require 'buffer'
|
||||
Anchor = require 'anchor'
|
||||
LanguageMode = require 'language-mode'
|
||||
DisplayBuffer = require 'display-buffer'
|
||||
Cursor = require 'cursor'
|
||||
Selection = require 'selection'
|
||||
@@ -22,6 +23,7 @@ class EditSession
|
||||
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
languageMode: null
|
||||
displayBuffer: null
|
||||
anchors: null
|
||||
anchorRanges: null
|
||||
@@ -34,7 +36,8 @@ class EditSession
|
||||
constructor: ({@project, @buffer, @tabText, @autoIndent, @softTabs, @softWrap}) ->
|
||||
@id = @constructor.idCounter++
|
||||
@softTabs ?= true
|
||||
@displayBuffer = new DisplayBuffer(@buffer, { @tabText })
|
||||
@languageMode = new LanguageMode(this, @buffer.getExtension())
|
||||
@displayBuffer = new DisplayBuffer(@buffer, { @languageMode, @tabText })
|
||||
@tokenizedBuffer = @displayBuffer.tokenizedBuffer
|
||||
@anchors = []
|
||||
@anchorRanges = []
|
||||
@@ -242,17 +245,17 @@ class EditSession
|
||||
@displayBuffer.largestFoldStartingAtScreenRow(screenRow)
|
||||
|
||||
indentationForRow: (row) ->
|
||||
@tokenizedBuffer.indentationForRow(row)
|
||||
@languageMode.indentationForRow(row)
|
||||
|
||||
autoIndentTextAfterBufferPosition: (text, bufferPosition) ->
|
||||
return { text } unless @autoIndent
|
||||
@tokenizedBuffer.autoIndentTextAfterBufferPosition(text, bufferPosition)
|
||||
@languageMode.autoIndentTextAfterBufferPosition(text, bufferPosition)
|
||||
|
||||
autoOutdentBufferRow: (bufferRow) ->
|
||||
@tokenizedBuffer.autoOutdentBufferRow(bufferRow)
|
||||
@languageMode.autoOutdentBufferRow(bufferRow)
|
||||
|
||||
toggleLineCommentsInRange: (range) ->
|
||||
@tokenizedBuffer.toggleLineCommentsInRange(range)
|
||||
@languageMode.toggleLineCommentsInRange(range)
|
||||
|
||||
mutateSelectedText: (fn) ->
|
||||
@transact => fn(selection) for selection in @getSelections()
|
||||
|
||||
60
src/app/language-mode.coffee
Normal file
60
src/app/language-mode.coffee
Normal file
@@ -0,0 +1,60 @@
|
||||
AceAdaptor = require 'ace-adaptor'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class LanguageMode
|
||||
constructor: (@editSession) ->
|
||||
@buffer = @editSession.buffer
|
||||
@aceMode = @requireAceMode()
|
||||
@aceAdaptor = new AceAdaptor(@editSession)
|
||||
|
||||
requireAceMode: (fileExtension) ->
|
||||
modeName = switch @editSession.buffer.getExtension()
|
||||
when 'js' then 'javascript'
|
||||
when 'coffee' then 'coffee'
|
||||
when 'rb', 'ru' then 'ruby'
|
||||
when 'c', 'h', 'cpp' then 'c_cpp'
|
||||
when 'html', 'htm' then 'html'
|
||||
when 'css' then 'css'
|
||||
when 'java' then 'java'
|
||||
when 'xml' then 'xml'
|
||||
else 'text'
|
||||
new (require("ace/mode/#{modeName}").Mode)
|
||||
|
||||
toggleLineCommentsInRange: (range) ->
|
||||
range = Range.fromObject(range)
|
||||
@aceMode.toggleCommentLines(@tokenizedBuffer.stateForRow(range.start.row), @aceAdaptor, range.start.row, range.end.row)
|
||||
|
||||
isBufferRowFoldable: (bufferRow) ->
|
||||
@aceMode.foldingRules?.getFoldWidget(@aceAdaptor, null, bufferRow) == "start"
|
||||
|
||||
rowRangeForFoldAtBufferRow: (bufferRow) ->
|
||||
if aceRange = @aceMode.foldingRules?.getFoldWidgetRange(@aceAdaptor, null, bufferRow)
|
||||
[aceRange.start.row, aceRange.end.row]
|
||||
else
|
||||
null
|
||||
|
||||
indentationForRow: (row) ->
|
||||
state = @tokenizedBuffer.stateForRow(row)
|
||||
previousRowText = @buffer.lineForRow(row - 1)
|
||||
@aceMode.getNextLineIndent(state, previousRowText, @editSession.tabText)
|
||||
|
||||
autoIndentTextAfterBufferPosition: (text, bufferPosition) ->
|
||||
{ row, column} = bufferPosition
|
||||
state = @tokenizedBuffer.stateForRow(row)
|
||||
lineBeforeCursor = @buffer.lineForRow(row)[0...column]
|
||||
if text[0] == "\n"
|
||||
indent = @aceMode.getNextLineIndent(state, lineBeforeCursor, @editSession.tabText)
|
||||
text = text[0] + indent + text[1..]
|
||||
else if @aceMode.checkOutdent(state, lineBeforeCursor, text)
|
||||
shouldOutdent = true
|
||||
|
||||
{text, shouldOutdent}
|
||||
|
||||
autoOutdentBufferRow: (bufferRow) ->
|
||||
state = @tokenizedBuffer.stateForRow(bufferRow)
|
||||
@aceMode.autoOutdent(state, @aceAdaptor, bufferRow)
|
||||
|
||||
getLineTokens: (line, state) ->
|
||||
{tokens, state} = @aceMode.getTokenizer().getLineTokens(line, state)
|
||||
|
||||
@@ -4,70 +4,21 @@ EventEmitter = require 'event-emitter'
|
||||
Token = require 'token'
|
||||
Range = require 'range'
|
||||
Point = require 'point'
|
||||
AceAdaptor = require 'ace-adaptor'
|
||||
|
||||
module.exports =
|
||||
class TokenizedBuffer
|
||||
@idCounter: 1
|
||||
|
||||
languageMode: null
|
||||
buffer: null
|
||||
aceMode: null
|
||||
aceAdaptor: null
|
||||
screenLines: null
|
||||
|
||||
constructor: (@buffer, @tabText) ->
|
||||
constructor: (@buffer, { @languageMode, @tabText }) ->
|
||||
@languageMode.tokenizedBuffer = this
|
||||
@id = @constructor.idCounter++
|
||||
@aceMode = @requireAceMode()
|
||||
@screenLines = @buildScreenLinesForRows('start', 0, @buffer.getLastRow())
|
||||
@buffer.on "change.tokenized-buffer#{@id}", (e) => @handleBufferChange(e)
|
||||
@aceAdaptor = new AceAdaptor(this)
|
||||
|
||||
requireAceMode: ->
|
||||
modeName = switch @buffer.getExtension()
|
||||
when 'js' then 'javascript'
|
||||
when 'coffee' then 'coffee'
|
||||
when 'rb', 'ru' then 'ruby'
|
||||
when 'c', 'h', 'cpp' then 'c_cpp'
|
||||
when 'html', 'htm' then 'html'
|
||||
when 'css' then 'css'
|
||||
when 'java' then 'java'
|
||||
when 'xml' then 'xml'
|
||||
else 'text'
|
||||
new (require("ace/mode/#{modeName}").Mode)
|
||||
|
||||
toggleLineCommentsInRange: (range) ->
|
||||
range = Range.fromObject(range)
|
||||
@aceMode.toggleCommentLines(@stateForRow(range.start.row), @aceAdaptor, range.start.row, range.end.row)
|
||||
|
||||
isBufferRowFoldable: (bufferRow) ->
|
||||
@aceMode.foldingRules?.getFoldWidget(@aceAdaptor, null, bufferRow) == "start"
|
||||
|
||||
rowRangeForFoldAtBufferRow: (bufferRow) ->
|
||||
if aceRange = @aceMode.foldingRules?.getFoldWidgetRange(@aceAdaptor, null, bufferRow)
|
||||
[aceRange.start.row, aceRange.end.row]
|
||||
else
|
||||
null
|
||||
|
||||
indentationForRow: (row) ->
|
||||
state = @stateForRow(row)
|
||||
previousRowText = @buffer.lineForRow(row - 1)
|
||||
@aceMode.getNextLineIndent(state, previousRowText, @tabText)
|
||||
|
||||
autoIndentTextAfterBufferPosition: (text, bufferPosition) ->
|
||||
{ row, column} = bufferPosition
|
||||
state = @stateForRow(row)
|
||||
lineBeforeCursor = @buffer.lineForRow(row)[0...column]
|
||||
if text[0] == "\n"
|
||||
indent = @aceMode.getNextLineIndent(state, lineBeforeCursor, @tabText)
|
||||
text = text[0] + indent + text[1..]
|
||||
else if @aceMode.checkOutdent(state, lineBeforeCursor, text)
|
||||
shouldOutdent = true
|
||||
|
||||
{text, shouldOutdent}
|
||||
|
||||
autoOutdentBufferRow: (bufferRow) ->
|
||||
state = @stateForRow(bufferRow)
|
||||
@aceMode.autoOutdent(state, @aceAdaptor, bufferRow)
|
||||
|
||||
handleBufferChange: (e) ->
|
||||
oldRange = e.oldRange.copy()
|
||||
@@ -108,9 +59,8 @@ class TokenizedBuffer
|
||||
screenLine
|
||||
|
||||
buildScreenLineForRow: (state, row) ->
|
||||
tokenizer = @aceMode.getTokenizer()
|
||||
line = @buffer.lineForRow(row)
|
||||
{tokens, state} = tokenizer.getLineTokens(line, state)
|
||||
{tokens, state} = @languageMode.getLineTokens(line, state)
|
||||
tokenObjects = []
|
||||
for tokenProperties in tokens
|
||||
token = new Token(tokenProperties)
|
||||
|
||||
Reference in New Issue
Block a user