mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-04-01 01:20:31 -04:00
Merge from vscode 011858832762aaff245b2336fb1c38166e7a10fb (#4663)
This commit is contained in:
@@ -163,7 +163,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
const indent = TextModel.computeIndentLevel(lineContent, tabSize);
|
||||
const lineHasSpace = config.fontInfo.spaceWidth * indent > 22;
|
||||
const isFolded = (lineNumber) => {
|
||||
const isFolded = (lineNumber: number) => {
|
||||
return lineNumber > 2 && this._editor.getTopForLineNumber(lineNumber) === this._editor.getTopForLineNumber(lineNumber - 1);
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class CodeLensViewZone implements editorBrowser.IViewZone {
|
||||
|
||||
afterLineNumber: number;
|
||||
|
||||
private _lastHeight: number;
|
||||
private _lastHeight?: number;
|
||||
private readonly _onHeight: Function;
|
||||
|
||||
constructor(afterLineNumber: number, onHeight: Function) {
|
||||
|
||||
@@ -67,7 +67,7 @@ suite('FindController', () => {
|
||||
getBoolean: (key: string) => !!queryState[key],
|
||||
getNumber: (key: string) => undefined,
|
||||
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
|
||||
remove: (key) => undefined
|
||||
remove: () => undefined
|
||||
} as any);
|
||||
|
||||
if (platform.isMacintosh) {
|
||||
@@ -442,7 +442,7 @@ suite('FindController query options persistence', () => {
|
||||
getBoolean: (key: string) => !!queryState[key],
|
||||
getNumber: (key: string) => undefined,
|
||||
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
|
||||
remove: (key) => undefined
|
||||
remove: () => undefined
|
||||
} as any);
|
||||
|
||||
test('matchCase', () => {
|
||||
|
||||
@@ -66,7 +66,7 @@ export class FoldingModel {
|
||||
public update(newRegions: FoldingRegions, blockedLineNumers: number[] = []): void {
|
||||
let newEditorDecorations: IModelDeltaDecoration[] = [];
|
||||
|
||||
let isBlocked = (startLineNumber, endLineNumber) => {
|
||||
let isBlocked = (startLineNumber: number, endLineNumber: number) => {
|
||||
for (let blockedLineNumber of blockedLineNumers) {
|
||||
if (startLineNumber < blockedLineNumber && blockedLineNumber <= endLineNumber) { // first line is visible
|
||||
return true;
|
||||
|
||||
@@ -3,138 +3,258 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions, TextEdit } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { first } from 'vs/base/common/async';
|
||||
import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState';
|
||||
import { IActiveCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerLanguageCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { ISingleEditOperation, ITextModel } from 'vs/editor/common/model';
|
||||
import { DocumentFormattingEditProvider, DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProvider, DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry, TextEdit } from 'vs/editor/common/modes';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export const enum FormatMode {
|
||||
Auto = 1,
|
||||
Manual = 2,
|
||||
}
|
||||
export function alertFormattingEdits(edits: ISingleEditOperation[]): void {
|
||||
|
||||
export const enum FormatKind {
|
||||
Document = 8,
|
||||
Range = 16,
|
||||
OnType = 32,
|
||||
}
|
||||
edits = edits.filter(edit => edit.range);
|
||||
if (!edits.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
export interface IFormatterConflictCallback {
|
||||
(extensionIds: (ExtensionIdentifier | undefined)[], model: ITextModel, mode: number): void;
|
||||
}
|
||||
|
||||
let _conflictResolver: IFormatterConflictCallback | undefined;
|
||||
|
||||
export function setFormatterConflictCallback(callback: IFormatterConflictCallback): IDisposable {
|
||||
let oldCallback = _conflictResolver;
|
||||
_conflictResolver = callback;
|
||||
return {
|
||||
dispose() {
|
||||
if (oldCallback) {
|
||||
_conflictResolver = oldCallback;
|
||||
oldCallback = undefined;
|
||||
}
|
||||
let { range } = edits[0];
|
||||
for (let i = 1; i < edits.length; i++) {
|
||||
range = Range.plusRange(range, edits[i].range);
|
||||
}
|
||||
const { startLineNumber, endLineNumber } = range;
|
||||
if (startLineNumber === endLineNumber) {
|
||||
if (edits.length === 1) {
|
||||
alert(nls.localize('hint11', "Made 1 formatting edit on line {0}", startLineNumber));
|
||||
} else {
|
||||
alert(nls.localize('hintn1', "Made {0} formatting edits on line {1}", edits.length, startLineNumber));
|
||||
}
|
||||
} else {
|
||||
if (edits.length === 1) {
|
||||
alert(nls.localize('hint1n', "Made 1 formatting edit between lines {0} and {1}", startLineNumber, endLineNumber));
|
||||
} else {
|
||||
alert(nls.localize('hintnn', "Made {0} formatting edits between lines {1} and {2}", edits.length, startLineNumber, endLineNumber));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function invokeFormatterCallback<T extends { extensionId?: ExtensionIdentifier }>(formatter: T[], model: ITextModel, mode: number): void {
|
||||
if (_conflictResolver) {
|
||||
const ids = formatter.map(formatter => formatter.extensionId);
|
||||
_conflictResolver(ids, model, mode);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDocumentRangeFormattingEdits(
|
||||
telemetryService: ITelemetryService,
|
||||
export function getRealAndSyntheticDocumentFormattersOrdered(model: ITextModel): DocumentFormattingEditProvider[] {
|
||||
const result: DocumentFormattingEditProvider[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
// (1) add all document formatter
|
||||
const docFormatter = DocumentFormattingEditProviderRegistry.ordered(model);
|
||||
for (const formatter of docFormatter) {
|
||||
result.push(formatter);
|
||||
if (formatter.extensionId) {
|
||||
seen.add(ExtensionIdentifier.toKey(formatter.extensionId));
|
||||
}
|
||||
}
|
||||
|
||||
// (2) add all range formatter as document formatter (unless the same extension already did that)
|
||||
const rangeFormatter = DocumentRangeFormattingEditProviderRegistry.ordered(model);
|
||||
for (const formatter of rangeFormatter) {
|
||||
if (formatter.extensionId) {
|
||||
if (seen.has(ExtensionIdentifier.toKey(formatter.extensionId))) {
|
||||
continue;
|
||||
}
|
||||
seen.add(ExtensionIdentifier.toKey(formatter.extensionId));
|
||||
}
|
||||
result.push({
|
||||
displayName: formatter.displayName,
|
||||
extensionId: formatter.extensionId,
|
||||
provideDocumentFormattingEdits(model, options, token) {
|
||||
return formatter.provideDocumentRangeFormattingEdits(model, model.getFullModelRange(), options, token);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function formatDocumentRangeWithProvider(
|
||||
accessor: ServicesAccessor,
|
||||
provider: DocumentRangeFormattingEditProvider,
|
||||
editorOrModel: ITextModel | IActiveCodeEditor,
|
||||
range: Range,
|
||||
token: CancellationToken
|
||||
): Promise<boolean> {
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
|
||||
let model: ITextModel;
|
||||
let validate: () => boolean;
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
model = editorOrModel.getModel();
|
||||
const state = new EditorState(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
validate = () => state.validate(editorOrModel);
|
||||
} else {
|
||||
model = editorOrModel;
|
||||
const versionNow = editorOrModel.getVersionId();
|
||||
validate = () => versionNow === editorOrModel.getVersionId();
|
||||
}
|
||||
|
||||
const rawEdits = await provider.provideDocumentRangeFormattingEdits(
|
||||
model,
|
||||
range,
|
||||
model.getFormattingOptions(),
|
||||
token
|
||||
);
|
||||
|
||||
const edits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits);
|
||||
|
||||
if (!validate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!edits || edits.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
// use editor to apply edits
|
||||
FormattingEdit.execute(editorOrModel, edits);
|
||||
alertFormattingEdits(edits);
|
||||
editorOrModel.pushUndoStop();
|
||||
editorOrModel.focus();
|
||||
editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate);
|
||||
|
||||
} else {
|
||||
// use model to apply edits
|
||||
const [{ range }] = edits;
|
||||
const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
|
||||
model.pushEditOperations([initialSelection], edits.map(edit => {
|
||||
return {
|
||||
text: edit.text,
|
||||
range: Range.lift(edit.range),
|
||||
forceMoveMarkers: true
|
||||
};
|
||||
}), undoEdits => {
|
||||
for (const { range } of undoEdits) {
|
||||
if (Range.areIntersectingOrTouching(range, initialSelection)) {
|
||||
return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function formatDocumentWithProvider(
|
||||
accessor: ServicesAccessor,
|
||||
provider: DocumentFormattingEditProvider,
|
||||
editorOrModel: ITextModel | IActiveCodeEditor,
|
||||
token: CancellationToken
|
||||
): Promise<boolean> {
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
|
||||
let model: ITextModel;
|
||||
let validate: () => boolean;
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
model = editorOrModel.getModel();
|
||||
const state = new EditorState(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
validate = () => state.validate(editorOrModel);
|
||||
} else {
|
||||
model = editorOrModel;
|
||||
const versionNow = editorOrModel.getVersionId();
|
||||
validate = () => versionNow === editorOrModel.getVersionId();
|
||||
}
|
||||
|
||||
const rawEdits = await provider.provideDocumentFormattingEdits(
|
||||
model,
|
||||
model.getFormattingOptions(),
|
||||
token
|
||||
);
|
||||
|
||||
const edits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits);
|
||||
|
||||
if (!validate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!edits || edits.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
// use editor to apply edits
|
||||
FormattingEdit.execute(editorOrModel, edits);
|
||||
alertFormattingEdits(edits);
|
||||
editorOrModel.pushUndoStop();
|
||||
editorOrModel.focus();
|
||||
editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate);
|
||||
|
||||
} else {
|
||||
// use model to apply edits
|
||||
const [{ range }] = edits;
|
||||
const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
|
||||
model.pushEditOperations([initialSelection], edits.map(edit => {
|
||||
return {
|
||||
text: edit.text,
|
||||
range: Range.lift(edit.range),
|
||||
forceMoveMarkers: true
|
||||
};
|
||||
}), undoEdits => {
|
||||
for (const { range } of undoEdits) {
|
||||
if (Range.areIntersectingOrTouching(range, initialSelection)) {
|
||||
return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function getDocumentRangeFormattingEditsUntilResult(
|
||||
workerService: IEditorWorkerService,
|
||||
model: ITextModel,
|
||||
range: Range,
|
||||
options: FormattingOptions,
|
||||
mode: FormatMode,
|
||||
token: CancellationToken
|
||||
): Promise<TextEdit[] | undefined | null> {
|
||||
): Promise<TextEdit[] | undefined> {
|
||||
|
||||
const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model);
|
||||
|
||||
/* __GDPR__
|
||||
"formatterInfo" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
for (const provider of providers) {
|
||||
let rawEdits = await Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).catch(onUnexpectedExternalError);
|
||||
if (isNonEmptyArray(rawEdits)) {
|
||||
return await workerService.computeMoreMinimalEdits(model.uri, rawEdits);
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('formatterInfo', {
|
||||
type: 'range',
|
||||
language: model.getLanguageIdentifier().language,
|
||||
count: providers.length,
|
||||
extensions: providers.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown')
|
||||
});
|
||||
|
||||
invokeFormatterCallback(providers, model, mode | FormatKind.Range);
|
||||
|
||||
return first(providers.map(provider => () => {
|
||||
return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).catch(onUnexpectedExternalError);
|
||||
}), isNonEmptyArray).then(edits => {
|
||||
// break edits into smaller edits
|
||||
return workerService.computeMoreMinimalEdits(model.uri, edits);
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getDocumentFormattingEdits(
|
||||
telemetryService: ITelemetryService,
|
||||
export async function getDocumentFormattingEditsUntilResult(
|
||||
workerService: IEditorWorkerService,
|
||||
model: ITextModel,
|
||||
options: FormattingOptions,
|
||||
mode: FormatMode,
|
||||
token: CancellationToken
|
||||
): Promise<TextEdit[] | null | undefined> {
|
||||
): Promise<TextEdit[] | undefined> {
|
||||
|
||||
const docFormattingProviders = DocumentFormattingEditProviderRegistry.ordered(model);
|
||||
|
||||
/* __GDPR__
|
||||
"formatterInfo" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
const providers = getRealAndSyntheticDocumentFormattersOrdered(model);
|
||||
for (const provider of providers) {
|
||||
let rawEdits = await Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).catch(onUnexpectedExternalError);
|
||||
if (isNonEmptyArray(rawEdits)) {
|
||||
return await workerService.computeMoreMinimalEdits(model.uri, rawEdits);
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('formatterInfo', {
|
||||
type: 'document',
|
||||
language: model.getLanguageIdentifier().language,
|
||||
count: docFormattingProviders.length,
|
||||
extensions: docFormattingProviders.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown')
|
||||
});
|
||||
|
||||
if (docFormattingProviders.length > 0) {
|
||||
return first(docFormattingProviders.map(provider => () => {
|
||||
// first with result wins...
|
||||
return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).catch(onUnexpectedExternalError);
|
||||
}), isNonEmptyArray).then(edits => {
|
||||
// break edits into smaller edits
|
||||
return workerService.computeMoreMinimalEdits(model.uri, edits);
|
||||
});
|
||||
} else {
|
||||
// try range formatters when no document formatter is registered
|
||||
return getDocumentRangeFormattingEdits(telemetryService, workerService, model, model.getFullModelRange(), options, mode | FormatKind.Document, token);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getOnTypeFormattingEdits(
|
||||
telemetryService: ITelemetryService,
|
||||
workerService: IEditorWorkerService,
|
||||
model: ITextModel,
|
||||
position: Position,
|
||||
@@ -144,21 +264,6 @@ export function getOnTypeFormattingEdits(
|
||||
|
||||
const providers = OnTypeFormattingEditProviderRegistry.ordered(model);
|
||||
|
||||
/* __GDPR__
|
||||
"formatterInfo" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('formatterInfo', {
|
||||
type: 'ontype',
|
||||
language: model.getLanguageIdentifier().language,
|
||||
count: providers.length,
|
||||
extensions: providers.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown')
|
||||
});
|
||||
|
||||
if (providers.length === 0) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
@@ -181,7 +286,7 @@ registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args)
|
||||
if (!model) {
|
||||
throw illegalArgument('resource');
|
||||
}
|
||||
return getDocumentRangeFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, Range.lift(range), options, FormatMode.Auto, CancellationToken.None);
|
||||
return getDocumentRangeFormattingEditsUntilResult(accessor.get(IEditorWorkerService), model, Range.lift(range), options, CancellationToken.None);
|
||||
});
|
||||
|
||||
registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) {
|
||||
@@ -194,7 +299,7 @@ registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, ar
|
||||
throw illegalArgument('resource');
|
||||
}
|
||||
|
||||
return getDocumentFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, options, FormatMode.Auto, CancellationToken.None);
|
||||
return getDocumentFormattingEditsUntilResult(accessor.get(IEditorWorkerService), model, options, CancellationToken.None);
|
||||
});
|
||||
|
||||
registerLanguageCommand('_executeFormatOnTypeProvider', function (accessor, args) {
|
||||
@@ -207,5 +312,5 @@ registerLanguageCommand('_executeFormatOnTypeProvider', function (accessor, args
|
||||
throw illegalArgument('resource');
|
||||
}
|
||||
|
||||
return getOnTypeFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, Position.lift(position), ch, options);
|
||||
return getOnTypeFormattingEdits(accessor.get(IEditorWorkerService), model, Position.lift(position), ch, options);
|
||||
});
|
||||
|
||||
@@ -3,122 +3,27 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState';
|
||||
import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { CharacterSet } from 'vs/editor/common/core/characterClassifier';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import { DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits, FormatMode } from 'vs/editor/contrib/format/format';
|
||||
import { getOnTypeFormattingEdits, formatDocumentWithProvider, formatDocumentRangeWithProvider, alertFormattingEdits, getRealAndSyntheticDocumentFormattersOrdered } from 'vs/editor/contrib/format/format';
|
||||
import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';
|
||||
import * as nls from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
function alertFormattingEdits(edits: ISingleEditOperation[]): void {
|
||||
|
||||
edits = edits.filter(edit => edit.range);
|
||||
if (!edits.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { range } = edits[0];
|
||||
for (let i = 1; i < edits.length; i++) {
|
||||
range = Range.plusRange(range, edits[i].range);
|
||||
}
|
||||
const { startLineNumber, endLineNumber } = range;
|
||||
if (startLineNumber === endLineNumber) {
|
||||
if (edits.length === 1) {
|
||||
alert(nls.localize('hint11', "Made 1 formatting edit on line {0}", startLineNumber));
|
||||
} else {
|
||||
alert(nls.localize('hintn1', "Made {0} formatting edits on line {1}", edits.length, startLineNumber));
|
||||
}
|
||||
} else {
|
||||
if (edits.length === 1) {
|
||||
alert(nls.localize('hint1n', "Made 1 formatting edit between lines {0} and {1}", startLineNumber, endLineNumber));
|
||||
} else {
|
||||
alert(nls.localize('hintnn', "Made {0} formatting edits between lines {1} and {2}", edits.length, startLineNumber, endLineNumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const enum FormatRangeType {
|
||||
Full,
|
||||
Selection,
|
||||
}
|
||||
|
||||
function formatDocumentRange(
|
||||
telemetryService: ITelemetryService,
|
||||
workerService: IEditorWorkerService,
|
||||
editor: IActiveCodeEditor,
|
||||
rangeOrRangeType: Range | FormatRangeType,
|
||||
options: FormattingOptions,
|
||||
token: CancellationToken
|
||||
): Promise<void> {
|
||||
|
||||
|
||||
const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
const model = editor.getModel();
|
||||
|
||||
let range: Range;
|
||||
if (rangeOrRangeType === FormatRangeType.Full) {
|
||||
// full
|
||||
range = model.getFullModelRange();
|
||||
|
||||
} else if (rangeOrRangeType === FormatRangeType.Selection) {
|
||||
// selection or line (when empty)
|
||||
range = editor.getSelection();
|
||||
if (range.isEmpty()) {
|
||||
range = new Range(range.startLineNumber, 1, range.endLineNumber, model.getLineMaxColumn(range.endLineNumber));
|
||||
}
|
||||
} else {
|
||||
// as is
|
||||
range = rangeOrRangeType;
|
||||
}
|
||||
|
||||
return getDocumentRangeFormattingEdits(telemetryService, workerService, model, range, options, FormatMode.Manual, token).then(edits => {
|
||||
// make edit only when the editor didn't change while
|
||||
// computing and only when there are edits
|
||||
if (state.validate(editor) && isNonEmptyArray(edits)) {
|
||||
FormattingEdit.execute(editor, edits);
|
||||
alertFormattingEdits(edits);
|
||||
editor.focus();
|
||||
editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function formatDocument(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, options: FormattingOptions, token: CancellationToken): Promise<void> {
|
||||
|
||||
const allEdits: ISingleEditOperation[] = [];
|
||||
const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
|
||||
return getDocumentFormattingEdits(telemetryService, workerService, editor.getModel(), options, FormatMode.Manual, token).then(edits => {
|
||||
// make edit only when the editor didn't change while
|
||||
// computing and only when there are edits
|
||||
if (state.validate(editor) && isNonEmptyArray(edits)) {
|
||||
FormattingEdit.execute(editor, edits);
|
||||
|
||||
alertFormattingEdits(allEdits);
|
||||
editor.pushUndoStop();
|
||||
editor.focus();
|
||||
editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate);
|
||||
}
|
||||
});
|
||||
}
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
class FormatOnType implements editorCommon.IEditorContribution {
|
||||
|
||||
@@ -130,17 +35,25 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IEditorWorkerService private readonly _workerService: IEditorWorkerService
|
||||
) {
|
||||
this._editor = editor;
|
||||
this._callOnDispose.push(editor.onDidChangeConfiguration(() => this.update()));
|
||||
this._callOnDispose.push(editor.onDidChangeModel(() => this.update()));
|
||||
this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update()));
|
||||
this._callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this.update, this));
|
||||
this._callOnDispose.push(editor.onDidChangeConfiguration(() => this._update()));
|
||||
this._callOnDispose.push(editor.onDidChangeModel(() => this._update()));
|
||||
this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this._update()));
|
||||
this._callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this._update, this));
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
getId(): string {
|
||||
return FormatOnType.ID;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._callOnDispose = dispose(this._callOnDispose);
|
||||
this._callOnModel = dispose(this._callOnModel);
|
||||
}
|
||||
|
||||
private _update(): void {
|
||||
|
||||
// clean up
|
||||
this._callOnModel = dispose(this._callOnModel);
|
||||
@@ -171,12 +84,12 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
this._callOnModel.push(this._editor.onDidType((text: string) => {
|
||||
let lastCharCode = text.charCodeAt(text.length - 1);
|
||||
if (triggerChars.has(lastCharCode)) {
|
||||
this.trigger(String.fromCharCode(lastCharCode));
|
||||
this._trigger(String.fromCharCode(lastCharCode));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private trigger(ch: string): void {
|
||||
private _trigger(ch: string): void {
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
@@ -214,7 +127,6 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
});
|
||||
|
||||
getOnTypeFormattingEdits(
|
||||
this._telemetryService,
|
||||
this._workerService,
|
||||
model,
|
||||
position,
|
||||
@@ -238,42 +150,41 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return FormatOnType.ID;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._callOnDispose = dispose(this._callOnDispose);
|
||||
this._callOnModel = dispose(this._callOnModel);
|
||||
}
|
||||
}
|
||||
|
||||
class FormatOnPaste implements editorCommon.IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.formatOnPaste';
|
||||
|
||||
private callOnDispose: IDisposable[];
|
||||
private callOnModel: IDisposable[];
|
||||
private _callOnDispose: IDisposable[];
|
||||
private _callOnModel: IDisposable[];
|
||||
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
@IEditorWorkerService private readonly workerService: IEditorWorkerService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
this.callOnDispose = [];
|
||||
this.callOnModel = [];
|
||||
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()));
|
||||
this.callOnDispose.push(DocumentRangeFormattingEditProviderRegistry.onDidChange(this.update, this));
|
||||
this._callOnDispose.push(editor.onDidChangeConfiguration(() => this._update()));
|
||||
this._callOnDispose.push(editor.onDidChangeModel(() => this._update()));
|
||||
this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this._update()));
|
||||
this._callOnDispose.push(DocumentRangeFormattingEditProviderRegistry.onDidChange(this._update, this));
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
getId(): string {
|
||||
return FormatOnPaste.ID;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._callOnDispose = dispose(this._callOnDispose);
|
||||
this._callOnModel = dispose(this._callOnModel);
|
||||
}
|
||||
|
||||
private _update(): void {
|
||||
|
||||
// clean up
|
||||
this.callOnModel = dispose(this.callOnModel);
|
||||
this._callOnModel = dispose(this._callOnModel);
|
||||
|
||||
// we are disabled
|
||||
if (!this.editor.getConfiguration().contribInfo.formatOnPaste) {
|
||||
@@ -285,53 +196,41 @@ class FormatOnPaste implements editorCommon.IEditorContribution {
|
||||
return;
|
||||
}
|
||||
|
||||
let model = this.editor.getModel();
|
||||
|
||||
// no support
|
||||
if (!DocumentRangeFormattingEditProviderRegistry.has(model)) {
|
||||
// no formatter
|
||||
if (!DocumentRangeFormattingEditProviderRegistry.has(this.editor.getModel())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.callOnModel.push(this.editor.onDidPaste((range: Range) => {
|
||||
this.trigger(range);
|
||||
}));
|
||||
this._callOnModel.push(this.editor.onDidPaste(range => this._trigger(range)));
|
||||
}
|
||||
|
||||
private trigger(range: Range): void {
|
||||
private _trigger(range: Range): void {
|
||||
if (!this.editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.editor.getSelections().length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this.editor.getModel();
|
||||
formatDocumentRange(this.telemetryService, this.workerService, this.editor, range, model.getFormattingOptions(), CancellationToken.None);
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return FormatOnPaste.ID;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.callOnDispose = dispose(this.callOnDispose);
|
||||
this.callOnModel = dispose(this.callOnModel);
|
||||
const provider = DocumentRangeFormattingEditProviderRegistry.ordered(this.editor.getModel());
|
||||
if (provider.length !== 1) {
|
||||
// print status in n>1 case?
|
||||
return;
|
||||
}
|
||||
this._instantiationService.invokeFunction(formatDocumentRangeWithProvider, provider[0], this.editor, range, CancellationToken.None).catch(onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
export class FormatDocumentAction extends EditorAction {
|
||||
class FormatDocumentAction extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.action.formatDocument',
|
||||
label: nls.localize('formatDocument.label', "Format Document"),
|
||||
alias: 'Format Document',
|
||||
precondition: EditorContextKeys.writable,
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider, EditorContextKeys.hasMultipleDocumentFormattingProvider.toNegated()),
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider),
|
||||
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F,
|
||||
// secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D)],
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I },
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
},
|
||||
@@ -343,26 +242,29 @@ export class FormatDocumentAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> | void {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
const telemetryService = accessor.get(ITelemetryService);
|
||||
return formatDocument(telemetryService, workerService, editor, editor.getModel().getFormattingOptions(), CancellationToken.None);
|
||||
const instaService = accessor.get(IInstantiationService);
|
||||
const model = editor.getModel();
|
||||
const [provider] = getRealAndSyntheticDocumentFormattersOrdered(model);
|
||||
if (provider) {
|
||||
await instaService.invokeFunction(formatDocumentWithProvider, provider, editor, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FormatSelectionAction extends EditorAction {
|
||||
class FormatSelectionAction extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.action.formatSelection',
|
||||
label: nls.localize('formatSelection.label', "Format Selection"),
|
||||
alias: 'Format Code',
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable),
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider.toNegated()),
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentSelectionFormattingProvider),
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F),
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
},
|
||||
@@ -374,13 +276,19 @@ export class FormatSelectionAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> | void {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
const telemetryService = accessor.get(ITelemetryService);
|
||||
return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, editor.getModel().getFormattingOptions(), CancellationToken.None);
|
||||
const instaService = accessor.get(IInstantiationService);
|
||||
const [best] = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel());
|
||||
if (best) {
|
||||
let range: Range = editor.getSelection();
|
||||
if (range.isEmpty()) {
|
||||
range = new Range(range.startLineNumber, 1, range.startLineNumber, editor.getModel().getLineMaxColumn(range.startLineNumber));
|
||||
}
|
||||
await instaService.invokeFunction(formatDocumentRangeWithProvider, best, editor, range, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,17 +299,15 @@ registerEditorAction(FormatSelectionAction);
|
||||
|
||||
// this is the old format action that does both (format document OR format selection)
|
||||
// and we keep it here such that existing keybinding configurations etc will still work
|
||||
CommandsRegistry.registerCommand('editor.action.format', accessor => {
|
||||
CommandsRegistry.registerCommand('editor.action.format', async accessor => {
|
||||
const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
|
||||
if (!editor || !editor.hasModel()) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
const telemetryService = accessor.get(ITelemetryService);
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
if (editor.getSelection().isEmpty()) {
|
||||
return formatDocument(telemetryService, workerService, editor, editor.getModel().getFormattingOptions(), CancellationToken.None);
|
||||
await commandService.executeCommand('editor.action.formatDocument');
|
||||
} else {
|
||||
return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, editor.getModel().getFormattingOptions(), CancellationToken.None);
|
||||
await commandService.executeCommand('editor.action.formatSelection');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -203,8 +203,8 @@ export class MarkerController implements editorCommon.IEditorContribution {
|
||||
}
|
||||
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _model: MarkerModel | null;
|
||||
private _widget: MarkerNavigationWidget | null;
|
||||
private _model: MarkerModel | null = null;
|
||||
private _widget: MarkerNavigationWidget | null = null;
|
||||
private readonly _widgetVisible: IContextKey<boolean>;
|
||||
private _disposeOnClose: IDisposable[] = [];
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export class MoveLinesCommand implements ICommand {
|
||||
private readonly _autoIndent: boolean;
|
||||
|
||||
private _selectionId: string;
|
||||
private _moveEndPositionDown: boolean;
|
||||
private _moveEndPositionDown?: boolean;
|
||||
private _moveEndLineSelectionShrink: boolean;
|
||||
|
||||
constructor(selection: Selection, isMovingDown: boolean, autoIndent: boolean) {
|
||||
|
||||
@@ -196,10 +196,9 @@ suite('Editor Contrib - Line Operations', () => {
|
||||
const endOfNonono = new Selection(5, 11, 5, 11);
|
||||
|
||||
editor.setSelections([beforeSecondWasoSelection, endOfBCCSelection, endOfNonono]);
|
||||
let selections;
|
||||
|
||||
deleteAllLeftAction.run(null!, editor);
|
||||
selections = editor.getSelections();
|
||||
let selections = editor.getSelections()!;
|
||||
|
||||
assert.equal(model.getLineContent(2), '');
|
||||
assert.equal(model.getLineContent(3), ' waso waso');
|
||||
@@ -227,7 +226,7 @@ suite('Editor Contrib - Line Operations', () => {
|
||||
], [5, 1, 5, 1]);
|
||||
|
||||
deleteAllLeftAction.run(null!, editor);
|
||||
selections = editor.getSelections();
|
||||
selections = editor.getSelections()!;
|
||||
|
||||
assert.equal(model.getLineContent(1), 'hi my name is Carlos Matos waso waso');
|
||||
assert.equal(selections.length, 2);
|
||||
|
||||
@@ -21,6 +21,7 @@ import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet } from './sni
|
||||
import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, CommentBasedVariableResolver, WorkspaceBasedVariableResolver } from './snippetVariables';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
|
||||
@@ -281,7 +282,7 @@ export class OneSnippet {
|
||||
let result: Range | undefined;
|
||||
const model = this._editor.getModel();
|
||||
this._placeholderDecorations.forEach((decorationId) => {
|
||||
const placeholderRange = model.getDecorationRange(decorationId) || undefined;
|
||||
const placeholderRange = withNullAsUndefined(model.getDecorationRange(decorationId));
|
||||
if (!result) {
|
||||
result = placeholderRange;
|
||||
} else {
|
||||
|
||||
@@ -18,8 +18,8 @@ export class SuggestAlternatives {
|
||||
private _index: number;
|
||||
private _model: CompletionModel | undefined;
|
||||
private _acceptNext: ((selected: ISelectedSuggestion) => any) | undefined;
|
||||
private _listener: IDisposable;
|
||||
private _ignore: boolean;
|
||||
private _listener: IDisposable | undefined;
|
||||
private _ignore: boolean | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
|
||||
Reference in New Issue
Block a user