提交 ed09ea34 编写于 作者: R rebornix

AutoIndent: Indent on paste

上级 0b80802d
......@@ -4,11 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import * as strings from 'vs/base/common/strings';
import { ICommonCodeEditor, IIdentifiedSingleEditOperation, ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon';
import { ICommonCodeEditor, IEditorContribution, IIdentifiedSingleEditOperation, ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel, EndOfLineSequence } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { editorAction, ServicesAccessor, IActionOptions, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { editorAction, ServicesAccessor, IActionOptions, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Range } from 'vs/editor/common/core/range';
......@@ -17,6 +18,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation';
import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
import { TextEdit } from 'vs/editor/common/modes';
export function shiftIndent(tabSize: number, indentation: string, count?: number): string {
count = count || 1;
......@@ -325,6 +327,233 @@ export class ReindentLinesAction extends EditorAction {
}
}
export class AutoIndentOnPasteCommand implements ICommand {
private _edits: TextEdit[];
private _newEol: EndOfLineSequence;
private _initialSelection: Selection;
private _selectionId: string;
constructor(edits: TextEdit[], initialSelection: Selection) {
this._initialSelection = initialSelection;
this._edits = [];
this._newEol = undefined;
for (let edit of edits) {
if (typeof edit.eol === 'number') {
this._newEol = edit.eol;
}
if (edit.range && typeof edit.text === 'string') {
this._edits.push(edit);
}
}
}
public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {
for (let edit of this._edits) {
builder.addEditOperation(Range.lift(edit.range), edit.text);
}
var selectionIsSet = false;
if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) {
if (this._edits[0].range.startColumn === this._initialSelection.endColumn &&
this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) {
selectionIsSet = true;
this._selectionId = builder.trackSelection(this._initialSelection, true);
} else if (this._edits[0].range.endColumn === this._initialSelection.startColumn &&
this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) {
selectionIsSet = true;
this._selectionId = builder.trackSelection(this._initialSelection, false);
}
}
if (!selectionIsSet) {
this._selectionId = builder.trackSelection(this._initialSelection);
}
}
public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection {
return helper.getTrackedSelection(this._selectionId);
}
}
@commonEditorContribution
export class AutoIndentOnPaste implements IEditorContribution {
private static ID = 'editor.contrib.autoIndentOnPaste';
private editor: ICommonCodeEditor;
private callOnDispose: IDisposable[];
private callOnModel: IDisposable[];
constructor(editor: ICommonCodeEditor) {
this.editor = editor;
this.callOnDispose = [];
this.callOnModel = [];
this.callOnDispose.push(editor.onDidChangeConfiguration(() => this.update()));
this.callOnDispose.push(editor.onDidChangeModel(() => this.update()));
this.callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update()));
}
private update(): void {
// clean up
this.callOnModel = dispose(this.callOnModel);
// we are disabled
if (!this.editor.getConfiguration().autoIndent) {
return;
}
// no model
if (!this.editor.getModel()) {
return;
}
this.callOnModel.push(this.editor.onDidPaste((range: Range) => {
this.trigger(range);
}));
}
private trigger(range: Range): void {
if (this.editor.getSelections().length > 1) {
return;
}
const model = this.editor.getModel();
const { tabSize, insertSpaces } = model.getOptions();
this.editor.pushUndoStop();
let textEdits: TextEdit[] = [];
let indentConverter = {
shiftIndent: (indentation) => {
let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + 1, tabSize);
let newIndentation = '';
for (let i = 0; i < desiredIndentCount; i++) {
newIndentation += '\t';
}
return newIndentation;
},
unshiftIndent: (indentation) => {
let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + 1, tabSize);
let newIndentation = '';
for (let i = 0; i < desiredIndentCount; i++) {
newIndentation += '\t';
}
return newIndentation;
}
};
let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(model, model.getLanguageIdentifier().id, range.startLineNumber, indentConverter);
if (indentOfFirstLine !== null) {
let firstLineText = model.getLineContent(range.startLineNumber);
let oldIndentation = strings.getLeadingWhitespace(firstLineText);
let newSpaceCnt = this.getSpaceCnt(indentOfFirstLine, tabSize);
let oldSpaceCnt = this.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
let newIndent = this.generateIndent(newSpaceCnt, tabSize, insertSpaces);
textEdits.push({
range: new Range(range.startLineNumber, 1, range.startLineNumber, oldIndentation.length + 1),
text: newIndent
});
firstLineText = newIndent + firstLineText.substr(oldIndentation.length + 1);
}
if (range.startLineNumber !== range.endLineNumber) {
let virtualModel = {
getLineTokens: (lineNumber: number) => {
return model.getLineTokens(lineNumber);
},
getLanguageIdentifier: () => {
return model.getLanguageIdentifier();
},
getLanguageIdAtPosition: (lineNumber: number, column: number) => {
return model.getLanguageIdAtPosition(lineNumber, column);
},
getLineContent: (lineNumber) => {
if (lineNumber === range.startLineNumber) {
return firstLineText;
} else {
return model.getLineContent(lineNumber);
}
}
};
let indentOfSecondLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdentifier().id, range.startLineNumber + 1, indentConverter);
let newSpaceCntOfSecondLine = this.getSpaceCnt(indentOfSecondLine, tabSize);
let oldSpaceCntOfSecondLine = this.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(range.startLineNumber + 1)), tabSize);
if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) {
let spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine;
for (let i = range.startLineNumber + 1; i <= range.endLineNumber; i++) {
let lineContent = model.getLineContent(i);
let originalIndent = strings.getLeadingWhitespace(lineContent);
let originalSpacesCnt = this.getSpaceCnt(originalIndent, tabSize);
let newSpacesCnt = originalSpacesCnt + spaceCntOffset;
let newIndent = this.generateIndent(newSpacesCnt, tabSize, insertSpaces);
if (newIndent !== originalIndent) {
textEdits.push({
range: new Range(i, 1, i, originalIndent.length),
text: newIndent
});
}
}
}
}
}
let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection());
this.editor.executeCommand('autoIndentOnPaste', cmd);
this.editor.pushUndoStop();
}
public getId(): string {
return AutoIndentOnPaste.ID;
}
public dispose(): void {
this.callOnDispose = dispose(this.callOnDispose);
this.callOnModel = dispose(this.callOnModel);
}
private getSpaceCnt(str, tabSize) {
let spacesCnt = 0;
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === '\t') {
spacesCnt += tabSize;
} else {
spacesCnt++;
}
}
return spacesCnt;
}
private generateIndent(spacesCnt: number, tabSize, insertSpaces) {
spacesCnt = spacesCnt < 0 ? 0 : spacesCnt;
let result = '';
if (!insertSpaces) {
let tabsCnt = Math.floor(spacesCnt / tabSize);
spacesCnt = spacesCnt % tabSize;
for (let i = 0; i < tabsCnt; i++) {
result += '\t';
}
}
for (let i = 0; i < spacesCnt; i++) {
result += ' ';
}
return result;
}
}
function getIndentationEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder, tabSize: number, tabsToSpaces: boolean): void {
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
// Model is empty
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册