mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
@@ -145,21 +145,6 @@ describe "DisplayBuffer", ->
|
||||
expect(event.lineNumbersChanged).toBeTruthy()
|
||||
|
||||
describe "structural folding", ->
|
||||
describe "the foldable flag on screen lines", ->
|
||||
it "sets 'foldable' to true for screen lines that start a foldable region", ->
|
||||
expect(displayBuffer.lineForRow(0).foldable).toBeTruthy()
|
||||
expect(displayBuffer.lineForRow(1).foldable).toBeTruthy()
|
||||
expect(displayBuffer.lineForRow(2).foldable).toBeFalsy()
|
||||
expect(displayBuffer.lineForRow(3).foldable).toBeFalsy()
|
||||
|
||||
describe "when a foldable line is wrapped", ->
|
||||
it "only marks the first screen line as foldable", ->
|
||||
displayBuffer.setSoftWrapColumn(20)
|
||||
expect(displayBuffer.lineForRow(0).foldable).toBeTruthy()
|
||||
expect(displayBuffer.lineForRow(1).foldable).toBeFalsy()
|
||||
expect(displayBuffer.lineForRow(2).foldable).toBeTruthy()
|
||||
expect(displayBuffer.lineForRow(3).foldable).toBeFalsy()
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
displayBuffer.foldBufferRow(0)
|
||||
|
||||
@@ -110,12 +110,12 @@ describe "EditSession", ->
|
||||
lastLine = buffer.lineForRow(lastLineIndex)
|
||||
expect(lastLine.length).toBeGreaterThan(0)
|
||||
|
||||
editSession.setCursorScreenPosition(row: lastLineIndex, column: 1)
|
||||
editSession.setCursorScreenPosition(row: lastLineIndex, column: editSession.getTabLength())
|
||||
editSession.moveCursorDown()
|
||||
expect(editSession.getCursorScreenPosition()).toEqual(row: lastLineIndex, column: lastLine.length)
|
||||
|
||||
editSession.moveCursorUp()
|
||||
expect(editSession.getCursorScreenPosition().column).toBe 1
|
||||
expect(editSession.getCursorScreenPosition().column).toBe editSession.getTabLength()
|
||||
|
||||
it "retains a goal column of 0 when moving back up", ->
|
||||
lastLineIndex = buffer.getLines().length - 1
|
||||
@@ -138,9 +138,9 @@ describe "EditSession", ->
|
||||
|
||||
describe ".moveCursorLeft()", ->
|
||||
it "moves the cursor by one column to the left", ->
|
||||
editSession.setCursorScreenPosition([3, 3])
|
||||
editSession.setCursorScreenPosition([1, 8])
|
||||
editSession.moveCursorLeft()
|
||||
expect(editSession.getCursorScreenPosition()).toEqual [3, 2]
|
||||
expect(editSession.getCursorScreenPosition()).toEqual [1, 7]
|
||||
|
||||
describe "when the cursor is in the first column", ->
|
||||
describe "when there is a previous line", ->
|
||||
@@ -155,6 +155,13 @@ describe "EditSession", ->
|
||||
editSession.moveCursorLeft()
|
||||
expect(editSession.getCursorScreenPosition()).toEqual(row: 0, column: 0)
|
||||
|
||||
describe "when softTabs is enabled and the cursor is preceded by leading whitespace", ->
|
||||
it "skips tabLength worth of whitespace at a time", ->
|
||||
editSession.setCursorBufferPosition([5, 6])
|
||||
|
||||
editSession.moveCursorLeft()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [5, 4]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.setCursorScreenPosition([0, 0])
|
||||
editSession.addCursorAtScreenPosition([0, 1])
|
||||
@@ -354,14 +361,14 @@ describe "EditSession", ->
|
||||
describe ".selectToScreenPosition(screenPosition)", ->
|
||||
it "expands the last selection to the given position", ->
|
||||
editSession.setSelectedBufferRange([[3, 0], [4, 5]])
|
||||
editSession.addCursorAtScreenPosition([5, 5])
|
||||
editSession.selectToScreenPosition([6, 1])
|
||||
editSession.addCursorAtScreenPosition([5, 6])
|
||||
editSession.selectToScreenPosition([6, 2])
|
||||
|
||||
selections = editSession.getSelections()
|
||||
expect(selections.length).toBe 2
|
||||
[selection1, selection2] = selections
|
||||
expect(selection1.getScreenRange()).toEqual [[3, 0], [4, 5]]
|
||||
expect(selection2.getScreenRange()).toEqual [[5, 5], [6, 1]]
|
||||
expect(selection2.getScreenRange()).toEqual [[5, 6], [6, 2]]
|
||||
|
||||
it "merges selections if they intersect, maintaining the directionality of the last selection", ->
|
||||
editSession.setCursorScreenPosition([4, 10])
|
||||
@@ -1210,7 +1217,7 @@ describe "EditSession", ->
|
||||
describe "when autoIndent is disabled", ->
|
||||
describe "if 'softTabs' is true (the default)", ->
|
||||
it "inserts 'tabLength' spaces into the buffer", ->
|
||||
tabRegex = new RegExp("^[ ]{#{editSession.tabLength}}")
|
||||
tabRegex = new RegExp("^[ ]{#{editSession.getTabLength()}}")
|
||||
expect(buffer.lineForRow(0)).not.toMatch(tabRegex)
|
||||
editSession.indent()
|
||||
expect(buffer.lineForRow(0)).toMatch(tabRegex)
|
||||
@@ -1225,10 +1232,9 @@ describe "EditSession", ->
|
||||
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", ->
|
||||
it "moves the cursor to the end of the leading whitespace and 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.setCursorBufferPosition [5, 0]
|
||||
editSession.setAutoIndent(true)
|
||||
editSession.indent()
|
||||
expect(buffer.lineForRow(5)).toMatch /^\s+$/
|
||||
@@ -1236,22 +1242,21 @@ describe "EditSession", ->
|
||||
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", ->
|
||||
it "moves the cursor to the end of the leading whitespace and inserts enough tabs to bring the line to the suggested level of indentaion", ->
|
||||
convertToHardTabs(buffer)
|
||||
editSession.softTabs = false
|
||||
buffer.insert([5, 0], "\t\n")
|
||||
editSession.setCursorBufferPosition [5, 1]
|
||||
editSession.setCursorBufferPosition [5, 0]
|
||||
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 the line's indent level is greater than the suggested level of indentation", ->
|
||||
describe "when 'softTabs' is true (the default)", ->
|
||||
it "inserts 'tabLength' spaces into the buffer", ->
|
||||
it "moves the cursor to the end of the leading whitespace and inserts 'tabLength' spaces into the buffer", ->
|
||||
buffer.insert([7, 0], " \n")
|
||||
editSession.tabLength = 2
|
||||
editSession.setCursorBufferPosition [7, 6]
|
||||
editSession.setCursorBufferPosition [7, 2]
|
||||
editSession.setAutoIndent(true)
|
||||
editSession.indent()
|
||||
expect(buffer.lineForRow(7)).toMatch /^\s+$/
|
||||
@@ -1259,11 +1264,11 @@ describe "EditSession", ->
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [7, 8]
|
||||
|
||||
describe "when 'softTabs' is false", ->
|
||||
it "inserts \t into the buffer", ->
|
||||
it "moves the cursor to the end of the leading whitespace and inserts \t into the buffer", ->
|
||||
convertToHardTabs(buffer)
|
||||
editSession.softTabs = false
|
||||
buffer.insert([7, 0], "\t\t\t\n")
|
||||
editSession.setCursorBufferPosition [7, 3]
|
||||
editSession.setCursorBufferPosition [7, 1]
|
||||
editSession.setAutoIndent(true)
|
||||
editSession.indent()
|
||||
expect(buffer.lineForRow(7)).toMatch /^\t\t\t\t$/
|
||||
@@ -1284,12 +1289,12 @@ describe "EditSession", ->
|
||||
editSession.indent()
|
||||
expect(buffer.lineForRow(0)).toMatch(/^\t/)
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [0, 1]
|
||||
expect(editSession.getCursorScreenPosition()).toEqual [0, editSession.tabLength]
|
||||
expect(editSession.getCursorScreenPosition()).toEqual [0, editSession.getTabLength()]
|
||||
|
||||
editSession.indent()
|
||||
expect(buffer.lineForRow(0)).toMatch(/^\t\t/)
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [0, 2]
|
||||
expect(editSession.getCursorScreenPosition()).toEqual [0, editSession.tabLength * 2]
|
||||
expect(editSession.getCursorScreenPosition()).toEqual [0, editSession.getTabLength() * 2]
|
||||
|
||||
describe "pasteboard operations", ->
|
||||
pasteboard = null
|
||||
@@ -1353,17 +1358,13 @@ describe "EditSession", ->
|
||||
expect(editSession.lineForBufferRow(13)).toBe " }"
|
||||
|
||||
describe ".indentSelectedRows()", ->
|
||||
beforeEach ->
|
||||
editSession.tabLength = 2
|
||||
|
||||
describe "when nothing is selected", ->
|
||||
describe "when softTabs is enabled", ->
|
||||
it "indents line and retains selection", ->
|
||||
editSession.tabLength = 2
|
||||
editSession.setSelectedBufferRange([[0,3], [0,3]])
|
||||
editSession.indentSelectedRows()
|
||||
expect(buffer.lineForRow(0)).toBe " var quicksort = function () {"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[0, 3 + editSession.tabLength], [0, 3 + editSession.tabLength]]
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[0, 3 + editSession.getTabLength()], [0, 3 + editSession.getTabLength()]]
|
||||
|
||||
describe "when softTabs is disabled", ->
|
||||
it "indents line and retains selection", ->
|
||||
@@ -1377,11 +1378,10 @@ describe "EditSession", ->
|
||||
describe "when one line is selected", ->
|
||||
describe "when softTabs is enabled", ->
|
||||
it "indents line and retains selection", ->
|
||||
editSession.tabLength = 2
|
||||
editSession.setSelectedBufferRange([[0,4], [0,14]])
|
||||
editSession.indentSelectedRows()
|
||||
expect(buffer.lineForRow(0)).toBe "#{editSession.getTabText()}var quicksort = function () {"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[0, 4 + editSession.tabLength], [0, 14 + editSession.tabLength]]
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[0, 4 + editSession.getTabLength()], [0, 14 + editSession.getTabLength()]]
|
||||
|
||||
describe "when softTabs is disabled", ->
|
||||
it "indents line and retains selection", ->
|
||||
@@ -1395,22 +1395,20 @@ describe "EditSession", ->
|
||||
describe "when multiple lines are selected", ->
|
||||
describe "when softTabs is enabled", ->
|
||||
it "indents selected lines (that are not empty) and retains selection", ->
|
||||
editSession.tabLength = 2
|
||||
editSession.setSelectedBufferRange([[9,1], [11,15]])
|
||||
editSession.indentSelectedRows()
|
||||
expect(buffer.lineForRow(9)).toBe " };"
|
||||
expect(buffer.lineForRow(10)).toBe ""
|
||||
expect(buffer.lineForRow(11)).toBe " return sort(Array.apply(this, arguments));"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[9, 1 + editSession.tabLength], [11, 15 + editSession.tabLength]]
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[9, 1 + editSession.getTabLength()], [11, 15 + editSession.getTabLength()]]
|
||||
|
||||
it "does not indent the last row if the selection ends at column 0", ->
|
||||
editSession.tabLength = 2
|
||||
editSession.setSelectedBufferRange([[9,1], [11,0]])
|
||||
editSession.indentSelectedRows()
|
||||
expect(buffer.lineForRow(9)).toBe " };"
|
||||
expect(buffer.lineForRow(10)).toBe ""
|
||||
expect(buffer.lineForRow(11)).toBe " return sort(Array.apply(this, arguments));"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[9, 1 + editSession.tabLength], [11, 0]]
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[9, 1 + editSession.getTabLength()], [11, 0]]
|
||||
|
||||
describe "when softTabs is disabled", ->
|
||||
it "indents selected lines (that are not empty) and retains selection", ->
|
||||
@@ -1424,15 +1422,12 @@ describe "EditSession", ->
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[9, 1 + 1], [11, 15 + 1]]
|
||||
|
||||
describe ".outdentSelectedRows()", ->
|
||||
beforeEach ->
|
||||
editSession.tabLength = 2
|
||||
|
||||
describe "when nothing is selected", ->
|
||||
it "outdents line and retains selection", ->
|
||||
editSession.setSelectedBufferRange([[1,3], [1,3]])
|
||||
editSession.outdentSelectedRows()
|
||||
expect(buffer.lineForRow(1)).toBe "var sort = function(items) {"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[1, 3 - editSession.tabLength], [1, 3 - editSession.tabLength]]
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[1, 3 - editSession.getTabLength()], [1, 3 - editSession.getTabLength()]]
|
||||
|
||||
it "outdents when indent is less than a tab length", ->
|
||||
editSession.insertText(' ')
|
||||
@@ -1460,7 +1455,7 @@ describe "EditSession", ->
|
||||
editSession.setSelectedBufferRange([[1,4], [1,14]])
|
||||
editSession.outdentSelectedRows()
|
||||
expect(buffer.lineForRow(1)).toBe "var sort = function(items) {"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[1, 4 - editSession.tabLength], [1, 14 - editSession.tabLength]]
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[1, 4 - editSession.getTabLength()], [1, 14 - editSession.getTabLength()]]
|
||||
|
||||
describe "when multiple lines are selected", ->
|
||||
it "outdents selected lines and retains editSession", ->
|
||||
@@ -1470,7 +1465,7 @@ describe "EditSession", ->
|
||||
expect(buffer.lineForRow(1)).toBe "var sort = function(items) {"
|
||||
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;"
|
||||
expect(buffer.lineForRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[0, 1], [3, 15 - editSession.tabLength]]
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[0, 1], [3, 15 - editSession.getTabLength()]]
|
||||
|
||||
it "does not outdent the last line of the selection if it ends at column 0", ->
|
||||
editSession.setSelectedBufferRange([[0,1], [3,0]])
|
||||
@@ -1637,18 +1632,18 @@ describe "EditSession", ->
|
||||
|
||||
it "merges cursors when the change causes them to overlap", ->
|
||||
editSession.setCursorScreenPosition([0, 0])
|
||||
editSession.addCursorAtScreenPosition([0, 1])
|
||||
editSession.addCursorAtScreenPosition([1, 1])
|
||||
editSession.addCursorAtScreenPosition([0, 2])
|
||||
editSession.addCursorAtScreenPosition([1, 2])
|
||||
|
||||
[cursor1, cursor2, cursor3] = editSession.getCursors()
|
||||
expect(editSession.getCursors().length).toBe 3
|
||||
|
||||
buffer.delete([[0, 0], [0, 1]])
|
||||
buffer.delete([[0, 0], [0, 2]])
|
||||
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
expect(editSession.getCursors()).toEqual [cursor1, cursor3]
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor3.getBufferPosition()).toEqual [1,1]
|
||||
expect(cursor3.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
describe "folding", ->
|
||||
describe "structural folding", ->
|
||||
|
||||
@@ -486,13 +486,13 @@ describe "Editor", ->
|
||||
rootView.setFontSize(10)
|
||||
lineHeightBefore = editor.lineHeight
|
||||
charWidthBefore = editor.charWidth
|
||||
editor.setCursorScreenPosition [5, 5]
|
||||
editor.setCursorScreenPosition [5, 6]
|
||||
|
||||
rootView.setFontSize(30)
|
||||
expect(editor.css('font-size')).toBe '30px'
|
||||
expect(editor.lineHeight).toBeGreaterThan lineHeightBefore
|
||||
expect(editor.charWidth).toBeGreaterThan charWidthBefore
|
||||
expect(editor.getCursorView().position()).toEqual { top: 5 * editor.lineHeight, left: 5 * editor.charWidth }
|
||||
expect(editor.getCursorView().position()).toEqual { top: 5 * editor.lineHeight, left: 6 * editor.charWidth }
|
||||
|
||||
# ensure we clean up font size subscription
|
||||
editor.trigger('core:close')
|
||||
|
||||
@@ -77,7 +77,7 @@ describe "RootView", ->
|
||||
editor4 = editor2.splitDown()
|
||||
editor2.edit(rootView.project.buildEditSessionForPath('dir/b'))
|
||||
editor3.edit(rootView.project.buildEditSessionForPath('sample.js'))
|
||||
editor3.setCursorScreenPosition([2, 3])
|
||||
editor3.setCursorScreenPosition([2, 4])
|
||||
editor4.edit(rootView.project.buildEditSessionForPath('sample.txt'))
|
||||
editor4.setCursorScreenPosition([0, 2])
|
||||
rootView.attachToDom()
|
||||
@@ -98,7 +98,7 @@ describe "RootView", ->
|
||||
expect(editor1.getPath()).toBe require.resolve('fixtures/dir/a')
|
||||
expect(editor2.getPath()).toBe require.resolve('fixtures/dir/b')
|
||||
expect(editor3.getPath()).toBe require.resolve('fixtures/sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 3]
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe require.resolve('fixtures/sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
|
||||
@@ -35,4 +35,4 @@ describe "TextMateBundle", ->
|
||||
|
||||
it "uses plain text if no grammar can be found", ->
|
||||
filePath = require.resolve("this-is-not-a-real-file")
|
||||
expect(TextMateBundle.grammarForFilePath(filePath).name).toBe "Plain Text"
|
||||
expect(TextMateBundle.grammarForFilePath(filePath).name).toBe "Plain Text"
|
||||
|
||||
@@ -10,10 +10,10 @@ describe "TextMateGrammar", ->
|
||||
beforeEach ->
|
||||
grammar = TextMateBundle.grammarForFilePath("hello.coffee")
|
||||
|
||||
describe ".getLineTokens(line, currentRule)", ->
|
||||
describe ".tokenizeLine(line, { ruleStack, tabLength })", ->
|
||||
describe "when the entire line matches a single pattern with no capture groups", ->
|
||||
it "returns a single token with the correct scope", ->
|
||||
{tokens} = grammar.getLineTokens("return")
|
||||
{tokens} = grammar.tokenizeLine("return")
|
||||
|
||||
expect(tokens.length).toBe 1
|
||||
[token] = tokens
|
||||
@@ -21,7 +21,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the entire line matches a single pattern with capture groups", ->
|
||||
it "returns a single token with the correct scope", ->
|
||||
{tokens} = grammar.getLineTokens("new foo.bar.Baz")
|
||||
{tokens} = grammar.tokenizeLine("new foo.bar.Baz")
|
||||
|
||||
expect(tokens.length).toBe 3
|
||||
[newOperator, whitespace, className] = tokens
|
||||
@@ -32,12 +32,12 @@ describe "TextMateGrammar", ->
|
||||
describe "when the line doesn't match any patterns", ->
|
||||
it "returns the entire line as a single simple token with the grammar's scope", ->
|
||||
textGrammar = TextMateBundle.grammarForFilePath('foo.txt')
|
||||
{tokens} = textGrammar.getLineTokens("abc def")
|
||||
{tokens} = textGrammar.tokenizeLine("abc def")
|
||||
expect(tokens.length).toBe 1
|
||||
|
||||
describe "when the line matches multiple patterns", ->
|
||||
it "returns multiple tokens, filling in regions that don't match patterns with tokens in the grammar's global scope", ->
|
||||
{tokens} = grammar.getLineTokens(" return new foo.bar.Baz ")
|
||||
{tokens} = grammar.tokenizeLine(" return new foo.bar.Baz ")
|
||||
|
||||
expect(tokens.length).toBe 7
|
||||
|
||||
@@ -51,7 +51,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line matches a pattern with optional capture groups", ->
|
||||
it "only returns tokens for capture groups that matched", ->
|
||||
{tokens} = grammar.getLineTokens("class Quicksort")
|
||||
{tokens} = grammar.tokenizeLine("class Quicksort")
|
||||
expect(tokens.length).toBe 3
|
||||
expect(tokens[0].value).toBe "class"
|
||||
expect(tokens[1].value).toBe " "
|
||||
@@ -59,7 +59,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line matches a rule with nested capture groups and lookahead capture groups beyond the scope of the overall match", ->
|
||||
it "creates distinct tokens for nested captures and does not return tokens beyond the scope of the overall capture", ->
|
||||
{tokens} = grammar.getLineTokens(" destroy: ->")
|
||||
{tokens} = grammar.tokenizeLine(" destroy: ->")
|
||||
expect(tokens.length).toBe 6
|
||||
expect(tokens[0]).toEqual(value: ' ', scopes: ["source.coffee", "meta.function.coffee"])
|
||||
expect(tokens[1]).toEqual(value: 'destro', scopes: ["source.coffee", "meta.function.coffee", "entity.name.function.coffee"])
|
||||
@@ -71,13 +71,13 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line matches a pattern that includes a rule", ->
|
||||
it "returns tokens based on the included rule", ->
|
||||
{tokens} = grammar.getLineTokens("7777777")
|
||||
{tokens} = grammar.tokenizeLine("7777777")
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0]).toEqual value: '7777777', scopes: ['source.coffee', 'constant.numeric.coffee']
|
||||
|
||||
describe "when the line is an interpolated string", ->
|
||||
it "returns the correct tokens", ->
|
||||
{tokens} = grammar.getLineTokens('"the value is #{@x} my friend"')
|
||||
{tokens} = grammar.tokenizeLine('"the value is #{@x} my friend"')
|
||||
|
||||
expect(tokens[0]).toEqual value: '"', scopes: ["source.coffee","string.quoted.double.coffee","punctuation.definition.string.begin.coffee"]
|
||||
expect(tokens[1]).toEqual value: "the value is ", scopes: ["source.coffee","string.quoted.double.coffee"]
|
||||
@@ -89,7 +89,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line has an interpolated string inside an interpolated string", ->
|
||||
it "returns the correct tokens", ->
|
||||
{tokens} = grammar.getLineTokens('"#{"#{@x}"}"')
|
||||
{tokens} = grammar.tokenizeLine('"#{"#{@x}"}"')
|
||||
|
||||
expect(tokens[0]).toEqual value: '"', scopes: ["source.coffee","string.quoted.double.coffee","punctuation.definition.string.begin.coffee"]
|
||||
expect(tokens[1]).toEqual value: '#{', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","punctuation.section.embedded.coffee"]
|
||||
@@ -103,26 +103,26 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line is empty", ->
|
||||
it "returns a single token which has the global scope", ->
|
||||
{tokens} = grammar.getLineTokens('')
|
||||
{tokens} = grammar.tokenizeLine('')
|
||||
expect(tokens[0]).toEqual value: '', scopes: ["source.coffee"]
|
||||
|
||||
describe "when the line matches no patterns", ->
|
||||
it "does not infinitely loop", ->
|
||||
grammar = TextMateBundle.grammarForFilePath("sample.txt")
|
||||
{tokens} = grammar.getLineTokens('hoo')
|
||||
{tokens} = grammar.tokenizeLine('hoo')
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0]).toEqual value: 'hoo', scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
describe "when the line matches a pattern with a 'contentName'", ->
|
||||
it "creates tokens using the content of contentName as the token name", ->
|
||||
grammar = TextMateBundle.grammarForFilePath("sample.txt")
|
||||
{tokens} = grammar.getLineTokens('ok, cool')
|
||||
{tokens} = grammar.tokenizeLine('ok, cool')
|
||||
expect(tokens[0]).toEqual value: 'ok, cool', scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
describe "when the line matches a pattern with no `name` or `contentName`", ->
|
||||
it "creates tokens without adding a new scope", ->
|
||||
grammar = TextMateBundle.grammarsByFileType["rb"]
|
||||
{tokens} = grammar.getLineTokens('%w|oh \\look|')
|
||||
{tokens} = grammar.tokenizeLine('%w|oh \\look|')
|
||||
expect(tokens.length).toBe 5
|
||||
expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[1]).toEqual value: 'oh ', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby"]
|
||||
@@ -131,7 +131,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line matches a begin/end pattern", ->
|
||||
it "returns tokens based on the beginCaptures, endCaptures and the child scope", ->
|
||||
{tokens} = grammar.getLineTokens("'''single-quoted heredoc'''")
|
||||
{tokens} = grammar.tokenizeLine("'''single-quoted heredoc'''")
|
||||
|
||||
expect(tokens.length).toBe 3
|
||||
|
||||
@@ -140,9 +140,9 @@ describe "TextMateGrammar", ->
|
||||
expect(tokens[2]).toEqual value: "'''", scopes: ['source.coffee', 'string.quoted.heredoc.coffee', 'punctuation.definition.string.end.coffee']
|
||||
|
||||
describe "when the pattern spans multiple lines", ->
|
||||
it "uses the currentRule returned by the first line to parse the second line", ->
|
||||
{tokens: firstTokens, stack} = grammar.getLineTokens("'''single-quoted")
|
||||
{tokens: secondTokens, stack} = grammar.getLineTokens("heredoc'''", stack)
|
||||
it "uses the ruleStack returned by the first line to parse the second line", ->
|
||||
{tokens: firstTokens, ruleStack} = grammar.tokenizeLine("'''single-quoted")
|
||||
{tokens: secondTokens, ruleStack} = grammar.tokenizeLine("heredoc'''", {ruleStack})
|
||||
|
||||
expect(firstTokens.length).toBe 2
|
||||
expect(secondTokens.length).toBe 2
|
||||
@@ -155,7 +155,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the pattern contains sub-patterns", ->
|
||||
it "returns tokens within the begin/end scope based on the sub-patterns", ->
|
||||
{tokens} = grammar.getLineTokens('"""heredoc with character escape \\t"""')
|
||||
{tokens} = grammar.tokenizeLine('"""heredoc with character escape \\t"""')
|
||||
|
||||
expect(tokens.length).toBe 4
|
||||
|
||||
@@ -167,7 +167,7 @@ describe "TextMateGrammar", ->
|
||||
describe "when the end pattern contains a back reference", ->
|
||||
it "constructs the end rule based on its back-references to captures in the begin rule", ->
|
||||
grammar = TextMateBundle.grammarsByFileType["rb"]
|
||||
{tokens} = grammar.getLineTokens('%w|oh|,')
|
||||
{tokens} = grammar.tokenizeLine('%w|oh|,')
|
||||
expect(tokens.length).toBe 4
|
||||
expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[1]).toEqual value: 'oh', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby"]
|
||||
@@ -176,7 +176,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
it "allows the rule containing that end pattern to be pushed to the stack multiple times", ->
|
||||
grammar = TextMateBundle.grammarsByFileType["rb"]
|
||||
{tokens} = grammar.getLineTokens('%Q+matz had some #{%Q-crazy ideas-} for ruby syntax+ # damn.')
|
||||
{tokens} = grammar.tokenizeLine('%Q+matz had some #{%Q-crazy ideas-} for ruby syntax+ # damn.')
|
||||
expect(tokens[0]).toEqual value: '%Q+', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[1]).toEqual value: 'matz had some ', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby"]
|
||||
expect(tokens[2]).toEqual value: '#{', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","source.ruby.embedded.source","punctuation.section.embedded.ruby"]
|
||||
@@ -193,7 +193,7 @@ describe "TextMateGrammar", ->
|
||||
describe "when the pattern includes rules from another grammar", ->
|
||||
it "parses tokens inside the begin/end patterns based on the included grammar's rules", ->
|
||||
grammar = TextMateBundle.grammarsByFileType["html.erb"]
|
||||
{tokens} = grammar.getLineTokens("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
expect(tokens[1]).toEqual value: 'div', scopes: ["text.html.ruby","meta.tag.block.any.html","entity.name.tag.block.any.html"]
|
||||
@@ -232,9 +232,9 @@ describe "TextMateGrammar", ->
|
||||
}
|
||||
]
|
||||
|
||||
{tokens, stack} = grammar.getLineTokens("// a singleLineComment")
|
||||
expect(stack.length).toBe 1
|
||||
expect(stack[0].scopeName).toBe "source.imaginaryLanguage"
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("// a singleLineComment")
|
||||
expect(ruleStack.length).toBe 1
|
||||
expect(ruleStack[0].scopeName).toBe "source.imaginaryLanguage"
|
||||
|
||||
expect(tokens.length).toBe 2
|
||||
expect(tokens[0].value).toBe "//"
|
||||
@@ -242,5 +242,5 @@ describe "TextMateGrammar", ->
|
||||
|
||||
it "does not loop infinitley (regression)", ->
|
||||
grammar = TextMateBundle.grammarForFilePath("hello.js")
|
||||
{tokens, stack} = grammar.getLineTokens("// line comment")
|
||||
{tokens, stack} = grammar.getLineTokens(" // second line comment with a single leading space", stack)
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("// line comment")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine(" // second line comment with a single leading space", ruleStack)
|
||||
|
||||
@@ -9,7 +9,8 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
beforeEach ->
|
||||
editSession = fixturesProject.buildEditSessionForPath('sample.js', autoIndent: false)
|
||||
{ tokenizedBuffer, buffer } = editSession
|
||||
buffer = editSession.buffer
|
||||
tokenizedBuffer = editSession.displayBuffer.tokenizedBuffer
|
||||
|
||||
afterEach ->
|
||||
editSession.destroy()
|
||||
@@ -49,7 +50,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(event.newRange).toEqual new Range([0, 0], [2,0])
|
||||
|
||||
# line 2 is unchanged
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
it "updates tokens for lines beyond the changed lines if needed", ->
|
||||
buffer.insert([5, 30], '/* */')
|
||||
@@ -85,9 +86,9 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
# lines below deleted regions should be shifted upward
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[1]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[1]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -126,7 +127,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
# previous line 3 is pushed down to become line 5
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -157,16 +158,18 @@ describe "TokenizedBuffer", ->
|
||||
beforeEach ->
|
||||
tabLength = 2
|
||||
editSession2 = fixturesProject.buildEditSessionForPath('sample-with-tabs.coffee', { tabLength })
|
||||
{ buffer, tokenizedBuffer } = editSession2
|
||||
buffer = editSession2.buffer
|
||||
tokenizedBuffer = editSession2.displayBuffer.tokenizedBuffer
|
||||
|
||||
afterEach ->
|
||||
editSession2.destroy()
|
||||
|
||||
it "always renders each tab as its own atomic token with a value of size tabLength", ->
|
||||
tabAsSpaces = _.multiplyString(' ', editSession2.tabLength)
|
||||
tabAsSpaces = _.multiplyString(' ', editSession2.getTabLength())
|
||||
screenLine0 = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(screenLine0.text).toBe "# Econ 101#{tabAsSpaces}"
|
||||
{ tokens } = screenLine0
|
||||
|
||||
expect(tokens.length).toBe 3
|
||||
expect(tokens[0].value).toBe "#"
|
||||
expect(tokens[1].value).toBe " Econ 101"
|
||||
@@ -175,3 +178,17 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokens[2].isAtomic).toBeTruthy()
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
|
||||
|
||||
describe ".setTabLength(tabLength)", ->
|
||||
describe "when the file contains soft tabs", ->
|
||||
it "retokenizes leading whitespace based on the new tab length", ->
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].value).toBe " "
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].value).toBe " "
|
||||
|
||||
tokenizedBuffer.setTabLength(4)
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].value).toBe " "
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].isAtomic).toBeFalsy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].value).toBe " current "
|
||||
|
||||
@@ -111,6 +111,15 @@ class Cursor
|
||||
newPosition = [position.row, 0] if newPosition.isEqual(position)
|
||||
@setBufferPosition(newPosition)
|
||||
|
||||
skipLeadingWhitespace: ->
|
||||
position = @getBufferPosition()
|
||||
range = @editSession.bufferRangeForBufferRow(position.row)
|
||||
endOfLeadingWhitespace = null
|
||||
@editSession.scanInRange /^[ \t]*/, range, (match, matchRange) =>
|
||||
endOfLeadingWhitespace = matchRange.end
|
||||
|
||||
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
|
||||
|
||||
moveToEndOfLine: ->
|
||||
@setBufferPosition([@getBufferRow(), Infinity])
|
||||
|
||||
@@ -160,7 +169,7 @@ class Cursor
|
||||
|
||||
getIndentLevel: ->
|
||||
if @editSession.softTabs
|
||||
@getBufferColumn() / @editSession.tabLength
|
||||
@getBufferColumn() / @editSession.getTabLength()
|
||||
else
|
||||
@getBufferColumn()
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ class DisplayBuffer
|
||||
tokenizedBuffer: null
|
||||
activeFolds: null
|
||||
foldsById: null
|
||||
lastTokenizedBufferChangeEvent: null
|
||||
|
||||
constructor: (@buffer, options={}) ->
|
||||
@id = @constructor.idCounter++
|
||||
@@ -26,8 +25,7 @@ class DisplayBuffer
|
||||
@activeFolds = {}
|
||||
@foldsById = {}
|
||||
@buildLineMap()
|
||||
@tokenizedBuffer.on 'change', (e) => @lastTokenizedBufferChangeEvent = e
|
||||
@buffer.on "change.displayBuffer#{@id}", (e) => @handleBufferChange(e)
|
||||
@tokenizedBuffer.on 'change', (e) => @handleTokenizedBufferChange(e)
|
||||
|
||||
buildLineMap: ->
|
||||
@lineMap = new LineMap
|
||||
@@ -177,8 +175,14 @@ class DisplayBuffer
|
||||
bufferPositionForScreenPosition: (position, options) ->
|
||||
@lineMap.bufferPositionForScreenPosition(position, options)
|
||||
|
||||
stateForScreenRow: (screenRow) ->
|
||||
@tokenizedBuffer.stackForRow(screenRow)
|
||||
scopesForBufferPosition: (bufferPosition) ->
|
||||
@tokenizedBuffer.scopesForPosition(bufferPosition)
|
||||
|
||||
getTabLength: ->
|
||||
@tokenizedBuffer.getTabLength()
|
||||
|
||||
setTabLength: (tabLength) ->
|
||||
@tokenizedBuffer.setTabLength(tabLength)
|
||||
|
||||
clipScreenPosition: (position, options) ->
|
||||
@lineMap.clipScreenPosition(position, options)
|
||||
@@ -188,9 +192,9 @@ class DisplayBuffer
|
||||
allFolds.push(folds...) for row, folds of @activeFolds
|
||||
fold.handleBufferChange(e) for fold in allFolds
|
||||
|
||||
@handleTokenizedBufferChange(@lastTokenizedBufferChangeEvent)
|
||||
|
||||
handleTokenizedBufferChange: (e) ->
|
||||
@handleBufferChange(e.bufferChange) if e.bufferChange
|
||||
|
||||
newRange = e.newRange.copy()
|
||||
newRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(newRange.start.row))
|
||||
|
||||
@@ -218,7 +222,6 @@ class DisplayBuffer
|
||||
startBufferColumn = 0
|
||||
while currentBufferRow <= endBufferRow
|
||||
screenLine = @tokenizedBuffer.lineForScreenRow(currentBufferRow)
|
||||
screenLine.foldable = @languageMode.doesBufferRowStartFold(currentBufferRow)
|
||||
|
||||
if fold = @largestFoldStartingAtBufferRow(currentBufferRow)
|
||||
screenLine = screenLine.copy()
|
||||
@@ -272,7 +275,6 @@ class DisplayBuffer
|
||||
|
||||
destroy: ->
|
||||
@tokenizedBuffer.destroy()
|
||||
@buffer.off ".displayBuffer#{@id}"
|
||||
|
||||
logLines: (start, end) ->
|
||||
@lineMap.logLines(start, end)
|
||||
|
||||
@@ -35,16 +35,14 @@ class EditSession
|
||||
cursors: null
|
||||
selections: null
|
||||
autoIndent: false # TODO: re-enabled auto-indent after fixing the rest of tokenization
|
||||
tabLength: null
|
||||
softTabs: true
|
||||
softWrap: false
|
||||
|
||||
constructor: ({@project, @buffer, @tabLength, @autoIndent, softTabs, @softWrap }) ->
|
||||
constructor: ({@project, @buffer, tabLength, @autoIndent, softTabs, @softWrap }) ->
|
||||
@id = @constructor.idCounter++
|
||||
@softTabs = @buffer.usesSoftTabs() ? softTabs ? true
|
||||
@languageMode = new LanguageMode(this, @buffer.getExtension())
|
||||
@displayBuffer = new DisplayBuffer(@buffer, { @languageMode, @tabLength })
|
||||
@tokenizedBuffer = @displayBuffer.tokenizedBuffer
|
||||
@displayBuffer = new DisplayBuffer(@buffer, { @languageMode, tabLength })
|
||||
@anchors = []
|
||||
@anchorRanges = []
|
||||
@cursors = []
|
||||
@@ -108,7 +106,9 @@ class EditSession
|
||||
setSoftWrap: (@softWrap) ->
|
||||
|
||||
getTabText: -> @buildIndentString(1)
|
||||
getTabLength: -> @tabLength
|
||||
|
||||
getTabLength: -> @displayBuffer.getTabLength()
|
||||
setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength)
|
||||
|
||||
clipBufferPosition: (bufferPosition) ->
|
||||
@buffer.clipPosition(bufferPosition)
|
||||
@@ -126,11 +126,11 @@ class EditSession
|
||||
if line.match(/^\t/)
|
||||
line.match(/^\t*/)?[0].length
|
||||
else
|
||||
line.match(/^\s*/)?[0].length / @tabLength
|
||||
line.match(/^\s*/)?[0].length / @getTabLength()
|
||||
|
||||
buildIndentString: (number) ->
|
||||
if @softTabs
|
||||
_.multiplyString(" ", number * @tabLength)
|
||||
_.multiplyString(" ", number * @getTabLength())
|
||||
else
|
||||
_.multiplyString("\t", number)
|
||||
|
||||
@@ -152,11 +152,11 @@ class EditSession
|
||||
clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options)
|
||||
lineForScreenRow: (row) -> @displayBuffer.lineForRow(row)
|
||||
linesForScreenRows: (start, end) -> @displayBuffer.linesForRows(start, end)
|
||||
stateForScreenRow: (screenRow) -> @displayBuffer.stateForScreenRow(screenRow)
|
||||
screenLineCount: -> @displayBuffer.lineCount()
|
||||
maxScreenLineLength: -> @displayBuffer.maxLineLength()
|
||||
getLastScreenRow: -> @displayBuffer.getLastRow()
|
||||
bufferRowsForScreenRows: (startRow, endRow) -> @displayBuffer.bufferRowsForScreenRows(startRow, endRow)
|
||||
scopesForBufferPosition: (bufferPosition) -> @displayBuffer.scopesForBufferPosition(bufferPosition)
|
||||
logScreenLines: (start, end) -> @displayBuffer.logLines(start, end)
|
||||
|
||||
insertText: (text, options) ->
|
||||
|
||||
@@ -250,10 +250,9 @@ class Editor extends View
|
||||
screenRangeForBufferRange: (range) -> @activeEditSession.screenRangeForBufferRange(range)
|
||||
bufferRangeForScreenRange: (range) -> @activeEditSession.bufferRangeForScreenRange(range)
|
||||
bufferRowsForScreenRows: (startRow, endRow) -> @activeEditSession.bufferRowsForScreenRows(startRow, endRow)
|
||||
stateForScreenRow: (row) -> @activeEditSession.stateForScreenRow(row)
|
||||
|
||||
logCursorScope: ->
|
||||
console.log @activeEditSession.tokenizedBuffer.scopesForPosition(@getCursorBufferPosition())
|
||||
console.log @activeEditSession.scopesForBufferPosition(@getCursorBufferPosition())
|
||||
|
||||
pageDown: ->
|
||||
newScrollTop = @scrollTop() + @scrollView[0].clientHeight
|
||||
|
||||
@@ -48,9 +48,6 @@ class LanguageMode
|
||||
@bracketAnchorRanges.push @editSession.addAnchorRange(range)
|
||||
false
|
||||
|
||||
getTokenizedBuffer: ->
|
||||
@editSession.tokenizedBuffer
|
||||
|
||||
isQuote: (string) ->
|
||||
/'|"/.test(string)
|
||||
|
||||
@@ -69,7 +66,7 @@ class LanguageMode
|
||||
@invertedPairedCharacters
|
||||
|
||||
toggleLineCommentsForBufferRows: (start, end) ->
|
||||
scopes = @getTokenizedBuffer().scopesForPosition([start, 0])
|
||||
scopes = @editSession.scopesForBufferPosition([start, 0])
|
||||
return unless commentString = TextMateBundle.lineCommentStringForScope(scopes[0])
|
||||
|
||||
commentRegexString = _.escapeRegExp(commentString)
|
||||
@@ -96,7 +93,7 @@ class LanguageMode
|
||||
return null unless @doesBufferRowStartFold(bufferRow)
|
||||
|
||||
startIndentLevel = @editSession.indentationForBufferRow(bufferRow)
|
||||
scopes = @getTokenizedBuffer().scopesForPosition([bufferRow, 0])
|
||||
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
|
||||
for row in [(bufferRow + 1)..@editSession.getLastBufferRow()]
|
||||
continue if @editSession.isBufferRowBlank(row)
|
||||
indentation = @editSession.indentationForBufferRow(row)
|
||||
@@ -111,7 +108,7 @@ class LanguageMode
|
||||
|
||||
suggestedIndentForBufferRow: (bufferRow) ->
|
||||
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
|
||||
scopes = @getTokenizedBuffer().scopesForPosition([bufferRow, 0])
|
||||
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
|
||||
return currentIndentLevel unless increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
|
||||
|
||||
currentLine = @buffer.lineForRow(bufferRow)
|
||||
@@ -140,7 +137,7 @@ class LanguageMode
|
||||
return unless precedingRow?
|
||||
|
||||
precedingLine = @editSession.lineForBufferRow(precedingRow)
|
||||
scopes = @getTokenizedBuffer().scopesForPosition([precedingRow, Infinity])
|
||||
scopes = @editSession.scopesForBufferPosition([precedingRow, Infinity])
|
||||
increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
|
||||
return unless increaseIndentPattern
|
||||
|
||||
@@ -151,7 +148,7 @@ class LanguageMode
|
||||
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
|
||||
|
||||
autoDecreaseIndentForBufferRow: (bufferRow) ->
|
||||
scopes = @getTokenizedBuffer().scopesForPosition([bufferRow, 0])
|
||||
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
|
||||
increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
|
||||
decreaseIndentPattern = TextMateBundle.outdentRegexForScope(scopes[0])
|
||||
return unless increaseIndentPattern and decreaseIndentPattern
|
||||
@@ -168,6 +165,5 @@ class LanguageMode
|
||||
if desiredIndentLevel < currentIndentLevel
|
||||
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
|
||||
|
||||
getLineTokens: (line, stack) ->
|
||||
{tokens, stack} = @grammar.getLineTokens(line, stack)
|
||||
|
||||
tokenizeLine: (line, stack) ->
|
||||
{tokens, stack} = @grammar.tokenizeLine(line, stack)
|
||||
|
||||
@@ -2,14 +2,13 @@ _ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class ScreenLine
|
||||
constructor: ({@tokens, @stack, @bufferRows, @startBufferColumn, @fold, @foldable}) ->
|
||||
constructor: ({@tokens, @ruleStack, @bufferRows, @startBufferColumn, @fold}) ->
|
||||
@bufferRows ?= 1
|
||||
@startBufferColumn ?= 0
|
||||
@foldable ?= false
|
||||
@text = _.pluck(@tokens, 'value').join('')
|
||||
|
||||
copy: ->
|
||||
new ScreenLine({@tokens, @stack, @bufferRows, @startBufferColumn, @fold, @foldable})
|
||||
new ScreenLine({@tokens, @ruleStack, @bufferRows, @startBufferColumn, @fold})
|
||||
|
||||
clipScreenColumn: (column, options={}) ->
|
||||
{ skipAtomicTokens } = options
|
||||
@@ -73,13 +72,12 @@ class ScreenLine
|
||||
tokens: leftTokens
|
||||
bufferRows: 0
|
||||
startBufferColumn: @startBufferColumn
|
||||
stack: @stack
|
||||
foldable: @foldable
|
||||
ruleStack: @ruleStack
|
||||
)
|
||||
rightFragment = new ScreenLine(
|
||||
tokens: rightTokens
|
||||
startBufferColumn: @startBufferColumn + column
|
||||
stack: @stack
|
||||
ruleStack: @ruleStack
|
||||
)
|
||||
[leftFragment, rightFragment]
|
||||
|
||||
|
||||
@@ -187,6 +187,7 @@ class Selection
|
||||
{ row, column } = @cursor.getBufferPosition()
|
||||
|
||||
if @isEmpty()
|
||||
@cursor.skipLeadingWhitespace()
|
||||
desiredIndent = @editSession.suggestedIndentForBufferRow(row)
|
||||
delta = desiredIndent - @cursor.getIndentLevel()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
_ = require 'underscore'
|
||||
fs = require 'fs'
|
||||
plist = require 'plist'
|
||||
Token = require 'token'
|
||||
|
||||
module.exports =
|
||||
class TextMateGrammar
|
||||
@@ -27,7 +28,8 @@ class TextMateGrammar
|
||||
for name, data of repository
|
||||
@repository[name] = new Rule(this, data)
|
||||
|
||||
getLineTokens: (line, ruleStack=[@initialRule]) ->
|
||||
tokenizeLine: (line, {ruleStack, tabLength}={}) ->
|
||||
ruleStack ?= [@initialRule]
|
||||
ruleStack = new Array(ruleStack...) # clone ruleStack
|
||||
tokens = []
|
||||
position = 0
|
||||
@@ -36,28 +38,38 @@ class TextMateGrammar
|
||||
scopes = scopesFromStack(ruleStack)
|
||||
|
||||
if line.length == 0
|
||||
tokens = [{value: "", scopes: scopes}]
|
||||
return { tokens, scopes }
|
||||
tokens = [new Token(value: "", scopes: scopes)]
|
||||
return { tokens, ruleStack }
|
||||
|
||||
break if position == line.length
|
||||
|
||||
if match = _.last(ruleStack).getNextTokens(ruleStack, line, position)
|
||||
{ nextTokens, tokensStartPosition, tokensEndPosition } = match
|
||||
if position < tokensStartPosition # unmatched text before next tokens
|
||||
tokens.push
|
||||
tokens.push(new Token(
|
||||
value: line[position...tokensStartPosition]
|
||||
scopes: scopes
|
||||
))
|
||||
|
||||
tokens.push(nextTokens...)
|
||||
position = tokensEndPosition
|
||||
|
||||
else # push filler token for unmatched text at end of line
|
||||
tokens.push
|
||||
tokens.push(new Token(
|
||||
value: line[position...line.length]
|
||||
scopes: scopes
|
||||
))
|
||||
break
|
||||
|
||||
{ tokens, stack: ruleStack }
|
||||
{ tokens: @breakOutAtomicTokens(tokens, tabLength), ruleStack }
|
||||
|
||||
breakOutAtomicTokens: (inputTokens, tabLength) ->
|
||||
outputTokens = []
|
||||
breakOutLeadingWhitespace = true
|
||||
for token in inputTokens
|
||||
outputTokens.push(token.breakOutAtomicTokens(tabLength, breakOutLeadingWhitespace)...)
|
||||
breakOutLeadingWhitespace = token.isOnlyWhitespace() if breakOutLeadingWhitespace
|
||||
outputTokens
|
||||
|
||||
ruleForInclude: (name) ->
|
||||
if name[0] == "#"
|
||||
@@ -158,7 +170,6 @@ class Pattern
|
||||
getIncludedPatterns: (included) ->
|
||||
if @include
|
||||
rule = @grammar.ruleForInclude(@include)
|
||||
# console.log "Could not find rule for include #{@include} in #{@grammar.name} grammar" unless rule
|
||||
rule?.getIncludedPatterns(included) ? []
|
||||
else
|
||||
[this]
|
||||
@@ -175,7 +186,7 @@ class Pattern
|
||||
if zeroLengthMatch
|
||||
tokens = []
|
||||
else
|
||||
tokens = [{ value: line[start...end], scopes: scopes }]
|
||||
tokens = [new Token(value: line[start...end], scopes: scopes)]
|
||||
if @pushRule
|
||||
stack.push(@pushRule.getRuleToPush(line, captureIndices))
|
||||
else if @popRule
|
||||
@@ -201,18 +212,20 @@ class Pattern
|
||||
continue
|
||||
|
||||
if childCaptureStart > previousChildCaptureEnd
|
||||
tokens.push
|
||||
tokens.push(new Token(
|
||||
value: line[previousChildCaptureEnd...childCaptureStart]
|
||||
scopes: scopes
|
||||
))
|
||||
|
||||
captureTokens = @getTokensForCaptureIndices(line, captureIndices, scopes)
|
||||
tokens.push(captureTokens...)
|
||||
previousChildCaptureEnd = childCaptureEnd
|
||||
|
||||
if parentCaptureEnd > previousChildCaptureEnd
|
||||
tokens.push
|
||||
tokens.push(new Token(
|
||||
value: line[previousChildCaptureEnd...parentCaptureEnd]
|
||||
scopes: scopes
|
||||
))
|
||||
|
||||
tokens
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ class Token
|
||||
value: null
|
||||
scopes: null
|
||||
isAtomic: null
|
||||
isTab: null
|
||||
isHardTab: null
|
||||
|
||||
constructor: ({@value, @scopes, @isAtomic, @bufferDelta, @fold, @isTab}) ->
|
||||
constructor: ({@value, @scopes, @isAtomic, @bufferDelta, @isHardTab}) ->
|
||||
@screenDelta = @value.length
|
||||
@bufferDelta ?= @screenDelta
|
||||
|
||||
@@ -22,24 +22,46 @@ class Token
|
||||
value2 = @value.substring(splitIndex)
|
||||
[new Token(value: value1, scopes: @scopes), new Token(value: value2, scopes: @scopes)]
|
||||
|
||||
breakOutTabCharacters: (tabLength, showInvisibles) ->
|
||||
return [this] unless /\t/.test(@value)
|
||||
breakOutAtomicTokens: (tabLength, breakOutLeadingWhitespace) ->
|
||||
if breakOutLeadingWhitespace
|
||||
return [this] unless /^[ ]|\t/.test(@value)
|
||||
else
|
||||
return [this] unless /\t/.test(@value)
|
||||
|
||||
for substring in @value.match(/[^\t]+|\t/g)
|
||||
if substring == "\t"
|
||||
@buildTabToken(tabLength)
|
||||
outputTokens = []
|
||||
regex = new RegExp("([ ]{#{tabLength}})|(\t)|([^\t]+)", "g")
|
||||
|
||||
while match = regex.exec(@value)
|
||||
[fullMatch, softTab, hardTab] = match
|
||||
if softTab and breakOutLeadingWhitespace
|
||||
outputTokens.push(@buildSoftTabToken(tabLength, false))
|
||||
else if hardTab
|
||||
breakOutLeadingWhitespace = false
|
||||
outputTokens.push(@buildHardTabToken(tabLength, true))
|
||||
else
|
||||
new Token(value: substring, scopes: @scopes)
|
||||
breakOutLeadingWhitespace = false
|
||||
outputTokens.push(new Token(value: match[0], scopes: @scopes))
|
||||
|
||||
buildTabToken: (tabLength) ->
|
||||
outputTokens
|
||||
|
||||
buildHardTabToken: (tabLength) ->
|
||||
@buildTabToken(tabLength, true)
|
||||
|
||||
buildSoftTabToken: (tabLength) ->
|
||||
@buildTabToken(tabLength, false)
|
||||
|
||||
buildTabToken: (tabLength, isHardTab) ->
|
||||
new Token(
|
||||
value: _.multiplyString(" ", tabLength)
|
||||
scopes: @scopes
|
||||
bufferDelta: 1
|
||||
bufferDelta: if isHardTab then 1 else tabLength
|
||||
isAtomic: true
|
||||
isTab: true
|
||||
isHardTab: isHardTab
|
||||
)
|
||||
|
||||
isOnlyWhitespace: ->
|
||||
not /\S/.test(@value)
|
||||
|
||||
getValueAsHtml: ({invisibles, hasLeadingWhitespace, hasTrailingWhitespace})->
|
||||
html = @value
|
||||
.replace(/&/g, '&')
|
||||
@@ -49,7 +71,7 @@ class Token
|
||||
.replace(/>/g, '>')
|
||||
|
||||
if invisibles
|
||||
if @isTab and invisibles.tab
|
||||
if @isHardTab and invisibles.tab
|
||||
html = html.replace(/^./, "<span class='invisible'>#{invisibles.tab}</span>")
|
||||
else if invisibles.space
|
||||
if hasLeadingWhitespace
|
||||
|
||||
@@ -50,27 +50,28 @@ class TokenizedBuffer
|
||||
newRange.end.column = endColumn
|
||||
oldRange.end.column = endColumn
|
||||
|
||||
@trigger("change", {oldRange, newRange})
|
||||
@trigger "change", {oldRange, newRange, bufferChange: e}
|
||||
|
||||
getTabLength: ->
|
||||
@tabLength
|
||||
|
||||
setTabLength: (@tabLength) ->
|
||||
@screenLines = @buildScreenLinesForRows(0, @buffer.getLastRow())
|
||||
@trigger "change", {oldRange: @buffer.getRange(), newRange: @buffer.getRange()}
|
||||
|
||||
buildScreenLinesForRows: (startRow, endRow, startingStack) ->
|
||||
stack = startingStack
|
||||
ruleStack = startingStack
|
||||
for row in [startRow..endRow]
|
||||
screenLine = @buildScreenLineForRow(row, stack)
|
||||
stack = screenLine.stack
|
||||
screenLine = @buildScreenLineForRow(row, ruleStack)
|
||||
ruleStack = screenLine.ruleStack
|
||||
screenLine
|
||||
|
||||
buildScreenLineForRow: (row, stack) ->
|
||||
buildScreenLineForRow: (row, ruleStack) ->
|
||||
line = @buffer.lineForRow(row)
|
||||
{tokens, stack} = @languageMode.getLineTokens(line, stack)
|
||||
tokenObjects = []
|
||||
for tokenProperties in tokens
|
||||
token = new Token(tokenProperties)
|
||||
tokenObjects.push(token.breakOutTabCharacters(@tabLength)...)
|
||||
text = _.pluck(tokenObjects, 'value').join('')
|
||||
new ScreenLine(
|
||||
tokens: tokenObjects
|
||||
stack: stack
|
||||
)
|
||||
|
||||
val = @languageMode.tokenizeLine(line, {ruleStack, @tabLength})
|
||||
console.log val, line unless val.ruleStack
|
||||
new ScreenLine(val)
|
||||
|
||||
lineForScreenRow: (row) ->
|
||||
@screenLines[row]
|
||||
@@ -79,7 +80,7 @@ class TokenizedBuffer
|
||||
@screenLines[startRow..endRow]
|
||||
|
||||
stackForRow: (row) ->
|
||||
@screenLines[row]?.stack
|
||||
@screenLines[row]?.ruleStack
|
||||
|
||||
scopesForPosition: (position) ->
|
||||
position = Point.fromObject(position)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Like sands through the hourglass, so are the days of our lives.
|
||||
require 'atom'
|
||||
require 'window'
|
||||
window.attachRootView(window.location.params.pathToOpen)
|
||||
window.attachRootView(window.location.params.pathToOpen)
|
||||
Reference in New Issue
Block a user