Merge pull request #15977 from atom/mb-use-language-modes

Move `toggleLineComments` method from TokenizedBuffer to TextEditor
This commit is contained in:
Max Brunsfeld 2017-10-24 16:03:21 -07:00 committed by GitHub
commit 9a95372613
6 changed files with 420 additions and 403 deletions

View File

@ -4376,108 +4376,6 @@ describe "TextEditor", ->
expect(editor.lineTextForBufferRow(4)).toBe " }"
expect(editor.lineTextForBufferRow(5)).toBe " i=1"
describe ".toggleLineCommentsInSelection()", ->
it "toggles comments on the selected lines", ->
editor.setSelectedBufferRange([[4, 5], [7, 5]])
editor.toggleLineCommentsInSelection()
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 " // }"
expect(editor.getSelectedBufferRange()).toEqual [[4, 8], [7, 8]]
editor.toggleLineCommentsInSelection()
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 " }"
it "does not comment the last line of a non-empty selection if it ends at column 0", ->
editor.setSelectedBufferRange([[4, 5], [7, 0]])
editor.toggleLineCommentsInSelection()
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 " }"
it "uncomments lines if all lines match the comment regex", ->
editor.setSelectedBufferRange([[0, 0], [0, 1]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {"
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "// // var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe "// var sort = function(items) {"
expect(buffer.lineForRow(2)).toBe "// if (items.length <= 1) return items;"
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe " var sort = function(items) {"
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;"
editor.setSelectedBufferRange([[0, 0], [0, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
it "uncomments commented lines separated by an empty line", ->
editor.setSelectedBufferRange([[0, 0], [1, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe "// var sort = function(items) {"
buffer.insert([0, Infinity], '\n')
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe ""
expect(buffer.lineForRow(2)).toBe " var sort = function(items) {"
it "preserves selection emptiness", ->
editor.setCursorBufferPosition([4, 0])
editor.toggleLineCommentsInSelection()
expect(editor.getLastSelection().isEmpty()).toBeTruthy()
it "does not explode if the current language mode has no comment regex", ->
editor = new TextEditor(buffer: new TextBuffer(text: 'hello'))
editor.setSelectedBufferRange([[0, 0], [0, 5]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(0)).toBe "hello"
it "does nothing for empty lines and null grammar", ->
runs ->
editor.setGrammar(atom.grammars.grammarForScopeName('text.plain.null-grammar'))
editor.setCursorBufferPosition([10, 0])
editor.toggleLineCommentsInSelection()
expect(editor.buffer.lineForRow(10)).toBe ""
it "uncomments when the line lacks the trailing whitespace in the comment regex", ->
editor.setCursorBufferPosition([10, 0])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(10)).toBe "// "
expect(editor.getSelectedBufferRange()).toEqual [[10, 3], [10, 3]]
editor.backspace()
expect(buffer.lineForRow(10)).toBe "//"
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(10)).toBe ""
expect(editor.getSelectedBufferRange()).toEqual [[10, 0], [10, 0]]
it "uncomments when the line has leading whitespace", ->
editor.setCursorBufferPosition([10, 0])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(10)).toBe "// "
editor.moveToBeginningOfLine()
editor.insertText(" ")
editor.setSelectedBufferRange([[10, 0], [10, 0]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(10)).toBe " "
describe ".undo() and .redo()", ->
it "undoes/redoes the last change", ->
editor.insertText("foo")

View File

@ -2,6 +2,8 @@ const fs = require('fs')
const temp = require('temp').track()
const {Point, Range} = require('text-buffer')
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
const TextBuffer = require('text-buffer')
const TextEditor = require('../src/text-editor')
describe('TextEditor', () => {
let editor
@ -58,6 +60,276 @@ describe('TextEditor', () => {
})
})
describe('.toggleLineCommentsInSelection()', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript')
editor = await atom.workspace.open('sample.js')
})
it('toggles comments on the selected lines', () => {
editor.setSelectedBufferRange([[4, 5], [7, 5]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {')
expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();')
expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);')
expect(editor.lineTextForBufferRow(7)).toBe(' // }')
expect(editor.getSelectedBufferRange()).toEqual([[4, 8], [7, 8]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {')
expect(editor.lineTextForBufferRow(5)).toBe(' current = items.shift();')
expect(editor.lineTextForBufferRow(6)).toBe(' current < pivot ? left.push(current) : right.push(current);')
expect(editor.lineTextForBufferRow(7)).toBe(' }')
})
it('does not comment the last line of a non-empty selection if it ends at column 0', () => {
editor.setSelectedBufferRange([[4, 5], [7, 0]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {')
expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();')
expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);')
expect(editor.lineTextForBufferRow(7)).toBe(' }')
})
it('uncomments lines if all lines match the comment regex', () => {
editor.setSelectedBufferRange([[0, 0], [0, 1]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {')
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(0)).toBe('// // var quicksort = function () {')
expect(editor.lineTextForBufferRow(1)).toBe('// var sort = function(items) {')
expect(editor.lineTextForBufferRow(2)).toBe('// if (items.length <= 1) return items;')
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {')
expect(editor.lineTextForBufferRow(1)).toBe(' var sort = function(items) {')
expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;')
editor.setSelectedBufferRange([[0, 0], [0, Infinity]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () {')
})
it('uncomments commented lines separated by an empty line', () => {
editor.setSelectedBufferRange([[0, 0], [1, Infinity]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {')
expect(editor.lineTextForBufferRow(1)).toBe('// var sort = function(items) {')
editor.getBuffer().insert([0, Infinity], '\n')
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () {')
expect(editor.lineTextForBufferRow(1)).toBe('')
expect(editor.lineTextForBufferRow(2)).toBe(' var sort = function(items) {')
})
it('preserves selection emptiness', () => {
editor.setCursorBufferPosition([4, 0])
editor.toggleLineCommentsInSelection()
expect(editor.getLastSelection().isEmpty()).toBeTruthy()
})
it('does not explode if the current language mode has no comment regex', () => {
const editor = new TextEditor({buffer: new TextBuffer({text: 'hello'})})
editor.setSelectedBufferRange([[0, 0], [0, 5]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(0)).toBe('hello')
})
it('does nothing for empty lines and null grammar', () => {
editor.setGrammar(atom.grammars.grammarForScopeName('text.plain.null-grammar'))
editor.setCursorBufferPosition([10, 0])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(10)).toBe('')
})
it('uncomments when the line lacks the trailing whitespace in the comment regex', () => {
editor.setCursorBufferPosition([10, 0])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(10)).toBe('// ')
expect(editor.getSelectedBufferRange()).toEqual([[10, 3], [10, 3]])
editor.backspace()
expect(editor.lineTextForBufferRow(10)).toBe('//')
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(10)).toBe('')
expect(editor.getSelectedBufferRange()).toEqual([[10, 0], [10, 0]])
})
it('uncomments when the line has leading whitespace', () => {
editor.setCursorBufferPosition([10, 0])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(10)).toBe('// ')
editor.moveToBeginningOfLine()
editor.insertText(' ')
editor.setSelectedBufferRange([[10, 0], [10, 0]])
editor.toggleLineCommentsInSelection()
expect(editor.lineTextForBufferRow(10)).toBe(' ')
})
})
describe('.toggleLineCommentsForBufferRows', () => {
describe('xml', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-xml')
editor = await atom.workspace.open('test.xml')
editor.setText('<!-- test -->')
})
it('removes the leading whitespace from the comment end pattern match when uncommenting lines', () => {
editor.toggleLineCommentsForBufferRows(0, 0)
expect(editor.lineTextForBufferRow(0)).toBe('test')
})
})
describe('less', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-less')
await atom.packages.activatePackage('language-css')
editor = await atom.workspace.open('sample.less')
})
it('only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart` when commenting lines', () => {
editor.toggleLineCommentsForBufferRows(0, 0)
expect(editor.lineTextForBufferRow(0)).toBe('// @color: #4D926F;')
})
})
describe('css', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-css')
editor = await atom.workspace.open('css.css')
})
it('comments/uncomments lines in the given range', () => {
editor.toggleLineCommentsForBufferRows(0, 1)
expect(editor.lineTextForBufferRow(0)).toBe('/* body {')
expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px; */')
expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%;')
expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;')
editor.toggleLineCommentsForBufferRows(2, 2)
expect(editor.lineTextForBufferRow(0)).toBe('/* body {')
expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px; */')
expect(editor.lineTextForBufferRow(2)).toBe(' /* width: 110%; */')
expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;')
editor.toggleLineCommentsForBufferRows(0, 1)
expect(editor.lineTextForBufferRow(0)).toBe('body {')
expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px;')
expect(editor.lineTextForBufferRow(2)).toBe(' /* width: 110%; */')
expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;')
})
it('uncomments lines with leading whitespace', () => {
editor.setTextInBufferRange([[2, 0], [2, Infinity]], ' /* width: 110%; */')
editor.toggleLineCommentsForBufferRows(2, 2)
expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%;')
})
it('uncomments lines with trailing whitespace', () => {
editor.setTextInBufferRange([[2, 0], [2, Infinity]], '/* width: 110%; */ ')
editor.toggleLineCommentsForBufferRows(2, 2)
expect(editor.lineTextForBufferRow(2)).toBe('width: 110%; ')
})
it('uncomments lines with leading and trailing whitespace', () => {
editor.setTextInBufferRange([[2, 0], [2, Infinity]], ' /* width: 110%; */ ')
editor.toggleLineCommentsForBufferRows(2, 2)
expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%; ')
})
})
describe('coffeescript', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-coffee-script')
editor = await atom.workspace.open('coffee.coffee')
})
it('comments/uncomments lines in the given range', () => {
editor.toggleLineCommentsForBufferRows(4, 6)
expect(editor.lineTextForBufferRow(4)).toBe(' # pivot = items.shift()')
expect(editor.lineTextForBufferRow(5)).toBe(' # left = []')
expect(editor.lineTextForBufferRow(6)).toBe(' # right = []')
editor.toggleLineCommentsForBufferRows(4, 5)
expect(editor.lineTextForBufferRow(4)).toBe(' pivot = items.shift()')
expect(editor.lineTextForBufferRow(5)).toBe(' left = []')
expect(editor.lineTextForBufferRow(6)).toBe(' # right = []')
})
it('comments/uncomments empty lines', () => {
editor.toggleLineCommentsForBufferRows(4, 7)
expect(editor.lineTextForBufferRow(4)).toBe(' # pivot = items.shift()')
expect(editor.lineTextForBufferRow(5)).toBe(' # left = []')
expect(editor.lineTextForBufferRow(6)).toBe(' # right = []')
expect(editor.lineTextForBufferRow(7)).toBe(' # ')
editor.toggleLineCommentsForBufferRows(4, 5)
expect(editor.lineTextForBufferRow(4)).toBe(' pivot = items.shift()')
expect(editor.lineTextForBufferRow(5)).toBe(' left = []')
expect(editor.lineTextForBufferRow(6)).toBe(' # right = []')
expect(editor.lineTextForBufferRow(7)).toBe(' # ')
})
})
describe('javascript', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript')
editor = await atom.workspace.open('sample.js')
})
it('comments/uncomments lines in the given range', () => {
editor.toggleLineCommentsForBufferRows(4, 7)
expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {')
expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();')
expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);')
expect(editor.lineTextForBufferRow(7)).toBe(' // }')
editor.toggleLineCommentsForBufferRows(4, 5)
expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {')
expect(editor.lineTextForBufferRow(5)).toBe(' current = items.shift();')
expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);')
expect(editor.lineTextForBufferRow(7)).toBe(' // }')
editor.setText('\tvar i;')
editor.toggleLineCommentsForBufferRows(0, 0)
expect(editor.lineTextForBufferRow(0)).toBe('\t// var i;')
editor.setText('var i;')
editor.toggleLineCommentsForBufferRows(0, 0)
expect(editor.lineTextForBufferRow(0)).toBe('// var i;')
editor.setText(' var i;')
editor.toggleLineCommentsForBufferRows(0, 0)
expect(editor.lineTextForBufferRow(0)).toBe(' // var i;')
editor.setText(' ')
editor.toggleLineCommentsForBufferRows(0, 0)
expect(editor.lineTextForBufferRow(0)).toBe(' // ')
editor.setText(' a\n \n b')
editor.toggleLineCommentsForBufferRows(0, 2)
expect(editor.lineTextForBufferRow(0)).toBe(' // a')
expect(editor.lineTextForBufferRow(1)).toBe(' // ')
expect(editor.lineTextForBufferRow(2)).toBe(' // b')
editor.setText(' \n // var i;')
editor.toggleLineCommentsForBufferRows(0, 1)
expect(editor.lineTextForBufferRow(0)).toBe(' ')
expect(editor.lineTextForBufferRow(1)).toBe(' var i;')
})
})
})
describe('folding', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript')

View File

@ -643,186 +643,6 @@ describe('TokenizedBuffer', () => {
})
})
describe('.toggleLineCommentsForBufferRows', () => {
describe('xml', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-xml')
buffer = new TextBuffer('<!-- test -->')
tokenizedBuffer = new TokenizedBuffer({
buffer,
grammar: atom.grammars.grammarForScopeName('text.xml'),
scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config)
})
})
it('removes the leading whitespace from the comment end pattern match when uncommenting lines', () => {
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0)
expect(buffer.lineForRow(0)).toBe('test')
})
})
describe('less', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-less')
await atom.packages.activatePackage('language-css')
buffer = await TextBuffer.load(require.resolve('./fixtures/sample.less'))
tokenizedBuffer = new TokenizedBuffer({
buffer,
grammar: atom.grammars.grammarForScopeName('source.css.less'),
scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config)
})
})
it('only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart` when commenting lines', () => {
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0)
expect(buffer.lineForRow(0)).toBe('// @color: #4D926F;')
})
})
describe('css', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-css')
buffer = await TextBuffer.load(require.resolve('./fixtures/css.css'))
tokenizedBuffer = new TokenizedBuffer({
buffer,
grammar: atom.grammars.grammarForScopeName('source.css'),
scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config)
})
})
it('comments/uncomments lines in the given range', () => {
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 1)
expect(buffer.lineForRow(0)).toBe('/*body {')
expect(buffer.lineForRow(1)).toBe(' font-size: 1234px;*/')
expect(buffer.lineForRow(2)).toBe(' width: 110%;')
expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;')
tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2)
expect(buffer.lineForRow(0)).toBe('/*body {')
expect(buffer.lineForRow(1)).toBe(' font-size: 1234px;*/')
expect(buffer.lineForRow(2)).toBe(' /*width: 110%;*/')
expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;')
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 1)
expect(buffer.lineForRow(0)).toBe('body {')
expect(buffer.lineForRow(1)).toBe(' font-size: 1234px;')
expect(buffer.lineForRow(2)).toBe(' /*width: 110%;*/')
expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;')
})
it('uncomments lines with leading whitespace', () => {
buffer.setTextInRange([[2, 0], [2, Infinity]], ' /*width: 110%;*/')
tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2)
expect(buffer.lineForRow(2)).toBe(' width: 110%;')
})
it('uncomments lines with trailing whitespace', () => {
buffer.setTextInRange([[2, 0], [2, Infinity]], '/*width: 110%;*/ ')
tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2)
expect(buffer.lineForRow(2)).toBe('width: 110%; ')
})
it('uncomments lines with leading and trailing whitespace', () => {
buffer.setTextInRange([[2, 0], [2, Infinity]], ' /*width: 110%;*/ ')
tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2)
expect(buffer.lineForRow(2)).toBe(' width: 110%; ')
})
})
describe('coffeescript', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-coffee-script')
buffer = await TextBuffer.load(require.resolve('./fixtures/coffee.coffee'))
tokenizedBuffer = new TokenizedBuffer({
buffer,
tabLength: 2,
grammar: atom.grammars.grammarForScopeName('source.coffee'),
scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config)
})
})
it('comments/uncomments lines in the given range', () => {
tokenizedBuffer.toggleLineCommentsForBufferRows(4, 6)
expect(buffer.lineForRow(4)).toBe(' # pivot = items.shift()')
expect(buffer.lineForRow(5)).toBe(' # left = []')
expect(buffer.lineForRow(6)).toBe(' # right = []')
tokenizedBuffer.toggleLineCommentsForBufferRows(4, 5)
expect(buffer.lineForRow(4)).toBe(' pivot = items.shift()')
expect(buffer.lineForRow(5)).toBe(' left = []')
expect(buffer.lineForRow(6)).toBe(' # right = []')
})
it('comments/uncomments empty lines', () => {
tokenizedBuffer.toggleLineCommentsForBufferRows(4, 7)
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.toggleLineCommentsForBufferRows(4, 5)
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('javascript', () => {
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript')
buffer = await TextBuffer.load(require.resolve('./fixtures/sample.js'))
tokenizedBuffer = new TokenizedBuffer({
buffer,
tabLength: 2,
grammar: atom.grammars.grammarForScopeName('source.js'),
scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config)
})
})
it('comments/uncomments lines in the given range', () => {
tokenizedBuffer.toggleLineCommentsForBufferRows(4, 7)
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.toggleLineCommentsForBufferRows(4, 5)
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(' // }')
buffer.setText('\tvar i;')
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0)
expect(buffer.lineForRow(0)).toBe('\t// var i;')
buffer.setText('var i;')
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0)
expect(buffer.lineForRow(0)).toBe('// var i;')
buffer.setText(' var i;')
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0)
expect(buffer.lineForRow(0)).toBe(' // var i;')
buffer.setText(' ')
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0)
expect(buffer.lineForRow(0)).toBe(' // ')
buffer.setText(' a\n \n b')
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 2)
expect(buffer.lineForRow(0)).toBe(' // a')
expect(buffer.lineForRow(1)).toBe(' // ')
expect(buffer.lineForRow(2)).toBe(' // b')
buffer.setText(' \n // var i;')
tokenizedBuffer.toggleLineCommentsForBufferRows(0, 1)
expect(buffer.lineForRow(0)).toBe(' ')
expect(buffer.lineForRow(1)).toBe(' var i;')
})
})
})
describe('.isFoldableAtRow(row)', () => {
beforeEach(() => {
buffer = atom.project.bufferForPathSync('sample.js')

139
src/text-editor-utils.js Normal file
View File

@ -0,0 +1,139 @@
// This file is temporary. We should gradually convert methods in `text-editor.coffee`
// from CoffeeScript to JavaScript and move them here, so that we can eventually convert
// the entire class to JavaScript.
const {Point, Range} = require('text-buffer')
const NON_WHITESPACE_REGEX = /\S/
module.exports = {
toggleLineCommentsForBufferRows (start, end) {
let {
commentStartString,
commentEndString
} = this.tokenizedBuffer.commentStringsForPosition(Point(start, 0))
if (!commentStartString) return
commentStartString = commentStartString.trim()
if (commentEndString) {
commentEndString = commentEndString.trim()
const startDelimiterColumnRange = columnRangeForStartDelimiter(
this.buffer.lineForRow(start),
commentStartString
)
if (startDelimiterColumnRange) {
const endDelimiterColumnRange = columnRangeForEndDelimiter(
this.buffer.lineForRow(end),
commentEndString
)
if (endDelimiterColumnRange) {
this.buffer.transact(() => {
this.buffer.delete([[end, endDelimiterColumnRange[0]], [end, endDelimiterColumnRange[1]]])
this.buffer.delete([[start, startDelimiterColumnRange[0]], [start, startDelimiterColumnRange[1]]])
})
}
} else {
this.buffer.transact(() => {
const indentLength = this.buffer.lineForRow(start).match(/^\s*/)[0].length
this.buffer.insert([start, indentLength], commentStartString + ' ')
this.buffer.insert([end, this.buffer.lineLengthForRow(end)], ' ' + commentEndString)
})
}
} else {
let hasCommentedLines = false
let hasUncommentedLines = false
for (let row = start; row <= end; row++) {
const line = this.buffer.lineForRow(row)
if (NON_WHITESPACE_REGEX.test(line)) {
if (columnRangeForStartDelimiter(line, commentStartString)) {
hasCommentedLines = true
} else {
hasUncommentedLines = true
}
}
}
const shouldUncomment = hasCommentedLines && !hasUncommentedLines
if (shouldUncomment) {
for (let row = start; row <= end; row++) {
const columnRange = columnRangeForStartDelimiter(
this.buffer.lineForRow(row),
commentStartString
)
if (columnRange) this.buffer.delete([[row, columnRange[0]], [row, columnRange[1]]])
}
} else {
let minIndentLevel = Infinity
let minBlankIndentLevel = Infinity
for (let row = start; row <= end; row++) {
const line = this.buffer.lineForRow(row)
const indentLevel = this.indentLevelForLine(line)
if (NON_WHITESPACE_REGEX.test(line)) {
if (indentLevel < minIndentLevel) minIndentLevel = indentLevel
} else {
if (indentLevel < minBlankIndentLevel) minBlankIndentLevel = indentLevel
}
}
minIndentLevel = Number.isFinite(minIndentLevel)
? minIndentLevel
: Number.isFinite(minBlankIndentLevel)
? minBlankIndentLevel
: 0
const tabLength = this.getTabLength()
const indentString = ' '.repeat(tabLength * minIndentLevel)
for (let row = start; row <= end; row++) {
const line = this.buffer.lineForRow(row)
if (NON_WHITESPACE_REGEX.test(line)) {
const indentColumn = columnForIndentLevel(line, minIndentLevel, this.getTabLength())
this.buffer.insert(Point(row, indentColumn), commentStartString + ' ')
} else {
this.buffer.setTextInRange(
new Range(new Point(row, 0), new Point(row, Infinity)),
indentString + commentStartString + ' '
)
}
}
}
}
}
}
function columnForIndentLevel (line, indentLevel, tabLength) {
let column = 0
let indentLength = 0
const goalIndentLength = indentLevel * tabLength
while (indentLength < goalIndentLength) {
const char = line[column]
if (char === '\t') {
indentLength += tabLength - (indentLength % tabLength)
} else if (char === ' ') {
indentLength++
} else {
break
}
column++
}
return column
}
function columnRangeForStartDelimiter (line, delimiter) {
const startColumn = line.search(NON_WHITESPACE_REGEX)
if (startColumn === -1) return null
if (!line.startsWith(delimiter, startColumn)) return null
let endColumn = startColumn + delimiter.length
if (line[endColumn] === ' ') endColumn++
return [startColumn, endColumn]
}
function columnRangeForEndDelimiter (line, delimiter) {
let startColumn = line.lastIndexOf(delimiter)
if (startColumn === -1) return null
const endColumn = startColumn + delimiter.length
if (NON_WHITESPACE_REGEX.test(line.slice(endColumn))) return null
if (line[startColumn - 1] === ' ') startColumn--
return [startColumn, endColumn]
}

View File

@ -9,6 +9,8 @@ TokenizedBuffer = require './tokenized-buffer'
Cursor = require './cursor'
Model = require './model'
Selection = require './selection'
TextEditorUtils = require './text-editor-utils'
TextMateScopeSelector = require('first-mate').ScopeSelector
GutterContainer = require './gutter-container'
TextEditorComponent = null
@ -123,6 +125,8 @@ class TextEditor extends Model
Object.defineProperty(@prototype, 'languageMode', get: -> @tokenizedBuffer)
Object.assign(@prototype, TextEditorUtils)
@deserialize: (state, atomEnvironment) ->
# TODO: Return null on version mismatch when 1.8.0 has been out for a while
if state.version isnt @prototype.serializationVersion and state.displayBuffer?
@ -3622,9 +3626,6 @@ class TextEditor extends Model
getNonWordCharacters: (scopes) ->
@scopedSettingsDelegate?.getNonWordCharacters?(scopes) ? @nonWordCharacters
getCommentStrings: (scopes) ->
@scopedSettingsDelegate?.getCommentStrings?(scopes)
###
Section: Event Handlers
###
@ -3887,8 +3888,6 @@ class TextEditor extends Model
toggleLineCommentForBufferRow: (row) -> @toggleLineCommentsForBufferRows(row, row)
toggleLineCommentsForBufferRows: (start, end) -> @tokenizedBuffer.toggleLineCommentsForBufferRows(start, end)
rowRangeForParagraphAtBufferRow: (bufferRow) ->
return unless NON_WHITESPACE_REGEXP.test(@lineTextForBufferRow(bufferRow))

View File

@ -163,99 +163,12 @@ class TokenizedBuffer {
Section - Comments
*/
toggleLineCommentsForBufferRows (start, end) {
const scope = this.scopeDescriptorForPosition([start, 0])
const commentStrings = this.commentStringsForScopeDescriptor(scope)
if (!commentStrings) return
const {commentStartString, commentEndString} = commentStrings
if (!commentStartString) return
const commentStartRegexString = _.escapeRegExp(commentStartString).replace(/(\s+)$/, '(?:$1)?')
const commentStartRegex = new OnigRegExp(`^(\\s*)(${commentStartRegexString})`)
if (commentEndString) {
const shouldUncomment = commentStartRegex.testSync(this.buffer.lineForRow(start))
if (shouldUncomment) {
const commentEndRegexString = _.escapeRegExp(commentEndString).replace(/^(\s+)/, '(?:$1)?')
const commentEndRegex = new OnigRegExp(`(${commentEndRegexString})(\\s*)$`)
const startMatch = commentStartRegex.searchSync(this.buffer.lineForRow(start))
const endMatch = commentEndRegex.searchSync(this.buffer.lineForRow(end))
if (startMatch && endMatch) {
this.buffer.transact(() => {
const columnStart = startMatch[1].length
const columnEnd = columnStart + startMatch[2].length
this.buffer.setTextInRange([[start, columnStart], [start, columnEnd]], '')
const endLength = this.buffer.lineLengthForRow(end) - endMatch[2].length
const endColumn = endLength - endMatch[1].length
return this.buffer.setTextInRange([[end, endColumn], [end, endLength]], '')
})
}
} else {
this.buffer.transact(() => {
const indentLength = this.buffer.lineForRow(start).match(/^\s*/)[0].length
this.buffer.insert([start, indentLength], commentStartString)
this.buffer.insert([end, this.buffer.lineLengthForRow(end)], commentEndString)
})
}
commentStringsForPosition (position) {
if (this.scopedSettingsDelegate) {
const scope = this.scopeDescriptorForPosition(position)
return this.scopedSettingsDelegate.getCommentStrings(scope)
} else {
let hasCommentedLines = false
let hasUncommentedLines = false
for (let row = start; row <= end; row++) {
const line = this.buffer.lineForRow(row)
if (NON_WHITESPACE_REGEX.test(line)) {
if (commentStartRegex.testSync(line)) {
hasCommentedLines = true
} else {
hasUncommentedLines = true
}
}
}
const shouldUncomment = hasCommentedLines && !hasUncommentedLines
if (shouldUncomment) {
for (let row = start; row <= end; row++) {
const match = commentStartRegex.searchSync(this.buffer.lineForRow(row))
if (match) {
const columnStart = match[1].length
const columnEnd = columnStart + match[2].length
this.buffer.setTextInRange([[row, columnStart], [row, columnEnd]], '')
}
}
} else {
let minIndentLevel = Infinity
let minBlankIndentLevel = Infinity
for (let row = start; row <= end; row++) {
const line = this.buffer.lineForRow(row)
const indentLevel = this.indentLevelForLine(line)
if (NON_WHITESPACE_REGEX.test(line)) {
if (indentLevel < minIndentLevel) minIndentLevel = indentLevel
} else {
if (indentLevel < minBlankIndentLevel) minBlankIndentLevel = indentLevel
}
}
minIndentLevel = Number.isFinite(minIndentLevel)
? minIndentLevel
: Number.isFinite(minBlankIndentLevel)
? minBlankIndentLevel
: 0
const tabLength = this.getTabLength()
const indentString = ' '.repeat(tabLength * minIndentLevel)
for (let row = start; row <= end; row++) {
const line = this.buffer.lineForRow(row)
if (NON_WHITESPACE_REGEX.test(line)) {
const indentColumn = this.columnForIndentLevel(line, minIndentLevel)
this.buffer.insert(Point(row, indentColumn), commentStartString)
} else {
this.buffer.setTextInRange(
new Range(new Point(row, 0), new Point(row, Infinity)),
indentString + commentStartString
)
}
}
}
return {}
}
}
@ -594,24 +507,6 @@ class TokenizedBuffer {
return scopes
}
columnForIndentLevel (line, indentLevel, tabLength = this.tabLength) {
let column = 0
let indentLength = 0
const goalIndentLength = indentLevel * tabLength
while (indentLength < goalIndentLength) {
const char = line[column]
if (char === '\t') {
indentLength += tabLength - (indentLength % tabLength)
} else if (char === ' ') {
indentLength++
} else {
break
}
column++
}
return column
}
indentLevelForLine (line, tabLength = this.tabLength) {
let indentLength = 0
for (let i = 0, {length} = line; i < length; i++) {
@ -841,12 +736,6 @@ class TokenizedBuffer {
}
}
commentStringsForScopeDescriptor (scopes) {
if (this.scopedSettingsDelegate) {
return this.scopedSettingsDelegate.getCommentStrings(scopes)
}
}
regexForPattern (pattern) {
if (pattern) {
if (!this.regexesByPattern[pattern]) {