mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-21 04:20:11 -04:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
37
src/vs/editor/contrib/indentation/common/indentUtils.ts
Normal file
37
src/vs/editor/contrib/indentation/common/indentUtils.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export function 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;
|
||||
}
|
||||
|
||||
export function 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;
|
||||
}
|
||||
620
src/vs/editor/contrib/indentation/common/indentation.ts
Normal file
620
src/vs/editor/contrib/indentation/common/indentation.ts
Normal file
@@ -0,0 +1,620 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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, IEditorContribution, IIdentifiedSingleEditOperation, ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel, EndOfLineSequence } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
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';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
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, StandardTokenType } from 'vs/editor/common/modes';
|
||||
import * as IndentUtil from './indentUtils';
|
||||
|
||||
export function shiftIndent(tabSize: number, indentation: string, count?: number): string {
|
||||
count = count || 1;
|
||||
let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + count, tabSize);
|
||||
let newIndentation = '';
|
||||
for (let i = 0; i < desiredIndentCount; i++) {
|
||||
newIndentation += '\t';
|
||||
}
|
||||
|
||||
return newIndentation;
|
||||
}
|
||||
|
||||
export function unshiftIndent(tabSize: number, indentation: string, count?: number): string {
|
||||
count = count || 1;
|
||||
let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + count, tabSize);
|
||||
let newIndentation = '';
|
||||
for (let i = 0; i < desiredIndentCount; i++) {
|
||||
newIndentation += '\t';
|
||||
}
|
||||
|
||||
return newIndentation;
|
||||
}
|
||||
|
||||
export function getReindentEditOperations(model: ITokenizedModel, startLineNumber: number, endLineNumber: number, inheritedIndent?: string): IIdentifiedSingleEditOperation[] {
|
||||
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
|
||||
// Model is empty
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let indentationRules = LanguageConfigurationRegistry.getIndentationRules(model.getLanguageIdentifier().id);
|
||||
if (!indentationRules) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
endLineNumber = Math.min(endLineNumber, model.getLineCount());
|
||||
|
||||
// Skip `unIndentedLinePattern` lines
|
||||
while (startLineNumber <= endLineNumber) {
|
||||
if (!indentationRules.unIndentedLinePattern) {
|
||||
break;
|
||||
}
|
||||
|
||||
let text = model.getLineContent(startLineNumber);
|
||||
if (!indentationRules.unIndentedLinePattern.test(text)) {
|
||||
break;
|
||||
}
|
||||
|
||||
startLineNumber++;
|
||||
}
|
||||
|
||||
if (startLineNumber > endLineNumber - 1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let { tabSize, insertSpaces } = model.getOptions();
|
||||
let indentEdits = [];
|
||||
|
||||
// indentation being passed to lines below
|
||||
let globalIndent: string;
|
||||
|
||||
// Calculate indentation for the first line
|
||||
// If there is no passed-in indentation, we use the indentation of the first line as base.
|
||||
let currentLineText = model.getLineContent(startLineNumber);
|
||||
let adjustedLineContent = currentLineText;
|
||||
if (inheritedIndent !== undefined && inheritedIndent !== null) {
|
||||
globalIndent = inheritedIndent;
|
||||
let oldIndentation = strings.getLeadingWhitespace(currentLineText);
|
||||
|
||||
adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
|
||||
if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
|
||||
globalIndent = unshiftIndent(tabSize, globalIndent);
|
||||
adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
|
||||
|
||||
}
|
||||
if (currentLineText !== adjustedLineContent) {
|
||||
indentEdits.push(EditOperation.replace(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(globalIndent, tabSize, insertSpaces)));
|
||||
}
|
||||
} else {
|
||||
globalIndent = strings.getLeadingWhitespace(currentLineText);
|
||||
}
|
||||
|
||||
// idealIndentForNextLine doesn't equal globalIndent when there is a line matching `indentNextLinePattern`.
|
||||
let idealIndentForNextLine: string = globalIndent;
|
||||
|
||||
if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
|
||||
idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine);
|
||||
globalIndent = shiftIndent(tabSize, globalIndent);
|
||||
}
|
||||
else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
|
||||
idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine);
|
||||
}
|
||||
|
||||
startLineNumber++;
|
||||
|
||||
// Calculate indentation adjustment for all following lines
|
||||
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
|
||||
let text = model.getLineContent(lineNumber);
|
||||
let oldIndentation = strings.getLeadingWhitespace(text);
|
||||
let adjustedLineContent = idealIndentForNextLine + text.substring(oldIndentation.length);
|
||||
|
||||
if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
|
||||
idealIndentForNextLine = unshiftIndent(tabSize, idealIndentForNextLine);
|
||||
globalIndent = unshiftIndent(tabSize, globalIndent);
|
||||
}
|
||||
|
||||
if (oldIndentation !== idealIndentForNextLine) {
|
||||
indentEdits.push(EditOperation.replace(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(idealIndentForNextLine, tabSize, insertSpaces)));
|
||||
}
|
||||
|
||||
// calculate idealIndentForNextLine
|
||||
if (indentationRules.unIndentedLinePattern && indentationRules.unIndentedLinePattern.test(text)) {
|
||||
// In reindent phase, if the line matches `unIndentedLinePattern` we inherit indentation from above lines
|
||||
// but don't change globalIndent and idealIndentForNextLine.
|
||||
continue;
|
||||
} else if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
|
||||
globalIndent = shiftIndent(tabSize, globalIndent);
|
||||
idealIndentForNextLine = globalIndent;
|
||||
} else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
|
||||
idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine);
|
||||
} else {
|
||||
idealIndentForNextLine = globalIndent;
|
||||
}
|
||||
}
|
||||
|
||||
return indentEdits;
|
||||
}
|
||||
|
||||
@editorAction
|
||||
export class IndentationToSpacesAction extends EditorAction {
|
||||
public static ID = 'editor.action.indentationToSpaces';
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: IndentationToSpacesAction.ID,
|
||||
label: nls.localize('indentationToSpaces', "Convert Indentation to Spaces"),
|
||||
alias: 'Convert Indentation to Spaces',
|
||||
precondition: EditorContextKeys.writable
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
|
||||
let model = editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
let modelOpts = model.getOptions();
|
||||
const command = new IndentationToSpacesCommand(editor.getSelection(), modelOpts.tabSize);
|
||||
|
||||
editor.pushUndoStop();
|
||||
editor.executeCommands(this.id, [command]);
|
||||
editor.pushUndoStop();
|
||||
|
||||
model.updateOptions({
|
||||
insertSpaces: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@editorAction
|
||||
export class IndentationToTabsAction extends EditorAction {
|
||||
public static ID = 'editor.action.indentationToTabs';
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: IndentationToTabsAction.ID,
|
||||
label: nls.localize('indentationToTabs', "Convert Indentation to Tabs"),
|
||||
alias: 'Convert Indentation to Tabs',
|
||||
precondition: EditorContextKeys.writable
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
|
||||
let model = editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
let modelOpts = model.getOptions();
|
||||
const command = new IndentationToTabsCommand(editor.getSelection(), modelOpts.tabSize);
|
||||
|
||||
editor.pushUndoStop();
|
||||
editor.executeCommands(this.id, [command]);
|
||||
editor.pushUndoStop();
|
||||
|
||||
model.updateOptions({
|
||||
insertSpaces: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ChangeIndentationSizeAction extends EditorAction {
|
||||
|
||||
constructor(private insertSpaces: boolean, opts: IActionOptions) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
|
||||
const quickOpenService = accessor.get(IQuickOpenService);
|
||||
const modelService = accessor.get(IModelService);
|
||||
|
||||
let model = editor.getModel();
|
||||
if (!model) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let creationOpts = modelService.getCreationOptions(model.getLanguageIdentifier().language, model.uri);
|
||||
const picks = [1, 2, 3, 4, 5, 6, 7, 8].map(n => ({
|
||||
id: n.toString(),
|
||||
label: n.toString(),
|
||||
// add description for tabSize value set in the configuration
|
||||
description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : null
|
||||
}));
|
||||
|
||||
// auto focus the tabSize set for the current editor
|
||||
const autoFocusIndex = Math.min(model.getOptions().tabSize - 1, 7);
|
||||
|
||||
return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).then(() =>
|
||||
quickOpenService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), autoFocus: { autoFocusIndex } }).then(pick => {
|
||||
if (pick) {
|
||||
model.updateOptions({
|
||||
tabSize: parseInt(pick.label, 10),
|
||||
insertSpaces: this.insertSpaces
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@editorAction
|
||||
export class IndentUsingTabs extends ChangeIndentationSizeAction {
|
||||
|
||||
public static ID = 'editor.action.indentUsingTabs';
|
||||
|
||||
constructor() {
|
||||
super(false, {
|
||||
id: IndentUsingTabs.ID,
|
||||
label: nls.localize('indentUsingTabs', "Indent Using Tabs"),
|
||||
alias: 'Indent Using Tabs',
|
||||
precondition: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@editorAction
|
||||
export class IndentUsingSpaces extends ChangeIndentationSizeAction {
|
||||
|
||||
public static ID = 'editor.action.indentUsingSpaces';
|
||||
|
||||
constructor() {
|
||||
super(true, {
|
||||
id: IndentUsingSpaces.ID,
|
||||
label: nls.localize('indentUsingSpaces', "Indent Using Spaces"),
|
||||
alias: 'Indent Using Spaces',
|
||||
precondition: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@editorAction
|
||||
export class DetectIndentation extends EditorAction {
|
||||
|
||||
public static ID = 'editor.action.detectIndentation';
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: DetectIndentation.ID,
|
||||
label: nls.localize('detectIndentation', "Detect Indentation from Content"),
|
||||
alias: 'Detect Indentation from Content',
|
||||
precondition: null
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
|
||||
const modelService = accessor.get(IModelService);
|
||||
|
||||
let model = editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
let creationOpts = modelService.getCreationOptions(model.getLanguageIdentifier().language, model.uri);
|
||||
model.detectIndentation(creationOpts.insertSpaces, creationOpts.tabSize);
|
||||
}
|
||||
}
|
||||
|
||||
@editorAction
|
||||
export class ReindentLinesAction extends EditorAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.action.reindentlines',
|
||||
label: nls.localize('editor.reindentlines', "Reindent Lines"),
|
||||
alias: 'Reindent Lines',
|
||||
precondition: EditorContextKeys.writable
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
|
||||
let model = editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
let edits = getReindentEditOperations(model, 1, model.getLineCount());
|
||||
if (edits) {
|
||||
editor.executeEdits(this.id, edits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 || this.editor.getConfiguration().contribInfo.formatOnPaste) {
|
||||
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();
|
||||
if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) {
|
||||
return;
|
||||
}
|
||||
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 startLineNumber = range.startLineNumber;
|
||||
|
||||
while (startLineNumber <= range.endLineNumber) {
|
||||
if (this.shouldIgnoreLine(model, startLineNumber)) {
|
||||
startLineNumber++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (startLineNumber > range.endLineNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
let firstLineText = model.getLineContent(startLineNumber);
|
||||
if (!/\S/.test(firstLineText.substring(0, range.startColumn - 1))) {
|
||||
let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(model, model.getLanguageIdentifier().id, startLineNumber, indentConverter);
|
||||
|
||||
if (indentOfFirstLine !== null) {
|
||||
let oldIndentation = strings.getLeadingWhitespace(firstLineText);
|
||||
let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize);
|
||||
let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
|
||||
|
||||
if (newSpaceCnt !== oldSpaceCnt) {
|
||||
let newIndent = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces);
|
||||
textEdits.push({
|
||||
range: new Range(startLineNumber, 1, startLineNumber, oldIndentation.length + 1),
|
||||
text: newIndent
|
||||
});
|
||||
firstLineText = newIndent + firstLineText.substr(oldIndentation.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (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 === startLineNumber) {
|
||||
return firstLineText;
|
||||
} else {
|
||||
return model.getLineContent(lineNumber);
|
||||
}
|
||||
}
|
||||
};
|
||||
let indentOfSecondLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdentifier().id, startLineNumber + 1, indentConverter);
|
||||
if (indentOfSecondLine !== null) {
|
||||
let newSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(indentOfSecondLine, tabSize);
|
||||
let oldSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize);
|
||||
|
||||
if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) {
|
||||
let spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine;
|
||||
for (let i = startLineNumber + 1; i <= range.endLineNumber; i++) {
|
||||
let lineContent = model.getLineContent(i);
|
||||
let originalIndent = strings.getLeadingWhitespace(lineContent);
|
||||
let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize);
|
||||
let newSpacesCnt = originalSpacesCnt + spaceCntOffset;
|
||||
let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces);
|
||||
|
||||
if (newIndent !== originalIndent) {
|
||||
textEdits.push({
|
||||
range: new Range(i, 1, i, originalIndent.length + 1),
|
||||
text: newIndent
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection());
|
||||
this.editor.executeCommand('autoIndentOnPaste', cmd);
|
||||
this.editor.pushUndoStop();
|
||||
}
|
||||
|
||||
private shouldIgnoreLine(model: ITokenizedModel, lineNumber: number): boolean {
|
||||
model.forceTokenization(lineNumber);
|
||||
let nonWhiteSpaceColumn = model.getLineFirstNonWhitespaceColumn(lineNumber);
|
||||
if (nonWhiteSpaceColumn === 0) {
|
||||
return true;
|
||||
}
|
||||
let tokens = model.getLineTokens(lineNumber);
|
||||
if (tokens.getTokenCount() > 0) {
|
||||
let firstNonWhiteSpaceToken = tokens.findTokenAtOffset(nonWhiteSpaceColumn);
|
||||
if (firstNonWhiteSpaceToken && firstNonWhiteSpaceToken.tokenType === StandardTokenType.Comment) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return AutoIndentOnPaste.ID;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.callOnDispose = dispose(this.callOnDispose);
|
||||
this.callOnModel = dispose(this.callOnModel);
|
||||
}
|
||||
}
|
||||
|
||||
function getIndentationEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder, tabSize: number, tabsToSpaces: boolean): void {
|
||||
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
|
||||
// Model is empty
|
||||
return;
|
||||
}
|
||||
|
||||
let spaces = '';
|
||||
for (let i = 0; i < tabSize; i++) {
|
||||
spaces += ' ';
|
||||
}
|
||||
|
||||
const content = model.getLinesContent();
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
let lastIndentationColumn = model.getLineFirstNonWhitespaceColumn(i + 1);
|
||||
if (lastIndentationColumn === 0) {
|
||||
lastIndentationColumn = model.getLineMaxColumn(i + 1);
|
||||
}
|
||||
|
||||
const text = (tabsToSpaces ? content[i].substr(0, lastIndentationColumn).replace(/\t/ig, spaces) :
|
||||
content[i].substr(0, lastIndentationColumn).replace(new RegExp(spaces, 'gi'), '\t')) +
|
||||
content[i].substr(lastIndentationColumn);
|
||||
|
||||
builder.addEditOperation(new Range(i + 1, 1, i + 1, model.getLineMaxColumn(i + 1)), text);
|
||||
}
|
||||
}
|
||||
|
||||
export class IndentationToSpacesCommand implements ICommand {
|
||||
|
||||
private selectionId: string;
|
||||
|
||||
constructor(private selection: Selection, private tabSize: number) { }
|
||||
|
||||
public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {
|
||||
this.selectionId = builder.trackSelection(this.selection);
|
||||
getIndentationEditOperations(model, builder, this.tabSize, true);
|
||||
}
|
||||
|
||||
public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection {
|
||||
return helper.getTrackedSelection(this.selectionId);
|
||||
}
|
||||
}
|
||||
|
||||
export class IndentationToTabsCommand implements ICommand {
|
||||
|
||||
private selectionId: string;
|
||||
|
||||
constructor(private selection: Selection, private tabSize: number) { }
|
||||
|
||||
public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {
|
||||
this.selectionId = builder.trackSelection(this.selection);
|
||||
getIndentationEditOperations(model, builder, this.tabSize, false);
|
||||
}
|
||||
|
||||
public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection {
|
||||
return helper.getTrackedSelection(this.selectionId);
|
||||
}
|
||||
}
|
||||
172
src/vs/editor/contrib/indentation/test/indentation.test.ts
Normal file
172
src/vs/editor/contrib/indentation/test/indentation.test.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IndentationToSpacesCommand, IndentationToTabsCommand } from 'vs/editor/contrib/indentation/common/indentation';
|
||||
import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils';
|
||||
|
||||
function testIndentationToSpacesCommand(lines: string[], selection: Selection, tabSize: number, expectedLines: string[], expectedSelection: Selection): void {
|
||||
testCommand(lines, null, selection, (sel) => new IndentationToSpacesCommand(sel, tabSize), expectedLines, expectedSelection);
|
||||
}
|
||||
|
||||
function testIndentationToTabsCommand(lines: string[], selection: Selection, tabSize: number, expectedLines: string[], expectedSelection: Selection): void {
|
||||
testCommand(lines, null, selection, (sel) => new IndentationToTabsCommand(sel, tabSize), expectedLines, expectedSelection);
|
||||
}
|
||||
|
||||
suite('Editor Contrib - Indentation to Spaces', () => {
|
||||
|
||||
test('single tabs only at start of line', function () {
|
||||
testIndentationToSpacesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'\tfourth line',
|
||||
'\tfifth'
|
||||
],
|
||||
new Selection(2, 3, 2, 3),
|
||||
4,
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
' fourth line',
|
||||
' fifth'
|
||||
],
|
||||
new Selection(2, 3, 2, 3)
|
||||
);
|
||||
});
|
||||
|
||||
test('multiple tabs at start of line', function () {
|
||||
testIndentationToSpacesCommand(
|
||||
[
|
||||
'\t\tfirst',
|
||||
'\tsecond line',
|
||||
'\t\t\t third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5),
|
||||
3,
|
||||
[
|
||||
' first',
|
||||
' second line',
|
||||
' third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('multiple tabs', function () {
|
||||
testIndentationToSpacesCommand(
|
||||
[
|
||||
'\t\tfirst\t',
|
||||
'\tsecond \t line \t',
|
||||
'\t\t\t third line',
|
||||
' \tfourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5),
|
||||
2,
|
||||
[
|
||||
' first\t',
|
||||
' second \t line \t',
|
||||
' third line',
|
||||
' fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('empty lines', function () {
|
||||
testIndentationToSpacesCommand(
|
||||
[
|
||||
'\t\t\t',
|
||||
'\t',
|
||||
'\t\t'
|
||||
],
|
||||
new Selection(1, 4, 1, 4),
|
||||
2,
|
||||
[
|
||||
' ',
|
||||
' ',
|
||||
' '
|
||||
],
|
||||
new Selection(1, 4, 1, 4)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Editor Contrib - Indentation to Tabs', () => {
|
||||
|
||||
test('spaces only at start of line', function () {
|
||||
testIndentationToTabsCommand(
|
||||
[
|
||||
' first',
|
||||
'second line',
|
||||
' third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 3, 2, 3),
|
||||
4,
|
||||
[
|
||||
'\tfirst',
|
||||
'second line',
|
||||
'\tthird line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 3, 2, 3)
|
||||
);
|
||||
});
|
||||
|
||||
test('multiple spaces at start of line', function () {
|
||||
testIndentationToTabsCommand(
|
||||
[
|
||||
'first',
|
||||
' second line',
|
||||
' third line',
|
||||
'fourth line',
|
||||
' fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5),
|
||||
3,
|
||||
[
|
||||
'first',
|
||||
'\tsecond line',
|
||||
'\t\t\t third line',
|
||||
'fourth line',
|
||||
'\t fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('multiple spaces', function () {
|
||||
testIndentationToTabsCommand(
|
||||
[
|
||||
' first ',
|
||||
' second line \t',
|
||||
' third line',
|
||||
' fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5),
|
||||
2,
|
||||
[
|
||||
'\t\t\tfirst ',
|
||||
'\tsecond line \t',
|
||||
'\t\t\t third line',
|
||||
'\t fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user