mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
Merge pull request #16999 from atom/aw/no-really-read-only
Prevent default commands from modifying readonly TextEditors
This commit is contained in:
@@ -154,4 +154,118 @@ describe('Selection', () => {
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('within a read-only editor', () => {
|
||||
beforeEach(() => {
|
||||
editor.setReadOnly(true)
|
||||
selection.setBufferRange([[0, 0], [0, 13]])
|
||||
})
|
||||
|
||||
const modifications = [
|
||||
{
|
||||
name: 'insertText',
|
||||
op: opts => selection.insertText('yes', opts)
|
||||
},
|
||||
{
|
||||
name: 'backspace',
|
||||
op: opts => selection.backspace(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteToPreviousWordBoundary',
|
||||
op: opts => selection.deleteToPreviousWordBoundary(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteToNextWordBoundary',
|
||||
op: opts => selection.deleteToNextWordBoundary(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteToBeginningOfWord',
|
||||
op: opts => selection.deleteToBeginningOfWord(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteToBeginningOfLine',
|
||||
op: opts => selection.deleteToBeginningOfLine(opts)
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
op: opts => selection.delete(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteToEndOfLine',
|
||||
op: opts => selection.deleteToEndOfLine(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteToEndOfWord',
|
||||
op: opts => selection.deleteToEndOfWord(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteToBeginningOfSubword',
|
||||
op: opts => selection.deleteToBeginningOfSubword(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteToEndOfSubword',
|
||||
op: opts => selection.deleteToEndOfSubword(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteSelectedText',
|
||||
op: opts => selection.deleteSelectedText(opts)
|
||||
},
|
||||
{
|
||||
name: 'deleteLine',
|
||||
op: opts => selection.deleteLine(opts)
|
||||
},
|
||||
{
|
||||
name: 'joinLines',
|
||||
op: opts => selection.joinLines(opts)
|
||||
},
|
||||
{
|
||||
name: 'outdentSelectedRows',
|
||||
op: opts => selection.outdentSelectedRows(opts)
|
||||
},
|
||||
{
|
||||
name: 'autoIndentSelectedRows',
|
||||
op: opts => selection.autoIndentSelectedRows(opts)
|
||||
},
|
||||
{
|
||||
name: 'toggleLineComments',
|
||||
op: opts => selection.toggleLineComments(opts)
|
||||
},
|
||||
{
|
||||
name: 'cutToEndOfLine',
|
||||
op: opts => selection.cutToEndOfLine(false, opts)
|
||||
},
|
||||
{
|
||||
name: 'cutToEndOfBufferLine',
|
||||
op: opts => selection.cutToEndOfBufferLine(false, opts)
|
||||
},
|
||||
{
|
||||
name: 'cut',
|
||||
op: opts => selection.cut(false, false, opts.bypassReadOnly)
|
||||
},
|
||||
{
|
||||
name: 'indent',
|
||||
op: opts => selection.indent(opts)
|
||||
},
|
||||
{
|
||||
name: 'indentSelectedRows',
|
||||
op: opts => selection.indentSelectedRows(opts)
|
||||
},
|
||||
]
|
||||
|
||||
describe('without bypassReadOnly', () => {
|
||||
for (const {name, op} of modifications) {
|
||||
it(`throws an error on ${name}`, () => {
|
||||
expect(op).toThrow()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('with bypassReadOnly', () => {
|
||||
for (const {name, op} of modifications) {
|
||||
it(`permits ${name}`, () => {
|
||||
op({bypassReadOnly: true})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5488,6 +5488,195 @@ describe('TextEditor', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when readonly', () => {
|
||||
beforeEach(() => {
|
||||
editor.setReadOnly(true)
|
||||
})
|
||||
|
||||
const modifications = [
|
||||
{
|
||||
name: 'moveLineUp',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([1, 0])
|
||||
editor.moveLineUp(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'moveLineDown',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.moveLineDown(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'insertText',
|
||||
op: (opts = {}) => {
|
||||
editor.setSelectedBufferRange([[1, 0], [1, 2]])
|
||||
editor.insertText('xxx', opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'insertNewline',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorScreenPosition({row: 1, column: 0})
|
||||
editor.insertNewline(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'insertNewlineBelow',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([0, 2])
|
||||
editor.insertNewlineBelow(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'insertNewlineAbove',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([0])
|
||||
editor.insertNewlineAbove(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'backspace',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorScreenPosition({row: 1, column: 7})
|
||||
editor.backspace(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'deleteToPreviousWordBoundary',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([0, 16])
|
||||
editor.deleteToPreviousWordBoundary(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'deleteToNextWordBoundary',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([0, 15])
|
||||
editor.deleteToNextWordBoundary(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'deleteToBeginningOfWord',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([1, 24])
|
||||
editor.deleteToBeginningOfWord(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'deleteToEndOfLine',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([1, 24])
|
||||
editor.deleteToEndOfLine(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'deleteToBeginningOfLine',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([1, 24])
|
||||
editor.deleteToBeginningOfLine(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorScreenPosition([1, 6])
|
||||
editor.delete(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'deleteToEndOfWord',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([1, 24])
|
||||
editor.deleteToEndOfWord(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'indent',
|
||||
op: (opts = {}) => {
|
||||
editor.indent(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cutSelectedText',
|
||||
op: (opts = {}) => {
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]])
|
||||
editor.cutSelectedText(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cutToEndOfLine',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([2, 20])
|
||||
editor.cutToEndOfLine(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cutToEndOfBufferLine',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([2, 20])
|
||||
editor.cutToEndOfBufferLine(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'pasteText',
|
||||
op: (opts = {}) => {
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]])
|
||||
atom.clipboard.write('first')
|
||||
editor.pasteText(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'indentSelectedRows',
|
||||
op: (opts = {}) => {
|
||||
editor.setSelectedBufferRange([[0, 3], [0, 3]])
|
||||
editor.indentSelectedRows(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'outdentSelectedRows',
|
||||
op: (opts = {}) => {
|
||||
editor.setSelectedBufferRange([[1, 3], [1, 3]])
|
||||
editor.outdentSelectedRows(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'autoIndentSelectedRows',
|
||||
op: (opts = {}) => {
|
||||
editor.setCursorBufferPosition([2, 0])
|
||||
editor.insertText('function() {\ninside=true\n}\n i=1\n', opts)
|
||||
editor.getLastSelection().setBufferRange([[2, 0], [6, 0]])
|
||||
editor.autoIndentSelectedRows(opts)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'undo/redo',
|
||||
op: (opts = {}) => {
|
||||
editor.insertText('foo', opts)
|
||||
editor.undo(opts)
|
||||
editor.redo(opts)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
describe('without bypassReadOnly', () => {
|
||||
for (const {name, op} of modifications) {
|
||||
it(`throws an error on ${name}`, () => {
|
||||
expect(op).toThrow()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('with bypassReadOnly', () => {
|
||||
for (const {name, op} of modifications) {
|
||||
it(`permits ${name}`, () => {
|
||||
op({bypassReadOnly: true})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('reading text', () => {
|
||||
|
||||
@@ -166,15 +166,35 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage
|
||||
false
|
||||
)
|
||||
|
||||
commandRegistry.add(
|
||||
'atom-text-editor:not([readonly])',
|
||||
stopEventPropagation({
|
||||
'core:undo': -> @undo()
|
||||
'core:redo': -> @redo()
|
||||
}),
|
||||
false
|
||||
)
|
||||
|
||||
commandRegistry.add(
|
||||
'atom-text-editor',
|
||||
stopEventPropagationAndGroupUndo(
|
||||
config,
|
||||
{
|
||||
'core:copy': -> @copySelectedText()
|
||||
'editor:copy-selection': -> @copyOnlySelectedText()
|
||||
}
|
||||
),
|
||||
false
|
||||
)
|
||||
|
||||
commandRegistry.add(
|
||||
'atom-text-editor:not([readonly])',
|
||||
stopEventPropagationAndGroupUndo(
|
||||
config,
|
||||
{
|
||||
'core:backspace': -> @backspace()
|
||||
'core:delete': -> @delete()
|
||||
'core:cut': -> @cutSelectedText()
|
||||
'core:copy': -> @copySelectedText()
|
||||
'core:paste': -> @pasteText()
|
||||
'editor:paste-without-reformatting': -> @pasteText({
|
||||
normalizeLineEndings: false,
|
||||
@@ -195,7 +215,6 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage
|
||||
'editor:transpose': -> @transpose()
|
||||
'editor:upper-case': -> @upperCase()
|
||||
'editor:lower-case': -> @lowerCase()
|
||||
'editor:copy-selection': -> @copyOnlySelectedText()
|
||||
}
|
||||
),
|
||||
false
|
||||
@@ -266,7 +285,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage
|
||||
)
|
||||
|
||||
commandRegistry.add(
|
||||
'atom-text-editor:not([mini])',
|
||||
'atom-text-editor:not([mini]):not([readonly])',
|
||||
stopEventPropagationAndGroupUndo(
|
||||
config,
|
||||
{
|
||||
|
||||
186
src/selection.js
186
src/selection.js
@@ -407,6 +407,25 @@ class Selection {
|
||||
if (autoscroll) this.cursor.autoscroll()
|
||||
}
|
||||
|
||||
// Private: Ensure that the {TextEditor} is not marked read-only before allowing a buffer modification to occur. if
|
||||
// the editor is read-only, require an explicit opt-in option to proceed (`bypassReadOnly`) or throw an Error.
|
||||
ensureWritable (methodName, opts) {
|
||||
if (!opts.bypassReadOnly && this.editor.isReadOnly()) {
|
||||
if (atom.inDevMode() || atom.inSpecMode()) {
|
||||
const e = new Error('Attempt to mutate a read-only TextEditor through a Selection')
|
||||
e.detail =
|
||||
`Your package is attempting to call ${methodName} on a selection within an editor that has been marked ` +
|
||||
' read-only. Pass {bypassReadOnly: true} to modify it anyway, or test editors with .isReadOnly() before ' +
|
||||
' attempting modifications.'
|
||||
throw e
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
Section: Modifying the selected text
|
||||
*/
|
||||
@@ -428,7 +447,10 @@ class Selection {
|
||||
// level between the first lines and the trailing lines.
|
||||
// * `normalizeLineEndings` (optional) {Boolean} (default: true)
|
||||
// * `undo` If `skip`, skips the undo stack for this operation.
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
insertText (text, options = {}) {
|
||||
if (!this.ensureWritable('insertText', options)) return
|
||||
|
||||
let desiredIndentLevel, indentAdjustment
|
||||
const oldBufferRange = this.getBufferRange()
|
||||
const wasReversed = this.isReversed()
|
||||
@@ -492,90 +514,134 @@ class Selection {
|
||||
|
||||
// Public: Removes the first character before the selection if the selection
|
||||
// is empty otherwise it deletes the selection.
|
||||
backspace () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
backspace (options = {}) {
|
||||
if (!this.ensureWritable('backspace', options)) return
|
||||
if (this.isEmpty()) this.selectLeft()
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes the selection or, if nothing is selected, then all
|
||||
// characters from the start of the selection back to the previous word
|
||||
// boundary.
|
||||
deleteToPreviousWordBoundary () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteToPreviousWordBoundary (options = {}) {
|
||||
if (!this.ensureWritable('deleteToPreviousWordBoundary', options)) return
|
||||
if (this.isEmpty()) this.selectToPreviousWordBoundary()
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes the selection or, if nothing is selected, then all
|
||||
// characters from the start of the selection up to the next word
|
||||
// boundary.
|
||||
deleteToNextWordBoundary () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteToNextWordBoundary (options = {}) {
|
||||
if (!this.ensureWritable('deleteToNextWordBoundary', options)) return
|
||||
if (this.isEmpty()) this.selectToNextWordBoundary()
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes from the start of the selection to the beginning of the
|
||||
// current word if the selection is empty otherwise it deletes the selection.
|
||||
deleteToBeginningOfWord () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteToBeginningOfWord (options = {}) {
|
||||
if (!this.ensureWritable('deleteToBeginningOfWord', options)) return
|
||||
if (this.isEmpty()) this.selectToBeginningOfWord()
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes from the beginning of the line which the selection begins on
|
||||
// all the way through to the end of the selection.
|
||||
deleteToBeginningOfLine () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteToBeginningOfLine (options = {}) {
|
||||
if (!this.ensureWritable('deleteToBeginningOfLine', options)) return
|
||||
if (this.isEmpty() && this.cursor.isAtBeginningOfLine()) {
|
||||
this.selectLeft()
|
||||
} else {
|
||||
this.selectToBeginningOfLine()
|
||||
}
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes the selection or the next character after the start of the
|
||||
// selection if the selection is empty.
|
||||
delete () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
delete (options = {}) {
|
||||
if (!this.ensureWritable('delete', options)) return
|
||||
if (this.isEmpty()) this.selectRight()
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: If the selection is empty, removes all text from the cursor to the
|
||||
// end of the line. If the cursor is already at the end of the line, it
|
||||
// removes the following newline. If the selection isn't empty, only deletes
|
||||
// the contents of the selection.
|
||||
deleteToEndOfLine () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteToEndOfLine (options = {}) {
|
||||
if (!this.ensureWritable('deleteToEndOfLine', options)) return
|
||||
if (this.isEmpty()) {
|
||||
if (this.cursor.isAtEndOfLine()) {
|
||||
this.delete()
|
||||
this.delete(options)
|
||||
return
|
||||
}
|
||||
this.selectToEndOfLine()
|
||||
}
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes the selection or all characters from the start of the
|
||||
// selection to the end of the current word if nothing is selected.
|
||||
deleteToEndOfWord () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteToEndOfWord (options = {}) {
|
||||
if (!this.ensureWritable('deleteToEndOfWord', options)) return
|
||||
if (this.isEmpty()) this.selectToEndOfWord()
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes the selection or all characters from the start of the
|
||||
// selection to the end of the current word if nothing is selected.
|
||||
deleteToBeginningOfSubword () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteToBeginningOfSubword (options = {}) {
|
||||
if (!this.ensureWritable('deleteToBeginningOfSubword', options)) return
|
||||
if (this.isEmpty()) this.selectToPreviousSubwordBoundary()
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes the selection or all characters from the start of the
|
||||
// selection to the end of the current word if nothing is selected.
|
||||
deleteToEndOfSubword () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteToEndOfSubword (options = {}) {
|
||||
if (!this.ensureWritable('deleteToEndOfSubword', options)) return
|
||||
if (this.isEmpty()) this.selectToNextSubwordBoundary()
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
// Public: Removes only the selected text.
|
||||
deleteSelectedText () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteSelectedText (options = {}) {
|
||||
if (!this.ensureWritable('deleteSelectedText', options)) return
|
||||
const bufferRange = this.getBufferRange()
|
||||
if (!bufferRange.isEmpty()) this.editor.buffer.delete(bufferRange)
|
||||
if (this.cursor) this.cursor.setBufferPosition(bufferRange.start)
|
||||
@@ -584,7 +650,11 @@ class Selection {
|
||||
// Public: Removes the line at the beginning of the selection if the selection
|
||||
// is empty unless the selection spans multiple lines in which case all lines
|
||||
// are removed.
|
||||
deleteLine () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
deleteLine (options = {}) {
|
||||
if (!this.ensureWritable('deleteLine', options)) return
|
||||
const range = this.getBufferRange()
|
||||
if (range.isEmpty()) {
|
||||
const start = this.cursor.getScreenRow()
|
||||
@@ -607,7 +677,11 @@ class Selection {
|
||||
// be separated by a single space.
|
||||
//
|
||||
// If there selection spans more than one line, all the lines are joined together.
|
||||
joinLines () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
joinLines (options = {}) {
|
||||
if (!this.ensureWritable('joinLines', options)) return
|
||||
let joinMarker
|
||||
const selectedRange = this.getBufferRange()
|
||||
if (selectedRange.isEmpty()) {
|
||||
@@ -629,7 +703,7 @@ class Selection {
|
||||
})
|
||||
if (trailingWhitespaceRange) {
|
||||
this.setBufferRange(trailingWhitespaceRange)
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
}
|
||||
|
||||
const currentRow = selectedRange.start.row
|
||||
@@ -638,7 +712,7 @@ class Selection {
|
||||
(nextRow <= this.editor.buffer.getLastRow()) &&
|
||||
(this.editor.buffer.lineLengthForRow(nextRow) > 0) &&
|
||||
(this.editor.buffer.lineLengthForRow(currentRow) > 0)
|
||||
if (insertSpace) this.insertText(' ')
|
||||
if (insertSpace) this.insertText(' ', options)
|
||||
|
||||
this.cursor.moveToEndOfLine()
|
||||
|
||||
@@ -647,7 +721,7 @@ class Selection {
|
||||
this.cursor.moveRight()
|
||||
this.cursor.moveToFirstCharacterOfLine()
|
||||
})
|
||||
this.deleteSelectedText()
|
||||
this.deleteSelectedText(options)
|
||||
|
||||
if (insertSpace) this.cursor.moveLeft()
|
||||
}
|
||||
@@ -660,7 +734,11 @@ class Selection {
|
||||
}
|
||||
|
||||
// Public: Removes one level of indent from the currently selected rows.
|
||||
outdentSelectedRows () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
outdentSelectedRows (options = {}) {
|
||||
if (!this.ensureWritable('outdentSelectedRows', options)) return
|
||||
const [start, end] = this.getBufferRowRange()
|
||||
const {buffer} = this.editor
|
||||
const leadingTabRegex = new RegExp(`^( {1,${this.editor.getTabLength()}}|\t)`)
|
||||
@@ -674,7 +752,11 @@ class Selection {
|
||||
|
||||
// Public: Sets the indentation level of all selected rows to values suggested
|
||||
// by the relevant grammars.
|
||||
autoIndentSelectedRows () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
autoIndentSelectedRows (options = {}) {
|
||||
if (!this.ensureWritable('autoIndentSelectedRows', options)) return
|
||||
const [start, end] = this.getBufferRowRange()
|
||||
return this.editor.autoIndentBufferRows(start, end)
|
||||
}
|
||||
@@ -683,29 +765,45 @@ class Selection {
|
||||
// of a comment.
|
||||
//
|
||||
// Removes the comment if they are currently wrapped in a comment.
|
||||
toggleLineComments () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
toggleLineComments (options = {}) {
|
||||
if (!this.ensureWritable('toggleLineComments', options)) return
|
||||
this.editor.toggleLineCommentsForBufferRows(...(this.getBufferRowRange() || []))
|
||||
}
|
||||
|
||||
// Public: Cuts the selection until the end of the screen line.
|
||||
cutToEndOfLine (maintainClipboard) {
|
||||
//
|
||||
// * `maintainClipboard` {Boolean}
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
cutToEndOfLine (maintainClipboard, options = {}) {
|
||||
if (!this.ensureWritable('cutToEndOfLine', options)) return
|
||||
if (this.isEmpty()) this.selectToEndOfLine()
|
||||
return this.cut(maintainClipboard)
|
||||
return this.cut(maintainClipboard, false, options.bypassReadOnly)
|
||||
}
|
||||
|
||||
// Public: Cuts the selection until the end of the buffer line.
|
||||
cutToEndOfBufferLine (maintainClipboard) {
|
||||
//
|
||||
// * `maintainClipboard` {Boolean}
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
cutToEndOfBufferLine (maintainClipboard, options = {}) {
|
||||
if (!this.ensureWritable('cutToEndOfBufferLine', options)) return
|
||||
if (this.isEmpty()) this.selectToEndOfBufferLine()
|
||||
this.cut(maintainClipboard)
|
||||
this.cut(maintainClipboard, false, options.bypassReadOnly)
|
||||
}
|
||||
|
||||
// Public: Copies the selection to the clipboard and then deletes it.
|
||||
//
|
||||
// * `maintainClipboard` {Boolean} (default: false) See {::copy}
|
||||
// * `fullLine` {Boolean} (default: false) See {::copy}
|
||||
cut (maintainClipboard = false, fullLine = false) {
|
||||
// * `bypassReadOnly` {Boolean} (default: false) Must be `true` to modify text within a read-only editor.
|
||||
cut (maintainClipboard = false, fullLine = false, bypassReadOnly = false) {
|
||||
if (!this.ensureWritable('cut', {bypassReadOnly})) return
|
||||
this.copy(maintainClipboard, fullLine)
|
||||
this.delete()
|
||||
this.delete({bypassReadOnly})
|
||||
}
|
||||
|
||||
// Public: Copies the current selection to the clipboard.
|
||||
@@ -783,7 +881,9 @@ class Selection {
|
||||
// * `options` (optional) {Object} with the keys:
|
||||
// * `autoIndent` If `true`, the line is indented to an automatically-inferred
|
||||
// level. Otherwise, {TextEditor::getTabText} is inserted.
|
||||
indent ({autoIndent} = {}) {
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
indent ({autoIndent, bypassReadOnly} = {}) {
|
||||
if (!this.ensureWritable('indent', {bypassReadOnly})) return
|
||||
const {row} = this.cursor.getBufferPosition()
|
||||
|
||||
if (this.isEmpty()) {
|
||||
@@ -793,17 +893,21 @@ class Selection {
|
||||
|
||||
if (autoIndent && delta > 0) {
|
||||
if (!this.editor.getSoftTabs()) delta = Math.max(delta, 1)
|
||||
this.insertText(this.editor.buildIndentString(delta))
|
||||
this.insertText(this.editor.buildIndentString(delta), {bypassReadOnly})
|
||||
} else {
|
||||
this.insertText(this.editor.buildIndentString(1, this.cursor.getBufferColumn()))
|
||||
this.insertText(this.editor.buildIndentString(1, this.cursor.getBufferColumn()), {bypassReadOnly})
|
||||
}
|
||||
} else {
|
||||
this.indentSelectedRows()
|
||||
this.indentSelectedRows({bypassReadOnly})
|
||||
}
|
||||
}
|
||||
|
||||
// Public: If the selection spans multiple rows, indent all of them.
|
||||
indentSelectedRows () {
|
||||
//
|
||||
// * `options` (optional) {Object} with the keys:
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false)
|
||||
indentSelectedRows (options = {}) {
|
||||
if (!this.ensureWritable('indentSelectedRows', options)) return
|
||||
const [start, end] = this.getBufferRowRange()
|
||||
for (let row = start; row <= end; row++) {
|
||||
if (this.editor.buffer.lineLengthForRow(row) !== 0) {
|
||||
|
||||
@@ -42,9 +42,10 @@ const DEFAULT_NON_WORD_CHARACTERS = "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…"
|
||||
// then be called with all current editor instances and also when any editor is
|
||||
// created in the future.
|
||||
//
|
||||
// ```coffee
|
||||
// atom.workspace.observeTextEditors (editor) ->
|
||||
// ```js
|
||||
// atom.workspace.observeTextEditors(editor => {
|
||||
// editor.insertText('Hello World')
|
||||
// })
|
||||
// ```
|
||||
//
|
||||
// ## Buffer vs. Screen Coordinates
|
||||
@@ -1306,7 +1307,12 @@ class TextEditor {
|
||||
// Essential: Replaces the entire contents of the buffer with the given {String}.
|
||||
//
|
||||
// * `text` A {String} to replace with
|
||||
setText (text) { return this.buffer.setText(text) }
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor.
|
||||
setText (text, options = {}) {
|
||||
if (!this.ensureWritable('setText', options)) return
|
||||
return this.buffer.setText(text)
|
||||
}
|
||||
|
||||
// Essential: Set the text in the given {Range} in buffer coordinates.
|
||||
//
|
||||
@@ -1315,9 +1321,11 @@ class TextEditor {
|
||||
// * `options` (optional) {Object}
|
||||
// * `normalizeLineEndings` (optional) {Boolean} (default: true)
|
||||
// * `undo` (optional) {String} 'skip' will skip the undo system
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
//
|
||||
// Returns the {Range} of the newly-inserted text.
|
||||
setTextInBufferRange (range, text, options) {
|
||||
setTextInBufferRange (range, text, options = {}) {
|
||||
if (!this.ensureWritable('setTextInBufferRange', options)) return
|
||||
return this.getBuffer().setTextInRange(range, text, options)
|
||||
}
|
||||
|
||||
@@ -1326,9 +1334,9 @@ class TextEditor {
|
||||
// * `text` A {String} representing the text to insert.
|
||||
// * `options` (optional) See {Selection::insertText}.
|
||||
//
|
||||
// Returns a {Range} when the text has been inserted
|
||||
// Returns a {Boolean} false when the text has not been inserted
|
||||
// Returns a {Range} when the text has been inserted. Returns a {Boolean} `false` when the text has not been inserted.
|
||||
insertText (text, options = {}) {
|
||||
if (!this.ensureWritable('insertText', options)) return
|
||||
if (!this.emitWillInsertTextEvent(text)) return false
|
||||
|
||||
let groupLastChanges = false
|
||||
@@ -1352,20 +1360,31 @@ class TextEditor {
|
||||
}
|
||||
|
||||
// Essential: For each selection, replace the selected text with a newline.
|
||||
insertNewline (options) {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
insertNewline (options = {}) {
|
||||
return this.insertText('\n', options)
|
||||
}
|
||||
|
||||
// Essential: For each selection, if the selection is empty, delete the character
|
||||
// following the cursor. Otherwise delete the selected text.
|
||||
delete () {
|
||||
return this.mutateSelectedText(selection => selection.delete())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
delete (options = {}) {
|
||||
if (!this.ensureWritable('delete', options)) return
|
||||
return this.mutateSelectedText(selection => selection.delete(options))
|
||||
}
|
||||
|
||||
// Essential: For each selection, if the selection is empty, delete the character
|
||||
// preceding the cursor. Otherwise delete the selected text.
|
||||
backspace () {
|
||||
return this.mutateSelectedText(selection => selection.backspace())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
backspace (options = {}) {
|
||||
if (!this.ensureWritable('backspace', options)) return
|
||||
return this.mutateSelectedText(selection => selection.backspace(options))
|
||||
}
|
||||
|
||||
// Extended: Mutate the text of all the selections in a single transaction.
|
||||
@@ -1386,7 +1405,12 @@ class TextEditor {
|
||||
|
||||
// Move lines intersecting the most recent selection or multiple selections
|
||||
// up by one row in screen coordinates.
|
||||
moveLineUp () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
moveLineUp (options = {}) {
|
||||
if (!this.ensureWritable('moveLineUp', options)) return
|
||||
|
||||
const selections = this.getSelectedBufferRanges().sort((a, b) => a.compare(b))
|
||||
|
||||
if (selections[0].start.row === 0) return
|
||||
@@ -1454,7 +1478,12 @@ class TextEditor {
|
||||
|
||||
// Move lines intersecting the most recent selection or multiple selections
|
||||
// down by one row in screen coordinates.
|
||||
moveLineDown () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
moveLineDown (options = {}) {
|
||||
if (!this.ensureWritable('moveLineDown', options)) return
|
||||
|
||||
const selections = this.getSelectedBufferRanges()
|
||||
selections.sort((a, b) => b.compare(a))
|
||||
|
||||
@@ -1526,7 +1555,11 @@ class TextEditor {
|
||||
}
|
||||
|
||||
// Move any active selections one column to the left.
|
||||
moveSelectionLeft () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
moveSelectionLeft (options = {}) {
|
||||
if (!this.ensureWritable('moveSelectionLeft', options)) return
|
||||
const selections = this.getSelectedBufferRanges()
|
||||
const noSelectionAtStartOfLine = selections.every(selection => selection.start.column !== 0)
|
||||
|
||||
@@ -1550,7 +1583,11 @@ class TextEditor {
|
||||
}
|
||||
|
||||
// Move any active selections one column to the right.
|
||||
moveSelectionRight () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
moveSelectionRight (options = {}) {
|
||||
if (!this.ensureWritable('moveSelectionRight', options)) return
|
||||
const selections = this.getSelectedBufferRanges()
|
||||
const noSelectionAtEndOfLine = selections.every(selection => {
|
||||
return selection.end.column !== this.buffer.lineLengthForRow(selection.end.row)
|
||||
@@ -1575,7 +1612,12 @@ class TextEditor {
|
||||
}
|
||||
}
|
||||
|
||||
duplicateLines () {
|
||||
// Duplicate all lines containing active selections.
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
duplicateLines (options = {}) {
|
||||
if (!this.ensureWritable('duplicateLines', options)) return
|
||||
this.transact(() => {
|
||||
const selections = this.getSelectionsOrderedByBufferPosition()
|
||||
const previousSelectionRanges = []
|
||||
@@ -1662,7 +1704,11 @@ class TextEditor {
|
||||
//
|
||||
// If the selection is empty, the characters preceding and following the cursor
|
||||
// are swapped. Otherwise, the selected characters are reversed.
|
||||
transpose () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
transpose (options = {}) {
|
||||
if (!this.ensureWritable('transpose', options)) return
|
||||
this.mutateSelectedText(selection => {
|
||||
if (selection.isEmpty()) {
|
||||
selection.selectRight()
|
||||
@@ -1680,23 +1726,35 @@ class TextEditor {
|
||||
//
|
||||
// For each selection, if the selection is empty, converts the containing word
|
||||
// to upper case. Otherwise convert the selected text to upper case.
|
||||
upperCase () {
|
||||
this.replaceSelectedText({selectWordIfEmpty: true}, text => text.toUpperCase())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
upperCase (options = {}) {
|
||||
if (!this.ensureWritable('upperCase', options)) return
|
||||
this.replaceSelectedText({selectWordIfEmpty: true}, text => text.toUpperCase(options))
|
||||
}
|
||||
|
||||
// Extended: Convert the selected text to lower case.
|
||||
//
|
||||
// For each selection, if the selection is empty, converts the containing word
|
||||
// to upper case. Otherwise convert the selected text to upper case.
|
||||
lowerCase () {
|
||||
this.replaceSelectedText({selectWordIfEmpty: true}, text => text.toLowerCase())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
lowerCase (options = {}) {
|
||||
if (!this.ensureWritable('lowerCase', options)) return
|
||||
this.replaceSelectedText({selectWordIfEmpty: true}, text => text.toLowerCase(options))
|
||||
}
|
||||
|
||||
// Extended: Toggle line comments for rows intersecting selections.
|
||||
//
|
||||
// If the current grammar doesn't support comments, does nothing.
|
||||
toggleLineCommentsInSelection () {
|
||||
this.mutateSelectedText(selection => selection.toggleLineComments())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
toggleLineCommentsInSelection (options = {}) {
|
||||
if (!this.ensureWritable('toggleLineCommentsInSelection', options)) return
|
||||
this.mutateSelectedText(selection => selection.toggleLineComments(options))
|
||||
}
|
||||
|
||||
// Convert multiple lines to a single line.
|
||||
@@ -1707,20 +1765,32 @@ class TextEditor {
|
||||
//
|
||||
// Joining a line means that multiple lines are converted to a single line with
|
||||
// the contents of each of the original non-empty lines separated by a space.
|
||||
joinLines () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
joinLines (options = {}) {
|
||||
if (!this.ensureWritable('joinLines', options)) return
|
||||
this.mutateSelectedText(selection => selection.joinLines())
|
||||
}
|
||||
|
||||
// Extended: For each cursor, insert a newline at beginning the following line.
|
||||
insertNewlineBelow () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
insertNewlineBelow (options = {}) {
|
||||
if (!this.ensureWritable('insertNewlineBelow', options)) return
|
||||
this.transact(() => {
|
||||
this.moveToEndOfLine()
|
||||
this.insertNewline()
|
||||
this.insertNewline(options)
|
||||
})
|
||||
}
|
||||
|
||||
// Extended: For each cursor, insert a newline at the end of the preceding line.
|
||||
insertNewlineAbove () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
insertNewlineAbove (options = {}) {
|
||||
if (!this.ensureWritable('insertNewlineAbove', options)) return
|
||||
this.transact(() => {
|
||||
const bufferRow = this.getCursorBufferPosition().row
|
||||
const indentLevel = this.indentationForBufferRow(bufferRow)
|
||||
@@ -1728,7 +1798,7 @@ class TextEditor {
|
||||
|
||||
this.moveToBeginningOfLine()
|
||||
this.moveLeft()
|
||||
this.insertNewline()
|
||||
this.insertNewline(options)
|
||||
|
||||
if (this.shouldAutoIndent() && (this.indentationForBufferRow(bufferRow) < indentLevel)) {
|
||||
this.setIndentationForBufferRow(bufferRow, indentLevel)
|
||||
@@ -1744,62 +1814,117 @@ class TextEditor {
|
||||
// Extended: For each selection, if the selection is empty, delete all characters
|
||||
// of the containing word that precede the cursor. Otherwise delete the
|
||||
// selected text.
|
||||
deleteToBeginningOfWord () {
|
||||
this.mutateSelectedText(selection => selection.deleteToBeginningOfWord())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteToBeginningOfWord (options = {}) {
|
||||
if (!this.ensureWritable('deleteToBeginningOfWord', options)) return
|
||||
this.mutateSelectedText(selection => selection.deleteToBeginningOfWord(options))
|
||||
}
|
||||
|
||||
// Extended: Similar to {::deleteToBeginningOfWord}, but deletes only back to the
|
||||
// previous word boundary.
|
||||
deleteToPreviousWordBoundary () {
|
||||
this.mutateSelectedText(selection => selection.deleteToPreviousWordBoundary())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteToPreviousWordBoundary (options = {}) {
|
||||
if (!this.ensureWritable('deleteToPreviousWordBoundary', options)) return
|
||||
this.mutateSelectedText(selection => selection.deleteToPreviousWordBoundary(options))
|
||||
}
|
||||
|
||||
// Extended: Similar to {::deleteToEndOfWord}, but deletes only up to the
|
||||
// next word boundary.
|
||||
deleteToNextWordBoundary () {
|
||||
this.mutateSelectedText(selection => selection.deleteToNextWordBoundary())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteToNextWordBoundary (options = {}) {
|
||||
if (!this.ensureWritable('deleteToNextWordBoundary', options)) return
|
||||
this.mutateSelectedText(selection => selection.deleteToNextWordBoundary(options))
|
||||
}
|
||||
|
||||
// Extended: For each selection, if the selection is empty, delete all characters
|
||||
// of the containing subword following the cursor. Otherwise delete the selected
|
||||
// text.
|
||||
deleteToBeginningOfSubword () {
|
||||
this.mutateSelectedText(selection => selection.deleteToBeginningOfSubword())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteToBeginningOfSubword (options = {}) {
|
||||
if (!this.ensureWritable('deleteToBeginningOfSubword', options)) return
|
||||
this.mutateSelectedText(selection => selection.deleteToBeginningOfSubword(options))
|
||||
}
|
||||
|
||||
// Extended: For each selection, if the selection is empty, delete all characters
|
||||
// of the containing subword following the cursor. Otherwise delete the selected
|
||||
// text.
|
||||
deleteToEndOfSubword () {
|
||||
this.mutateSelectedText(selection => selection.deleteToEndOfSubword())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteToEndOfSubword (options = {}) {
|
||||
if (!this.ensureWritable('deleteToEndOfSubword', options)) return
|
||||
this.mutateSelectedText(selection => selection.deleteToEndOfSubword(options))
|
||||
}
|
||||
|
||||
// Extended: For each selection, if the selection is empty, delete all characters
|
||||
// of the containing line that precede the cursor. Otherwise delete the
|
||||
// selected text.
|
||||
deleteToBeginningOfLine () {
|
||||
this.mutateSelectedText(selection => selection.deleteToBeginningOfLine())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteToBeginningOfLine (options = {}) {
|
||||
if (!this.ensureWritable('deleteToBeginningOfLine', options)) return
|
||||
this.mutateSelectedText(selection => selection.deleteToBeginningOfLine(options))
|
||||
}
|
||||
|
||||
// Extended: For each selection, if the selection is not empty, deletes the
|
||||
// selection; otherwise, deletes all characters of the containing line
|
||||
// following the cursor. If the cursor is already at the end of the line,
|
||||
// deletes the following newline.
|
||||
deleteToEndOfLine () {
|
||||
this.mutateSelectedText(selection => selection.deleteToEndOfLine())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteToEndOfLine (options = {}) {
|
||||
if (!this.ensureWritable('deleteToEndOfLine', options)) return
|
||||
this.mutateSelectedText(selection => selection.deleteToEndOfLine(options))
|
||||
}
|
||||
|
||||
// Extended: For each selection, if the selection is empty, delete all characters
|
||||
// of the containing word following the cursor. Otherwise delete the selected
|
||||
// text.
|
||||
deleteToEndOfWord () {
|
||||
this.mutateSelectedText(selection => selection.deleteToEndOfWord())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteToEndOfWord (options = {}) {
|
||||
if (!this.ensureWritable('deleteToEndOfWord', options)) return
|
||||
this.mutateSelectedText(selection => selection.deleteToEndOfWord(options))
|
||||
}
|
||||
|
||||
// Extended: Delete all lines intersecting selections.
|
||||
deleteLine () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
deleteLine (options = {}) {
|
||||
if (!this.ensureWritable('deleteLine', options)) return
|
||||
this.mergeSelectionsOnSameRows()
|
||||
this.mutateSelectedText(selection => selection.deleteLine())
|
||||
this.mutateSelectedText(selection => selection.deleteLine(options))
|
||||
}
|
||||
|
||||
// Private: Ensure that this editor is not marked read-only before allowing a buffer modification to occur. If
|
||||
// the editor is read-only, require an explicit opt-in option to proceed (`bypassReadOnly`) or throw an Error.
|
||||
ensureWritable (methodName, opts) {
|
||||
if (!opts.bypassReadOnly && this.isReadOnly()) {
|
||||
if (atom.inDevMode() || atom.inSpecMode()) {
|
||||
const e = new Error('Attempt to mutate a read-only TextEditor')
|
||||
e.detail =
|
||||
`Your package is attempting to call ${methodName} on an editor that has been marked read-only. ` +
|
||||
'Pass {bypassReadOnly: true} to modify it anyway, or test editors with .isReadOnly() before attempting ' +
|
||||
'modifications.'
|
||||
throw e
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1807,13 +1932,21 @@ class TextEditor {
|
||||
*/
|
||||
|
||||
// Essential: Undo the last change.
|
||||
undo () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
undo (options = {}) {
|
||||
if (!this.ensureWritable('undo', options)) return
|
||||
this.avoidMergingSelections(() => this.buffer.undo({selectionsMarkerLayer: this.selectionsMarkerLayer}))
|
||||
this.getLastSelection().autoscroll()
|
||||
}
|
||||
|
||||
// Essential: Redo the last change.
|
||||
redo () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false)
|
||||
redo (options = {}) {
|
||||
if (!this.ensureWritable('redo', options)) return
|
||||
this.avoidMergingSelections(() => this.buffer.redo({selectionsMarkerLayer: this.selectionsMarkerLayer}))
|
||||
this.getLastSelection().autoscroll()
|
||||
}
|
||||
@@ -1967,11 +2100,11 @@ class TextEditor {
|
||||
//
|
||||
// ## Examples
|
||||
//
|
||||
// ```coffee
|
||||
// editor.clipBufferPosition([-1, -1]) # -> `[0, 0]`
|
||||
// ```js
|
||||
// editor.clipBufferPosition([-1, -1]) // -> `[0, 0]`
|
||||
//
|
||||
// # When the line at buffer row 2 is 10 characters long
|
||||
// editor.clipBufferPosition([2, Infinity]) # -> `[2, 10]`
|
||||
// // When the line at buffer row 2 is 10 characters long
|
||||
// editor.clipBufferPosition([2, Infinity]) // -> `[2, 10]`
|
||||
// ```
|
||||
//
|
||||
// * `bufferPosition` The {Point} representing the position to clip.
|
||||
@@ -1996,11 +2129,11 @@ class TextEditor {
|
||||
//
|
||||
// ## Examples
|
||||
//
|
||||
// ```coffee
|
||||
// editor.clipScreenPosition([-1, -1]) # -> `[0, 0]`
|
||||
// ```js
|
||||
// editor.clipScreenPosition([-1, -1]) // -> `[0, 0]`
|
||||
//
|
||||
// # When the line at screen row 2 is 10 characters long
|
||||
// editor.clipScreenPosition([2, Infinity]) # -> `[2, 10]`
|
||||
// // When the line at screen row 2 is 10 characters long
|
||||
// editor.clipScreenPosition([2, Infinity]) // -> `[2, 10]`
|
||||
// ```
|
||||
//
|
||||
// * `screenPosition` The {Point} representing the position to clip.
|
||||
@@ -3558,13 +3691,21 @@ class TextEditor {
|
||||
}
|
||||
|
||||
// Extended: Indent rows intersecting selections by one level.
|
||||
indentSelectedRows () {
|
||||
return this.mutateSelectedText(selection => selection.indentSelectedRows())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor.
|
||||
indentSelectedRows (options = {}) {
|
||||
if (!this.ensureWritable('indentSelectedRows', options)) return
|
||||
return this.mutateSelectedText(selection => selection.indentSelectedRows(options))
|
||||
}
|
||||
|
||||
// Extended: Outdent rows intersecting selections by one level.
|
||||
outdentSelectedRows () {
|
||||
return this.mutateSelectedText(selection => selection.outdentSelectedRows())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor.
|
||||
outdentSelectedRows (options = {}) {
|
||||
if (!this.ensureWritable('outdentSelectedRows', options)) return
|
||||
return this.mutateSelectedText(selection => selection.outdentSelectedRows(options))
|
||||
}
|
||||
|
||||
// Extended: Get the indentation level of the given line of text.
|
||||
@@ -3595,13 +3736,21 @@ class TextEditor {
|
||||
|
||||
// Extended: Indent rows intersecting selections based on the grammar's suggested
|
||||
// indent level.
|
||||
autoIndentSelectedRows () {
|
||||
return this.mutateSelectedText(selection => selection.autoIndentSelectedRows())
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor.
|
||||
autoIndentSelectedRows (options = {}) {
|
||||
if (!this.ensureWritable('autoIndentSelectedRows', options)) return
|
||||
return this.mutateSelectedText(selection => selection.autoIndentSelectedRows(options))
|
||||
}
|
||||
|
||||
// Indent all lines intersecting selections. See {Selection::indent} for more
|
||||
// information.
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor.
|
||||
indent (options = {}) {
|
||||
if (!this.ensureWritable('indent', options)) return
|
||||
if (options.autoIndent == null) options.autoIndent = this.shouldAutoIndent()
|
||||
this.mutateSelectedText(selection => selection.indent(options))
|
||||
}
|
||||
@@ -3739,14 +3888,18 @@ class TextEditor {
|
||||
}
|
||||
|
||||
// Essential: For each selection, cut the selected text.
|
||||
cutSelectedText () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor.
|
||||
cutSelectedText (options = {}) {
|
||||
if (!this.ensureWritable('cutSelectedText', options)) return
|
||||
let maintainClipboard = false
|
||||
this.mutateSelectedText(selection => {
|
||||
if (selection.isEmpty()) {
|
||||
selection.selectLine()
|
||||
selection.cut(maintainClipboard, true)
|
||||
selection.cut(maintainClipboard, true, options.bypassReadOnly)
|
||||
} else {
|
||||
selection.cut(maintainClipboard, false)
|
||||
selection.cut(maintainClipboard, false, options.bypassReadOnly)
|
||||
}
|
||||
maintainClipboard = true
|
||||
})
|
||||
@@ -3760,7 +3913,8 @@ class TextEditor {
|
||||
// corresponding clipboard selection text.
|
||||
//
|
||||
// * `options` (optional) See {Selection::insertText}.
|
||||
pasteText (options) {
|
||||
pasteText (options = {}) {
|
||||
if (!this.ensureWritable('parseText', options)) return
|
||||
options = Object.assign({}, options)
|
||||
let {text: clipboardText, metadata} = this.constructor.clipboard.readWithMetadata()
|
||||
if (!this.emitWillInsertTextEvent(clipboardText)) return false
|
||||
@@ -3801,10 +3955,14 @@ class TextEditor {
|
||||
// Essential: For each selection, if the selection is empty, cut all characters
|
||||
// of the containing screen line following the cursor. Otherwise cut the selected
|
||||
// text.
|
||||
cutToEndOfLine () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor.
|
||||
cutToEndOfLine (options = {}) {
|
||||
if (!this.ensureWritable('cutToEndOfLine', options)) return
|
||||
let maintainClipboard = false
|
||||
this.mutateSelectedText(selection => {
|
||||
selection.cutToEndOfLine(maintainClipboard)
|
||||
selection.cutToEndOfLine(maintainClipboard, options)
|
||||
maintainClipboard = true
|
||||
})
|
||||
}
|
||||
@@ -3812,10 +3970,14 @@ class TextEditor {
|
||||
// Essential: For each selection, if the selection is empty, cut all characters
|
||||
// of the containing buffer line following the cursor. Otherwise cut the
|
||||
// selected text.
|
||||
cutToEndOfBufferLine () {
|
||||
//
|
||||
// * `options` (optional) {Object}
|
||||
// * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor.
|
||||
cutToEndOfBufferLine (options = {}) {
|
||||
if (!this.ensureWritable('cutToEndOfBufferLine', options)) return
|
||||
let maintainClipboard = false
|
||||
this.mutateSelectedText(selection => {
|
||||
selection.cutToEndOfBufferLine(maintainClipboard)
|
||||
selection.cutToEndOfBufferLine(maintainClipboard, options)
|
||||
maintainClipboard = true
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user