in patched-vscode/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts [43:282]
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
const getLanguageId = () => {
return model.getLanguageId();
};
const getLanguageIdAtPosition = (lineNumber: number, column: number) => {
return model.getLanguageIdAtPosition(lineNumber, column);
};
const modelLineCount = model.getLineCount();
if (this._isMovingDown && this._selection.endLineNumber === modelLineCount) {
this._selectionId = builder.trackSelection(this._selection);
return;
}
if (!this._isMovingDown && this._selection.startLineNumber === 1) {
this._selectionId = builder.trackSelection(this._selection);
return;
}
this._moveEndPositionDown = false;
let s = this._selection;
if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) {
this._moveEndPositionDown = true;
s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1));
}
const { tabSize, indentSize, insertSpaces } = model.getOptions();
const indentConverter = this.buildIndentConverter(tabSize, indentSize, insertSpaces);
if (s.startLineNumber === s.endLineNumber && model.getLineMaxColumn(s.startLineNumber) === 1) {
// Current line is empty
const lineNumber = s.startLineNumber;
const otherLineNumber = (this._isMovingDown ? lineNumber + 1 : lineNumber - 1);
if (model.getLineMaxColumn(otherLineNumber) === 1) {
// Other line number is empty too, so no editing is needed
// Add a no-op to force running by the model
builder.addEditOperation(new Range(1, 1, 1, 1), null);
} else {
// Type content from other line number on line number
builder.addEditOperation(new Range(lineNumber, 1, lineNumber, 1), model.getLineContent(otherLineNumber));
// Remove content from other line number
builder.addEditOperation(new Range(otherLineNumber, 1, otherLineNumber, model.getLineMaxColumn(otherLineNumber)), null);
}
// Track selection at the other line number
s = new Selection(otherLineNumber, 1, otherLineNumber, 1);
} else {
let movingLineNumber: number;
let movingLineText: string;
if (this._isMovingDown) {
movingLineNumber = s.endLineNumber + 1;
movingLineText = model.getLineContent(movingLineNumber);
// Delete line that needs to be moved
builder.addEditOperation(new Range(movingLineNumber - 1, model.getLineMaxColumn(movingLineNumber - 1), movingLineNumber, model.getLineMaxColumn(movingLineNumber)), null);
let insertingText = movingLineText;
if (this.shouldAutoIndent(model, s)) {
const movingLineMatchResult = this.matchEnterRule(model, indentConverter, tabSize, movingLineNumber, s.startLineNumber - 1);
// if s.startLineNumber - 1 matches onEnter rule, we still honor that.
if (movingLineMatchResult !== null) {
const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber));
const newSpaceCnt = movingLineMatchResult + indentUtils.getSpaceCnt(oldIndentation, tabSize);
const newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
insertingText = newIndentation + this.trimStart(movingLineText);
} else {
// no enter rule matches, let's check indentatin rules then.
const virtualModel: IVirtualModel = {
tokenization: {
getLineTokens: (lineNumber: number) => {
if (lineNumber === s.startLineNumber) {
return model.tokenization.getLineTokens(movingLineNumber);
} else {
return model.tokenization.getLineTokens(lineNumber);
}
},
getLanguageId,
getLanguageIdAtPosition,
},
getLineContent: (lineNumber: number) => {
if (lineNumber === s.startLineNumber) {
return model.getLineContent(movingLineNumber);
} else {
return model.getLineContent(lineNumber);
}
},
};
const indentOfMovingLine = getGoodIndentForLine(
this._autoIndent,
virtualModel,
model.getLanguageIdAtPosition(movingLineNumber, 1),
s.startLineNumber,
indentConverter,
this._languageConfigurationService
);
if (indentOfMovingLine !== null) {
const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber));
const newSpaceCnt = indentUtils.getSpaceCnt(indentOfMovingLine, tabSize);
const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
const newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
insertingText = newIndentation + this.trimStart(movingLineText);
}
}
}
// add edit operations for moving line first to make sure it's executed after we make indentation change
// to s.startLineNumber
builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n');
const ret = this.matchEnterRuleMovingDown(model, indentConverter, tabSize, s.startLineNumber, movingLineNumber, insertingText);
// check if the line being moved before matches onEnter rules, if so let's adjust the indentation by onEnter rules.
if (ret !== null) {
if (ret !== 0) {
this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, ret);
}
} else {
// it doesn't match onEnter rules, let's check indentation rules then.
const virtualModel: IVirtualModel = {
tokenization: {
getLineTokens: (lineNumber: number) => {
if (lineNumber === s.startLineNumber) {
return model.tokenization.getLineTokens(movingLineNumber);
} else if (lineNumber >= s.startLineNumber + 1 && lineNumber <= s.endLineNumber + 1) {
return model.tokenization.getLineTokens(lineNumber - 1);
} else {
return model.tokenization.getLineTokens(lineNumber);
}
},
getLanguageId,
getLanguageIdAtPosition,
},
getLineContent: (lineNumber: number) => {
if (lineNumber === s.startLineNumber) {
return insertingText;
} else if (lineNumber >= s.startLineNumber + 1 && lineNumber <= s.endLineNumber + 1) {
return model.getLineContent(lineNumber - 1);
} else {
return model.getLineContent(lineNumber);
}
},
};
const newIndentatOfMovingBlock = getGoodIndentForLine(
this._autoIndent,
virtualModel,
model.getLanguageIdAtPosition(movingLineNumber, 1),
s.startLineNumber + 1,
indentConverter,
this._languageConfigurationService
);
if (newIndentatOfMovingBlock !== null) {
const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
const newSpaceCnt = indentUtils.getSpaceCnt(newIndentatOfMovingBlock, tabSize);
const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
const spaceCntOffset = newSpaceCnt - oldSpaceCnt;
this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, spaceCntOffset);
}
}
}
} else {
// Insert line that needs to be moved before
builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n');
}
} else {
movingLineNumber = s.startLineNumber - 1;
movingLineText = model.getLineContent(movingLineNumber);
// Delete line that needs to be moved
builder.addEditOperation(new Range(movingLineNumber, 1, movingLineNumber + 1, 1), null);
// Insert line that needs to be moved after
builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + movingLineText);
if (this.shouldAutoIndent(model, s)) {
const virtualModel: IVirtualModel = {
tokenization: {
getLineTokens: (lineNumber: number) => {
if (lineNumber === movingLineNumber) {
return model.tokenization.getLineTokens(s.startLineNumber);
} else {
return model.tokenization.getLineTokens(lineNumber);
}
},
getLanguageId,
getLanguageIdAtPosition,
},
getLineContent: (lineNumber: number) => {
if (lineNumber === movingLineNumber) {
return model.getLineContent(s.startLineNumber);
} else {
return model.getLineContent(lineNumber);
}
},
};
const ret = this.matchEnterRule(model, indentConverter, tabSize, s.startLineNumber, s.startLineNumber - 2);
// check if s.startLineNumber - 2 matches onEnter rules, if so adjust the moving block by onEnter rules.
if (ret !== null) {
if (ret !== 0) {
this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, ret);
}
} else {
// it doesn't match any onEnter rule, let's check indentation rules then.
const indentOfFirstLine = getGoodIndentForLine(
this._autoIndent,
virtualModel,
model.getLanguageIdAtPosition(s.startLineNumber, 1),
movingLineNumber,
indentConverter,
this._languageConfigurationService
);
if (indentOfFirstLine !== null) {
// adjust the indentation of the moving block
const oldIndent = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
const newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize);
const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndent, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
const spaceCntOffset = newSpaceCnt - oldSpaceCnt;
this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, spaceCntOffset);
}
}
}
}
}
}
this._selectionId = builder.trackSelection(s);
}