Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c (#8525)

* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c

* remove files we don't want

* fix hygiene

* update distro

* update distro

* fix hygiene

* fix strict nulls

* distro

* distro

* fix tests

* fix tests

* add another edit

* fix viewlet icon

* fix azure dialog

* fix some padding

* fix more padding issues
This commit is contained in:
Anthony Dresser
2019-12-04 19:28:22 -08:00
committed by GitHub
parent a8818ab0df
commit f5ce7fb2a5
1507 changed files with 42813 additions and 27370 deletions

View File

@@ -18,7 +18,7 @@ import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'v
import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsPanel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape } from '../common/extHost.protocol';
import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from '../common/extHost.protocol';
import { COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer';
@@ -116,17 +116,15 @@ export class MainThreadCommentThread implements modes.CommentThread {
this._isDisposed = false;
}
batchUpdate(
range: IRange,
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
collapsibleState: modes.CommentThreadCollapsibleState) {
this._range = range;
this._label = label;
this._contextValue = contextValue;
this._comments = comments;
this._collapsibleState = collapsibleState;
batchUpdate(changes: CommentThreadChanges) {
const modified = (value: keyof CommentThreadChanges): boolean =>
Object.prototype.hasOwnProperty.call(changes, value);
if (modified('range')) { this._range = changes.range!; }
if (modified('label')) { this._label = changes.label; }
if (modified('contextValue')) { this._contextValue = changes.contextValue; }
if (modified('comments')) { this._comments = changes.comments; }
if (modified('collapseState')) { this._collapsibleState = changes.collapseState; }
}
dispose() {
@@ -228,13 +226,9 @@ export class MainThreadCommentController {
updateCommentThread(commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
collapsibleState: modes.CommentThreadCollapsibleState): void {
changes: CommentThreadChanges): void {
let thread = this.getKnownThread(commentThreadHandle);
thread.batchUpdate(range, label, contextValue, comments, collapsibleState);
thread.batchUpdate(changes);
this._commentService.updateComments(this._uniqueId, {
added: [],
@@ -430,18 +424,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments
commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
collapsibleState: modes.CommentThreadCollapsibleState): void {
changes: CommentThreadChanges): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return undefined;
}
return provider.updateCommentThread(commentThreadHandle, threadId, resource, range, label, contextValue, comments, collapsibleState);
return provider.updateCommentThread(commentThreadHandle, threadId, resource, changes);
}
$deleteCommentThread(handle: number, commentThreadHandle: number) {
@@ -456,7 +446,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
private registerPanel(commentsPanelAlreadyConstructed: boolean) {
if (!commentsPanelAlreadyConstructed) {
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(PanelDescriptor.create(
CommentsPanel,
COMMENTS_PANEL_ID,
COMMENTS_PANEL_TITLE,

View File

@@ -33,11 +33,11 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
private static _convertOpenOptions(options: MainThreadDialogOpenOptions): IOpenDialogOptions {
const result: IOpenDialogOptions = {
openLabel: options.openLabel,
openLabel: options.openLabel || undefined,
canSelectFiles: options.canSelectFiles || (!options.canSelectFiles && !options.canSelectFolders),
canSelectFolders: options.canSelectFolders,
canSelectMany: options.canSelectMany,
defaultUri: URI.revive(options.defaultUri)
defaultUri: options.defaultUri ? URI.revive(options.defaultUri) : undefined
};
if (options.filters) {
result.filters = [];
@@ -48,8 +48,8 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
private static _convertSaveOptions(options: MainThreadDialogSaveOptions): ISaveDialogOptions {
const result: ISaveDialogOptions = {
defaultUri: URI.revive(options.defaultUri),
saveLabel: options.saveLabel
defaultUri: options.defaultUri ? URI.revive(options.defaultUri) : undefined,
saveLabel: options.saveLabel || undefined
};
if (options.filters) {
result.filters = [];

View File

@@ -16,7 +16,7 @@ import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThre
import { ExtHostContext, ExtHostDocumentsShape, IExtHostContext, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
import { ITextEditorModel } from 'vs/workbench/common/editor';
import { ITextFileService, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { toLocalResource } from 'vs/base/common/resources';
@@ -70,7 +70,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private readonly _textModelResolverService: ITextModelService;
private readonly _textFileService: ITextFileService;
private readonly _fileService: IFileService;
private readonly _untitledEditorService: IUntitledEditorService;
private readonly _untitledTextEditorService: IUntitledTextEditorService;
private readonly _environmentService: IWorkbenchEnvironmentService;
private readonly _toDispose = new DisposableStore();
@@ -87,14 +87,14 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
@ITextFileService textFileService: ITextFileService,
@IFileService fileService: IFileService,
@ITextModelService textModelResolverService: ITextModelService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
@IUntitledTextEditorService untitledTextEditorService: IUntitledTextEditorService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) {
this._modelService = modelService;
this._textModelResolverService = textModelResolverService;
this._textFileService = textFileService;
this._fileService = fileService;
this._untitledEditorService = untitledEditorService;
this._untitledTextEditorService = untitledTextEditorService;
this._environmentService = environmentService;
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocuments);
@@ -227,13 +227,13 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
}
private _doCreateUntitled(resource?: URI, mode?: string, initialValue?: string): Promise<URI> {
return this._untitledEditorService.loadOrCreate({
return this._untitledTextEditorService.loadOrCreate({
resource,
mode,
initialValue,
useResourcePath: Boolean(resource && resource.path)
}).then(model => {
const resource = model.getResource();
const resource = model.resource;
if (!this._modelIsSynced.has(resource.toString())) {
throw new Error(`expected URI ${resource.toString()} to have come to LIFE`);

View File

@@ -28,7 +28,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
namespace delta {
@@ -327,7 +327,7 @@ export class MainThreadDocumentsAndEditors {
@IModeService modeService: IModeService,
@IFileService fileService: IFileService,
@ITextModelService textModelResolverService: ITextModelService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
@IUntitledTextEditorService untitledTextEditorService: IUntitledTextEditorService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IBulkEditService bulkEditService: IBulkEditService,
@IPanelService panelService: IPanelService,
@@ -335,7 +335,7 @@ export class MainThreadDocumentsAndEditors {
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors);
const mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService, environmentService));
const mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledTextEditorService, environmentService));
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
const mainThreadTextEditors = this._toDispose.add(new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._editorService, this._editorGroupService));

View File

@@ -6,7 +6,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions, IFileStat, FileOperationError, FileOperationResult, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions, IFileStat, FileOperationError, FileOperationResult, FileSystemProviderErrorCode, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileFolderCopyCapability } from 'vs/platform/files/common/files';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../common/extHost.protocol';
import { VSBuffer } from 'vs/base/common/buffer';
@@ -52,10 +52,10 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
$stat(uri: UriComponents): Promise<IStat> {
return this._fileService.resolve(URI.revive(uri), { resolveMetadata: true }).then(stat => {
return {
ctime: 0,
ctime: stat.ctime,
mtime: stat.mtime,
size: stat.size,
type: MainThreadFileSystem._getFileType(stat)
type: MainThreadFileSystem._asFileType(stat)
};
}).catch(MainThreadFileSystem._handleError);
}
@@ -67,12 +67,22 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
err.name = FileSystemProviderErrorCode.FileNotADirectory;
throw err;
}
return !stat.children ? [] : stat.children.map(child => [child.name, MainThreadFileSystem._getFileType(child)]);
return !stat.children ? [] : stat.children.map(child => [child.name, MainThreadFileSystem._asFileType(child)] as [string, FileType]);
}).catch(MainThreadFileSystem._handleError);
}
private static _getFileType(stat: IFileStat): FileType {
return (stat.isDirectory ? FileType.Directory : FileType.File) + (stat.isSymbolicLink ? FileType.SymbolicLink : 0);
private static _asFileType(stat: IFileStat): FileType {
let res = 0;
if (stat.isFile) {
res += FileType.File;
} else if (stat.isDirectory) {
res += FileType.Directory;
}
if (stat.isSymbolicLink) {
res += FileType.SymbolicLink;
}
return res;
}
$readFile(uri: UriComponents): Promise<VSBuffer> {
@@ -80,19 +90,23 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
}
$writeFile(uri: UriComponents, content: VSBuffer): Promise<void> {
return this._fileService.writeFile(URI.revive(uri), content).catch(MainThreadFileSystem._handleError);
return this._fileService.writeFile(URI.revive(uri), content)
.then(() => undefined).catch(MainThreadFileSystem._handleError);
}
$rename(source: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
return this._fileService.move(URI.revive(source), URI.revive(target), opts.overwrite).catch(MainThreadFileSystem._handleError);
return this._fileService.move(URI.revive(source), URI.revive(target), opts.overwrite)
.then(() => undefined).catch(MainThreadFileSystem._handleError);
}
$copy(source: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
return this._fileService.copy(URI.revive(source), URI.revive(target), opts.overwrite).catch(MainThreadFileSystem._handleError);
return this._fileService.copy(URI.revive(source), URI.revive(target), opts.overwrite)
.then(() => undefined).catch(MainThreadFileSystem._handleError);
}
$mkdir(uri: UriComponents): Promise<void> {
return this._fileService.createFolder(URI.revive(uri)).catch(MainThreadFileSystem._handleError);
return this._fileService.createFolder(URI.revive(uri))
.then(() => undefined).catch(MainThreadFileSystem._handleError);
}
$delete(uri: UriComponents, opts: FileDeleteOptions): Promise<void> {
@@ -121,7 +135,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
}
}
class RemoteFileSystemProvider implements IFileSystemProvider {
class RemoteFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileFolderCopyCapability {
private readonly _onDidChange = new Emitter<readonly IFileChange[]>();
private readonly _registration: IDisposable;

View File

@@ -3,21 +3,31 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { FileChangeType, IFileService, FileOperation } from 'vs/platform/files/common/files';
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, FileSystemEvents, IExtHostContext } from '../common/extHost.protocol';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { localize } from 'vs/nls';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
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';
@extHostCustomer
export class MainThreadFileSystemEventService {
private readonly _listener = new Array<IDisposable>();
private readonly _listener = new DisposableStore();
constructor(
extHostContext: IExtHostContext,
@IFileService fileService: IFileService,
@ITextFileService textfileService: ITextFileService,
@ITextFileService textFileService: ITextFileService,
@IProgressService progressService: IProgressService,
@IConfigurationService configService: IConfigurationService,
@ILogService logService: ILogService,
) {
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService);
@@ -28,7 +38,7 @@ export class MainThreadFileSystemEventService {
changed: [],
deleted: []
};
fileService.onFileChanges(event => {
this._listener.add(fileService.onFileChanges(event => {
for (let change of event.changes) {
switch (change.type) {
case FileChangeType.ADDED:
@@ -47,22 +57,64 @@ export class MainThreadFileSystemEventService {
events.created.length = 0;
events.changed.length = 0;
events.deleted.length = 0;
}, undefined, this._listener);
}));
// file operation events - (changes the editor makes)
fileService.onAfterOperation(e => {
if (e.isOperation(FileOperation.MOVE)) {
proxy.$onFileRename(e.resource, e.target.resource);
// BEFORE file operation
const messages = new Map<FileOperation, string>();
messages.set(FileOperation.CREATE, localize('msg-create', "Running 'File Create' participants..."));
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 => {
const timeout = configService.getValue<number>('files.participants.timeout');
if (timeout <= 0) {
return; // disabled
}
}, undefined, this._listener);
textfileService.onWillMove(e => {
const promise = proxy.$onWillRename(e.oldResource, e.newResource);
e.waitUntil(promise);
}, undefined, this._listener);
const p = progressService.withProgress({ location: ProgressLocation.Window }, progress => {
progress.report({ message: messages.get(e.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);
cts.cancel();
reject(new Error('timeout'));
}, timeout);
proxy.$onWillRunFileOperation(e.operation, e.target, e.source, timeout, cts.token)
.then(resolve, reject)
.finally(() => clearTimeout(timeoutHandle));
});
});
e.waitUntil(p);
}));
// AFTER file operation
this._listener.add(textFileService.onDidRunOperation(e => proxy.$onDidRunFileOperation(e.operation, e.target, e.source)));
}
dispose(): void {
dispose(this._listener);
this._listener.dispose();
}
}
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
id: 'files',
properties: {
'files.participants.timeout': {
type: 'number',
default: 5000,
markdownDescription: localize('files.participants.timeout', "Timeout in milliseconds after which file participants for create, rename, and delete are cancelled. Use `0` to disable participants."),
}
}
});

View File

@@ -21,6 +21,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import * as callh from 'vs/workbench/contrib/callHierarchy/browser/callHierarchy';
import { mixin } from 'vs/base/common/objects';
import { decodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokens';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
@@ -324,9 +325,16 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}));
}
// --- semantic tokens
$registerSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void {
this._registrations.set(handle, modes.SemanticTokensProviderRegistry.register(selector, new MainThreadSemanticTokensProvider(this._proxy, handle, legend)));
}
// --- suggest
private static _inflateSuggestDto(defaultRange: IRange, data: ISuggestDataDto): modes.CompletionItem {
private static _inflateSuggestDto(defaultRange: IRange | { insert: IRange, replace: IRange }, data: ISuggestDataDto): modes.CompletionItem {
return {
label: data[ISuggestDataDtoField.label],
kind: data[ISuggestDataDtoField.kind],
@@ -337,8 +345,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
filterText: data[ISuggestDataDtoField.filterText],
preselect: data[ISuggestDataDtoField.preselect],
insertText: typeof data.h === 'undefined' ? data[ISuggestDataDtoField.label] : data.h,
insertTextRules: data[ISuggestDataDtoField.insertTextRules],
range: data[ISuggestDataDtoField.range] || defaultRange,
insertTextRules: data[ISuggestDataDtoField.insertTextRules],
commitCharacters: data[ISuggestDataDtoField.commitCharacters],
additionalTextEdits: data[ISuggestDataDtoField.additionalTextEdits],
command: data[ISuggestDataDtoField.command],
@@ -371,6 +379,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
if (!result) {
return suggestion;
}
let newSuggestion = MainThreadLanguageFeatures._inflateSuggestDto(suggestion.range, result);
return mixin(suggestion, newSuggestion, true);
});
@@ -495,29 +504,37 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, callh.CallHierarchyProviderRegistry.register(selector, {
provideOutgoingCalls: async (model, position, token) => {
const outgoing = await this._proxy.$provideCallHierarchyOutgoingCalls(handle, model.uri, position, token);
prepareCallHierarchy: async (document, position, token) => {
const item = await this._proxy.$prepareCallHierarchy(handle, document.uri, position, token);
if (!item) {
return undefined;
}
return {
dispose: () => this._proxy.$releaseCallHierarchy(handle, item._sessionId),
root: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item)
};
},
provideOutgoingCalls: async (item, token) => {
const outgoing = await this._proxy.$provideCallHierarchyOutgoingCalls(handle, item._sessionId, item._itemId, token);
if (!outgoing) {
return undefined; // {{SQL CARBON EDIT}} strict-null-check
}
return outgoing.map(([item, fromRanges]): callh.OutgoingCall => {
return {
to: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item),
fromRanges
};
outgoing.forEach(value => {
value.to = MainThreadLanguageFeatures._reviveCallHierarchyItemDto(value.to);
});
return <any>outgoing;
},
provideIncomingCalls: async (model, position, token) => {
const incoming = await this._proxy.$provideCallHierarchyIncomingCalls(handle, model.uri, position, token);
provideIncomingCalls: async (item, token) => {
const incoming = await this._proxy.$provideCallHierarchyIncomingCalls(handle, item._sessionId, item._itemId, token);
if (!incoming) {
return undefined; // {{SQL CARBON EDIT}} strict-null-check
}
return incoming.map(([item, fromRanges]): callh.IncomingCall => {
return {
from: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item),
fromRanges
};
incoming.forEach(value => {
value.from = MainThreadLanguageFeatures._reviveCallHierarchyItemDto(value.from);
});
return <any>incoming;
}
}));
}
@@ -585,3 +602,45 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
export class MainThreadSemanticTokensProvider implements modes.SemanticTokensProvider {
constructor(
private readonly _proxy: ExtHostLanguageFeaturesShape,
private readonly _handle: number,
private readonly _legend: modes.SemanticTokensLegend,
) {
}
public releaseSemanticTokens(resultId: string | undefined): void {
if (resultId) {
this._proxy.$releaseSemanticTokens(this._handle, parseInt(resultId, 10));
}
}
public getLegend(): modes.SemanticTokensLegend {
return this._legend;
}
async provideSemanticTokens(model: ITextModel, lastResultId: string | null, ranges: EditorRange[] | null, token: CancellationToken): Promise<modes.SemanticTokens | modes.SemanticTokensEdits | null> {
const nLastResultId = lastResultId ? parseInt(lastResultId, 10) : 0;
const encodedDto = await this._proxy.$provideSemanticTokens(this._handle, model.uri, ranges, nLastResultId, token);
if (!encodedDto) {
return null;
}
if (token.isCancellationRequested) {
return null;
}
const dto = decodeSemanticTokensDto(encodedDto);
if (dto.type === 'full') {
return {
resultId: String(dto.id),
data: dto.data
};
}
return {
resultId: String(dto.id),
edits: dto.deltas
};
}
}

View File

@@ -288,7 +288,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): void {
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri && URI.revive(rootUri), this.scmService);
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined, this.scmService);
const repository = this.scmService.registerSCMProvider(provider);
this._repositories.set(handle, repository);

View File

@@ -19,7 +19,7 @@ import { CodeAction } from 'vs/editor/common/modes';
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/codeActionTrigger';
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';
@@ -30,7 +30,8 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { ISaveParticipant, SaveReason, IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; // {{SQL CARBON EDIT}}
import { ISaveParticipant, IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { SaveReason } from 'vs/workbench/common/editor';
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; // {{SQL CARBON EDIT}}
@@ -53,7 +54,7 @@ class NotebookUpdateParticipant implements ISaveParticipantParticipant { // {{SQ
}
public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
let uri = model.getResource();
let uri = model.resource;
let notebookEditor = this.notebookService.findNotebookEditor(uri);
if (notebookEditor) {
notebookEditor.notebookParams.input.updateModel();
@@ -76,7 +77,7 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
}
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO);
}
}
@@ -138,7 +139,7 @@ export class FinalNewLineParticipant implements ISaveParticipantParticipant {
}
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
this.doInsertFinalNewLine(model.textEditorModel);
}
}
@@ -172,7 +173,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant
}
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO);
}
}
@@ -282,7 +283,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
const model = editorModel.textEditorModel;
const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() };
const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.resource };
const setting = this._configurationService.getValue<ICodeActionsOnSaveOptions>('editor.codeActionsOnSave', settingsOverrides);
if (!setting) {
return undefined;
@@ -307,6 +308,10 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
return undefined;
}
const excludedActions = Object.keys(setting)
.filter(x => setting[x] === false)
.map(x => new CodeActionKind(x));
const tokenSource = new CancellationTokenSource();
const timeout = this._configurationService.getValue<number>('editor.codeActionsOnSaveTimeout', settingsOverrides);
@@ -317,17 +322,17 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
tokenSource.cancel();
reject(localize('codeActionsOnSave.didTimeout', "Aborted codeActionsOnSave after {0}ms", timeout));
}, timeout)),
this.applyOnSaveActions(model, codeActionsOnSave, tokenSource.token)
this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, tokenSource.token)
]).finally(() => {
tokenSource.cancel();
});
}
private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: CodeActionKind[], token: CancellationToken): Promise<void> {
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, token);
const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token);
try {
await this.applyCodeActions(actionsToRun.actions);
await this.applyCodeActions(actionsToRun.validActions);
} catch {
// Failure to apply a code action should not block other on save actions
} finally {
@@ -342,10 +347,10 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
}
}
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, token: CancellationToken) {
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) {
return getCodeActions(model, model.getFullModelRange(), {
type: 'auto',
filter: { kind: codeActionKind, includeSourceActions: true },
filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true },
}, token);
}
}
@@ -368,7 +373,7 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant {
return new Promise<any>((resolve, reject) => {
setTimeout(() => reject(localize('timeout.onWillSave', "Aborted onWillSaveTextDocument-event after 1750ms")), 1750);
this._proxy.$participateInSave(editorModel.getResource(), env.reason).then(values => {
this._proxy.$participateInSave(editorModel.resource, env.reason).then(values => {
if (!values.every(success => success)) {
return Promise.reject(new Error('listener failed'));
}

View File

@@ -12,6 +12,7 @@ import { StopWatch } from 'vs/base/common/stopwatch';
import { ITerminalInstanceService, ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
@@ -162,15 +163,22 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalClosed(terminalInstance.id);
this._proxy.$acceptTerminalClosed(terminalInstance.id, terminalInstance.exitCode);
}
private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
const shellLaunchConfigDto: IShellLaunchConfigDto = {
name: terminalInstance.shellLaunchConfig.name,
executable: terminalInstance.shellLaunchConfig.executable,
args: terminalInstance.shellLaunchConfig.args,
cwd: terminalInstance.shellLaunchConfig.cwd,
env: terminalInstance.shellLaunchConfig.env
};
if (terminalInstance.title) {
this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title);
this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title, shellLaunchConfigDto);
} else {
terminalInstance.waitForTitle().then(title => {
this._proxy.$acceptTerminalOpened(terminalInstance.id, title);
this._proxy.$acceptTerminalOpened(terminalInstance.id, title, shellLaunchConfigDto);
});
}
}
@@ -256,7 +264,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd));
}
public $sendProcessExit(terminalId: number, exitCode: number): void {
public $sendProcessExit(terminalId: number, exitCode: number | undefined): void {
this._getTerminalProcess(terminalId).then(e => e.emitExit(exitCode));
this._terminalProcesses.delete(terminalId);
}
@@ -327,16 +335,23 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
* listeners are removed.
*/
class TerminalDataEventTracker extends Disposable {
private readonly _bufferer: TerminalDataBufferer;
constructor(
private readonly _callback: (id: number, data: string) => void,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super();
this._register(this._bufferer = new TerminalDataBufferer());
this._terminalService.terminalInstances.forEach(instance => this._registerInstance(instance));
this._register(this._terminalService.onInstanceCreated(instance => this._registerInstance(instance)));
this._register(this._terminalService.onInstanceDisposed(instance => this._bufferer.stopBuffering(instance.id)));
}
private _registerInstance(instance: ITerminalInstance): void {
this._register(instance.onData(e => this._callback(instance.id, e)));
// Buffer data events to reduce the amount of messages going to the extension host
this._register(this._bufferer.startBuffering(instance.id, instance.onData, this._callback));
}
}

View File

@@ -12,6 +12,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { isUndefinedOrNull, isNumber } from 'vs/base/common/types';
import { Registry } from 'vs/platform/registry/common/platform';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape {
@@ -23,13 +24,16 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
extHostContext: IExtHostContext,
@IViewsService private readonly viewsService: IViewsService,
@INotificationService private readonly notificationService: INotificationService,
@IExtensionService private readonly extensionService: IExtensionService
@IExtensionService private readonly extensionService: IExtensionService,
@ILogService private readonly logService: ILogService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
}
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void {
this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options);
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
@@ -49,6 +53,8 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
}
$reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void> {
this.logService.trace('MainThreadTreeViews#$reveal', treeViewId, item, parentChain, options);
return this.viewsService.openView(treeViewId, options.focus)
.then(() => {
const viewer = this.getTreeView(treeViewId);
@@ -60,6 +66,8 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
}
$refresh(treeViewId: string, itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }): Promise<void> {
this.logService.trace('MainThreadTreeViews#$refresh', treeViewId, itemsToRefreshByHandle);
const viewer = this.getTreeView(treeViewId);
const dataProvider = this._dataProviders.get(treeViewId);
if (viewer && dataProvider) {
@@ -70,6 +78,8 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
}
$setMessage(treeViewId: string, message: string): void {
this.logService.trace('MainThreadTreeViews#$setMessage', treeViewId, message);
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.message = message;
@@ -77,6 +87,8 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
}
$setTitle(treeViewId: string, title: string): void {
this.logService.trace('MainThreadTreeViews#$setTitle', treeViewId, title);
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.title = title;

View File

@@ -68,7 +68,11 @@ export class MainThreadUrls implements MainThreadUrlsShape {
return Promise.resolve(undefined);
}
async $createAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial<UriComponents> }): Promise<URI> {
async $createAppUri(uri: UriComponents): Promise<URI> {
return this.urlService.create(uri);
}
async $proposedCreateAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial<UriComponents> }): Promise<URI> {
const payload: Partial<UriComponents> = options && options.payload ? options.payload : Object.create(null);
// we define the authority to be the extension ID to ensure

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { isWeb } from 'vs/base/common/platform';
import { startsWith } from 'vs/base/common/strings';
@@ -20,6 +20,7 @@ import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } fr
import { IEditorInput } from 'vs/workbench/common/editor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
import { ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
@@ -98,6 +99,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
constructor(
context: extHostProtocol.IExtHostContext,
@IExtensionService extensionService: IExtensionService,
@ICustomEditorService private readonly _customEditorService: ICustomEditorService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IEditorService private readonly _editorService: IEditorService,
@IOpenerService private readonly _openerService: IOpenerService,
@@ -168,13 +170,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
webview.setName(value);
}
public $setState(handle: extHostProtocol.WebviewPanelHandle, state: modes.WebviewContentState): void {
const webview = this.getWebviewInput(handle);
if (webview instanceof CustomFileEditorInput) {
webview.setState(state);
}
}
public $setIconPath(handle: extHostProtocol.WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void {
const webview = this.getWebviewInput(handle);
webview.iconPath = reviveWebviewIcon(value);
@@ -268,7 +263,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
canResolve: (webviewInput) => {
return webviewInput instanceof CustomFileEditorInput && webviewInput.viewType === viewType;
},
resolveWebview: async (webviewInput) => {
resolveWebview: async (webviewInput: CustomFileEditorInput) => {
const handle = webviewInput.id;
this._webviewInputs.add(handle, webviewInput);
this.hookupWebviewEventDelegate(handle, webviewInput);
@@ -276,15 +271,25 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
webviewInput.webview.options = options;
webviewInput.webview.extension = extension;
if (webviewInput instanceof CustomFileEditorInput) {
webviewInput.onWillSave(e => {
e.waitUntil(this._proxy.$save(handle));
});
}
const model = await this._customEditorService.models.loadOrCreate(webviewInput.getResource(), webviewInput.viewType);
model.onUndo(edits => { this._proxy.$undoEdits(handle, edits.map(x => x.data)); });
model.onApplyEdit(edits => {
const editsToApply = edits.filter(x => x.source !== webviewInput).map(x => x.data);
if (editsToApply.length) {
this._proxy.$applyEdits(handle, editsToApply);
}
});
model.onWillSave(e => { e.waitUntil(this._proxy.$onSave(handle)); });
model.onWillSaveAs(e => { e.waitUntil(this._proxy.$onSaveAs(handle, e.resource.toJSON(), e.targetResource.toJSON())); });
webviewInput.onDisposeWebview(() => {
this._customEditorService.models.disposeModel(model);
});
try {
await this._proxy.$resolveWebviewEditor(
webviewInput.getResource(),
{ resource: webviewInput.getResource(), edits: model.currentEdits },
handle,
viewType,
webviewInput.getTitle(),
@@ -294,6 +299,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
} catch (error) {
onUnexpectedError(error);
webviewInput.webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
return;
}
}
}));
@@ -309,15 +315,35 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._editorProviders.delete(viewType);
}
public $onEdit(handle: extHostProtocol.WebviewPanelHandle, editData: any): void {
const webview = this.getWebviewInput(handle);
if (!(webview instanceof CustomFileEditorInput)) {
throw new Error('Webview is not a webview editor');
}
const model = this._customEditorService.models.get(webview.getResource(), webview.viewType);
if (!model) {
throw new Error('Could not find model for webview editor');
}
model.makeEdit({ source: webview, data: editData });
}
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) {
input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri));
input.webview.onMessage((message: any) => this._proxy.$onMessage(handle, message));
const disposables = new DisposableStore();
disposables.add(input.webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri)));
disposables.add(input.webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); }));
disposables.add(input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value)));
input.onDispose(() => {
disposables.dispose();
});
input.onDisposeWebview(() => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
this._webviewInputs.delete(handle);
});
});
input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value));
}
private updateWebviewViewStates() {
@@ -361,9 +387,9 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
}
}
private onDidClickLink(handle: extHostProtocol.WebviewPanelHandle, link: URI): void {
private onDidClickLink(handle: extHostProtocol.WebviewPanelHandle, link: string): void {
const webview = this.getWebviewInput(handle);
if (this.isSupportedLink(webview, link)) {
if (this.isSupportedLink(webview, URI.parse(link))) {
this._openerService.open(link, { fromUserGesture: true });
}
}

View File

@@ -42,9 +42,17 @@ export class MainThreadWindow implements MainThreadWindowShape {
return Promise.resolve(this.hostService.hasFocus);
}
async $openUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise<boolean> {
async $openUri(uriComponents: UriComponents, uriString: string | undefined, options: IOpenUriOptions): Promise<boolean> {
const uri = URI.from(uriComponents);
return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling });
let target: URI | string;
if (uriString && URI.parse(uriString).toString() === uri.toString()) {
// called with string and no transformation happened -> keep string
target = uriString;
} else {
// called with URI or transformed -> use uri
target = uri;
}
return this.openerService.open(target, { openExternal: true, allowTunneling: options.allowTunneling });
}
async $asExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise<UriComponents> {

View File

@@ -15,11 +15,11 @@ import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'v
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isEqualOrParent } from 'vs/base/common/resources';
import { isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IFileService } from 'vs/platform/files/common/files';
@@ -37,7 +37,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
extHostContext: IExtHostContext,
@ISearchService private readonly _searchService: ISearchService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IEditorService private readonly _editorService: IEditorService,
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService,
@INotificationService private readonly _notificationService: INotificationService,
@IRequestService private readonly _requestService: IRequestService,
@@ -121,7 +121,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
}
return {
configuration: workspace.configuration || undefined,
isUntitled: workspace.configuration ? isEqualOrParent(workspace.configuration, this._environmentService.untitledWorkspacesHome) : false,
isUntitled: workspace.configuration ? isUntitledWorkspace(workspace.configuration, this._environmentService) : false,
folders: workspace.folders,
id: workspace.id,
name: this._labelService.getWorkspaceLabel(workspace)
@@ -155,13 +155,14 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
if (!isPromiseCanceledError(err)) {
return Promise.reject(err);
}
return undefined;
return null;
});
}
$startTextSearch(pattern: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete> {
$startTextSearch(pattern: IPatternInfo, _folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null> {
const folder = URI.revive(_folder);
const workspace = this._contextService.getWorkspace();
const folders = workspace.folders.map(folder => folder.uri);
const folders = folder ? [folder] : workspace.folders.map(folder => folder.uri);
const query = this._queryBuilder.text(pattern, folders, options);
query._reason = 'startTextSearch';
@@ -181,7 +182,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
return Promise.reject(err);
}
return undefined;
return null;
});
return search;
@@ -198,23 +199,21 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
return this._searchService.fileSearch(query, token).then(
result => {
return result.limitHit;
return !!result.limitHit;
},
err => {
if (!isPromiseCanceledError(err)) {
return Promise.reject(err);
}
return undefined;
return false;
});
}
// --- save & edit resources ---
$saveAll(includeUntitled?: boolean): Promise<boolean> {
return this._textFileService.saveAll(includeUntitled).then(result => {
return result.results.every(each => each.success === true);
});
return this._editorService.saveAll({ includeUntitled });
}
$resolveProxy(url: string): Promise<string | undefined> {

View File

@@ -9,7 +9,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as resources from 'vs/base/common/resources';
import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor } from 'vs/workbench/common/views';
import { CustomTreeViewPanel, CustomTreeView } from 'vs/workbench/browser/parts/views/customView';
import { CustomTreeViewPane, CustomTreeView } from 'vs/workbench/browser/parts/views/customView';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { coalesce, } from 'vs/base/common/arrays';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
@@ -37,7 +37,6 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { createCSSRule, asCSSUrl } from 'vs/base/browser/dom';
export interface IUserFriendlyViewsContainerDescriptor {
id: string;
@@ -254,10 +253,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
private registerTestViewContainer(): void {
const title = localize('test', "Test");
const cssClass = `extensionViewlet-test`;
const icon = URI.parse(require.toUrl('./media/test.svg'));
this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, cssClass, undefined);
this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined);
}
private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean {
@@ -290,10 +288,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[]): number {
containers.forEach(descriptor => {
const cssClass = `extensionViewlet-${descriptor.id}`;
const icon = resources.joinPath(extension.extensionLocation, descriptor.icon);
const id = `workbench.view.extension.${descriptor.id}`;
const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, cssClass, extension.identifier);
const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, extension.identifier);
// Move those views that belongs to this container
if (existingViewContainers.length) {
@@ -311,7 +308,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return order;
}
private registerCustomViewContainer(id: string, title: string, icon: URI, order: number, cssClass: string, extensionId: ExtensionIdentifier | undefined): ViewContainer {
private registerCustomViewContainer(id: string, title: string, icon: URI, order: number, extensionId: ExtensionIdentifier | undefined): ViewContainer {
let viewContainer = this.viewContainersRegistry.get(id);
if (!viewContainer) {
@@ -335,11 +332,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
super(id, `${id}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
}
}
const viewletDescriptor = new ViewletDescriptor(
const viewletDescriptor = ViewletDescriptor.create(
CustomViewlet,
id,
title,
cssClass,
undefined,
order,
icon
);
@@ -359,14 +356,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(
new SyncActionDescriptor(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", title)),
SyncActionDescriptor.create(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", title)),
`View: Show ${title}`,
localize('view', "View")
);
// Generate CSS to show the icon in the activity bar
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`;
createCSSRule(iconClass, `-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; -webkit-mask-size: 24px;`);
}
return viewContainer;
@@ -429,7 +422,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
const viewDescriptor = <ICustomViewDescriptor>{
id: item.id,
name: item.name,
ctorDescriptor: { ctor: CustomTreeViewPanel },
ctorDescriptor: { ctor: CustomTreeViewPane },
when: ContextKeyExpr.deserialize(item.when),
canToggleVisibility: true,
collapsed: this.showCollapsed(container),

View File

@@ -309,7 +309,9 @@ jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', {
$ref: 'vscode://schemas/extensions'
},
'remoteAuthority': {
type: 'string'
type: 'string',
doNotSuggest: true,
description: nls.localize('workspaceConfig.remoteAuthority', "The remote server where the workspace is located. Only used by unsaved remote workspaces."),
}
},
additionalProperties: false,

View File

@@ -106,7 +106,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, accessor.get(IExtHostOutputService));
// manually create and register addressable instances
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace));
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService));
const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol));
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
@@ -114,10 +114,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment));
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol));
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
@@ -133,7 +133,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
// Other instances
const extHostClipboard = new ExtHostClipboard(rpcProtocol);
const extHostMessageService = new ExtHostMessageService(rpcProtocol);
const extHostMessageService = new ExtHostMessageService(rpcProtocol, extHostLogService);
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
@@ -152,7 +152,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
let done = (!extension.isUnderDevelopment);
function informOnce(selector: vscode.DocumentSelector) {
if (!done) {
console.info(`Extension '${extension.identifier.value}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`);
extHostLogService.info(`Extension '${extension.identifier.value}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`);
done = true;
}
}
@@ -183,20 +183,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostCommands.registerCommand(true, id, (...args: any[]): any => {
const activeTextEditor = extHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
extHostLogService.warn('Cannot execute ' + id + ' because there is no active text editor.');
return undefined;
}
return activeTextEditor.edit((edit: vscode.TextEditorEdit) => {
args.unshift(activeTextEditor, edit);
callback.apply(thisArg, args);
callback.apply(thisArg, [activeTextEditor, edit, ...args]);
}).then((result) => {
if (!result) {
console.warn('Edits from command ' + id + ' were not applied.');
extHostLogService.warn('Edits from command ' + id + ' were not applied.');
}
}, (err) => {
console.warn('An error occurred while running command ' + id, err);
extHostLogService.warn('An error occurred while running command ' + id, err);
});
});
},
@@ -205,7 +204,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise<any> => {
const activeTextEditor = extHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
extHostLogService.warn('Cannot execute ' + id + ' because there is no active text editor.');
return undefined;
}
@@ -231,7 +230,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
get uriScheme() { return initData.environment.appUriScheme; },
createAppUri(options?) {
checkProposedApiEnabled(extension);
return extHostUrls.createAppUri(extension.identifier, options);
return extHostUrls.proposedCreateAppUri(extension.identifier, options);
},
get logLevel() {
checkProposedApiEnabled(extension);
@@ -251,6 +250,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote });
},
asExternalUri(uri: URI) {
if (uri.scheme === initData.environment.appUriScheme) {
return extHostUrls.createAppUri(uri);
}
return extHostWindow.asExternalUri(uri, { allowTunneling: !!initData.remote.isRemote });
},
get remoteName() {
@@ -351,6 +354,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable {
return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(extension, checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
},
registerSemanticTokensProvider(selector: vscode.DocumentSelector, provider: vscode.SemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerSemanticTokensProvider(extension, checkSelector(selector), provider, legend);
},
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, firstItem?: string | vscode.SignatureHelpProviderMetadata, ...remaining: string[]): vscode.Disposable {
if (typeof firstItem === 'object') {
return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, firstItem);
@@ -372,8 +379,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerSelectionRangeProvider(selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable {
return extHostLanguageFeatures.registerSelectionRangeProvider(extension, selector, provider);
},
registerCallHierarchyProvider(selector: vscode.DocumentSelector, provider: vscode.CallHierarchyItemProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
registerCallHierarchyProvider(selector: vscode.DocumentSelector, provider: vscode.CallHierarchyProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCallHierarchyProvider(extension, selector, provider);
},
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
@@ -698,11 +704,27 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostLabelService.$registerResourceLabelFormatter(formatter);
},
onDidRenameFile: (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
onDidCreateFiles: (listener, thisArg, disposables) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.onDidCreateFile(listener, thisArg, disposables);
},
onDidDeleteFiles: (listener, thisArg, disposables) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.onDidDeleteFile(listener, thisArg, disposables);
},
onDidRenameFiles: (listener, thisArg, disposables) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
},
onWillRenameFile: (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
onWillCreateFiles: (listener: (e: vscode.FileWillCreateEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.getOnWillCreateFileEvent(extension)(listener, thisArg, disposables);
},
onWillDeleteFiles: (listener: (e: vscode.FileWillDeleteEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.getOnWillDeleteFileEvent(extension)(listener, thisArg, disposables);
},
onWillRenameFiles: (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables);
}
@@ -770,6 +792,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
},
removeBreakpoints(breakpoints: vscode.Breakpoint[]) {
return undefined;
},
asDebugSourceUri(source: vscode.DebugProtocolSource, session?: vscode.DebugSession): vscode.Uri {
return undefined;
}
};
@@ -836,6 +861,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
ConfigurationTarget: extHostTypes.ConfigurationTarget,
DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
DebugAdapterServer: extHostTypes.DebugAdapterServer,
DebugAdapterInlineImplementation: extHostTypes.DebugAdapterInlineImplementation,
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
Diagnostic: extHostTypes.Diagnostic,
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
@@ -850,6 +876,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
EventEmitter: Emitter,
ExtensionKind: extHostTypes.ExtensionKind,
CustomExecution: extHostTypes.CustomExecution,
CustomExecution2: extHostTypes.CustomExecution,
FileChangeType: extHostTypes.FileChangeType,
FileSystemError: extHostTypes.FileSystemError,
FileType: files.FileType,
@@ -871,6 +898,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
RelativePattern: extHostTypes.RelativePattern,
ResolvedAuthority: extHostTypes.ResolvedAuthority,
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
SemanticTokensLegend: extHostTypes.SemanticTokensLegend,
SemanticTokensBuilder: extHostTypes.SemanticTokensBuilder,
SemanticTokens: extHostTypes.SemanticTokens,
SemanticTokensEdits: extHostTypes.SemanticTokensEdits,
SemanticTokensEdit: extHostTypes.SemanticTokensEdit,
Selection: extHostTypes.Selection,
SelectionRange: extHostTypes.SelectionRange,
ShellExecution: extHostTypes.ShellExecution,

View File

@@ -45,7 +45,7 @@ import { ITerminalDimensions, IShellLaunchConfig } from 'vs/workbench/contrib/te
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import * as search from 'vs/workbench/services/search/common/search';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { SaveReason } from 'vs/workbench/common/editor';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
// {{SQL CARBON EDIT}}
@@ -74,7 +74,7 @@ export interface IStaticWorkspaceData {
}
export interface IWorkspaceData extends IStaticWorkspaceData {
folders: { uri: UriComponents, name: string, index: number }[];
folders: { uri: UriComponents, name: string, index: number; }[];
}
export interface IInitData {
@@ -101,7 +101,7 @@ export interface IConfigurationInitData extends IConfigurationData {
export interface IWorkspaceConfigurationChangeEventData {
changedConfiguration: IConfigurationModel;
changedConfigurationByResource: { [folder: string]: IConfigurationModel };
changedConfigurationByResource: { [folder: string]: IConfigurationModel; };
}
export interface IExtHostContext extends IRPCProtocol {
@@ -135,12 +135,20 @@ export interface CommentProviderFeatures {
reactionHandler?: boolean;
}
export type CommentThreadChanges = Partial<{
range: IRange,
label: string,
contextValue: string,
comments: modes.Comment[],
collapseState: modes.CommentThreadCollapsibleState;
}>;
export interface MainThreadCommentsShape extends IDisposable {
$registerCommentController(handle: number, id: string, label: string): void;
$unregisterCommentController(handle: number): void;
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, extensionId: ExtensionIdentifier): modes.CommentThread | undefined;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, label: string | undefined, contextValue: string | undefined, comments: modes.Comment[], collapseState: modes.CommentThreadCollapsibleState): void;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, changes: CommentThreadChanges): void;
$deleteCommentThread(handle: number, commentThreadHandle: number): void;
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void;
}
@@ -161,13 +169,13 @@ export interface MainThreadDialogOpenOptions {
canSelectFiles?: boolean;
canSelectFolders?: boolean;
canSelectMany?: boolean;
filters?: { [name: string]: string[] };
filters?: { [name: string]: string[]; };
}
export interface MainThreadDialogSaveOptions {
defaultUri?: UriComponents;
saveLabel?: string;
filters?: { [name: string]: string[] };
filters?: { [name: string]: string[]; };
}
export interface MainThreadDiaglogsShape extends IDisposable {
@@ -250,8 +258,8 @@ export interface MainThreadTextEditorsShape extends IDisposable {
}
export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise<void>;
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise<void>;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void>;
$setMessage(treeViewId: string, message: string): void;
$setTitle(treeViewId: string, title: string): void;
@@ -274,7 +282,7 @@ export interface MainThreadKeytarShape extends IDisposable {
$setPassword(service: string, account: string, password: string): Promise<void>;
$deletePassword(service: string, account: string): Promise<boolean>;
$findPassword(service: string): Promise<string | null>;
$findCredentials(service: string): Promise<Array<{ account: string, password: string }>>;
$findCredentials(service: string): Promise<Array<{ account: string, password: string; }>>;
}
export interface IRegExpDto {
@@ -317,7 +325,7 @@ export interface ILanguageConfigurationDto {
};
}
export type GlobPattern = string | { base: string; pattern: string };
export type GlobPattern = string | { base: string; pattern: string; };
export interface IDocumentFilterDto {
$serialized: true;
@@ -350,6 +358,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
$registerNavigateTypeSupport(handle: number): void;
$registerRenameSupport(handle: number, selector: IDocumentFilterDto[], supportsResolveInitialValues: boolean): void;
$registerSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void;
$registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void;
$registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void;
$registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void;
@@ -396,7 +405,7 @@ export interface TerminalLaunchConfig {
shellPath?: string;
shellArgs?: string[] | string;
cwd?: string | UriComponents;
env?: { [key: string]: string | null };
env?: { [key: string]: string | null; };
waitOnExit?: boolean;
strictEnv?: boolean;
hideFromUser?: boolean;
@@ -404,7 +413,7 @@ export interface TerminalLaunchConfig {
}
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(config: TerminalLaunchConfig): Promise<{ id: number, name: string }>;
$createTerminal(config: TerminalLaunchConfig): Promise<{ id: number, name: string; }>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
@@ -416,7 +425,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$sendProcessTitle(terminalId: number, title: string): void;
$sendProcessData(terminalId: number, data: string): void;
$sendProcessReady(terminalId: number, pid: number, cwd: string): void;
$sendProcessExit(terminalId: number, exitCode: number): void;
$sendProcessExit(terminalId: number, exitCode: number | undefined): void;
$sendProcessInitialCwd(terminalId: number, cwd: string): void;
$sendProcessCwd(terminalId: number, initialCwd: string): void;
$sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void;
@@ -471,6 +480,8 @@ export interface TransferQuickPick extends BaseTransferQuickInput {
matchOnDescription?: boolean;
matchOnDetail?: boolean;
sortByLabel?: boolean;
}
export interface TransferInputBox extends BaseTransferQuickInput {
@@ -554,8 +565,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void;
$setTitle(handle: WebviewPanelHandle, value: string): void;
$setState(handle: WebviewPanelHandle, state: modes.WebviewContentState): void;
$setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void;
$setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void;
$setHtml(handle: WebviewPanelHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void;
@@ -567,6 +577,8 @@ export interface MainThreadWebviewsShape extends IDisposable {
$registerEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void;
$unregisterEditorProvider(viewType: string): void;
$onEdit(handle: WebviewPanelHandle, editJson: any): void;
}
export interface WebviewPanelViewStateData {
@@ -582,15 +594,22 @@ export interface ExtHostWebviewsShape {
$onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void;
$onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
$save(handle: WebviewPanelHandle): Promise<boolean>;
$resolveWebviewEditor(input: { resource: UriComponents, edits: readonly any[] }, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
$undoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void;
$applyEdits(handle: WebviewPanelHandle, edits: readonly any[]): void;
$onSave(handle: WebviewPanelHandle): Promise<void>;
$onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise<void>;
}
export interface MainThreadUrlsShape extends IDisposable {
$registerUriHandler(handle: number, extensionId: ExtensionIdentifier): Promise<void>;
$unregisterUriHandler(handle: number): Promise<void>;
$createAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial<UriComponents> }): Promise<UriComponents>;
$createAppUri(uri: UriComponents): Promise<UriComponents>;
$proposedCreateAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial<UriComponents>; }): Promise<UriComponents>;
}
export interface ExtHostUrlsShape {
@@ -603,10 +622,10 @@ export interface ITextSearchComplete {
export interface MainThreadWorkspaceShape extends IDisposable {
$startFileSearch(includePattern: string | null, includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false | null, maxResults: number | null, token: CancellationToken): Promise<UriComponents[] | null>;
$startTextSearch(query: search.IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete>;
$startTextSearch(query: search.IPatternInfo, folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null>;
$checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise<boolean>;
$saveAll(includeUntitled?: boolean): Promise<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Promise<void>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string; }[]): Promise<void>;
$resolveProxy(url: string): Promise<string | undefined>;
}
@@ -753,7 +772,7 @@ export interface IOpenUriOptions {
export interface MainThreadWindowShape extends IDisposable {
$getWindowVisibility(): Promise<boolean>;
$openUri(uri: UriComponents, options: IOpenUriOptions): Promise<boolean>;
$openUri(uri: UriComponents, uriString: string | undefined, options: IOpenUriOptions): Promise<boolean>;
$asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise<UriComponents>;
}
@@ -761,7 +780,7 @@ export interface MainThreadWindowShape extends IDisposable {
export interface ExtHostCommandsShape {
$executeContributedCommand<T>(id: string, ...args: any[]): Promise<T>;
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }>;
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription; }>;
}
export interface ExtHostConfigurationShape {
@@ -896,7 +915,7 @@ export interface ExtHostExtensionServiceShape {
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
$activateByEvent(activationEvent: string): Promise<void>;
$activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean>;
$setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
$setRemoteEnvironment(env: { [key: string]: string | null; }): Promise<void>;
$deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void>;
@@ -910,10 +929,11 @@ export interface FileSystemEvents {
changed: UriComponents[];
deleted: UriComponents[];
}
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onFileRename(oldUri: UriComponents, newUri: UriComponents): void;
$onWillRename(oldUri: UriComponents, newUri: UriComponents): Promise<any>;
$onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined, timeout: number, token: CancellationToken): Promise<any>;
$onDidRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): void;
}
export interface ObjectIdentifier {
@@ -975,7 +995,7 @@ export interface ISuggestDataDto {
[ISuggestDataDtoField.preselect]?: boolean;
[ISuggestDataDtoField.insertText]?: string;
[ISuggestDataDtoField.insertTextRules]?: modes.CompletionItemInsertTextRule;
[ISuggestDataDtoField.range]?: IRange;
[ISuggestDataDtoField.range]?: IRange | { insert: IRange, replace: IRange; };
[ISuggestDataDtoField.commitCharacters]?: string[];
[ISuggestDataDtoField.additionalTextEdits]?: ISingleEditOperation[];
[ISuggestDataDtoField.command]?: modes.Command;
@@ -986,7 +1006,7 @@ export interface ISuggestDataDto {
export interface ISuggestResultDto {
x?: number;
a: IRange;
a: { insert: IRange, replace: IRange; };
b: ISuggestDataDto[];
c?: boolean;
}
@@ -1075,6 +1095,7 @@ export interface ICodeActionDto {
command?: ICommandDto;
kind?: string;
isPreferred?: boolean;
disabled?: string;
}
export interface ICodeActionListDto {
@@ -1109,6 +1130,8 @@ export interface ICodeLensDto {
}
export interface ICallHierarchyItemDto {
_sessionId: string;
_itemId: string;
kind: modes.SymbolKind;
name: string;
detail?: string;
@@ -1117,6 +1140,16 @@ export interface ICallHierarchyItemDto {
selectionRange: IRange;
}
export interface IIncomingCallDto {
from: ICallHierarchyItemDto;
fromRanges: IRange[];
}
export interface IOutgoingCallDto {
fromRanges: IRange[];
to: ICallHierarchyItemDto;
}
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined>;
@@ -1139,6 +1172,8 @@ export interface ExtHostLanguageFeaturesShape {
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
$provideSemanticTokens(handle: number, resource: UriComponents, ranges: IRange[] | null, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null>;
$releaseSemanticTokens(handle: number, semanticColoringResultId: number): void;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<ISuggestResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<ISuggestDataDto | undefined>;
$releaseCompletionItems(handle: number, id: number): void;
@@ -1151,8 +1186,10 @@ export interface ExtHostLanguageFeaturesShape {
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Promise<modes.IColorPresentation[] | undefined>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext, token: CancellationToken): Promise<modes.FoldingRange[] | undefined>;
$provideSelectionRanges(handle: number, resource: UriComponents, positions: IPosition[], token: CancellationToken): Promise<modes.SelectionRange[][]>;
$provideCallHierarchyIncomingCalls(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<[ICallHierarchyItemDto, IRange[]][] | undefined>;
$provideCallHierarchyOutgoingCalls(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<[ICallHierarchyItemDto, IRange[]][] | undefined>;
$prepareCallHierarchy(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<ICallHierarchyItemDto | undefined>;
$provideCallHierarchyIncomingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<IIncomingCallDto[] | undefined>;
$provideCallHierarchyOutgoingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<IOutgoingCallDto[] | undefined>;
$releaseCallHierarchy(handle: number, sessionId: string): void;
}
export interface ExtHostQuickOpenShape {
@@ -1171,7 +1208,7 @@ export interface IShellLaunchConfigDto {
executable?: string;
args?: string[] | string;
cwd?: string | UriComponents;
env?: { [key: string]: string | null };
env?: { [key: string]: string | null; };
}
export interface IShellDefinitionDto {
@@ -1190,8 +1227,8 @@ export interface ITerminalDimensionsDto {
}
export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalOpened(id: number, name: string): void;
$acceptTerminalClosed(id: number, exitCode: number | undefined): void;
$acceptTerminalOpened(id: number, name: string, shellLaunchConfig: IShellLaunchConfigDto): void;
$acceptActiveTerminalChanged(id: number | null): void;
$acceptTerminalProcessId(id: number, processId: number): void;
$acceptTerminalProcessData(id: number, data: string): void;
@@ -1226,8 +1263,8 @@ export interface ExtHostTaskShape {
$onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): void;
$onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): void;
$OnDidEndTask(execution: tasks.TaskExecutionDTO): void;
$resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string }, variables: string[] }): Promise<{ process?: string; variables: { [key: string]: string } }>;
$getDefaultShellAndArgs(): Thenable<{ shell: string, args: string[] | string | undefined }>;
$resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string; }, variables: string[]; }): Promise<{ process?: string; variables: { [key: string]: string; }; }>;
$getDefaultShellAndArgs(): Thenable<{ shell: string, args: string[] | string | undefined; }>;
$jsonTasksSupported(): Thenable<boolean>;
}
@@ -1314,7 +1351,7 @@ export interface DecorationRequest {
}
export type DecorationData = [number, boolean, string, string, ThemeColor];
export type DecorationReply = { [id: number]: DecorationData };
export type DecorationReply = { [id: number]: DecorationData; };
export interface ExtHostDecorationsShape {
$provideDecorations(requests: DecorationRequest[], token: CancellationToken): Promise<DecorationReply>;

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as types from 'vs/workbench/api/common/extHostTypes';
import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto } from 'vs/workbench/api/common/extHost.protocol';
import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallDto, IOutgoingCallDto } from 'vs/workbench/api/common/extHost.protocol';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/contrib/search/common/search';
@@ -19,10 +19,97 @@ import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { IRange } from 'vs/editor/common/core/range';
import { IPosition } from 'vs/editor/common/core/position';
//#region --- NEW world
export class ApiCommandArgument<V, O = V> {
static readonly Uri = new ApiCommandArgument<URI>('uri', 'Uri of a text document', v => URI.isUri(v), v => v);
static readonly Position = new ApiCommandArgument<types.Position, IPosition>('position', 'A position in a text document', v => types.Position.isPosition(v), typeConverters.Position.from);
static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof types.CallHierarchyItem, typeConverters.CallHierarchyItem.to);
constructor(
readonly name: string,
readonly description: string,
readonly validate: (v: V) => boolean,
readonly convert: (v: V) => O
) { }
}
export class ApiCommandResult<V, O = V> {
constructor(
readonly description: string,
readonly convert: (v: V) => O
) { }
}
export class ApiCommand {
constructor(
readonly id: string,
readonly internalId: string,
readonly description: string,
readonly args: ApiCommandArgument<any, any>[],
readonly result: ApiCommandResult<any, any>
) { }
register(commands: ExtHostCommands): IDisposable {
return commands.registerCommand(false, this.id, async (...apiArgs) => {
const internalArgs = this.args.map((arg, i) => {
if (!arg.validate(apiArgs[i])) {
throw new Error(`Invalid argument '${arg.name}' when running '${this.id}', receieved: ${apiArgs[i]}`);
}
return arg.convert(apiArgs[i]);
});
const internalResult = await commands.executeCommand(this.internalId, ...internalArgs);
return this.result.convert(internalResult);
}, undefined, this._getCommandHandlerDesc());
}
private _getCommandHandlerDesc(): ICommandHandlerDescription {
return {
description: this.description,
args: this.args,
returns: this.result.description
};
}
}
const newCommands: ApiCommand[] = [
new ApiCommand(
'vscode.prepareCallHierarchy', '_executePrepareCallHierarchy', 'Prepare call hierarchy at a position inside a document',
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
new ApiCommandResult<ICallHierarchyItemDto[], types.CallHierarchyItem[]>('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyItem.to))
),
new ApiCommand(
'vscode.provideIncomingCalls', '_executeProvideIncomingCalls', 'Compute incoming calls for an item',
[ApiCommandArgument.CallHierarchyItem],
new ApiCommandResult<IIncomingCallDto[], types.CallHierarchyIncomingCall[]>('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyIncomingCall.to))
),
new ApiCommand(
'vscode.provideOutgoingCalls', '_executeProvideOutgoingCalls', 'Compute outgoing calls for an item',
[ApiCommandArgument.CallHierarchyItem],
new ApiCommandResult<IOutgoingCallDto[], types.CallHierarchyOutgoingCall[]>('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyOutgoingCall.to))
),
];
//#endregion
//#region OLD world
export class ExtHostApiCommands {
static register(commands: ExtHostCommands) {
newCommands.forEach(command => command.register(commands));
return new ExtHostApiCommands(commands).registerCommands();
}
@@ -182,22 +269,6 @@ export class ExtHostApiCommands {
],
returns: 'A promise that resolves to an array of DocumentLink-instances.'
});
this._register('vscode.executeCallHierarchyProviderIncomingCalls', this._executeCallHierarchyIncomingCallsProvider, {
description: 'Execute call hierarchy provider for incoming calls',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
],
returns: 'A promise that resolves to an array of CallHierarchyIncomingCall-instances.'
});
this._register('vscode.executeCallHierarchyProviderOutgoingCalls', this._executeCallHierarchyOutgoingCallsProvider, {
description: 'Execute call hierarchy provider for outgoing calls',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
],
returns: 'A promise that resolves to an array of CallHierarchyOutgoingCall-instances.'
});
this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, {
description: 'Execute document color provider.',
args: [
@@ -449,7 +520,7 @@ export class ExtHostApiCommands {
});
}
private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range }): Promise<types.ColorPresentation[]> {
private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range; }): Promise<types.ColorPresentation[]> {
const args = {
resource: context.uri,
color: typeConverters.Color.from(color),
@@ -573,36 +644,6 @@ export class ExtHostApiCommands {
return this._commands.executeCommand<modes.ILink[]>('_executeLinkProvider', resource)
.then(tryMapWith(typeConverters.DocumentLink.to));
}
private async _executeCallHierarchyIncomingCallsProvider(resource: URI, position: types.Position): Promise<vscode.CallHierarchyIncomingCall[]> {
type IncomingCallDto = {
from: ICallHierarchyItemDto;
fromRanges: IRange[];
};
const args = { resource, position: typeConverters.Position.from(position) };
const calls = await this._commands.executeCommand<IncomingCallDto[]>('_executeCallHierarchyIncomingCalls', args);
const result: vscode.CallHierarchyIncomingCall[] = [];
for (const call of calls) {
result.push(new types.CallHierarchyIncomingCall(typeConverters.CallHierarchyItem.to(call.from), <vscode.Range[]>call.fromRanges.map(typeConverters.Range.to)));
}
return result;
}
private async _executeCallHierarchyOutgoingCallsProvider(resource: URI, position: types.Position): Promise<vscode.CallHierarchyOutgoingCall[]> {
type OutgoingCallDto = {
fromRanges: IRange[];
to: ICallHierarchyItemDto;
};
const args = { resource, position: typeConverters.Position.from(position) };
const calls = await this._commands.executeCommand<OutgoingCallDto[]>('_executeCallHierarchyOutgoingCalls', args);
const result: vscode.CallHierarchyOutgoingCall[] = [];
for (const call of calls) {
result.push(new types.CallHierarchyOutgoingCall(typeConverters.CallHierarchyItem.to(call.to), <vscode.Range[]>call.fromRanges.map(typeConverters.Range.to)));
}
return result;
}
}
function tryMapWith<T, R>(f: (x: T) => R) {

View File

@@ -47,7 +47,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadCommands);
this._logService = logService;
this._converter = new CommandsConverter(this);
this._converter = new CommandsConverter(this, logService);
this._argumentProcessors = [
{
processArgument(a) {
@@ -218,14 +218,15 @@ export class ExtHostCommands implements ExtHostCommandsShape {
export class CommandsConverter {
private readonly _delegatingCommandId: string;
private readonly _commands: ExtHostCommands;
private readonly _cache = new Map<number, vscode.Command>();
private _cachIdPool = 0;
// --- conversion between internal and api commands
constructor(commands: ExtHostCommands) {
constructor(
private readonly _commands: ExtHostCommands,
private readonly _logService: ILogService
) {
this._delegatingCommandId = `_vscode_delegate_cmd_${Date.now().toString(36)}`;
this._commands = commands;
this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this);
}
@@ -248,12 +249,16 @@ export class CommandsConverter {
const id = ++this._cachIdPool;
this._cache.set(id, command);
disposables.add(toDisposable(() => this._cache.delete(id)));
disposables.add(toDisposable(() => {
this._cache.delete(id);
this._logService.trace('CommandsConverter#DISPOSE', id);
}));
result.$ident = id;
result.id = this._delegatingCommandId;
result.arguments = [id];
this._logService.trace('CommandsConverter#CREATE', command.command, id);
}
return result;
@@ -276,6 +281,8 @@ export class CommandsConverter {
private _executeConvertedCommand<R>(...args: any[]): Promise<R> {
const actualCmd = this._cache.get(args[0]);
this._logService.trace('CommandsConverter#EXECUTE', args[0], actualCmd ? actualCmd.command : 'MISSING');
if (!actualCmd) {
return Promise.reject('actual command NOT FOUND');
}

View File

@@ -16,7 +16,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import * as types from 'vs/workbench/api/common/extHostTypes';
import * as vscode from 'vscode';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape } from './extHost.protocol';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from './extHost.protocol';
import { ExtHostCommands } from './extHostCommands';
type ProviderHandle = number;
@@ -213,12 +213,21 @@ export class ExtHostComments implements ExtHostCommentsShape, IDisposable {
}
}
type CommentThreadModification = Partial<{
range: vscode.Range,
label: string | undefined,
contextValue: string | undefined,
comments: vscode.Comment[],
collapsibleState: vscode.CommentThreadCollapsibleState
}>;
export class ExtHostCommentThread implements vscode.CommentThread {
private static _handlePool: number = 0;
readonly handle = ExtHostCommentThread._handlePool++;
public commentHandle: number = 0;
private modifications: CommentThreadModification = Object.create(null);
set threadId(id: string) {
this._id = id;
}
@@ -245,6 +254,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set range(range: vscode.Range) {
if (!range.isEqual(this._range)) {
this._range = range;
this.modifications.range = range;
this._onDidUpdateCommentThread.fire();
}
}
@@ -261,6 +271,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set label(label: string | undefined) {
this._label = label;
this.modifications.label = label;
this._onDidUpdateCommentThread.fire();
}
@@ -272,6 +283,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set contextValue(context: string | undefined) {
this._contextValue = context;
this.modifications.contextValue = context;
this._onDidUpdateCommentThread.fire();
}
@@ -281,6 +293,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set comments(newComments: vscode.Comment[]) {
this._comments = newComments;
this.modifications.comments = newComments;
this._onDidUpdateCommentThread.fire();
}
@@ -292,6 +305,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set collapsibleState(newState: vscode.CommentThreadCollapsibleState) {
this._collapseState = newState;
this.modifications.collapsibleState = newState;
this._onDidUpdateCommentThread.fire();
}
@@ -353,22 +367,34 @@ export class ExtHostCommentThread implements vscode.CommentThread {
this._acceptInputDisposables.value = new DisposableStore();
}
const commentThreadRange = extHostTypeConverter.Range.from(this._range);
const label = this.label;
const contextValue = this.contextValue;
const comments = this._comments.map(cmt => { return convertToModeComment(this, this._commentController, cmt, this._commentsMap); });
const collapsibleState = convertToCollapsibleState(this._collapseState);
const modified = (value: keyof CommentThreadModification): boolean =>
Object.prototype.hasOwnProperty.call(this.modifications, value);
const formattedModifications: CommentThreadChanges = {};
if (modified('range')) {
formattedModifications.range = extHostTypeConverter.Range.from(this._range);
}
if (modified('label')) {
formattedModifications.label = this.label;
}
if (modified('contextValue')) {
formattedModifications.contextValue = this.contextValue;
}
if (modified('comments')) {
formattedModifications.comments =
this._comments.map(cmt => convertToModeComment(this, this._commentController, cmt, this._commentsMap));
}
if (modified('collapsibleState')) {
formattedModifications.collapseState = convertToCollapsibleState(this._collapseState);
}
this.modifications = {};
this._proxy.$updateCommentThread(
this._commentController.handle,
this.handle,
this._id!,
this._uri,
commentThreadRange,
label,
contextValue,
comments,
collapsibleState
formattedModifications
);
}

View File

@@ -20,6 +20,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { Barrier } from 'vs/base/common/async';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ILogService } from 'vs/platform/log/common/log';
function lookUp(tree: any, key: string) {
if (key) {
@@ -45,16 +46,19 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
readonly _serviceBrand: undefined;
private readonly _proxy: MainThreadConfigurationShape;
private readonly _logService: ILogService;
private readonly _extHostWorkspace: ExtHostWorkspace;
private readonly _barrier: Barrier;
private _actual: ExtHostConfigProvider | null;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostWorkspace extHostWorkspace: IExtHostWorkspace
@IExtHostWorkspace extHostWorkspace: IExtHostWorkspace,
@ILogService logService: ILogService,
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadConfiguration);
this._extHostWorkspace = extHostWorkspace;
this._logService = logService;
this._barrier = new Barrier();
this._actual = null;
}
@@ -64,7 +68,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
}
$initializeConfiguration(data: IConfigurationInitData): void {
this._actual = new ExtHostConfigProvider(this._proxy, this._extHostWorkspace, data);
this._actual = new ExtHostConfigProvider(this._proxy, this._extHostWorkspace, data, this._logService);
this._barrier.open();
}
@@ -80,9 +84,11 @@ export class ExtHostConfigProvider {
private readonly _extHostWorkspace: ExtHostWorkspace;
private _configurationScopes: Map<string, ConfigurationScope | undefined>;
private _configuration: Configuration;
private _logService: ILogService;
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData) {
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData, logService: ILogService) {
this._proxy = proxy;
this._logService = logService;
this._extHostWorkspace = extHostWorkspace;
this._configuration = ExtHostConfigProvider.parse(data);
this._configurationScopes = this._toMap(data.configurationScopes);
@@ -236,13 +242,13 @@ export class ExtHostConfigProvider {
const extensionIdText = extensionId ? `[${extensionId.value}] ` : '';
if (ConfigurationScope.RESOURCE === scope) {
if (resource === undefined) {
console.warn(`${extensionIdText}Accessing a resource scoped configuration without providing a resource is not expected. To get the effective value for '${key}', provide the URI of a resource or 'null' for any resource.`);
this._logService.warn(`${extensionIdText}Accessing a resource scoped configuration without providing a resource is not expected. To get the effective value for '${key}', provide the URI of a resource or 'null' for any resource.`);
}
return;
}
if (ConfigurationScope.WINDOW === scope) {
if (resource) {
console.warn(`${extensionIdText}Accessing a window scoped configuration for a resource is not expected. To associate '${key}' to a resource, define its scope to 'resource' in configuration contributions in 'package.json'.`);
this._logService.warn(`${extensionIdText}Accessing a window scoped configuration for a resource is not expected. To associate '${key}' to a resource, define its scope to 'resource' in configuration contributions in 'package.json'.`);
}
return;
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
import { IConstructorSignature1, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
@@ -13,12 +13,12 @@ export type IExtHostNamedCustomer<T extends IDisposable> = [ProxyIdentifier<T>,
export type IExtHostCustomerCtor<T extends IDisposable> = IConstructorSignature1<IExtHostContext, T>;
export function extHostNamedCustomer<T extends IDisposable>(id: ProxyIdentifier<T>) {
return function (ctor: IExtHostCustomerCtor<T>): void {
return function <Services extends BrandedService[]>(ctor: { new(context: IExtHostContext, ...services: Services): T }): void {
ExtHostCustomersRegistryImpl.INSTANCE.registerNamedCustomer(id, ctor);
};
}
export function extHostCustomer<T extends IDisposable>(ctor: IExtHostCustomerCtor<T>): void {
export function extHostCustomer<T extends IDisposable, Services extends BrandedService[]>(ctor: { new(context: IExtHostContext, ...services: Services): T }): void {
ExtHostCustomersRegistryImpl.INSTANCE.registerCustomer(ctor);
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { asArray } from 'vs/base/common/arrays';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ILogService } from 'vs/platform/log/common/log';
interface ProviderData {
provider: vscode.DecorationProvider;
@@ -28,6 +29,7 @@ export class ExtHostDecorations implements IExtHostDecorations {
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@ILogService private readonly _logService: ILogService,
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadDecorations);
}
@@ -66,10 +68,10 @@ export class ExtHostDecorations implements IExtHostDecorations {
Decoration.validate(data);
result[id] = <DecorationData>[data.priority, data.bubble, data.title, data.letter, data.color];
} catch (e) {
console.warn(`INVALID decoration from extension '${extensionId.value}': ${e}`);
this._logService.warn(`INVALID decoration from extension '${extensionId.value}': ${e}`);
}
}, err => {
console.error(err);
this._logService.error(err);
});
})).then(() => {

View File

@@ -13,6 +13,7 @@ import * as converter from './extHostTypeConverters';
import { mergeSort } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { keys } from 'vs/base/common/map';
import { ILogService } from 'vs/platform/log/common/log';
export class DiagnosticCollection implements vscode.DiagnosticCollection {
@@ -253,7 +254,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
readonly onDidChangeDiagnostics: Event<vscode.DiagnosticChangeEvent> = Event.map(Event.debounce(this._onDidChangeDiagnostics.event, ExtHostDiagnostics._debouncer, 50), ExtHostDiagnostics._mapper);
constructor(mainContext: IMainContext) {
constructor(mainContext: IMainContext, @ILogService private readonly _logService: ILogService) {
this._proxy = mainContext.getProxy(MainContext.MainThreadDiagnostics);
}
@@ -266,7 +267,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
} else if (!_collections.has(name)) {
owner = name;
} else {
console.warn(`DiagnosticCollection with name '${name}' does already exist.`);
this._logService.warn(`DiagnosticCollection with name '${name}' does already exist.`);
do {
owner = name + ExtHostDiagnostics._idPool++;
} while (_collections.has(owner));

View File

@@ -28,7 +28,6 @@ export class ExtHostDocumentData extends MirrorTextModel {
private _languageId: string;
private _isDirty: boolean;
private _document?: vscode.TextDocument;
private _textLines: vscode.TextLine[] = [];
private _isDisposed: boolean = false;
constructor(proxy: MainThreadDocumentsShape, uri: URI, lines: string[], eol: string,
@@ -130,33 +129,11 @@ export class ExtHostDocumentData extends MirrorTextModel {
line = lineOrPosition;
}
if (typeof line !== 'number' || line < 0 || line >= this._lines.length) {
if (typeof line !== 'number' || line < 0 || line >= this._lines.length || Math.floor(line) !== line) {
throw new Error('Illegal value for `line`');
}
let result = this._textLines[line];
if (!result || result.lineNumber !== line || result.text !== this._lines[line]) {
const text = this._lines[line];
const firstNonWhitespaceCharacterIndex = /^(\s*)/.exec(text)![1].length;
const range = new Range(line, 0, line, text.length);
const rangeIncludingLineBreak = line < this._lines.length - 1
? new Range(line, 0, line + 1, 0)
: range;
result = Object.freeze({
lineNumber: line,
range,
rangeIncludingLineBreak,
text,
firstNonWhitespaceCharacterIndex, //TODO@api, rename to 'leadingWhitespaceLength'
isEmptyOrWhitespace: firstNonWhitespaceCharacterIndex === text.length
});
this._textLines[line] = result;
}
return result;
return new ExtHostDocumentLine(line, this._lines[line], line === this._lines.length - 1);
}
private _offsetAt(position: vscode.Position): number {
@@ -239,8 +216,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
} else if (regExpLeadsToEndlessLoop(regexp)) {
// use default when custom-regexp is bad
console.warn(`[getWordRangeAtPosition]: ignoring custom regexp '${regexp.source}' because it matches the empty string.`);
regexp = getWordDefinitionFor(this._languageId);
throw new Error(`[getWordRangeAtPosition]: ignoring custom regexp '${regexp.source}' because it matches the empty string.`);
}
const wordAtText = getWordAtText(
@@ -256,3 +232,44 @@ export class ExtHostDocumentData extends MirrorTextModel {
return undefined;
}
}
class ExtHostDocumentLine implements vscode.TextLine {
private readonly _line: number;
private readonly _text: string;
private readonly _isLastLine: boolean;
constructor(line: number, text: string, isLastLine: boolean) {
this._line = line;
this._text = text;
this._isLastLine = isLastLine;
}
public get lineNumber(): number {
return this._line;
}
public get text(): string {
return this._text;
}
public get range(): Range {
return new Range(this._line, 0, this._line, this._text.length);
}
public get rangeIncludingLineBreak(): Range {
if (this._isLastLine) {
return this.range;
}
return new Range(this._line, 0, this._line + 1, 0);
}
public get firstNonWhitespaceCharacterIndex(): number {
//TODO@api, rename to 'leadingWhitespaceLength'
return /^(\s*)/.exec(this._text)![1].length;
}
public get isEmptyOrWhitespace(): boolean {
return this.firstNonWhitespaceCharacterIndex === this._text.length;
}
}

View File

@@ -11,7 +11,7 @@ import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IResou
import { TextEdit } from 'vs/workbench/api/common/extHostTypes';
import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/common/extHostTypeConverters';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { SaveReason } from 'vs/workbench/common/editor';
import * as vscode from 'vscode';
import { LinkedList } from 'vs/base/common/linkedList';
import { ILogService } from 'vs/platform/log/common/log';

View File

@@ -12,6 +12,7 @@ import { ExtHostDocumentData, setWordDefinitionFor } from 'vs/workbench/api/comm
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as vscode from 'vscode';
import { assertIsDefined } from 'vs/base/common/types';
export class ExtHostDocuments implements ExtHostDocumentsShape {
@@ -84,7 +85,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
if (!promise) {
promise = this._proxy.$tryOpenDocument(uri).then(() => {
this._documentLoader.delete(uri.toString());
return this._documentsAndEditors.getDocument(uri);
return assertIsDefined(this._documentsAndEditors.getDocument(uri));
}, err => {
this._documentLoader.delete(uri.toString());
return Promise.reject(err);

View File

@@ -9,6 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionActivationError, MissingDependencyError } from 'vs/workbench/services/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
@@ -182,7 +183,13 @@ export class ExtensionsActivator {
*/
private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; };
constructor(registry: ExtensionDescriptionRegistry, resolvedExtensions: ExtensionIdentifier[], hostExtensions: ExtensionIdentifier[], host: IExtensionsActivatorHost) {
constructor(
registry: ExtensionDescriptionRegistry,
resolvedExtensions: ExtensionIdentifier[],
hostExtensions: ExtensionIdentifier[],
host: IExtensionsActivatorHost,
@ILogService private readonly _logService: ILogService
) {
this._registry = registry;
this._resolvedExtensionsSet = new Set<string>();
resolvedExtensions.forEach((extensionId) => this._resolvedExtensionsSet.add(ExtensionIdentifier.toKey(extensionId)));
@@ -308,7 +315,6 @@ export class ExtensionsActivator {
}
private _activateExtensions(extensions: ActivationIdAndReason[]): Promise<void> {
// console.log('_activateExtensions: ', extensions.map(p => p.id.value));
if (extensions.length === 0) {
return Promise.resolve(undefined);
}
@@ -335,9 +341,6 @@ export class ExtensionsActivator {
const green = Object.keys(greenMap).map(id => greenMap[id]);
// console.log('greenExtensions: ', green.map(p => p.id.value));
// console.log('redExtensions: ', red.map(p => p.id.value));
if (red.length === 0) {
// Finally reached only leafs!
return Promise.all(green.map((p) => this._activateExtension(p.id, p.reason))).then(_ => undefined);
@@ -362,8 +365,8 @@ export class ExtensionsActivator {
const newlyActivatingExtension = this._host.actualActivateExtension(extensionId, reason).then(undefined, (err) => {
this._host.onExtensionActivationError(extensionId, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionId.value, err.message));
console.error('Activating extension `' + extensionId.value + '` failed: ', err.message);
console.log('Here is the error stack: ', err.stack);
this._logService.error(`Activating extension ${extensionId.value} failed due to an error:`);
this._logService.error(err);
// Treat the extension as being empty
return new FailedExtension(err);
}).then((x: ActivatedExtension) => {

View File

@@ -133,20 +133,26 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
const hostExtensions = new Set<string>();
this._initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId)));
this._activator = new ExtensionsActivator(this._registry, this._initData.resolvedExtensions, this._initData.hostExtensions, {
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => {
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error);
},
this._activator = new ExtensionsActivator(
this._registry,
this._initData.resolvedExtensions,
this._initData.hostExtensions,
{
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => {
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error);
},
actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) {
await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason);
return new HostExtension();
actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) {
await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason);
return new HostExtension();
}
const extensionDescription = this._registry.getExtensionDescription(extensionId)!;
return this._activateExtension(extensionDescription, reason);
}
const extensionDescription = this._registry.getExtensionDescription(extensionId)!;
return this._activateExtension(extensionDescription, reason);
}
});
},
this._logService
);
this._extensionPathIndex = null;
this._resolvers = Object.create(null);
this._started = false;
@@ -405,7 +411,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
// Handle "eager" activation extensions
private _handleEagerExtensions(): Promise<void> {
this._activateByEvent('*', true).then(undefined, (err) => {
console.error(err);
this._logService.error(err);
});
this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added)));
@@ -467,7 +473,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
// the file was found
return (
this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContains:${fileName}` })
.then(undefined, err => console.error(err))
.then(undefined, err => this._logService.error(err))
);
}
}
@@ -488,7 +494,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
const timer = setTimeout(async () => {
tokenSource.cancel();
this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContainsTimeout:${globPatterns.join(',')}` })
.then(undefined, err => console.error(err));
.then(undefined, err => this._logService.error(err));
}, AbstractExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);
let exists: boolean = false;
@@ -496,7 +502,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
exists = await searchP;
} catch (err) {
if (!errors.isPromiseCanceledError(err)) {
console.error(err);
this._logService.error(err);
}
}
@@ -507,7 +513,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
// a file was found matching one of the glob patterns
return (
this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContains:${globPatterns.join(',')}` })
.then(undefined, err => console.error(err))
.then(undefined, err => this._logService.error(err))
);
}

View File

@@ -44,6 +44,7 @@ class FsLinkProvider {
const edges: Edge[] = [];
let prevScheme: string | undefined;
let prevState: State;
let lastState = State.LastKnownState;
let nextState = State.LastKnownState;
for (const scheme of schemes) {
@@ -60,6 +61,8 @@ class FsLinkProvider {
// keep creating new (next) states until the
// end (and the BeforeColon-state) is reached
if (pos + 1 === scheme.length) {
// Save the last state here, because we need to continue for the next scheme
lastState = nextState;
nextState = State.BeforeColon;
} else {
nextState += 1;
@@ -70,6 +73,8 @@ class FsLinkProvider {
}
prevScheme = scheme;
// Restore the last state
nextState = lastState;
}
// all link must match this pattern `<scheme>:/<more>`

View File

@@ -3,16 +3,19 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { flatten } from 'vs/base/common/arrays';
import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event';
import { AsyncEmitter, Emitter, Event, IWaitUntil } from 'vs/base/common/event';
import { IRelativePattern, parse } from 'vs/base/common/glob';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import * as vscode from 'vscode';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, IResourceFileEditDto, IResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, IResourceFileEditDto, IResourceTextEditDto } from './extHost.protocol';
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { FileOperation } from 'vs/platform/files/common/files';
import { flatten } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILogService } from 'vs/platform/log/common/log';
class FileSystemWatcher implements vscode.FileSystemWatcher {
@@ -96,87 +99,132 @@ class FileSystemWatcher implements vscode.FileSystemWatcher {
}
}
interface WillRenameListener {
interface IExtensionListener<E> {
extension: IExtensionDescription;
(e: vscode.FileWillRenameEvent): any;
(e: E): any;
}
export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape {
private readonly _onFileEvent = new Emitter<FileSystemEvents>();
private readonly _onFileSystemEvent = new Emitter<FileSystemEvents>();
private readonly _onDidRenameFile = new Emitter<vscode.FileRenameEvent>();
private readonly _onDidCreateFile = new Emitter<vscode.FileCreateEvent>();
private readonly _onDidDeleteFile = new Emitter<vscode.FileDeleteEvent>();
private readonly _onWillRenameFile = new AsyncEmitter<vscode.FileWillRenameEvent>();
private readonly _onWillCreateFile = new AsyncEmitter<vscode.FileWillCreateEvent>();
private readonly _onWillDeleteFile = new AsyncEmitter<vscode.FileWillDeleteEvent>();
readonly onDidRenameFile: Event<vscode.FileRenameEvent> = this._onDidRenameFile.event;
readonly onDidCreateFile: Event<vscode.FileCreateEvent> = this._onDidCreateFile.event;
readonly onDidDeleteFile: Event<vscode.FileDeleteEvent> = this._onDidDeleteFile.event;
constructor(
mainContext: IMainContext,
private readonly _logService: ILogService,
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = mainContext.getProxy(MainContext.MainThreadTextEditors)
) {
//
}
public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher {
return new FileSystemWatcher(this._onFileEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
//--- file events
createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher {
return new FileSystemWatcher(this._onFileSystemEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
}
$onFileEvent(events: FileSystemEvents) {
this._onFileEvent.fire(events);
this._onFileSystemEvent.fire(events);
}
$onFileRename(oldUri: UriComponents, newUri: UriComponents) {
this._onDidRenameFile.fire(Object.freeze({ oldUri: URI.revive(oldUri), newUri: URI.revive(newUri) }));
//--- file operations
$onDidRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined): void {
switch (operation) {
case FileOperation.MOVE:
this._onDidRenameFile.fire(Object.freeze({ files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }));
break;
case FileOperation.DELETE:
this._onDidDeleteFile.fire(Object.freeze({ files: [URI.revive(target)] }));
break;
case FileOperation.CREATE:
this._onDidCreateFile.fire(Object.freeze({ files: [URI.revive(target)] }));
break;
default:
//ignore, dont send
}
}
getOnWillRenameFileEvent(extension: IExtensionDescription): Event<vscode.FileWillRenameEvent> {
return this._createWillExecuteEvent(extension, this._onWillRenameFile);
}
getOnWillCreateFileEvent(extension: IExtensionDescription): Event<vscode.FileWillCreateEvent> {
return this._createWillExecuteEvent(extension, this._onWillCreateFile);
}
getOnWillDeleteFileEvent(extension: IExtensionDescription): Event<vscode.FileWillDeleteEvent> {
return this._createWillExecuteEvent(extension, this._onWillDeleteFile);
}
private _createWillExecuteEvent<E extends IWaitUntil>(extension: IExtensionDescription, emitter: AsyncEmitter<E>): Event<E> {
return (listener, thisArg, disposables) => {
const wrappedListener: WillRenameListener = <any>((e: vscode.FileWillRenameEvent) => {
listener.call(thisArg, e);
});
const wrappedListener: IExtensionListener<E> = function wrapped(e: E) { listener.call(thisArg, e); };
wrappedListener.extension = extension;
return this._onWillRenameFile.event(wrappedListener, undefined, disposables);
return emitter.event(wrappedListener, undefined, disposables);
};
}
$onWillRename(oldUriDto: UriComponents, newUriDto: UriComponents): Promise<any> {
const oldUri = URI.revive(oldUriDto);
const newUri = URI.revive(newUriDto);
async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined, timeout: number, token: CancellationToken): Promise<any> {
switch (operation) {
case FileOperation.MOVE:
await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }, timeout, token);
break;
case FileOperation.DELETE:
await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] }, timeout, token);
break;
case FileOperation.CREATE:
await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] }, timeout, token);
break;
default:
//ignore, dont send
}
}
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, timeout: number, token: CancellationToken): Promise<any> {
const edits: WorkspaceEdit[] = [];
return Promise.resolve(this._onWillRenameFile.fireAsync((bucket, _listener) => {
return {
oldUri,
newUri,
waitUntil: (thenable: Promise<vscode.WorkspaceEdit>): void => {
if (Object.isFrozen(bucket)) {
throw new TypeError('waitUntil cannot be called async');
}
const index = bucket.length;
const wrappedThenable = Promise.resolve(thenable).then(result => {
// ignore all results except for WorkspaceEdits. Those
// are stored in a spare array
if (result instanceof WorkspaceEdit) {
edits[index] = result;
}
});
bucket.push(wrappedThenable);
}
};
}).then((): any => {
if (edits.length === 0) {
return undefined;
await emitter.fireAsync(data, token, async (thenable, listener: IExtensionListener<E>) => {
// ignore all results except for WorkspaceEdits. Those are stored in an array.
const now = Date.now();
const result = await Promise.resolve(thenable);
if (result instanceof WorkspaceEdit) {
edits.push(result);
}
if (Date.now() - now > timeout) {
this._logService.warn('SLOW file-participant', listener.extension?.identifier);
}
});
if (token.isCancellationRequested) {
return;
}
if (edits.length > 0) {
// flatten all WorkspaceEdits collected via waitUntil-call
// and apply them in one go.
const allEdits = new Array<Array<IResourceFileEditDto | IResourceTextEditDto>>();
for (let edit of edits) {
if (edit) { // sparse array
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
allEdits.push(edits);
}
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
allEdits.push(edits);
}
return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) });
}));
}
}
}

View File

@@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { mixin } from 'vs/base/common/objects';
import * as vscode from 'vscode';
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol } from 'vs/workbench/api/common/extHostTypes';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits } from 'vs/workbench/api/common/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
@@ -26,6 +26,9 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import { encodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokens';
import { IdGenerator } from 'vs/base/common/idGenerator';
// --- adapter
@@ -348,7 +351,7 @@ class CodeActionAdapter {
only: context.only ? new CodeActionKind(context.only) : undefined
};
return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then(commandsOrActions => {
return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then((commandsOrActions): extHostProtocol.ICodeActionListDto | undefined => {
if (!isNonEmptyArray(commandsOrActions) || token.isCancellationRequested) {
return undefined;
}
@@ -386,11 +389,12 @@ class CodeActionAdapter {
edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit),
kind: candidate.kind && candidate.kind.value,
isPreferred: candidate.isPreferred,
disabled: candidate.disabled
});
}
}
return <extHostProtocol.ICodeActionListDto>{ cacheId, actions };
return { cacheId, actions };
});
}
@@ -471,13 +475,13 @@ class OnTypeFormattingAdapter {
class NavigateTypeAdapter {
private readonly _symbolCache: { [id: number]: vscode.SymbolInformation } = Object.create(null);
private readonly _resultCache: { [id: number]: [number, number] } = Object.create(null);
private readonly _provider: vscode.WorkspaceSymbolProvider;
private readonly _symbolCache = new Map<number, vscode.SymbolInformation>();
private readonly _resultCache = new Map<number, [number, number]>();
constructor(provider: vscode.WorkspaceSymbolProvider) {
this._provider = provider;
}
constructor(
private readonly _provider: vscode.WorkspaceSymbolProvider,
private readonly _logService: ILogService
) { }
provideWorkspaceSymbols(search: string, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolsDto> {
const result: extHostProtocol.IWorkspaceSymbolsDto = extHostProtocol.IdObject.mixin({ symbols: [] });
@@ -489,44 +493,42 @@ class NavigateTypeAdapter {
continue;
}
if (!item.name) {
console.warn('INVALID SymbolInformation, lacks name', item);
this._logService.warn('INVALID SymbolInformation, lacks name', item);
continue;
}
const symbol = extHostProtocol.IdObject.mixin(typeConvert.WorkspaceSymbol.from(item));
this._symbolCache[symbol._id!] = item;
this._symbolCache.set(symbol._id!, item);
result.symbols.push(symbol);
}
}
}).then(() => {
if (result.symbols.length > 0) {
this._resultCache[result._id!] = [result.symbols[0]._id!, result.symbols[result.symbols.length - 1]._id!];
this._resultCache.set(result._id!, [result.symbols[0]._id!, result.symbols[result.symbols.length - 1]._id!]);
}
return result;
});
}
resolveWorkspaceSymbol(symbol: extHostProtocol.IWorkspaceSymbolDto, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolDto | undefined> {
async resolveWorkspaceSymbol(symbol: extHostProtocol.IWorkspaceSymbolDto, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolDto | undefined> {
if (typeof this._provider.resolveWorkspaceSymbol !== 'function') {
return Promise.resolve(symbol);
return symbol;
}
const item = this._symbolCache[symbol._id!];
const item = this._symbolCache.get(symbol._id!);
if (item) {
return asPromise(() => this._provider.resolveWorkspaceSymbol!(item, token)).then(value => {
return value && mixin(symbol, typeConvert.WorkspaceSymbol.from(value), true);
});
const value = await asPromise(() => this._provider.resolveWorkspaceSymbol!(item, token));
return value && mixin(symbol, typeConvert.WorkspaceSymbol.from(value), true);
}
return Promise.resolve(undefined);
return undefined;
}
releaseWorkspaceSymbols(id: number): any {
const range = this._resultCache[id];
const range = this._resultCache.get(id);
if (range) {
for (let [from, to] = range; from <= to; from++) {
delete this._symbolCache[from];
this._symbolCache.delete(from);
}
delete this._resultCache[id];
this._resultCache.delete(id);
}
}
}
@@ -539,7 +541,8 @@ class RenameAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.RenameProvider
private readonly _provider: vscode.RenameProvider,
private readonly _logService: ILogService
) { }
provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Promise<extHostProtocol.IWorkspaceEditDto | undefined> {
@@ -588,7 +591,7 @@ class RenameAdapter {
return undefined;
}
if (range.start.line > pos.line || range.end.line < pos.line) {
console.warn('INVALID rename location: position line must be within range start/end lines');
this._logService.warn('INVALID rename location: position line must be within range start/end lines');
return undefined;
}
return { range: typeConvert.Range.from(range), text };
@@ -613,30 +616,148 @@ class RenameAdapter {
}
}
class SemanticTokensPreviousResult {
constructor(
public readonly resultId: string | undefined,
public readonly tokens?: Uint32Array,
) { }
}
export class SemanticTokensAdapter {
private readonly _previousResults: Map<number, SemanticTokensPreviousResult>;
private _nextResultId = 1;
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.SemanticTokensProvider,
) {
this._previousResults = new Map<number, SemanticTokensPreviousResult>();
}
provideSemanticTokens(resource: URI, ranges: IRange[] | null, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
const doc = this._documents.getDocument(resource);
const previousResult = (previousResultId !== 0 ? this._previousResults.get(previousResultId) : null);
const opts: vscode.SemanticTokensRequestOptions = {
ranges: (Array.isArray(ranges) && ranges.length > 0 ? ranges.map<Range>(typeConvert.Range.to) : undefined),
previousResultId: (previousResult ? previousResult.resultId : undefined)
};
return asPromise(() => this._provider.provideSemanticTokens(doc, opts, token)).then(value => {
if (!value) {
return null;
}
if (previousResult) {
this._previousResults.delete(previousResultId);
}
return this._send(SemanticTokensAdapter._convertToEdits(previousResult, value), value);
});
}
async releaseSemanticColoring(semanticColoringResultId: number): Promise<void> {
this._previousResults.delete(semanticColoringResultId);
}
private static _isSemanticTokens(v: vscode.SemanticTokens | vscode.SemanticTokensEdits): v is vscode.SemanticTokens {
return v && !!((v as vscode.SemanticTokens).data);
}
private static _isSemanticTokensEdits(v: vscode.SemanticTokens | vscode.SemanticTokensEdits): v is vscode.SemanticTokensEdits {
return v && Array.isArray((v as vscode.SemanticTokensEdits).edits);
}
private static _convertToEdits(previousResult: SemanticTokensPreviousResult | null | undefined, newResult: vscode.SemanticTokens | vscode.SemanticTokensEdits): vscode.SemanticTokens | vscode.SemanticTokensEdits {
if (!SemanticTokensAdapter._isSemanticTokens(newResult)) {
return newResult;
}
if (!previousResult || !previousResult.tokens) {
return newResult;
}
const oldData = previousResult.tokens;
const oldLength = oldData.length;
const newData = newResult.data;
const newLength = newData.length;
let commonPrefixLength = 0;
const maxCommonPrefixLength = Math.min(oldLength, newLength);
while (commonPrefixLength < maxCommonPrefixLength && oldData[commonPrefixLength] === newData[commonPrefixLength]) {
commonPrefixLength++;
}
if (commonPrefixLength === oldLength && commonPrefixLength === newLength) {
// complete overlap!
return new SemanticTokensEdits([], newResult.resultId);
}
let commonSuffixLength = 0;
const maxCommonSuffixLength = maxCommonPrefixLength - commonPrefixLength;
while (commonSuffixLength < maxCommonSuffixLength && oldData[oldLength - commonSuffixLength - 1] === newData[newLength - commonSuffixLength - 1]) {
commonSuffixLength++;
}
return new SemanticTokensEdits([{
start: commonPrefixLength,
deleteCount: (oldLength - commonPrefixLength - commonSuffixLength),
data: newData.subarray(commonPrefixLength, newLength - commonSuffixLength)
}], newResult.resultId);
}
private _send(value: vscode.SemanticTokens | vscode.SemanticTokensEdits, original: vscode.SemanticTokens | vscode.SemanticTokensEdits): VSBuffer | null {
if (SemanticTokensAdapter._isSemanticTokens(value)) {
const myId = this._nextResultId++;
this._previousResults.set(myId, new SemanticTokensPreviousResult(value.resultId, value.data));
return encodeSemanticTokensDto({
id: myId,
type: 'full',
data: value.data
});
}
if (SemanticTokensAdapter._isSemanticTokensEdits(value)) {
const myId = this._nextResultId++;
if (SemanticTokensAdapter._isSemanticTokens(original)) {
// store the original
this._previousResults.set(myId, new SemanticTokensPreviousResult(original.resultId, original.data));
} else {
this._previousResults.set(myId, new SemanticTokensPreviousResult(value.resultId));
}
return encodeSemanticTokensDto({
id: myId,
type: 'delta',
deltas: (value.edits || []).map(edit => ({ start: edit.start, deleteCount: edit.deleteCount, data: edit.data }))
});
}
return null;
}
}
class SuggestAdapter {
static supportsResolving(provider: vscode.CompletionItemProvider): boolean {
return typeof provider.resolveCompletionItem === 'function';
}
private _documents: ExtHostDocuments;
private _commands: CommandsConverter;
private _provider: vscode.CompletionItemProvider;
private _cache = new Cache<vscode.CompletionItem>('CompletionItem');
private _disposables = new Map<number, DisposableStore>();
constructor(documents: ExtHostDocuments, commands: CommandsConverter, provider: vscode.CompletionItemProvider) {
this._documents = documents;
this._commands = commands;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
private readonly _provider: vscode.CompletionItemProvider,
private readonly _logService: ILogService
) { }
provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<extHostProtocol.ISuggestResultDto | undefined> {
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
// The default insert/replace ranges. It's important to compute them
// before asynchronously asking the provider for its results. See
// https://github.com/microsoft/vscode/issues/83400#issuecomment-546851421
const replaceRange = doc.getWordRangeAtPosition(pos) || new Range(pos, pos);
const insertRange = replaceRange.with({ end: pos });
return asPromise(() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context))).then(value => {
if (!value) {
@@ -657,14 +778,10 @@ class SuggestAdapter {
const disposables = new DisposableStore();
this._disposables.set(pid, disposables);
// the default text edit range
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos))
.with({ end: pos });
const result: extHostProtocol.ISuggestResultDto = {
x: pid,
b: [],
a: typeConvert.Range.from(wordRangeBeforePos),
a: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) },
c: list.isIncomplete || undefined
};
@@ -711,7 +828,7 @@ class SuggestAdapter {
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: extHostProtocol.ChainedCacheId): extHostProtocol.ISuggestDataDto | undefined {
if (typeof item.label !== 'string' || item.label.length === 0) {
console.warn('INVALID text edit -> must have at least a label');
this._logService.warn('INVALID text edit -> must have at least a label');
return undefined;
}
@@ -751,21 +868,46 @@ class SuggestAdapter {
}
// 'overwrite[Before|After]'-logic
let range: vscode.Range | undefined;
let range: vscode.Range | { inserting: vscode.Range, replacing: vscode.Range; } | undefined;
if (item.textEdit) {
range = item.textEdit.range;
} else if (item.range) {
range = item.range;
} else if (item.range2) {
range = item.range2;
}
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
if (range && (!range.isSingleLine || range.start.line !== position.line)) {
console.warn('INVALID text edit -> must be single line and on the same line');
return undefined;
if (range) {
if (Range.isRange(range)) {
if (!SuggestAdapter._isValidRangeForCompletion(range, position)) {
this._logService.trace('INVALID range -> must be single line and on the same line');
return undefined;
}
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
} else {
if (
!SuggestAdapter._isValidRangeForCompletion(range.inserting, position)
|| !SuggestAdapter._isValidRangeForCompletion(range.replacing, position)
|| !range.inserting.start.isEqual(range.replacing.start)
|| !range.replacing.contains(range.inserting)
) {
this._logService.trace('INVALID range -> must be single line, on the same line, insert range must be a prefix of replace range');
return undefined;
}
result[extHostProtocol.ISuggestDataDtoField.range] = {
insert: typeConvert.Range.from(range.inserting),
replace: typeConvert.Range.from(range.replacing)
};
}
}
return result;
}
private static _isValidRangeForCompletion(range: vscode.Range, position: vscode.Position): boolean {
return range.isSingleLine || range.start.line === position.line;
}
}
class SignatureHelpAdapter {
@@ -966,7 +1108,8 @@ class SelectionRangeAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.SelectionRangeProvider
private readonly _provider: vscode.SelectionRangeProvider,
private readonly _logService: ILogService
) { }
provideSelectionRanges(resource: URI, pos: IPosition[], token: CancellationToken): Promise<modes.SelectionRange[][]> {
@@ -978,7 +1121,7 @@ class SelectionRangeAdapter {
return [];
}
if (allProviderRanges.length !== positions.length) {
console.warn('BAD selection ranges, provider must return ranges for each position');
this._logService.warn('BAD selection ranges, provider must return ranges for each position');
return [];
}
@@ -1009,37 +1152,95 @@ class SelectionRangeAdapter {
class CallHierarchyAdapter {
private readonly _idPool = new IdGenerator('');
private readonly _cache = new Map<string, Map<string, vscode.CallHierarchyItem>>();
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.CallHierarchyItemProvider
private readonly _provider: vscode.CallHierarchyProvider
) { }
async provideCallsTo(uri: URI, position: IPosition, token: CancellationToken): Promise<[extHostProtocol.ICallHierarchyItemDto, IRange[]][] | undefined> {
async prepareSession(uri: URI, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ICallHierarchyItemDto | undefined> {
const doc = this._documents.getDocument(uri);
const pos = typeConvert.Position.to(position);
const calls = await this._provider.provideCallHierarchyIncomingCalls(doc, pos, token);
if (!calls) {
const item = await this._provider.prepareCallHierarchy(doc, pos, token);
if (!item) {
return undefined;
}
return calls.map(call => (<[extHostProtocol.ICallHierarchyItemDto, IRange[]]>[typeConvert.CallHierarchyItem.from(call.from), call.fromRanges.map(typeConvert.Range.from)]));
const sessionId = this._idPool.nextId();
this._cache.set(sessionId, new Map());
return this._cacheAndConvertItem(sessionId, item);
}
async provideCallsFrom(uri: URI, position: IPosition, token: CancellationToken): Promise<[extHostProtocol.ICallHierarchyItemDto, IRange[]][] | undefined> {
const doc = this._documents.getDocument(uri);
const pos = typeConvert.Position.to(position);
const calls = await this._provider.provideCallHierarchyOutgoingCalls(doc, pos, token);
async provideCallsTo(sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.IIncomingCallDto[] | undefined> {
const item = this._itemFromCache(sessionId, itemId);
if (!item) {
throw new Error('missing call hierarchy item');
}
const calls = await this._provider.provideCallHierarchyIncomingCalls(item, token);
if (!calls) {
return undefined;
}
return calls.map(call => (<[extHostProtocol.ICallHierarchyItemDto, IRange[]]>[typeConvert.CallHierarchyItem.from(call.to), call.fromRanges.map(typeConvert.Range.from)]));
return calls.map(call => {
return {
from: this._cacheAndConvertItem(sessionId, call.from),
fromRanges: call.fromRanges.map(r => typeConvert.Range.from(r))
};
});
}
async provideCallsFrom(sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.IOutgoingCallDto[] | undefined> {
const item = this._itemFromCache(sessionId, itemId);
if (!item) {
throw new Error('missing call hierarchy item');
}
const calls = await this._provider.provideCallHierarchyOutgoingCalls(item, token);
if (!calls) {
return undefined;
}
return calls.map(call => {
return {
to: this._cacheAndConvertItem(sessionId, call.to),
fromRanges: call.fromRanges.map(r => typeConvert.Range.from(r))
};
});
}
releaseSession(sessionId: string): void {
this._cache.delete(sessionId.charAt(0));
}
private _cacheAndConvertItem(itemOrSessionId: string, item: vscode.CallHierarchyItem): extHostProtocol.ICallHierarchyItemDto {
const sessionId = itemOrSessionId.charAt(0);
const map = this._cache.get(sessionId)!;
const dto: extHostProtocol.ICallHierarchyItemDto = {
_sessionId: sessionId,
_itemId: map.size.toString(36),
name: item.name,
detail: item.detail,
kind: typeConvert.SymbolKind.from(item.kind),
uri: item.uri,
range: typeConvert.Range.from(item.range),
selectionRange: typeConvert.Range.from(item.selectionRange),
};
map.set(dto._itemId, item);
return dto;
}
private _itemFromCache(sessionId: string, itemId: string): vscode.CallHierarchyItem | undefined {
const map = this._cache.get(sessionId);
return map && map.get(itemId);
}
}
type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
| ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
| SemanticTokensAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter
| ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter
| DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
class AdapterData {
constructor(
@@ -1119,7 +1320,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
return ExtHostLanguageFeatures._handlePool++;
}
private _withAdapter<A, R>(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A, extension: IExtensionDescription | undefined) => Promise<R>, fallbackValue: R): Promise<R> {
private _withAdapter<A, R>(handle: number, ctor: { new(...args: any[]): A; }, callback: (adapter: A, extension: IExtensionDescription | undefined) => Promise<R>, fallbackValue: R): Promise<R> {
const data = this._adapter.get(handle);
if (!data) {
return Promise.resolve(fallbackValue);
@@ -1330,7 +1531,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- navigate types
registerWorkspaceSymbolProvider(extension: IExtensionDescription, provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
const handle = this._addNewAdapter(new NavigateTypeAdapter(provider), extension);
const handle = this._addNewAdapter(new NavigateTypeAdapter(provider, this._logService), extension);
this._proxy.$registerNavigateTypeSupport(handle);
return this._createDisposable(handle);
}
@@ -1350,7 +1551,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- rename
registerRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider), extension);
const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider, this._logService), extension);
this._proxy.$registerRenameSupport(handle, this._transformDocumentSelector(selector), RenameAdapter.supportsResolving(provider));
return this._createDisposable(handle);
}
@@ -1363,10 +1564,28 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token), undefined);
}
//#region semantic coloring
registerSemanticTokensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
const handle = this._addNewAdapter(new SemanticTokensAdapter(this._documents, provider), extension);
this._proxy.$registerSemanticTokensProvider(handle, this._transformDocumentSelector(selector), legend);
return this._createDisposable(handle);
}
$provideSemanticTokens(handle: number, resource: UriComponents, ranges: IRange[] | null, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
return this._withAdapter(handle, SemanticTokensAdapter, adapter => adapter.provideSemanticTokens(URI.revive(resource), ranges, previousResultId, token), null);
}
$releaseSemanticTokens(handle: number, semanticColoringResultId: number): void {
this._withAdapter(handle, SemanticTokensAdapter, adapter => adapter.releaseSemanticColoring(semanticColoringResultId), undefined);
}
//#endregion
// --- suggestion
registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider), extension);
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService), extension);
this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), extension.identifier);
return this._createDisposable(handle);
}
@@ -1450,7 +1669,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- smart select
registerSelectionRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable {
const handle = this._addNewAdapter(new SelectionRangeAdapter(this._documents, provider), extension);
const handle = this._addNewAdapter(new SelectionRangeAdapter(this._documents, provider, this._logService), extension);
this._proxy.$registerSelectionRangeProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -1461,18 +1680,26 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- call hierarchy
registerCallHierarchyProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CallHierarchyItemProvider): vscode.Disposable {
registerCallHierarchyProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CallHierarchyProvider): vscode.Disposable {
const handle = this._addNewAdapter(new CallHierarchyAdapter(this._documents, provider), extension);
this._proxy.$registerCallHierarchyProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideCallHierarchyIncomingCalls(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<[extHostProtocol.ICallHierarchyItemDto, IRange[]][] | undefined> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallsTo(URI.revive(resource), position, token), undefined);
$prepareCallHierarchy(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ICallHierarchyItemDto | undefined> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => Promise.resolve(adapter.prepareSession(URI.revive(resource), position, token)), undefined);
}
$provideCallHierarchyOutgoingCalls(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<[extHostProtocol.ICallHierarchyItemDto, IRange[]][] | undefined> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallsFrom(URI.revive(resource), position, token), undefined);
$provideCallHierarchyIncomingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.IIncomingCallDto[] | undefined> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallsTo(sessionId, itemId, token), undefined);
}
$provideCallHierarchyOutgoingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.IOutgoingCallDto[] | undefined> {
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallsFrom(sessionId, itemId, token), undefined);
}
$releaseCallHierarchy(handle: number, sessionId: string): void {
this._withAdapter(handle, CallHierarchyAdapter, adapter => Promise.resolve(adapter.releaseSession(sessionId)), undefined);
}
// --- configuration

View File

@@ -7,6 +7,7 @@ import Severity from 'vs/base/common/severity';
import * as vscode from 'vscode';
import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
function isMessageItem(item: any): item is vscode.MessageItem {
return item && item.title;
@@ -16,7 +17,10 @@ export class ExtHostMessageService {
private _proxy: MainThreadMessageServiceShape;
constructor(mainContext: IMainContext) {
constructor(
mainContext: IMainContext,
@ILogService private readonly _logService: ILogService
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadMessageService);
}
@@ -45,7 +49,7 @@ export class ExtHostMessageService {
let { title, isCloseAffordance } = command;
commands.push({ title, isCloseAffordance: !!isCloseAffordance, handle });
} else {
console.warn('Invalid message item:', command);
this._logService.warn('Invalid message item:', command);
}
}

View File

@@ -135,7 +135,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
// ---- input
showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise<string> {
showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise<string | undefined> {
// global validate fn used in callback below
this._validateInput = options ? options.validateInput : undefined;
@@ -160,7 +160,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
// ---- workspace folder picker
showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Promise<WorkspaceFolder | undefined> {
return this._commands.executeCommand('_workbench.pickWorkspaceFolder', [options]).then(async (selectedFolder: WorkspaceFolder) => {
return this._commands.executeCommand<WorkspaceFolder>('_workbench.pickWorkspaceFolder', [options]).then(async (selectedFolder: WorkspaceFolder) => {
if (!selectedFolder) {
return undefined;
}
@@ -485,6 +485,7 @@ class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implem
private _canSelectMany = false;
private _matchOnDescription = true;
private _matchOnDetail = true;
private _sortByLabel = true;
private _activeItems: T[] = [];
private readonly _onDidChangeActiveEmitter = new Emitter<T[]>();
private _selectedItems: T[] = [];
@@ -550,6 +551,15 @@ class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implem
this.update({ matchOnDetail });
}
get sortByLabel() {
return this._sortByLabel;
}
set sortByLabel(sortByLabel: boolean) {
this._sortByLabel = sortByLabel;
this.update({ sortByLabel });
}
get activeItems() {
return this._activeItems;
}

View File

@@ -18,6 +18,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { platform } from 'vs/base/common/process';
import { ILogService } from 'vs/platform/log/common/log';
import { AzdataNodeModuleFactory, SqlopsNodeModuleFactory } from 'sql/workbench/api/common/extHostRequireInterceptor'; // {{SQL CARBON EDIT}}
import { IExtensionApiFactory as sqlIApiFactory } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}}
@@ -44,7 +45,8 @@ export abstract class RequireInterceptor {
@IInstantiationService private readonly _instaService: IInstantiationService,
@IExtHostConfiguration private readonly _extHostConfiguration: IExtHostConfiguration,
@IExtHostExtensionService private readonly _extHostExtensionService: IExtHostExtensionService,
@IExtHostInitDataService private readonly _initData: IExtHostInitDataService
@IExtHostInitDataService private readonly _initData: IExtHostInitDataService,
@ILogService private readonly _logService: ILogService,
) {
this._factories = new Map<string, INodeModuleFactory>();
this._alternatives = [];
@@ -57,7 +59,7 @@ export abstract class RequireInterceptor {
const configProvider = await this._extHostConfiguration.getConfigProvider();
const extensionPaths = await this._extHostExtensionService.getExtensionPathIndex();
this.register(new VSCodeNodeModuleFactory(this._apiFactory.vscode, extensionPaths, this._extensionRegistry, configProvider)); // {{SQL CARBON EDIT}} // add node module
this.register(new VSCodeNodeModuleFactory(this._apiFactory.vscode, extensionPaths, this._extensionRegistry, configProvider, this._logService)); // {{SQL CARBON EDIT}} // add node module
this.register(new AzdataNodeModuleFactory(this._apiFactory.azdata, extensionPaths)); // {{SQL CARBON EDIT}} // add node module
this.register(new SqlopsNodeModuleFactory(this._apiFactory.sqlops, extensionPaths)); // {{SQL CARBON EDIT}} // add node module
this.register(this._instaService.createInstance(KeytarNodeModuleFactory));
@@ -96,7 +98,8 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory {
private readonly _apiFactory: IExtensionApiFactory,
private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>,
private readonly _extensionRegistry: ExtensionDescriptionRegistry,
private readonly _configProvider: ExtHostConfigProvider
private readonly _configProvider: ExtHostConfigProvider,
private readonly _logService: ILogService,
) {
}
@@ -117,7 +120,7 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory {
if (!this._defaultApiImpl) {
let extensionPathsPretty = '';
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
this._logService.warn(`Could not identify extension for 'vscode' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);
}
return this._defaultApiImpl;
@@ -247,9 +250,9 @@ class OpenNodeModuleFactory implements INodeModuleFactory {
return this.callOriginal(target, options);
}
if (uri.scheme === 'http' || uri.scheme === 'https') {
return mainThreadWindow.$openUri(uri, { allowTunneling: true });
return mainThreadWindow.$openUri(uri, target, { allowTunneling: true });
} else if (uri.scheme === 'mailto' || uri.scheme === this._appUriScheme) {
return mainThreadWindow.$openUri(uri, {});
return mainThreadWindow.$openUri(uri, target, {});
}
return this.callOriginal(target, options);
};

View File

@@ -180,8 +180,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
}
if (fn && typeof fn !== 'function') {
console.warn('Invalid SCM input box validation function');
return;
throw new Error(`[${this._extension.identifier.value}]: Invalid SCM input box validation function`);
}
this._validateInput = fn;

View File

@@ -3,10 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import * as vscode from 'vscode';
import { ExtHostSearchShape } from '../common/extHost.protocol';
import { ExtHostSearchShape, MainThreadSearchShape, MainContext } from '../common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { FileSearchManager } from 'vs/workbench/services/search/common/fileSearchManager';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
import { ILogService } from 'vs/platform/log/common/log';
import { IRawFileQuery, ISearchCompleteStats, IFileQuery, IRawTextQuery, IRawQuery, ITextQuery, IFolderQuery } from 'vs/workbench/services/search/common/search';
import { URI, UriComponents } from 'vs/base/common/uri';
import { TextSearchManager } from 'vs/workbench/services/search/common/textSearchManager';
export interface IExtHostSearch extends ExtHostSearchShape {
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable;
@@ -14,3 +21,111 @@ export interface IExtHostSearch extends ExtHostSearchShape {
}
export const IExtHostSearch = createDecorator<IExtHostSearch>('IExtHostSearch');
export class ExtHostSearch implements ExtHostSearchShape {
protected readonly _proxy: MainThreadSearchShape = this.extHostRpc.getProxy(MainContext.MainThreadSearch);
protected _handlePool: number = 0;
private readonly _textSearchProvider = new Map<number, vscode.TextSearchProvider>();
private readonly _textSearchUsedSchemes = new Set<string>();
private readonly _fileSearchProvider = new Map<number, vscode.FileSearchProvider>();
private readonly _fileSearchUsedSchemes = new Set<string>();
private readonly _fileSearchManager = new FileSearchManager();
constructor(
@IExtHostRpcService private extHostRpc: IExtHostRpcService,
@IURITransformerService protected _uriTransformer: IURITransformerService,
@ILogService protected _logService: ILogService
) { }
protected _transformScheme(scheme: string): string {
return this._uriTransformer.transformOutgoingScheme(scheme);
}
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable {
if (this._textSearchUsedSchemes.has(scheme)) {
throw new Error(`a text search provider for the scheme '${scheme}' is already registered`);
}
this._textSearchUsedSchemes.add(scheme);
const handle = this._handlePool++;
this._textSearchProvider.set(handle, provider);
this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._textSearchUsedSchemes.delete(scheme);
this._textSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable {
if (this._fileSearchUsedSchemes.has(scheme)) {
throw new Error(`a file search provider for the scheme '${scheme}' is already registered`);
}
this._fileSearchUsedSchemes.add(scheme);
const handle = this._handlePool++;
this._fileSearchProvider.set(handle, provider);
this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._fileSearchUsedSchemes.delete(scheme);
this._fileSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
const query = reviveQuery(rawQuery);
const provider = this._fileSearchProvider.get(handle);
if (provider) {
return this._fileSearchManager.fileSearch(query, provider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}, token);
} else {
throw new Error('unknown provider: ' + handle);
}
}
$clearCache(cacheKey: string): Promise<void> {
this._fileSearchManager.clearCache(cacheKey);
return Promise.resolve(undefined);
}
$provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
const provider = this._textSearchProvider.get(handle);
if (!provider || !provider.provideTextSearchResults) {
throw new Error(`Unknown provider ${handle}`);
}
const query = reviveQuery(rawQuery);
const engine = this.createTextSearchManager(query, provider);
return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token);
}
protected createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager {
return new TextSearchManager(query, provider, {
readdir: resource => Promise.resolve([]), // TODO@rob implement
toCanonicalName: encoding => encoding
});
}
}
export function reviveQuery<U extends IRawQuery>(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery {
return {
...<any>rawQuery, // TODO@rob ???
...{
folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery),
extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components))
}
};
}
function reviveFolderQuery(rawFolderQuery: IFolderQuery<UriComponents>): IFolderQuery<URI> {
return {
...rawFolderQuery,
folder: URI.revive(rawFolderQuery.folder)
};
}

View File

@@ -24,6 +24,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Schemas } from 'vs/base/common/network';
import * as Platform from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
export interface IExtHostTask extends ExtHostTaskShape {
@@ -374,6 +375,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
protected readonly _editorService: IExtHostDocumentsAndEditors;
protected readonly _configurationService: IExtHostConfiguration;
protected readonly _terminalService: IExtHostTerminalService;
protected readonly _logService: ILogService;
protected _handleCounter: number;
protected _handlers: Map<number, HandlerData>;
protected _taskExecutions: Map<string, TaskExecutionImpl>;
@@ -393,7 +395,8 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
@IExtHostConfiguration configurationService: IExtHostConfiguration,
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
@ILogService logService: ILogService
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask);
this._workspaceProvider = workspaceService;
@@ -406,6 +409,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
this._providedCustomExecutions2 = new Map<string, types.CustomExecution>();
this._notProvidedCustomExecutions = new Set<string>();
this._activeCustomExecutions2 = new Map<string, types.CustomExecution>();
this._logService = logService;
}
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
@@ -457,6 +461,10 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
return this._onDidExecuteTask.event;
}
protected async resolveDefinition(uri: number | UriComponents | undefined, definition: vscode.TaskDefinition | undefined): Promise<vscode.TaskDefinition | undefined> {
return definition;
}
public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number): Promise<void> {
const customExecution: types.CustomExecution | undefined = this._providedCustomExecutions2.get(execution.id);
if (customExecution) {
@@ -466,7 +474,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
// Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task.
this._activeCustomExecutions2.set(execution.id, customExecution);
this._terminalService.attachPtyToTerminal(terminalId, await customExecution.callback());
this._terminalService.attachPtyToTerminal(terminalId, await customExecution.callback(await this.resolveDefinition(execution.task?.source.scope, execution.task?.definition)));
}
this._lastStartedTask = execution.id;
@@ -657,9 +665,10 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
@IExtHostConfiguration configurationService: IExtHostConfiguration,
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
@ILogService logService: ILogService
) {
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService);
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService);
if (initData.remote.isRemote && initData.remote.authority) {
this.registerTaskSystem(Schemas.vscodeRemote, {
scheme: Schemas.vscodeRemote,
@@ -692,7 +701,7 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
if (value) {
for (let task of value) {
if (!task.definition || !validTypes[task.definition.type]) {
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
this._logService.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
}
const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension);
@@ -703,7 +712,7 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
// is invoked, we have to be able to map it back to our data.
taskIdPromises.push(this.addCustomExecution(taskDTO, task, true));
} else {
console.warn('Only custom execution tasks supported.');
this._logService.warn('Only custom execution tasks supported.');
}
}
}
@@ -717,7 +726,7 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
if (CustomExecutionDTO.is(resolvedTaskDTO.execution)) {
return resolvedTaskDTO;
} else {
console.warn('Only custom execution tasks supported.');
this._logService.warn('Only custom execution tasks supported.');
}
return undefined;
}

