Merge branch 'master' into sandy081/outputChannel

This commit is contained in:
Sandeep Somavarapu
2018-09-21 10:15:35 +02:00
843 changed files with 18519 additions and 10988 deletions

View File

@@ -77,7 +77,7 @@ CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand
export class OpenAPICommand {
public static ID = 'vscode.open';
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions): Thenable<any> {
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Thenable<any> {
let options: ITextEditorOptions;
let position: EditorViewColumn;
@@ -93,7 +93,8 @@ export class OpenAPICommand {
return executor.executeCommand('_workbench.open', [
resource,
options,
position
position,
label
]);
}
}

View File

@@ -61,6 +61,7 @@ import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview';
import { ExtHostComments } from './extHostComments';
import { ExtHostSearch } from './extHostSearch';
import { ExtHostUrls } from './extHostUrls';
import { localize } from 'vs/nls';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
@@ -119,7 +120,7 @@ export function createApiFactory(
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, extHostLogService));
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService));
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration));
@@ -137,7 +138,10 @@ export function createApiFactory(
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
const extHostOutputService = new ExtHostOutputService(initData.logsLocation, rpcProtocol);
const extHostLanguages = new ExtHostLanguages(rpcProtocol);
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
// Register an output channel for exthost log
extHostOutputService.createOutputChannelFromLogFile(localize('extensionsLog', "Extension Host"), extHostLogService.logFile);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
@@ -265,8 +269,7 @@ export function createApiFactory(
getLanguages(): Thenable<string[]> {
return extHostLanguages.getLanguages();
},
changeLanguage(document: vscode.TextDocument, languageId: string): Thenable<void> {
checkProposedApiEnabled(extension);
setTextDocumentLanguage(document: vscode.TextDocument, languageId: string): Thenable<vscode.TextDocument> {
return extHostLanguages.changeLanguage(document.uri, languageId);
},
match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number {
@@ -652,7 +655,7 @@ export function createApiFactory(
return extHostDebugService.onDidChangeBreakpoints(listener, thisArgs, disposables);
},
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider) {
return extHostDebugService.registerDebugConfigurationProvider(debugType, provider);
return extHostDebugService.registerDebugConfigurationProvider(extension, debugType, provider);
},
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) {
return extHostDebugService.startDebugging(folder, nameOrConfig);
@@ -721,6 +724,8 @@ export function createApiFactory(
CompletionTriggerKind: extHostTypes.CompletionTriggerKind,
ConfigurationTarget: extHostTypes.ConfigurationTarget,
DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
DebugAdapterServer: extHostTypes.DebugAdapterServer,
DebugAdapterImplementation: extHostTypes.DebugAdapterImplementation,
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
Diagnostic: extHostTypes.Diagnostic,
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
@@ -755,6 +760,7 @@ export function createApiFactory(
Selection: extHostTypes.Selection,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
SignatureHelpTriggerReason: extHostTypes.SignatureHelpTriggerReason,
SignatureHelp: extHostTypes.SignatureHelp,
SignatureInformation: extHostTypes.SignatureInformation,
SnippetString: extHostTypes.SnippetString,

View File

@@ -33,7 +33,7 @@ import { EndOfLine, IFileOperationOptions, TextEditorLineNumbersStyle } from 'vs
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { TaskDTO, TaskExecutionDTO, TaskFilterDTO, TaskHandleDTO, TaskProcessEndedDTO, TaskProcessStartedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks';
import { ITreeItem } from 'vs/workbench/common/views';
import { IAdapterExecutable, IConfig, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { IConfig, ITerminalSettings, IAdapterDescriptor } from 'vs/workbench/parts/debug/common/debug';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
import { ITerminalDimensions } from 'vs/workbench/parts/terminal/common/terminal';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
@@ -59,6 +59,7 @@ export interface IWorkspaceData {
}
export interface IInitData {
commit: string;
parentPid: number;
environment: IEnvironment;
workspace: IWorkspaceData;
@@ -226,6 +227,7 @@ export interface ISerializedIndentationRule {
export interface ISerializedOnEnterRule {
beforeText: ISerializedRegExp;
afterText?: ISerializedRegExp;
oneLineAboveText?: ISerializedRegExp;
action: EnterAction;
}
export interface ISerializedLanguageConfiguration {
@@ -300,7 +302,7 @@ export interface MainThreadMessageServiceShape extends IDisposable {
}
export interface MainThreadOutputServiceShape extends IDisposable {
$register(label: string, file?: UriComponents): Thenable<string>;
$register(label: string, log: boolean, file?: UriComponents): Thenable<string>;
$append(channelId: string, value: string): Thenable<void>;
$clear(channelId: string): Thenable<void>;
$reveal(channelId: string, preserveFocus: boolean): Thenable<void>;
@@ -469,8 +471,8 @@ export interface ExtHostUrlsShape {
}
export interface MainThreadWorkspaceShape extends IDisposable {
$startFileSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Thenable<UriComponents[]>;
$startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number, token: CancellationToken): Thenable<void>;
$startFileSearch(includePattern: string, includeFolder: URI, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Thenable<UriComponents[]>;
$startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number, token: CancellationToken): Thenable<vscode.TextSearchComplete>;
$checkExists(includes: string[], token: CancellationToken): Thenable<boolean>;
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>;
@@ -575,7 +577,7 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
$acceptDAError(handle: number, name: string, message: string, stack: string): void;
$acceptDAExit(handle: number, code: number, signal: string): void;
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasDebugAdapterExecutable: boolean, handle: number): Thenable<void>;
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, hasProvideTrackerMethod: boolean, handle: number): Thenable<void>;
$unregisterDebugConfigurationProvider(handle: number): Thenable<void>;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable<boolean>;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Thenable<any>;
@@ -843,7 +845,7 @@ export interface ExtHostLanguageFeaturesShape {
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.SuggestContext, token: CancellationToken): Thenable<SuggestResultDto>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable<modes.ISuggestion>;
$releaseCompletionItems(handle: number, id: number): void;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.SignatureHelp>;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Thenable<modes.SignatureHelp>;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.ILink[]>;
$resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Thenable<modes.ILink>;
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Thenable<IRawColorInfo[]>;
@@ -942,20 +944,26 @@ export interface ISourceMultiBreakpointDto {
}[];
}
export interface IDebugSessionDto {
id: DebugSessionUUID;
type: string;
name: string;
}
export interface ExtHostDebugServiceShape {
$substituteVariables(folder: UriComponents | undefined, config: IConfig): Thenable<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable<void>;
$startDASession(handle: number, debugType: string, adapterExecutableInfo: IAdapterExecutable | null, debugPort: number): Thenable<void>;
$startDASession(handle: number, session: IDebugSessionDto, folder: UriComponents | undefined, debugConfiguration: IConfig): Thenable<void>;
$stopDASession(handle: number): Thenable<void>;
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Thenable<IConfig>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined): Thenable<IConfig[]>;
$debugAdapterExecutable(handle: number, folder: UriComponents | undefined): Thenable<IAdapterExecutable>;
$acceptDebugSessionStarted(id: DebugSessionUUID, type: string, name: string): void;
$acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void;
$acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void;
$acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void;
$acceptBreakpointsDelta(delat: IBreakpointsDeltaDto): void;
$provideDebugAdapter(handle: number, session: IDebugSessionDto, folderUri: UriComponents | undefined, debugConfiguration: IConfig): Thenable<IAdapterDescriptor>;
$acceptDebugSessionStarted(session: IDebugSessionDto): void;
$acceptDebugSessionTerminated(session: IDebugSessionDto): void;
$acceptDebugSessionActiveChanged(session: IDebugSessionDto): void;
$acceptDebugSessionCustomEvent(session: IDebugSessionDto, event: any): void;
$acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void;
}
@@ -988,6 +996,8 @@ export interface ExtHostCommentsShape {
$provideDocumentComments(handle: number, document: UriComponents): Thenable<modes.CommentInfo>;
$createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Thenable<modes.CommentThread>;
$replyToCommentThread(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): Thenable<modes.CommentThread>;
$editComment(handle: number, document: UriComponents, comment: modes.Comment, text: string): Thenable<modes.Comment>;
$deleteComment(handle: number, document: UriComponents, comment: modes.Comment): Thenable<void>;
$provideWorkspaceComments(handle: number): Thenable<modes.CommentThread[]>;
}

View File

@@ -112,7 +112,7 @@ export class ExtHostApiCommands {
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
],
returns: 'A promise that resolves to an array of SymbolInformation-instances.'
returns: 'A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.'
});
this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, {
description: 'Execute completion item provider.',
@@ -420,16 +420,27 @@ export class ExtHostApiCommands {
if (isFalsyOrEmpty(value)) {
return undefined;
}
let result: vscode.SymbolInformation[] = [];
for (const symbol of value) {
result.push(new types.SymbolInformation(
symbol.name,
typeConverters.SymbolKind.to(symbol.kind),
symbol.containerName,
new types.Location(resource, typeConverters.Range.to(symbol.range))
));
class MergedInfo extends types.SymbolInformation implements vscode.DocumentSymbol {
static to(symbol: modes.DocumentSymbol): MergedInfo {
let res = new MergedInfo(
symbol.name,
typeConverters.SymbolKind.to(symbol.kind),
symbol.containerName,
new types.Location(resource, typeConverters.Range.to(symbol.range))
);
res.detail = symbol.detail;
res.range = res.location.range;
res.selectionRange = typeConverters.Range.to(symbol.selectionRange);
res.children = symbol.children && symbol.children.map(MergedInfo.to);
return res;
}
detail: string;
range: vscode.Range;
selectionRange: vscode.Range;
children: vscode.DocumentSymbol[];
}
return result;
return value.map(MergedInfo.to);
});
}

View File

@@ -73,10 +73,10 @@ export class ExtHostComments implements ExtHostCommentsShape {
return TPromise.as(null);
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
let provider = this._documentProviders.get(handle);
return provider.createNewCommentThread(data.document, ran, text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(commentThread, this._commandsConverter) : null);
}).then(commentThread => commentThread ? convertToCommentThread(provider, commentThread, this._commandsConverter) : null);
}
$replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): Thenable<modes.CommentThread> {
@@ -87,10 +87,36 @@ export class ExtHostComments implements ExtHostCommentsShape {
return TPromise.as(null);
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
let provider = this._documentProviders.get(handle);
return provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(commentThread, this._commandsConverter) : null);
}).then(commentThread => commentThread ? convertToCommentThread(provider, commentThread, this._commandsConverter) : null);
}
$editComment(handle: number, uri: UriComponents, comment: modes.Comment, text: string): Thenable<modes.Comment> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
return provider.editComment(data.document, convertFromComment(comment), text, CancellationToken.None);
}).then(comment => convertToComment(provider, comment, this._commandsConverter));
}
$deleteComment(handle: number, uri: UriComponents, comment: modes.Comment): Thenable<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
return provider.deleteComment(data.document, convertFromComment(comment), CancellationToken.None);
});
}
$provideDocumentComments(handle: number, uri: UriComponents): Thenable<modes.CommentInfo> {
@@ -99,11 +125,10 @@ export class ExtHostComments implements ExtHostCommentsShape {
return TPromise.as(null);
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
let provider = this._documentProviders.get(handle);
return provider.provideDocumentComments(data.document, CancellationToken.None);
})
.then(commentInfo => commentInfo ? convertCommentInfo(handle, commentInfo, this._commandsConverter) : null);
}).then(commentInfo => commentInfo ? convertCommentInfo(handle, provider, commentInfo, this._commandsConverter) : null);
}
$provideWorkspaceComments(handle: number): Thenable<modes.CommentThread[]> {
@@ -115,7 +140,7 @@ export class ExtHostComments implements ExtHostCommentsShape {
return asThenable(() => {
return provider.provideWorkspaceComments(CancellationToken.None);
}).then(comments =>
comments.map(x => convertToCommentThread(x, this._commandsConverter)
comments.map(comment => convertToCommentThread(provider, comment, this._commandsConverter)
));
}
@@ -124,28 +149,28 @@ export class ExtHostComments implements ExtHostCommentsShape {
this._proxy.$onDidCommentThreadsChange(handle, {
owner: handle,
changed: event.changed.map(x => convertToCommentThread(x, this._commandsConverter)),
added: event.added.map(x => convertToCommentThread(x, this._commandsConverter)),
removed: event.removed.map(x => convertToCommentThread(x, this._commandsConverter))
changed: event.changed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)),
added: event.added.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)),
removed: event.removed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter))
});
});
}
}
function convertCommentInfo(owner: number, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo {
function convertCommentInfo(owner: number, provider: vscode.DocumentCommentProvider, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo {
return {
owner: owner,
threads: vscodeCommentInfo.threads.map(x => convertToCommentThread(x, commandsConverter)),
threads: vscodeCommentInfo.threads.map(x => convertToCommentThread(provider, x, commandsConverter)),
commentingRanges: vscodeCommentInfo.commentingRanges ? vscodeCommentInfo.commentingRanges.map(range => extHostTypeConverter.Range.from(range)) : []
};
}
function convertToCommentThread(vscodeCommentThread: vscode.CommentThread, commandsConverter: CommandsConverter): modes.CommentThread {
function convertToCommentThread(provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, vscodeCommentThread: vscode.CommentThread, commandsConverter: CommandsConverter): modes.CommentThread {
return {
threadId: vscodeCommentThread.threadId,
resource: vscodeCommentThread.resource.toString(),
range: extHostTypeConverter.Range.from(vscodeCommentThread.range),
comments: vscodeCommentThread.comments.map(comment => convertToComment(comment, commandsConverter)),
comments: vscodeCommentThread.comments.map(comment => convertToComment(provider, comment, commandsConverter)),
collapsibleState: vscodeCommentThread.collapsibleState
};
}
@@ -165,16 +190,22 @@ function convertFromComment(comment: modes.Comment): vscode.Comment {
commentId: comment.commentId,
body: extHostTypeConverter.MarkdownString.to(comment.body),
userName: comment.userName,
gravatar: comment.gravatar
gravatar: comment.gravatar,
canEdit: comment.canEdit,
canDelete: comment.canDelete
};
}
function convertToComment(vscodeComment: vscode.Comment, commandsConverter: CommandsConverter): modes.Comment {
function convertToComment(provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, vscodeComment: vscode.Comment, commandsConverter: CommandsConverter): modes.Comment {
const canEdit = !!(provider as vscode.DocumentCommentProvider).editComment && vscodeComment.canEdit;
const canDelete = !!(provider as vscode.DocumentCommentProvider).deleteComment && vscodeComment.canDelete;
return {
commentId: vscodeComment.commentId,
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
userName: vscodeComment.userName,
gravatar: vscodeComment.gravatar,
canEdit: canEdit,
canDelete: canDelete,
command: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : null
};
}

View File

@@ -13,16 +13,16 @@ import { asThenable } from 'vs/base/common/async';
import * as nls from 'vs/nls';
import {
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto
IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
} from 'vs/workbench/api/node/extHost.protocol';
import * as vscode from 'vscode';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint } from 'vs/workbench/api/node/extHostTypes';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable } from 'vs/workbench/api/node/extHostTypes';
import { generateUuid } from 'vs/base/common/uuid';
import { DebugAdapter, StreamDebugAdapter, SocketDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExecutableDebugAdapter, SocketDebugAdapter, AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { IAdapterExecutable, ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter } from 'vs/workbench/parts/debug/common/debug';
import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter } from 'vs/workbench/parts/debug/common/debug';
import { getTerminalLauncher, hasChildprocesses, prepareCommand } from 'vs/workbench/parts/debug/node/terminals';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver';
@@ -32,12 +32,16 @@ import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalSer
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/node/extHostCommands';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _handleCounter: number;
private _handlers: Map<number, vscode.DebugConfigurationProvider>;
private _providerHandleCounter: number;
private _providerByHandle: Map<number, vscode.DebugConfigurationProvider>;
private _providerByType: Map<string, vscode.DebugConfigurationProvider>;
private _providers: TypeProviderPair[];
private _debugServiceProxy: MainThreadDebugServiceShape;
private _debugSessions: Map<DebugSessionUUID, ExtHostDebugSession> = new Map<DebugSessionUUID, ExtHostDebugSession>();
@@ -65,7 +69,9 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private readonly _onDidChangeBreakpoints: Emitter<vscode.BreakpointsChangeEvent>;
private _aexCommands: Map<string, string>;
private _debugAdapters: Map<number, IDebugAdapter>;
private _debugAdaptersTrackers: Map<number, vscode.IDebugAdapterTracker>;
private _variableResolver: IConfigurationResolverService;
@@ -78,11 +84,17 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _extensionService: ExtHostExtensionService,
private _editorsService: ExtHostDocumentsAndEditors,
private _configurationService: ExtHostConfiguration,
private _terminalService: ExtHostTerminalService
private _terminalService: ExtHostTerminalService,
private _commandService: ExtHostCommands
) {
this._providerHandleCounter = 0;
this._providerByHandle = new Map();
this._providerByType = new Map();
this._providers = [];
this._handleCounter = 0;
this._handlers = new Map<number, vscode.DebugConfigurationProvider>();
this._aexCommands = new Map();
this._debugAdapters = new Map();
this._debugAdaptersTrackers = new Map();
this._onDidStartDebugSession = new Emitter<vscode.DebugSession>();
this._onDidTerminateDebugSession = new Emitter<vscode.DebugSession>();
@@ -102,7 +114,6 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._breakpoints = new Map<string, vscode.Breakpoint>();
this._breakpointEventsActive = false;
this._debugAdapters = new Map<number, DebugAdapter>();
// register all debug extensions
const debugTypes: string[] = [];
@@ -111,9 +122,12 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
const debuggers = <IDebuggerContribution[]>ed.contributes['debuggers'];
if (debuggers && debuggers.length > 0) {
for (const dbg of debuggers) {
// only debugger contributions with a "label" are considered a "main" debugger contribution
// only debugger contributions with a "label" are considered a "defining" debugger contribution
if (dbg.type && dbg.label) {
debugTypes.push(dbg.type);
if (dbg.adapterExecutableCommand) {
this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand);
}
}
}
}
@@ -124,143 +138,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
}
public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void> {
if (args.kind === 'integrated') {
if (!this._terminalDisposedListener) {
// React on terminal disposed and check if that is the debug terminal #12956
this._terminalDisposedListener = this._terminalService.onDidCloseTerminal(terminal => {
if (this._integratedTerminalInstance && this._integratedTerminalInstance === terminal) {
this._integratedTerminalInstance = null;
}
});
}
return new TPromise(resolve => {
if (this._integratedTerminalInstance) {
this._integratedTerminalInstance.processId.then(pid => {
resolve(hasChildprocesses(pid));
}, err => {
resolve(true);
});
} else {
resolve(true);
}
}).then(needNewTerminal => {
if (needNewTerminal) {
this._integratedTerminalInstance = this._terminalService.createTerminal(args.title || nls.localize('debug.terminal.title', "debuggee"));
}
this._integratedTerminalInstance.show();
return new TPromise((resolve, error) => {
setTimeout(_ => {
const command = prepareCommand(args, config);
this._integratedTerminalInstance.sendText(command, true);
resolve(void 0);
}, 500);
});
});
} else if (args.kind === 'external') {
const terminalLauncher = getTerminalLauncher();
if (terminalLauncher) {
return terminalLauncher.runInTerminal(args, config);
}
}
return void 0;
}
public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): TPromise<IConfig> {
if (!this._variableResolver) {
this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, this._configurationService);
}
let ws: IWorkspaceFolder;
const folder = this.getFolder(folderUri);
if (folder) {
ws = {
uri: folder.uri,
name: folder.name,
index: folder.index,
toResource: () => {
throw new Error('Not implemented');
}
};
}
return TPromise.wrap(this._variableResolver.resolveAny(ws, config));
}
public $startDASession(handle: number, debugType: string, adpaterExecutable: IAdapterExecutable | null, debugPort: number): TPromise<void> {
const mythis = this;
let da: StreamDebugAdapter = null;
if (debugPort > 0) {
da = new class extends SocketDebugAdapter {
// DA -> VS Code
public acceptMessage(message: DebugProtocol.ProtocolMessage) {
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);
}
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
}
}(debugPort);
} else {
da = new class extends DebugAdapter {
// DA -> VS Code
public acceptMessage(message: DebugProtocol.ProtocolMessage) {
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);
}
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
}
}(debugType, adpaterExecutable, this._extensionService.getAllExtensionDescriptions());
}
this._debugAdapters.set(handle, da);
da.onError(err => this._debugServiceProxy.$acceptDAError(handle, err.name, err.message, err.stack));
da.onExit(code => this._debugServiceProxy.$acceptDAExit(handle, code, null));
return da.startSession();
}
public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise<void> {
// VS Code -> DA
convertToDAPaths(message, source => {
if (typeof source.path === 'object') {
source.path = URI.revive(source.path).fsPath;
}
});
const da = this._debugAdapters.get(handle);
if (da) {
da.sendMessage(message);
}
return void 0;
}
public $stopDASession(handle: number): TPromise<void> {
const da = this._debugAdapters.get(handle);
this._debugAdapters.delete(handle);
return da ? da.stopSession() : void 0;
}
private startBreakpoints() {
if (!this._breakpointEventsActive) {
this._breakpointEventsActive = true;
this._debugServiceProxy.$startBreakpointEvents();
}
}
// extension debug API
get onDidChangeBreakpoints(): Event<vscode.BreakpointsChangeEvent> {
return this._onDidChangeBreakpoints.event;
@@ -275,67 +153,6 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return result;
}
public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void {
let a: vscode.Breakpoint[] = [];
let r: vscode.Breakpoint[] = [];
let c: vscode.Breakpoint[] = [];
if (delta.added) {
for (const bpd of delta.added) {
if (!this._breakpoints.has(bpd.id)) {
let bp: vscode.Breakpoint;
if (bpd.type === 'function') {
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, 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);
}
bp['_id'] = bpd.id;
this._breakpoints.set(bpd.id, bp);
a.push(bp);
}
}
}
if (delta.removed) {
for (const id of delta.removed) {
const bp = this._breakpoints.get(id);
if (bp) {
this._breakpoints.delete(id);
r.push(bp);
}
}
}
if (delta.changed) {
for (const bpd of delta.changed) {
let bp = this._breakpoints.get(bpd.id);
if (bp) {
if (bp instanceof FunctionBreakpoint && bpd.type === 'function') {
const fbp = <any>bp;
fbp.enabled = bpd.enabled;
fbp.condition = bpd.condition;
fbp.hitCondition = bpd.hitCondition;
fbp.logMessage = bpd.logMessage;
fbp.functionName = bpd.functionName;
} else if (bp instanceof SourceBreakpoint && bpd.type === 'source') {
const sbp = <any>bp;
sbp.enabled = bpd.enabled;
sbp.condition = bpd.condition;
sbp.hitCondition = bpd.hitCondition;
sbp.logMessage = bpd.logMessage;
sbp.location = new Location(URI.revive(bpd.uri), new Position(bpd.line, bpd.character));
}
c.push(bp);
}
}
}
this.fireBreakpointChanges(a, r, c);
}
public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Thenable<void> {
this.startBreakpoints();
@@ -424,6 +241,424 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return this._debugServiceProxy.$unregisterBreakpoints(ids, fids);
}
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig);
}
public registerDebugConfigurationProvider(extension: IExtensionDescription, type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
if (!provider) {
return new Disposable(() => { });
}
// if a provider has a provideDebugAdapter method, we check the constraints specified in the API doc
if (provider.provideDebugAdapter) {
// a provider with this method can only be registered in the extension that contributes the debugger
if (!this.definesDebugType(extension, type)) {
throw new Error(`method 'provideDebugAdapter' must only be called from the extension that defines the '${type}' debugger.`);
}
// make sure that only one provider for this type is registered
if (this._providerByType.has(type)) {
throw new Error(`a provider with method 'provideDebugAdapter' can only be registered once per a type.`);
} else {
this._providerByType.set(type, provider);
}
}
let handle = this._providerHandleCounter++;
this._providerByHandle.set(handle, provider);
this._providers.push({ type, provider });
this._debugServiceProxy.$registerDebugConfigurationProvider(type,
!!provider.provideDebugConfigurations,
!!provider.resolveDebugConfiguration,
!!provider.debugAdapterExecutable || !!provider.provideDebugAdapter,
!!provider.provideDebugAdapterTracker, handle);
return new Disposable(() => {
this._providerByHandle.delete(handle);
this._providerByType.delete(type);
this._providers = this._providers.filter(p => p.provider !== provider); // remove
this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle);
});
}
// RPC methods (ExtHostDebugServiceShape)
public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void> {
if (args.kind === 'integrated') {
if (!this._terminalDisposedListener) {
// React on terminal disposed and check if that is the debug terminal #12956
this._terminalDisposedListener = this._terminalService.onDidCloseTerminal(terminal => {
if (this._integratedTerminalInstance && this._integratedTerminalInstance === terminal) {
this._integratedTerminalInstance = null;
}
});
}
return new TPromise(resolve => {
if (this._integratedTerminalInstance) {
this._integratedTerminalInstance.processId.then(pid => {
resolve(hasChildprocesses(pid));
}, err => {
resolve(true);
});
} else {
resolve(true);
}
}).then(needNewTerminal => {
if (needNewTerminal) {
this._integratedTerminalInstance = this._terminalService.createTerminal(args.title || nls.localize('debug.terminal.title', "debuggee"));
}
this._integratedTerminalInstance.show();
return new TPromise((resolve, error) => {
setTimeout(_ => {
const command = prepareCommand(args, config);
this._integratedTerminalInstance.sendText(command, true);
resolve(void 0);
}, 500);
});
});
} else if (args.kind === 'external') {
const terminalLauncher = getTerminalLauncher();
if (terminalLauncher) {
return terminalLauncher.runInTerminal(args, config);
}
}
return void 0;
}
public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): TPromise<IConfig> {
if (!this._variableResolver) {
this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, this._configurationService);
}
let ws: IWorkspaceFolder;
const folder = this.getFolder(folderUri);
if (folder) {
ws = {
uri: folder.uri,
name: folder.name,
index: folder.index,
toResource: () => {
throw new Error('Not implemented');
}
};
}
return TPromise.wrap(this._variableResolver.resolveAny(ws, config));
}
public $startDASession(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise<void> {
const mythis = this;
return this.getAdapterDescriptor(this._providerByType.get(config.type), sessionDto, folderUri, config).then(adapter => {
let da: AbstractDebugAdapter = undefined;
switch (adapter.type) {
case 'server':
da = new SocketDebugAdapter(adapter);
break;
case 'executable':
da = new ExecutableDebugAdapter(adapter, config.type);
break;
case 'implementation':
da = new DirectDebugAdapter(adapter.implementation);
break;
default:
break;
}
if (da) {
this._debugAdapters.set(handle, da);
return this.getDebugAdapterTrackers(sessionDto, folderUri, config).then(tracker => {
if (tracker) {
this._debugAdaptersTrackers.set(handle, tracker);
}
da.onMessage(message => {
if (tracker) {
tracker.fromDebugAdapter(message);
}
// DA -> VS Code
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);
}
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
});
da.onError(err => {
tracker.debugAdapterError(err);
this._debugServiceProxy.$acceptDAError(handle, err.name, err.message, err.stack);
});
da.onExit(code => {
tracker.debugAdapterExit(code, null);
this._debugServiceProxy.$acceptDAExit(handle, code, null);
});
if (tracker) {
tracker.startDebugAdapter();
}
return da.startSession();
});
}
return undefined;
});
}
public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise<void> {
// VS Code -> DA
convertToDAPaths(message, source => {
if (typeof source.path === 'object') {
source.path = URI.revive(source.path).fsPath;
}
});
const tracker = this._debugAdaptersTrackers.get(handle);
if (tracker) {
tracker.toDebugAdapter(message);
}
const da = this._debugAdapters.get(handle);
if (da) {
da.sendMessage(message);
}
return void 0;
}
public $stopDASession(handle: number): TPromise<void> {
const tracker = this._debugAdaptersTrackers.get(handle);
this._debugAdaptersTrackers.delete(handle);
if (tracker) {
tracker.stopDebugAdapter();
}
const da = this._debugAdapters.get(handle);
this._debugAdapters.delete(handle);
if (da) {
return da.stopSession();
} else {
return void 0;
}
}
public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void {
let a: vscode.Breakpoint[] = [];
let r: vscode.Breakpoint[] = [];
let c: vscode.Breakpoint[] = [];
if (delta.added) {
for (const bpd of delta.added) {
if (!this._breakpoints.has(bpd.id)) {
let bp: vscode.Breakpoint;
if (bpd.type === 'function') {
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, 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);
}
bp['_id'] = bpd.id;
this._breakpoints.set(bpd.id, bp);
a.push(bp);
}
}
}
if (delta.removed) {
for (const id of delta.removed) {
const bp = this._breakpoints.get(id);
if (bp) {
this._breakpoints.delete(id);
r.push(bp);
}
}
}
if (delta.changed) {
for (const bpd of delta.changed) {
let bp = this._breakpoints.get(bpd.id);
if (bp) {
if (bp instanceof FunctionBreakpoint && bpd.type === 'function') {
const fbp = <any>bp;
fbp.enabled = bpd.enabled;
fbp.condition = bpd.condition;
fbp.hitCondition = bpd.hitCondition;
fbp.logMessage = bpd.logMessage;
fbp.functionName = bpd.functionName;
} else if (bp instanceof SourceBreakpoint && bpd.type === 'source') {
const sbp = <any>bp;
sbp.enabled = bpd.enabled;
sbp.condition = bpd.condition;
sbp.hitCondition = bpd.hitCondition;
sbp.logMessage = bpd.logMessage;
sbp.location = new Location(URI.revive(bpd.uri), new Position(bpd.line, bpd.character));
}
c.push(bp);
}
}
}
this.fireBreakpointChanges(a, r, c);
}
public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): Thenable<vscode.DebugConfiguration[]> {
let provider = this._providerByHandle.get(handle);
if (!provider) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('no handler found'));
}
if (!provider.provideDebugConfigurations) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('handler has no method provideDebugConfigurations'));
}
return asThenable(() => provider.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None));
}
public $resolveDebugConfiguration(handle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Thenable<vscode.DebugConfiguration> {
let provider = this._providerByHandle.get(handle);
if (!provider) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('no handler found'));
}
if (!provider.resolveDebugConfiguration) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('handler has no method resolveDebugConfiguration'));
}
return asThenable(() => provider.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None));
}
public $provideDebugAdapter(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable<vscode.DebugAdapterDescriptor> {
let provider = this._providerByHandle.get(handle);
if (!provider) {
return TPromise.wrapError<vscode.DebugAdapterExecutable>(new Error('no handler found'));
}
if (!provider.debugAdapterExecutable && !provider.provideDebugAdapter) {
return TPromise.wrapError<vscode.DebugAdapterExecutable>(new Error('handler has no methods provideDebugAdapter or debugAdapterExecutable'));
}
return this.getAdapterDescriptor(provider, this.getSession(sessionDto), folderUri, config);
}
public $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): void {
this._onDidStartDebugSession.fire(this.getSession(sessionDto));
}
public $acceptDebugSessionTerminated(sessionDto: IDebugSessionDto): void {
this._onDidTerminateDebugSession.fire(this.getSession(sessionDto));
this._debugSessions.delete(sessionDto.id);
}
public $acceptDebugSessionActiveChanged(sessionDto: IDebugSessionDto): void {
this._activeDebugSession = sessionDto ? this.getSession(sessionDto) : undefined;
this._onDidChangeActiveDebugSession.fire(this._activeDebugSession);
}
public $acceptDebugSessionCustomEvent(sessionDto: IDebugSessionDto, event: any): void {
const ee: vscode.DebugSessionCustomEvent = {
session: this.getSession(sessionDto),
event: event.event,
body: event.body
};
this._onDidReceiveDebugSessionCustomEvent.fire(ee);
}
// private & dto helpers
private definesDebugType(ed: IExtensionDescription, type: string) {
if (ed.contributes) {
const debuggers = <IDebuggerContribution[]>ed.contributes['debuggers'];
if (debuggers && debuggers.length > 0) {
for (const dbg of debuggers) {
// only debugger contributions with a "label" are considered a "defining" debugger contribution
if (dbg.label && dbg.type) {
if (dbg.type === type) {
return true;
}
}
}
}
}
return false;
}
private getDebugAdapterTrackers(sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise<vscode.IDebugAdapterTracker> {
const session = this.getSession(sessionDto);
const folder = this.getFolder(folderUri);
const type = config.type;
const promises = this._providers
.filter(pair => pair.provider.provideDebugAdapterTracker && (pair.type === type || pair.type === '*'))
.map(pair => pair.provider.provideDebugAdapterTracker(session, folder, config, CancellationToken.None));
return TPromise.join(promises).then(trackers => {
if (trackers.length > 0) {
return new MultiTracker(trackers);
}
return undefined;
});
}
private getAdapterDescriptor(debugConfigProvider, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable<vscode.DebugAdapterDescriptor> {
// a "debugServer" attribute in the launch config takes precedence
if (typeof config.debugServer === 'number') {
return TPromise.wrap(new DebugAdapterServer(config.debugServer));
}
if (debugConfigProvider) {
// try the proposed "provideDebugAdapter" API
if (debugConfigProvider.provideDebugAdapter) {
const adapterExecutable = ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type);
return asThenable(() => debugConfigProvider.provideDebugAdapter(this.getSession(sessionDto), this.getFolder(folderUri), adapterExecutable, config, CancellationToken.None));
}
// try the deprecated "debugAdapterExecutable" API
if (debugConfigProvider.debugAdapterExecutable) {
return asThenable(() => debugConfigProvider.debugAdapterExecutable(this.getFolder(folderUri), CancellationToken.None));
}
}
// try deprecated command based extension API "adapterExecutableCommand" to determine the executable
const aex = this._aexCommands.get(config.type);
if (aex) {
const rootFolder = folderUri ? URI.revive(folderUri).toString() : undefined;
return this._commandService.executeCommand(aex, rootFolder).then((ae: { command: string, args: string[] }) => {
return new DebugAdapterExecutable(ae.command, ae.args || []);
});
}
// fallback: use executable information from package.json
return TPromise.wrap(ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type));
}
private startBreakpoints() {
if (!this._breakpointEventsActive) {
this._breakpointEventsActive = true;
this._debugServiceProxy.$startBreakpointEvents();
}
}
private fireBreakpointChanges(added: vscode.Breakpoint[], removed: vscode.Breakpoint[], changed: vscode.Breakpoint[]) {
if (added.length > 0 || removed.length > 0 || changed.length > 0) {
this._onDidChangeBreakpoints.fire(Object.freeze({
@@ -434,109 +669,16 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
}
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
if (!provider) {
return new Disposable(() => { });
}
let handle = this.nextHandle();
this._handlers.set(handle, provider);
this._debugServiceProxy.$registerDebugConfigurationProvider(type,
!!provider.provideDebugConfigurations,
!!provider.resolveDebugConfiguration,
!!provider.debugAdapterExecutable, handle);
return new Disposable(() => {
this._handlers.delete(handle);
this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle);
});
}
public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): Thenable<vscode.DebugConfiguration[]> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('no handler found'));
}
if (!handler.provideDebugConfigurations) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('handler has no method provideDebugConfigurations'));
}
return asThenable(() => handler.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None));
}
public $resolveDebugConfiguration(handle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Thenable<vscode.DebugConfiguration> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('no handler found'));
}
if (!handler.resolveDebugConfiguration) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('handler has no method resolveDebugConfiguration'));
}
return asThenable(() => handler.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None));
}
public $debugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Thenable<vscode.DebugAdapterExecutable> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<vscode.DebugAdapterExecutable>(new Error('no handler found'));
}
if (!handler.debugAdapterExecutable) {
return TPromise.wrapError<vscode.DebugAdapterExecutable>(new Error('handler has no method debugAdapterExecutable'));
}
return asThenable(() => handler.debugAdapterExecutable(this.getFolder(folderUri), CancellationToken.None));
}
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig);
}
public $acceptDebugSessionStarted(id: DebugSessionUUID, type: string, name: string): void {
let debugSession = this._debugSessions.get(id);
if (!debugSession) {
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
this._debugSessions.set(id, debugSession);
}
this._onDidStartDebugSession.fire(debugSession);
}
public $acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void {
let debugSession = this._debugSessions.get(id);
if (!debugSession) {
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
this._debugSessions.set(id, debugSession);
}
this._onDidTerminateDebugSession.fire(debugSession);
this._debugSessions.delete(id);
}
public $acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void {
if (id) {
this._activeDebugSession = this._debugSessions.get(id);
if (!this._activeDebugSession) {
this._activeDebugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
this._debugSessions.set(id, this._activeDebugSession);
private getSession(dto: IDebugSessionDto): ExtHostDebugSession {
if (dto) {
let debugSession = this._debugSessions.get(dto.id);
if (!debugSession) {
debugSession = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name);
this._debugSessions.set(dto.id, debugSession);
}
} else {
this._activeDebugSession = undefined;
return debugSession;
}
this._onDidChangeActiveDebugSession.fire(this._activeDebugSession);
}
public $acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void {
let debugSession = this._debugSessions.get(id);
if (!debugSession) {
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
this._debugSessions.set(id, debugSession);
}
const ee: vscode.DebugSessionCustomEvent = {
session: debugSession,
event: event.event,
body: event.body
};
this._onDidReceiveDebugSessionCustomEvent.fire(ee);
return undefined;
}
private getFolder(_folderUri: UriComponents | undefined): vscode.WorkspaceFolder | undefined {
@@ -546,10 +688,6 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
return undefined;
}
private nextHandle(): number {
return this._handleCounter++;
}
}
export class ExtHostDebugSession implements vscode.DebugSession {
@@ -557,7 +695,6 @@ export class ExtHostDebugSession implements vscode.DebugSession {
private _debugServiceProxy: MainThreadDebugServiceShape;
private _id: DebugSessionUUID;
private _type: string;
private _name: string;
@@ -650,3 +787,99 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
});
}
}
interface TypeProviderPair {
type: string;
provider: vscode.DebugConfigurationProvider;
}
interface IDapTransport {
start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void);
send(message: DebugProtocol.ProtocolMessage);
stop(): void;
}
class MultiTracker implements vscode.IDebugAdapterTracker {
constructor(private trackers: vscode.IDebugAdapterTracker[]) {
}
startDebugAdapter(): void {
this.trackers.forEach(t => t.startDebugAdapter ? t.startDebugAdapter() : void 0);
}
toDebugAdapter(message: any): void {
this.trackers.forEach(t => t.toDebugAdapter ? t.toDebugAdapter(message) : void 0);
}
fromDebugAdapter(message: any): void {
this.trackers.forEach(t => t.fromDebugAdapter ? t.fromDebugAdapter(message) : void 0);
}
debugAdapterError(error: Error): void {
this.trackers.forEach(t => t.debugAdapterError ? t.debugAdapterError(error) : void 0);
}
debugAdapterExit(code: number, signal: string): void {
this.trackers.forEach(t => t.debugAdapterExit ? t.debugAdapterExit(code, signal) : void 0);
}
stopDebugAdapter(): void {
this.trackers.forEach(t => t.stopDebugAdapter ? t.stopDebugAdapter() : void 0);
}
}
class DirectTransport implements IDapTransport {
private _sendUp: (msg: DebugProtocol.ProtocolMessage) => void;
constructor(private da: DirectDebugAdapter) {
}
start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void) {
this._sendUp = cb;
}
sendUp(message: DebugProtocol.ProtocolMessage) {
this._sendUp(message);
}
// DA -> VSCode
send(message: DebugProtocol.ProtocolMessage) {
this.da.acceptMessage(message);
}
stop(): void {
throw new Error('Method not implemented.');
}
}
class DirectDebugAdapter extends AbstractDebugAdapter {
readonly onError: Event<Error>;
readonly onExit: Event<number>;
private transport: DirectTransport;
constructor(implementation: any) {
super();
if (implementation.__setTransport) {
this.transport = new DirectTransport(this);
implementation.__setTransport(this.transport);
}
}
startSession(): TPromise<void> {
return TPromise.wrap(void 0);
}
// VSCode -> DA
sendMessage(message: DebugProtocol.ProtocolMessage): void {
this.transport.sendUp(message);
}
stopSession(): TPromise<void> {
this.transport.stop();
return TPromise.wrap(void 0);
}
}

