const { Point, Range } = require('text-buffer');
const { pick } = require('underscore-plus');
const { Emitter } = require('event-kit');
const NonWhitespaceRegExp = /\S/;
let nextId = 0;
module.exports = class Selection {
constructor({ cursor, marker, editor, id }) {
this.id = id != null ? id : nextId++;
this.cursor = cursor;
this.marker = marker;
this.editor = editor;
this.emitter = new Emitter();
this.initialScreenRange = null;
this.wordwise = false;
this.cursor.selection = this;
this.decoration = this.editor.decorateMarker(this.marker, {
type: 'highlight',
class: 'selection'
});
this.marker.onDidChange(e => this.markerDidChange(e));
this.marker.onDidDestroy(() => this.markerDidDestroy());
}
destroy() {
this.marker.destroy();
}
isLastSelection() {
return this === this.editor.getLastSelection();
}
Section: Event Subscription
*/
onDidChangeRange(callback) {
return this.emitter.on('did-change-range', callback);
}
onDidDestroy(callback) {
return this.emitter.once('did-destroy', callback);
}
Section: Managing the selection range
*/
getScreenRange() {
return this.marker.getScreenRange();
}
setScreenRange(screenRange, options) {
return this.setBufferRange(
this.editor.bufferRangeForScreenRange(screenRange),
options
);
}
getBufferRange() {
return this.marker.getBufferRange();
}
setBufferRange(bufferRange, options = {}) {
bufferRange = Range.fromObject(bufferRange);
if (options.reversed == null) options.reversed = this.isReversed();
if (!options.preserveFolds)
this.editor.destroyFoldsContainingBufferPositions(
[bufferRange.start, bufferRange.end],
true
);
this.modifySelection(() => {
const needsFlash = options.flash;
options.flash = null;
this.marker.setBufferRange(bufferRange, options);
const autoscroll =
options.autoscroll != null
? options.autoscroll
: this.isLastSelection();
if (autoscroll) this.autoscroll();
if (needsFlash)
this.decoration.flash('flash', this.editor.selectionFlashDuration);
});
}
getBufferRowRange() {
const range = this.getBufferRange();
const start = range.start.row;
let end = range.end.row;
if (range.end.column === 0) end = Math.max(start, end - 1);
return [start, end];
}
getTailScreenPosition() {
return this.marker.getTailScreenPosition();
}
getTailBufferPosition() {
return this.marker.getTailBufferPosition();
}
getHeadScreenPosition() {
return this.marker.getHeadScreenPosition();
}
getHeadBufferPosition() {
return this.marker.getHeadBufferPosition();
}
Section: Info about the selection
*/
isEmpty() {
return this.getBufferRange().isEmpty();
}
isReversed() {
return this.marker.isReversed();
}
isSingleScreenLine() {
return this.getScreenRange().isSingleLine();
}
getText() {
return this.editor.buffer.getTextInRange(this.getBufferRange());
}
intersectsBufferRange(bufferRange) {
return this.getBufferRange().intersectsWith(bufferRange);
}
intersectsScreenRowRange(startRow, endRow) {
return this.getScreenRange().intersectsRowRange(startRow, endRow);
}
intersectsScreenRow(screenRow) {
return this.getScreenRange().intersectsRow(screenRow);
}
intersectsWith(otherSelection, exclusive) {
return this.getBufferRange().intersectsWith(
otherSelection.getBufferRange(),
exclusive
);
}
Section: Modifying the selected range
*/
clear(options) {
this.goalScreenRange = null;
if (!this.retainSelection) this.marker.clearTail();
const autoscroll =
options && options.autoscroll != null
? options.autoscroll
: this.isLastSelection();
if (autoscroll) this.autoscroll();
this.finalize();
}
selectToScreenPosition(position, options) {
position = Point.fromObject(position);
this.modifySelection(() => {
if (this.initialScreenRange) {
if (position.isLessThan(this.initialScreenRange.start)) {
this.marker.setScreenRange([position, this.initialScreenRange.end], {
reversed: true
});
} else {
this.marker.setScreenRange(
[this.initialScreenRange.start, position],
{ reversed: false }
);
}
} else {
this.cursor.setScreenPosition(position, options);
}
if (this.linewise) {
this.expandOverLine(options);
} else if (this.wordwise) {
this.expandOverWord(options);
}
});
}
selectToBufferPosition(position) {
this.modifySelection(() => this.cursor.setBufferPosition(position));
}
selectRight(columnCount) {
this.modifySelection(() => this.cursor.moveRight(columnCount));
}
selectLeft(columnCount) {
this.modifySelection(() => this.cursor.moveLeft(columnCount));
}
selectUp(rowCount) {
this.modifySelection(() => this.cursor.moveUp(rowCount));
}
selectDown(rowCount) {
this.modifySelection(() => this.cursor.moveDown(rowCount));
}
selectToTop() {
this.modifySelection(() => this.cursor.moveToTop());
}
selectToBottom() {
this.modifySelection(() => this.cursor.moveToBottom());
}
selectAll() {
this.setBufferRange(this.editor.buffer.getRange(), { autoscroll: false });
}
selectToBeginningOfLine() {
this.modifySelection(() => this.cursor.moveToBeginningOfLine());
}
selectToFirstCharacterOfLine() {
this.modifySelection(() => this.cursor.moveToFirstCharacterOfLine());
}
selectToEndOfLine() {
this.modifySelection(() => this.cursor.moveToEndOfScreenLine());
}
selectToEndOfBufferLine() {
this.modifySelection(() => this.cursor.moveToEndOfLine());
}
selectToBeginningOfWord() {
this.modifySelection(() => this.cursor.moveToBeginningOfWord());
}
selectToEndOfWord() {
this.modifySelection(() => this.cursor.moveToEndOfWord());
}
selectToBeginningOfNextWord() {
this.modifySelection(() => this.cursor.moveToBeginningOfNextWord());
}
selectToPreviousWordBoundary() {
this.modifySelection(() => this.cursor.moveToPreviousWordBoundary());
}
selectToNextWordBoundary() {
this.modifySelection(() => this.cursor.moveToNextWordBoundary());
}
selectToPreviousSubwordBoundary() {
this.modifySelection(() => this.cursor.moveToPreviousSubwordBoundary());
}
selectToNextSubwordBoundary() {
this.modifySelection(() => this.cursor.moveToNextSubwordBoundary());
}
selectToBeginningOfNextParagraph() {
this.modifySelection(() => this.cursor.moveToBeginningOfNextParagraph());
}
selectToBeginningOfPreviousParagraph() {
this.modifySelection(() =>
this.cursor.moveToBeginningOfPreviousParagraph()
);
}
selectWord(options = {}) {
if (this.cursor.isSurroundedByWhitespace()) options.wordRegex = /[\t ]*/;
if (this.cursor.isBetweenWordAndNonWord()) {
options.includeNonWordCharacters = false;
}
this.setBufferRange(
this.cursor.getCurrentWordBufferRange(options),
options
);
this.wordwise = true;
this.initialScreenRange = this.getScreenRange();
}
expandOverWord(options) {
this.setBufferRange(
this.getBufferRange().union(this.cursor.getCurrentWordBufferRange()),
{ autoscroll: false }
);
const autoscroll =
options && options.autoscroll != null
? options.autoscroll
: this.isLastSelection();
if (autoscroll) this.cursor.autoscroll();
}
selectLine(row, options) {
if (row != null) {
this.setBufferRange(
this.editor.bufferRangeForBufferRow(row, { includeNewline: true }),
options
);
} else {
const startRange = this.editor.bufferRangeForBufferRow(
this.marker.getStartBufferPosition().row
);
const endRange = this.editor.bufferRangeForBufferRow(
this.marker.getEndBufferPosition().row,
{ includeNewline: true }
);
this.setBufferRange(startRange.union(endRange), options);
}
this.linewise = true;
this.wordwise = false;
this.initialScreenRange = this.getScreenRange();
}
expandOverLine(options) {
const range = this.getBufferRange().union(
this.cursor.getCurrentLineBufferRange({ includeNewline: true })
);
this.setBufferRange(range, { autoscroll: false });
const autoscroll =
options && options.autoscroll != null
? options.autoscroll
: this.isLastSelection();
if (autoscroll) this.cursor.autoscroll();
}
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
*/
insertText(text, options = {}) {
if (!this.ensureWritable('insertText', options)) return;
let desiredIndentLevel, indentAdjustment;
const oldBufferRange = this.getBufferRange();
const wasReversed = this.isReversed();
this.clear(options);
let autoIndentFirstLine = false;
const precedingText = this.editor.getTextInRange([
[oldBufferRange.start.row, 0],
oldBufferRange.start
]);
const remainingLines = text.split('\n');
const firstInsertedLine = remainingLines.shift();
if (
options.indentBasis != null &&
!options.preserveTrailingLineIndentation
) {
indentAdjustment =
this.editor.indentLevelForLine(precedingText) - options.indentBasis;
this.adjustIndent(remainingLines, indentAdjustment);
}
const textIsAutoIndentable =
text === '\n' || text === '\r\n' || NonWhitespaceRegExp.test(text);
if (
options.autoIndent &&
textIsAutoIndentable &&
!NonWhitespaceRegExp.test(precedingText) &&
remainingLines.length > 0
) {
autoIndentFirstLine = true;
const firstLine = precedingText + firstInsertedLine;
const languageMode = this.editor.buffer.getLanguageMode();
desiredIndentLevel =
languageMode.suggestedIndentForLineAtBufferRow &&
languageMode.suggestedIndentForLineAtBufferRow(
oldBufferRange.start.row,
firstLine,
this.editor.getTabLength()
);
if (desiredIndentLevel != null) {
indentAdjustment =
desiredIndentLevel - this.editor.indentLevelForLine(firstLine);
this.adjustIndent(remainingLines, indentAdjustment);
}
}
text = firstInsertedLine;
if (remainingLines.length > 0) text += `\n${remainingLines.join('\n')}`;
const newBufferRange = this.editor.buffer.setTextInRange(
oldBufferRange,
text,
pick(options, 'undo', 'normalizeLineEndings')
);
if (options.select) {
this.setBufferRange(newBufferRange, { reversed: wasReversed });
} else {
if (wasReversed) this.cursor.setBufferPosition(newBufferRange.end);
}
if (autoIndentFirstLine) {
this.editor.setIndentationForBufferRow(
oldBufferRange.start.row,
desiredIndentLevel
);
}
if (options.autoIndentNewline && text === '\n') {
this.editor.autoIndentBufferRow(newBufferRange.end.row, {
preserveLeadingWhitespace: true,
skipBlankLines: false
});
} else if (options.autoDecreaseIndent && NonWhitespaceRegExp.test(text)) {
this.editor.autoDecreaseIndentForBufferRow(newBufferRange.start.row);
}
const autoscroll =
options.autoscroll != null ? options.autoscroll : this.isLastSelection();
if (autoscroll) this.autoscroll();
return newBufferRange;
}
backspace(options = {}) {
if (!this.ensureWritable('backspace', options)) return;
if (this.isEmpty()) this.selectLeft();
this.deleteSelectedText(options);
}
deleteToPreviousWordBoundary(options = {}) {
if (!this.ensureWritable('deleteToPreviousWordBoundary', options)) return;
if (this.isEmpty()) this.selectToPreviousWordBoundary();
this.deleteSelectedText(options);
}
deleteToNextWordBoundary(options = {}) {
if (!this.ensureWritable('deleteToNextWordBoundary', options)) return;
if (this.isEmpty()) this.selectToNextWordBoundary();
this.deleteSelectedText(options);
}
deleteToBeginningOfWord(options = {}) {
if (!this.ensureWritable('deleteToBeginningOfWord', options)) return;
if (this.isEmpty()) this.selectToBeginningOfWord();
this.deleteSelectedText(options);
}
deleteToBeginningOfLine(options = {}) {
if (!this.ensureWritable('deleteToBeginningOfLine', options)) return;
if (this.isEmpty() && this.cursor.isAtBeginningOfLine()) {
this.selectLeft();
} else {
this.selectToBeginningOfLine();
}
this.deleteSelectedText(options);
}
delete(options = {}) {
if (!this.ensureWritable('delete', options)) return;
if (this.isEmpty()) this.selectRight();
this.deleteSelectedText(options);
}
deleteToEndOfLine(options = {}) {
if (!this.ensureWritable('deleteToEndOfLine', options)) return;
if (this.isEmpty()) {
if (this.cursor.isAtEndOfLine()) {
this.delete(options);
return;
}
this.selectToEndOfLine();
}
this.deleteSelectedText(options);
}
deleteToEndOfWord(options = {}) {
if (!this.ensureWritable('deleteToEndOfWord', options)) return;
if (this.isEmpty()) this.selectToEndOfWord();
this.deleteSelectedText(options);
}
deleteToBeginningOfSubword(options = {}) {
if (!this.ensureWritable('deleteToBeginningOfSubword', options)) return;
if (this.isEmpty()) this.selectToPreviousSubwordBoundary();
this.deleteSelectedText(options);
}
deleteToEndOfSubword(options = {}) {
if (!this.ensureWritable('deleteToEndOfSubword', options)) return;
if (this.isEmpty()) this.selectToNextSubwordBoundary();
this.deleteSelectedText(options);
}
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);
}
deleteLine(options = {}) {
if (!this.ensureWritable('deleteLine', options)) return;
const range = this.getBufferRange();
if (range.isEmpty()) {
const start = this.cursor.getScreenRow();
const range = this.editor.bufferRowsForScreenRows(start, start + 1);
if (range[1] > range[0]) {
this.editor.buffer.deleteRows(range[0], range[1] - 1);
} else {
this.editor.buffer.deleteRow(range[0]);
}
} else {
const start = range.start.row;
let end = range.end.row;
if (end !== this.editor.buffer.getLastRow() && range.end.column === 0)
end--;
this.editor.buffer.deleteRows(start, end);
}
this.cursor.setBufferPosition({
row: this.cursor.getBufferRow(),
column: range.start.column
});
}
joinLines(options = {}) {
if (!this.ensureWritable('joinLines', options)) return;
let joinMarker;
const selectedRange = this.getBufferRange();
if (selectedRange.isEmpty()) {
if (selectedRange.start.row === this.editor.buffer.getLastRow()) return;
} else {
joinMarker = this.editor.markBufferRange(selectedRange, {
invalidate: 'never'
});
}
const rowCount = Math.max(1, selectedRange.getRowCount() - 1);
for (let i = 0; i < rowCount; i++) {
this.cursor.setBufferPosition([selectedRange.start.row]);
this.cursor.moveToEndOfLine();
const scanRange = this.cursor.getCurrentLineBufferRange();
let trailingWhitespaceRange = null;
this.editor.scanInBufferRange(/[ \t]+$/, scanRange, ({ range }) => {
trailingWhitespaceRange = range;
});
if (trailingWhitespaceRange) {
this.setBufferRange(trailingWhitespaceRange);
this.deleteSelectedText(options);
}
const currentRow = selectedRange.start.row;
const nextRow = currentRow + 1;
const insertSpace =
nextRow <= this.editor.buffer.getLastRow() &&
this.editor.buffer.lineLengthForRow(nextRow) > 0 &&
this.editor.buffer.lineLengthForRow(currentRow) > 0;
if (insertSpace) this.insertText(' ', options);
this.cursor.moveToEndOfLine();
this.modifySelection(() => {
this.cursor.moveRight();
this.cursor.moveToFirstCharacterOfLine();
});
this.deleteSelectedText(options);
if (insertSpace) this.cursor.moveLeft();
}
if (joinMarker) {
const newSelectedRange = joinMarker.getBufferRange();
this.setBufferRange(newSelectedRange);
joinMarker.destroy();
}
}
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)`
);
for (let row = start; row <= end; row++) {
const match = buffer.lineForRow(row).match(leadingTabRegex);
if (match && match[0].length > 0) {
buffer.delete([[row, 0], [row, match[0].length]]);
}
}
}
autoIndentSelectedRows(options = {}) {
if (!this.ensureWritable('autoIndentSelectedRows', options)) return;
const [start, end] = this.getBufferRowRange();
return this.editor.autoIndentBufferRows(start, end);
}
toggleLineComments(options = {}) {
if (!this.ensureWritable('toggleLineComments', options)) return;
let bufferRowRange = this.getBufferRowRange() || [null, null];
this.editor.toggleLineCommentsForBufferRows(...bufferRowRange, {
correctSelection: true,
selection: this
});
}
cutToEndOfLine(maintainClipboard, options = {}) {
if (!this.ensureWritable('cutToEndOfLine', options)) return;
if (this.isEmpty()) this.selectToEndOfLine();
return this.cut(maintainClipboard, false, options.bypassReadOnly);
}
cutToEndOfBufferLine(maintainClipboard, options = {}) {
if (!this.ensureWritable('cutToEndOfBufferLine', options)) return;
if (this.isEmpty()) this.selectToEndOfBufferLine();
this.cut(maintainClipboard, false, options.bypassReadOnly);
}
cut(maintainClipboard = false, fullLine = false, bypassReadOnly = false) {
if (!this.ensureWritable('cut', { bypassReadOnly })) return;
this.copy(maintainClipboard, fullLine);
this.delete({ bypassReadOnly });
}
copy(maintainClipboard = false, fullLine = false) {
if (this.isEmpty()) return;
const { start, end } = this.getBufferRange();
const selectionText = this.editor.getTextInRange([start, end]);
const precedingText = this.editor.getTextInRange([[start.row, 0], start]);
const startLevel = this.editor.indentLevelForLine(precedingText);
if (maintainClipboard) {
let {
text: clipboardText,
metadata
} = this.editor.constructor.clipboard.readWithMetadata();
if (!metadata) metadata = {};
if (!metadata.selections) {
metadata.selections = [
{
text: clipboardText,
indentBasis: metadata.indentBasis,
fullLine: metadata.fullLine
}
];
}
metadata.selections.push({
text: selectionText,
indentBasis: startLevel,
fullLine
});
this.editor.constructor.clipboard.write(
[clipboardText, selectionText].join('\n'),
metadata
);
} else {
this.editor.constructor.clipboard.write(selectionText, {
indentBasis: startLevel,
fullLine
});
}
}
fold() {
const range = this.getBufferRange();
if (!range.isEmpty()) {
this.editor.foldBufferRange(range);
this.cursor.setBufferPosition(range.end);
}
}
adjustIndent(lines, indentAdjustment) {
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (indentAdjustment === 0 || line === '') {
continue;
} else if (indentAdjustment > 0) {
lines[i] = this.editor.buildIndentString(indentAdjustment) + line;
} else {
const currentIndentLevel = this.editor.indentLevelForLine(lines[i]);
const indentLevel = Math.max(0, currentIndentLevel + indentAdjustment);
lines[i] = line.replace(
/^[\t ]+/,
this.editor.buildIndentString(indentLevel)
);
}
}
}
indent({ autoIndent, bypassReadOnly } = {}) {
if (!this.ensureWritable('indent', { bypassReadOnly })) return;
const { row } = this.cursor.getBufferPosition();
if (this.isEmpty()) {
this.cursor.skipLeadingWhitespace();
const desiredIndent = this.editor.suggestedIndentForBufferRow(row);
let delta = desiredIndent - this.cursor.getIndentLevel();
if (autoIndent && delta > 0) {
if (!this.editor.getSoftTabs()) delta = Math.max(delta, 1);
this.insertText(this.editor.buildIndentString(delta), {
bypassReadOnly
});
} else {
this.insertText(
this.editor.buildIndentString(1, this.cursor.getBufferColumn()),
{ bypassReadOnly }
);
}
} else {
this.indentSelectedRows({ bypassReadOnly });
}
}
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) {
this.editor.buffer.insert([row, 0], this.editor.getTabText());
}
}
}
Section: Managing multiple selections
*/
addSelectionBelow() {
const range = this.getGoalScreenRange().copy();
const nextRow = range.end.row + 1;
for (
let row = nextRow, end = this.editor.getLastScreenRow();
row <= end;
row++
) {
range.start.row = row;
range.end.row = row;
const clippedRange = this.editor.clipScreenRange(range, {
skipSoftWrapIndentation: true
});
if (range.isEmpty()) {
if (range.end.column > 0 && clippedRange.end.column === 0) continue;
} else {
if (clippedRange.isEmpty()) continue;
}
const containingSelections = this.editor.selectionsMarkerLayer.findMarkers(
{ containsScreenRange: clippedRange }
);
if (containingSelections.length === 0) {
const selection = this.editor.addSelectionForScreenRange(clippedRange);
selection.setGoalScreenRange(range);
}
break;
}
}
addSelectionAbove() {
const range = this.getGoalScreenRange().copy();
const previousRow = range.end.row - 1;
for (let row = previousRow; row >= 0; row--) {
range.start.row = row;
range.end.row = row;
const clippedRange = this.editor.clipScreenRange(range, {
skipSoftWrapIndentation: true
});
if (range.isEmpty()) {
if (range.end.column > 0 && clippedRange.end.column === 0) continue;
} else {
if (clippedRange.isEmpty()) continue;
}
const containingSelections = this.editor.selectionsMarkerLayer.findMarkers(
{ containsScreenRange: clippedRange }
);
if (containingSelections.length === 0) {
const selection = this.editor.addSelectionForScreenRange(clippedRange);
selection.setGoalScreenRange(range);
}
break;
}
}
merge(otherSelection, options = {}) {
const myGoalScreenRange = this.getGoalScreenRange();
const otherGoalScreenRange = otherSelection.getGoalScreenRange();
if (myGoalScreenRange && otherGoalScreenRange) {
options.goalScreenRange = myGoalScreenRange.union(otherGoalScreenRange);
} else {
options.goalScreenRange = myGoalScreenRange || otherGoalScreenRange;
}
const bufferRange = this.getBufferRange().union(
otherSelection.getBufferRange()
);
this.setBufferRange(
bufferRange,
Object.assign({ autoscroll: false }, options)
);
otherSelection.destroy();
}
Section: Comparing to other selections
*/
compare(otherSelection) {
return this.marker.compare(otherSelection.marker);
}
Section: Private Utilities
*/
setGoalScreenRange(range) {
this.goalScreenRange = Range.fromObject(range);
}
getGoalScreenRange() {
return this.goalScreenRange || this.getScreenRange();
}
markerDidChange(e) {
const {
oldHeadBufferPosition,
oldTailBufferPosition,
newHeadBufferPosition
} = e;
const {
oldHeadScreenPosition,
oldTailScreenPosition,
newHeadScreenPosition
} = e;
const { textChanged } = e;
if (!oldHeadScreenPosition.isEqual(newHeadScreenPosition)) {
this.cursor.goalColumn = null;
const cursorMovedEvent = {
oldBufferPosition: oldHeadBufferPosition,
oldScreenPosition: oldHeadScreenPosition,
newBufferPosition: newHeadBufferPosition,
newScreenPosition: newHeadScreenPosition,
textChanged,
cursor: this.cursor
};
this.cursor.emitter.emit('did-change-position', cursorMovedEvent);
this.editor.cursorMoved(cursorMovedEvent);
}
const rangeChangedEvent = {
oldBufferRange: new Range(oldHeadBufferPosition, oldTailBufferPosition),
oldScreenRange: new Range(oldHeadScreenPosition, oldTailScreenPosition),
newBufferRange: this.getBufferRange(),
newScreenRange: this.getScreenRange(),
selection: this
};
this.emitter.emit('did-change-range', rangeChangedEvent);
this.editor.selectionRangeChanged(rangeChangedEvent);
}
markerDidDestroy() {
if (this.editor.isDestroyed()) return;
this.destroyed = true;
this.cursor.destroyed = true;
this.editor.removeSelection(this);
this.cursor.emitter.emit('did-destroy');
this.emitter.emit('did-destroy');
this.cursor.emitter.dispose();
this.emitter.dispose();
}
finalize() {
if (
!this.initialScreenRange ||
!this.initialScreenRange.isEqual(this.getScreenRange())
) {
this.initialScreenRange = null;
}
if (this.isEmpty()) {
this.wordwise = false;
this.linewise = false;
}
}
autoscroll(options) {
if (this.marker.hasTail()) {
this.editor.scrollToScreenRange(
this.getScreenRange(),
Object.assign({ reversed: this.isReversed() }, options)
);
} else {
this.cursor.autoscroll(options);
}
}
clearAutoscroll() {}
modifySelection(fn) {
this.retainSelection = true;
this.plantTail();
fn();
this.retainSelection = false;
}
plantTail() {
this.marker.plantTail();
}
};