Merge branch 'master' into joh/completionsDeprecated

This commit is contained in:
Johannes Rieken
2019-08-21 10:13:39 +02:00
committed by GitHub
1052 changed files with 27430 additions and 16142 deletions

View File

@@ -8,7 +8,7 @@ import * as modes from 'vs/editor/common/modes';
import { MainContext, MainThreadEditorInsetsShape, IExtHostContext, ExtHostEditorInsetsShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/common/webview';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@@ -21,7 +21,7 @@ class EditorWebviewZone implements IViewZone {
readonly afterColumn: number;
readonly heightInLines: number;
private _id: number;
private _id?: string;
// suppressMouseDown?: boolean | undefined;
// heightInPx?: number | undefined;
// minWidthInPx?: number | undefined;
@@ -46,7 +46,7 @@ class EditorWebviewZone implements IViewZone {
}
dispose(): void {
this.editor.changeViewZones(accessor => accessor.removeZone(this._id));
this.editor.changeViewZones(accessor => this._id && accessor.removeZone(this._id));
}
}
@@ -90,7 +90,6 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
const webview = this._webviewService.createWebview('' + handle, {
enableFindWidget: false,
allowSvgs: false,
extension: { id: extensionId, location: URI.revive(extensionLocation) }
}, {
allowScripts: options.enableScripts,

View File

@@ -15,7 +15,6 @@ export class MainThreadCommands implements MainThreadCommandsShape {
private readonly _commandRegistrations = new Map<string, IDisposable>();
private readonly _generateCommandsDocumentationRegistration: IDisposable;
private readonly _proxy: ExtHostCommandsShape;
private _onDidExecuteCommandListener?: IDisposable;
constructor(
extHostContext: IExtHostContext,
@@ -78,19 +77,6 @@ export class MainThreadCommands implements MainThreadCommandsShape {
return this._commandService.executeCommand<T>(id, ...args);
}
$registerCommandListener() {
if (!this._onDidExecuteCommandListener) {
this._onDidExecuteCommandListener = this._commandService.onDidExecuteCommand(command => this._proxy.$handleDidExecuteCommand(command));
}
}
$unregisterCommandListener() {
if (this._onDidExecuteCommandListener) {
this._onDidExecuteCommandListener.dispose();
this._onDidExecuteCommandListener = undefined;
}
}
$getCommands(): Promise<string[]> {
return Promise.resolve([...CommandsRegistry.getCommands().keys()]);
}

View File

@@ -16,9 +16,10 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel';
import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
import { CommentsPanel, COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsPanel';
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 { COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer';
export class MainThreadCommentThread implements modes.CommentThread {
@@ -35,13 +36,13 @@ export class MainThreadCommentThread implements modes.CommentThread {
private _onDidChangeInput = new Emitter<modes.CommentInput | undefined>();
get onDidChangeInput(): Event<modes.CommentInput | undefined> { return this._onDidChangeInput.event; }
private _label: string;
private _label: string | undefined;
get label(): string {
get label(): string | undefined {
return this._label;
}
set label(label: string) {
set label(label: string | undefined) {
this._label = label;
this._onDidChangeLabel.fire(this._label);
}
@@ -56,8 +57,8 @@ export class MainThreadCommentThread implements modes.CommentThread {
this._contextValue = context;
}
private _onDidChangeLabel = new Emitter<string>();
readonly onDidChangeLabel: Event<string> = this._onDidChangeLabel.event;
private _onDidChangeLabel = new Emitter<string | undefined>();
readonly onDidChangeLabel: Event<string | undefined> = this._onDidChangeLabel.event;
private _comments: modes.Comment[] | undefined;
@@ -348,7 +349,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
private _activeCommentThread?: MainThreadCommentThread;
private readonly _activeCommentThreadDisposables = this._register(new DisposableStore());
private _openPanelListener: IDisposable | null;
private _openPanelListener: IDisposable | null = null;
constructor(

View File

@@ -5,10 +5,10 @@
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI as uri } from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import {
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto
} from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import severity from 'vs/base/common/severity';
@@ -110,15 +110,16 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
// send all breakpoints
const bps = this.debugService.getModel().getBreakpoints();
const fbps = this.debugService.getModel().getFunctionBreakpoints();
const dbps = this.debugService.getModel().getDataBreakpoints();
if (bps.length > 0 || fbps.length > 0) {
this._proxy.$acceptBreakpointsDelta({
added: this.convertToDto(bps).concat(this.convertToDto(fbps))
added: this.convertToDto(bps).concat(this.convertToDto(fbps)).concat(this.convertToDto(dbps))
});
}
}
}
public $registerBreakpoints(DTOs: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto>): Promise<void> {
public $registerBreakpoints(DTOs: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto>): Promise<void> {
for (let dto of DTOs) {
if (dto.type === 'sourceMulti') {
@@ -136,14 +137,17 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps, 'extension');
} else if (dto.type === 'function') {
this.debugService.addFunctionBreakpoint(dto.functionName, dto.id);
} else if (dto.type === 'data') {
this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist);
}
}
return Promise.resolve();
}
public $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise<void> {
public $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[], dataBreakpointIds: string[]): Promise<void> {
breakpointIds.forEach(id => this.debugService.removeBreakpoints(id));
functionBreakpointIds.forEach(id => this.debugService.removeFunctionBreakpoints(id));
dataBreakpointIds.forEach(id => this.debugService.removeDataBreakpoints(id));
return Promise.resolve();
}
@@ -153,13 +157,13 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
type: debugType
};
if (hasProvide) {
provider.provideDebugConfigurations = (folder) => {
return this._proxy.$provideDebugConfigurations(handle, folder);
provider.provideDebugConfigurations = (folder, token) => {
return this._proxy.$provideDebugConfigurations(handle, folder, token);
};
}
if (hasResolve) {
provider.resolveDebugConfiguration = (folder, config) => {
return this._proxy.$resolveDebugConfiguration(handle, folder, config);
provider.resolveDebugConfiguration = (folder, config, token) => {
return this._proxy.$resolveDebugConfiguration(handle, folder, config, token);
};
}
if (hasProvideDebugAdapter) {
@@ -294,7 +298,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
return undefined;
}
private convertToDto(bps: (ReadonlyArray<IBreakpoint | IFunctionBreakpoint>)): Array<ISourceBreakpointDto | IFunctionBreakpointDto> {
private convertToDto(bps: (ReadonlyArray<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint>)): Array<ISourceBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto> {
return bps.map(bp => {
if ('name' in bp) {
const fbp = <IFunctionBreakpoint>bp;
@@ -307,6 +311,19 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
logMessage: fbp.logMessage,
functionName: fbp.name
};
} else if ('dataId' in bp) {
const dbp = <IDataBreakpoint>bp;
return <IDataBreakpointDto>{
type: 'data',
id: dbp.getId(),
dataId: dbp.dataId,
enabled: dbp.enabled,
condition: dbp.condition,
hitCondition: dbp.hitCondition,
logMessage: dbp.logMessage,
label: dbp.label,
canPersist: dbp.canPersist
};
} else {
const sbp = <IBreakpoint>bp;
return <ISourceBreakpointDto>{

View File

@@ -5,24 +5,43 @@
import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers';
import { URI, UriComponents } from 'vs/base/common/uri';
import { MainThreadDiagnosticsShape, MainContext, IExtHostContext } from '../common/extHost.protocol';
import { MainThreadDiagnosticsShape, MainContext, IExtHostContext, ExtHostDiagnosticsShape, ExtHostContext } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { IDisposable } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(MainContext.MainThreadDiagnostics)
export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
private readonly _activeOwners = new Set<string>();
private readonly _proxy: ExtHostDiagnosticsShape;
private readonly _markerService: IMarkerService;
private readonly _markerListener: IDisposable;
constructor(
extHostContext: IExtHostContext,
@IMarkerService markerService: IMarkerService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDiagnostics);
this._markerService = markerService;
this._markerListener = this._markerService.onMarkerChanged(this._forwardMarkers, this);
}
dispose(): void {
this._markerListener.dispose();
this._activeOwners.forEach(owner => this._markerService.changeAll(owner, []));
this._activeOwners.clear();
}
private _forwardMarkers(resources: URI[]): void {
const data: [UriComponents, IMarkerData[]][] = [];
for (const resource of resources) {
data.push([
resource,
this._markerService.read({ resource }).filter(marker => !this._activeOwners.has(marker.owner))
]);
}
this._proxy.$acceptMarkersChange(data);
}
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {

View File

@@ -109,7 +109,7 @@ class DocumentAndEditorStateDelta {
class DocumentAndEditorState {
static compute(before: DocumentAndEditorState, after: DocumentAndEditorState): DocumentAndEditorStateDelta {
static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): DocumentAndEditorStateDelta {
if (!before) {
return new DocumentAndEditorStateDelta(
[], values(after.documents),
@@ -146,7 +146,7 @@ class MainThreadDocumentAndEditorStateComputer {
private readonly _toDispose = new DisposableStore();
private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
private _currentState: DocumentAndEditorState;
private _currentState?: DocumentAndEditorState;
private _activeEditorOrder: ActiveEditorOrder = ActiveEditorOrder.Editor;
constructor(

View File

@@ -178,7 +178,7 @@ export class MainThreadTextEditor {
private readonly _focusTracker: IFocusTracker;
private readonly _codeEditorListeners = new DisposableStore();
private _properties: MainThreadTextEditorProperties;
private _properties: MainThreadTextEditorProperties | null;
private readonly _onPropertiesChanged: Emitter<IEditorPropertiesChangeData>;
constructor(
@@ -191,6 +191,7 @@ export class MainThreadTextEditor {
this._id = id;
this._model = model;
this._codeEditor = null;
this._properties = null;
this._focusTracker = focusTracker;
this._modelService = modelService;
@@ -289,7 +290,7 @@ export class MainThreadTextEditor {
}
public getProperties(): MainThreadTextEditorProperties {
return this._properties;
return this._properties!;
}
public get onPropertiesChanged(): Event<IEditorPropertiesChangeData> {
@@ -304,7 +305,7 @@ export class MainThreadTextEditor {
const newSelections = selections.map(Selection.liftSelection);
this._setProperties(
new MainThreadTextEditorProperties(newSelections, this._properties.options, this._properties.visibleRanges),
new MainThreadTextEditorProperties(newSelections, this._properties!.options, this._properties!.visibleRanges),
null
);
}

View File

@@ -20,7 +20,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors';
import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor';
import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, IWorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol';
import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -212,7 +212,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
return Promise.resolve(editor.applyEdits(modelVersionId, edits, opts));
}
$tryApplyWorkspaceEdit(dto: WorkspaceEditDto): Promise<boolean> {
$tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise<boolean> {
const { edits } = reviveWorkspaceEditDto(dto);
return this._bulkEditService.apply({ edits }, undefined).then(() => true, err => false);
}

View File

@@ -5,49 +5,33 @@
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials';
@extHostNamedCustomer(MainContext.MainThreadKeytar)
export class MainThreadKeytar implements MainThreadKeytarShape {
private readonly _credentialsService?: ICredentialsService;
constructor(
_extHostContext: IExtHostContext,
@optional(ICredentialsService) credentialsService: ICredentialsService,
) {
this._credentialsService = credentialsService;
@ICredentialsService private readonly _credentialsService: ICredentialsService,
) { }
async $getPassword(service: string, account: string): Promise<string | null> {
return this._credentialsService.getPassword(service, account);
}
async $setPassword(service: string, account: string, password: string): Promise<void> {
return this._credentialsService.setPassword(service, account, password);
}
async $deletePassword(service: string, account: string): Promise<boolean> {
return this._credentialsService.deletePassword(service, account);
}
async $findPassword(service: string): Promise<string | null> {
return this._credentialsService.findPassword(service);
}
dispose(): void {
//
}
async $getPassword(service: string, account: string): Promise<string | null> {
if (this._credentialsService) {
return this._credentialsService.getPassword(service, account);
}
return null;
}
async $setPassword(service: string, account: string, password: string): Promise<void> {
if (this._credentialsService) {
return this._credentialsService.setPassword(service, account, password);
}
}
async $deletePassword(service: string, account: string): Promise<boolean> {
if (this._credentialsService) {
return this._credentialsService.deletePassword(service, account);
}
return false;
}
async $findPassword(service: string): Promise<string | null> {
if (this._credentialsService) {
return this._credentialsService.findPassword(service);
}
return null;
}
}

View File

@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CallHierarchyDto, SuggestDataDto, CodeActionDto } from '../common/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyDto, ISuggestDataDto, ICodeActionDto } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
@@ -54,9 +54,9 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
//#region --- revive functions
private static _reviveLocationDto(data: LocationDto): modes.Location;
private static _reviveLocationDto(data: LocationDto[]): modes.Location[];
private static _reviveLocationDto(data: LocationDto | LocationDto[]): modes.Location | modes.Location[] {
private static _reviveLocationDto(data: ILocationDto): modes.Location;
private static _reviveLocationDto(data: ILocationDto[]): modes.Location[];
private static _reviveLocationDto(data: ILocationDto | ILocationDto[]): modes.Location | modes.Location[] {
if (!data) {
return <modes.Location>data;
} else if (Array.isArray(data)) {
@@ -68,9 +68,9 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
private static _reviveLocationLinkDto(data: DefinitionLinkDto): modes.LocationLink;
private static _reviveLocationLinkDto(data: DefinitionLinkDto[]): modes.LocationLink[];
private static _reviveLocationLinkDto(data: DefinitionLinkDto | DefinitionLinkDto[]): modes.LocationLink | modes.LocationLink[] {
private static _reviveLocationLinkDto(data: IDefinitionLinkDto): modes.LocationLink;
private static _reviveLocationLinkDto(data: IDefinitionLinkDto[]): modes.LocationLink[];
private static _reviveLocationLinkDto(data: IDefinitionLinkDto | IDefinitionLinkDto[]): modes.LocationLink | modes.LocationLink[] {
if (!data) {
return <modes.LocationLink>data;
} else if (Array.isArray(data)) {
@@ -82,10 +82,10 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto): search.IWorkspaceSymbol;
private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto[]): search.IWorkspaceSymbol[];
private static _reviveWorkspaceSymbolDto(data: IWorkspaceSymbolDto): search.IWorkspaceSymbol;
private static _reviveWorkspaceSymbolDto(data: IWorkspaceSymbolDto[]): search.IWorkspaceSymbol[];
private static _reviveWorkspaceSymbolDto(data: undefined): undefined;
private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto | WorkspaceSymbolDto[] | undefined): search.IWorkspaceSymbol | search.IWorkspaceSymbol[] | undefined {
private static _reviveWorkspaceSymbolDto(data: IWorkspaceSymbolDto | IWorkspaceSymbolDto[] | undefined): search.IWorkspaceSymbol | search.IWorkspaceSymbol[] | undefined {
if (!data) {
return <undefined>data;
} else if (Array.isArray(data)) {
@@ -97,21 +97,21 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
private static _reviveCodeActionDto(data: ReadonlyArray<CodeActionDto>): modes.CodeAction[] {
private static _reviveCodeActionDto(data: ReadonlyArray<ICodeActionDto>): modes.CodeAction[] {
if (data) {
data.forEach(code => reviveWorkspaceEditDto(code.edit));
}
return <modes.CodeAction[]>data;
}
private static _reviveLinkDTO(data: LinkDto): modes.ILink {
private static _reviveLinkDTO(data: ILinkDto): modes.ILink {
if (data.url && typeof data.url !== 'string') {
data.url = URI.revive(data.url);
}
return <modes.ILink>data;
}
private static _reviveCallHierarchyItemDto(data: CallHierarchyDto | undefined): callh.CallHierarchyItem {
private static _reviveCallHierarchyItemDto(data: ICallHierarchyDto | undefined): callh.CallHierarchyItem {
if (data) {
data.uri = URI.revive(data.uri);
}
@@ -122,7 +122,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- outline
$registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void {
$registerDocumentSymbolProvider(handle: number, selector: IDocumentFilterDto[], displayName: string): void {
this._registrations.set(handle, modes.DocumentSymbolProviderRegistry.register(selector, <modes.DocumentSymbolProvider>{
displayName,
provideDocumentSymbols: (model: ITextModel, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined> => {
@@ -133,7 +133,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- code lens
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void {
$registerCodeLensSupport(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void {
const provider = <modes.CodeLensProvider>{
provideCodeLenses: (model: ITextModel, token: CancellationToken): Promise<modes.CodeLensList | undefined> => {
@@ -170,7 +170,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- declaration
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.DefinitionProviderRegistry.register(selector, <modes.DefinitionProvider>{
provideDefinition: (model, position, token): Promise<modes.LocationLink[]> => {
return this._proxy.$provideDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto);
@@ -178,7 +178,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}));
}
$registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerDeclarationSupport(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.DeclarationProviderRegistry.register(selector, <modes.DeclarationProvider>{
provideDeclaration: (model, position, token) => {
return this._proxy.$provideDeclaration(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto);
@@ -186,7 +186,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}));
}
$registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerImplementationSupport(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.ImplementationProviderRegistry.register(selector, <modes.ImplementationProvider>{
provideImplementation: (model, position, token): Promise<modes.LocationLink[]> => {
return this._proxy.$provideImplementation(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto);
@@ -194,7 +194,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}));
}
$registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerTypeDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.TypeDefinitionProviderRegistry.register(selector, <modes.TypeDefinitionProvider>{
provideTypeDefinition: (model, position, token): Promise<modes.LocationLink[]> => {
return this._proxy.$provideTypeDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto);
@@ -204,7 +204,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- extra info
$registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.HoverProviderRegistry.register(selector, <modes.HoverProvider>{
provideHover: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.Hover | undefined> => {
return this._proxy.$provideHover(handle, model.uri, position, token);
@@ -214,7 +214,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- occurrences
$registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.DocumentHighlightProviderRegistry.register(selector, <modes.DocumentHighlightProvider>{
provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined> => {
return this._proxy.$provideDocumentHighlights(handle, model.uri, position, token);
@@ -224,7 +224,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- references
$registerReferenceSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.ReferenceProviderRegistry.register(selector, <modes.ReferenceProvider>{
provideReferences: (model: ITextModel, position: EditorPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<modes.Location[]> => {
return this._proxy.$provideReferences(handle, model.uri, position, context, token).then(MainThreadLanguageFeatures._reviveLocationDto);
@@ -234,7 +234,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- quick fix
$registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], providedCodeActionKinds?: string[]): void {
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], providedCodeActionKinds?: string[]): void {
this._registrations.set(handle, modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
provideCodeActions: async (model: ITextModel, rangeOrSelection: EditorRange | Selection, context: modes.CodeActionContext, token: CancellationToken): Promise<modes.CodeActionList | undefined> => {
const listDto = await this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token);
@@ -256,7 +256,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- formatting
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void {
$registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void {
this._registrations.set(handle, modes.DocumentFormattingEditProviderRegistry.register(selector, <modes.DocumentFormattingEditProvider>{
extensionId,
displayName,
@@ -266,7 +266,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}));
}
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void {
$registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void {
this._registrations.set(handle, modes.DocumentRangeFormattingEditProviderRegistry.register(selector, <modes.DocumentRangeFormattingEditProvider>{
extensionId,
displayName,
@@ -276,7 +276,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}));
}
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void {
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void {
this._registrations.set(handle, modes.OnTypeFormattingEditProviderRegistry.register(selector, <modes.OnTypeFormattingEditProvider>{
extensionId,
autoFormatTriggerCharacters,
@@ -313,7 +313,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- rename
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportResolveLocation: boolean): void {
$registerRenameSupport(handle: number, selector: IDocumentFilterDto[], supportResolveLocation: boolean): void {
this._registrations.set(handle, modes.RenameProviderRegistry.register(selector, <modes.RenameProvider>{
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken): Promise<modes.WorkspaceEdit> => {
return this._proxy.$provideRenameEdits(handle, model.uri, position, newName, token).then(reviveWorkspaceEditDto);
@@ -326,7 +326,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- suggest
private static _inflateSuggestDto(defaultRange: IRange, data: SuggestDataDto): modes.CompletionItem {
private static _inflateSuggestDto(defaultRange: IRange, data: ISuggestDataDto): modes.CompletionItem {
return {
label: data.a,
kind: data.b,
@@ -347,7 +347,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
}
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void {
$registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void {
const provider: modes.CompletionItemProvider = {
triggerCharacters,
_debugDisplayName: extensionId.value,
@@ -380,7 +380,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- parameter hints
$registerSignatureHelpProvider(handle: number, selector: ISerializedDocumentFilter[], metadata: ISerializedSignatureHelpProviderMetadata): void {
$registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void {
this._registrations.set(handle, modes.SignatureHelpProviderRegistry.register(selector, <modes.SignatureHelpProvider>{
signatureHelpTriggerCharacters: metadata.triggerCharacters,
@@ -403,7 +403,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- links
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[], supportsResolve: boolean): void {
$registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void {
const provider: modes.LinkProvider = {
provideLinks: (model, token) => {
return this._proxy.$provideDocumentLinks(handle, model.uri, token).then(dto => {
@@ -423,7 +423,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
if (supportsResolve) {
provider.resolveLink = (link, token) => {
const dto: LinkDto = link;
const dto: ILinkDto = link;
if (!dto.cacheId) {
return link;
}
@@ -437,7 +437,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- colors
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void {
const proxy = this._proxy;
this._registrations.set(handle, modes.ColorProviderRegistry.register(selector, <modes.DocumentColorProvider>{
provideDocumentColors: (model, token) => {
@@ -471,7 +471,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- folding
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void {
const proxy = this._proxy;
this._registrations.set(handle, modes.FoldingRangeProviderRegistry.register(selector, <modes.FoldingRangeProvider>{
provideFoldingRanges: (model, context, token) => {
@@ -482,7 +482,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// -- smart select
$registerSelectionRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, modes.SelectionRangeRegistry.register(selector, {
provideSelectionRanges: (model, positions, token) => {
return this._proxy.$provideSelectionRanges(handle, model.uri, positions, token);
@@ -492,7 +492,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- call hierarchy
$registerCallHierarchyProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
$registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, callh.CallHierarchyProviderRegistry.register(selector, {
provideCallHierarchyItem: (document, position, token) => {
return this._proxy.$provideCallHierarchyItem(handle, document.uri, position, token).then(MainThreadLanguageFeatures._reviveCallHierarchyItemDto);
@@ -516,11 +516,11 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- configuration
private static _reviveRegExp(regExp: ISerializedRegExp): RegExp {
private static _reviveRegExp(regExp: IRegExpDto): RegExp {
return new RegExp(regExp.pattern, regExp.flags);
}
private static _reviveIndentationRule(indentationRule: ISerializedIndentationRule): IndentationRule {
private static _reviveIndentationRule(indentationRule: IIndentationRuleDto): IndentationRule {
return {
decreaseIndentPattern: MainThreadLanguageFeatures._reviveRegExp(indentationRule.decreaseIndentPattern),
increaseIndentPattern: MainThreadLanguageFeatures._reviveRegExp(indentationRule.increaseIndentPattern),
@@ -529,7 +529,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
}
private static _reviveOnEnterRule(onEnterRule: ISerializedOnEnterRule): OnEnterRule {
private static _reviveOnEnterRule(onEnterRule: IOnEnterRuleDto): OnEnterRule {
return {
beforeText: MainThreadLanguageFeatures._reviveRegExp(onEnterRule.beforeText),
afterText: onEnterRule.afterText ? MainThreadLanguageFeatures._reviveRegExp(onEnterRule.afterText) : undefined,
@@ -538,11 +538,11 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
}
private static _reviveOnEnterRules(onEnterRules: ISerializedOnEnterRule[]): OnEnterRule[] {
private static _reviveOnEnterRules(onEnterRules: IOnEnterRuleDto[]): OnEnterRule[] {
return onEnterRules.map(MainThreadLanguageFeatures._reviveOnEnterRule);
}
$setLanguageConfiguration(handle: number, languageId: string, _configuration: ISerializedLanguageConfiguration): void {
$setLanguageConfiguration(handle: number, languageId: string, _configuration: ILanguageConfigurationDto): void {
const configuration: LanguageConfiguration = {
comments: _configuration.comments,

View File

@@ -29,7 +29,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import {
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, CustomExecution2DTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecution2DTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
RunOptionsDTO
} from 'vs/workbench/api/common/shared/tasks';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
@@ -131,7 +131,7 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is ProcessExecutionDTO {
const candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
@@ -199,7 +199,7 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is ShellExecutionDTO {
const candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
@@ -230,28 +230,8 @@ namespace ShellExecutionDTO {
}
}
namespace CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is CustomExecutionDTO {
const candidate = value as CustomExecutionDTO;
return candidate && candidate.customExecution === 'customExecution';
}
export function from(value: CommandConfiguration): CustomExecutionDTO {
return {
customExecution: 'customExecution'
};
}
export function to(value: CustomExecutionDTO): CommandConfiguration {
return {
runtime: RuntimeType.CustomExecution,
presentation: undefined
};
}
}
namespace CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is CustomExecution2DTO {
const candidate = value as CustomExecution2DTO;
return candidate && candidate.customExecution === 'customExecution2';
}
@@ -371,8 +351,6 @@ namespace TaskDTO {
command = ShellExecutionDTO.to(task.execution);
} else if (ProcessExecutionDTO.is(task.execution)) {
command = ProcessExecutionDTO.to(task.execution);
} else if (CustomExecutionDTO.is(task.execution)) {
command = CustomExecutionDTO.to(task.execution);
} else if (CustomExecution2DTO.is(task.execution)) {
command = CustomExecution2DTO.to(task.execution);
}
@@ -420,7 +398,7 @@ namespace TaskFilterDTO {
@extHostNamedCustomer(MainContext.MainThreadTask)
export class MainThreadTask implements MainThreadTaskShape {
private readonly _extHostContext: IExtHostContext;
private readonly _extHostContext: IExtHostContext | undefined;
private readonly _proxy: ExtHostTaskShape;
private readonly _providers: Map<number, { disposable: IDisposable, provider: ITaskProvider }>;

View File

@@ -3,14 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { URI } from 'vs/base/common/uri';
import { StopWatch } from 'vs/base/common/stopwatch';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
@@ -21,13 +22,14 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
private readonly _terminalProcesses = new Map<number, Promise<ITerminalProcessExtHostProxy>>();
private readonly _terminalProcessesReady = new Map<number, (proxy: ITerminalProcessExtHostProxy) => void>();
private readonly _terminalOnDidWriteDataListeners = new Map<number, IDisposable>();
private readonly _terminalOnDidAcceptInputListeners = new Map<number, IDisposable>();
private _dataEventTracker: TerminalDataEventTracker | undefined;
constructor(
extHostContext: IExtHostContext,
@ITerminalService private readonly _terminalService: ITerminalService,
@ITerminalInstanceService readonly terminalInstanceService: ITerminalInstanceService,
@IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService
@IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
this._remoteAuthority = extHostContext.remoteAuthority;
@@ -100,11 +102,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
});
}
public $createTerminalRenderer(name: string): Promise<number> {
const instance = this._terminalService.createTerminalRenderer(name);
return Promise.resolve(instance.id);
}
public $show(terminalId: number, preserveFocus: boolean): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
@@ -127,44 +124,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
}
public $terminalRendererWrite(terminalId: number, text: string): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.write(text);
}
}
public $terminalRendererSetName(terminalId: number, name: string): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.setTitle(name, false);
}
}
public $terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.setDimensions(dimensions);
}
}
public $terminalRendererRegisterOnInputListener(terminalId: number): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (!terminalInstance) {
return;
}
// Listener already registered
if (this._terminalOnDidAcceptInputListeners.has(terminalId)) {
return;
}
// Register
const listener = terminalInstance.onRendererInput(data => this._onTerminalRendererInput(terminalId, data));
this._terminalOnDidAcceptInputListeners.set(terminalId, listener);
terminalInstance.addDisposable(listener);
}
public $sendText(terminalId: number, text: string, addNewLine: boolean): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
@@ -172,6 +131,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
}
/** @deprecated */
public $registerOnDataListener(terminalId: number): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (!terminalInstance) {
@@ -191,14 +151,34 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
terminalInstance.addDisposable(listener);
}
public $startSendingDataEvents(): void {
if (!this._dataEventTracker) {
this._dataEventTracker = this._instantiationService.createInstance(TerminalDataEventTracker, (id, data) => {
this._onTerminalData2(id, data);
});
}
}
public $stopSendingDataEvents(): void {
if (this._dataEventTracker) {
this._dataEventTracker.dispose();
this._dataEventTracker = undefined;
}
}
private _onActiveTerminalChanged(terminalId: number | null): void {
this._proxy.$acceptActiveTerminalChanged(terminalId);
}
/** @deprecated */
private _onTerminalData(terminalId: number, data: string): void {
this._proxy.$acceptTerminalProcessData(terminalId, data);
}
private _onTerminalData2(terminalId: number, data: string): void {
this._proxy.$acceptTerminalProcessData2(terminalId, data);
}
private _onTitleChanged(terminalId: number, name: string): void {
this._proxy.$acceptTerminalTitleChange(terminalId, name);
}
@@ -207,10 +187,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$acceptWorkspacePermissionsChanged(isAllowed);
}
private _onTerminalRendererInput(terminalId: number, data: string): void {
this._proxy.$acceptTerminalRendererInput(terminalId, data);
}
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalClosed(terminalInstance.id);
}
@@ -254,7 +230,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
} else {
this._terminalProcesses.set(proxy.terminalId, Promise.resolve(proxy));
}
const shellLaunchConfigDto: ShellLaunchConfigDto = {
const shellLaunchConfigDto: IShellLaunchConfigDto = {
name: request.shellLaunchConfig.name,
executable: request.shellLaunchConfig.executable,
args: request.shellLaunchConfig.args,
@@ -359,7 +335,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
private _onRequestDefaultShellAndArgs(request: IDefaultShellAndArgsRequest): void {
if (this._isPrimaryExtHost()) {
this._proxy.$requestDefaultShellAndArgs().then(e => request(e.shell, e.args));
this._proxy.$requestDefaultShellAndArgs(request.useAutomationShell).then(e => request.callback(e.shell, e.args));
}
}
@@ -371,3 +347,22 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
return terminal;
}
}
/**
* Encapsulates temporary tracking of data events from terminal instances, once disposed all
* listeners are removed.
*/
class TerminalDataEventTracker extends Disposable {
constructor(
private readonly _callback: (id: number, data: string) => void,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super();
this._terminalService.terminalInstances.forEach(instance => this._registerInstance(instance));
this._register(this._terminalService.onInstanceCreated(instance => this._registerInstance(instance)));
}
private _registerInstance(instance: ITerminalInstance): void {
this._register(instance.onData(e => this._callback(instance.id, e)));
}
}

View File

@@ -10,8 +10,8 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { distinct } from 'vs/base/common/arrays';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { isUndefinedOrNull, isNumber } from 'vs/base/common/types';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { Registry } from 'vs/platform/registry/common/platform';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape {
@@ -22,24 +22,28 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
constructor(
extHostContext: IExtHostContext,
@IViewsService private readonly viewsService: IViewsService,
@INotificationService private readonly notificationService: INotificationService
@INotificationService private readonly notificationService: INotificationService,
@IExtensionService private readonly extensionService: IExtensionService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
}
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.dataProvider = dataProvider;
viewer.showCollapseAllAction = !!options.showCollapseAll;
this.registerListeners(treeViewId, viewer);
this._proxy.$setVisible(treeViewId, viewer.visible);
} else {
this.notificationService.error('No view is registered with id: ' + treeViewId);
}
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void {
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.dataProvider = dataProvider;
viewer.showCollapseAllAction = !!options.showCollapseAll;
viewer.canSelectMany = !!options.canSelectMany;
this.registerListeners(treeViewId, viewer);
this._proxy.$setVisible(treeViewId, viewer.visible);
} else {
this.notificationService.error('No view is registered with id: ' + treeViewId);
}
});
}
$reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void> {
@@ -63,7 +67,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
return Promise.resolve();
}
$setMessage(treeViewId: string, message: string | IMarkdownString): void {
$setMessage(treeViewId: string, message: string): void {
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.message = message;
@@ -125,7 +129,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
this._dataProviders.forEach((dataProvider, treeViewId) => {
const treeView = this.getTreeView(treeViewId);
if (treeView) {
treeView.dataProvider = null;
treeView.dataProvider = undefined;
}
});
this._dataProviders.clear();

View File

@@ -5,30 +5,62 @@
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { startsWith } from 'vs/base/common/strings';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IProductService } from 'vs/platform/product/common/product';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { IProductService } from 'vs/platform/product/common/product';
import { startsWith } from 'vs/base/common/strings';
import { Webview } from 'vs/workbench/contrib/webview/common/webview';
interface OldMainThreadWebviewState {
readonly viewType: string;
state: any;
}
/**
* Bi-directional map between webview handles and inputs.
*/
class WebviewHandleStore {
private readonly _handlesToInputs = new Map<string, WebviewEditorInput>();
private readonly _inputsToHandles = new Map<WebviewEditorInput, string>();
public add(handle: string, input: WebviewEditorInput): void {
this._handlesToInputs.set(handle, input);
this._inputsToHandles.set(input, handle);
}
public getHandleForInput(input: WebviewEditorInput): string | undefined {
return this._inputsToHandles.get(input);
}
public getInputForHandle(handle: string): WebviewEditorInput | undefined {
return this._handlesToInputs.get(handle);
}
public delete(handle: string): void {
const input = this.getInputForHandle(handle);
this._handlesToInputs.delete(handle);
if (input) {
this._inputsToHandles.delete(input);
}
}
public get size(): number {
return this._handlesToInputs.size;
}
}
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape {
@@ -43,12 +75,9 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
private static revivalPool = 0;
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviewEditorInputs = new Map<string, WebviewEditorInput>();
private readonly _webviews = new Map<string, Webview>();
private readonly _webviewEditorInputs = new WebviewHandleStore();
private readonly _revivers = new Map<string, IDisposable>();
private _activeWebview: WebviewPanelHandle | undefined = undefined;
constructor(
context: IExtHostContext,
@IExtensionService extensionService: IExtensionService,
@@ -62,8 +91,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
super();
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this));
this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this));
this._register(_editorService.onDidActiveEditorChange(this.updateWebviewViewStates, this));
this._register(_editorService.onDidVisibleEditorsChange(this.updateWebviewViewStates, this));
// This reviver's only job is to activate webview extensions
// This should trigger the real reviver to be registered from the extension host side.
@@ -104,8 +133,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
});
this.hookupWebviewEventDelegate(handle, webview);
this._webviewEditorInputs.set(handle, webview);
this._webviews.set(handle, webview.webview);
this._webviewEditorInputs.add(handle, webview);
/* __GDPR__
"webviews:createWebviewPanel" : {
@@ -175,8 +203,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
const handle = `revival-${MainThreadWebviews.revivalPool++}`;
this._webviewEditorInputs.set(handle, webviewEditorInput);
this._webviews.set(handle, webviewEditorInput.webview);
this._webviewEditorInputs.add(handle, webviewEditorInput);
this.hookupWebviewEventDelegate(handle, webviewEditorInput);
let state = undefined;
@@ -234,7 +261,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
input.onDispose(() => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
this._webviewEditorInputs.delete(handle);
this._webviews.delete(handle);
});
});
input.webview.onDidUpdateState((newState: any) => {
@@ -246,70 +272,35 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
});
}
private onActiveEditorChanged() {
const activeEditor = this._editorService.activeControl;
let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined;
if (activeEditor && activeEditor.input instanceof WebviewEditorInput) {
for (const handle of map.keys(this._webviewEditorInputs)) {
const input = this._webviewEditorInputs.get(handle)!;
if (input.matches(activeEditor.input)) {
newActiveWebview = { input, handle };
break;
}
}
}
if (newActiveWebview && newActiveWebview.handle === this._activeWebview) {
// Webview itself unchanged but position may have changed
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0)
});
private updateWebviewViewStates() {
if (!this._webviewEditorInputs.size) {
return;
}
// Broadcast view state update for currently active
if (typeof this._activeWebview !== 'undefined') {
const oldActiveWebview = this._webviewEditorInputs.get(this._activeWebview);
if (oldActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, {
active: false,
visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)),
position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0),
});
}
}
const activeInput = this._editorService.activeControl && this._editorService.activeControl.input;
const viewStates: WebviewPanelViewStateData = {};
for (const group of this._editorGroupService.groups) {
for (const input of group.editors) {
if (!(input instanceof WebviewEditorInput)) {
continue;
}
// Then for newly active
if (newActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP),
});
this._activeWebview = newActiveWebview.handle;
} else {
this._activeWebview = undefined;
}
}
input.updateGroup(group.id);
private onVisibleEditorsChanged(): void {
this._webviewEditorInputs.forEach((input, handle) => {
for (const workbenchEditor of this._editorService.visibleControls) {
if (workbenchEditor.input && workbenchEditor.input.matches(input)) {
const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!);
input.updateGroup(workbenchEditor.group!.id);
this._proxy.$onDidChangeWebviewPanelViewState(handle, {
active: handle === this._activeWebview,
visible: true,
position: editorPosition
});
break;
const handle = this._webviewEditorInputs.getHandleForInput(input);
if (handle) {
viewStates[handle] = {
visible: input === group.activeEditor,
active: input === activeInput,
position: editorGroupToViewColumn(this._editorGroupService, group.id || 0),
};
}
}
});
}
if (Object.keys(viewStates).length) {
this._proxy.$onDidChangeWebviewPanelViewStates(viewStates);
}
}
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
@@ -342,28 +333,19 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput | undefined {
return this._webviewEditorInputs.get(handle);
return this._webviewEditorInputs.getInputForHandle(handle);
}
private getWebview(handle: WebviewPanelHandle): Webview {
const webview = this.tryGetWebview(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webview;
}
private tryGetWebview(handle: WebviewPanelHandle): Webview | undefined {
return this._webviews.get(handle);
return this.getWebviewEditorInput(handle).webview;
}
private static getDeserializationFailedContents(viewType: string) {
return `<!DOCTYPE html>
<html>
<head>
<base href="https://code.visualstudio.com/raw/">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src vscode-resource: https: 'unsafe-inline'; child-src 'none'; frame-src 'none';">
<meta http-equiv="Content-Security-Policy" content="default-src 'none';">
</head>
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)}</body>
</html>`;

View File

@@ -6,12 +6,13 @@
import { Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape, IOpenUriOptions } from '../common/extHost.protocol';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { extractLocalHostUriMetaDataForPortMapping } from 'vs/workbench/contrib/webview/common/portMapping';
import { IOpenerService } from 'vs/platform/opener/common/opener';
@extHostNamedCustomer(MainContext.MainThreadWindow)
export class MainThreadWindow implements MainThreadWindowShape {
@@ -23,7 +24,7 @@ export class MainThreadWindow implements MainThreadWindowShape {
constructor(
extHostContext: IExtHostContext,
@IWindowService private readonly windowService: IWindowService,
@IWindowsService private readonly windowsService: IWindowsService,
@IOpenerService private readonly openerService: IOpenerService,
@ITunnelService private readonly tunnelService: ITunnelService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
@@ -58,7 +59,7 @@ export class MainThreadWindow implements MainThreadWindowShape {
}
}
return this.windowsService.openExternal(encodeURI(uri.toString(true)));
return this.openerService.open(uri, { openExternal: true });
}
private getOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {

View File

@@ -19,6 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files';
import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm';
import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug';
import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/common/remote.contribution';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet';
@@ -36,7 +37,7 @@ 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, asDomUri } from 'vs/base/browser/dom';
import { createCSSRule, asCSSUrl } from 'vs/base/browser/dom';
export interface IUserFriendlyViewsContainerDescriptor {
id: string;
@@ -79,6 +80,7 @@ interface IUserFriendlyViewDescriptor {
id: string;
name: string;
when?: string;
group?: string;
}
const viewDescriptor: IJSONSchema = {
@@ -99,6 +101,27 @@ const viewDescriptor: IJSONSchema = {
}
};
const nestableViewDescriptor: IJSONSchema = {
type: 'object',
properties: {
id: {
description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'),
type: 'string'
},
name: {
description: localize('vscode.extension.contributes.view.name', 'The human-readable name of the view. Will be shown'),
type: 'string'
},
when: {
description: localize('vscode.extension.contributes.view.when', 'Condition which must be true to show this view'),
type: 'string'
},
group: {
description: localize('vscode.extension.contributes.view.group', 'Nested group in the viewlet'),
type: 'string'
}
}
};
const viewsContribution: IJSONSchema = {
description: localize('vscode.extension.contributes.views', "Contributes views to the editor"),
type: 'object',
@@ -126,6 +149,12 @@ const viewsContribution: IJSONSchema = {
type: 'array',
items: viewDescriptor,
default: []
},
'remote': {
description: localize('views.remote', "Contributes views to Remote container in the Activity bar. To contribute to this container, enableProposedApi needs to be turned on"),
type: 'array',
items: nestableViewDescriptor,
default: []
}
},
additionalProperties: {
@@ -181,7 +210,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
});
}
private addCustomViewContainers(extensionPoints: IExtensionPointUser<ViewContainerExtensionPointType>[], existingViewContainers: ViewContainer[]): void {
private addCustomViewContainers(extensionPoints: readonly IExtensionPointUser<ViewContainerExtensionPointType>[], existingViewContainers: ViewContainer[]): void {
const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
let order = TEST_VIEW_CONTAINER_ORDER + viewContainersRegistry.all.filter(v => !!v.extensionId).length + 1;
for (let { value, collector, description } of extensionPoints) {
@@ -198,7 +227,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
}
}
private removeCustomViewContainers(extensionPoints: IExtensionPointUser<ViewContainerExtensionPointType>[]): void {
private removeCustomViewContainers(extensionPoints: readonly IExtensionPointUser<ViewContainerExtensionPointType>[]): void {
const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const removedExtensions: Set<string> = extensionPoints.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set<string>());
for (const viewContainer of viewContainersRegistry.all) {
@@ -327,7 +356,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
// 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: url('${asDomUri(icon)}') no-repeat 50% 50%; -webkit-mask-size: 24px;`);
createCSSRule(iconClass, `-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; -webkit-mask-size: 24px;`);
}
return viewContainer;
@@ -349,7 +378,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
});
}
private addViews(extensions: IExtensionPointUser<ViewExtensionPointType>[]): void {
private addViews(extensions: readonly IExtensionPointUser<ViewExtensionPointType>[]): void {
for (const extension of extensions) {
const { value, collector } = extension;
@@ -358,6 +387,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return;
}
if (entry.key === 'remote' && !extension.description.enableProposedApi) {
collector.warn(localize('ViewContainerRequiresProposedAPI', "View container '{0}' requires 'enableProposedApi' turned on to be added to 'Remote'.", entry.key));
return;
}
const viewContainer = this.getViewContainer(entry.key);
if (!viewContainer) {
collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key));
@@ -376,6 +410,12 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return null;
}
const order = ExtensionIdentifier.equals(extension.description.identifier, container.extensionId)
? index + 1
: container.orderDelegate
? container.orderDelegate.getOrder(item.group)
: undefined;
const viewDescriptor = <ICustomViewDescriptor>{
id: item.id,
name: item.name,
@@ -384,9 +424,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
canToggleVisibility: true,
collapsed: this.showCollapsed(container),
treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name, container),
order: ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : undefined,
order: order,
extensionId: extension.description.identifier,
originalContainerId: entry.key
originalContainerId: entry.key,
group: item.group
};
viewIds.push(viewDescriptor.id);
@@ -401,7 +442,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return this.viewContainersRegistry.get(EXPLORER)!;
}
private removeViews(extensions: IExtensionPointUser<ViewExtensionPointType>[]): void {
private removeViews(extensions: readonly IExtensionPointUser<ViewExtensionPointType>[]): void {
const removedExtensions: Set<string> = extensions.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set<string>());
for (const viewContainer of this.viewContainersRegistry.all) {
const removedViews = this.viewsRegistry.getViews(viewContainer).filter((v: ICustomViewDescriptor) => v.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(v.extensionId)));
@@ -440,6 +481,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
case 'explorer': return this.viewContainersRegistry.get(EXPLORER);
case 'debug': return this.viewContainersRegistry.get(DEBUG);
case 'scm': return this.viewContainersRegistry.get(SCM);
case 'remote': return this.viewContainersRegistry.get(REMOTE);
default: return this.viewContainersRegistry.get(`workbench.view.extension.${value}`);
}
}

View File

@@ -175,6 +175,11 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio
return windowService.addRecentlyOpened([recent]);
});
CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function (accessor: ServicesAccessor) {
const windowService = accessor.get(IWindowService);
return windowService.getRecentlyOpened();
});
export class SetEditorLayoutAPICommand {
public static ID = 'vscode.setEditorLayout';
public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise<any> {
@@ -205,4 +210,4 @@ CommandsRegistry.registerCommand({
}
}]
}
});
});

View File

@@ -39,13 +39,14 @@ const configurationEntrySchema: IJSONSchema = {
},
scope: {
type: 'string',
enum: ['application', 'machine', 'window', 'resource'],
enum: ['application', 'machine', 'window', 'resource', 'machine-overridable'],
default: 'window',
enumDescriptions: [
nls.localize('scope.application.description', "Application specific configuration, which can be configured only in the user settings."),
nls.localize('scope.machine.description', "Machine specific configuration, which can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."),
nls.localize('scope.window.description', "Window specific configuration, which can be configured in the user, remote or workspace settings."),
nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the user, remote, workspace or folder settings.")
nls.localize('scope.application.description', "Configuration that can be configured only in the user settings."),
nls.localize('scope.machine.description', "Configuration that can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."),
nls.localize('scope.window.description', "Configuration that can be configured in the user, remote or workspace settings."),
nls.localize('scope.resource.description', "Configuration that can be configured in the user, remote, workspace or folder settings."),
nls.localize('scope.machine-overridable.description', "Machine configuration that can be configured also in workspace or folder settings.")
],
description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `application`, `machine`, `window` and `resource`.")
},
@@ -215,6 +216,8 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten
propertyConfiguration.scope = ConfigurationScope.MACHINE;
} else if (propertyConfiguration.scope.toString() === 'resource') {
propertyConfiguration.scope = ConfigurationScope.RESOURCE;
} else if (propertyConfiguration.scope.toString() === 'machine-overridable') {
propertyConfiguration.scope = ConfigurationScope.MACHINE_OVERRIDABLE;
} else {
propertyConfiguration.scope = ConfigurationScope.WINDOW;
}

View File

@@ -15,63 +15,59 @@ import { OverviewRulerLane } from 'vs/editor/common/model';
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
import { score } from 'vs/editor/common/modes/languageSelector';
import * as files from 'vs/platform/files/common/files';
import { ExtHostContext, IInitData, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostContext, MainContext, ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands';
import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostComments } from 'vs/workbench/api/common/extHostComments';
import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations';
import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
import { ExtHostDialogs } from 'vs/workbench/api/common/extHostDialogs';
import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostDocumentContentProviders';
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { ExtensionActivatedByAPI } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem';
import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages';
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService';
import { ExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
import { LogOutputChannelFactory } from 'vs/workbench/api/node/extHostOutputService';
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress';
import { ExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen';
import { ExtHostSCM } from 'vs/workbench/api/common/extHostSCM';
import { ExtHostSearch, registerEHSearchProviders } from 'vs/workbench/api/node/extHostSearch';
import { ExtHostStatusBar } from 'vs/workbench/api/common/extHostStatusBar';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
import { ExtHostTreeViews } from 'vs/workbench/api/common/extHostTreeViews';
import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadService';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls';
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
import { ExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { originalFSPath } from 'vs/base/common/resources';
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { values } from 'vs/base/common/collections';
import { Schemas } from 'vs/base/common/network';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations';
import { IExtHostTask } from 'vs/workbench/api/common/extHostTask';
import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
import { ILogService } from 'vs/platform/log/common/log';
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -88,68 +84,55 @@ function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
/**
* This method instantiates and returns the extension API surface
*/
export function createApiFactory(
initData: IInitData,
rpcProtocol: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extensionService: ExtHostExtensionService,
extHostLogService: ExtHostLogService,
extHostStorage: ExtHostStorage,
uriTransformer: IURITransformer | null
): IExtensionApiFactory {
export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): IExtensionApiFactory {
// bootstrap services
const services = new ServiceCollection(...getSingletonServiceDescriptors());
const instaService = new InstantiationService(services);
// services
const initData = accessor.get(IExtHostInitDataService);
const extensionService = accessor.get(IExtHostExtensionService);
const extHostWorkspace = accessor.get(IExtHostWorkspace);
const extHostConfiguration = accessor.get(IExtHostConfiguration);
const uriTransformer = accessor.get(IURITransformerService);
const rpcProtocol = accessor.get(IExtHostRpcService);
const extHostStorage = accessor.get(IExtHostStorage);
const extHostLogService = accessor.get(ILogService);
// Addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol));
// register addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, <ExtHostLogServiceShape><any>extHostLogService);
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
// automatically create and register addressable instances
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations));
const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, accessor.get(IExtHostDocumentsAndEditors));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, accessor.get(IExtHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, accessor.get(IExtHostTerminalService));
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, accessor.get(IExtHostDebugService));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, accessor.get(IExtHostSearch));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, accessor.get(IExtHostTask));
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));
const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol));
const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol));
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors)));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostLogService));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
rpcProtocol.set(ExtHostContext.ExtHostDownloadService, new ExtHostDownloadService(rpcProtocol.getProxy(MainContext.MainThreadDownloadService), extHostCommands));
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
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 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 extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostWorkspace, extHostDocumentsAndEditors, extHostLogService));
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, instaService.createInstance(ExtHostDebugService, rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, uriTransformer, extHostLogService));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService));
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(LogOutputChannelFactory, initData.logsLocation, rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol));
if (initData.remote.isRemote && initData.remote.authority) {
extHostTask.registerTaskSystem(Schemas.vscodeRemote, {
scheme: Schemas.vscodeRemote,
authority: initData.remote.authority,
platform: process.platform
});
registerEHSearchProviders(extHostSearch, extHostLogService);
const cliServer = new CLIServer(extHostCommands);
process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath;
}
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = values(ExtHostContext);
rpcProtocol.assertRegistered(expected);
@@ -161,10 +144,6 @@ export function createApiFactory(
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
// Register an output channel for exthost log
const outputChannelName = initData.remote.isRemote ? nls.localize('remote extension host Log', "Remote Extension Host") : nls.localize('extension host Log', "Extension Host");
extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
@@ -244,11 +223,7 @@ export function createApiFactory(
},
getCommands(filterInternal: boolean = false): Thenable<string[]> {
return extHostCommands.getCommands(filterInternal);
},
onDidExecuteCommand: proposedApiFunction(extension, (listener, thisArgs?, disposables?) => {
checkProposedApiEnabled(extension);
return extHostCommands.onDidExecuteCommand(listener, thisArgs, disposables);
}),
}
};
// namespace: env
@@ -271,7 +246,7 @@ export function createApiFactory(
return extHostClipboard;
},
get shell() {
return extHostTerminalService.getDefaultShell(configProvider);
return extHostTerminalService.getDefaultShell(false, configProvider);
},
openExternal(uri: URI) {
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote });
@@ -457,8 +432,13 @@ export function createApiFactory(
return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables);
},
onDidChangeTerminalDimensions(listener, thisArg?, disposables?) {
checkProposedApiEnabled(extension);
return extHostTerminalService.onDidChangeTerminalDimensions(listener, thisArg, disposables);
},
onDidWriteTerminalData(listener, thisArg?, disposables?) {
checkProposedApiEnabled(extension);
return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables);
},
get state() {
return extHostWindow.state;
},
@@ -532,16 +512,11 @@ export function createApiFactory(
if (typeof nameOrOptions === 'object') {
if ('pty' in nameOrOptions) {
return extHostTerminalService.createExtensionTerminal(nameOrOptions);
} else {
nameOrOptions.hideFromUser = nameOrOptions.hideFromUser || (nameOrOptions.runInBackground && extension.enableProposedApi);
return extHostTerminalService.createTerminalFromOptions(nameOrOptions);
}
return extHostTerminalService.createTerminalFromOptions(nameOrOptions);
}
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
},
createTerminalRenderer(name: string): vscode.TerminalRenderer {
return extHostTerminalService.createTerminalRenderer(name);
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension);
},
@@ -847,7 +822,6 @@ export function createApiFactory(
EventEmitter: Emitter,
ExtensionExecutionContext: extHostTypes.ExtensionExecutionContext,
ExtensionKind: extHostTypes.ExtensionKind,
CustomExecution: extHostTypes.CustomExecution,
CustomExecution2: extHostTypes.CustomExecution2,
FileChangeType: extHostTypes.FileChangeType,
FileSystemError: extHostTypes.FileSystemError,
@@ -912,7 +886,7 @@ export function createApiFactory(
class Extension<T> implements vscode.Extension<T> {
private _extensionService: ExtHostExtensionService;
private _extensionService: IExtHostExtensionService;
private _identifier: ExtensionIdentifier;
readonly id: string;
@@ -920,7 +894,7 @@ class Extension<T> implements vscode.Extension<T> {
readonly packageJSON: IExtensionDescription;
readonly extensionKind: vscode.ExtensionKind;
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) {
constructor(extensionService: IExtHostExtensionService, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) {
this._extensionService = extensionService;
this._identifier = description.identifier;
this.id = description.identifier.value;

View File

@@ -21,7 +21,7 @@ import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model'
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription, ICommandEvent } from 'vs/platform/commands/common/commands';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@@ -115,8 +115,6 @@ export interface MainThreadClipboardShape extends IDisposable {
export interface MainThreadCommandsShape extends IDisposable {
$registerCommand(id: string): void;
$registerCommandListener(): void;
$unregisterCommandListener(): void;
$unregisterCommand(id: string): void;
$executeCommand<T>(id: string, args: any[]): Promise<T | undefined>;
$getCommands(): Promise<string[]>;
@@ -132,7 +130,7 @@ export interface MainThreadCommentsShape extends IDisposable {
$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, contextValue: string | undefined, comments: modes.Comment[], collapseState: modes.CommentThreadCollapsibleState): void;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, label: string | undefined, contextValue: string | undefined, comments: modes.Comment[], collapseState: modes.CommentThreadCollapsibleState): void;
$deleteCommentThread(handle: number, commentThreadHandle: number): void;
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void;
}
@@ -236,16 +234,16 @@ export interface MainThreadTextEditorsShape extends IDisposable {
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): Promise<void>;
$trySetSelections(id: string, selections: ISelection[]): Promise<void>;
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Promise<boolean>;
$tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto): Promise<boolean>;
$tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto): Promise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: readonly IRange[], opts: IUndoStopOptions): Promise<boolean>;
$getDiffInformation(id: string): Promise<editorCommon.ILineChange[]>;
}
export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): 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 | IMarkdownString): void;
$setMessage(treeViewId: string, message: string): void;
}
export interface MainThreadDownloadServiceShape extends IDisposable {
@@ -267,28 +265,28 @@ export interface MainThreadKeytarShape extends IDisposable {
$findPassword(service: string): Promise<string | null>;
}
export interface ISerializedRegExp {
export interface IRegExpDto {
pattern: string;
flags?: string;
}
export interface ISerializedIndentationRule {
decreaseIndentPattern: ISerializedRegExp;
increaseIndentPattern: ISerializedRegExp;
indentNextLinePattern?: ISerializedRegExp;
unIndentedLinePattern?: ISerializedRegExp;
export interface IIndentationRuleDto {
decreaseIndentPattern: IRegExpDto;
increaseIndentPattern: IRegExpDto;
indentNextLinePattern?: IRegExpDto;
unIndentedLinePattern?: IRegExpDto;
}
export interface ISerializedOnEnterRule {
beforeText: ISerializedRegExp;
afterText?: ISerializedRegExp;
oneLineAboveText?: ISerializedRegExp;
export interface IOnEnterRuleDto {
beforeText: IRegExpDto;
afterText?: IRegExpDto;
oneLineAboveText?: IRegExpDto;
action: EnterAction;
}
export interface ISerializedLanguageConfiguration {
export interface ILanguageConfigurationDto {
comments?: CommentRule;
brackets?: CharacterPair[];
wordPattern?: ISerializedRegExp;
indentationRules?: ISerializedIndentationRule;
onEnterRules?: ISerializedOnEnterRule[];
wordPattern?: IRegExpDto;
indentationRules?: IIndentationRuleDto;
onEnterRules?: IOnEnterRuleDto[];
__electricCharacterSupport?: {
brackets?: any;
docComment?: {
@@ -309,7 +307,7 @@ export interface ISerializedLanguageConfiguration {
export type GlobPattern = string | { base: string; pattern: string };
export interface ISerializedDocumentFilter {
export interface IDocumentFilterDto {
$serialized: true;
language?: string;
scheme?: string;
@@ -317,37 +315,37 @@ export interface ISerializedDocumentFilter {
exclusive?: boolean;
}
export interface ISerializedSignatureHelpProviderMetadata {
export interface ISignatureHelpProviderMetadataDto {
readonly triggerCharacters: readonly string[];
readonly retriggerCharacters: readonly string[];
}
export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): void;
$registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], label: string): void;
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void;
$registerDocumentSymbolProvider(handle: number, selector: IDocumentFilterDto[], label: string): void;
$registerCodeLensSupport(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void;
$emitCodeLensEvent(eventHandle: number, event?: any): void;
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerReferenceSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], supportedKinds?: string[]): void;
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
$registerDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerDeclarationSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerImplementationSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerTypeDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], supportedKinds?: string[]): void;
$registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
$registerNavigateTypeSupport(handle: number): void;
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportsResolveInitialValues: boolean): void;
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void;
$registerSignatureHelpProvider(handle: number, selector: ISerializedDocumentFilter[], metadata: ISerializedSignatureHelpProviderMetadata): void;
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[], supportsResolve: boolean): void;
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerSelectionRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerCallHierarchyProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ISerializedLanguageConfiguration): void;
$registerRenameSupport(handle: number, selector: IDocumentFilterDto[], supportsResolveInitialValues: boolean): 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;
$registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void;
}
export interface MainThreadLanguagesShape extends IDisposable {
@@ -395,12 +393,14 @@ export interface TerminalLaunchConfig {
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(config: TerminalLaunchConfig): Promise<{ id: number, name: string }>;
$createTerminalRenderer(name: string): Promise<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
$show(terminalId: number, preserveFocus: boolean): void;
/** @deprecated */
$registerOnDataListener(terminalId: number): void;
$startSendingDataEvents(): void;
$stopSendingDataEvents(): void;
// Process
$sendProcessTitle(terminalId: number, title: string): void;
@@ -411,12 +411,6 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$sendProcessCwd(terminalId: number, initialCwd: string): void;
$sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void;
$sendResolvedLaunchConfig(terminalId: number, shellLaunchConfig: IShellLaunchConfig): void;
// Renderer
$terminalRendererSetName(terminalId: number, name: string): void;
$terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void;
$terminalRendererWrite(terminalId: number, text: string): void;
$terminalRendererRegisterOnInputListener(terminalId: number): void;
}
export interface TransferQuickPickItems extends quickInput.IQuickPickItem {
@@ -555,15 +549,17 @@ export interface MainThreadWebviewsShape extends IDisposable {
$unregisterSerializer(viewType: string): void;
}
export interface WebviewPanelViewState {
readonly active: boolean;
readonly visible: boolean;
readonly position: EditorViewColumn;
export interface WebviewPanelViewStateData {
[handle: string]: {
readonly active: boolean;
readonly visible: boolean;
readonly position: EditorViewColumn;
};
}
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewPanelHandle, message: any): void;
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): 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>;
}
@@ -649,7 +645,7 @@ export interface SCMProviderFeatures {
count?: number;
commitTemplate?: string;
acceptInputCommand?: modes.Command;
statusBarCommands?: CommandDto[];
statusBarCommands?: ICommandDto[];
}
export interface SCMGroupFeatures {
@@ -721,8 +717,8 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>;
$appendDebugConsole(value: string): void;
$startBreakpointEvents(): void;
$registerBreakpoints(breakpoints: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto>): Promise<void>;
$unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise<void>;
$registerBreakpoints(breakpoints: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto>): Promise<void>;
$unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[], dataBreakpointIds: string[]): Promise<void>;
}
export interface IOpenUriOptions {
@@ -739,7 +735,6 @@ export interface MainThreadWindowShape extends IDisposable {
export interface ExtHostCommandsShape {
$executeContributedCommand<T>(id: string, ...args: any[]): Promise<T>;
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }>;
$handleDidExecuteCommand(command: ICommandEvent): void;
}
export interface ExtHostConfigurationShape {
@@ -748,7 +743,7 @@ export interface ExtHostConfigurationShape {
}
export interface ExtHostDiagnosticsShape {
$acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void;
}
export interface ExtHostDocumentContentProvidersShape {
@@ -819,9 +814,6 @@ export interface ExtHostTreeViewsShape {
$setVisible(treeViewId: string, visible: boolean): void;
}
export interface ExtHostDownloadServiceShape {
}
export interface ExtHostWorkspaceShape {
$initializeWorkspace(workspace: IWorkspaceData | null): void;
$acceptWorkspaceData(workspace: IWorkspaceData | null): void;
@@ -876,6 +868,7 @@ export interface ExtHostExtensionServiceShape {
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
$activateByEvent(activationEvent: string): Promise<void>;
$activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean>;
$setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
$deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void>;
@@ -927,7 +920,7 @@ export class IdObject {
}
}
export interface SuggestDataDto {
export interface ISuggestDataDto {
a/* label */: string;
b/* kind */: modes.CompletionItemKind;
c/* detail */?: string;
@@ -946,51 +939,51 @@ export interface SuggestDataDto {
x?: ChainedCacheId;
}
export interface SuggestResultDto {
export interface ISuggestResultDto {
x?: number;
a: IRange;
b: SuggestDataDto[];
b: ISuggestDataDto[];
c?: boolean;
}
export interface SignatureHelpDto {
export interface ISignatureHelpDto {
id: CacheId;
signatures: modes.SignatureInformation[];
activeSignature: number;
activeParameter: number;
}
export interface SignatureHelpContextDto {
export interface ISignatureHelpContextDto {
readonly triggerKind: modes.SignatureHelpTriggerKind;
readonly triggerCharacter?: string;
readonly isRetrigger: boolean;
readonly activeSignatureHelp?: SignatureHelpDto;
readonly activeSignatureHelp?: ISignatureHelpDto;
}
export interface LocationDto {
export interface ILocationDto {
uri: UriComponents;
range: IRange;
}
export interface DefinitionLinkDto {
export interface IDefinitionLinkDto {
originSelectionRange?: IRange;
uri: UriComponents;
range: IRange;
targetSelectionRange?: IRange;
}
export interface WorkspaceSymbolDto extends IdObject {
export interface IWorkspaceSymbolDto extends IdObject {
name: string;
containerName?: string;
kind: modes.SymbolKind;
location: LocationDto;
location: ILocationDto;
}
export interface WorkspaceSymbolsDto extends IdObject {
symbols: WorkspaceSymbolDto[];
export interface IWorkspaceSymbolsDto extends IdObject {
symbols: IWorkspaceSymbolDto[];
}
export interface ResourceFileEditDto {
export interface IResourceFileEditDto {
oldUri?: UriComponents;
newUri?: UriComponents;
options?: {
@@ -1001,76 +994,76 @@ export interface ResourceFileEditDto {
};
}
export interface ResourceTextEditDto {
export interface IResourceTextEditDto {
resource: UriComponents;
modelVersionId?: number;
edits: modes.TextEdit[];
}
export interface WorkspaceEditDto {
edits: Array<ResourceFileEditDto | ResourceTextEditDto>;
export interface IWorkspaceEditDto {
edits: Array<IResourceFileEditDto | IResourceTextEditDto>;
// todo@joh reject should go into rename
rejectReason?: string;
}
export function reviveWorkspaceEditDto(data: WorkspaceEditDto | undefined): modes.WorkspaceEdit {
export function reviveWorkspaceEditDto(data: IWorkspaceEditDto | undefined): modes.WorkspaceEdit {
if (data && data.edits) {
for (const edit of data.edits) {
if (typeof (<ResourceTextEditDto>edit).resource === 'object') {
(<ResourceTextEditDto>edit).resource = URI.revive((<ResourceTextEditDto>edit).resource);
if (typeof (<IResourceTextEditDto>edit).resource === 'object') {
(<IResourceTextEditDto>edit).resource = URI.revive((<IResourceTextEditDto>edit).resource);
} else {
(<ResourceFileEditDto>edit).newUri = URI.revive((<ResourceFileEditDto>edit).newUri);
(<ResourceFileEditDto>edit).oldUri = URI.revive((<ResourceFileEditDto>edit).oldUri);
(<IResourceFileEditDto>edit).newUri = URI.revive((<IResourceFileEditDto>edit).newUri);
(<IResourceFileEditDto>edit).oldUri = URI.revive((<IResourceFileEditDto>edit).oldUri);
}
}
}
return <modes.WorkspaceEdit>data;
}
export type CommandDto = ObjectIdentifier & modes.Command;
export type ICommandDto = ObjectIdentifier & modes.Command;
export interface CodeActionDto {
export interface ICodeActionDto {
title: string;
edit?: WorkspaceEditDto;
edit?: IWorkspaceEditDto;
diagnostics?: IMarkerData[];
command?: CommandDto;
command?: ICommandDto;
kind?: string;
isPreferred?: boolean;
}
export interface CodeActionListDto {
export interface ICodeActionListDto {
cacheId: number;
actions: ReadonlyArray<CodeActionDto>;
actions: ReadonlyArray<ICodeActionDto>;
}
export type CacheId = number;
export type ChainedCacheId = [CacheId, CacheId];
export interface LinksListDto {
export interface ILinksListDto {
id?: CacheId;
links: LinkDto[];
links: ILinkDto[];
}
export interface LinkDto {
export interface ILinkDto {
cacheId?: ChainedCacheId;
range: IRange;
url?: string | UriComponents;
tooltip?: string;
}
export interface CodeLensListDto {
export interface ICodeLensListDto {
cacheId?: number;
lenses: CodeLensDto[];
lenses: ICodeLensDto[];
}
export interface CodeLensDto {
export interface ICodeLensDto {
cacheId?: ChainedCacheId;
range: IRange;
command?: CommandDto;
command?: ICommandDto;
}
export interface CallHierarchyDto {
export interface ICallHierarchyDto {
_id: number;
kind: modes.SymbolKind;
name: string;
@@ -1082,40 +1075,40 @@ export interface CallHierarchyDto {
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensListDto | undefined>;
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined>;
$resolveCodeLens(handle: number, symbol: ICodeLensDto, token: CancellationToken): Promise<ICodeLensDto | undefined>;
$releaseCodeLenses(handle: number, id: number): void;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IDefinitionLinkDto[]>;
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IDefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IDefinitionLinkDto[]>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IDefinitionLinkDto[]>;
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover | undefined>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<LocationDto[] | undefined>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionListDto | undefined>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<ILocationDto[] | undefined>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<ICodeActionListDto | undefined>;
$releaseCodeActions(handle: number, cacheId: number): void;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<WorkspaceSymbolsDto>;
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Promise<WorkspaceSymbolDto | undefined>;
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<IWorkspaceSymbolsDto>;
$resolveWorkspaceSymbol(handle: number, symbol: IWorkspaceSymbolDto, token: CancellationToken): Promise<IWorkspaceSymbolDto | undefined>;
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto | undefined>;
$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>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<SuggestDataDto | undefined>;
$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;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<SignatureHelpDto | undefined>;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<ISignatureHelpDto | undefined>;
$releaseSignatureHelp(handle: number, id: number): void;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinksListDto | undefined>;
$resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<LinkDto | undefined>;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<ILinksListDto | undefined>;
$resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<ILinkDto | undefined>;
$releaseDocumentLinks(handle: number, id: number): void;
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Promise<IRawColorInfo[]>;
$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[][]>;
$provideCallHierarchyItem(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<CallHierarchyDto | undefined>;
$resolveCallHierarchyItem(handle: number, item: callHierarchy.CallHierarchyItem, direction: callHierarchy.CallHierarchyDirection, token: CancellationToken): Promise<[CallHierarchyDto, modes.Location[]][]>;
$provideCallHierarchyItem(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<ICallHierarchyDto | undefined>;
$resolveCallHierarchyItem(handle: number, item: callHierarchy.CallHierarchyItem, direction: callHierarchy.CallHierarchyDirection, token: CancellationToken): Promise<[ICallHierarchyDto, modes.Location[]][]>;
}
export interface ExtHostQuickOpenShape {
@@ -1129,7 +1122,7 @@ export interface ExtHostQuickOpenShape {
$onDidHide(sessionId: number): void;
}
export interface ShellLaunchConfigDto {
export interface IShellLaunchConfigDto {
name?: string;
executable?: string;
args?: string[] | string;
@@ -1157,12 +1150,13 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalOpened(id: number, name: string): void;
$acceptActiveTerminalChanged(id: number | null): void;
$acceptTerminalProcessId(id: number, processId: number): void;
/** @deprecated */
$acceptTerminalProcessData(id: number, data: string): void;
$acceptTerminalRendererInput(id: number, data: string): void;
$acceptTerminalProcessData2(id: number, data: string): void;
$acceptTerminalTitleChange(id: number, name: string): void;
$acceptTerminalDimensions(id: number, cols: number, rows: number): void;
$acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void;
$spawnExtHostProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void;
$spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void;
$startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): void;
$acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void;
@@ -1172,7 +1166,7 @@ export interface ExtHostTerminalServiceShape {
$acceptProcessRequestLatency(id: number): number;
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
$requestAvailableShells(): Promise<IShellDefinitionDto[]>;
$requestDefaultShellAndArgs(): Promise<IShellAndArgsDto>;
$requestDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
}
export interface ExtHostSCMShape {
@@ -1207,6 +1201,13 @@ export interface IFunctionBreakpointDto extends IBreakpointDto {
functionName: string;
}
export interface IDataBreakpointDto extends IBreakpointDto {
type: 'data';
dataId: string;
canPersist: boolean;
label: string;
}
export interface ISourceBreakpointDto extends IBreakpointDto {
type: 'source';
uri: UriComponents;
@@ -1215,9 +1216,9 @@ export interface ISourceBreakpointDto extends IBreakpointDto {
}
export interface IBreakpointsDeltaDto {
added?: Array<ISourceBreakpointDto | IFunctionBreakpointDto>;
added?: Array<ISourceBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto>;
removed?: string[];
changed?: Array<ISourceBreakpointDto | IFunctionBreakpointDto>;
changed?: Array<ISourceBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto>;
}
export interface ISourceMultiBreakpointDto {
@@ -1250,8 +1251,8 @@ export interface ExtHostDebugServiceShape {
$startDASession(handle: number, session: IDebugSessionDto): Promise<void>;
$stopDASession(handle: number): Promise<void>;
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Promise<IConfig | null | undefined>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined): Promise<IConfig[]>;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined, token: CancellationToken): Promise<IConfig[]>;
$legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Promise<IAdapterDescriptor>; // TODO@AW legacy
$provideDebugAdapter(handle: number, session: IDebugSessionDto): Promise<IAdapterDescriptor>;
$acceptDebugSessionStarted(session: IDebugSessionDto): void;
@@ -1357,7 +1358,6 @@ export const ExtHostContext = {
ExtHostDocumentSaveParticipant: createExtId<ExtHostDocumentSaveParticipantShape>('ExtHostDocumentSaveParticipant'),
ExtHostEditors: createExtId<ExtHostEditorsShape>('ExtHostEditors'),
ExtHostTreeViews: createExtId<ExtHostTreeViewsShape>('ExtHostTreeViews'),
ExtHostDownloadService: createExtId<ExtHostDownloadServiceShape>('ExtHostDownloadService'),
ExtHostFileSystem: createExtId<ExtHostFileSystemShape>('ExtHostFileSystem'),
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventServiceShape>('ExtHostFileSystemEventService'),
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),

View File

@@ -8,7 +8,7 @@ import { 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, WorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol';
import { IRawColorInfo, IWorkspaceEditDto } 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';
@@ -135,7 +135,7 @@ export class ExtHostApiCommands {
description: 'Execute code action provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'range', description: 'Range in a text document', constraint: types.Range },
{ name: 'rangeOrSelection', description: 'Range in a text document. Some refactoring provider requires Selection object.', constraint: types.Range },
{ name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' },
],
returns: 'A promise that resolves to an array of Command-instances.'
@@ -362,7 +362,7 @@ export class ExtHostApiCommands {
position: position && typeConverters.Position.from(position),
newName
};
return this._commands.executeCommand<WorkspaceEditDto>('_executeDocumentRenameProvider', args).then(value => {
return this._commands.executeCommand<IWorkspaceEditDto>('_executeDocumentRenameProvider', args).then(value => {
if (!value) {
return undefined;
}
@@ -470,20 +470,22 @@ export class ExtHostApiCommands {
return res;
}
detail: string;
range: vscode.Range;
selectionRange: vscode.Range;
children: vscode.DocumentSymbol[];
containerName: string;
detail!: string;
range!: vscode.Range;
selectionRange!: vscode.Range;
children!: vscode.DocumentSymbol[];
containerName!: string;
}
return value.map(MergedInfo.to);
});
}
private _executeCodeActionProvider(resource: URI, range: types.Range, kind?: string): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> {
private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> {
const args = {
resource,
range: typeConverters.Range.from(range),
rangeOrSelection: types.Selection.isSelection(rangeOrSelection)
? typeConverters.Selection.from(rangeOrSelection)
: typeConverters.Range.from(rangeOrSelection),
kind
};
return this._commands.executeCommand<CustomCodeAction[]>('_executeCodeActionProvider', args)
@@ -504,6 +506,7 @@ export class ExtHostApiCommands {
if (codeAction.command) {
ret.command = this._commands.converter.fromInternal(codeAction.command);
}
ret.isPreferred = codeAction.isPreferred;
return ret;
}
}));

View File

@@ -10,7 +10,7 @@ import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor';
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
import * as vscode from 'vscode';
import { ExtHostEditorInsetsShape, MainThreadEditorInsetsShape } from './extHost.protocol';
import { toWebviewResource, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { generateUuid } from 'vs/base/common/uuid';
export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
@@ -63,10 +63,10 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
private readonly _uuid = generateUuid();
private _html: string = '';
private _options: vscode.WebviewOptions;
private _options: vscode.WebviewOptions = Object.create(null);
toWebviewResource(resource: vscode.Uri): vscode.Uri {
return toWebviewResource(that._initData, this._uuid, resource);
asWebviewUri(resource: vscode.Uri): vscode.Uri {
return asWebviewUri(that._initData, this._uuid, resource);
}
get cspSource(): string {

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { validateConstraint } from 'vs/base/common/types';
import { ICommandHandlerDescription, ICommandEvent } from 'vs/platform/commands/common/commands';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { cloneAndChange } from 'vs/base/common/objects';
import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, IMainContext, CommandDto } from './extHost.protocol';
import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, ICommandDto } from './extHost.protocol';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import * as modes from 'vs/editor/common/modes';
import * as vscode from 'vscode';
@@ -17,8 +17,9 @@ import { revive } from 'vs/base/common/marshalling';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
interface CommandHandler {
callback: Function;
@@ -32,8 +33,7 @@ export interface ArgumentProcessor {
export class ExtHostCommands implements ExtHostCommandsShape {
private readonly _onDidExecuteCommand: Emitter<vscode.CommandExecutionEvent>;
readonly onDidExecuteCommand: Event<vscode.CommandExecutionEvent>;
readonly _serviceBrand: any;
private readonly _commands = new Map<string, CommandHandler>();
private readonly _proxy: MainThreadCommandsShape;
@@ -42,15 +42,10 @@ export class ExtHostCommands implements ExtHostCommandsShape {
private readonly _argumentProcessors: ArgumentProcessor[];
constructor(
mainContext: IMainContext,
logService: ILogService
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@ILogService logService: ILogService
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadCommands);
this._onDidExecuteCommand = new Emitter<vscode.CommandExecutionEvent>({
onFirstListenerDidAdd: () => this._proxy.$registerCommandListener(),
onLastListenerRemove: () => this._proxy.$unregisterCommandListener(),
});
this.onDidExecuteCommand = Event.filter(this._onDidExecuteCommand.event, e => e.command[0] !== '_'); // filter 'private' commands
this._proxy = extHostRpc.getProxy(MainContext.MainThreadCommands);
this._logService = logService;
this._converter = new CommandsConverter(this);
this._argumentProcessors = [
@@ -115,13 +110,6 @@ export class ExtHostCommands implements ExtHostCommandsShape {
});
}
$handleDidExecuteCommand(command: ICommandEvent): void {
this._onDidExecuteCommand.fire({
command: command.commandId,
arguments: command.args.map(arg => this._argumentProcessors.reduce((r, p) => p.processArgument(r), arg))
});
}
executeCommand<T>(id: string, ...args: any[]): Promise<T> {
this._logService.trace('ExtHostCommands#executeCommand', id);
@@ -170,7 +158,6 @@ export class ExtHostCommands implements ExtHostCommandsShape {
try {
const result = callback.apply(thisArg, args);
this._onDidExecuteCommand.fire({ command: id, arguments: args });
return Promise.resolve(result);
} catch (err) {
this._logService.error(err, id);
@@ -227,13 +214,13 @@ export class CommandsConverter {
this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this);
}
toInternal(command: vscode.Command | undefined, disposables: DisposableStore): CommandDto | undefined {
toInternal(command: vscode.Command | undefined, disposables: DisposableStore): ICommandDto | undefined {
if (!command) {
return undefined;
}
const result: CommandDto = {
const result: ICommandDto = {
$ident: undefined,
id: command.command,
title: command.title,
@@ -281,3 +268,6 @@ export class CommandsConverter {
}
}
export interface IExtHostCommands extends ExtHostCommands { }
export const IExtHostCommands = createDecorator<IExtHostCommands>('IExtHostCommands');

View File

@@ -253,13 +253,13 @@ export class ExtHostCommentThread implements vscode.CommentThread {
return this._range;
}
private _label: string;
private _label: string | undefined;
get label(): string {
get label(): string | undefined {
return this._label;
}
set label(label: string) {
set label(label: string | undefined) {
this._label = label;
this._onDidUpdateCommentThread.fire();
}

View File

@@ -7,8 +7,8 @@ import { mixin, deepClone } from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import * as vscode from 'vscode';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from './extHost.protocol';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData, IConfigurationInitData, MainContext } from './extHost.protocol';
import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
@@ -18,6 +18,8 @@ import { ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/confi
import { isObject } from 'vs/base/common/types';
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';
function lookUp(tree: any, key: string) {
if (key) {
@@ -40,13 +42,18 @@ type ConfigurationInspect<T> = {
export class ExtHostConfiguration implements ExtHostConfigurationShape {
readonly _serviceBrand: any;
private readonly _proxy: MainThreadConfigurationShape;
private readonly _extHostWorkspace: ExtHostWorkspace;
private readonly _barrier: Barrier;
private _actual: ExtHostConfigProvider | null;
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace) {
this._proxy = proxy;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostWorkspace extHostWorkspace: IExtHostWorkspace
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadConfiguration);
this._extHostWorkspace = extHostWorkspace;
this._barrier = new Barrier();
this._actual = null;
@@ -263,8 +270,8 @@ export class ExtHostConfigProvider {
const defaultConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.defaults);
const userConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.user);
const workspaceConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.workspace);
const folders: ResourceMap<ConfigurationModel> = Object.keys(data.folders).reduce((result, key) => {
result.set(URI.parse(key), ExtHostConfigProvider.parseConfigurationModel(data.folders[key]));
const folders: ResourceMap<ConfigurationModel> = data.folders.reduce((result, value) => {
result.set(URI.revive(value[0]), ExtHostConfigProvider.parseConfigurationModel(value[1]));
return result;
}, new ResourceMap<ConfigurationModel>());
return new Configuration(defaultConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), false);
@@ -274,3 +281,6 @@ export class ExtHostConfigProvider {
return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze();
}
}
export const IExtHostConfiguration = createDecorator<IExtHostConfiguration>('IExtHostConfiguration');
export interface IExtHostConfiguration extends ExtHostConfiguration { }

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { ExtHostDebugServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import * as vscode from 'vscode';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IExtHostDebugService = createDecorator<IExtHostDebugService>('IExtHostDebugService');
export interface IExtHostDebugService extends ExtHostDebugServiceShape {
readonly _serviceBrand: any;
onDidStartDebugSession: Event<vscode.DebugSession>;
onDidTerminateDebugSession: Event<vscode.DebugSession>;
onDidChangeActiveDebugSession: Event<vscode.DebugSession | undefined>;
activeDebugSession: vscode.DebugSession | undefined;
activeDebugConsole: vscode.DebugConsole;
onDidReceiveDebugSessionCustomEvent: Event<vscode.DebugSessionCustomEvent>;
onDidChangeBreakpoints: Event<vscode.BreakpointsChangeEvent>;
breakpoints: vscode.Breakpoint[];
addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession): Promise<boolean>;
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable;
registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable;
registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable;
}

View File

@@ -5,26 +5,31 @@
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { MainContext, IMainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, DecorationData, DecorationRequest, DecorationReply } from 'vs/workbench/api/common/extHost.protocol';
import { MainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, DecorationData, DecorationRequest, DecorationReply } from 'vs/workbench/api/common/extHost.protocol';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { CancellationToken } from 'vs/base/common/cancellation';
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';
interface ProviderData {
provider: vscode.DecorationProvider;
extensionId: ExtensionIdentifier;
}
export class ExtHostDecorations implements ExtHostDecorationsShape {
export class ExtHostDecorations implements IExtHostDecorations {
private static _handlePool = 0;
readonly _serviceBrand: undefined;
private readonly _provider = new Map<number, ProviderData>();
private readonly _proxy: MainThreadDecorationsShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadDecorations);
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadDecorations);
}
registerDecorationProvider(provider: vscode.DecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable {
@@ -70,3 +75,6 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
});
}
}
export const IExtHostDecorations = createDecorator<IExtHostDecorations>('IExtHostDecorations');
export interface IExtHostDecorations extends ExtHostDecorations, ExtHostDecorationsShape { }

View File

@@ -5,7 +5,7 @@
import { localize } from 'vs/nls';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as vscode from 'vscode';
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
import { DiagnosticSeverity } from './extHostTypes';
@@ -20,12 +20,12 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
private readonly _owner: string;
private readonly _maxDiagnosticsPerFile: number;
private readonly _onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>;
private readonly _proxy: MainThreadDiagnosticsShape;
private readonly _proxy: MainThreadDiagnosticsShape | undefined;
private _isDisposed = false;
private _data = new Map<string, vscode.Diagnostic[]>();
constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape | undefined, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
this._name = name;
this._owner = owner;
this._maxDiagnosticsPerFile = maxDiagnosticsPerFile;
@@ -36,7 +36,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
dispose(): void {
if (!this._isDisposed) {
this._onDidChangeDiagnostics.fire(keys(this._data));
this._proxy.$clear(this._owner);
if (this._proxy) {
this._proxy.$clear(this._owner);
}
this._data = undefined!;
this._isDisposed = true;
}
@@ -112,6 +114,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
this._onDidChangeDiagnostics.fire(toSync);
// compute change and send to main side
if (!this._proxy) {
return;
}
const entries: [URI, IMarkerData[]][] = [];
for (let uri of toSync) {
let marker: IMarkerData[] = [];
@@ -149,7 +154,6 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
entries.push([uri, marker]);
}
this._proxy.$changeMany(this._owner, entries);
}
@@ -157,14 +161,18 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
this._checkDisposed();
this._onDidChangeDiagnostics.fire([uri]);
this._data.delete(uri.toString());
this._proxy.$changeMany(this._owner, [[uri, undefined]]);
if (this._proxy) {
this._proxy.$changeMany(this._owner, [[uri, undefined]]);
}
}
clear(): void {
this._checkDisposed();
this._onDidChangeDiagnostics.fire(keys(this._data));
this._data.clear();
this._proxy.$clear(this._owner);
if (this._proxy) {
this._proxy.$clear(this._owner);
}
}
forEach(callback: (uri: URI, diagnostics: ReadonlyArray<vscode.Diagnostic>, collection: DiagnosticCollection) => any, thisArg?: any): void {
@@ -311,4 +319,20 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
});
return res;
}
private _mirrorCollection: vscode.DiagnosticCollection | undefined;
$acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void {
if (!this._mirrorCollection) {
const name = '_generated_mirror';
const collection = new DiagnosticCollection(name, name, ExtHostDiagnostics._maxDiagnosticsPerFile, undefined, this._onDidChangeDiagnostics);
this._collections.set(name, collection);
this._mirrorCollection = collection;
}
for (const [uri, markers] of data) {
this._mirrorCollection.set(URI.revive(uri), markers.map(converter.Diagnostic.to));
}
}
}

View File

@@ -50,7 +50,7 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro
}
if (this._documentsAndEditors.getDocument(uri)) {
this.$provideTextDocumentContent(handle, uri).then(value => {
if (!value) {
if (!value && typeof value !== 'string') {
return;
}

View File

@@ -26,7 +26,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
private _proxy: MainThreadDocumentsShape;
private _languageId: string;
private _isDirty: boolean;
private _document: vscode.TextDocument;
private _document?: vscode.TextDocument;
private _textLines: vscode.TextLine[] = [];
private _isDisposed: boolean = false;

View File

@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { sequence } from 'vs/base/common/async';
import { illegalState } from 'vs/base/common/errors';
import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, ResourceTextEditDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IResourceTextEditDto } from 'vs/workbench/api/common/extHost.protocol';
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';
@@ -142,7 +142,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
}).then(values => {
const resourceEdit: ResourceTextEditDto = {
const resourceEdit: IResourceTextEditDto = {
resource: document.uri,
edits: []
};
@@ -153,7 +153,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
resourceEdit.edits.push({
range: range && Range.from(range),
text: newText,
eol: EndOfLine.from(newEol)
eol: newEol && EndOfLine.from(newEol)
});
}
}

View File

@@ -7,17 +7,21 @@ import * as assert from 'vs/base/common/assert';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {
readonly _serviceBrand: any;
private _disposables: Disposable[] = [];
private _activeEditorId: string | null;
private _activeEditorId: string | null = null;
private readonly _editors = new Map<string, ExtHostTextEditor>();
private readonly _documents = new Map<string, ExtHostDocumentData>();
@@ -33,7 +37,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
readonly onDidChangeActiveTextEditor: Event<ExtHostTextEditor | undefined> = this._onDidChangeActiveTextEditor.event;
constructor(
private readonly _mainContext: IMainContext,
@IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService,
) { }
dispose() {
@@ -64,7 +68,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
assert.ok(!this._documents.has(resource.toString()), `document '${resource} already exists!'`);
const documentData = new ExtHostDocumentData(
this._mainContext.getProxy(MainContext.MainThreadDocuments),
this._extHostRpc.getProxy(MainContext.MainThreadDocuments),
resource,
data.lines,
data.EOL,
@@ -95,7 +99,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
const documentData = this._documents.get(resource.toString())!;
const editor = new ExtHostTextEditor(
this._mainContext.getProxy(MainContext.MainThreadTextEditors),
this._extHostRpc.getProxy(MainContext.MainThreadTextEditors),
data.id,
documentData,
data.selections.map(typeConverters.Selection.to),
@@ -159,3 +163,6 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
return result;
}
}
export interface IExtHostDocumentsAndEditors extends ExtHostDocumentsAndEditors { }
export const IExtHostDocumentsAndEditors = createDecorator<IExtHostDocumentsAndEditors>('IExtHostDocumentsAndEditors');

View File

@@ -0,0 +1,779 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import { originalFSPath, joinPath } from 'vs/base/common/resources';
import { Barrier } from 'vs/base/common/async';
import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtHostExtensionServiceShape, IInitData, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Schemas } from 'vs/base/common/network';
import { VSBuffer } from 'vs/base/common/buffer';
import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { RemoteAuthorityResolverError, ExtensionExecutionContext } from 'vs/workbench/api/common/extHostTypes';
import { ResolvedAuthority, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
interface ITestRunner {
/** Old test runner API, as exported from `vscode/lib/testrunner` */
run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}
interface INewTestRunner {
/** New test runner API, as explained in the extension test doc */
run(): Promise<void>;
}
export const IHostUtils = createDecorator<IHostUtils>('IHostUtils');
export interface IHostUtils {
_serviceBrand: undefined;
exit(code?: number): void;
exists(path: string): Promise<boolean>;
realpath(path: string): Promise<string>;
}
type TelemetryActivationEventFragment = {
id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
extensionVersion: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
activationEvents: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
reason: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
export abstract class AbstractExtHostExtensionService implements ExtHostExtensionServiceShape {
readonly _serviceBrand: any;
private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;
protected readonly _hostUtils: IHostUtils;
protected readonly _initData: IInitData;
protected readonly _extHostContext: IExtHostRpcService;
protected readonly _instaService: IInstantiationService;
protected readonly _extHostWorkspace: ExtHostWorkspace;
protected readonly _extHostConfiguration: ExtHostConfiguration;
protected readonly _logService: ILogService;
protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;
protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
protected readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;
private readonly _almostReadyToRunExtensions: Barrier;
private readonly _readyToStartExtensionHost: Barrier;
private readonly _readyToRunExtensions: Barrier;
protected readonly _registry: ExtensionDescriptionRegistry;
private readonly _storage: ExtHostStorage;
private readonly _storagePath: IExtensionStoragePaths;
private readonly _activator: ExtensionsActivator;
private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>> | null;
private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; };
private _started: boolean;
private readonly _disposables: DisposableStore;
constructor(
@IInstantiationService instaService: IInstantiationService,
@IHostUtils hostUtils: IHostUtils,
@IExtHostRpcService extHostContext: IExtHostRpcService,
@IExtHostWorkspace extHostWorkspace: IExtHostWorkspace,
@IExtHostConfiguration extHostConfiguration: IExtHostConfiguration,
@ILogService logService: ILogService,
@IExtHostInitDataService initData: IExtHostInitDataService,
@IExtensionStoragePaths storagePath: IExtensionStoragePaths
) {
this._hostUtils = hostUtils;
this._extHostContext = extHostContext;
this._initData = initData;
this._extHostWorkspace = extHostWorkspace;
this._extHostConfiguration = extHostConfiguration;
this._logService = logService;
this._disposables = new DisposableStore();
this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);
this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._almostReadyToRunExtensions = new Barrier();
this._readyToStartExtensionHost = new Barrier();
this._readyToRunExtensions = new Barrier();
this._registry = new ExtensionDescriptionRegistry(this._initData.extensions);
this._storage = new ExtHostStorage(this._extHostContext);
this._storagePath = storagePath;
this._instaService = instaService.createChild(new ServiceCollection(
[IExtHostStorage, this._storage]
));
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);
},
actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) {
const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent);
return new HostExtension();
}
const extensionDescription = this._registry.getExtensionDescription(extensionId)!;
return this._activateExtension(extensionDescription, reason);
}
});
this._extensionPathIndex = null;
this._resolvers = Object.create(null);
this._started = false;
}
public async initialize(): Promise<void> {
try {
await this._beforeAlmostReadyToRunExtensions();
this._almostReadyToRunExtensions.open();
await this._extHostWorkspace.waitForInitializeCall();
this._readyToStartExtensionHost.open();
if (this._initData.autoStart) {
this._startExtensionHost();
}
} catch (err) {
errors.onUnexpectedError(err);
}
}
protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
public async deactivateAll(): Promise<void> {
let allPromises: Promise<void>[] = [];
try {
const allExtensions = this._registry.getAllExtensionDescriptions();
const allExtensionsIds = allExtensions.map(ext => ext.identifier);
const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));
allPromises = activatedExtensions.map((extensionId) => {
return this._deactivate(extensionId);
});
} catch (err) {
// TODO: write to log once we have one
}
await allPromises;
}
public isActivated(extensionId: ExtensionIdentifier): boolean {
if (this._readyToRunExtensions.isOpen()) {
return this._activator.isActivated(extensionId);
}
return false;
}
private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
const reason = new ExtensionActivatedByEvent(startup, activationEvent);
return this._activator.activateByEvent(activationEvent, reason);
}
private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
return this._activator.activateById(extensionId, reason);
}
public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
return this._activateById(extensionId, reason).then(() => {
const extension = this._activator.getActivatedExtension(extensionId);
if (extension.activationFailed) {
// activation failed => bubble up the error as the promise result
return Promise.reject(extension.activationFailedError);
}
return undefined;
});
}
public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
return this._readyToRunExtensions.wait().then(_ => this._registry);
}
public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {
if (this._readyToRunExtensions.isOpen()) {
return this._activator.getActivatedExtension(extensionId).exports;
} else {
return null;
}
}
// create trie to enable fast 'filename -> extension id' look up
public getExtensionPathIndex(): Promise<TernarySearchTree<IExtensionDescription>> {
if (!this._extensionPathIndex) {
const tree = TernarySearchTree.forPaths<IExtensionDescription>();
const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
if (!ext.main) {
return undefined;
}
return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
});
this._extensionPathIndex = Promise.all(extensions).then(() => tree);
}
return this._extensionPathIndex;
}
private _deactivate(extensionId: ExtensionIdentifier): Promise<void> {
let result = Promise.resolve(undefined);
if (!this._readyToRunExtensions.isOpen()) {
return result;
}
if (!this._activator.isActivated(extensionId)) {
return result;
}
const extension = this._activator.getActivatedExtension(extensionId);
if (!extension) {
return result;
}
// call deactivate if available
try {
if (typeof extension.module.deactivate === 'function') {
result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => {
// TODO: Do something with err if this is not the shutdown case
return Promise.resolve(undefined);
});
}
} catch (err) {
// TODO: Do something with err if this is not the shutdown case
}
// clean up subscriptions
try {
dispose(extension.subscriptions);
} catch (err) {
// TODO: Do something with err if this is not the shutdown case
}
return result;
}
// --- impl
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
const activationTimes = activatedExtension.activationTimes;
const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
return activatedExtension;
}, (err) => {
this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
throw err;
});
}
private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
const event = getTelemetryActivationEvent(extensionDescription, reason);
type ExtensionActivationTimesClassification = {
outcome: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
} & TelemetryActivationEventFragment & ExtensionActivationTimesFragment;
type ExtensionActivationTimesEvent = {
outcome: string
} & ActivationTimesEvent & TelemetryActivationEvent;
type ActivationTimesEvent = {
startup?: boolean;
codeLoadingTime?: number;
activateCallTime?: number;
activateResolvedTime?: number;
};
this._mainThreadTelemetryProxy.$publicLog2<ExtensionActivationTimesEvent, ExtensionActivationTimesClassification>('extensionActivationTimes', {
...event,
...(activationTimes || {}),
outcome
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
const event = getTelemetryActivationEvent(extensionDescription, reason);
type ActivatePluginClassification = {} & TelemetryActivationEventFragment;
this._mainThreadTelemetryProxy.$publicLog2<TelemetryActivationEvent, ActivatePluginClassification>('activatePlugin', event);
if (!extensionDescription.main) {
// Treat the extension as being empty => NOT AN ERROR CASE
return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
}
this._logService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`);
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return Promise.all<any>([
this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
});
}
protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<vscode.ExtensionContext> {
const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
return Promise.all([
globalState.whenReady,
workspaceState.whenReady,
this._storagePath.whenReady
]).then(() => {
const that = this;
return Object.freeze(<IExtensionContext>{
globalState,
workspaceState,
subscriptions: [],
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
get storagePath() { return that._storagePath.workspaceValue(extensionDescription); },
get globalStoragePath() { return that._storagePath.globalValue(extensionDescription); },
asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); },
executionContext: this._initData.remote.isRemote ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local,
});
});
}
private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
// Make sure the extension's surface is not undefined
extensionModule = extensionModule || {
activate: undefined,
deactivate: undefined
};
return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {
return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
});
}
private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
if (typeof extensionModule.activate === 'function') {
try {
activationTimesBuilder.activateCallStart();
logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);
const scope = typeof global === 'object' ? global : self; // `global` is nodejs while `self` is for workers
const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(scope, [context]);
activationTimesBuilder.activateCallStop();
activationTimesBuilder.activateResolveStart();
return Promise.resolve(activateResult).then((value) => {
activationTimesBuilder.activateResolveStop();
return value;
});
} catch (err) {
return Promise.reject(err);
}
} else {
// No activate found => the module is the extension's exports
return Promise.resolve<IExtensionAPI>(extensionModule);
}
}
// -- eager activation
// Handle "eager" activation extensions
private _handleEagerExtensions(): Promise<void> {
this._activateByEvent('*', true).then(undefined, (err) => {
console.error(err);
});
this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added)));
const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : [];
return this._handleWorkspaceContainsEagerExtensions(folders);
}
private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray<vscode.WorkspaceFolder>): Promise<void> {
if (folders.length === 0) {
return Promise.resolve(undefined);
}
return Promise.all(
this._registry.getAllExtensionDescriptions().map((desc) => {
return this._handleWorkspaceContainsEagerExtension(folders, desc);
})
).then(() => { });
}
private _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray<vscode.WorkspaceFolder>, desc: IExtensionDescription): Promise<void> {
const activationEvents = desc.activationEvents;
if (!activationEvents) {
return Promise.resolve(undefined);
}
if (this.isActivated(desc.identifier)) {
return Promise.resolve(undefined);
}
const fileNames: string[] = [];
const globPatterns: string[] = [];
for (const activationEvent of activationEvents) {
if (/^workspaceContains:/.test(activationEvent)) {
const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length);
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
globPatterns.push(fileNameOrGlob);
} else {
fileNames.push(fileNameOrGlob);
}
}
}
if (fileNames.length === 0 && globPatterns.length === 0) {
return Promise.resolve(undefined);
}
const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(folders, desc.identifier, fileName))).then(() => { });
const globPatternPromise = this._activateIfGlobPatterns(folders, desc.identifier, globPatterns);
return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
}
private async _activateIfFileName(folders: ReadonlyArray<vscode.WorkspaceFolder>, extensionId: ExtensionIdentifier, fileName: string): Promise<void> {
// find exact path
for (const { uri } of folders) {
if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) {
// the file was found
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`))
.then(undefined, err => console.error(err))
);
}
}
return undefined;
}
private async _activateIfGlobPatterns(folders: ReadonlyArray<vscode.WorkspaceFolder>, extensionId: ExtensionIdentifier, globPatterns: string[]): Promise<void> {
this._logService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`);
if (globPatterns.length === 0) {
return Promise.resolve(undefined);
}
const tokenSource = new CancellationTokenSource();
const searchP = this._mainThreadWorkspaceProxy.$checkExists(folders.map(folder => folder.uri), globPatterns, tokenSource.token);
const timer = setTimeout(async () => {
tokenSource.cancel();
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`))
.then(undefined, err => console.error(err));
}, AbstractExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);
let exists: boolean = false;
try {
exists = await searchP;
} catch (err) {
if (!errors.isPromiseCanceledError(err)) {
console.error(err);
}
}
tokenSource.dispose();
clearTimeout(timer);
if (exists) {
// a file was found matching one of the glob patterns
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`))
.then(undefined, err => console.error(err))
);
}
return Promise.resolve(undefined);
}
private _handleExtensionTests(): Promise<void> {
return this._doHandleExtensionTests().then(undefined, error => {
console.error(error); // ensure any error message makes it onto the console
return Promise.reject(error);
});
}
private async _doHandleExtensionTests(): Promise<void> {
const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) {
return Promise.resolve(undefined);
}
const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
// Require the test runner via node require from the provided path
let testRunner: ITestRunner | INewTestRunner | undefined;
let requireError: Error | undefined;
try {
testRunner = await this._loadCommonJSModule(URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false));
} catch (error) {
requireError = error;
}
// Execute the runner if it follows the old `run` spec
if (testRunner && typeof testRunner.run === 'function') {
return new Promise<void>((c, e) => {
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
if (error) {
e(error.toString());
} else {
c(undefined);
}
// after tests have run, we shutdown the host
this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
};
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
// Using the new API `run(): Promise<void>`
if (runResult && runResult.then) {
runResult
.then(() => {
c();
this._gracefulExit(0);
})
.catch((err: Error) => {
e(err.toString());
this._gracefulExit(1);
});
}
});
}
// Otherwise make sure to shutdown anyway even in case of an error
else {
this._gracefulExit(1 /* ERROR */);
}
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
}
private _gracefulExit(code: number): void {
// to give the PH process a chance to flush any outstanding console
// messages to the main process, we delay the exit() by some time
setTimeout(() => {
// If extension tests are running, give the exit code to the renderer
if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) {
this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
return;
}
this._hostUtils.exit(code);
}, 500);
}
private _startExtensionHost(): Promise<void> {
if (this._started) {
throw new Error(`Extension host is already started!`);
}
this._started = true;
return this._readyToStartExtensionHost.wait()
.then(() => this._readyToRunExtensions.open())
.then(() => this._handleEagerExtensions())
.then(() => this._handleExtensionTests())
.then(() => {
this._logService.info(`eager extensions activated`);
});
}
// -- called by extensions
public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {
this._resolvers[authorityPrefix] = resolver;
return toDisposable(() => {
delete this._resolvers[authorityPrefix];
});
}
// -- called by main thread
public async $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
throw new Error(`Not an authority that can be resolved!`);
}
const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex);
await this._almostReadyToRunExtensions.wait();
await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false);
const resolver = this._resolvers[authorityPrefix];
if (!resolver) {
throw new Error(`No remote extension installed to resolve ${authorityPrefix}.`);
}
try {
const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
// Split merged API result into separate authority/options
const authority: ResolvedAuthority = {
authority: remoteAuthority,
host: result.host,
port: result.port
};
const options: ResolvedOptions = {
extensionHostEnv: result.extensionHostEnv
};
return {
type: 'ok',
value: {
authority,
options
}
};
} catch (err) {
if (err instanceof RemoteAuthorityResolverError) {
return {
type: 'error',
error: {
code: err._code,
message: err._message,
detail: err._detail
}
};
}
throw err;
}
}
public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
this._registry.keepOnly(enabledExtensionIds);
return this._startExtensionHost();
}
public $activateByEvent(activationEvent: string): Promise<void> {
return (
this._readyToRunExtensions.wait()
.then(_ => this._activateByEvent(activationEvent, false))
);
}
public async $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean> {
await this._readyToRunExtensions.wait();
if (!this._registry.getExtensionDescription(extensionId)) {
// unknown extension => ignore
return false;
}
await this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent));
return true;
}
public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
const trie = await this.getExtensionPathIndex();
await Promise.all(toRemove.map(async (extensionId) => {
const extensionDescription = this._registry.getExtensionDescription(extensionId);
if (!extensionDescription) {
return;
}
const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath);
trie.delete(URI.file(realpathValue).fsPath);
}));
await Promise.all(toAdd.map(async (extensionDescription) => {
const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath);
trie.set(URI.file(realpathValue).fsPath, extensionDescription);
}));
this._registry.deltaExtensions(toAdd, toRemove);
return Promise.resolve(undefined);
}
public async $test_latency(n: number): Promise<number> {
return n;
}
public async $test_up(b: VSBuffer): Promise<number> {
return b.byteLength;
}
public async $test_down(size: number): Promise<VSBuffer> {
let buff = VSBuffer.alloc(size);
let value = Math.random() % 256;
for (let i = 0; i < size; i++) {
buff.writeUInt8(value, i);
}
return buff;
}
public abstract async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
}
type TelemetryActivationEvent = {
id: string;
name: string;
extensionVersion: string;
publisherDisplayName: string;
activationEvents: string | null;
isBuiltin: boolean;
reason: string;
};
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent {
const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent :
reason instanceof ExtensionActivatedByAPI ? 'api' :
'';
const event = {
id: extensionDescription.identifier.value,
name: extensionDescription.name,
extensionVersion: extensionDescription.version,
publisherDisplayName: extensionDescription.publisher,
activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
isBuiltin: extensionDescription.isBuiltin,
reason: reasonStr
};
return event;
}
export const IExtHostExtensionService = createDecorator<IExtHostExtensionService>('IExtHostExtensionService');
export interface IExtHostExtensionService extends AbstractExtHostExtensionService {
_serviceBrand: any;
initialize(): Promise<void>;
isActivated(extensionId: ExtensionIdentifier): boolean;
activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
deactivateAll(): Promise<void>;
getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined;
getExtensionRegistry(): Promise<ExtensionDescriptionRegistry>;
getExtensionPathIndex(): Promise<TernarySearchTree<IExtensionDescription>>;
registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable;
}

View File

@@ -155,7 +155,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _usedSchemes = new Set<string>();
private readonly _watches = new Map<number, IDisposable>();
private _linkProviderRegistration: IDisposable;
private _linkProviderRegistration?: IDisposable;
private _handlePool: number = 0;
readonly fileSystem: vscode.FileSystem;

View File

@@ -9,7 +9,7 @@ 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, ResourceFileEditDto, ResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, IResourceFileEditDto, IResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol';
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@@ -169,7 +169,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
}
// flatten all WorkspaceEdits collected via waitUntil-call
// and apply them in one go.
const allEdits = new Array<Array<ResourceFileEditDto | ResourceTextEditDto>>();
const allEdits = new Array<Array<IResourceFileEditDto | IResourceTextEditDto>>();
for (let edit of edits) {
if (edit) { // sparse array
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);

View File

@@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInitData } from './extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IExtHostInitDataService = createDecorator<IExtHostInitDataService>('IExtHostInitDataService');
export interface IExtHostInitDataService extends Readonly<IInitData> {
_serviceBrand: undefined;
}

View File

@@ -14,7 +14,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
import { asPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, SuggestDataDto, LinksListDto, ChainedCacheId, CodeLensListDto, CodeActionListDto, SignatureHelpDto, SignatureHelpContextDto } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, IRawColorInfo, IMainContext, IdObject, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILanguageConfigurationDto, IWorkspaceSymbolDto, ISuggestResultDto, IWorkspaceSymbolsDto, ICodeActionDto, IDocumentFilterDto, IWorkspaceEditDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICodeLensDto, ISuggestDataDto, ILinksListDto, ChainedCacheId, ICodeLensListDto, ICodeActionListDto, ISignatureHelpDto, ISignatureHelpContextDto } from './extHost.protocol';
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
@@ -111,7 +111,7 @@ class CodeLensAdapter {
private readonly _provider: vscode.CodeLensProvider
) { }
provideCodeLenses(resource: URI, token: CancellationToken): Promise<CodeLensListDto | undefined> {
provideCodeLenses(resource: URI, token: CancellationToken): Promise<ICodeLensListDto | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => {
@@ -124,7 +124,7 @@ class CodeLensAdapter {
const disposables = new DisposableStore();
this._disposables.set(cacheId, disposables);
const result: CodeLensListDto = {
const result: ICodeLensListDto = {
cacheId,
lenses: [],
};
@@ -141,7 +141,7 @@ class CodeLensAdapter {
});
}
resolveCodeLens(symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
resolveCodeLens(symbol: ICodeLensDto, token: CancellationToken): Promise<ICodeLensDto | undefined> {
const lens = symbol.cacheId && this._cache.get(...symbol.cacheId);
if (!lens) {
@@ -308,7 +308,7 @@ class ReferenceAdapter {
}
}
export interface CustomCodeAction extends CodeActionDto {
export interface CustomCodeAction extends ICodeActionDto {
_isSynthetic?: boolean;
}
@@ -327,7 +327,7 @@ class CodeActionAdapter {
private readonly _extensionId: ExtensionIdentifier
) { }
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionListDto | undefined> {
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<ICodeActionListDto | undefined> {
const doc = this._documents.getDocument(resource);
const ran = Selection.isISelection(rangeOrSelection)
@@ -391,7 +391,7 @@ class CodeActionAdapter {
}
}
return <CodeActionListDto>{ cacheId, actions };
return <ICodeActionListDto>{ cacheId, actions };
});
}
@@ -480,8 +480,8 @@ class NavigateTypeAdapter {
this._provider = provider;
}
provideWorkspaceSymbols(search: string, token: CancellationToken): Promise<WorkspaceSymbolsDto> {
const result: WorkspaceSymbolsDto = IdObject.mixin({ symbols: [] });
provideWorkspaceSymbols(search: string, token: CancellationToken): Promise<IWorkspaceSymbolsDto> {
const result: IWorkspaceSymbolsDto = IdObject.mixin({ symbols: [] });
return asPromise(() => this._provider.provideWorkspaceSymbols(search, token)).then(value => {
if (isNonEmptyArray(value)) {
for (const item of value) {
@@ -506,7 +506,7 @@ class NavigateTypeAdapter {
});
}
resolveWorkspaceSymbol(symbol: WorkspaceSymbolDto, token: CancellationToken): Promise<WorkspaceSymbolDto | undefined> {
resolveWorkspaceSymbol(symbol: IWorkspaceSymbolDto, token: CancellationToken): Promise<IWorkspaceSymbolDto | undefined> {
if (typeof this._provider.resolveWorkspaceSymbol !== 'function') {
return Promise.resolve(symbol);
@@ -543,7 +543,7 @@ class RenameAdapter {
private readonly _provider: vscode.RenameProvider
) { }
provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto | undefined> {
provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto | undefined> {
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
@@ -556,10 +556,10 @@ class RenameAdapter {
}, err => {
const rejectReason = RenameAdapter._asMessage(err);
if (rejectReason) {
return <WorkspaceEditDto>{ rejectReason, edits: undefined! };
return <IWorkspaceEditDto>{ rejectReason, edits: undefined! };
} else {
// generic error
return Promise.reject<WorkspaceEditDto>(err);
return Promise.reject<IWorkspaceEditDto>(err);
}
});
}
@@ -633,7 +633,7 @@ class SuggestAdapter {
this._provider = provider;
}
provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto | undefined> {
provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<ISuggestResultDto | undefined> {
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
@@ -662,7 +662,7 @@ class SuggestAdapter {
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos))
.with({ end: pos });
const result: SuggestResultDto = {
const result: ISuggestResultDto = {
x: pid,
b: [],
a: typeConvert.Range.from(wordRangeBeforePos),
@@ -682,7 +682,7 @@ class SuggestAdapter {
});
}
resolveCompletionItem(_resource: URI, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<SuggestDataDto | undefined> {
resolveCompletionItem(_resource: URI, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<ISuggestDataDto | undefined> {
if (typeof this._provider.resolveCompletionItem !== 'function') {
return Promise.resolve(undefined);
@@ -710,7 +710,7 @@ class SuggestAdapter {
this._cache.delete(id);
}
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: ChainedCacheId): SuggestDataDto | undefined {
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: ChainedCacheId): ISuggestDataDto | undefined {
if (typeof item.label !== 'string' || item.label.length === 0) {
console.warn('INVALID text edit -> must have at least a label');
return undefined;
@@ -721,7 +721,7 @@ class SuggestAdapter {
throw Error('DisposableStore is missing...');
}
const result: SuggestDataDto = {
const result: ISuggestDataDto = {
//
x: id,
//
@@ -778,7 +778,7 @@ class SignatureHelpAdapter {
private readonly _provider: vscode.SignatureHelpProvider,
) { }
provideSignatureHelp(resource: URI, position: IPosition, context: SignatureHelpContextDto, token: CancellationToken): Promise<SignatureHelpDto | undefined> {
provideSignatureHelp(resource: URI, position: IPosition, context: ISignatureHelpContextDto, token: CancellationToken): Promise<ISignatureHelpDto | undefined> {
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
const vscodeContext = this.reviveContext(context);
@@ -792,7 +792,7 @@ class SignatureHelpAdapter {
});
}
private reviveContext(context: SignatureHelpContextDto): vscode.SignatureHelpContext {
private reviveContext(context: ISignatureHelpContextDto): vscode.SignatureHelpContext {
let activeSignatureHelp: vscode.SignatureHelp | undefined = undefined;
if (context.activeSignatureHelp) {
const revivedSignatureHelp = typeConvert.SignatureHelp.to(context.activeSignatureHelp);
@@ -856,7 +856,7 @@ class LinkProviderAdapter {
private readonly _provider: vscode.DocumentLinkProvider
) { }
provideLinks(resource: URI, token: CancellationToken): Promise<LinksListDto | undefined> {
provideLinks(resource: URI, token: CancellationToken): Promise<ILinksListDto | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideDocumentLinks(doc, token)).then(links => {
@@ -878,9 +878,9 @@ class LinkProviderAdapter {
} else {
// cache links for future resolving
const pid = this._cache.add(links);
const result: LinksListDto = { links: [], id: pid };
const result: ILinksListDto = { links: [], id: pid };
for (let i = 0; i < links.length; i++) {
const dto: LinkDto = typeConvert.DocumentLink.from(links[i]);
const dto: ILinkDto = typeConvert.DocumentLink.from(links[i]);
dto.cacheId = [pid, i];
result.links.push(dto);
}
@@ -889,7 +889,7 @@ class LinkProviderAdapter {
});
}
resolveLink(id: ChainedCacheId, token: CancellationToken): Promise<LinkDto | undefined> {
resolveLink(id: ChainedCacheId, token: CancellationToken): Promise<ILinkDto | undefined> {
if (typeof this._provider.resolveDocumentLink !== 'function') {
return Promise.resolve(undefined);
}
@@ -1104,11 +1104,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
this._logService = logService;
}
private _transformDocumentSelector(selector: vscode.DocumentSelector): Array<ISerializedDocumentFilter> {
private _transformDocumentSelector(selector: vscode.DocumentSelector): Array<IDocumentFilterDto> {
return coalesce(asArray(selector).map(sel => this._doTransformDocumentSelector(sel)));
}
private _doTransformDocumentSelector(selector: string | vscode.DocumentFilter): ISerializedDocumentFilter | undefined {
private _doTransformDocumentSelector(selector: string | vscode.DocumentFilter): IDocumentFilterDto | undefined {
if (typeof selector === 'string') {
return {
$serialized: true,
@@ -1216,11 +1216,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return result;
}
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensListDto | undefined> {
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined);
}
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
$resolveCodeLens(handle: number, symbol: ICodeLensDto, token: CancellationToken): Promise<ICodeLensDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined);
}
@@ -1315,7 +1315,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
}
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionListDto | undefined> {
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<ICodeActionListDto | undefined> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context, token), undefined);
}
@@ -1363,11 +1363,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<WorkspaceSymbolsDto> {
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<IWorkspaceSymbolsDto> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search, token), { symbols: [] });
}
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Promise<WorkspaceSymbolDto | undefined> {
$resolveWorkspaceSymbol(handle: number, symbol: IWorkspaceSymbolDto, token: CancellationToken): Promise<IWorkspaceSymbolDto | undefined> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol, token), undefined);
}
@@ -1383,7 +1383,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto | undefined> {
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto | undefined> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName, token), undefined);
}
@@ -1399,11 +1399,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto | undefined> {
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<ISuggestResultDto | undefined> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined);
}
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<SuggestDataDto | undefined> {
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<ISuggestDataDto | undefined> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, id, token), undefined);
}
@@ -1414,7 +1414,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- parameter hints
registerSignatureHelpProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, metadataOrTriggerChars: string[] | vscode.SignatureHelpProviderMetadata): vscode.Disposable {
const metadata: ISerializedSignatureHelpProviderMetadata | undefined = Array.isArray(metadataOrTriggerChars)
const metadata: ISignatureHelpProviderMetadataDto | undefined = Array.isArray(metadataOrTriggerChars)
? { triggerCharacters: metadataOrTriggerChars, retriggerCharacters: [] }
: metadataOrTriggerChars;
@@ -1423,7 +1423,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: SignatureHelpContextDto, token: CancellationToken): Promise<SignatureHelpDto | undefined> {
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: ISignatureHelpContextDto, token: CancellationToken): Promise<ISignatureHelpDto | undefined> {
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token), undefined);
}
@@ -1439,11 +1439,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinksListDto | undefined> {
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<ILinksListDto | undefined> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token), undefined);
}
$resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<LinkDto | undefined> {
$resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<ILinkDto | undefined> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(id, token), undefined);
}
@@ -1505,14 +1505,14 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- configuration
private static _serializeRegExp(regExp: RegExp): ISerializedRegExp {
private static _serializeRegExp(regExp: RegExp): IRegExpDto {
return {
pattern: regExp.source,
flags: regExpFlags(regExp),
};
}
private static _serializeIndentationRule(indentationRule: vscode.IndentationRule): ISerializedIndentationRule {
private static _serializeIndentationRule(indentationRule: vscode.IndentationRule): IIndentationRuleDto {
return {
decreaseIndentPattern: ExtHostLanguageFeatures._serializeRegExp(indentationRule.decreaseIndentPattern),
increaseIndentPattern: ExtHostLanguageFeatures._serializeRegExp(indentationRule.increaseIndentPattern),
@@ -1521,7 +1521,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
};
}
private static _serializeOnEnterRule(onEnterRule: vscode.OnEnterRule): ISerializedOnEnterRule {
private static _serializeOnEnterRule(onEnterRule: vscode.OnEnterRule): IOnEnterRuleDto {
return {
beforeText: ExtHostLanguageFeatures._serializeRegExp(onEnterRule.beforeText),
afterText: onEnterRule.afterText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.afterText) : undefined,
@@ -1530,7 +1530,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
};
}
private static _serializeOnEnterRules(onEnterRules: vscode.OnEnterRule[]): ISerializedOnEnterRule[] {
private static _serializeOnEnterRules(onEnterRules: vscode.OnEnterRule[]): IOnEnterRuleDto[] {
return onEnterRules.map(ExtHostLanguageFeatures._serializeOnEnterRule);
}
@@ -1550,7 +1550,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
}
const handle = this._nextHandle();
const serializedConfiguration: ISerializedLanguageConfiguration = {
const serializedConfiguration: ILanguageConfigurationDto = {
comments: configuration.comments,
brackets: configuration.brackets,
wordPattern: configuration.wordPattern ? ExtHostLanguageFeatures._serializeRegExp(configuration.wordPattern) : undefined,

View File

@@ -1,34 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join } from 'vs/base/common/path';
import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log';
import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
private _logsPath: string;
readonly logFile: URI;
constructor(
delegate: ILogService,
logsPath: string,
) {
super(delegate);
this._logsPath = logsPath;
this.logFile = URI.file(join(logsPath, `${ExtensionHostLogFileName}.log`));
}
$setLevel(level: LogLevel): void {
this.setLevel(level);
}
getLogDirectory(extensionID: ExtensionIdentifier): string {
return join(this._logsPath, extensionID.value);
}
}

View File

@@ -14,7 +14,7 @@ export class ExtensionMemento implements IExtensionMemento {
private readonly _storage: ExtHostStorage;
private readonly _init: Promise<ExtensionMemento>;
private _value: { [n: string]: any; };
private _value?: { [n: string]: any; };
private readonly _storageListener: IDisposable;
constructor(id: string, global: boolean, storage: ExtHostStorage) {
@@ -41,7 +41,7 @@ export class ExtensionMemento implements IExtensionMemento {
get<T>(key: string): T | undefined;
get<T>(key: string, defaultValue: T): T;
get<T>(key: string, defaultValue?: T): T {
let value = this._value[key];
let value = this._value![key];
if (typeof value === 'undefined') {
value = defaultValue;
}
@@ -49,8 +49,8 @@ export class ExtensionMemento implements IExtensionMemento {
}
update(key: string, value: any): Promise<void> {
this._value[key] = value;
return this._storage.setValue(this._shared, this._id, this._value);
this._value![key] = value;
return this._storage.setValue(this._shared, this._id, this._value!);
}
dispose(): void {

View File

@@ -3,12 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadOutputServiceShape, IMainContext, ExtHostOutputServiceShape } from './extHost.protocol';
import { MainContext, MainThreadOutputServiceShape, ExtHostOutputServiceShape } from './extHost.protocol';
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export abstract class AbstractExtHostOutputChannel extends Disposable implements vscode.OutputChannel {
@@ -27,6 +29,7 @@ export abstract class AbstractExtHostOutputChannel extends Disposable implements
this._name = name;
this._proxy = proxy;
this._id = proxy.$register(this.name, log, file);
this._disposed = false;
this._offset = 0;
}
@@ -105,38 +108,50 @@ class ExtHostLogFileOutputChannel extends AbstractExtHostOutputChannel {
}
}
export interface IOutputChannelFactory {
createOutputChannel(name: string, logsLocation: URI, proxy: MainThreadOutputServiceShape): Promise<AbstractExtHostOutputChannel>;
}
export class LazyOutputChannel implements vscode.OutputChannel {
export const PushOutputChannelFactory = new class implements IOutputChannelFactory {
async createOutputChannel(name: string, _logsLocation: URI, proxy: MainThreadOutputServiceShape): Promise<AbstractExtHostOutputChannel> {
return new ExtHostPushOutputChannel(name, proxy);
constructor(
readonly name: string,
private readonly _channel: Promise<AbstractExtHostOutputChannel>
) { }
append(value: string): void {
this._channel.then(channel => channel.append(value));
}
};
appendLine(value: string): void {
this._channel.then(channel => channel.appendLine(value));
}
clear(): void {
this._channel.then(channel => channel.clear());
}
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
this._channel.then(channel => channel.show(columnOrPreserveFocus, preserveFocus));
}
hide(): void {
this._channel.then(channel => channel.hide());
}
dispose(): void {
this._channel.then(channel => channel.dispose());
}
}
export class ExtHostOutputService implements ExtHostOutputServiceShape {
private readonly _factory: IOutputChannelFactory;
private readonly _logsLocation: URI;
private readonly _proxy: MainThreadOutputServiceShape;
private readonly _channels: Map<string, AbstractExtHostOutputChannel> = new Map<string, AbstractExtHostOutputChannel>();
private _visibleChannelDisposable: IDisposable;
readonly _serviceBrand: any;
constructor(factory: IOutputChannelFactory, logsLocation: URI, mainContext: IMainContext) {
this._factory = factory;
this._logsLocation = logsLocation;
this._proxy = mainContext.getProxy(MainContext.MainThreadOutputService);
protected readonly _proxy: MainThreadOutputServiceShape;
protected readonly _channels: Map<string, AbstractExtHostOutputChannel> = new Map<string, AbstractExtHostOutputChannel>();
protected readonly _visibleChannelDisposable = new MutableDisposable();
constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadOutputService);
}
$setVisibleChannel(channelId: string): void {
if (this._visibleChannelDisposable) {
this._visibleChannelDisposable = dispose(this._visibleChannelDisposable);
}
if (channelId) {
const channel = this._channels.get(channelId);
if (channel) {
this._visibleChannelDisposable = channel.onDidAppend(() => channel.update());
this._visibleChannelDisposable.value = channel.onDidAppend(() => channel.update());
}
}
}
@@ -145,33 +160,8 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
} else {
const extHostOutputChannel = this._factory.createOutputChannel(name, this._logsLocation, this._proxy);
extHostOutputChannel.then(channel => channel._id.then(id => this._channels.set(id, channel)));
return {
get name(): string {
return name;
},
append(value: string): void {
extHostOutputChannel.then(channel => channel.append(value));
},
appendLine(value: string): void {
extHostOutputChannel.then(channel => channel.appendLine(value));
},
clear(): void {
extHostOutputChannel.then(channel => channel.clear());
},
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
extHostOutputChannel.then(channel => channel.show(columnOrPreserveFocus, preserveFocus));
},
hide(): void {
extHostOutputChannel.then(channel => channel.hide());
},
dispose(): void {
extHostOutputChannel.then(channel => channel.dispose());
}
};
}
return new ExtHostPushOutputChannel(name, this._proxy);
}
createOutputChannelFromLogFile(name: string, file: URI): vscode.OutputChannel {
@@ -185,3 +175,6 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
return new ExtHostLogFileOutputChannel(name, file, this._proxy);
}
}
export interface IExtHostOutputService extends ExtHostOutputService { }
export const IExtHostOutputService = createDecorator<IExtHostOutputService>('IExtHostOutputService');

View File

@@ -5,56 +5,64 @@
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { MainThreadKeytarShape, IEnvironment, MainThreadWindowShape, MainThreadTelemetryShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { MainThreadTelemetryShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { endsWith } from 'vs/base/common/strings';
import { IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { platform } from 'vs/base/common/process';
interface LoadFunction {
(request: string, parent: { filename: string; }, isMain: any): any;
(request: string): any;
}
interface INodeModuleFactory {
readonly nodeModuleName: string | string[];
load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any;
alternaiveModuleName?(name: string): string | undefined;
load(request: string, parent: URI, original: LoadFunction): any;
alternativeModuleName?(name: string): string | undefined;
}
export class NodeModuleRequireInterceptor {
public static INSTANCE = new NodeModuleRequireInterceptor();
export abstract class RequireInterceptor {
private readonly _factories: Map<string, INodeModuleFactory>;
private readonly _alternatives: ((moduleName: string) => string | undefined)[];
protected readonly _factories: Map<string, INodeModuleFactory>;
protected readonly _alternatives: ((moduleName: string) => string | undefined)[];
constructor() {
constructor(
private _apiFactory: IExtensionApiFactory,
private _extensionRegistry: ExtensionDescriptionRegistry,
@IInstantiationService private readonly _instaService: IInstantiationService,
@IExtHostConfiguration private readonly _extHostConfiguration: IExtHostConfiguration,
@IExtHostExtensionService private readonly _extHostExtensionService: IExtHostExtensionService,
@IExtHostInitDataService private readonly _initData: IExtHostInitDataService
) {
this._factories = new Map<string, INodeModuleFactory>();
this._alternatives = [];
this._installInterceptor(this._factories, this._alternatives);
}
private _installInterceptor(factories: Map<string, INodeModuleFactory>, alternatives: ((moduleName: string) => string | undefined)[]): void {
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
for (let alternativeModuleName of alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {
request = alternative;
break;
}
}
if (!factories.has(request)) {
return original.apply(this, arguments);
}
return factories.get(request)!.load(request, parent, isMain, original);
};
async install(): Promise<void> {
this._installInterceptor();
const configProvider = await this._extHostConfiguration.getConfigProvider();
const extensionPaths = await this._extHostExtensionService.getExtensionPathIndex();
this.register(new VSCodeNodeModuleFactory(this._apiFactory, extensionPaths, this._extensionRegistry, configProvider));
this.register(this._instaService.createInstance(KeytarNodeModuleFactory));
if (this._initData.remote.isRemote) {
this.register(this._instaService.createInstance(OpenNodeModuleFactory, extensionPaths));
}
}
protected abstract _installInterceptor(): void;
public register(interceptor: INodeModuleFactory): void {
if (Array.isArray(interceptor.nodeModuleName)) {
for (let moduleName of interceptor.nodeModuleName) {
@@ -63,19 +71,21 @@ export class NodeModuleRequireInterceptor {
} else {
this._factories.set(interceptor.nodeModuleName, interceptor);
}
if (typeof interceptor.alternaiveModuleName === 'function') {
if (typeof interceptor.alternativeModuleName === 'function') {
this._alternatives.push((moduleName) => {
return interceptor.alternaiveModuleName!(moduleName);
return interceptor.alternativeModuleName!(moduleName);
});
}
}
}
export class VSCodeNodeModuleFactory implements INodeModuleFactory {
//#region --- vscode-module
class VSCodeNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName = 'vscode';
private readonly _extApiImpl = new Map<string, typeof vscode>();
private _defaultApiImpl: typeof vscode;
private _defaultApiImpl?: typeof vscode;
constructor(
private readonly _apiFactory: IExtensionApiFactory,
@@ -85,10 +95,10 @@ export class VSCodeNodeModuleFactory implements INodeModuleFactory {
) {
}
public load(request: string, parent: { filename: string; }): any {
public load(_request: string, parent: URI): any {
// get extension id from filename and api for extension
const ext = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
const ext = this._extensionPaths.findSubstr(parent.fsPath);
if (ext) {
let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
@@ -102,13 +112,18 @@ export 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.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
console.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;
}
}
//#endregion
//#region --- keytar-module
interface IKeytarModule {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
@@ -116,16 +131,23 @@ interface IKeytarModule {
findPassword(service: string): Promise<string | null>;
}
export class KeytarNodeModuleFactory implements INodeModuleFactory {
class KeytarNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string = 'keytar';
private alternativeNames: Set<string> | undefined;
private _impl: IKeytarModule;
constructor(mainThreadKeytar: MainThreadKeytarShape, environment: IEnvironment) {
constructor(
@IExtHostRpcService rpcService: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
) {
const { environment } = initData;
const mainThreadKeytar = rpcService.getProxy(MainContext.MainThreadKeytar);
if (environment.appRoot) {
let appRoot = environment.appRoot.fsPath;
if (process.platform === 'win32') {
if (platform === 'win32') {
appRoot = appRoot.replace(/\\/g, '/');
}
if (appRoot[appRoot.length - 1] === '/') {
@@ -151,11 +173,11 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory {
};
}
public load(request: string, parent: { filename: string; }): any {
public load(_request: string, _parent: URI): any {
return this._impl;
}
public alternaiveModuleName(name: string): string | undefined {
public alternativeModuleName(name: string): string | undefined {
const length = name.length;
// We need at least something like: `?/keytar` which requires
// more than 7 characters.
@@ -173,6 +195,11 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory {
}
}
//#endregion
//#region --- opn/open-module
interface OpenOptions {
wait: boolean;
app: string | string[];
@@ -186,15 +213,23 @@ interface IOpenModule {
(target: string, options?: OpenOptions): Thenable<void>;
}
export class OpenNodeModuleFactory implements INodeModuleFactory {
class OpenNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string[] = ['open', 'opn'];
private _extensionId: string | undefined;
private _original: IOriginalOpen;
private _original?: IOriginalOpen;
private _impl: IOpenModule;
private _mainThreadTelemetry: MainThreadTelemetryShape;
constructor(
private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>,
@IExtHostRpcService rpcService: IExtHostRpcService,
) {
this._mainThreadTelemetry = rpcService.getProxy(MainContext.MainThreadTelemetry);
const mainThreadWindow = rpcService.getProxy(MainContext.MainThreadWindow);
constructor(mainThreadWindow: MainThreadWindowShape, private _mainThreadTelemerty: MainThreadTelemetryShape, private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>) {
this._impl = (target, options) => {
const uri: URI = URI.parse(target);
// If we have options use the original method.
@@ -210,21 +245,21 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
};
}
public load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any {
public load(request: string, parent: URI, original: LoadFunction): any {
// get extension id from filename and api for extension
const extension = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
const extension = this._extensionPaths.findSubstr(parent.fsPath);
if (extension) {
this._extensionId = extension.identifier.value;
this.sendShimmingTelemetry();
}
this._original = original(request, parent, isMain);
this._original = original(request);
return this._impl;
}
private callOriginal(target: string, options: OpenOptions | undefined): Thenable<any> {
this.sendNoForwardTelemetry();
return this._original(target, options);
return this._original!(target, options);
}
private sendShimmingTelemetry(): void {
@@ -234,7 +269,7 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
type ShimmingOpenClassification = {
extension: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
this._mainThreadTelemerty.$publicLog2<{ extension: string }, ShimmingOpenClassification>('shimming.open', { extension: this._extensionId });
this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenClassification>('shimming.open', { extension: this._extensionId });
}
private sendNoForwardTelemetry(): void {
@@ -244,6 +279,8 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
type ShimmingOpenCallNoForwardClassification = {
extension: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
this._mainThreadTelemerty.$publicLog2<{ extension: string }, ShimmingOpenCallNoForwardClassification>('shimming.open.call.noForward', { extension: this._extensionId });
this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenCallNoForwardClassification>('shimming.open.call.noForward', { extension: this._extensionId });
}
}
//#endregion

View File

@@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IExtHostRpcService = createDecorator<IExtHostRpcService>('IExtHostRpcService');
export interface IExtHostRpcService extends IRPCProtocol {
_serviceBrand: any;
}
export class ExtHostRpcService implements IExtHostRpcService {
readonly _serviceBrand: any;
readonly getProxy: <T>(identifier: ProxyIdentifier<T>) => T;
readonly set: <T, R extends T> (identifier: ProxyIdentifier<T>, instance: R) => R;
readonly assertRegistered: (identifiers: ProxyIdentifier<any>[]) => void;
constructor(rpcProtocol: IRPCProtocol) {
this.getProxy = rpcProtocol.getProxy.bind(rpcProtocol);
this.set = rpcProtocol.set.bind(rpcProtocol);
this.assertRegistered = rpcProtocol.assertRegistered.bind(rpcProtocol);
}
}

View File

@@ -9,7 +9,7 @@ import { debounce } from 'vs/base/common/decorators';
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { asPromise } from 'vs/base/common/async';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, CommandDto } from './extHost.protocol';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto } from './extHost.protocol';
import { sortedDiff } from 'vs/base/common/arrays';
import { comparePaths } from 'vs/base/common/comparers';
import * as vscode from 'vscode';
@@ -445,7 +445,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
this._statusBarCommands = statusBarCommands;
const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c, this._statusBarDisposables.value!)) as CommandDto[];
const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c, this._statusBarDisposables.value!)) as ICommandDto[];
this._proxy.$updateSourceControl(this.handle, { statusBarCommands: internal });
}

View File

@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import * as vscode from 'vscode';
import { ExtHostSearchShape } from '../common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface IExtHostSearch extends ExtHostSearchShape {
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable;
registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable;
}
export const IExtHostSearch = createDecorator<IExtHostSearch>('IExtHostSearch');

View File

@@ -3,8 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadStorageShape, IMainContext, ExtHostStorageShape } from './extHost.protocol';
import { MainContext, MainThreadStorageShape, ExtHostStorageShape } from './extHost.protocol';
import { Emitter } from 'vs/base/common/event';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface IStorageChangeEvent {
shared: boolean;
@@ -14,12 +16,14 @@ export interface IStorageChangeEvent {
export class ExtHostStorage implements ExtHostStorageShape {
readonly _serviceBrand: any;
private _proxy: MainThreadStorageShape;
private _onDidChangeStorage = new Emitter<IStorageChangeEvent>();
readonly onDidChangeStorage = this._onDidChangeStorage.event;
constructor(mainContext: IMainContext) {
constructor(mainContext: IExtHostRpcService) {
this._proxy = mainContext.getProxy(MainContext.MainThreadStorage);
}
@@ -35,3 +39,6 @@ export class ExtHostStorage implements ExtHostStorageShape {
this._onDidChangeStorage.fire({ shared, key, value });
}
}
export interface IExtHostStorage extends ExtHostStorage { }
export const IExtHostStorage = createDecorator<IExtHostStorage>('IExtHostStorage');

View File

@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IExtensionStoragePaths = createDecorator<IExtensionStoragePaths>('IExtensionStoragePaths');
export interface IExtensionStoragePaths {
_serviceBrand: any;
whenReady: Promise<any>;
workspaceValue(extension: IExtensionDescription): string | undefined;
globalValue(extension: IExtensionDescription): string;
}

View File

@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol';
import * as vscode from 'vscode';
import { TaskSystemInfoDTO } from '../common/shared/tasks';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface IExtHostTask extends ExtHostTaskShape {
readonly _serviceBrand: any;
taskExecutions: vscode.TaskExecution[];
onDidStartTask: Event<vscode.TaskStartEvent>;
onDidEndTask: Event<vscode.TaskEndEvent>;
onDidStartTaskProcess: Event<vscode.TaskProcessStartEvent>;
onDidEndTaskProcess: Event<vscode.TaskProcessEndEvent>;
registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable;
registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void;
fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]>;
executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution>;
terminateTask(execution: vscode.TaskExecution): Promise<void>;
}
export const IExtHostTask = createDecorator<IExtHostTask>('IExtHostTask');

View File

@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { Event } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
_serviceBrand: any;
activeTerminal: vscode.Terminal | undefined;
terminals: vscode.Terminal[];
onDidCloseTerminal: Event<vscode.Terminal>;
onDidOpenTerminal: Event<vscode.Terminal>;
onDidChangeActiveTerminal: Event<vscode.Terminal | undefined>;
onDidChangeTerminalDimensions: Event<vscode.TerminalDimensionsChangeEvent>;
onDidWriteTerminalData: Event<vscode.TerminalDataWriteEvent>;
createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal;
createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal;
createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal;
attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void;
getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
}
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');

View File

@@ -151,11 +151,11 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
private _proxy: MainThreadTextEditorsShape;
private _id: string;
private _tabSize: number;
private _indentSize: number;
private _insertSpaces: boolean;
private _cursorStyle: TextEditorCursorStyle;
private _lineNumbers: TextEditorLineNumbersStyle;
private _tabSize!: number;
private _indentSize!: number;
private _insertSpaces!: boolean;
private _cursorStyle!: TextEditorCursorStyle;
private _lineNumbers!: TextEditorLineNumbersStyle;
constructor(proxy: MainThreadTextEditorsShape, id: string, source: IResolvedTextEditorConfiguration) {
this._proxy = proxy;

View File

@@ -13,7 +13,7 @@ import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.proto
import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { asPromise } from 'vs/base/common/async';
import { TreeItemCollapsibleState, ThemeIcon, MarkdownString } from 'vs/workbench/api/common/extHostTypes';
import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/common/extHostTypes';
import { isUndefinedOrNull, isString } from 'vs/base/common/types';
import { equals, coalesce } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
@@ -52,10 +52,21 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
private commands: ExtHostCommands,
private logService: ILogService
) {
function isTreeViewItemHandleArg(arg: any): boolean {
return arg && arg.$treeViewId && arg.$treeItemHandle;
}
commands.registerArgumentProcessor({
processArgument: arg => {
if (arg && arg.$treeViewId && arg.$treeItemHandle) {
if (isTreeViewItemHandleArg(arg)) {
return this.convertArgument(arg);
} else if (Array.isArray(arg) && (arg.length > 0)) {
return arg.map(item => {
if (isTreeViewItemHandleArg(item)) {
return this.convertArgument(item);
}
return item;
});
}
return arg;
}
@@ -81,7 +92,10 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
get visible() { return treeView.visible; },
get onDidChangeVisibility() { return treeView.onDidChangeVisibility; },
get message() { return treeView.message; },
set message(message: string | MarkdownString) { checkProposedApiEnabled(extension); treeView.message = message; },
set message(message: string) {
checkProposedApiEnabled(extension);
treeView.message = message;
},
reveal: (element: T, options?: IRevealOptions): Promise<void> => {
return treeView.reveal(element, options);
},
@@ -177,11 +191,12 @@ class ExtHostTreeView<T> extends Disposable {
private _onDidChangeData: Emitter<TreeData<T>> = this._register(new Emitter<TreeData<T>>());
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.TreeViewOptions2<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) {
super();
this.dataProvider = options.treeDataProvider;
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll });
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany });
if (this.dataProvider.onDidChangeTreeData) {
this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element })));
}
@@ -206,9 +221,11 @@ class ExtHostTreeView<T> extends Disposable {
return result;
}, 200)(({ message, elements }) => {
if (elements.length) {
const _promiseCallback = promiseCallback;
refreshingPromise = null;
this.refresh(elements).then(() => _promiseCallback());
this.refreshQueue = this.refreshQueue.then(() => {
const _promiseCallback = promiseCallback;
refreshingPromise = null;
return this.refresh(elements).then(() => _promiseCallback());
});
}
if (message) {
this.proxy.$setMessage(this.viewId, this._message);
@@ -247,12 +264,12 @@ class ExtHostTreeView<T> extends Disposable {
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus, expand })), error => this.logService.error(error));
}
private _message: string | MarkdownString;
get message(): string | MarkdownString {
private _message: string = '';
get message(): string {
return this._message;
}
set message(message: string | MarkdownString) {
set message(message: string) {
this._message = message;
this._onDidChangeData.fire({ message: true, element: false });
}
@@ -565,9 +582,9 @@ class ExtHostTreeView<T> extends Disposable {
if (node) {
if (node.children) {
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
if (childEleement) {
this.clear(childEleement);
const childElement = this.elements.get(child.item.handle);
if (childElement) {
this.clear(childElement);
}
}
}
@@ -583,9 +600,9 @@ class ExtHostTreeView<T> extends Disposable {
if (node) {
if (node.children) {
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
if (childEleement) {
this.clear(childEleement);
const childElement = this.elements.get(child.item.handle);
if (childElement) {
this.clear(childElement);
}
}
}

View File

@@ -19,7 +19,7 @@ import { IRange } 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';
import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto } from 'vs/workbench/api/common/extHost.protocol';
import { IWorkspaceEditDto, IResourceTextEditDto, IResourceFileEditDto } from 'vs/workbench/api/common/extHost.protocol';
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
@@ -113,6 +113,15 @@ export namespace DiagnosticTag {
}
return undefined;
}
export function to(value: MarkerTag): vscode.DiagnosticTag | undefined {
switch (value) {
case MarkerTag.Unnecessary:
return types.DiagnosticTag.Unnecessary;
case MarkerTag.Deprecated:
return types.DiagnosticTag.Deprecated;
}
return undefined;
}
}
export namespace Diagnostic {
@@ -127,6 +136,15 @@ export namespace Diagnostic {
tags: Array.isArray(value.tags) ? coalesce(value.tags.map(DiagnosticTag.from)) : undefined,
};
}
export function to(value: IMarkerData): vscode.Diagnostic {
const res = new types.Diagnostic(Range.to(value), value.message, DiagnosticSeverity.to(value.severity));
res.source = value.source;
res.code = value.code;
res.relatedInformation = value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.to);
res.tags = value.tags && coalesce(value.tags.map(DiagnosticTag.to));
return res;
}
}
export namespace DiagnosticRelatedInformation {
@@ -442,7 +460,7 @@ export namespace TextEdit {
export function from(edit: vscode.TextEdit): modes.TextEdit {
return <modes.TextEdit>{
text: edit.newText,
eol: EndOfLine.from(edit.newEol),
eol: edit.newEol && EndOfLine.from(edit.newEol),
range: Range.from(edit.range)
};
}
@@ -455,8 +473,8 @@ export namespace TextEdit {
}
export namespace WorkspaceEdit {
export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors): WorkspaceEditDto {
const result: WorkspaceEditDto = {
export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors): IWorkspaceEditDto {
const result: IWorkspaceEditDto = {
edits: []
};
for (const entry of (value as types.WorkspaceEdit)._allEntries()) {
@@ -464,28 +482,28 @@ export namespace WorkspaceEdit {
if (Array.isArray(uriOrEdits)) {
// text edits
const doc = documents && uri ? documents.getDocument(uri) : undefined;
result.edits.push(<ResourceTextEditDto>{ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) });
result.edits.push(<IResourceTextEditDto>{ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) });
} else {
// resource edits
result.edits.push(<ResourceFileEditDto>{ oldUri: uri, newUri: uriOrEdits, options: entry[2] });
result.edits.push(<IResourceFileEditDto>{ oldUri: uri, newUri: uriOrEdits, options: entry[2] });
}
}
return result;
}
export function to(value: WorkspaceEditDto) {
export function to(value: IWorkspaceEditDto) {
const result = new types.WorkspaceEdit();
for (const edit of value.edits) {
if (Array.isArray((<ResourceTextEditDto>edit).edits)) {
if (Array.isArray((<IResourceTextEditDto>edit).edits)) {
result.set(
URI.revive((<ResourceTextEditDto>edit).resource),
<types.TextEdit[]>(<ResourceTextEditDto>edit).edits.map(TextEdit.to)
URI.revive((<IResourceTextEditDto>edit).resource),
<types.TextEdit[]>(<IResourceTextEditDto>edit).edits.map(TextEdit.to)
);
} else {
result.renameFile(
URI.revive((<ResourceFileEditDto>edit).oldUri!),
URI.revive((<ResourceFileEditDto>edit).newUri!),
(<ResourceFileEditDto>edit).options
URI.revive((<IResourceFileEditDto>edit).oldUri!),
URI.revive((<IResourceFileEditDto>edit).newUri!),
(<IResourceFileEditDto>edit).options
);
}
}

View File

@@ -514,7 +514,7 @@ export class TextEdit {
protected _range: Range;
protected _newText: string | null;
protected _newEol: EndOfLine;
protected _newEol?: EndOfLine;
get range(): Range {
return this._range;
@@ -538,11 +538,11 @@ export class TextEdit {
this._newText = value;
}
get newEol(): EndOfLine {
get newEol(): EndOfLine | undefined {
return this._newEol;
}
set newEol(value: EndOfLine) {
set newEol(value: EndOfLine | undefined) {
if (value && typeof value !== 'number') {
throw illegalArgument('newEol');
}
@@ -550,7 +550,7 @@ export class TextEdit {
}
constructor(range: Range, newText: string | null) {
this.range = range;
this._range = range;
this._newText = newText;
}
@@ -798,7 +798,7 @@ export class Location {
}
uri: URI;
range: Range;
range!: Range;
constructor(uri: URI, rangeOrPosition: Range | Position) {
this.uri = uri;
@@ -861,10 +861,10 @@ export class Diagnostic {
range: Range;
message: string;
source: string;
code: string | number;
severity: DiagnosticSeverity;
relatedInformation: DiagnosticRelatedInformation[];
source?: string;
code?: string | number;
relatedInformation?: DiagnosticRelatedInformation[];
tags?: DiagnosticTag[];
constructor(range: Range, message: string, severity: DiagnosticSeverity = DiagnosticSeverity.Error) {
@@ -989,7 +989,7 @@ export class SymbolInformation {
}
name: string;
location: Location;
location!: Location;
kind: SymbolKind;
containerName: string | undefined;
@@ -1075,6 +1075,8 @@ export class CodeAction {
kind?: CodeActionKind;
isPreferred?: boolean;
constructor(title: string, kind?: CodeActionKind) {
this.title = title;
this.kind = kind;
@@ -1253,8 +1255,8 @@ export class SignatureInformation {
export class SignatureHelp {
signatures: SignatureInformation[];
activeSignature: number;
activeParameter: number;
activeSignature: number = 0;
activeParameter: number = 0;
constructor() {
this.signatures = [];
@@ -1310,19 +1312,19 @@ export enum CompletionItemKind {
export class CompletionItem implements vscode.CompletionItem {
label: string;
kind: CompletionItemKind | undefined;
kind?: CompletionItemKind;
detail?: string;
documentation?: string | MarkdownString;
sortText?: string;
filterText?: string;
preselect?: boolean;
insertText: string | SnippetString;
insertText?: string | SnippetString;
keepWhitespace?: boolean;
range: Range;
range?: Range;
commitCharacters?: string[];
textEdit: TextEdit;
additionalTextEdits: TextEdit[];
command: vscode.Command;
textEdit?: TextEdit;
additionalTextEdits?: TextEdit[];
command?: vscode.Command;
constructor(label: string, kind?: CompletionItemKind) {
this.label = label;
@@ -1753,26 +1755,6 @@ export enum TaskScope {
Workspace = 2
}
export class CustomExecution implements vscode.CustomExecution {
private _callback: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>;
constructor(callback: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>) {
this._callback = callback;
}
public computeId(): string {
return 'customExecution' + generateUuid();
}
public set callback(value: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>) {
this._callback = value;
}
public get callback(): (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number> {
return this._callback;
}
}
export class CustomExecution2 implements vscode.CustomExecution2 {
private _callback: () => Thenable<vscode.Pseudoterminal>;
constructor(callback: () => Thenable<vscode.Pseudoterminal>) {
@@ -1804,7 +1786,7 @@ export class Task implements vscode.Task2 {
private _definition: vscode.TaskDefinition;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
private _name: string;
private _execution: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined;
private _execution: ProcessExecution | ShellExecution | CustomExecution2 | undefined;
private _problemMatchers: string[];
private _hasDefinedMatchers: boolean;
private _isBackground: boolean;
@@ -1813,8 +1795,8 @@ export class Task implements vscode.Task2 {
private _presentationOptions: vscode.TaskPresentationOptions;
private _runOptions: vscode.RunOptions;
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, arg2: string | (vscode.TaskScope.Global | vscode.TaskScope.Workspace) | vscode.WorkspaceFolder, arg3: any, arg4?: any, arg5?: any, arg6?: any) {
this.definition = definition;
let problemMatchers: string | string[];
@@ -1879,7 +1861,7 @@ export class Task implements vscode.Task2 {
type: Task.ShellType,
id: this._execution.computeId()
};
} else if (this._execution instanceof CustomExecution) {
} else if (this._execution instanceof CustomExecution2) {
this._definition = {
type: Task.ExtensionCallbackType,
id: this._execution.computeId()
@@ -1926,18 +1908,18 @@ export class Task implements vscode.Task2 {
}
get execution(): ProcessExecution | ShellExecution | undefined {
return ((this._execution instanceof CustomExecution) || (this._execution instanceof CustomExecution2)) ? undefined : this._execution;
return (this._execution instanceof CustomExecution2) ? undefined : this._execution;
}
set execution(value: ProcessExecution | ShellExecution | undefined) {
this.execution2 = value;
}
get execution2(): ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined {
get execution2(): ProcessExecution | ShellExecution | CustomExecution2 | undefined {
return this._execution;
}
set execution2(value: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined) {
set execution2(value: ProcessExecution | ShellExecution | CustomExecution2 | undefined) {
if (value === null) {
value = undefined;
}
@@ -2184,6 +2166,24 @@ export class FunctionBreakpoint extends Breakpoint {
}
}
@es5ClassCompat
export class DataBreakpoint extends Breakpoint {
readonly label: string;
readonly dataId: string;
readonly canPersist: boolean;
constructor(label: string, dataId: string, canPersist: boolean, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
super(enabled, condition, hitCondition, logMessage);
if (!dataId) {
throw illegalArgument('dataId');
}
this.label = label;
this.dataId = dataId;
this.canPersist = canPersist;
}
}
@es5ClassCompat
export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable {
readonly command: string;

View File

@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IURITransformer } from 'vs/base/common/uriIpc';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI, UriComponents } from 'vs/base/common/uri';
export interface IURITransformerService extends IURITransformer {
_serviceBrand: any;
}
export const IURITransformerService = createDecorator<IURITransformerService>('IURITransformerService');
export class URITransformerService implements IURITransformerService {
_serviceBrand: any;
transformIncoming: (uri: UriComponents) => UriComponents;
transformOutgoing: (uri: UriComponents) => UriComponents;
transformOutgoingURI: (uri: URI) => URI;
transformOutgoingScheme: (scheme: string) => string;
constructor(delegate: IURITransformer | null) {
if (!delegate) {
this.transformIncoming = arg => arg;
this.transformOutgoing = arg => arg;
this.transformOutgoingURI = arg => arg;
this.transformOutgoingScheme = arg => arg;
} else {
this.transformIncoming = delegate.transformIncoming.bind(delegate);
this.transformOutgoing = delegate.transformOutgoing.bind(delegate);
this.transformOutgoingURI = delegate.transformOutgoingURI.bind(delegate);
this.transformOutgoingScheme = delegate.transformOutgoingScheme.bind(delegate);
}
}
}

View File

@@ -5,15 +5,15 @@
import { Emitter, Event } from 'vs/base/common/event';
import { URI } 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 * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
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, WebviewPanelViewState } from './extHost.protocol';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as modes from 'vs/editor/common/modes';
import { WebviewInitData, toWebviewResource } from 'vs/workbench/api/common/shared/webview';
import { generateUuid } from 'vs/base/common/uuid';
type IconPath = URI | { light: URI, dark: URI };
@@ -35,8 +35,8 @@ export class ExtHostWebview implements vscode.Webview {
this._onMessageEmitter.dispose();
}
public toWebviewResource(resource: vscode.Uri): vscode.Uri {
return toWebviewResource(this._initData, this._handle, resource);
public asWebviewUri(resource: vscode.Uri): vscode.Uri {
return asWebviewUri(this._initData, this._handle, resource);
}
public get cspSource(): string {
@@ -89,11 +89,12 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
private readonly _options: vscode.WebviewPanelOptions;
private readonly _webview: ExtHostWebview;
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn | undefined;
private _visible: boolean = true;
private _active: boolean = true;
_isDisposed: boolean = false;
readonly _onDisposeEmitter = new Emitter<void>();
public readonly onDidDispose: Event<void> = this._onDisposeEmitter.event;
@@ -297,21 +298,21 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
}
public $onDidChangeWebviewPanelViewState(
handle: WebviewPanelHandle,
newState: WebviewPanelViewState
): void {
const panel = this.getWebviewPanel(handle);
if (!panel) {
return;
}
public $onDidChangeWebviewPanelViewStates(newStates: WebviewPanelViewStateData): void {
for (const handle of Object.keys(newStates)) {
const panel = this.getWebviewPanel(handle);
if (!panel || panel._isDisposed) {
continue;
}
const viewColumn = typeConverters.ViewColumn.to(newState.position);
if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) {
panel._setActive(newState.active);
panel._setVisible(newState.visible);
panel._setViewColumn(viewColumn);
panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel });
const newState = newStates[handle];
const viewColumn = typeConverters.ViewColumn.to(newState.position);
if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) {
panel._setActive(newState.active);
panel._setVisible(newState.visible);
panel._setViewColumn(viewColumn);
panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel });
}
}
}

View File

@@ -20,11 +20,14 @@ import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspa
import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IWorkspaceData, MainThreadMessageServiceShape, MainThreadWorkspaceShape, IMainContext, MainContext, IStaticWorkspaceData } from './extHost.protocol';
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';
export interface IExtHostWorkspaceProvider {
getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise<vscode.WorkspaceFolder | undefined>;
@@ -153,6 +156,8 @@ class ExtHostWorkspaceImpl extends Workspace {
export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspaceProvider {
readonly _serviceBrand: any;
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
@@ -169,21 +174,21 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
constructor(
mainContext: IMainContext,
logService: ILogService,
requestIdProvider: Counter,
data?: IStaticWorkspaceData
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
@ILogService logService: ILogService,
) {
this._logService = logService;
this._requestIdProvider = requestIdProvider;
this._requestIdProvider = new Counter();
this._barrier = new Barrier();
this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace);
this._messageService = mainContext.getProxy(MainContext.MainThreadMessageService);
this._proxy = extHostRpc.getProxy(MainContext.MainThreadWorkspace);
this._messageService = extHostRpc.getProxy(MainContext.MainThreadMessageService);
const data = initData.workspace;
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled) : undefined;
}
$initializeWorkspace(data: IWorkspaceData): void {
$initializeWorkspace(data: IWorkspaceData | null): void {
this.$acceptWorkspaceData(data);
this._barrier.open();
}
@@ -289,7 +294,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
this._unconfirmedWorkspace = undefined;
// show error to user
this._messageService.$showMessage(Severity.Error, localize('updateerror', "Extension '{0}' failed to update workspace folders: {1}", extName, error), { extension }, []);
this._messageService.$showMessage(Severity.Error, localize('updateerror', "Extension '{0}' failed to update workspace folders: {1}", extName, error.toString()), { extension }, []);
});
}
@@ -390,7 +395,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
}
}
$acceptWorkspaceData(data: IWorkspaceData): void {
$acceptWorkspaceData(data: IWorkspaceData | null): void {
const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace);
@@ -546,3 +551,6 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
return this._proxy.$resolveProxy(url);
}
}
export const IExtHostWorkspace = createDecorator<IExtHostWorkspace>('IExtHostWorkspace');
export interface IExtHostWorkspace extends ExtHostWorkspace, ExtHostWorkspaceShape, IExtHostWorkspaceProvider { }

View File

@@ -12,7 +12,7 @@ import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } fr
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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
namespace schema {
@@ -329,7 +329,7 @@ namespace schema {
};
}
let _commandRegistrations: IDisposable[] = [];
const _commandRegistrations = new DisposableStore();
export const commandsExtensionPoint = ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.IUserFriendlyCommand[]>({
extensionPoint: 'commands',
@@ -338,7 +338,7 @@ export const commandsExtensionPoint = ExtensionsRegistry.registerExtensionPoint<
commandsExtensionPoint.setHandler(extensions => {
function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser<any>, disposables: IDisposable[]) {
function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser<any>) {
if (!schema.isValidCommand(userFriendlyCommand, extension.collector)) {
return;
@@ -368,20 +368,20 @@ commandsExtensionPoint.setHandler(extensions => {
precondition: ContextKeyExpr.deserialize(enablement),
iconLocation: absoluteIcon
});
disposables.push(registration);
_commandRegistrations.add(registration);
}
// remove all previous command registrations
_commandRegistrations = dispose(_commandRegistrations);
_commandRegistrations.clear();
for (let extension of extensions) {
for (const extension of extensions) {
const { value } = extension;
if (Array.isArray<schema.IUserFriendlyCommand>(value)) {
for (let command of value) {
handleCommand(command, extension, _commandRegistrations);
if (Array.isArray(value)) {
for (const command of value) {
handleCommand(command, extension);
}
} else {
handleCommand(value, extension, _commandRegistrations);
handleCommand(value, extension);
}
}
});

View File

@@ -66,10 +66,6 @@ export interface ShellExecutionDTO {
options?: ShellExecutionOptionsDTO;
}
export interface CustomExecutionDTO {
customExecution: 'customExecution';
}
export interface CustomExecution2DTO {
customExecution: 'customExecution2';
}
@@ -88,7 +84,7 @@ export interface TaskHandleDTO {
export interface TaskDTO {
_id: string;
name?: string;
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined;
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecution2DTO | undefined;
definition: TaskDefinitionDTO;
isBackground?: boolean;
source: TaskSourceDTO;
@@ -129,4 +125,4 @@ export interface TaskSystemInfoDTO {
scheme: string;
authority: string;
platform: string;
}
}

View File

@@ -11,7 +11,7 @@ export interface WebviewInitData {
readonly webviewCspSource: string;
}
export function toWebviewResource(
export function asWebviewUri(
initData: WebviewInitData,
uuid: string,
resource: vscode.Uri

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
import { ExtHostOutputService2 } from 'vs/workbench/api/node/extHostOutputService';
import { IExtHostWorkspace, ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { IExtHostDecorations, ExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations';
import { IExtHostConfiguration, ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { IExtHostCommands, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
import { IExtHostTask } from 'vs/workbench/api/common/extHostTask';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
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 { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
// register singleton services
registerSingleton(ILogService, ExtHostLogService);
registerSingleton(IExtHostOutputService, ExtHostOutputService2);
registerSingleton(IExtHostWorkspace, ExtHostWorkspace);
registerSingleton(IExtHostDecorations, ExtHostDecorations);
registerSingleton(IExtHostConfiguration, ExtHostConfiguration);
registerSingleton(IExtHostCommands, ExtHostCommands);
registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors);
registerSingleton(IExtHostTerminalService, ExtHostTerminalService);
registerSingleton(IExtHostTask, ExtHostTask);
registerSingleton(IExtHostDebugService, ExtHostDebugService);
registerSingleton(IExtHostSearch, ExtHostSearch);
registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
registerSingleton(IExtHostExtensionService, ExtHostExtensionService);
registerSingleton(IExtHostStorage, ExtHostStorage);

View File

@@ -6,7 +6,7 @@
import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net';
import * as http from 'http';
import * as fs from 'fs';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IURIToOpen, IOpenSettings } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
@@ -38,7 +38,7 @@ export class CLIServer {
private _server: http.Server;
private _ipcHandlePath: string | undefined;
constructor(private _commands: ExtHostCommands) {
constructor(@IExtHostCommands private _commands: IExtHostCommands) {
this._server = http.createServer((req, res) => this.onRequest(req, res));
this.setup().catch(err => {
console.error(err);
@@ -179,4 +179,4 @@ export class CLIServer {
fs.unlinkSync(this._ipcHandlePath);
}
}
}
}

View File

@@ -11,33 +11,37 @@ import { asPromise } from 'vs/base/common/async';
import * as nls from 'vs/nls';
import {
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
} from 'vs/workbench/api/common/extHost.protocol';
import * as vscode from 'vscode';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint } from 'vs/workbench/api/common/extHostTypes';
import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug';
import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
import { ExtHostConfiguration, ExtHostConfigProvider } from '../common/extHostConfiguration';
import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration';
import { convertToVSCPaths, convertToDAPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { SignService } from 'vs/platform/sign/node/signService';
import { ISignService } from 'vs/platform/sign/common/sign';
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugServiceShape {
readonly _serviceBrand: any;
private _configProviderHandleCounter: number;
private _configProviders: ConfigProviderTuple[];
@@ -86,13 +90,14 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _signService: ISignService;
constructor(mainContext: IMainContext,
private _workspaceService: IExtHostWorkspaceProvider,
private _extensionService: ExtHostExtensionService,
private _editorsService: ExtHostDocumentsAndEditors,
private _configurationService: ExtHostConfiguration,
private _terminalService: ExtHostTerminalService,
private _commandService: ExtHostCommands
constructor(
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
@IExtHostWorkspace private _workspaceService: IExtHostWorkspace,
@IExtHostExtensionService private _extensionService: IExtHostExtensionService,
@IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors,
@IExtHostConfiguration private _configurationService: IExtHostConfiguration,
@IExtHostTerminalService private _terminalService: IExtHostTerminalService,
@IExtHostCommands private _commandService: IExtHostCommands
) {
this._configProviderHandleCounter = 0;
this._configProviders = [];
@@ -112,7 +117,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._onDidChangeActiveDebugSession = new Emitter<vscode.DebugSession>();
this._onDidReceiveDebugSessionCustomEvent = new Emitter<vscode.DebugSessionCustomEvent>();
this._debugServiceProxy = mainContext.getProxy(MainContext.MainThreadDebugService);
this._debugServiceProxy = extHostRpcService.getProxy(MainContext.MainThreadDebugService);
this._onDidChangeBreakpoints = new Emitter<vscode.BreakpointsChangeEvent>({
onFirstListenerAdd: () => {
@@ -243,7 +248,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
// unregister with VS Code
const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp.id);
const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp.id);
return this._debugServiceProxy.$unregisterBreakpoints(ids, fids);
const dids = breakpoints.filter(bp => bp instanceof DataBreakpoint).map(bp => bp.id);
return this._debugServiceProxy.$unregisterBreakpoints(ids, fids, dids);
}
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession): Promise<boolean> {
@@ -344,7 +350,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}).then(async needNewTerminal => {
const configProvider = await this._configurationService.getConfigProvider();
const shell = this._terminalService.getDefaultShell(configProvider);
const shell = this._terminalService.getDefaultShell(true, configProvider);
if (needNewTerminal || !this._integratedTerminalInstance) {
const options: vscode.TerminalOptions = {
@@ -549,6 +555,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
let bp: vscode.Breakpoint;
if (bpd.type === 'function') {
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage);
} else if (bpd.type === 'data') {
bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage);
} else {
const uri = URI.revive(bpd.uri);
bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage);
@@ -599,7 +607,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this.fireBreakpointChanges(a, r, c);
}
public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Promise<vscode.DebugConfiguration[]> {
public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined, token: CancellationToken): Promise<vscode.DebugConfiguration[]> {
return asPromise(async () => {
const provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
@@ -609,7 +617,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
throw new Error('DebugConfigurationProvider has no method provideDebugConfigurations');
}
const folder = await this.getFolder(folderUri);
return provider.provideDebugConfigurations(folder, CancellationToken.None);
return provider.provideDebugConfigurations(folder, token);
}).then(debugConfigurations => {
if (!debugConfigurations) {
throw new Error('nothing returned from DebugConfigurationProvider.provideDebugConfigurations');
@@ -618,7 +626,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
});
}
public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Promise<vscode.DebugConfiguration | null | undefined> {
public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration, token: CancellationToken): Promise<vscode.DebugConfiguration | null | undefined> {
return asPromise(async () => {
const provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
@@ -628,7 +636,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
throw new Error('DebugConfigurationProvider has no method resolveDebugConfiguration');
}
const folder = await this.getFolder(folderUri);
return provider.resolveDebugConfiguration(folder, debugConfiguration, CancellationToken.None);
return provider.resolveDebugConfiguration(folder, debugConfiguration, token);
});
}

View File

@@ -6,23 +6,26 @@
import { join } from 'vs/base/common/path';
import { tmpdir } from 'os';
import { generateUuid } from 'vs/base/common/uuid';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { Disposable } from 'vs/base/common/lifecycle';
import { MainThreadDownloadServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { URI } from 'vs/base/common/uri';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export class ExtHostDownloadService extends Disposable {
constructor(
proxy: MainThreadDownloadServiceShape,
commands: ExtHostCommands
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostCommands commands: IExtHostCommands
) {
super();
const proxy = extHostRpc.getProxy(MainContext.MainThreadDownloadService);
commands.registerCommand(false, '_workbench.downloadResource', async (resource: URI): Promise<any> => {
const location = URI.file(join(tmpdir(), generateUuid()));
await proxy.$download(resource, location);
return location;
});
}
}
}

View File

@@ -3,791 +3,107 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import { originalFSPath } from 'vs/base/common/resources';
import { Barrier } from 'vs/base/common/async';
import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { createApiFactory, IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl';
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
import { MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator';
import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadService';
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { withNullAsUndefined } from 'vs/base/common/types';
import { VSBuffer } from 'vs/base/common/buffer';
import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
import { RemoteAuthorityResolverError, ExtensionExecutionContext } from 'vs/workbench/api/common/extHostTypes';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { ResolvedAuthority, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver';
interface ITestRunner {
/** Old test runner API, as exported from `vscode/lib/testrunner` */
run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}
interface INewTestRunner {
/** New test runner API, as explained in the extension test doc */
run(): Promise<void>;
}
export interface IHostUtils {
exit(code?: number): void;
exists(path: string): Promise<boolean>;
realpath(path: string): Promise<string>;
}
type TelemetryActivationEventFragment = {
id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
extensionVersion: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
activationEvents: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
reason: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;
private readonly _hostUtils: IHostUtils;
private readonly _initData: IInitData;
private readonly _extHostContext: IMainContext;
private readonly _extHostWorkspace: ExtHostWorkspace;
private readonly _extHostConfiguration: ExtHostConfiguration;
private readonly _environment: IEnvironment;
private readonly _extHostLogService: ExtHostLogService;
private readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;
private readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
private readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;
private readonly _almostReadyToRunExtensions: Barrier;
private readonly _readyToStartExtensionHost: Barrier;
private readonly _readyToRunExtensions: Barrier;
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePaths;
private readonly _activator: ExtensionsActivator;
private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>> | null;
private readonly _extensionApiFactory: IExtensionApiFactory;
private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; };
private _started: boolean;
private readonly _disposables: DisposableStore;
constructor(
hostUtils: IHostUtils,
initData: IInitData,
extHostContext: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
environment: IEnvironment,
extHostLogService: ExtHostLogService,
uriTransformer: IURITransformer | null
) {
this._hostUtils = hostUtils;
this._initData = initData;
this._extHostContext = extHostContext;
this._extHostWorkspace = extHostWorkspace;
this._extHostConfiguration = extHostConfiguration;
this._environment = environment;
this._extHostLogService = extHostLogService;
this._disposables = new DisposableStore();
this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);
this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._almostReadyToRunExtensions = new Barrier();
this._readyToStartExtensionHost = new Barrier();
this._readyToRunExtensions = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._storage = new ExtHostStorage(this._extHostContext);
this._storagePath = new ExtensionStoragePaths(withNullAsUndefined(initData.workspace), initData.environment);
const hostExtensions = new Set<string>();
initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId)));
this._activator = new ExtensionsActivator(this._registry, initData.resolvedExtensions, 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))) {
const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent);
return new HostExtension();
}
const extensionDescription = this._registry.getExtensionDescription(extensionId)!;
return this._activateExtension(extensionDescription, reason);
}
});
this._extensionPathIndex = null;
// initialize API first (i.e. do not release barrier until the API is initialized)
this._extensionApiFactory = createApiFactory(
this._initData,
this._extHostContext,
this._extHostWorkspace,
this._extHostConfiguration,
this,
this._extHostLogService,
this._storage,
uriTransformer
);
this._resolvers = Object.create(null);
this._started = false;
this._initialize();
if (this._initData.autoStart) {
this._startExtensionHost();
}
}
private async _initialize(): Promise<void> {
try {
const configProvider = await this._extHostConfiguration.getConfigProvider();
const extensionPaths = await this.getExtensionPathIndex();
NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(this._extensionApiFactory, extensionPaths, this._registry, configProvider));
NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar), this._environment));
if (this._initData.remote.isRemote) {
NodeModuleRequireInterceptor.INSTANCE.register(new OpenNodeModuleFactory(
this._extHostContext.getProxy(MainContext.MainThreadWindow),
this._extHostContext.getProxy(MainContext.MainThreadTelemetry),
extensionPaths
));
}
// Do this when extension service exists, but extensions are not being activated yet.
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy);
this._almostReadyToRunExtensions.open();
await this._extHostWorkspace.waitForInitializeCall();
this._readyToStartExtensionHost.open();
} catch (err) {
errors.onUnexpectedError(err);
}
}
public async deactivateAll(): Promise<void> {
let allPromises: Promise<void>[] = [];
try {
const allExtensions = this._registry.getAllExtensionDescriptions();
const allExtensionsIds = allExtensions.map(ext => ext.identifier);
const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));
allPromises = activatedExtensions.map((extensionId) => {
return this._deactivate(extensionId);
});
} catch (err) {
// TODO: write to log once we have one
}
await allPromises;
}
public isActivated(extensionId: ExtensionIdentifier): boolean {
if (this._readyToRunExtensions.isOpen()) {
return this._activator.isActivated(extensionId);
}
return false;
}
private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
const reason = new ExtensionActivatedByEvent(startup, activationEvent);
return this._activator.activateByEvent(activationEvent, reason);
}
private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
return this._activator.activateById(extensionId, reason);
}
public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
return this._activateById(extensionId, reason).then(() => {
const extension = this._activator.getActivatedExtension(extensionId);
if (extension.activationFailed) {
// activation failed => bubble up the error as the promise result
return Promise.reject(extension.activationFailedError);
}
return undefined;
});
}
public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
return this._readyToRunExtensions.wait().then(_ => this._registry);
}
public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {
if (this._readyToRunExtensions.isOpen()) {
return this._activator.getActivatedExtension(extensionId).exports;
} else {
return null;
}
}
// create trie to enable fast 'filename -> extension id' look up
public getExtensionPathIndex(): Promise<TernarySearchTree<IExtensionDescription>> {
if (!this._extensionPathIndex) {
const tree = TernarySearchTree.forPaths<IExtensionDescription>();
const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
if (!ext.main) {
return undefined;
}
return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
});
this._extensionPathIndex = Promise.all(extensions).then(() => tree);
}
return this._extensionPathIndex;
}
private _deactivate(extensionId: ExtensionIdentifier): Promise<void> {
let result = Promise.resolve(undefined);
if (!this._readyToRunExtensions.isOpen()) {
return result;
}
if (!this._activator.isActivated(extensionId)) {
return result;
}
const extension = this._activator.getActivatedExtension(extensionId);
if (!extension) {
return result;
}
// call deactivate if available
try {
if (typeof extension.module.deactivate === 'function') {
result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => {
// TODO: Do something with err if this is not the shutdown case
return Promise.resolve(undefined);
});
}
} catch (err) {
// TODO: Do something with err if this is not the shutdown case
}
// clean up subscriptions
try {
dispose(extension.subscriptions);
} catch (err) {
// TODO: Do something with err if this is not the shutdown case
}
return result;
}
// --- impl
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
const activationTimes = activatedExtension.activationTimes;
const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
return activatedExtension;
}, (err) => {
this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
throw err;
});
}
private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
const event = getTelemetryActivationEvent(extensionDescription, reason);
type ExtensionActivationTimesClassification = {
outcome: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
} & TelemetryActivationEventFragment & ExtensionActivationTimesFragment;
type ExtensionActivationTimesEvent = {
outcome: string
} & ActivationTimesEvent & TelemetryActivationEvent;
type ActivationTimesEvent = {
startup?: boolean;
codeLoadingTime?: number;
activateCallTime?: number;
activateResolvedTime?: number;
};
this._mainThreadTelemetryProxy.$publicLog2<ExtensionActivationTimesEvent, ExtensionActivationTimesClassification>('extensionActivationTimes', {
...event,
...(activationTimes || {}),
outcome
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
const event = getTelemetryActivationEvent(extensionDescription, reason);
type ActivatePluginClassification = {} & TelemetryActivationEventFragment;
this._mainThreadTelemetryProxy.$publicLog2<TelemetryActivationEvent, ActivatePluginClassification>('activatePlugin', event);
if (!extensionDescription.main) {
// Treat the extension as being empty => NOT AN ERROR CASE
return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
}
this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`);
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return Promise.all<any>([
loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
});
}
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<vscode.ExtensionContext> {
const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
return Promise.all([
globalState.whenReady,
workspaceState.whenReady,
this._storagePath.whenReady
]).then(() => {
const that = this;
return Object.freeze(<IExtensionContext>{
globalState,
workspaceState,
subscriptions: [],
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
storagePath: this._storagePath.workspaceValue(extensionDescription),
globalStoragePath: this._storagePath.globalValue(extensionDescription),
asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier),
executionContext: this._initData.remote.isRemote ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local,
});
});
}
private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
// Make sure the extension's surface is not undefined
extensionModule = extensionModule || {
activate: undefined,
deactivate: undefined
};
return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {
return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
});
}
private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
if (typeof extensionModule.activate === 'function') {
try {
activationTimesBuilder.activateCallStart();
logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);
const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
activationTimesBuilder.activateCallStop();
activationTimesBuilder.activateResolveStart();
return Promise.resolve(activateResult).then((value) => {
activationTimesBuilder.activateResolveStop();
return value;
});
} catch (err) {
return Promise.reject(err);
}
} else {
// No activate found => the module is the extension's exports
return Promise.resolve<IExtensionAPI>(extensionModule);
}
}
// -- eager activation
// Handle "eager" activation extensions
private _handleEagerExtensions(): Promise<void> {
this._activateByEvent('*', true).then(undefined, (err) => {
console.error(err);
});
this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added)));
const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : [];
return this._handleWorkspaceContainsEagerExtensions(folders);
}
private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray<vscode.WorkspaceFolder>): Promise<void> {
if (folders.length === 0) {
return Promise.resolve(undefined);
}
return Promise.all(
this._registry.getAllExtensionDescriptions().map((desc) => {
return this._handleWorkspaceContainsEagerExtension(folders, desc);
})
).then(() => { });
}
private _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray<vscode.WorkspaceFolder>, desc: IExtensionDescription): Promise<void> {
const activationEvents = desc.activationEvents;
if (!activationEvents) {
return Promise.resolve(undefined);
}
if (this.isActivated(desc.identifier)) {
return Promise.resolve(undefined);
}
const fileNames: string[] = [];
const globPatterns: string[] = [];
for (const activationEvent of activationEvents) {
if (/^workspaceContains:/.test(activationEvent)) {
const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length);
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
globPatterns.push(fileNameOrGlob);
} else {
fileNames.push(fileNameOrGlob);
class NodeModuleRequireInterceptor extends RequireInterceptor {
protected _installInterceptor(): void {
const that = this;
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
for (let alternativeModuleName of that._alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {
request = alternative;
break;
}
}
}
if (fileNames.length === 0 && globPatterns.length === 0) {
return Promise.resolve(undefined);
}
const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(folders, desc.identifier, fileName))).then(() => { });
const globPatternPromise = this._activateIfGlobPatterns(folders, desc.identifier, globPatterns);
return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
}
private async _activateIfFileName(folders: ReadonlyArray<vscode.WorkspaceFolder>, extensionId: ExtensionIdentifier, fileName: string): Promise<void> {
// find exact path
for (const { uri } of folders) {
if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) {
// the file was found
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`))
.then(undefined, err => console.error(err))
);
if (!that._factories.has(request)) {
return original.apply(this, arguments);
}
}
return undefined;
}
private async _activateIfGlobPatterns(folders: ReadonlyArray<vscode.WorkspaceFolder>, extensionId: ExtensionIdentifier, globPatterns: string[]): Promise<void> {
this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`);
if (globPatterns.length === 0) {
return Promise.resolve(undefined);
}
const tokenSource = new CancellationTokenSource();
const searchP = this._mainThreadWorkspaceProxy.$checkExists(folders.map(folder => folder.uri), globPatterns, tokenSource.token);
const timer = setTimeout(async () => {
tokenSource.cancel();
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`))
.then(undefined, err => console.error(err));
}, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);
let exists: boolean = false;
try {
exists = await searchP;
} catch (err) {
if (!errors.isPromiseCanceledError(err)) {
console.error(err);
}
}
tokenSource.dispose();
clearTimeout(timer);
if (exists) {
// a file was found matching one of the glob patterns
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`))
.then(undefined, err => console.error(err))
return that._factories.get(request)!.load(
request,
URI.file(parent.filename),
request => original.apply(this, [request, parent, isMain])
);
};
}
}
export class ExtHostExtensionService extends AbstractExtHostExtensionService {
protected async _beforeAlmostReadyToRunExtensions(): Promise<void> {
// initialize API and register actors
const extensionApiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
// Register Download command
this._instaService.createInstance(ExtHostDownloadService);
// Register CLI Server for ipc
if (this._initData.remote.isRemote && this._initData.remote.authority) {
const cliServer = this._instaService.createInstance(CLIServer);
process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath;
}
return Promise.resolve(undefined);
// Module loading tricks
const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._registry);
await interceptor.install();
// Do this when extension service exists, but extensions are not being activated yet.
const configProvider = await this._extHostConfiguration.getConfigProvider();
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy);
// Use IPC messages to forward console-calls, note that the console is
// already patched to use`process.send()`
const nativeProcessSend = process.send!;
const mainThreadConsole = this._extHostContext.getProxy(MainContext.MainThreadConsole);
process.send = (...args: any[]) => {
if (args.length === 0 || !args[0] || args[0].type !== '__$console') {
return nativeProcessSend.apply(process, args);
}
mainThreadConsole.$logExtensionHostMessage(args[0]);
};
}
private _handleExtensionTests(): Promise<void> {
return this._doHandleExtensionTests().then(undefined, error => {
console.error(error); // ensure any error message makes it onto the console
return Promise.reject(error);
});
}
private _doHandleExtensionTests(): Promise<void> {
const { extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) {
return Promise.resolve(undefined);
protected _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
if (module.scheme !== Schemas.file) {
throw new Error(`Cannot load URI: '${module}', must be of file-scheme`);
}
const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
// Require the test runner via node require from the provided path
let testRunner: ITestRunner | INewTestRunner | undefined;
let requireError: Error | undefined;
let r: T | null = null;
activationTimesBuilder.codeLoadingStart();
this._logService.info(`ExtensionService#loadCommonJSModule ${module.toString(true)}`);
try {
testRunner = <any>require.__$__nodeRequire(extensionTestsPath);
} catch (error) {
requireError = error;
r = require.__$__nodeRequire<T>(module.fsPath);
} catch (e) {
return Promise.reject(e);
} finally {
activationTimesBuilder.codeLoadingStop();
}
// Execute the runner if it follows the old `run` spec
if (testRunner && typeof testRunner.run === 'function') {
return new Promise<void>((c, e) => {
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
if (error) {
e(error.toString());
} else {
c(undefined);
}
// after tests have run, we shutdown the host
this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
};
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
// Using the new API `run(): Promise<void>`
if (runResult && runResult.then) {
runResult
.then(() => {
c();
this._gracefulExit(0);
})
.catch((err: Error) => {
e(err.toString());
this._gracefulExit(1);
});
}
});
}
// Otherwise make sure to shutdown anyway even in case of an error
else {
this._gracefulExit(1 /* ERROR */);
}
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
return Promise.resolve(r);
}
private _gracefulExit(code: number): void {
// to give the PH process a chance to flush any outstanding console
// messages to the main process, we delay the exit() by some time
setTimeout(() => {
// If extension tests are running, give the exit code to the renderer
if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) {
this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
return;
public async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
if (!this._initData.remote.isRemote) {
return;
}
for (const key in env) {
const value = env[key];
if (value === null) {
delete process.env[key];
} else {
process.env[key] = value;
}
this._hostUtils.exit(code);
}, 500);
}
private _startExtensionHost(): Promise<void> {
if (this._started) {
throw new Error(`Extension host is already started!`);
}
this._started = true;
return this._readyToStartExtensionHost.wait()
.then(() => this._readyToRunExtensions.open())
.then(() => this._handleEagerExtensions())
.then(() => this._handleExtensionTests())
.then(() => {
this._extHostLogService.info(`eager extensions activated`);
});
}
// -- called by extensions
public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {
this._resolvers[authorityPrefix] = resolver;
return toDisposable(() => {
delete this._resolvers[authorityPrefix];
});
}
// -- called by main thread
public async $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
throw new Error(`Not an authority that can be resolved!`);
}
const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex);
await this._almostReadyToRunExtensions.wait();
await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false);
const resolver = this._resolvers[authorityPrefix];
if (!resolver) {
throw new Error(`No remote extension installed to resolve ${authorityPrefix}.`);
}
try {
const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
// Split merged API result into separate authority/options
const authority: ResolvedAuthority = {
authority: remoteAuthority,
host: result.host,
port: result.port
};
const options: ResolvedOptions = {
extensionHostEnv: result.extensionHostEnv
};
return {
type: 'ok',
value: {
authority,
options
}
};
} catch (err) {
if (err instanceof RemoteAuthorityResolverError) {
return {
type: 'error',
error: {
code: err._code,
message: err._message,
detail: err._detail
}
};
}
throw err;
}
}
public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
this._registry.keepOnly(enabledExtensionIds);
return this._startExtensionHost();
}
public $activateByEvent(activationEvent: string): Promise<void> {
return (
this._readyToRunExtensions.wait()
.then(_ => this._activateByEvent(activationEvent, false))
);
}
public async $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean> {
await this._readyToRunExtensions.wait();
if (!this._registry.getExtensionDescription(extensionId)) {
// unknown extension => ignore
return false;
}
await this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent));
return true;
}
public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
const trie = await this.getExtensionPathIndex();
await Promise.all(toRemove.map(async (extensionId) => {
const extensionDescription = this._registry.getExtensionDescription(extensionId);
if (!extensionDescription) {
return;
}
const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath);
trie.delete(URI.file(realpathValue).fsPath);
}));
await Promise.all(toAdd.map(async (extensionDescription) => {
const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath);
trie.set(URI.file(realpathValue).fsPath, extensionDescription);
}));
this._registry.deltaExtensions(toAdd, toRemove);
return Promise.resolve(undefined);
}
public async $test_latency(n: number): Promise<number> {
return n;
}
public async $test_up(b: VSBuffer): Promise<number> {
return b.byteLength;
}
public async $test_down(size: number): Promise<VSBuffer> {
let buff = VSBuffer.alloc(size);
let value = Math.random() % 256;
for (let i = 0; i < size; i++) {
buff.writeUInt8(value, i);
}
return buff;
}
}
function loadCommonJSModule<T>(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
let r: T | null = null;
activationTimesBuilder.codeLoadingStart();
logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`);
try {
r = require.__$__nodeRequire<T>(modulePath);
} catch (e) {
return Promise.reject(e);
} finally {
activationTimesBuilder.codeLoadingStop();
}
return Promise.resolve(r);
}
type TelemetryActivationEvent = {
id: string;
name: string;
extensionVersion: string;
publisherDisplayName: string;
activationEvents: string | null;
isBuiltin: boolean;
reason: string;
};
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent {
const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent :
reason instanceof ExtensionActivatedByAPI ? 'api' :
'';
const event = {
id: extensionDescription.identifier.value,
name: extensionDescription.name,
extensionVersion: extensionDescription.version,
publisherDisplayName: extensionDescription.publisher,
activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
isBuiltin: extensionDescription.isBuiltin,
reason: reasonStr
};
return event;
}

View File

@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { join } from 'vs/base/common/path';
import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log';
import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { Schemas } from 'vs/base/common/network';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
constructor(
@IExtHostInitDataService initData: IExtHostInitDataService,
@IExtHostOutputService extHostOutputService: IExtHostOutputService
) {
if (initData.logsLocation.scheme !== Schemas.file) { throw new Error('Only file-logging supported'); }
super(new SpdLogService(ExtensionHostLogFileName, initData.logsLocation.fsPath, initData.logLevel));
// Register an output channel for exthost log
extHostOutputService.createOutputChannelFromLogFile(
initData.remote.isRemote ? localize('remote extension host Log', "Remote Extension Host") : localize('extension host Log', "Extension Host"),
URI.file(join(initData.logsLocation.fsPath, `${ExtensionHostLogFileName}.log`))
);
}
$setLevel(level: LogLevel): void {
this.setLevel(level);
}
}

View File

@@ -10,7 +10,9 @@ import { join } from 'vs/base/common/path';
import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender';
import { toLocalISOString } from 'vs/base/common/date';
import { dirExists, mkdirp } from 'vs/base/node/pfs';
import { AbstractExtHostOutputChannel, IOutputChannelFactory, ExtHostPushOutputChannel } from 'vs/workbench/api/common/extHostOutput';
import { AbstractExtHostOutputChannel, ExtHostPushOutputChannel, ExtHostOutputService, LazyOutputChannel } from 'vs/workbench/api/common/extHostOutput';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChannel {
@@ -43,23 +45,39 @@ export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChann
}
}
export const LogOutputChannelFactory = new class implements IOutputChannelFactory {
export class ExtHostOutputService2 extends ExtHostOutputService {
_namePool = 1;
private _logsLocation: URI;
private _namePool: number = 1;
async createOutputChannel(name: string, logsLocation: URI, proxy: MainThreadOutputServiceShape): Promise<AbstractExtHostOutputChannel> {
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
) {
super(extHostRpc);
this._logsLocation = initData.logsLocation;
}
createOutputChannel(name: string): vscode.OutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
}
return new LazyOutputChannel(name, this._doCreateOutChannel(name));
}
private async _doCreateOutChannel(name: string): Promise<AbstractExtHostOutputChannel> {
try {
const outputDirPath = join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
const outputDir = await dirExists(outputDirPath).then(exists => exists ? exists : mkdirp(outputDirPath).then(() => true)).then(() => outputDirPath);
const outputDirPath = join(this._logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
const outputDir = await dirExists(outputDirPath).then(exists => exists || mkdirp(outputDirPath).then(() => true)).then(() => outputDirPath);
const fileName = `${this._namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}`;
const file = URI.file(join(outputDir, `${fileName}.log`));
const appender = new OutputAppender(fileName, file.fsPath);
return new ExtHostOutputChannelBackedByFile(name, appender, proxy);
return new ExtHostOutputChannelBackedByFile(name, appender, this._proxy);
} catch (error) {
// Do not crash if logger cannot be created
console.log(error);
return new ExtHostPushOutputChannel(name, proxy);
return new ExtHostPushOutputChannel(name, this._proxy);
}
}
};
}

View File

@@ -15,8 +15,10 @@ import { RipgrepSearchProvider } from 'vs/workbench/services/search/node/ripgrep
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, IMainContext, MainContext, MainThreadSearchShape } from '../common/extHost.protocol';
import { IURITransformer } from 'vs/base/common/uriIpc';
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';
export class ExtHostSearch implements ExtHostSearchShape {
@@ -32,16 +34,30 @@ export class ExtHostSearch implements ExtHostSearchShape {
private _fileSearchManager: FileSearchManager;
constructor(mainContext: IMainContext, private _uriTransformer: IURITransformer | null, private _logService: ILogService, private _pfs = pfs) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSearch);
protected _pfs: typeof pfs = pfs; // allow extending for tests
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
@IURITransformerService private _uriTransformer: IURITransformerService,
@ILogService private _logService: ILogService,
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadSearch);
this._fileSearchManager = new FileSearchManager();
if (initData.remote.isRemote && initData.remote.authority) {
this._registerEHSearchProviders();
}
}
private _registerEHSearchProviders(): void {
const outputChannel = new OutputChannel(this._logService);
this.registerTextSearchProvider('file', new RipgrepSearchProvider(outputChannel));
this.registerInternalFileSearchProvider('file', new SearchService());
}
private _transformScheme(scheme: string): string {
if (this._uriTransformer) {
return this._uriTransformer.transformOutgoingScheme(scheme);
}
return scheme;
return this._uriTransformer.transformOutgoingScheme(scheme);
}
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable {
@@ -148,12 +164,6 @@ export class ExtHostSearch implements ExtHostSearchShape {
}
}
export function registerEHSearchProviders(extHostSearch: ExtHostSearch, logService: ILogService): void {
const outputChannel = new OutputChannel(logService);
extHostSearch.registerTextSearchProvider('file', new RipgrepSearchProvider(outputChannel));
extHostSearch.registerInternalFileSearchProvider('file', new SearchService());
}
function reviveQuery<U extends IRawQuery>(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery {
return {
...<any>rawQuery, // TODO

View File

@@ -8,23 +8,24 @@ import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { IEnvironment, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol';
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';
export class ExtensionStoragePaths {
export class ExtensionStoragePaths implements IExtensionStoragePaths {
readonly _serviceBrand: undefined;
private readonly _workspace?: IStaticWorkspaceData;
private readonly _environment: IEnvironment;
private readonly _ready: Promise<string | undefined>;
readonly whenReady: Promise<string | undefined>;
private _value?: string;
constructor(workspace: IStaticWorkspaceData | undefined, environment: IEnvironment) {
this._workspace = workspace;
this._environment = environment;
this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
}
get whenReady(): Promise<any> {
return this._ready;
constructor(@IExtHostInitDataService initData: IExtHostInitDataService) {
this._workspace = withNullAsUndefined(initData.workspace);
this._environment = initData.environment;
this.whenReady = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
}
workspaceValue(extension: IExtensionDescription): string | undefined {

View File

@@ -12,27 +12,28 @@ import { Event, Emitter } from 'vs/base/common/event';
import { win32 } from 'vs/base/node/processes';
import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { MainContext, MainThreadTaskShape, ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol';
import * as types from 'vs/workbench/api/common/extHostTypes';
import { ExtHostWorkspace, IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorkspace';
import { IExtHostWorkspaceProvider, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import * as vscode from 'vscode';
import {
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO,
ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO,
CustomExecutionDTO,
CustomExecution2DTO,
TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
} from '../common/shared/tasks';
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/node/extHostTerminalService';
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 { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { CancellationToken } from 'vs/base/common/cancellation';
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 { Schemas } from 'vs/base/common/network';
namespace TaskDefinitionDTO {
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO | undefined {
@@ -80,7 +81,7 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO {
if (value) {
const candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
@@ -125,7 +126,7 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO {
if (value) {
const candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
@@ -162,25 +163,8 @@ namespace ShellExecutionDTO {
}
}
namespace CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecutionDTO {
if (value) {
let candidate = value as CustomExecutionDTO;
return candidate && candidate.customExecution === 'customExecution';
} else {
return false;
}
}
export function from(value: vscode.CustomExecution): CustomExecutionDTO {
return {
customExecution: 'customExecution'
};
}
}
namespace CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO {
if (value) {
let candidate = value as CustomExecution2DTO;
return candidate && candidate.customExecution === 'customExecution2';
@@ -229,13 +213,11 @@ namespace TaskDTO {
if (value === undefined || value === null) {
return undefined;
}
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined;
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined;
if (value.execution instanceof types.ProcessExecution) {
execution = ProcessExecutionDTO.from(value.execution);
} else if (value.execution instanceof types.ShellExecution) {
execution = ShellExecutionDTO.from(value.execution);
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution) {
execution = CustomExecutionDTO.from(<types.CustomExecution>(<vscode.Task2>value).execution2);
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution2) {
execution = CustomExecution2DTO.from(<types.CustomExecution2>(<vscode.Task2>value).execution2);
}
@@ -373,122 +355,18 @@ interface HandlerData {
extension: IExtensionDescription;
}
class CustomExecutionData implements IDisposable {
private static waitForDimensionsTimeoutInMs: number = 5000;
private _cancellationSource?: CancellationTokenSource;
private readonly _onTaskExecutionComplete: Emitter<CustomExecutionData> = new Emitter<CustomExecutionData>();
private readonly _disposables = new DisposableStore();
private terminal?: vscode.Terminal;
private terminalId?: number;
public result: number | undefined;
constructor(
private readonly customExecution: vscode.CustomExecution,
private readonly terminalService: ExtHostTerminalService) {
}
public dispose(): void {
this._cancellationSource = undefined;
this._disposables.dispose();
}
public get onTaskExecutionComplete(): Event<CustomExecutionData> {
return this._onTaskExecutionComplete.event;
}
private onDidCloseTerminal(terminal: vscode.Terminal): void {
if ((this.terminal === terminal) && this._cancellationSource) {
this._cancellationSource.cancel();
}
}
private onDidOpenTerminal(terminal: vscode.Terminal): void {
if (!(terminal instanceof ExtHostTerminal)) {
throw new Error('How could this not be a extension host terminal?');
}
if (this.terminalId && terminal._id === this.terminalId) {
this.startCallback(this.terminalId);
}
}
public async startCallback(terminalId: number): Promise<void> {
this.terminalId = terminalId;
// If we have already started the extension task callback, then
// do not start it again.
// It is completely valid for multiple terminals to be opened
// before the one for our task.
if (this._cancellationSource) {
return undefined;
}
const callbackTerminals: vscode.Terminal[] = this.terminalService.terminals.filter((terminal) => terminal._id === terminalId);
if (!callbackTerminals || callbackTerminals.length === 0) {
this._disposables.add(this.terminalService.onDidOpenTerminal(this.onDidOpenTerminal.bind(this)));
return;
}
if (callbackTerminals.length !== 1) {
throw new Error(`Expected to only have one terminal at this point`);
}
this.terminal = callbackTerminals[0];
const terminalRenderer: vscode.TerminalRenderer = await this.terminalService.resolveTerminalRenderer(terminalId);
// If we don't have the maximum dimensions yet, then we need to wait for them (but not indefinitely).
// Custom executions will expect the dimensions to be set properly before they are launched.
// BUT, due to the API contract VSCode has for terminals and dimensions, they are still responsible for
// handling cases where they are not set.
if (!terminalRenderer.maximumDimensions) {
const dimensionTimeout: Promise<void> = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, CustomExecutionData.waitForDimensionsTimeoutInMs);
});
let dimensionsRegistration: IDisposable | undefined;
const dimensionsPromise: Promise<void> = new Promise((resolve) => {
dimensionsRegistration = terminalRenderer.onDidChangeMaximumDimensions((newDimensions) => {
resolve();
});
});
await Promise.race([dimensionTimeout, dimensionsPromise]);
if (dimensionsRegistration) {
dimensionsRegistration.dispose();
}
}
this._cancellationSource = new CancellationTokenSource();
this._disposables.add(this._cancellationSource);
this._disposables.add(this.terminalService.onDidCloseTerminal(this.onDidCloseTerminal.bind(this)));
// Regardless of how the task completes, we are done with this custom execution task.
this.customExecution.callback(terminalRenderer, this._cancellationSource.token).then(
(success) => {
this.result = success;
this._onTaskExecutionComplete.fire(this);
}, (rejected) => {
this._onTaskExecutionComplete.fire(this);
});
}
}
export class ExtHostTask implements ExtHostTaskShape {
private _proxy: MainThreadTaskShape;
private _workspaceProvider: IExtHostWorkspaceProvider;
private _editorService: ExtHostDocumentsAndEditors;
private _configurationService: ExtHostConfiguration;
private _terminalService: ExtHostTerminalService;
readonly _serviceBrand: any;
private readonly _proxy: MainThreadTaskShape;
private readonly _workspaceProvider: IExtHostWorkspaceProvider;
private readonly _editorService: IExtHostDocumentsAndEditors;
private readonly _configurationService: IExtHostConfiguration;
private readonly _terminalService: IExtHostTerminalService;
private _handleCounter: number;
private _handlers: Map<number, HandlerData>;
private _taskExecutions: Map<string, TaskExecutionImpl>;
private _providedCustomExecutions: Map<string, CustomExecutionData>;
private _activeCustomExecutions: Map<string, CustomExecutionData>;
private _providedCustomExecutions2: Map<string, vscode.CustomExecution2>;
private _activeCustomExecutions2: Map<string, vscode.CustomExecution2>;
@@ -499,12 +377,14 @@ export class ExtHostTask implements ExtHostTaskShape {
private readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
constructor(
mainContext: IMainContext,
workspaceService: ExtHostWorkspace,
editorService: ExtHostDocumentsAndEditors,
configurationService: ExtHostConfiguration,
extHostTerminalService: ExtHostTerminalService) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTask);
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
@IExtHostConfiguration configurationService: IExtHostConfiguration,
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask);
this._workspaceProvider = workspaceService;
this._editorService = editorService;
this._configurationService = configurationService;
@@ -512,10 +392,16 @@ export class ExtHostTask implements ExtHostTaskShape {
this._handleCounter = 0;
this._handlers = new Map<number, HandlerData>();
this._taskExecutions = new Map<string, TaskExecutionImpl>();
this._providedCustomExecutions = new Map<string, CustomExecutionData>();
this._activeCustomExecutions = new Map<string, CustomExecutionData>();
this._providedCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
this._activeCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
if (initData.remote.isRemote && initData.remote.authority) {
this.registerTaskSystem(Schemas.vscodeRemote, {
scheme: Schemas.vscodeRemote,
authority: initData.remote.authority,
platform: process.platform
});
}
}
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
@@ -558,6 +444,14 @@ export class ExtHostTask implements ExtHostTaskShape {
if (dto === undefined) {
return Promise.reject(new Error('Task is not valid'));
}
// If this task is a custom execution, then we need to save it away
// in the provided custom execution map that is cleaned up after the
// task is executed.
if (CustomExecution2DTO.is(dto.execution)) {
await this.addCustomExecution2(dto, <vscode.Task2>task);
}
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
}
}
@@ -588,26 +482,7 @@ export class ExtHostTask 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, execution2);
await this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback());
}
// Once a terminal is spun up for the custom execution task this event will be fired.
// At that point, we need to actually start the callback, but
// only if it hasn't already begun.
const extensionCallback: CustomExecutionData | undefined = this._providedCustomExecutions.get(execution.id);
if (extensionCallback) {
if (this._activeCustomExecutions.get(execution.id) !== undefined) {
throw new Error('We should not be trying to start the same custom task executions twice.');
}
this._activeCustomExecutions.set(execution.id, extensionCallback);
const taskExecutionComplete: IDisposable = extensionCallback.onTaskExecutionComplete(() => {
this.customExecutionComplete(execution);
taskExecutionComplete.dispose();
});
extensionCallback.startCallback(terminalId);
this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback());
}
this._onDidExecuteTask.fire({
@@ -662,12 +537,6 @@ export class ExtHostTask implements ExtHostTaskShape {
return Promise.reject(new Error('no handler found'));
}
// For custom execution tasks, we need to store the execution objects locally
// since we obviously cannot send callback functions through the proxy.
// So, clear out any existing ones.
this._providedCustomExecutions.clear();
this._providedCustomExecutions2.clear();
// Set up a list of task ID promises that we can wait on
// before returning the provided tasks. The ensures that
// our task IDs are calculated for any custom execution tasks.
@@ -689,14 +558,11 @@ export class ExtHostTask implements ExtHostTaskShape {
if (taskDTO) {
taskDTOs.push(taskDTO);
if (CustomExecutionDTO.is(taskDTO.execution)) {
if (CustomExecution2DTO.is(taskDTO.execution)) {
// The ID is calculated on the main thread task side, so, let's call into it here.
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
// is invoked, we have to be able to map it back to our data.
taskIdPromises.push(this.addCustomExecution(taskDTO, <vscode.Task2>task));
} else if (CustomExecution2DTO.is(taskDTO.execution)) {
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task));
}
}
}
@@ -741,12 +607,12 @@ export class ExtHostTask implements ExtHostTaskShape {
throw new Error('Unexpected: Task cannot be resolved.');
}
if (CustomExecutionDTO.is(resolvedTaskDTO.execution)) {
await this.addCustomExecution(taskDTO, <vscode.Task2>task);
if (resolvedTask.definition !== task.definition) {
throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.');
}
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
await this.addCustomExecution2(taskDTO, <vscode.Task2>task);
await this.addCustomExecution2(resolvedTaskDTO, <vscode.Task2>resolvedTask);
}
return resolvedTaskDTO;
@@ -797,11 +663,6 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._handleCounter++;
}
private async addCustomExecution(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
const taskId = await this._proxy.$createTaskId(taskDTO);
this._providedCustomExecutions.set(taskId, new CustomExecutionData(<vscode.CustomExecution>(<vscode.Task2>task).execution2, this._terminalService));
}
private async addCustomExecution2(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
const taskId = await this._proxy.$createTaskId(taskDTO);
this._providedCustomExecutions2.set(taskId, <vscode.CustomExecution2>(<vscode.Task2>task).execution2);
@@ -830,15 +691,21 @@ export class ExtHostTask implements ExtHostTaskShape {
}
private customExecutionComplete(execution: TaskExecutionDTO): void {
const extensionCallback: CustomExecutionData | undefined = this._activeCustomExecutions.get(execution.id);
if (extensionCallback) {
this._activeCustomExecutions.delete(execution.id);
this._proxy.$customExecutionComplete(execution.id, extensionCallback.result);
extensionCallback.dispose();
}
const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id);
if (extensionCallback2) {
this._activeCustomExecutions2.delete(execution.id);
}
const lastCustomExecution = this._providedCustomExecutions2.get(execution.id);
// Technically we don't really need to do this, however, if an extension
// is executing a task through "executeTask" over and over again
// with different properties in the task definition, then this list
// could grow indefinitely, something we don't want.
this._providedCustomExecutions2.clear();
// We do still need to hang on to the last custom execution so that the
// Rerun Task command doesn't choke when it tries to rerun a custom execution
if (lastCustomExecution) {
this._providedCustomExecutions2.set(execution.id, lastCustomExecution);
}
}
}

View File

@@ -10,26 +10,25 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration, ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ILogService } from 'vs/platform/log/common/log';
import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig, ITerminalEnvironment, ITerminalChildProcess, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
import { timeout } from 'vs/base/common/async';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
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 { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
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';
import { IDisposable } from 'vs/base/common/lifecycle';
const RENDERER_NO_PROCESS_ID = -1;
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export class BaseExtHostTerminal {
public _id: number;
public _id: number | undefined;
protected _idPromise: Promise<number>;
private _idPromiseComplete: (value: number) => any;
private _idPromiseComplete: ((value: number) => any) | undefined;
private _disposed: boolean = false;
private _queuedRequests: ApiRequest[] = [];
@@ -71,9 +70,12 @@ export class BaseExtHostTerminal {
public _runQueuedRequests(id: number): void {
this._id = id;
this._idPromiseComplete(id);
if (this._idPromiseComplete) {
this._idPromiseComplete(id);
this._idPromiseComplete = undefined;
}
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
r.run(this._proxy, id);
});
this._queuedRequests.length = 0;
}
@@ -82,32 +84,29 @@ export class BaseExtHostTerminal {
export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Terminal {
private _pidPromise: Promise<number | undefined>;
private _cols: number | undefined;
private _pidPromiseComplete: ((value: number | undefined) => any) | null;
private _pidPromiseComplete: ((value: number | undefined) => any) | undefined;
private _rows: number | undefined;
/** @deprecated */
private readonly _onData = new Emitter<string>();
/** @deprecated */
public get onDidWriteData(): Event<string> {
// Tell the main side to start sending data if it's not already
this._idPromise.then(c => {
this._proxy.$registerOnDataListener(this._id);
this._idPromise.then(id => {
this._proxy.$registerOnDataListener(id);
});
return this._onData.event;
}
public isOpen: boolean = false;
constructor(
proxy: MainThreadTerminalServiceShape,
private _name?: string,
id?: number,
pid?: number
id?: number
) {
super(proxy, id);
this._pidPromise = new Promise<number>(c => {
if (pid === RENDERER_NO_PROCESS_ID) {
c(undefined);
} else {
this._pidPromiseComplete = c;
}
});
this._pidPromise = new Promise<number>(c => this._pidPromiseComplete = c);
}
public async create(
@@ -124,10 +123,11 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
this._runQueuedRequests(terminal.id);
}
public async createExtensionTerminal(): Promise<void> {
public async createExtensionTerminal(): Promise<number> {
const terminal = await this._proxy.$createTerminal({ name: this._name, isExtensionTerminal: true });
this._name = terminal.name;
this._runQueuedRequests(terminal.id);
return terminal.id;
}
public get name(): string {
@@ -181,7 +181,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
// The event may fire 2 times when the panel is restored
if (this._pidPromiseComplete) {
this._pidPromiseComplete(processId);
this._pidPromiseComplete = null;
this._pidPromiseComplete = undefined;
} else {
// Recreate the promise if this is the nth processId set (e.g. reused task terminals)
this._pidPromise.then(pid => {
@@ -197,92 +197,14 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
}
}
export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vscode.TerminalRenderer {
public get name(): string { return this._name; }
public set name(newName: string) {
this._name = newName;
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererSetName, [this._name]);
}
export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostTerminalServiceShape {
private readonly _onInput = new Emitter<string>();
public get onDidAcceptInput(): Event<string> {
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererRegisterOnInputListener, [this._id]);
// Tell the main side to start sending data if it's not already
// this._proxy.$terminalRendererRegisterOnDataListener(this._id);
return this._onInput && this._onInput.event;
}
readonly _serviceBrand: any;
private _dimensions: vscode.TerminalDimensions | undefined;
public get dimensions(): vscode.TerminalDimensions | undefined { return this._dimensions; }
public set dimensions(dimensions: vscode.TerminalDimensions | undefined) {
this._checkDisposed();
this._dimensions = dimensions;
this._queueApiRequest(this._proxy.$terminalRendererSetDimensions, [dimensions]);
}
private _maximumDimensions: vscode.TerminalDimensions | undefined;
public get maximumDimensions(): vscode.TerminalDimensions | undefined {
if (!this._maximumDimensions) {
return undefined;
}
return {
rows: this._maximumDimensions.rows,
columns: this._maximumDimensions.columns
};
}
private readonly _onDidChangeMaximumDimensions: Emitter<vscode.TerminalDimensions> = new Emitter<vscode.TerminalDimensions>();
public get onDidChangeMaximumDimensions(): Event<vscode.TerminalDimensions> {
return this._onDidChangeMaximumDimensions && this._onDidChangeMaximumDimensions.event;
}
public get terminal(): ExtHostTerminal {
return this._terminal;
}
constructor(
proxy: MainThreadTerminalServiceShape,
private _name: string,
private _terminal: ExtHostTerminal,
id?: number
) {
super(proxy, id);
if (!id) {
this._proxy.$createTerminalRenderer(this._name).then(id => {
this._runQueuedRequests(id);
(<any>this._terminal)._runQueuedRequests(id);
});
}
}
public write(data: string): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererWrite, [data]);
}
public _fireOnInput(data: string): void {
this._onInput.fire(data);
}
public _setMaximumDimensions(columns: number, rows: number): void {
if (this._maximumDimensions && this._maximumDimensions.columns === columns && this._maximumDimensions.rows === rows) {
return;
}
const newValue = { columns, rows };
this._maximumDimensions = newValue;
this._onDidChangeMaximumDimensions.fire(newValue);
}
}
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _proxy: MainThreadTerminalServiceShape;
private _activeTerminal: ExtHostTerminal | undefined;
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: ITerminalChildProcess } = {};
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
private _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
private _variableResolver: ExtHostVariableResolverService | undefined;
private _lastActiveWorkspace: IWorkspaceFolder | undefined;
@@ -301,15 +223,21 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
public get onDidChangeActiveTerminal(): Event<vscode.Terminal | undefined> { return this._onDidChangeActiveTerminal && this._onDidChangeActiveTerminal.event; }
private readonly _onDidChangeTerminalDimensions: Emitter<vscode.TerminalDimensionsChangeEvent> = new Emitter<vscode.TerminalDimensionsChangeEvent>();
public get onDidChangeTerminalDimensions(): Event<vscode.TerminalDimensionsChangeEvent> { return this._onDidChangeTerminalDimensions && this._onDidChangeTerminalDimensions.event; }
private readonly _onDidWriteTerminalData: Emitter<vscode.TerminalDataWriteEvent>;
public get onDidWriteTerminalData(): Event<vscode.TerminalDataWriteEvent> { return this._onDidWriteTerminalData && this._onDidWriteTerminalData.event; }
constructor(
mainContext: IMainContext,
private _extHostConfiguration: ExtHostConfiguration,
private _extHostWorkspace: ExtHostWorkspace,
private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
private _logService: ILogService
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostConfiguration private _extHostConfiguration: ExtHostConfiguration,
@IExtHostWorkspace private _extHostWorkspace: ExtHostWorkspace,
@IExtHostDocumentsAndEditors private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
@ILogService private _logService: ILogService
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTerminalService);
this._onDidWriteTerminalData = new Emitter<vscode.TerminalDataWriteEvent>({
onFirstListenerAdd: () => this._proxy.$startSendingDataEvents(),
onLastListenerRemove: () => this._proxy.$stopSendingDataEvents()
});
this._updateLastActiveWorkspace();
this._updateVariableResolver();
this._registerListeners();
@@ -332,12 +260,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, options.name);
const p = new ExtHostPseudoterminal(options.pty);
terminal.createExtensionTerminal().then(() => this._setupExtHostProcessListeners(terminal._id, p));
terminal.createExtensionTerminal().then(id => this._setupExtHostProcessListeners(id, p));
this._terminals.push(terminal);
return terminal;
}
public async attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): Promise<void> {
public attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void {
const terminal = this._getTerminalByIdEventually(id);
if (!terminal) {
throw new Error(`Cannot resolve terminal with id ${id} for virtual process`);
@@ -346,18 +274,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._setupExtHostProcessListeners(id, p);
}
public createTerminalRenderer(name: string): vscode.TerminalRenderer {
const terminal = new ExtHostTerminal(this._proxy, name);
terminal._setProcessId(undefined);
this._terminals.push(terminal);
const renderer = new ExtHostTerminalRenderer(this._proxy, name, terminal);
this._terminalRenderers.push(renderer);
return renderer;
}
public getDefaultShell(configProvider: ExtHostConfigProvider): string {
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
const fetchSetting = (key: string) => {
const setting = configProvider
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
@@ -371,11 +288,13 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
process.env.windir,
this._lastActiveWorkspace,
this._variableResolver
this._variableResolver,
this._logService,
useAutomationShell
);
}
private _getDefaultShellArgs(configProvider: ExtHostConfigProvider): string[] | string {
private _getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
const fetchSetting = (key: string) => {
const setting = configProvider
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
@@ -383,28 +302,10 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return this._apiInspectConfigToPlain<string | string[]>(setting);
};
return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed, this._lastActiveWorkspace, this._variableResolver);
return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed, useAutomationShell, this._lastActiveWorkspace, this._variableResolver, this._logService);
}
public async resolveTerminalRenderer(id: number): Promise<vscode.TerminalRenderer> {
// Check to see if the extension host already knows about this terminal.
for (const terminalRenderer of this._terminalRenderers) {
if (terminalRenderer._id === id) {
return terminalRenderer;
}
}
const terminal = this._getTerminalById(id);
if (!terminal) {
throw new Error(`Cannot resolve terminal renderer for terminal id ${id}`);
}
const renderer = new ExtHostTerminalRenderer(this._proxy, terminal.name, terminal, terminal._id);
this._terminalRenderers.push(renderer);
return renderer;
}
public $acceptActiveTerminalChanged(id: number | null): void {
public async $acceptActiveTerminalChanged(id: number | null): Promise<void> {
const original = this._activeTerminal;
if (id === null) {
this._activeTerminal = undefined;
@@ -413,69 +314,62 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
return;
}
this.performTerminalIdAction(id, terminal => {
if (terminal) {
this._activeTerminal = terminal;
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
}
const terminal = await this._getTerminalByIdEventually(id);
if (terminal) {
this._activeTerminal = terminal;
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
}
});
}
}
public $acceptTerminalProcessData(id: number, data: string): void {
this._getTerminalByIdEventually(id).then(terminal => {
if (terminal) {
terminal._fireOnData(data);
}
});
/** @deprecated */
public async $acceptTerminalProcessData(id: number, data: string): Promise<void> {
const terminal = await this._getTerminalByIdEventually(id);
if (terminal) {
terminal._fireOnData(data);
}
}
public $acceptTerminalDimensions(id: number, cols: number, rows: number): void {
this._getTerminalByIdEventually(id).then(terminal => {
if (terminal) {
if (terminal.setDimensions(cols, rows)) {
this._onDidChangeTerminalDimensions.fire({
terminal: terminal,
dimensions: terminal.dimensions as vscode.TerminalDimensions
});
}
}
});
public async $acceptTerminalProcessData2(id: number, data: string): Promise<void> {
const terminal = await this._getTerminalByIdEventually(id);
if (terminal) {
this._onDidWriteTerminalData.fire({ terminal, data });
}
}
public $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void {
public async $acceptTerminalDimensions(id: number, cols: number, rows: number): Promise<void> {
const terminal = await this._getTerminalByIdEventually(id);
if (terminal) {
if (terminal.setDimensions(cols, rows)) {
this._onDidChangeTerminalDimensions.fire({
terminal: terminal,
dimensions: terminal.dimensions as vscode.TerminalDimensions
});
}
}
}
public async $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): Promise<void> {
await this._getTerminalByIdEventually(id);
if (this._terminalProcesses[id]) {
// Virtual processes only - when virtual process resize fires it means that the
// Extension pty terminal only - when virtual process resize fires it means that the
// terminal's maximum dimensions changed
this._terminalProcesses[id].resize(cols, rows);
} else {
// Terminal renderer
this._getTerminalByIdEventually(id).then(() => {
// When a terminal's dimensions change, a renderer's _maximum_ dimensions change
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._setMaximumDimensions(cols, rows);
}
});
}
}
public $acceptTerminalRendererInput(id: number, data: string): void {
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._fireOnInput(data);
}
}
public $acceptTerminalTitleChange(id: number, name: string): void {
public async $acceptTerminalTitleChange(id: number, name: string): Promise<void> {
await this._getTerminalByIdEventually(id);
const extHostTerminal = this._getTerminalObjectById(this.terminals, id);
if (extHostTerminal) {
extHostTerminal.name = name;
}
}
public $acceptTerminalClosed(id: number): void {
public async $acceptTerminalClosed(id: number): Promise<void> {
await this._getTerminalByIdEventually(id);
const index = this._getTerminalObjectIndexById(this.terminals, id);
if (index !== null) {
const terminal = this._terminals.splice(index, 1)[0];
@@ -488,20 +382,25 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
this._onDidOpenTerminal.fire(this.terminals[index]);
this.terminals[index].isOpen = true;
return;
}
const renderer = this._getTerminalRendererById(id);
const terminal = new ExtHostTerminal(this._proxy, name, id, renderer ? RENDERER_NO_PROCESS_ID : undefined);
const terminal = new ExtHostTerminal(this._proxy, name, id);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
terminal.isOpen = true;
}
public $acceptTerminalProcessId(id: number, processId: number): void {
this.performTerminalIdAction(id, terminal => terminal._setProcessId(processId));
public async $acceptTerminalProcessId(id: number, processId: number): Promise<void> {
const terminal = await this._getTerminalByIdEventually(id);
if (terminal) {
terminal._setProcessId(processId);
}
}
public performTerminalIdAction(id: number, callback: (terminal: ExtHostTerminal) => void): void {
// TODO: Use await this._getTerminalByIdEventually(id);
let terminal = this._getTerminalById(id);
if (terminal) {
callback(terminal);
@@ -550,7 +449,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider);
}
public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
const shellLaunchConfig: IShellLaunchConfig = {
name: shellLaunchConfigDto.name,
executable: shellLaunchConfigDto.executable,
@@ -563,8 +462,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
const configProvider = await this._extHostConfiguration.getConfigProvider();
if (!shellLaunchConfig.executable) {
shellLaunchConfig.executable = this.getDefaultShell(configProvider);
shellLaunchConfig.args = this._getDefaultShellArgs(configProvider);
shellLaunchConfig.executable = this.getDefaultShell(false, configProvider);
shellLaunchConfig.args = this._getDefaultShellArgs(false, configProvider);
} else {
if (this._variableResolver) {
shellLaunchConfig.executable = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.executable);
@@ -598,6 +497,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
const terminalConfig = configProvider.getConfiguration('terminal.integrated');
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), lastActiveWorkspace ? lastActiveWorkspace : undefined, this._variableResolver, activeWorkspaceRootUri, terminalConfig.cwd, this._logService);
shellLaunchConfig.cwd = initialCwd;
const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect<ITerminalEnvironment>(`env.${platformKey}`));
const baseEnv = terminalConfig.get<boolean>('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv();
@@ -624,7 +524,27 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<void> {
// Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call
// Pseudoterminal.start
await this._getTerminalByIdEventually(id);
const terminal = await this._getTerminalByIdEventually(id);
if (!terminal) {
return;
}
// Wait for onDidOpenTerminal to fire
let openPromise: Promise<void>;
if (terminal.isOpen) {
openPromise = Promise.resolve();
} else {
openPromise = new Promise<void>(r => {
// Ensure open is called after onDidOpenTerminal
const listener = this.onDidOpenTerminal(async e => {
if (e === terminal) {
listener.dispose();
r();
}
});
});
}
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
@@ -685,11 +605,11 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return detectAvailableShells();
}
public async $requestDefaultShellAndArgs(): Promise<IShellAndArgsDto> {
public async $requestDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
const configProvider = await this._extHostConfiguration.getConfigProvider();
return Promise.resolve({
shell: this.getDefaultShell(configProvider),
args: this._getDefaultShellArgs(configProvider)
shell: this.getDefaultShell(useAutomationShell, configProvider),
args: this._getDefaultShellArgs(useAutomationShell, configProvider)
});
}
@@ -701,7 +621,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._proxy.$sendProcessExit(id, exitCode);
}
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal> {
// TODO: This could be improved by using a single promise and resolve it when the terminal is ready
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal | undefined> {
if (!this._getTerminalPromises[id]) {
this._getTerminalPromises[id] = this._createGetTerminalPromise(id, retries);
} else {
@@ -734,16 +655,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return this._getTerminalObjectById(this._terminals, id);
}
private _getTerminalRendererById(id: number): ExtHostTerminalRenderer | null {
return this._getTerminalObjectById(this._terminalRenderers, id);
}
private _getTerminalObjectById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): T | null {
private _getTerminalObjectById<T extends ExtHostTerminal>(array: T[], id: number): T | null {
const index = this._getTerminalObjectIndexById(array, id);
return index !== null ? array[index] : null;
}
private _getTerminalObjectIndexById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): number | null {
private _getTerminalObjectIndexById<T extends ExtHostTerminal>(array: T[], id: number): number | null {
let index: number | null = null;
array.some((item, i) => {
const thisId = item._id;
@@ -776,9 +693,6 @@ class ApiRequest {
}
class ExtHostPseudoterminal implements ITerminalChildProcess {
private _queuedEvents: (IQueuedEvent<string> | IQueuedEvent<number> | IQueuedEvent<{ pid: number, cwd: string }> | IQueuedEvent<ITerminalDimensions | undefined>)[] = [];
private _queueDisposables: IDisposable[] | undefined;
private readonly _onProcessData = new Emitter<string>();
public readonly onProcessData: Event<string> = this._onProcessData.event;
private readonly _onProcessExit = new Emitter<number>();
@@ -790,23 +704,10 @@ class ExtHostPseudoterminal implements ITerminalChildProcess {
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensions | undefined>();
public get onProcessOverrideDimensions(): Event<ITerminalDimensions | undefined> { return this._onProcessOverrideDimensions.event; }
constructor(
private readonly _pty: vscode.Pseudoterminal
) {
this._queueDisposables = [];
this._queueDisposables.push(this._pty.onDidWrite(e => this._queuedEvents.push({ emitter: this._onProcessData, data: e })));
if (this._pty.onDidClose) {
this._queueDisposables.push(this._pty.onDidClose(e => this._queuedEvents.push({ emitter: this._onProcessExit, data: 0 })));
}
if (this._pty.onDidOverrideDimensions) {
this._queueDisposables.push(this._pty.onDidOverrideDimensions(e => this._queuedEvents.push({ emitter: this._onProcessOverrideDimensions, data: e ? { cols: e.columns, rows: e.rows } : undefined })));
}
}
constructor(private readonly _pty: vscode.Pseudoterminal) { }
shutdown(): void {
if (this._pty.close) {
this._pty.close();
}
this._pty.close();
}
input(data: string): void {
@@ -834,12 +735,7 @@ class ExtHostPseudoterminal implements ITerminalChildProcess {
}
startSendingEvents(initialDimensions: ITerminalDimensionsDto | undefined): void {
// Flush all buffered events
this._queuedEvents.forEach(e => (<any>e.emitter.fire)(e.data));
this._queuedEvents = [];
this._queueDisposables = undefined;
// Attach the real listeners
// Attach the listeners
this._pty.onDidWrite(e => this._onProcessData.fire(e));
if (this._pty.onDidClose) {
this._pty.onDidClose(e => this._onProcessExit.fire(0));
@@ -848,13 +744,7 @@ class ExtHostPseudoterminal implements ITerminalChildProcess {
this._pty.onDidOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e ? { cols: e.columns, rows: e.rows } : e));
}
if (this._pty.open) {
this._pty.open(initialDimensions);
}
this._pty.open(initialDimensions ? initialDimensions : undefined);
}
}
interface IQueuedEvent<T> {
emitter: Emitter<T>;
data: T;
}

View File

@@ -0,0 +1,168 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl';
import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator';
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { endsWith, startsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { joinPath } from 'vs/base/common/resources';
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
class ExportsTrap {
static readonly Instance = new ExportsTrap();
private readonly _names: string[] = [];
private readonly _exports = new Map<string, any>();
private constructor() {
const exportsProxy = new Proxy({}, {
set: (target: any, p: PropertyKey, value: any) => {
// store in target
target[p] = value;
// store in named-bucket
const name = this._names[this._names.length - 1];
this._exports.get(name)![p] = value;
return true;
}
});
const moduleProxy = new Proxy({}, {
get: (target: any, p: PropertyKey) => {
if (p === 'exports') {
return exportsProxy;
}
return target[p];
},
set: (target: any, p: PropertyKey, value: any) => {
// store in target
target[p] = value;
// override bucket
if (p === 'exports') {
const name = this._names[this._names.length - 1];
this._exports.set(name, value);
}
return true;
}
});
(<any>self).exports = exportsProxy;
(<any>self).module = moduleProxy;
}
add(name: string) {
this._exports.set(name, Object.create(null));
this._names.push(name);
return {
claim: () => {
const result = this._exports.get(name);
this._exports.delete(name);
this._names.pop();
return result;
}
};
}
}
class WorkerRequireInterceptor extends RequireInterceptor {
_installInterceptor() { }
getModule(request: string, parent: URI): undefined | any {
for (let alternativeModuleName of this._alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {
request = alternative;
break;
}
}
if (this._factories.has(request)) {
return this._factories.get(request)!.load(request, parent, () => { throw new Error('CANNOT LOAD MODULE from here.'); });
}
return undefined;
}
}
export class ExtHostExtensionService extends AbstractExtHostExtensionService {
private _fakeModules: WorkerRequireInterceptor;
protected async _beforeAlmostReadyToRunExtensions(): Promise<void> {
// initialize API and register actors
const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._registry);
await this._fakeModules.install();
}
protected _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
(<any>self).window = self; // <- that's improper but might help extensions that aren't authored correctly
// FAKE require function that only works for the vscode-module
const moduleStack: URI[] = [];
(<any>self).require = (mod: string) => {
const parent = moduleStack[moduleStack.length - 1];
const result = this._fakeModules.getModule(mod, parent);
if (result !== undefined) {
return result;
}
if (!startsWith(mod, '.')) {
throw new Error(`Cannot load module '${mod}'`);
}
const next = joinPath(parent, '..', ensureSuffix(mod, '.js'));
moduleStack.push(next);
const trap = ExportsTrap.Instance.add(next.toString());
importScripts(asDomUri(next).toString(true));
moduleStack.pop();
return trap.claim();
};
try {
activationTimesBuilder.codeLoadingStart();
module = module.with({ path: ensureSuffix(module.path, '.js') });
moduleStack.push(module);
const trap = ExportsTrap.Instance.add(module.toString());
importScripts(asDomUri(module).toString(true));
moduleStack.pop();
return Promise.resolve<T>(trap.claim());
} finally {
activationTimesBuilder.codeLoadingStop();
}
}
async $setRemoteEnvironment(_env: { [key: string]: string | null }): Promise<void> {
throw new Error('Not supported');
}
}
// todo@joh this is a copy of `dom.ts#asDomUri`
function asDomUri(uri: URI): URI {
if (Schemas.vscodeRemote === uri.scheme) {
// rewrite vscode-remote-uris to uris of the window location
// so that they can be intercepted by the service worker
return URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(uri) });
}
return uri;
}
function ensureSuffix(path: string, suffix: string): string {
return endsWith(path, suffix) ? path : path + suffix;
}

View File

@@ -0,0 +1,84 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
import * as vscode from 'vscode';
export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape {
_serviceBrand: any;
private readonly _logChannel: vscode.OutputChannel;
constructor(
@IExtHostInitDataService initData: IExtHostInitDataService,
@IExtHostOutputService extHostOutputService: IExtHostOutputService
) {
super();
this.setLevel(initData.logLevel);
this._logChannel = extHostOutputService.createOutputChannel('Log (Worker Extension Host)');
}
$setLevel(level: LogLevel): void {
this.setLevel(level);
}
trace(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
this._logChannel.appendLine(this._format(arguments));
}
}
debug(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
this._logChannel.appendLine(this._format(arguments));
}
}
info(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
this._logChannel.appendLine(this._format(arguments));
}
}
warn(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
this._logChannel.appendLine(this._format(arguments));
}
}
error(_message: string | Error, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
this._logChannel.appendLine(this._format(arguments));
}
}
critical(_message: string | Error, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
this._logChannel.appendLine(String(arguments));
}
}
private _format(args: any): string {
let result = '';
for (let i = 0; i < args.length; i++) {
let a = args[i];
if (typeof a === 'object') {
try {
a = JSON.stringify(a);
} catch (e) { }
}
result += (i > 0 ? ' ' : '') + a;
}
return result;
}
}