View File

@@ -9,9 +9,9 @@ import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'
import { URI } from 'vs/base/common/uri';
import * as vscode from 'vscode';
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
import { DiagnosticSeverity } from './extHostTypes';
import { DiagnosticSeverity, Diagnostic } from './extHostTypes';
import * as converter from './extHostTypeConverters';
import { mergeSort } from 'vs/base/common/arrays';
import { mergeSort, equals } from 'vs/base/common/arrays';
import { Event, Emitter, debounceEvent, mapEvent } from 'vs/base/common/event';
import { keys } from 'vs/base/common/map';
@@ -62,9 +62,13 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
this._checkDisposed();
let toSync: vscode.Uri[];
let hasChanged = true;
if (first instanceof URI) {
// check if this has actually changed
hasChanged = hasChanged && !equals(diagnostics, this.get(first), Diagnostic.isEqual);
if (!diagnostics) {
// remove this entry
this.delete(first);
@@ -103,6 +107,17 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
}
}
// send event for extensions
this._onDidChangeDiagnostics.fire(toSync);
// if nothing has changed then there is nothing else to do
// we have updated the diagnostics but we don't send a message
// to the renderer. tho we have still send an event for other
// extensions because the diagnostic might carry more information
// than known to us
if (!hasChanged) {
return;
}
// compute change and send to main side
const entries: [URI, IMarkerData[]][] = [];
for (let uri of toSync) {
@@ -142,7 +157,6 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
entries.push([uri, marker]);
}
this._onDidChangeDiagnostics.fire(toSync);
this._proxy.$changeMany(this._owner, entries);
}