View File

@@ -9,9 +9,10 @@ import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShap
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI, UriComponents } from 'vs/base/common/uri';
import { EXT_HOST_CREATION_DELAY, ITerminalChildProcess, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal';
import { timeout } from 'vs/base/common/async';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
@@ -96,15 +97,18 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
private _cols: number | undefined;
private _pidPromiseComplete: ((value: number | undefined) => any) | undefined;
private _rows: number | undefined;
private _exitStatus: vscode.TerminalExitStatus | undefined;
public isOpen: boolean = false;
constructor(
proxy: MainThreadTerminalServiceShape,
private readonly _creationOptions: vscode.TerminalOptions | vscode.ExtensionTerminalOptions,
private _name?: string,
id?: number
) {
super(proxy, id);
this._creationOptions = Object.freeze(this._creationOptions);
this._pidPromise = new Promise<number>(c => this._pidPromiseComplete = c);
}
@@ -137,6 +141,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
this._name = name;
}
public get exitStatus(): vscode.TerminalExitStatus | undefined {
return this._exitStatus;
}
public get dimensions(): vscode.TerminalDimensions | undefined {
if (this._cols === undefined || this._rows === undefined) {
return undefined;
@@ -147,6 +155,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
};
}
public setExitCode(code: number | undefined) {
this._exitStatus = Object.freeze({ code });
}
public setDimensions(cols: number, rows: number): boolean {
if (cols === this._cols && rows === this._rows) {
// Nothing changed
@@ -161,6 +173,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
return this._pidPromise;
}
public get creationOptions(): Readonly<vscode.TerminalOptions | vscode.ExtensionTerminalOptions> {
return this._creationOptions;
}
public sendText(text: string, addNewLine: boolean = true): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$sendText, [text, addNewLine]);
@@ -209,8 +225,8 @@ class ApiRequest {
export class ExtHostPseudoterminal implements ITerminalChildProcess {
private readonly _onProcessData = new Emitter<string>();
public readonly onProcessData: Event<string> = this._onProcessData.event;
private readonly _onProcessExit = new Emitter<number>();
public readonly onProcessExit: Event<number> = this._onProcessExit.event;
private readonly _onProcessExit = new Emitter<number | undefined>();
public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
private readonly _onProcessReady = new Emitter<{ pid: number, cwd: string }>();
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = new Emitter<string>();
@@ -252,7 +268,9 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
// Attach the listeners
this._pty.onDidWrite(e => this._onProcessData.fire(e));
if (this._pty.onDidClose) {
this._pty.onDidClose(e => this._onProcessExit.fire(e || 0));
this._pty.onDidClose((e: number | void = undefined) => {
this._onProcessExit.fire(e === void 0 ? undefined : e as number); // {{SQL CARBON EDIT}} strict-null-checks
});
}
if (this._pty.onDidOverrideDimensions) {
this._pty.onDidOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e ? { cols: e.columns, rows: e.rows } : undefined)); // {{SQL CARBONEDIT}} strict-null-checks
@@ -270,6 +288,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
protected _activeTerminal: ExtHostTerminal | undefined;
protected _terminals: ExtHostTerminal[] = [];
protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {};
protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {};
protected _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
@@ -286,9 +305,13 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
protected readonly _onDidWriteTerminalData: Emitter<vscode.TerminalDataWriteEvent>;
public get onDidWriteTerminalData(): Event<vscode.TerminalDataWriteEvent> { return this._onDidWriteTerminalData && this._onDidWriteTerminalData.event; }
private readonly _bufferer: TerminalDataBufferer;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService
) {
this._bufferer = new TerminalDataBufferer();
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTerminalService);
this._onDidWriteTerminalData = new Emitter<vscode.TerminalDataWriteEvent>({
onFirstListenerAdd: () => this._proxy.$startSendingDataEvents(),
@@ -305,7 +328,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, options.name);
const terminal = new ExtHostTerminal(this._proxy, options, options.name);
const p = new ExtHostPseudoterminal(options.pty);
terminal.createExtensionTerminal().then(id => this._setupExtHostProcessListeners(id, p));
this._terminals.push(terminal);
@@ -364,7 +387,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
if (this._terminalProcesses[id]) {
// Extension pty terminal only - when virtual process resize fires it means that the
// terminal's maximum dimensions changed
this._terminalProcesses[id].resize(cols, rows);
this._terminalProcesses[id]?.resize(cols, rows);
}
}
@@ -376,16 +399,17 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
}
}
public async $acceptTerminalClosed(id: number): Promise<void> {
public async $acceptTerminalClosed(id: number, exitCode: number | undefined): Promise<void> {
await this._getTerminalByIdEventually(id);
const index = this._getTerminalObjectIndexById(this.terminals, id);
if (index !== null) {
const terminal = this._terminals.splice(index, 1)[0];
terminal.setExitCode(exitCode);
this._onDidCloseTerminal.fire(terminal);
}
}
public $acceptTerminalOpened(id: number, name: string): void {
public $acceptTerminalOpened(id: number, name: string, shellLaunchConfigDto: IShellLaunchConfigDto): void {
const index = this._getTerminalObjectIndexById(this._terminals, id);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
@@ -394,7 +418,14 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
return;
}
const terminal = new ExtHostTerminal(this._proxy, name, id);
const creationOptions: vscode.TerminalOptions = {
name: shellLaunchConfigDto.name,
shellPath: shellLaunchConfigDto.executable,
shellArgs: shellLaunchConfigDto.args,
cwd: typeof shellLaunchConfigDto.cwd === 'string' ? shellLaunchConfigDto.cwd : URI.revive(shellLaunchConfigDto.cwd),
env: shellLaunchConfigDto.env
};
const terminal = new ExtHostTerminal(this._proxy, creationOptions, name, id);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
terminal.isOpen = true;
@@ -407,22 +438,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
}
}
public performTerminalIdAction(id: number, callback: (terminal: ExtHostTerminal) => void): void {
// TODO: Use await this._getTerminalByIdEventually(id);
let terminal = this._getTerminalById(id);
if (terminal) {
callback(terminal);
} else {
// Retry one more time in case the terminal has not yet been initialized.
setTimeout(() => {
terminal = this._getTerminalById(id);
if (terminal) {
callback(terminal);
}
}, EXT_HOST_CREATION_DELAY * 2);
}
}
public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<void> {
// Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call
// Pseudoterminal.start
@@ -448,37 +463,42 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
}
await openPromise;
// Processes should be initialized here for normal virtual process terminals, however for
// tasks they are responsible for attaching the virtual process to a terminal so this
// function may be called before tasks is able to attach to the terminal.
let retries = 5;
while (retries-- > 0) {
if (this._terminalProcesses[id]) {
(this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions);
return;
}
await timeout(50);
if (this._terminalProcesses[id]) {
(this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions);
} else {
// Defer startSendingEvents call to when _setupExtHostProcessListeners is called
this._extensionTerminalAwaitingStart[id] = { initialDimensions };
}
}
protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): void {
p.onProcessReady((e: { pid: number, cwd: string }) => this._proxy.$sendProcessReady(id, e.pid, e.cwd));
p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title));
p.onProcessData(data => this._proxy.$sendProcessData(id, data));
// Buffer data events to reduce the amount of messages going to the renderer
this._bufferer.startBuffering(id, p.onProcessData, this._proxy.$sendProcessData);
p.onProcessExit(exitCode => this._onProcessExit(id, exitCode));
if (p.onProcessOverrideDimensions) {
p.onProcessOverrideDimensions(e => this._proxy.$sendOverrideDimensions(id, e));
}
this._terminalProcesses[id] = p;
const awaitingStart = this._extensionTerminalAwaitingStart[id];
if (awaitingStart && p instanceof ExtHostPseudoterminal) {
p.startSendingEvents(awaitingStart.initialDimensions);
delete this._extensionTerminalAwaitingStart[id];
}
}
public $acceptProcessInput(id: number, data: string): void {
this._terminalProcesses[id].input(data);
this._terminalProcesses[id]?.input(data);
}
public $acceptProcessResize(id: number, cols: number, rows: number): void {
try {
this._terminalProcesses[id].resize(cols, rows);
this._terminalProcesses[id]?.resize(cols, rows);
} catch (error) {
// We tried to write to a closed pipe / channel.
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
@@ -488,24 +508,27 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
}
public $acceptProcessShutdown(id: number, immediate: boolean): void {
this._terminalProcesses[id].shutdown(immediate);
this._terminalProcesses[id]?.shutdown(immediate);
}
public $acceptProcessRequestInitialCwd(id: number): void {
this._terminalProcesses[id].getInitialCwd().then(initialCwd => this._proxy.$sendProcessInitialCwd(id, initialCwd));
this._terminalProcesses[id]?.getInitialCwd().then(initialCwd => this._proxy.$sendProcessInitialCwd(id, initialCwd));
}
public $acceptProcessRequestCwd(id: number): void {
this._terminalProcesses[id].getCwd().then(cwd => this._proxy.$sendProcessCwd(id, cwd));
this._terminalProcesses[id]?.getCwd().then(cwd => this._proxy.$sendProcessCwd(id, cwd));
}
public $acceptProcessRequestLatency(id: number): number {
return id;
}
private _onProcessExit(id: number, exitCode: number): void {
private _onProcessExit(id: number, exitCode: number | undefined): void {
this._bufferer.stopBuffering(id);
// Remove process reference
delete this._terminalProcesses[id];
delete this._extensionTerminalAwaitingStart[id];
// Send exit event to main side
this._proxy.$sendProcessExit(id, exitCode);
@@ -515,10 +538,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal | undefined> {
if (!this._getTerminalPromises[id]) {
this._getTerminalPromises[id] = this._createGetTerminalPromise(id, retries);
} else {
this._getTerminalPromises[id].then(c => {
return this._createGetTerminalPromise(id, retries);
});
}
return this._getTerminalPromises[id];
}
@@ -536,7 +555,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
} else {
// This should only be needed immediately after createTerminalRenderer is called as
// the ExtHostTerminal has not yet been iniitalized
timeout(200).then(() => c(this._createGetTerminalPromise(id, retries - 1)));
timeout(EXT_HOST_CREATION_DELAY * 2).then(() => c(this._createGetTerminalPromise(id, retries - 1)));
}
});
}

