mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-04 01:25:38 -05:00
Merge from vscode 9bc92b48d945144abb405b9e8df05e18accb9148
This commit is contained in:
@@ -15,6 +15,9 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWaitUntil } from 'vs/base/common/event';
|
||||
|
||||
@extHostCustomer
|
||||
export class MainThreadFileSystemEventService {
|
||||
@@ -28,6 +31,7 @@ export class MainThreadFileSystemEventService {
|
||||
@IProgressService progressService: IProgressService,
|
||||
@IConfigurationService configService: IConfigurationService,
|
||||
@ILogService logService: ILogService,
|
||||
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService
|
||||
) {
|
||||
|
||||
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService);
|
||||
@@ -38,7 +42,7 @@ export class MainThreadFileSystemEventService {
|
||||
changed: [],
|
||||
deleted: []
|
||||
};
|
||||
this._listener.add(fileService.onFileChanges(event => {
|
||||
this._listener.add(fileService.onDidFilesChange(event => {
|
||||
for (let change of event.changes) {
|
||||
switch (change.type) {
|
||||
case FileChangeType.ADDED:
|
||||
@@ -66,9 +70,7 @@ export class MainThreadFileSystemEventService {
|
||||
messages.set(FileOperation.DELETE, localize('msg-delete', "Running 'File Delete' participants..."));
|
||||
messages.set(FileOperation.MOVE, localize('msg-rename', "Running 'File Rename' participants..."));
|
||||
|
||||
|
||||
this._listener.add(textFileService.onWillRunOperation(e => {
|
||||
|
||||
function participateInFileOperation(e: IWaitUntil, operation: FileOperation, target: URI, source?: URI): void {
|
||||
const timeout = configService.getValue<number>('files.participants.timeout');
|
||||
if (timeout <= 0) {
|
||||
return; // disabled
|
||||
@@ -76,19 +78,19 @@ export class MainThreadFileSystemEventService {
|
||||
|
||||
const p = progressService.withProgress({ location: ProgressLocation.Window }, progress => {
|
||||
|
||||
progress.report({ message: messages.get(e.operation) });
|
||||
progress.report({ message: messages.get(operation) });
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const cts = new CancellationTokenSource();
|
||||
|
||||
const timeoutHandle = setTimeout(() => {
|
||||
logService.trace('CANCELLED file participants because of timeout', timeout, e.target, e.operation);
|
||||
logService.trace('CANCELLED file participants because of timeout', timeout, target, operation);
|
||||
cts.cancel();
|
||||
reject(new Error('timeout'));
|
||||
}, timeout);
|
||||
|
||||
proxy.$onWillRunFileOperation(e.operation, e.target, e.source, timeout, cts.token)
|
||||
proxy.$onWillRunFileOperation(operation, target, source, timeout, cts.token)
|
||||
.then(resolve, reject)
|
||||
.finally(() => clearTimeout(timeoutHandle));
|
||||
});
|
||||
@@ -96,10 +98,14 @@ export class MainThreadFileSystemEventService {
|
||||
});
|
||||
|
||||
e.waitUntil(p);
|
||||
}));
|
||||
}
|
||||
|
||||
this._listener.add(textFileService.onWillCreateTextFile(e => participateInFileOperation(e, FileOperation.CREATE, e.resource)));
|
||||
this._listener.add(workingCopyFileService.onBeforeWorkingCopyFileOperation(e => participateInFileOperation(e, e.operation, e.target, e.source)));
|
||||
|
||||
// AFTER file operation
|
||||
this._listener.add(textFileService.onDidRunOperation(e => proxy.$onDidRunFileOperation(e.operation, e.target, e.source)));
|
||||
this._listener.add(textFileService.onDidCreateTextFile(e => proxy.$onDidRunFileOperation(FileOperation.CREATE, e.resource, undefined)));
|
||||
this._listener.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => proxy.$onDidRunFileOperation(e.operation, e.target, e.source)));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Position as EditorPosition } from 'vs/editor/common/core/position';
|
||||
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto } from '../common/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto, ILanguageWordDefinitionDto } from '../common/extHost.protocol';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
@@ -36,6 +36,34 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures);
|
||||
this._modeService = modeService;
|
||||
|
||||
if (this._modeService) {
|
||||
const updateAllWordDefinitions = () => {
|
||||
const langWordPairs = LanguageConfigurationRegistry.getWordDefinitions();
|
||||
let wordDefinitionDtos: ILanguageWordDefinitionDto[] = [];
|
||||
for (const [languageId, wordDefinition] of langWordPairs) {
|
||||
const language = this._modeService.getLanguageIdentifier(languageId);
|
||||
if (!language) {
|
||||
continue;
|
||||
}
|
||||
wordDefinitionDtos.push({
|
||||
languageId: language.language,
|
||||
regexSource: wordDefinition.source,
|
||||
regexFlags: wordDefinition.flags
|
||||
});
|
||||
}
|
||||
this._proxy.$setWordDefinitions(wordDefinitionDtos);
|
||||
};
|
||||
LanguageConfigurationRegistry.onDidChange((e) => {
|
||||
const wordDefinition = LanguageConfigurationRegistry.getWordDefinition(e.languageIdentifier.id);
|
||||
this._proxy.$setWordDefinitions([{
|
||||
languageId: e.languageIdentifier.language,
|
||||
regexSource: wordDefinition.source,
|
||||
regexFlags: wordDefinition.flags
|
||||
}]);
|
||||
});
|
||||
updateAllWordDefinitions();
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
@@ -3,329 +3,19 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IdleValue, raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
|
||||
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
|
||||
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
|
||||
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProgressService, ProgressLocation, IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ISaveParticipant, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileSaveParticipant, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface ISaveParticipantParticipant {
|
||||
participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void>;
|
||||
}
|
||||
|
||||
/*
|
||||
* An update participant that ensures any un-tracked changes are synced to the JSON file contents for a
|
||||
* Notebook before save occurs. While every effort is made to ensure model changes are notified and a listener
|
||||
* updates the backing model in-place, this is a backup mechanism to hard-update the file before save in case
|
||||
* some are missed.
|
||||
*/
|
||||
class NotebookUpdateParticipant implements ISaveParticipantParticipant { // {{SQL CARBON EDIT}} add notebook participant
|
||||
|
||||
constructor(
|
||||
@INotebookService private notebookService: INotebookService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
public participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
|
||||
let uri = model.resource;
|
||||
let notebookEditor = this.notebookService.findNotebookEditor(uri);
|
||||
if (notebookEditor) {
|
||||
notebookEditor.notebookParams.input.updateModel();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
private doTrimTrailingWhitespace(model: ITextModel, isAutoSaved: boolean): void {
|
||||
let prevSelection: Selection[] = [];
|
||||
let cursors: Position[] = [];
|
||||
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
// Find `prevSelection` in any case do ensure a good undo stack when pushing the edit
|
||||
// Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump
|
||||
prevSelection = editor.getSelections();
|
||||
if (isAutoSaved) {
|
||||
cursors = prevSelection.map(s => s.getPosition());
|
||||
const snippetsRange = SnippetController2.get(editor).getSessionEnclosingRange();
|
||||
if (snippetsRange) {
|
||||
for (let lineNumber = snippetsRange.startLineNumber; lineNumber <= snippetsRange.endLineNumber; lineNumber++) {
|
||||
cursors.push(new Position(lineNumber, model.getLineMaxColumn(lineNumber)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ops = trimTrailingWhitespace(model, cursors);
|
||||
if (!ops.length) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, ops, (_edits) => prevSelection);
|
||||
}
|
||||
}
|
||||
|
||||
function findEditor(model: ITextModel, codeEditorService: ICodeEditorService): IActiveCodeEditor | null {
|
||||
let candidate: IActiveCodeEditor | null = null;
|
||||
|
||||
if (model.isAttachedToEditor()) {
|
||||
for (const editor of codeEditorService.listCodeEditors()) {
|
||||
if (editor.hasModel() && editor.getModel() === model) {
|
||||
if (editor.hasTextFocus()) {
|
||||
return editor; // favour focused editor if there are multiple
|
||||
}
|
||||
|
||||
candidate = editor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
export class FinalNewLineParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, _env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doInsertFinalNewLine(model.textEditorModel);
|
||||
}
|
||||
}
|
||||
|
||||
private doInsertFinalNewLine(model: ITextModel): void {
|
||||
const lineCount = model.getLineCount();
|
||||
const lastLine = model.getLineContent(lineCount);
|
||||
const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1;
|
||||
|
||||
if (!lineCount || lastLineIsEmptyOrWhitespace) {
|
||||
return;
|
||||
}
|
||||
|
||||
const edits = [EditOperation.insert(new Position(lineCount, model.getLineMaxColumn(lineCount)), model.getEOL())];
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
editor.executeEdits('insertFinalNewLine', edits, editor.getSelections());
|
||||
} else {
|
||||
model.pushEditOperations([], edits, () => null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns 0 if the entire file is empty or whitespace only
|
||||
*/
|
||||
private findLastLineWithContent(model: ITextModel): number {
|
||||
for (let lineNumber = model.getLineCount(); lineNumber >= 1; lineNumber--) {
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
if (strings.lastNonWhitespaceIndex(lineContent) !== -1) {
|
||||
// this line has content
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
||||
// no line has content
|
||||
return 0;
|
||||
}
|
||||
|
||||
private doTrimFinalNewLines(model: ITextModel, isAutoSaved: boolean): void {
|
||||
const lineCount = model.getLineCount();
|
||||
|
||||
// Do not insert new line if file does not end with new line
|
||||
if (lineCount === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let prevSelection: Selection[] = [];
|
||||
let cannotTouchLineNumber = 0;
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
prevSelection = editor.getSelections();
|
||||
if (isAutoSaved) {
|
||||
for (let i = 0, len = prevSelection.length; i < len; i++) {
|
||||
const positionLineNumber = prevSelection[i].positionLineNumber;
|
||||
if (positionLineNumber > cannotTouchLineNumber) {
|
||||
cannotTouchLineNumber = positionLineNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lastLineNumberWithContent = this.findLastLineWithContent(model);
|
||||
const deleteFromLineNumber = Math.max(lastLineNumberWithContent + 1, cannotTouchLineNumber + 1);
|
||||
const deletionRange = model.validateRange(new Range(deleteFromLineNumber, 1, lineCount, model.getLineMaxColumn(lineCount)));
|
||||
|
||||
if (deletionRange.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], _edits => prevSelection);
|
||||
|
||||
if (editor) {
|
||||
editor.setSelections(prevSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FormatOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
|
||||
const model = editorModel.textEditorModel;
|
||||
const overrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri };
|
||||
|
||||
if (env.reason === SaveReason.AUTO || !this._configurationService.getValue('editor.formatOnSave', overrides)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
progress.report({ message: localize('formatting', "Formatting") });
|
||||
const editorOrModel = findEditor(model, this._codeEditorService) || model;
|
||||
await this._instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, token);
|
||||
}
|
||||
}
|
||||
|
||||
class CodeActionOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) { }
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
if (env.reason === SaveReason.AUTO) {
|
||||
return undefined;
|
||||
}
|
||||
const model = editorModel.textEditorModel;
|
||||
|
||||
const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.resource };
|
||||
const setting = this._configurationService.getValue<{ [kind: string]: boolean }>('editor.codeActionsOnSave', settingsOverrides);
|
||||
if (!setting) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const codeActionsOnSave = Object.keys(setting)
|
||||
.filter(x => setting[x]).map(x => new CodeActionKind(x))
|
||||
.sort((a, b) => {
|
||||
if (CodeActionKind.SourceFixAll.contains(a)) {
|
||||
if (CodeActionKind.SourceFixAll.contains(b)) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (CodeActionKind.SourceFixAll.contains(b)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (!codeActionsOnSave.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const excludedActions = Object.keys(setting)
|
||||
.filter(x => setting[x] === false)
|
||||
.map(x => new CodeActionKind(x));
|
||||
|
||||
progress.report({ message: localize('codeaction', "Quick Fixes") });
|
||||
await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, token);
|
||||
}
|
||||
|
||||
private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], token: CancellationToken): Promise<void> {
|
||||
for (const codeActionKind of codeActionsOnSave) {
|
||||
const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token);
|
||||
try {
|
||||
await this.applyCodeActions(actionsToRun.validActions);
|
||||
} catch {
|
||||
// Failure to apply a code action should not block other on save actions
|
||||
} finally {
|
||||
actionsToRun.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async applyCodeActions(actionsToRun: readonly CodeAction[]) {
|
||||
for (const action of actionsToRun) {
|
||||
await this._instantiationService.invokeFunction(applyCodeAction, action);
|
||||
}
|
||||
}
|
||||
|
||||
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) {
|
||||
return getCodeActions(model, model.getFullModelRange(), {
|
||||
type: CodeActionTriggerType.Auto,
|
||||
filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true },
|
||||
}, token);
|
||||
}
|
||||
}
|
||||
|
||||
class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
||||
class ExtHostSaveParticipant implements ITextFileSaveParticipant {
|
||||
|
||||
private readonly _proxy: ExtHostDocumentSaveParticipantShape;
|
||||
|
||||
@@ -361,67 +51,19 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
// The save participant can change a model before its saved to support various scenarios like trimming trailing whitespace
|
||||
@extHostCustomer
|
||||
export class SaveParticipant implements ISaveParticipant {
|
||||
export class SaveParticipant {
|
||||
|
||||
private readonly _saveParticipants: IdleValue<ISaveParticipantParticipant[]>;
|
||||
private _saveParticipantDisposable: IDisposable;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IProgressService private readonly _progressService: IProgressService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@ITextFileService private readonly _textFileService: ITextFileService
|
||||
) {
|
||||
this._saveParticipants = new IdleValue(() => [
|
||||
instantiationService.createInstance(TrimWhitespaceParticipant),
|
||||
instantiationService.createInstance(CodeActionOnSaveParticipant),
|
||||
instantiationService.createInstance(FormatOnSaveParticipant),
|
||||
instantiationService.createInstance(FinalNewLineParticipant),
|
||||
instantiationService.createInstance(TrimFinalNewLinesParticipant),
|
||||
// {{SQL CARBON EDIT}}
|
||||
instantiationService.createInstance(NotebookUpdateParticipant),
|
||||
instantiationService.createInstance(ExtHostSaveParticipant, extHostContext),
|
||||
]);
|
||||
// Set as save participant for all text files
|
||||
this._textFileService.saveParticipant = this;
|
||||
this._saveParticipantDisposable = this._textFileService.files.addSaveParticipant(instantiationService.createInstance(ExtHostSaveParticipant, extHostContext));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._textFileService.saveParticipant = undefined;
|
||||
this._saveParticipants.dispose();
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise<void> {
|
||||
|
||||
const cts = new CancellationTokenSource(token);
|
||||
|
||||
return this._progressService.withProgress({
|
||||
title: localize('saveParticipants', "Running Save Participants for '{0}'", this._labelService.getUriLabel(model.resource, { relative: true })),
|
||||
location: ProgressLocation.Notification,
|
||||
cancellable: true,
|
||||
delay: model.isDirty() ? 3000 : 5000
|
||||
}, async progress => {
|
||||
// undoStop before participation
|
||||
model.textEditorModel.pushStackElement();
|
||||
|
||||
for (let p of this._saveParticipants.getValue()) {
|
||||
if (cts.token.isCancellationRequested) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
const promise = p.participate(model, context, progress, cts.token);
|
||||
await raceCancellation(promise, cts.token);
|
||||
} catch (err) {
|
||||
this._logService.warn(err);
|
||||
}
|
||||
}
|
||||
|
||||
// undoStop after participation
|
||||
model.textEditorModel.pushStackElement();
|
||||
}, () => {
|
||||
// user cancel
|
||||
cts.dispose(true);
|
||||
});
|
||||
this._saveParticipantDisposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,10 +80,14 @@ class SearchOperation {
|
||||
}
|
||||
|
||||
addMatch(match: IFileMatch): void {
|
||||
if (this.matches.has(match.resource.toString())) {
|
||||
// Merge with previous IFileMatches
|
||||
const existingMatch = this.matches.get(match.resource.toString());
|
||||
if (existingMatch) {
|
||||
// TODO@rob clean up text/file result types
|
||||
this.matches.get(match.resource.toString())!.results!.push(...match.results!);
|
||||
// If a file search returns the same file twice, we would enter this branch.
|
||||
// It's possible that could happen, #90813
|
||||
if (existingMatch.results && match.results) {
|
||||
existingMatch.results.push(...match.results);
|
||||
}
|
||||
} else {
|
||||
this.matches.set(match.resource.toString(), match);
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
|
||||
webviewInput.webview.options = options;
|
||||
webviewInput.webview.extension = extension;
|
||||
const resource = webviewInput.getResource();
|
||||
const resource = webviewInput.resource;
|
||||
|
||||
const model = await this.retainCustomEditorModel(webviewInput, resource, viewType, capabilities);
|
||||
webviewInput.onDisposeWebview(() => {
|
||||
@@ -312,7 +312,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
}
|
||||
|
||||
private async retainCustomEditorModel(webviewInput: WebviewInput, resource: URI, viewType: string, capabilities: readonly extHostProtocol.WebviewEditorCapabilities[]) {
|
||||
const model = await this._customEditorService.models.resolve(webviewInput.getResource(), webviewInput.viewType);
|
||||
const model = await this._customEditorService.models.resolve(webviewInput.resource, webviewInput.viewType);
|
||||
|
||||
const existingEntry = this._customEditorModels.get(model);
|
||||
if (existingEntry) {
|
||||
|
||||
@@ -82,7 +82,7 @@ const viewDescriptor: IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'),
|
||||
description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. This should be unique across all views. It is recommended to include your extension id as part of the view id. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'),
|
||||
type: 'string'
|
||||
},
|
||||
name: {
|
||||
@@ -100,7 +100,7 @@ const remoteViewDescriptor: IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'),
|
||||
description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. This should be unique across all views. It is recommended to include your extension id as part of the view id. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'),
|
||||
type: 'string'
|
||||
},
|
||||
name: {
|
||||
@@ -375,16 +375,15 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key));
|
||||
}
|
||||
const container = viewContainer || this.getDefaultViewContainer();
|
||||
const registeredViews = this.viewsRegistry.getViews(container);
|
||||
const viewIds: string[] = [];
|
||||
const viewDescriptors = coalesce(entry.value.map((item, index) => {
|
||||
// validate
|
||||
if (viewIds.indexOf(item.id) !== -1) {
|
||||
collector.error(localize('duplicateView1', "Cannot register multiple views with same id `{0}` in the view container `{1}`", item.id, container.id));
|
||||
collector.error(localize('duplicateView1', "Cannot register multiple views with same id `{0}`", item.id));
|
||||
return null;
|
||||
}
|
||||
if (registeredViews.some(v => v.id === item.id)) {
|
||||
collector.error(localize('duplicateView2', "A view with id `{0}` is already registered in the view container `{1}`", item.id, container.id));
|
||||
if (this.viewsRegistry.getView(item.id) !== null) {
|
||||
collector.error(localize('duplicateView2', "A view with id `{0}` is already registered.", item.id));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user