View File

@@ -244,7 +244,7 @@ export class ExtensionsActivator {
if (!depDesc) {
// Error condition 1: unknown dependency
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Extension '{1}' failed to activate. Reason: unknown dependency '{0}'.", depId, currentExtension.id));
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' as the depending extension '{1}' is not found. Please install or enable the depending extension and reload the window.", currentExtension.displayName || currentExtension.id, depId));
const error = new Error(`Unknown dependency '${depId}'`);
this._activatedExtensions[currentExtension.id] = new FailedExtension(error);
return;
@@ -254,7 +254,7 @@ export class ExtensionsActivator {
let dep = this._activatedExtensions[depId];
if (dep.activationFailed) {
// Error condition 2: a dependency has already failed activation
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Extension '{1}' failed to activate. Reason: dependency '{0}' failed to activate.", depId, currentExtension.id));
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' as the depending extension '{1}' is failed to activate.", currentExtension.displayName || currentExtension.id, depId));
const error = new Error(`Dependency ${depId} failed to activate`);
(<any>error).detail = dep.activationFailedError;
this._activatedExtensions[currentExtension.id] = new FailedExtension(error);

View File

@@ -14,7 +14,7 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl';
import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IMainContext } from './extHost.protocol';
import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes, ExtensionActivationReason, ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator';
import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes, ExtensionActivationReason, ExtensionActivatedByEvent, ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { TernarySearchTree } from 'vs/base/common/map';
@@ -243,7 +243,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
if (!ext.main) {
return undefined;
}
return realpath(ext.extensionLocation.fsPath).then(value => tree.set(value, ext));
return realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
});
this._extensionPathIndex = TPromise.join(extensions).then(() => tree);
}
@@ -308,7 +308,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<ActivatedExtension> {
let event = getTelemetryActivationEvent(extensionDescription);
let event = getTelemetryActivationEvent(extensionDescription, reason);
/* __GDPR__
"activatePlugin" : {
"${include}": [
@@ -421,14 +421,19 @@ function loadCommonJSModule<T>(logService: ILogService, modulePath: string, acti
return TPromise.as(r);
}
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription): any {
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any {
const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent :
reason instanceof ExtensionActivatedByAPI ? 'api' :
'';
/* __GDPR__FRAGMENT__
"TelemetryActivationEvent" : {
"id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
let event = {
@@ -436,7 +441,8 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
name: extensionDescription.name,
publisherDisplayName: extensionDescription.publisher,
activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
isBuiltin: extensionDescription.isBuiltin
isBuiltin: extensionDescription.isBuiltin,
reason: reasonStr
};
return event;

View File

@@ -492,24 +492,17 @@ class RenameAdapter {
}
return typeConvert.WorkspaceEdit.from(value);
}, err => {
if (typeof err === 'string') {
return <WorkspaceEditDto>{
edits: undefined,
rejectReason: err
};
} else if (err instanceof Error && typeof err.message === 'string') {
return <WorkspaceEditDto>{
edits: undefined,
rejectReason: err.message
};
let rejectReason = RenameAdapter._asMessage(err);
if (rejectReason) {
return <WorkspaceEditDto>{ rejectReason, edits: undefined };
} else {
// generic error
return TPromise.wrapError<WorkspaceEditDto>(err);
return Promise.reject<WorkspaceEditDto>(err);
}
});
}
resolveRenameLocation(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.RenameLocation> {
resolveRenameLocation(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.RenameLocation & modes.Rejection> {
if (typeof this._provider.prepareRename !== 'function') {
return TPromise.as(undefined);
}
@@ -539,8 +532,25 @@ class RenameAdapter {
return undefined;
}
return { range: typeConvert.Range.from(range), text };
}, err => {
let rejectReason = RenameAdapter._asMessage(err);
if (rejectReason) {
return <modes.RenameLocation & modes.Rejection>{ rejectReason, range: undefined, text: undefined };
} else {
return Promise.reject(err);
}
});
}
private static _asMessage(err: any): string {
if (typeof err === 'string') {
return err;
} else if (err instanceof Error && typeof err.message === 'string') {
return err.message;
} else {
return undefined;
}
}
}
class SuggestAdapter {
@@ -671,19 +681,19 @@ class SuggestAdapter {
// 'insertText'-logic
if (item.textEdit) {
result.insertText = item.textEdit.newText;
result.snippetType = 'internal';
result.insertTextIsSnippet = false;
} else if (typeof item.insertText === 'string') {
result.insertText = item.insertText;
result.snippetType = 'internal';
result.insertTextIsSnippet = false;
} else if (item.insertText instanceof SnippetString) {
result.insertText = item.insertText.value;
result.snippetType = 'textmate';
result.insertTextIsSnippet = true;
} else {
result.insertText = item.label;
result.snippetType = 'internal';
result.insertTextIsSnippet = false;
}
// 'overwrite[Before|After]'-logic
@@ -714,12 +724,12 @@ class SignatureHelpAdapter {
private readonly _provider: vscode.SignatureHelpProvider
) { }
provideSignatureHelp(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.SignatureHelp> {
provideSignatureHelp(resource: URI, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Thenable<modes.SignatureHelp> {
const doc = this._documents.getDocumentData(resource).document;
const pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideSignatureHelp(doc, pos, token)).then(value => {
return asThenable(() => this._provider.provideSignatureHelp(doc, pos, token, context)).then(value => {
if (value) {
return typeConvert.SignatureHelp.from(value);
}
@@ -1144,8 +1154,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.SignatureHelp> {
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, token));
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Thenable<modes.SignatureHelp> {
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token));
}
// --- links
@@ -1222,6 +1232,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return {
beforeText: ExtHostLanguageFeatures._serializeRegExp(onEnterRule.beforeText),
afterText: ExtHostLanguageFeatures._serializeRegExp(onEnterRule.afterText),
oneLineAboveText: ExtHostLanguageFeatures._serializeRegExp(onEnterRule.oneLineAboveText),
action: onEnterRule.action
};
}

View File

@@ -6,21 +6,28 @@
import { MainContext, MainThreadLanguagesShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
export class ExtHostLanguages {
private _proxy: MainThreadLanguagesShape;
private readonly _proxy: MainThreadLanguagesShape;
private readonly _documents: ExtHostDocuments;
constructor(
mainContext: IMainContext
mainContext: IMainContext,
documents: ExtHostDocuments
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadLanguages);
this._documents = documents;
}
getLanguages(): Thenable<string[]> {
return this._proxy.$getLanguages();
}
changeLanguage(documentUri: vscode.Uri, languageId: string): Thenable<void> {
return this._proxy.$changeLanguage(documentUri, languageId);
changeLanguage(uri: vscode.Uri, languageId: string): Thenable<vscode.TextDocument> {
return this._proxy.$changeLanguage(uri, languageId).then(() => {
return this._documents.getDocumentData(uri).document;
});
}
}

View File

@@ -10,11 +10,13 @@ import { ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
private _logsPath: string;
readonly logFile: URI;
constructor(
logLevel: LogLevel,
@@ -22,6 +24,7 @@ export class ExtHostLogService extends DelegatedLogService implements ILogServic
) {
super(createSpdLogService(ExtensionHostLogFileName, logLevel, logsPath));
this._logsPath = logsPath;
this.logFile = URI.file(join(logsPath, `${ExtensionHostLogFileName}.log`));
}
$setLevel(level: LogLevel): void {

View File

@@ -18,10 +18,10 @@ export abstract class AbstractExtHostOutputChannel implements vscode.OutputChann
protected readonly _proxy: MainThreadOutputServiceShape;
private _disposed: boolean;
constructor(name: string, file: URI, proxy: MainThreadOutputServiceShape) {
constructor(name: string, log: boolean, file: URI, proxy: MainThreadOutputServiceShape) {
this._name = name;
this._proxy = proxy;
this._id = proxy.$register(this.name, file);
this._id = proxy.$register(this.name, log, file);
}
get name(): string {
@@ -68,7 +68,7 @@ export abstract class AbstractExtHostOutputChannel implements vscode.OutputChann
export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
constructor(name: string, proxy: MainThreadOutputServiceShape) {
super(name, null, proxy);
super(name, false, null, proxy);
}
append(value: string): void {
@@ -77,16 +77,16 @@ export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
}
}
export class ExtHostPullOutputChannel extends AbstractExtHostOutputChannel {
export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChannel {
private static _namePool = 1;
private _appender: OutputAppender;
constructor(name: string, outputDir: string, proxy: MainThreadOutputServiceShape) {
const fileName = `${ExtHostPullOutputChannel._namePool++}-${name}`;
const fileName = `${ExtHostOutputChannelBackedByFile._namePool++}-${name}`;
const file = URI.file(posix.join(outputDir, `${fileName}.log`));
super(name, file, proxy);
super(name, false, file, proxy);
this._appender = new OutputAppender(fileName, file.fsPath);
}
@@ -96,6 +96,17 @@ export class ExtHostPullOutputChannel extends AbstractExtHostOutputChannel {
}
}
export class ExtHostLogFileOutputChannel extends AbstractExtHostOutputChannel {
constructor(name: string, file: URI, proxy: MainThreadOutputServiceShape) {
super(name, true, file, proxy);
}
append(value: string): void {
throw new Error('Not supported');
}
}
export class ExtHostOutputService {
private _proxy: MainThreadOutputServiceShape;
@@ -111,7 +122,28 @@ export class ExtHostOutputService {
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
} else {
return options && options.force ? new ExtHostPushOutputChannel(name, this._proxy) : new ExtHostPullOutputChannel(name, this._outputDir, this._proxy);
if (options && options.force) {
return new ExtHostPushOutputChannel(name, this._proxy);
} else {
// Do not crash if logger cannot be created
try {
return new ExtHostOutputChannelBackedByFile(name, this._outputDir, this._proxy);
} catch (error) {
console.log(error);
return new ExtHostPushOutputChannel(name, this._proxy);
}
}
}
}
createOutputChannelFromLogFile(name: string, file: URI): vscode.OutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
}
if (!file) {
throw new Error('illegal argument `file`. must not be falsy');
}
return new ExtHostLogFileOutputChannel(name, file, this._proxy);
}
}

View File

@@ -69,6 +69,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
let description: string;
let detail: string;
let picked: boolean;
let alwaysShow: boolean;
if (typeof item === 'string') {
label = item;
@@ -77,13 +78,15 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
description = item.description;
detail = item.detail;
picked = item.picked;
alwaysShow = item.shouldAlwaysShow;
}
pickItems.push({
label,
description,
handle,
detail,
picked
picked,
alwaysShow
});
}

View File

@@ -111,6 +111,37 @@ function compareResourceStates(a: vscode.SourceControlResourceState, b: vscode.S
return result;
}
function compareArgs(a: any[], b: any[]): boolean {
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
function commandEquals(a: vscode.Command, b: vscode.Command): boolean {
return a.command === b.command
&& a.title === b.title
&& a.tooltip === b.tooltip
&& (a.arguments && b.arguments ? compareArgs(a.arguments, b.arguments) : a.arguments === b.arguments);
}
function commandListEquals(a: vscode.Command[], b: vscode.Command[]): boolean {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!commandEquals(a[i], b[i])) {
return false;
}
}
return true;
}
export interface IValidateInput {
(value: string, cursorPosition: number): vscode.ProviderResult<vscode.SourceControlInputBoxValidation | undefined | null>;
}
@@ -344,6 +375,10 @@ class ExtHostSourceControl implements vscode.SourceControl {
}
set count(count: number | undefined) {
if (this._count === count) {
return;
}
this._count = count;
this._proxy.$updateSourceControl(this.handle, { count });
}
@@ -390,6 +425,10 @@ class ExtHostSourceControl implements vscode.SourceControl {
}
set statusBarCommands(statusBarCommands: vscode.Command[] | undefined) {
if (this._statusBarCommands && statusBarCommands && commandListEquals(this._statusBarCommands, statusBarCommands)) {
return;
}
this._statusBarCommands = statusBarCommands;
const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c));

View File

@@ -6,8 +6,10 @@
import * as path from 'path';
import * as arrays from 'vs/base/common/arrays';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { canceled } from 'vs/base/common/errors';
import * as glob from 'vs/base/common/glob';
import * as resources from 'vs/base/common/resources';
import { StopWatch } from 'vs/base/common/stopwatch';
@@ -15,9 +17,8 @@ import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { ICachedSearchStats, IFileMatch, IFolderQuery, IRawSearchQuery, ISearchCompleteStats, ISearchQuery, IFileSearchStats, IFileIndexProviderStats } from 'vs/platform/search/common/search';
import { ICachedSearchStats, IFileIndexProviderStats, IFileMatch, IFileSearchStats, IFolderQuery, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search';
import * as vscode from 'vscode';
import { canceled } from 'vs/base/common/errors';
export interface IInternalFileMatch {
base: URI;
@@ -418,9 +419,9 @@ export class FileIndexSearchManager {
private readonly folderCacheKeys = new Map<string, Set<string>>();
public fileSearch(config: ISearchQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void): TPromise<ISearchCompleteStats> {
public fileSearch(config: ISearchQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise<ISearchCompleteStats> {
if (config.sortByScore) {
let sortedSearch = this.trySortedSearchFromCache(config);
let sortedSearch = this.trySortedSearchFromCache(config, token);
if (!sortedSearch) {
const engineConfig = config.maxResults ?
{
@@ -430,21 +431,17 @@ export class FileIndexSearchManager {
config;
const engine = new FileIndexSearchEngine(engineConfig, provider);
sortedSearch = this.doSortedSearch(engine, config);
sortedSearch = this.doSortedSearch(engine, config, token);
}
return new TPromise<ISearchCompleteStats>((c, e) => {
sortedSearch.then(complete => {
this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE);
c(complete);
}, e);
}, () => {
sortedSearch.cancel();
return sortedSearch.then(complete => {
this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE);
return complete;
});
}
const engine = new FileIndexSearchEngine(config, provider);
return this.doSearch(engine)
return this.doSearch(engine, token)
.then(complete => {
this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE);
return <ISearchCompleteStats>{
@@ -477,12 +474,9 @@ export class FileIndexSearchManager {
};
}
private doSortedSearch(engine: FileIndexSearchEngine, config: ISearchQuery): TPromise<IInternalSearchComplete> {
let searchPromise: TPromise<void>;
let allResultsPromise = new TPromise<IInternalSearchComplete<IFileIndexProviderStats>>((c, e) => {
searchPromise = this.doSearch(engine).then(c, e);
}, () => {
searchPromise.cancel();
private doSortedSearch(engine: FileIndexSearchEngine, config: ISearchQuery, token: CancellationToken): TPromise<IInternalSearchComplete> {
let allResultsPromise = createCancelablePromise<IInternalSearchComplete<IFileIndexProviderStats>>(token => {
return this.doSearch(engine, token);
});
const folderCacheKey = this.getFolderCacheKey(config);
@@ -502,17 +496,16 @@ export class FileIndexSearchManager {
allResultsPromise = this.preventCancellation(allResultsPromise);
}
let chained: TPromise<void>;
return new TPromise<IInternalSearchComplete>((c, e) => {
chained = allResultsPromise.then(complete => {
return TPromise.wrap<IInternalSearchComplete>(
allResultsPromise.then(complete => {
const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null);
const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create();
return this.sortResults(config, complete.results, scorerCache)
return this.sortResults(config, complete.results, scorerCache, token)
.then(sortedResults => {
// sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened.
// Contrasting with findFiles which is not sorted and will have sortingTime: undefined
const sortingTime = sortSW ? sortSW.elapsed() : -1;
c(<IInternalSearchComplete>{
return <IInternalSearchComplete>{
limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults, // ??
results: sortedResults,
stats: {
@@ -522,12 +515,9 @@ export class FileIndexSearchManager {
sortingTime,
type: 'fileIndexProvider'
}
});
};
});
}, e);
}, () => {
chained.cancel();
});
}));
}
private getOrCreateCache(cacheKey: string): Cache {
@@ -538,42 +528,41 @@ export class FileIndexSearchManager {
return this.caches[cacheKey] = new Cache();
}
private trySortedSearchFromCache(config: ISearchQuery): TPromise<IInternalSearchComplete> {
private trySortedSearchFromCache(config: ISearchQuery, token: CancellationToken): TPromise<IInternalSearchComplete> {
const folderCacheKey = this.getFolderCacheKey(config);
const cache = folderCacheKey && this.caches[folderCacheKey];
if (!cache) {
return undefined;
}
const cached = this.getResultsFromCache(cache, config.filePattern);
const cached = this.getResultsFromCache(cache, config.filePattern, token);
if (cached) {
let chained: TPromise<void>;
return new TPromise<IInternalSearchComplete>((c, e) => {
chained = cached.then(complete => {
const sortSW = StopWatch.create();
return this.sortResults(config, complete.results, cache.scorerCache)
.then(sortedResults => {
c(<IInternalSearchComplete<IFileSearchStats>>{
limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults,
results: sortedResults,
stats: {
fromCache: true,
detailStats: complete.stats,
type: 'fileIndexProvider',
resultCount: sortedResults.length,
sortingTime: sortSW.elapsed()
}
});
});
}, e);
}, () => {
chained.cancel();
return cached.then(complete => {
const sortSW = StopWatch.create();
return this.sortResults(config, complete.results, cache.scorerCache, token)
.then(sortedResults => {
if (token && token.isCancellationRequested) {
throw canceled();
}
return <IInternalSearchComplete<IFileSearchStats>>{
limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults,
results: sortedResults,
stats: {
fromCache: true,
detailStats: complete.stats,
type: 'fileIndexProvider',
resultCount: sortedResults.length,
sortingTime: sortSW.elapsed()
}
};
});
});
}
return undefined;
}
private sortResults(config: IRawSearchQuery, results: IInternalFileMatch[], scorerCache: ScorerCache): TPromise<IInternalFileMatch[]> {
private sortResults(config: IRawSearchQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): TPromise<IInternalFileMatch[]> {
// we use the same compare function that is used later when showing the results using fuzzy scoring
// this is very important because we are also limiting the number of results by config.maxResults
// and as such we want the top items to be included in this result set if the number of items
@@ -581,7 +570,7 @@ export class FileIndexSearchManager {
const query = prepareQuery(config.filePattern);
const compare = (matchA: IInternalFileMatch, matchB: IInternalFileMatch) => compareItemsByScore(matchA, matchB, query, true, FileMatchItemAccessor, scorerCache);
return arrays.topAsync(results, compare, config.maxResults, 10000);
return arrays.topAsync(results, compare, config.maxResults, 10000, token);
}
private sendAsBatches(rawMatches: IInternalFileMatch[], onBatch: (batch: IFileMatch[]) => void, batchSize: number) {
@@ -595,7 +584,7 @@ export class FileIndexSearchManager {
}
}
private getResultsFromCache(cache: Cache, searchValue: string): TPromise<IInternalSearchComplete<ICachedSearchStats>> {
private getResultsFromCache(cache: Cache, searchValue: string, token: CancellationToken): TPromise<IInternalSearchComplete<ICachedSearchStats>> {
const cacheLookupSW = StopWatch.create();
if (path.isAbsolute(searchValue)) {
@@ -630,7 +619,13 @@ export class FileIndexSearchManager {
const cacheFilterSW = StopWatch.create();
return new TPromise<IInternalSearchComplete<ICachedSearchStats>>((c, e) => {
token.onCancellationRequested(() => e(canceled()));
cacheRow.promise.then(complete => {
if (token && token.isCancellationRequested) {
e(canceled());
}
// Pattern match on results
let results: IInternalFileMatch[] = [];
const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase();
@@ -656,24 +651,20 @@ export class FileIndexSearchManager {
}
});
}, e);
}, () => {
cacheRow.promise.cancel();
});
}
private doSearch(engine: FileIndexSearchEngine): TPromise<IInternalSearchComplete<IFileIndexProviderStats>> {
private doSearch(engine: FileIndexSearchEngine, token: CancellationToken): TPromise<IInternalSearchComplete<IFileIndexProviderStats>> {
token.onCancellationRequested(() => engine.cancel());
const results: IInternalFileMatch[] = [];
const onResult = match => results.push(match);
return new TPromise<IInternalSearchComplete<IFileIndexProviderStats>>((c, e) => {
engine.search(onResult).then(result => {
c(<IInternalSearchComplete<IFileIndexProviderStats>>{
limitHit: result.isLimitHit,
results,
stats: result.stats
});
}, e);
}, () => {
engine.cancel();
return engine.search(onResult).then(result => {
return <IInternalSearchComplete<IFileIndexProviderStats>>{
limitHit: result.isLimitHit,
results,
stats: result.stats
};
});
}
@@ -690,20 +681,23 @@ export class FileIndexSearchManager {
return TPromise.as(undefined);
}
private preventCancellation<C>(promise: TPromise<C>): TPromise<C> {
return new TPromise<C>((c, e) => {
// Allow for piled up cancellations to come through first.
process.nextTick(() => {
promise.then(c, e);
});
}, () => {
// Do not propagate.
});
private preventCancellation<C>(promise: CancelablePromise<C>): CancelablePromise<C> {
return new class implements CancelablePromise<C> {
cancel() {
// Do nothing
}
then(resolve, reject) {
return promise.then(resolve, reject);
}
catch(reject?) {
return this.then(undefined, reject);
}
};
}
}
interface ICacheRow {
promise: TPromise<IInternalSearchComplete<IFileIndexProviderStats>>;
promise: CancelablePromise<IInternalSearchComplete<IFileIndexProviderStats>>;
resolved: boolean;
}

View File

@@ -7,7 +7,6 @@
import * as path from 'path';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import * as glob from 'vs/base/common/glob';
import { toDisposable } from 'vs/base/common/lifecycle';
import * as resources from 'vs/base/common/resources';
@@ -82,30 +81,14 @@ export class ExtHostSearch implements ExtHostSearchShape {
const provider = this._fileSearchProvider.get(handle);
const query = reviveQuery(rawQuery);
if (provider) {
return new Promise((c, e) => {
this._fileSearchManager.fileSearch(query, provider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}, token).then(c, err => {
if (!isPromiseCanceledError(err)) {
e(err);
}
});
});
return this._fileSearchManager.fileSearch(query, provider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}, token);
} else {
const indexProvider = this._fileIndexProvider.get(handle);
const searchP = this._fileIndexSearchManager.fileSearch(query, indexProvider, batch => {
return this._fileIndexSearchManager.fileSearch(query, indexProvider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}).then(null, err => {
if (!isPromiseCanceledError(err)) {
throw err;
}
return null;
});
token.onCancellationRequested(() => searchP.cancel());
return searchP;
}, token);
}
}
@@ -332,11 +315,13 @@ class TextSearchEngine {
// For each root folder
TPromise.join(folderQueries.map((fq, i) => {
return this.searchInFolder(fq, r => onResult(r, i), tokenSource.token);
})).then(() => {
})).then(results => {
tokenSource.dispose();
this.collector.flush();
const someFolderHitLImit = results.some(result => result && result.limitHit);
resolve({
limitHit: this.isLimitHit,
limitHit: this.isLimitHit || someFolderHitLImit,
stats: {
type: 'textSearchProvider'
}
@@ -352,40 +337,33 @@ class TextSearchEngine {
});
}
private searchInFolder(folderQuery: IFolderQuery<URI>, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): TPromise<void> {
return new TPromise((resolve, reject) => {
private searchInFolder(folderQuery: IFolderQuery<URI>, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): TPromise<vscode.TextSearchComplete> {
const queryTester = new QueryGlobTester(this.config, folderQuery);
const testingPs = [];
const progress = {
report: (result: vscode.TextSearchResult) => {
const hasSibling = folderQuery.folder.scheme === 'file' && glob.hasSiblingPromiseFn(() => {
return this.readdir(path.dirname(result.uri.fsPath));
});
const queryTester = new QueryGlobTester(this.config, folderQuery);
const testingPs = [];
const progress = {
report: (result: vscode.TextSearchResult) => {
const hasSibling = folderQuery.folder.scheme === 'file' && glob.hasSiblingPromiseFn(() => {
return this.readdir(path.dirname(result.uri.fsPath));
});
const relativePath = path.relative(folderQuery.folder.fsPath, result.uri.fsPath);
testingPs.push(
queryTester.includedInQuery(relativePath, path.basename(relativePath), hasSibling)
.then(included => {
if (included) {
onResult(result);
}
}));
}
};
const relativePath = path.relative(folderQuery.folder.fsPath, result.uri.fsPath);
testingPs.push(
queryTester.includedInQuery(relativePath, path.basename(relativePath), hasSibling)
.then(included => {
if (included) {
onResult(result);
}
}));
}
};
const searchOptions = this.getSearchOptionsForFolder(folderQuery);
new TPromise(resolve => process.nextTick(resolve))
.then(() => {
return this.provider.provideTextSearchResults(patternInfoToQuery(this.pattern), searchOptions, progress, token);
})
.then(() => {
return TPromise.join(testingPs);
})
.then(
() => resolve(null),
reject);
});
const searchOptions = this.getSearchOptionsForFolder(folderQuery);
return new TPromise(resolve => process.nextTick(resolve))
.then(() => this.provider.provideTextSearchResults(patternInfoToQuery(this.pattern), searchOptions, progress, token))
.then(result => {
return TPromise.join(testingPs)
.then(() => result);
});
}
private readdir(dirname: string): TPromise<string[]> {

View File

@@ -77,7 +77,9 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
private readonly _onData: Emitter<string> = new Emitter<string>();
public get onDidWriteData(): Event<string> {
// Tell the main side to start sending data if it's not already
this._proxy.$registerOnDataListener(this._id);
this._idPromise.then(c => {
this._proxy.$registerOnDataListener(this._id);
});
return this._onData && this._onData.event;
}
@@ -226,6 +228,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: TerminalProcess } = {};
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
private _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; }
public get terminals(): ExtHostTerminal[] { return this._terminals; }
@@ -289,11 +292,11 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
public $acceptTerminalProcessData(id: number, data: string): void {
// TODO: Queue requests, currently the first 100ms of data may get missed
const terminal = this._getTerminalById(id);
if (terminal) {
terminal._fireOnData(data);
}
this._getTerminalByIdEventually(id).then(terminal => {
if (terminal) {
terminal._fireOnData(data);
}
});
}
public $acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void {
@@ -389,7 +392,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
terminalEnvironment.addTerminalEnvironmentKeys(env, locale);
terminalEnvironment.addTerminalEnvironmentKeys(env, platform.isWindows, locale);
// Fork the process and listen for messages
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
@@ -431,6 +434,23 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal> {
if (!this._getTerminalPromises[id]) {
this._getTerminalPromises[id] = this._createGetTerminalPromise(id, retries);
} else {
this._getTerminalPromises[id].then(c => {
return this._createGetTerminalPromise(id, retries);
});
}
return this._getTerminalPromises[id];
}
private _delay(timeout: number, result: any) {
return new Promise(c => {
setTimeout(c(result), timeout);
});
}
private _createGetTerminalPromise(id: number, retries: number = 5): Promise<ExtHostTerminal> {
return new Promise(c => {
if (retries === 0) {
c(undefined);
@@ -443,9 +463,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
} else {
// This should only be needed immediately after createTerminalRenderer is called as
// the ExtHostTerminal has not yet been iniitalized
setTimeout(() => {
c(this._getTerminalByIdEventually(id, retries - 1));
}, 200);
this._delay(200, c(this._getTerminalByIdEventually(id, retries - 1)));
}
});
}

View File

@@ -19,12 +19,12 @@ import { IPosition } from 'vs/editor/common/core/position';
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 { IRelativePattern } from 'vs/base/common/glob';
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto } from 'vs/workbench/api/node/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/node/extHostDocumentsAndEditors';
import { isString, isNumber } from 'vs/base/common/types';
export interface PositionLike {
line: number;
@@ -108,7 +108,7 @@ export namespace Diagnostic {
...Range.from(value.range),
message: value.message,
source: value.source,
code: String(value.code),
code: isString(value.code) || isNumber(value.code) ? String(value.code) : void 0,
severity: DiagnosticSeverity.from(value.severity),
relatedInformation: value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.from),
tags: Array.isArray(value.tags) ? value.tags.map(DiagnosticTag.from) : undefined,
@@ -530,6 +530,7 @@ export namespace Suggest {
result.sortText = suggestion.sortText;
result.filterText = suggestion.filterText;
result.preselect = suggestion.preselect;
result.commitCharacters = suggestion.commitCharacters;
// 'overwrite[Before|After]'-logic
let overwriteBefore = (typeof suggestion.overwriteBefore === 'number') ? suggestion.overwriteBefore : 0;
@@ -541,7 +542,7 @@ export namespace Suggest {
result.range = new types.Range(startPosition, endPosition);
// 'inserText'-logic
if (suggestion.snippetType === 'textmate') {
if (suggestion.insertTextIsSnippet) {
result.insertText = new types.SnippetString(suggestion.insertText);
} else {
result.insertText = suggestion.insertText;
@@ -743,7 +744,11 @@ export namespace TextEditorOptions {
export namespace GlobPattern {
export function from(pattern: vscode.GlobPattern): string | IRelativePattern {
export function from(pattern: vscode.GlobPattern): string | types.RelativePattern {
if (pattern instanceof types.RelativePattern) {
return pattern;
}
if (typeof pattern === 'string') {
return pattern;
}

View File

@@ -14,7 +14,7 @@ import { IRelativePattern } from 'vs/base/common/glob';
import { relative } from 'path';
import { startsWith } from 'vs/base/common/strings';
import { values } from 'vs/base/common/map';
import { coalesce } from 'vs/base/common/arrays';
import { coalesce, equals } from 'vs/base/common/arrays';
export class Disposable {
@@ -771,6 +771,18 @@ export class DiagnosticRelatedInformation {
this.location = location;
this.message = message;
}
static isEqual(a: DiagnosticRelatedInformation, b: DiagnosticRelatedInformation): boolean {
if (a === b) {
return true;
}
if (!a || !b) {
return false;
}
return a.message === b.message
&& a.location.range.isEqual(b.location.range)
&& a.location.uri.toString() === b.location.uri.toString();
}
}
export class Diagnostic {
@@ -798,6 +810,23 @@ export class Diagnostic {
code: this.code,
};
}
static isEqual(a: Diagnostic, b: Diagnostic): boolean {
if (a === b) {
return true;
}
if (!a || !b) {
return false;
}
return a.message === b.message
&& a.severity === b.severity
&& a.code === b.code
&& a.severity === b.severity
&& a.source === b.source
&& a.range.isEqual(b.range)
&& equals(a.tags, b.tags)
&& equals(a.relatedInformation, b.relatedInformation, DiagnosticRelatedInformation.isEqual);
}
}
export class Hover {
@@ -1064,6 +1093,12 @@ export class SignatureHelp {
}
}
export enum SignatureHelpTriggerReason {
Invoke = 1,
TriggerCharacter = 2,
Retrigger = 3,
}
export enum CompletionTriggerKind {
Invoke = 0,
TriggerCharacter = 1,
@@ -1114,6 +1149,7 @@ export class CompletionItem implements vscode.CompletionItem {
preselect: boolean;
insertText: string | SnippetString;
range: Range;
commitCharacters: string[];
textEdit: TextEdit;
additionalTextEdits: TextEdit[];
command: vscode.Command;
@@ -1785,6 +1821,8 @@ export enum ConfigurationTarget {
export class RelativePattern implements IRelativePattern {
base: string;
baseFolder?: URI;
pattern: string;
constructor(base: vscode.WorkspaceFolder | string, pattern: string) {
@@ -1798,7 +1836,13 @@ export class RelativePattern implements IRelativePattern {
throw illegalArgument('pattern');
}
this.base = typeof base === 'string' ? base : base.uri.fsPath;
if (typeof base === 'string') {
this.base = base;
} else {
this.baseFolder = base.uri;
this.base = base.uri.fsPath;
}
this.pattern = pattern;
}
@@ -1853,12 +1897,37 @@ export class FunctionBreakpoint extends Breakpoint {
}
export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable {
readonly type = 'executable';
readonly command: string;
readonly args: string[];
readonly env?: { [key: string]: string };
readonly cwd?: string;
constructor(command: string, args?: string[]) {
constructor(command: string, args?: string[], env?: { [key: string]: string }, cwd?: string) {
this.command = command;
this.args = args;
this.env = env;
this.cwd = cwd;
}
}
export class DebugAdapterServer implements vscode.DebugAdapterServer {
readonly type = 'server';
readonly port: number;
readonly host: string;
constructor(port: number, host?: string) {
this.port = port;
this.host = host;
}
}
export class DebugAdapterImplementation implements vscode.DebugAdapterImplementation {
readonly type = 'implementation';
readonly implementation: any;
constructor(transport: any) {
this.implementation = transport;
}
}

View File

@@ -20,7 +20,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { Severity } from 'vs/platform/notification/common/notification';
import { IQueryOptions, IRawFileMatch2 } from 'vs/platform/search/common/search';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Range } from 'vs/workbench/api/node/extHostTypes';
import { Range, RelativePattern } from 'vs/workbench/api/node/extHostTypes';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
@@ -346,17 +346,19 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
// --- search ---
findFiles(include: vscode.GlobPattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Thenable<vscode.Uri[]> {
findFiles(include: string | RelativePattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Thenable<vscode.Uri[]> {
this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId}, entryPoint: findFiles`);
let includePattern: string;
let includeFolder: string;
let includeFolder: URI;
if (include) {
if (typeof include === 'string') {
includePattern = include;
} else {
includePattern = include.pattern;
includeFolder = include.base;
// include.base must be an absolute path
includeFolder = include.baseFolder || URI.file(include.base);
}
}
@@ -379,7 +381,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
}
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token: vscode.CancellationToken = CancellationToken.None) {
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Thenable<vscode.TextSearchComplete> {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`);
if (options.previewOptions && options.previewOptions.totalChars <= options.previewOptions.leadingChars) {
@@ -432,8 +434,9 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
return this._proxy.$startTextSearch(query, queryOptions, requestId, token).then(
() => {
result => {
delete this._activeSearchCallbacks[requestId];
return result;
},
err => {
delete this._activeSearchCallbacks[requestId];