View File

@@ -95,12 +95,10 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
get onDidChangeVisibility() { return treeView.onDidChangeVisibility; },
get message() { return treeView.message; },
set message(message: string) {
checkProposedApiEnabled(extension);
treeView.message = message;
},
get title() { return treeView.title; },
set title(title: string) {
checkProposedApiEnabled(extension);
treeView.title = title;
},
reveal: (element: T, options?: IRevealOptions): Promise<void> => {
@@ -202,7 +200,13 @@ export class ExtHostTreeView<T> extends Disposable {
private refreshPromise: Promise<void> = Promise.resolve();
private refreshQueue: Promise<void> = Promise.resolve();
constructor(private viewId: string, options: vscode.TreeViewOptions<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) {
constructor(
private viewId: string, options: vscode.TreeViewOptions<T>,
private proxy: MainThreadTreeViewsShape,
private commands: CommandsConverter,
private logService: ILogService,
private extension: IExtensionDescription
) {
super();
if (extension.contributes && extension.contributes.views) {
for (const location in extension.contributes.views) {
@@ -257,7 +261,7 @@ export class ExtHostTreeView<T> extends Disposable {
getChildren(parentHandle: TreeItemHandle | Root): Promise<ITreeItem[]> {
const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : undefined;
if (parentHandle && !parentElement) {
console.error(`No tree item with id \'${parentHandle}\' found.`);
this.logService.error(`No tree item with id \'${parentHandle}\' found.`);
return Promise.resolve([]);
}
@@ -425,7 +429,7 @@ export class ExtHostTreeView<T> extends Disposable {
// check if an ancestor of extElement is already in the elements to update list
let currentNode: TreeNode | undefined = elementNode;
while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent.item.handle)) {
const parentElement = this.elements.get(currentNode.parent.item.handle);
const parentElement: T | undefined = this.elements.get(currentNode.parent.item.handle);
currentNode = parentElement ? this.nodes.get(parentElement) : undefined;
}
if (currentNode && !currentNode.parent) {

View File

@@ -13,9 +13,9 @@ import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/mode
import * as vscode from 'vscode';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { SaveReason } from 'vs/workbench/common/editor';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import * as editorRange from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import * as htmlContent from 'vs/base/common/htmlContent';
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
@@ -31,7 +31,6 @@ import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log';
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
export interface PositionLike {
line: number;
character: number;
@@ -68,9 +67,9 @@ export namespace Selection {
export namespace Range {
export function from(range: undefined): undefined;
export function from(range: RangeLike): IRange;
export function from(range: RangeLike | undefined): IRange | undefined;
export function from(range: RangeLike | undefined): IRange | undefined {
export function from(range: RangeLike): editorRange.IRange;
export function from(range: RangeLike | undefined): editorRange.IRange | undefined;
export function from(range: RangeLike | undefined): editorRange.IRange | undefined {
if (!range) {
return undefined;
}
@@ -84,9 +83,9 @@ export namespace Range {
}
export function to(range: undefined): types.Range;
export function to(range: IRange): types.Range;
export function to(range: IRange | undefined): types.Range | undefined;
export function to(range: IRange | undefined): types.Range | undefined {
export function to(range: editorRange.IRange): types.Range;
export function to(range: editorRange.IRange | undefined): types.Range | undefined;
export function to(range: editorRange.IRange | undefined): types.Range | undefined {
if (!range) {
return undefined;
}
@@ -260,7 +259,7 @@ export namespace MarkdownString {
const collectUri = (href: string): string => {
try {
let uri = URI.parse(href);
let uri = URI.parse(href, true);
uri = uri.with({ query: _uriMassage(uri.query, resUris) });
resUris[href] = uri;
} catch (e) {
@@ -290,16 +289,23 @@ export namespace MarkdownString {
if (!data) {
return part;
}
let changed = false;
data = cloneAndChange(data, value => {
if (URI.isUri(value)) {
const key = `__uri_${Math.random().toString(16).slice(2, 8)}`;
bucket[key] = value;
changed = true;
return key;
} else {
return undefined;
}
});
return encodeURIComponent(JSON.stringify(data));
if (!changed) {
return part;
}
return JSON.stringify(data);
}
export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString {
@@ -627,19 +633,8 @@ export namespace DocumentSymbol {
export namespace CallHierarchyItem {
export function from(item: vscode.CallHierarchyItem): extHostProtocol.ICallHierarchyItemDto {
return {
name: item.name,
detail: item.detail,
kind: SymbolKind.from(item.kind),
uri: item.uri,
range: Range.from(item.range),
selectionRange: Range.from(item.selectionRange),
};
}
export function to(item: extHostProtocol.ICallHierarchyItemDto): vscode.CallHierarchyItem {
return new types.CallHierarchyItem(
export function to(item: extHostProtocol.ICallHierarchyItemDto): types.CallHierarchyItem {
const result = new types.CallHierarchyItem(
SymbolKind.to(item.kind),
item.name,
item.detail || '',
@@ -647,6 +642,31 @@ export namespace CallHierarchyItem {
Range.to(item.range),
Range.to(item.selectionRange)
);
result._sessionId = item._sessionId;
result._itemId = item._itemId;
return result;
}
}
export namespace CallHierarchyIncomingCall {
export function to(item: extHostProtocol.IIncomingCallDto): types.CallHierarchyIncomingCall {
return new types.CallHierarchyIncomingCall(
CallHierarchyItem.to(item.from),
item.fromRanges.map(r => Range.to(r))
);
}
}
export namespace CallHierarchyOutgoingCall {
export function to(item: extHostProtocol.IOutgoingCallDto): types.CallHierarchyOutgoingCall {
return new types.CallHierarchyOutgoingCall(
CallHierarchyItem.to(item.to),
item.fromRanges.map(r => Range.to(r))
);
}
}
@@ -821,14 +841,15 @@ export namespace CompletionItem {
result.filterText = suggestion.filterText;
result.preselect = suggestion.preselect;
result.commitCharacters = suggestion.commitCharacters;
result.range = Range.to(suggestion.range);
result.range = editorRange.Range.isIRange(suggestion.range) ? Range.to(suggestion.range) : undefined;
result.range2 = editorRange.Range.isIRange(suggestion.range) ? undefined : { inserting: Range.to(suggestion.range.insert), replacing: Range.to(suggestion.range.replace) };
result.keepWhitespace = typeof suggestion.insertTextRules === 'undefined' ? false : Boolean(suggestion.insertTextRules & modes.CompletionItemInsertTextRule.KeepWhitespace);
// 'inserText'-logic
if (typeof suggestion.insertTextRules !== 'undefined' && suggestion.insertTextRules & modes.CompletionItemInsertTextRule.InsertAsSnippet) {
result.insertText = new types.SnippetString(suggestion.insertText);
} else {
result.insertText = suggestion.insertText;
result.textEdit = new types.TextEdit(result.range, result.insertText);
result.textEdit = result.range instanceof types.Range ? new types.TextEdit(result.range, result.insertText) : undefined;
}
// TODO additionalEdits, command
@@ -903,7 +924,7 @@ export namespace DocumentLink {
let target: URI | undefined = undefined;
if (link.url) {
try {
target = typeof link.url === 'string' ? URI.parse(link.url) : URI.revive(link.url);
target = typeof link.url === 'string' ? URI.parse(link.url, true) : URI.revive(link.url);
} catch (err) {
// ignore
}

View File

@@ -29,8 +29,8 @@ function es5ClassCompat(target: Function): any {
@es5ClassCompat
export class Disposable {
static from(...inDisposables: { dispose(): any }[]): Disposable {
let disposables: ReadonlyArray<{ dispose(): any }> | undefined = inDisposables;
static from(...inDisposables: { dispose(): any; }[]): Disposable {
let disposables: ReadonlyArray<{ dispose(): any; }> | undefined = inDisposables;
return new Disposable(function () {
if (disposables) {
for (const disposable of disposables) {
@@ -333,9 +333,9 @@ export class Range {
return this._start.line === this._end.line;
}
with(change: { start?: Position, end?: Position }): Range;
with(change: { start?: Position, end?: Position; }): Range;
with(start?: Position, end?: Position): Range;
with(startOrChange: Position | undefined | { start?: Position, end?: Position }, end: Position = this.end): Range {
with(startOrChange: Position | undefined | { start?: Position, end?: Position; }, end: Position = this.end): Range {
if (startOrChange === null || end === null) {
throw illegalArgument();
@@ -589,15 +589,15 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
private _edits = new Array<IFileOperation | IFileTextEdit>();
renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void {
renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean; }): void {
this._edits.push({ _type: 1, from, to, options });
}
createFile(uri: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void {
createFile(uri: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean; }): void {
this._edits.push({ _type: 1, from: undefined, to: uri, options });
}
deleteFile(uri: vscode.Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean }): void {
deleteFile(uri: vscode.Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean; }): void {
this._edits.push({ _type: 1, from: uri, to: undefined, options });
}
@@ -740,6 +740,18 @@ export class SnippetString {
return this;
}
appendChoice(values: string[], number: number = this._tabstop++): SnippetString {
const value = SnippetString._escape(values.toString());
this.value += '${';
this.value += number;
this.value += '|';
this.value += value;
this.value += '|}';
return this;
}
appendVariable(name: string, defaultValue?: string | ((snippet: SnippetString) => any)): SnippetString {
if (typeof defaultValue === 'function') {
@@ -1142,6 +1154,10 @@ export class SelectionRange {
}
export class CallHierarchyItem {
_sessionId?: string;
_itemId?: string;
kind: SymbolKind;
name: string;
detail?: string;
@@ -1344,6 +1360,7 @@ export class CompletionItem implements vscode.CompletionItem {
insertText?: string | SnippetString;
keepWhitespace?: boolean;
range?: Range;
range2?: Range | { inserting: Range; replacing: Range; };
commitCharacters?: string[];
textEdit?: TextEdit;
additionalTextEdits?: TextEdit[];
@@ -1495,7 +1512,7 @@ export class Color {
}
}
export type IColorFormat = string | { opaque: string, transparent: string };
export type IColorFormat = string | { opaque: string, transparent: string; };
@es5ClassCompat
export class ColorInformation {
@@ -1776,20 +1793,20 @@ export enum TaskScope {
Workspace = 2
}
export class CustomExecution implements vscode.CustomExecution {
private _callback: () => Thenable<vscode.Pseudoterminal>;
constructor(callback: () => Thenable<vscode.Pseudoterminal>) {
export class CustomExecution implements vscode.CustomExecution2 {
private _callback: (resolvedDefintion?: vscode.TaskDefinition) => Thenable<vscode.Pseudoterminal>;
constructor(callback: (resolvedDefintion?: vscode.TaskDefinition) => Thenable<vscode.Pseudoterminal>) {
this._callback = callback;
}
public computeId(): string {
return 'customExecution' + generateUuid();
}
public set callback(value: () => Thenable<vscode.Pseudoterminal>) {
public set callback(value: (resolvedDefintion?: vscode.TaskDefinition) => Thenable<vscode.Pseudoterminal>) {
this._callback = value;
}
public get callback(): (() => Thenable<vscode.Pseudoterminal>) {
public get callback(): ((resolvedDefintion?: vscode.TaskDefinition) => Thenable<vscode.Pseudoterminal>) {
return this._callback;
}
}
@@ -2050,13 +2067,13 @@ export class TreeItem {
label?: string | vscode.TreeItemLabel;
resourceUri?: URI;
iconPath?: string | URI | { light: string | URI; dark: string | URI };
iconPath?: string | URI | { light: string | URI; dark: string | URI; };
command?: vscode.Command;
contextValue?: string;
tooltip?: string;
constructor(label: string | vscode.TreeItemLabel, collapsibleState?: vscode.TreeItemCollapsibleState)
constructor(resourceUri: URI, collapsibleState?: vscode.TreeItemCollapsibleState)
constructor(label: string | vscode.TreeItemLabel, collapsibleState?: vscode.TreeItemCollapsibleState);
constructor(resourceUri: URI, collapsibleState?: vscode.TreeItemCollapsibleState);
constructor(arg1: string | vscode.TreeItemLabel | URI, public collapsibleState: vscode.TreeItemCollapsibleState = TreeItemCollapsibleState.None) {
if (URI.isUri(arg1)) {
this.resourceUri = arg1;
@@ -2233,16 +2250,14 @@ export class DebugAdapterServer implements vscode.DebugAdapterServer {
}
}
/*
@es5ClassCompat
export class DebugAdapterImplementation implements vscode.DebugAdapterImplementation {
readonly implementation: any;
export class DebugAdapterInlineImplementation implements vscode.DebugAdapterInlineImplementation {
readonly implementation: vscode.DebugAdapter;
constructor(transport: any) {
this.implementation = transport;
constructor(impl: vscode.DebugAdapter) {
this.implementation = impl;
}
}
*/
export enum LogLevel {
Trace = 1,
@@ -2351,6 +2366,91 @@ export enum CommentMode {
//#endregion
//#region Semantic Coloring
export class SemanticTokensLegend {
public readonly tokenTypes: string[];
public readonly tokenModifiers: string[];
constructor(tokenTypes: string[], tokenModifiers: string[]) {
this.tokenTypes = tokenTypes;
this.tokenModifiers = tokenModifiers;
}
}
export class SemanticTokensBuilder {
private _prevLine: number;
private _prevChar: number;
private _data: number[];
private _dataLen: number;
constructor() {
this._prevLine = 0;
this._prevChar = 0;
this._data = [];
this._dataLen = 0;
}
public push(line: number, char: number, length: number, tokenType: number, tokenModifiers: number): void {
let pushLine = line;
let pushChar = char;
if (this._dataLen > 0) {
pushLine -= this._prevLine;
if (pushLine === 0) {
pushChar -= this._prevChar;
}
}
this._data[this._dataLen++] = pushLine;
this._data[this._dataLen++] = pushChar;
this._data[this._dataLen++] = length;
this._data[this._dataLen++] = tokenType;
this._data[this._dataLen++] = tokenModifiers;
this._prevLine = line;
this._prevChar = char;
}
public build(): Uint32Array {
return new Uint32Array(this._data);
}
}
export class SemanticTokens {
readonly resultId?: string;
readonly data: Uint32Array;
constructor(data: Uint32Array, resultId?: string) {
this.resultId = resultId;
this.data = data;
}
}
export class SemanticTokensEdit {
readonly start: number;
readonly deleteCount: number;
readonly data?: Uint32Array;
constructor(start: number, deleteCount: number, data?: Uint32Array) {
this.start = start;
this.deleteCount = deleteCount;
this.data = data;
}
}
export class SemanticTokensEdits {
readonly resultId?: string;
readonly edits: SemanticTokensEdit[];
constructor(edits: SemanticTokensEdit[], resultId?: string) {
this.resultId = resultId;
this.edits = edits;
}
}
//#endregion
//#region debug
export enum DebugConsoleMode {
/**

View File

@@ -56,7 +56,11 @@ export class ExtHostUrls implements ExtHostUrlsShape {
return Promise.resolve(undefined);
}
async createAppUri(extensionId: ExtensionIdentifier, options?: vscode.AppUriOptions): Promise<vscode.Uri> {
return URI.revive(await this._proxy.$createAppUri(extensionId, options));
async createAppUri(uri: URI): Promise<vscode.Uri> {
return URI.revive(await this._proxy.$createAppUri(uri));
}
async proposedCreateAppUri(extensionId: ExtensionIdentifier, options?: vscode.AppUriOptions): Promise<vscode.Uri> {
return URI.revive(await this._proxy.$proposedCreateAppUri(extensionId, options));
}
}

View File

@@ -4,17 +4,20 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { assertIsDefined } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import * as modes from 'vs/editor/common/modes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import { Disposable as VSCodeDisposable } from './extHostTypes';
type IconPath = URI | { light: URI, dark: URI };
@@ -33,6 +36,7 @@ export class ExtHostWebview implements vscode.Webview {
private readonly _initData: WebviewInitData,
private readonly _workspace: IExtHostWorkspace | undefined,
private readonly _extension: IExtensionDescription,
private readonly _logService: ILogService,
) { }
public dispose() {
@@ -61,7 +65,7 @@ export class ExtHostWebview implements vscode.Webview {
if (this._initData.isExtensionDevelopmentDebug && !this._hasCalledAsWebviewUri) {
if (/(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) {
this._hasCalledAsWebviewUri = true;
console.warn(`${this._extension.identifier.value} created a webview that appears to use the vscode-resource scheme directly. Please migrate to use the 'webview.asWebviewUri' api instead: https://aka.ms/vscode-webview-use-aswebviewuri`);
this._logService.warn(`${this._extension.identifier.value} created a webview that appears to use the vscode-resource scheme directly. Please migrate to use the 'webview.asWebviewUri' api instead: https://aka.ms/vscode-webview-use-aswebviewuri`);
}
}
this._proxy.$setHtml(this._handle, value);
@@ -91,7 +95,7 @@ export class ExtHostWebview implements vscode.Webview {
}
}
export class ExtHostWebviewEditor implements vscode.WebviewEditor {
export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPanel {
private readonly _handle: WebviewPanelHandle;
private readonly _proxy: MainThreadWebviewsShape;
@@ -107,12 +111,14 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor {
_isDisposed: boolean = false;
readonly _onDisposeEmitter = new Emitter<void>();
readonly _onDisposeEmitter = this._register(new Emitter<void>());
public readonly onDidDispose: Event<void> = this._onDisposeEmitter.event;
readonly _onDidChangeViewStateEmitter = new Emitter<vscode.WebviewPanelOnDidChangeViewStateEvent>();
readonly _onDidChangeViewStateEmitter = this._register(new Emitter<vscode.WebviewPanelOnDidChangeViewStateEvent>());
public readonly onDidChangeViewState: Event<vscode.WebviewPanelOnDidChangeViewStateEvent> = this._onDidChangeViewStateEmitter.event;
public _capabilities?: vscode.WebviewEditorCapabilities;
constructor(
handle: WebviewPanelHandle,
proxy: MainThreadWebviewsShape,
@@ -122,6 +128,7 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor {
editorOptions: vscode.WebviewPanelOptions,
webview: ExtHostWebview
) {
super();
this._handle = handle;
this._proxy = proxy;
this._viewType = viewType;
@@ -135,16 +142,12 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
this._onDisposeEmitter.fire();
this._proxy.$disposeWebview(this._handle);
this._webview.dispose();
this._onDisposeEmitter.dispose();
this._onDidChangeViewStateEmitter.dispose();
super.dispose();
}
get webview() {
@@ -223,18 +226,6 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor {
this._visible = value;
}
private readonly _onWillSave = new Emitter<{ waitUntil: (thenable: Thenable<boolean>) => void }>();
public readonly onWillSave = this._onWillSave.event;
async _save(): Promise<boolean> {
const waitingOn: Thenable<boolean>[] = [];
this._onWillSave.fire({
waitUntil: (thenable: Thenable<boolean>): void => { waitingOn.push(thenable); },
});
const result = await Promise.all(waitingOn);
return result.every(x => x);
}
public postMessage(message: any): Promise<boolean> {
this.assertNotDisposed();
return this._proxy.$postMessage(this._handle, message);
@@ -248,6 +239,32 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor {
});
}
_setCapabilities(capabilities: vscode.WebviewEditorCapabilities) {
this._capabilities = capabilities;
if (capabilities.editingCapability) {
this._register(capabilities.editingCapability.onEdit(edit => {
this._proxy.$onEdit(this._handle, edit);
}));
}
}
_undoEdits(edits: readonly any[]): void {
assertIsDefined(this._capabilities).editingCapability?.undoEdits(edits);
}
_redoEdits(edits: readonly any[]): void {
assertIsDefined(this._capabilities).editingCapability?.applyEdits(edits);
}
async _onSave(): Promise<void> {
await assertIsDefined(this._capabilities).editingCapability?.save();
}
async _onSaveAs(resource: vscode.Uri, targetResource: vscode.Uri): Promise<void> {
await assertIsDefined(this._capabilities).editingCapability?.saveAs(resource, targetResource);
}
private assertNotDisposed() {
if (this._isDisposed) {
throw new Error('Webview is disposed');
@@ -270,6 +287,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
mainContext: IMainContext,
private readonly initData: WebviewInitData,
private readonly workspace: IExtHostWorkspace | undefined,
private readonly _logService: ILogService,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews);
}
@@ -290,7 +308,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
const handle = ExtHostWebviews.newHandle();
this._proxy.$createWebviewPanel({ id: extension.identifier, location: extension.extensionLocation }, handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options));
const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension);
const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension, this._logService);
const panel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, viewColumn, options, webview);
this._webviewPanels.set(handle, panel);
return panel;
@@ -308,7 +326,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
this._serializers.set(viewType, { serializer, extension });
this._proxy.$registerSerializer(viewType);
return new Disposable(() => {
return new VSCodeDisposable(() => {
this._serializers.delete(viewType);
this._proxy.$unregisterSerializer(viewType);
});
@@ -327,7 +345,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
this._editorProviders.set(viewType, { extension, provider, });
this._proxy.$registerEditorProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType, options || {});
return new Disposable(() => {
return new VSCodeDisposable(() => {
this._editorProviders.delete(viewType);
this._proxy.$unregisterEditorProvider(viewType);
});
@@ -347,7 +365,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
_handle: WebviewPanelHandle,
extensionId: string
): void {
console.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`);
this._logService.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`);
}
public $onDidChangeWebviewPanelViewStates(newStates: WebviewPanelViewStateData): void {
@@ -385,16 +403,15 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
}
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void> {
async $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void> {
const panel = this.getWebviewPanel(handle);
if (panel) {
panel.dispose();
this._webviewPanels.delete(handle);
}
return Promise.resolve(undefined);
}
$deserializeWebviewPanel(
async $deserializeWebviewPanel(
webviewHandle: WebviewPanelHandle,
viewType: string,
title: string,
@@ -404,22 +421,18 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
): Promise<void> {
const entry = this._serializers.get(viewType);
if (!entry) {
return Promise.reject(new Error(`No serializer found for '${viewType}'`));
throw new Error(`No serializer found for '${viewType}'`);
}
const { serializer, extension } = entry;
const webview = new ExtHostWebview(webviewHandle, this._proxy, options, this.initData, this.workspace, extension);
const webview = new ExtHostWebview(webviewHandle, this._proxy, options, this.initData, this.workspace, extension, this._logService);
const revivedPanel = new ExtHostWebviewEditor(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
this._webviewPanels.set(webviewHandle, revivedPanel);
return Promise.resolve(serializer.deserializeWebviewPanel(revivedPanel, state));
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
return this._webviewPanels.get(handle);
await serializer.deserializeWebviewPanel(revivedPanel, state);
}
async $resolveWebviewEditor(
resource: UriComponents,
input: { resource: UriComponents, edits: readonly any[] },
handle: WebviewPanelHandle,
viewType: string,
title: string,
@@ -430,19 +443,42 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
if (!entry) {
return Promise.reject(new Error(`No provider found for '${viewType}'`));
}
const { provider, extension } = entry;
const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension);
const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension, this._logService);
const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
this._webviewPanels.set(handle, revivedPanel);
return Promise.resolve(provider.resolveWebviewEditor(URI.revive(resource), revivedPanel));
const capabilities = await provider.resolveWebviewEditor({ resource: URI.revive(input.resource) }, revivedPanel);
revivedPanel._setCapabilities(capabilities);
// TODO: the first set of edits should likely be passed when resolving
if (input.edits.length) {
revivedPanel._redoEdits(input.edits);
}
}
async $save(handle: WebviewPanelHandle): Promise<boolean> {
$undoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void {
const panel = this.getWebviewPanel(handle);
if (panel) {
return panel._save();
}
return false;
panel?._undoEdits(edits);
}
$applyEdits(handle: WebviewPanelHandle, edits: readonly any[]): void {
const panel = this.getWebviewPanel(handle);
panel?._redoEdits(edits);
}
async $onSave(handle: WebviewPanelHandle): Promise<void> {
const panel = this.getWebviewPanel(handle);
return panel?._onSave();
}
async $onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise<void> {
const panel = this.getWebviewPanel(handle);
return panel?._onSaveAs(URI.revive(resource), URI.revive(targetResource));
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
return this._webviewPanels.get(handle);
}
}
@@ -462,7 +498,7 @@ function getDefaultLocalResourceRoots(
workspace: IExtHostWorkspace | undefined,
): URI[] {
return [
...(workspace && workspace.getWorkspaceFolders() || []).map(x => x.uri),
...(workspace?.getWorkspaceFolders() || []).map(x => x.uri),
extension.extensionLocation,
];
}

View File

@@ -39,7 +39,9 @@ export class ExtHostWindow implements ExtHostWindowShape {
}
openUri(stringOrUri: string | URI, options: IOpenUriOptions): Promise<boolean> {
let uriAsString: string | undefined;
if (typeof stringOrUri === 'string') {
uriAsString = stringOrUri;
try {
stringOrUri = URI.parse(stringOrUri);
} catch (e) {
@@ -51,7 +53,7 @@ export class ExtHostWindow implements ExtHostWindowShape {
} else if (stringOrUri.scheme === Schemas.command) {
return Promise.reject(`Invalid scheme '${stringOrUri.scheme}'`);
}
return this._proxy.$openUri(stringOrUri, options);
return this._proxy.$openUri(stringOrUri, uriAsString, options);
}
async asExternalUri(uri: URI, options: IOpenUriOptions): Promise<URI> {

View File

@@ -3,31 +3,30 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join } from 'vs/base/common/path';
import { delta as arrayDelta, mapArrayOrNot } from 'vs/base/common/arrays';
import { Barrier } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { Schemas } from 'vs/base/common/network';
import { Counter } from 'vs/base/common/numbers';
import { basenameOrAuthority, dirname, isEqual, relativePath, basename } from 'vs/base/common/resources';
import { basename, basenameOrAuthority, dirname, isEqual, relativePath } from 'vs/base/common/resources';
import { compare } from 'vs/base/common/strings';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { Severity } from 'vs/platform/notification/common/notification';
import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search';
import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IWorkspaceData, MainThreadMessageServiceShape, MainThreadWorkspaceShape, MainContext } from './extHost.protocol';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Barrier } from 'vs/base/common/async';
import { Schemas } from 'vs/base/common/network';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ExtHostWorkspaceShape, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
export interface IExtHostWorkspaceProvider {
getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise<vscode.WorkspaceFolder | undefined>;
@@ -419,19 +418,6 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
findFiles(include: string | RelativePattern | undefined, exclude: vscode.GlobPattern | null | undefined, maxResults: number | undefined, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.Uri[]> {
this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId.value}, entryPoint: findFiles`);
let includePattern: string | undefined;
let includeFolder: URI | undefined;
if (include) {
if (typeof include === 'string') {
includePattern = include;
} else {
includePattern = include.pattern;
// include.base must be an absolute path
includeFolder = include.baseFolder || URI.file(include.base);
}
}
let excludePatternOrDisregardExcludes: string | false | undefined = undefined;
if (exclude === null) {
excludePatternOrDisregardExcludes = false;
@@ -447,9 +433,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
return Promise.resolve([]);
}
const { includePattern, folder } = parseSearchInclude(include);
return this._proxy.$startFileSearch(
withUndefinedAsNull(includePattern),
withUndefinedAsNull(includeFolder),
withUndefinedAsNull(folder),
withUndefinedAsNull(excludePatternOrDisregardExcludes),
withUndefinedAsNull(maxResults),
token
@@ -457,19 +444,11 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
.then(data => Array.isArray(data) ? data.map(d => URI.revive(d)) : []);
}
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete | undefined> {
async findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete> {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId.value}, entryPoint: findTextInFiles`);
const requestId = this._requestIdProvider.getNext();
const globPatternToString = (pattern: vscode.GlobPattern | string) => {
if (typeof pattern === 'string') {
return pattern;
}
return join(pattern.base, pattern.pattern);
};
const previewOptions: vscode.TextSearchPreviewOptions = typeof options.previewOptions === 'undefined' ?
{
matchLines: 100,
@@ -477,6 +456,19 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
} :
options.previewOptions;
let includePattern: string | undefined;
let folder: URI | undefined;
if (options.include) {
if (typeof options.include === 'string') {
includePattern = options.include;
} else {
includePattern = options.include.pattern;
folder = (options.include as RelativePattern).baseFolder || URI.file(options.include.base);
}
}
const excludePattern = (typeof options.exclude === 'string') ? options.exclude :
options.exclude ? options.exclude.pattern : undefined;
const queryOptions: ITextQueryBuilderOptions = {
ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined,
disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined,
@@ -488,8 +480,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
afterContext: options.afterContext,
beforeContext: options.beforeContext,
includePattern: options.include && globPatternToString(options.include),
excludePattern: options.exclude ? globPatternToString(options.exclude) : undefined
includePattern: includePattern,
excludePattern: excludePattern
};
const isCanceled = false;
@@ -525,16 +517,22 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
};
if (token.isCancellationRequested) {
return Promise.resolve(undefined);
return {};
}
return this._proxy.$startTextSearch(query, queryOptions, requestId, token).then(result => {
try {
const result = await this._proxy.$startTextSearch(
query,
withUndefinedAsNull(folder),
queryOptions,
requestId,
token);
delete this._activeSearchCallbacks[requestId];
return result;
}, err => {
return result || {};
} catch (err) {
delete this._activeSearchCallbacks[requestId];
return Promise.reject(err);
});
throw err;
}
}
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void {
@@ -554,3 +552,23 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
export const IExtHostWorkspace = createDecorator<IExtHostWorkspace>('IExtHostWorkspace');
export interface IExtHostWorkspace extends ExtHostWorkspace, ExtHostWorkspaceShape, IExtHostWorkspaceProvider { }
function parseSearchInclude(include: RelativePattern | string | undefined): { includePattern?: string, folder?: URI } {
let includePattern: string | undefined;
let includeFolder: URI | undefined;
if (include) {
if (typeof include === 'string') {
includePattern = include;
} else {
includePattern = include.pattern;
// include.base must be an absolute path
includeFolder = include.baseFolder || URI.file(include.base);
}
}
return {
includePattern: includePattern,
folder: includeFolder
};
}

View File

@@ -13,6 +13,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { MenuId, MenuRegistry, ILocalizedString, IMenuItem } from 'vs/platform/actions/common/actions';
import { URI } from 'vs/base/common/uri';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
namespace schema {
@@ -352,10 +353,14 @@ commandsExtensionPoint.setHandler(extensions => {
const { icon, enablement, category, title, command } = userFriendlyCommand;
let absoluteIcon: { dark: URI; light?: URI; } | undefined;
let absoluteIcon: { dark: URI; light?: URI; } | ThemeIcon | undefined;
if (icon) {
if (typeof icon === 'string') {
absoluteIcon = { dark: resources.joinPath(extension.description.extensionLocation, icon) };
if (extension.description.enableProposedApi) {
absoluteIcon = ThemeIcon.fromString(icon) || { dark: resources.joinPath(extension.description.extensionLocation, icon) };
} else {
absoluteIcon = { dark: resources.joinPath(extension.description.extensionLocation, icon) };
}
} else {
absoluteIcon = {
dark: resources.joinPath(extension.description.extensionLocation, icon.dark),
@@ -372,7 +377,7 @@ commandsExtensionPoint.setHandler(extensions => {
title,
category,
precondition: ContextKeyExpr.deserialize(enablement),
iconLocation: absoluteIcon
icon: absoluteIcon
});
_commandRegistrations.add(registration);
}

View File

@@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
export interface IFullSemanticTokensDto {
id: number;
type: 'full';
data: Uint32Array;
}
export interface IDeltaSemanticTokensDto {
id: number;
type: 'delta';
deltas: { start: number; deleteCount: number; data?: Uint32Array; }[];
}
export type ISemanticTokensDto = IFullSemanticTokensDto | IDeltaSemanticTokensDto;
const enum EncodedSemanticTokensType {
Full = 1,
Delta = 2
}
export function encodeSemanticTokensDto(semanticTokens: ISemanticTokensDto): VSBuffer {
const buff = VSBuffer.alloc(encodedSize2(semanticTokens));
let offset = 0;
buff.writeUInt32BE(semanticTokens.id, offset); offset += 4;
if (semanticTokens.type === 'full') {
buff.writeUInt8(EncodedSemanticTokensType.Full, offset); offset += 1;
buff.writeUInt32BE(semanticTokens.data.length, offset); offset += 4;
for (const uint of semanticTokens.data) {
buff.writeUInt32BE(uint, offset); offset += 4;
}
} else {
buff.writeUInt8(EncodedSemanticTokensType.Delta, offset); offset += 1;
buff.writeUInt32BE(semanticTokens.deltas.length, offset); offset += 4;
for (const delta of semanticTokens.deltas) {
buff.writeUInt32BE(delta.start, offset); offset += 4;
buff.writeUInt32BE(delta.deleteCount, offset); offset += 4;
if (delta.data) {
buff.writeUInt32BE(delta.data.length, offset); offset += 4;
for (const uint of delta.data) {
buff.writeUInt32BE(uint, offset); offset += 4;
}
} else {
buff.writeUInt32BE(0, offset); offset += 4;
}
}
}
return buff;
}
function encodedSize2(semanticTokens: ISemanticTokensDto): number {
let result = 0;
result += 4; // id
result += 1; // type
if (semanticTokens.type === 'full') {
result += 4; // data length
result += semanticTokens.data.byteLength;
} else {
result += 4; // delta count
for (const delta of semanticTokens.deltas) {
result += 4; // start
result += 4; // deleteCount
result += 4; // data length
if (delta.data) {
result += delta.data.byteLength;
}
}
}
return result;
}
export function decodeSemanticTokensDto(buff: VSBuffer): ISemanticTokensDto {
let offset = 0;
const id = buff.readUInt32BE(offset); offset += 4;
const type: EncodedSemanticTokensType = buff.readUInt8(offset); offset += 1;
if (type === EncodedSemanticTokensType.Full) {
const length = buff.readUInt32BE(offset); offset += 4;
const data = new Uint32Array(length);
for (let j = 0; j < length; j++) {
data[j] = buff.readUInt32BE(offset); offset += 4;
}
return {
id: id,
type: 'full',
data: data
};
}
const deltaCount = buff.readUInt32BE(offset); offset += 4;
let deltas: { start: number; deleteCount: number; data?: Uint32Array; }[] = [];
for (let i = 0; i < deltaCount; i++) {
const start = buff.readUInt32BE(offset); offset += 4;
const deleteCount = buff.readUInt32BE(offset); offset += 4;
const length = buff.readUInt32BE(offset); offset += 4;
let data: Uint32Array | undefined;
if (length > 0) {
data = new Uint32Array(length);
for (let j = 0; j < length; j++) {
data[j] = buff.readUInt32BE(offset); offset += 4;
}
}
deltas[i] = { start, deleteCount, data };
}
return {
id: id,
type: 'delta',
deltas: deltas
};
}

View File

@@ -18,7 +18,7 @@ import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminal
// import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
// import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch';
import { NativeExtHostSearch } from 'vs/workbench/api/node/extHostSearch';
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
@@ -38,7 +38,7 @@ registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors);
registerSingleton(IExtHostTerminalService, ExtHostTerminalService);
// registerSingleton(IExtHostTask, ExtHostTask); {{SQL CABON EDIT}} disable exthost tasks
// registerSingleton(IExtHostDebugService, ExtHostDebugService); {{SQL CARBON EDIT}} remove debug service
registerSingleton(IExtHostSearch, ExtHostSearch);
registerSingleton(IExtHostSearch, NativeExtHostSearch);
registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
registerSingleton(IExtHostExtensionService, ExtHostExtensionService);
registerSingleton(IExtHostStorage, ExtHostStorage);

View File

@@ -101,9 +101,6 @@ export class CLIServer {
for (const s of folderURIs) {
try {
urisToOpen.push({ folderUri: URI.parse(s) });
if (!addMode && !forceReuseWindow) {
forceNewWindow = true;
}
} catch (e) {
// ignore
}
@@ -114,9 +111,6 @@ export class CLIServer {
try {
if (hasWorkspaceFileExtension(s)) {
urisToOpen.push({ workspaceUri: URI.parse(s) });
if (!forceReuseWindow) {
forceNewWindow = true;
}
} else {
urisToOpen.push({ fileUri: URI.parse(s) });
}
@@ -127,7 +121,8 @@ export class CLIServer {
}
if (urisToOpen.length) {
const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined;
const windowOpenArgs: INativeOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, waitMarkerFileURI };
const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode;
const windowOpenArgs: INativeOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI };
this._commands.executeCommand('_files.windowOpen', urisToOpen, windowOpenArgs);
}
res.writeHead(200);

File diff suppressed because it is too large Load Diff

View File

@@ -72,6 +72,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
return nativeProcessSend.apply(process, args);
}
mainThreadConsole.$logExtensionHostMessage(args[0]);
return false;
};
}

View File

@@ -3,47 +3,37 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { ILogService } from 'vs/platform/log/common/log';
import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery, isSerializedFileMatch, ISerializedSearchProgressItem } from 'vs/workbench/services/search/common/search';
import { FileSearchManager } from 'vs/workbench/services/search/node/fileSearchManager';
import { IFileQuery, IRawFileQuery, ISearchCompleteStats, isSerializedFileMatch, ISerializedSearchProgressItem, ITextQuery } from 'vs/workbench/services/search/common/search';
import { SearchService } from 'vs/workbench/services/search/node/rawSearchService';
import { RipgrepSearchProvider } from 'vs/workbench/services/search/node/ripgrepSearchProvider';
import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUtils';
import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
import * as vscode from 'vscode';
import { ExtHostSearchShape, MainContext, MainThreadSearchShape } from '../common/extHost.protocol';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { ExtHostSearch, reviveQuery } from 'vs/workbench/api/common/extHostSearch';
import { Schemas } from 'vs/base/common/network';
import { NativeTextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
import { TextSearchManager } from 'vs/workbench/services/search/common/textSearchManager';
export class ExtHostSearch implements ExtHostSearchShape {
export class NativeExtHostSearch extends ExtHostSearch {
private readonly _proxy: MainThreadSearchShape;
private readonly _textSearchProvider = new Map<number, vscode.TextSearchProvider>();
private readonly _textSearchUsedSchemes = new Set<string>();
private readonly _fileSearchProvider = new Map<number, vscode.FileSearchProvider>();
private readonly _fileSearchUsedSchemes = new Set<string>();
private _handlePool: number = 0;
protected _pfs: typeof pfs = pfs; // allow extending for tests
private _internalFileSearchHandle: number = -1;
private _internalFileSearchProvider: SearchService | null = null;
private _fileSearchManager: FileSearchManager;
protected _pfs: typeof pfs = pfs; // allow extending for tests
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
@IURITransformerService private _uriTransformer: IURITransformerService,
@ILogService private _logService: ILogService,
@IURITransformerService _uriTransformer: IURITransformerService,
@ILogService _logService: ILogService,
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadSearch);
this._fileSearchManager = new FileSearchManager();
super(extHostRpc, _uriTransformer, _logService);
if (initData.remote.isRemote && initData.remote.authority) {
this._registerEHSearchProviders();
@@ -52,47 +42,11 @@ export class ExtHostSearch implements ExtHostSearchShape {
private _registerEHSearchProviders(): void {
const outputChannel = new OutputChannel(this._logService);
this.registerTextSearchProvider('file', new RipgrepSearchProvider(outputChannel));
this.registerInternalFileSearchProvider('file', new SearchService());
this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel));
this.registerInternalFileSearchProvider(Schemas.file, new SearchService());
}
private _transformScheme(scheme: string): string {
return this._uriTransformer.transformOutgoingScheme(scheme);
}
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable {
if (this._textSearchUsedSchemes.has(scheme)) {
throw new Error(`a text search provider for the scheme '${scheme}' is already registered`);
}
this._textSearchUsedSchemes.add(scheme);
const handle = this._handlePool++;
this._textSearchProvider.set(handle, provider);
this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._textSearchUsedSchemes.delete(scheme);
this._textSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable {
if (this._fileSearchUsedSchemes.has(scheme)) {
throw new Error(`a file search provider for the scheme '${scheme}' is already registered`);
}
this._fileSearchUsedSchemes.add(scheme);
const handle = this._handlePool++;
this._fileSearchProvider.set(handle, provider);
this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._fileSearchUsedSchemes.delete(scheme);
this._fileSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable {
private registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable {
const handle = this._handlePool++;
this._internalFileSearchProvider = provider;
this._internalFileSearchHandle = handle;
@@ -103,23 +57,16 @@ export class ExtHostSearch implements ExtHostSearchShape {
});
}
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
const query = reviveQuery(rawQuery);
if (handle === this._internalFileSearchHandle) {
return this.doInternalFileSearch(handle, session, query, token);
} else {
const provider = this._fileSearchProvider.get(handle);
if (provider) {
return this._fileSearchManager.fileSearch(query, provider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}, token);
} else {
throw new Error('unknown provider: ' + handle);
}
}
return super.$provideFileSearchResults(handle, session, rawQuery, token);
}
private doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
private doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
const onResult = (ev: ISerializedSearchProgressItem) => {
if (isSerializedFileMatch(ev)) {
ev = [ev];
@@ -147,37 +94,11 @@ export class ExtHostSearch implements ExtHostSearchShape {
this._internalFileSearchProvider.clearCache(cacheKey);
}
this._fileSearchManager.clearCache(cacheKey);
return Promise.resolve(undefined);
return super.$clearCache(cacheKey);
}
$provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
const provider = this._textSearchProvider.get(handle);
if (!provider || !provider.provideTextSearchResults) {
throw new Error(`Unknown provider ${handle}`);
}
const query = reviveQuery(rawQuery);
const engine = new TextSearchManager(query, provider, this._pfs);
return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token);
protected createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager {
return new NativeTextSearchManager(query, provider);
}
}
function reviveQuery<U extends IRawQuery>(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery {
return {
...<any>rawQuery, // TODO
...{
folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery),
extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components))
}
};
}
function reviveFolderQuery(rawFolderQuery: IFolderQuery<UriComponents>): IFolderQuery<URI> {
return {
...rawFolderQuery,
folder: URI.revive(rawFolderQuery.folder)
};
}

View File

@@ -11,6 +11,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { withNullAsUndefined } from 'vs/base/common/types';
import { ILogService } from 'vs/platform/log/common/log';
export class ExtensionStoragePaths implements IExtensionStoragePaths {
@@ -22,7 +23,10 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
readonly whenReady: Promise<string | undefined>;
private _value?: string;
constructor(@IExtHostInitDataService initData: IExtHostInitDataService) {
constructor(
@IExtHostInitDataService initData: IExtHostInitDataService,
@ILogService private readonly _logService: ILogService,
) {
this._workspace = withNullAsUndefined(initData.workspace);
this._environment = initData.environment;
this.whenReady = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
@@ -69,7 +73,7 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
return storagePath;
} catch (e) {
console.error(e);
this._logService.error(e);
return undefined;
}
}

View File

@@ -5,24 +5,28 @@
// import * as path from 'vs/base/common/path';
import { /*URI,*/ UriComponents } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
// import { win32 } from 'vs/base/node/processes';
import * as types from 'vs/workbench/api/common/extHostTypes';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import * as vscode from 'vscode';
import * as tasks from '../common/shared/tasks';
// import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
import * as Objects from 'vs/base/common/objects';
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
// import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecutionDTO, HandlerData } from 'vs/workbench/api/common/extHostTask';
import { Schemas } from 'vs/base/common/network';
import { ILogService } from 'vs/platform/log/common/log';
import { IProcessEnvironment } from 'vs/base/common/platform';
export class ExtHostTask extends ExtHostTaskBase {
private _variableResolver: ExtHostVariableResolverService | undefined;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@@ -30,9 +34,10 @@ export class ExtHostTask extends ExtHostTaskBase {
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
@IExtHostConfiguration configurationService: IExtHostConfiguration,
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
@ILogService logService: ILogService
) {
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService);
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService);
if (initData.remote.isRemote && initData.remote.authority) {
this.registerTaskSystem(Schemas.vscodeRemote, {
scheme: Schemas.vscodeRemote,
@@ -69,7 +74,7 @@ export class ExtHostTask extends ExtHostTaskBase {
if (value) {
for (let task of value) {
if (!task.definition || !validTypes[task.definition.type]) {
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
this._logService.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
}
const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension);
@@ -95,9 +100,42 @@ export class ExtHostTask extends ExtHostTaskBase {
return resolvedTaskDTO;
}
private async getVariableResolver(workspaceFolders: vscode.WorkspaceFolder[]): Promise<ExtHostVariableResolverService> {
if (this._variableResolver === undefined) {
const configProvider = await this._configurationService.getConfigProvider();
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, process.env as IProcessEnvironment);
}
return this._variableResolver;
}
protected async resolveDefinition(uri: number | UriComponents | undefined, definition: vscode.TaskDefinition | undefined): Promise<vscode.TaskDefinition | undefined> {
if (!uri || (typeof uri === 'number') || !definition) {
return definition;
}
const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(URI.revive(uri));
const workspaceFolders = await this._workspaceProvider.getWorkspaceFolders2();
if (!workspaceFolders || !workspaceFolder) {
return definition;
}
const resolver = await this.getVariableResolver(workspaceFolders);
const ws: IWorkspaceFolder = {
uri: workspaceFolder.uri,
name: workspaceFolder.name,
index: workspaceFolder.index,
toResource: () => {
throw new Error('Not implemented');
}
};
const resolvedDefinition = Objects.deepClone(definition);
for (const key in resolvedDefinition) {
resolvedDefinition[key] = resolver.resolve(ws, resolvedDefinition[key]);
}
return resolvedDefinition;
}
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
/*const configProvider = await this._configurationService.getConfigProvider();
const uri: URI = URI.revive(uriComponents);
/*const uri: URI = URI.revive(uriComponents);
const result = {
process: <unknown>undefined as string,
variables: Object.create(null)
@@ -107,7 +145,7 @@ export class ExtHostTask extends ExtHostTaskBase {
if (!workspaceFolders || !workspaceFolder) {
throw new Error('Unexpected: Tasks can only be run in a workspace folder');
}
const resolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider);
const resolver = await this.getVariableResolver(workspaceFolders);
const ws: IWorkspaceFolder = {
uri: workspaceFolder.uri,
name: workspaceFolder.name,

View File

@@ -16,7 +16,7 @@ import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/t
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { getSystemShell, detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal';
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
@@ -45,14 +45,14 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, name);
const terminal = new ExtHostTerminal(this._proxy, { name, shellPath, shellArgs }, name);
terminal.create(shellPath, shellArgs);
this._terminals.push(terminal);
return terminal;
}
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, options.name);
const terminal = new ExtHostTerminal(this._proxy, options, options.name);
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser);
this._terminals.push(terminal);
return terminal;
@@ -120,7 +120,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
private async _updateVariableResolver(): Promise<void> {
const configProvider = await this._extHostConfiguration.getConfigProvider();
const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2();
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider);
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider, process.env as platform.IProcessEnvironment);
}
public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {