mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-26 19:44:25 +01:00
Merge branch 'master' into sandy081/outputChannel
This commit is contained in:
@@ -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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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[]>;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user