From 3516dea210d3b755d6214dfa30031e3ca88ca2bc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 27 Jul 2012 12:30:06 -0600 Subject: [PATCH] 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. --- spec/app/display-buffer-spec.coffee | 18 +++-- spec/app/language-mode-spec.coffee | 78 ++++++++++++++++++++++ spec/app/line-map-spec.coffee | 8 +-- spec/app/screen-line-spec.coffee | 8 +-- spec/app/tokenized-buffer-spec.coffee | 96 ++++----------------------- src/app/ace-adaptor.coffee | 8 +-- src/app/display-buffer.coffee | 11 +-- src/app/edit-session.coffee | 13 ++-- src/app/language-mode.coffee | 60 +++++++++++++++++ src/app/tokenized-buffer.coffee | 58 ++-------------- 10 files changed, 192 insertions(+), 166 deletions(-) create mode 100644 spec/app/language-mode-spec.coffee create mode 100644 src/app/language-mode.coffee diff --git a/spec/app/display-buffer-spec.coffee b/spec/app/display-buffer-spec.coffee index 70faf4c21..a9a92cf71 100644 --- a/spec/app/display-buffer-spec.coffee +++ b/spec/app/display-buffer-spec.coffee @@ -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", -> diff --git a/spec/app/language-mode-spec.coffee b/spec/app/language-mode-spec.coffee new file mode 100644 index 000000000..68a2beaa0 --- /dev/null +++ b/spec/app/language-mode-spec.coffee @@ -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] diff --git a/spec/app/line-map-spec.coffee b/spec/app/line-map-spec.coffee index 2891b92af..8aaf4347c 100644 --- a/spec/app/line-map-spec.coffee +++ b/spec/app/line-map-spec.coffee @@ -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", -> diff --git a/spec/app/screen-line-spec.coffee b/spec/app/screen-line-spec.coffee index 86c5e795a..a3275866f 100644 --- a/spec/app/screen-line-spec.coffee +++ b/spec/app/screen-line-spec.coffee @@ -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", -> diff --git a/spec/app/tokenized-buffer-spec.coffee b/spec/app/tokenized-buffer-spec.coffee index 7fb803825..ecdf2a128 100644 --- a/spec/app/tokenized-buffer-spec.coffee +++ b/spec/app/tokenized-buffer-spec.coffee @@ -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) diff --git a/src/app/ace-adaptor.coffee b/src/app/ace-adaptor.coffee index 200cead62..8869db8aa 100644 --- a/src/app/ace-adaptor.coffee +++ b/src/app/ace-adaptor.coffee @@ -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]) diff --git a/src/app/display-buffer.coffee b/src/app/display-buffer.coffee index 2ea45661a..29035a32b 100644 --- a/src/app/display-buffer.coffee +++ b/src/app/display-buffer.coffee @@ -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() diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 90996f294..aeb2e24c6 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -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() diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee new file mode 100644 index 000000000..077a0549f --- /dev/null +++ b/src/app/language-mode.coffee @@ -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) + diff --git a/src/app/tokenized-buffer.coffee b/src/app/tokenized-buffer.coffee index 15dc42a02..a028a6707 100644 --- a/src/app/tokenized-buffer.coffee +++ b/src/app/tokenized-buffer.coffee @@ -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)