Merge VS Code 1.26.1 (#2394)

* Squash merge commits for 1.26 (#1) (#2323)

* Polish tag search as per feedback (#55269)

* Polish tag search as per feedback

* Updated regex

* Allow users to opt-out of features that send online requests in the background (#55097)

* settings sweep #54690

* Minor css tweaks to enable eoverflow elipsis in more places (#55277)

* fix an issue with titlebarheight when not scaling with zoom

* Settings descriptions update #54690

* fixes #55209

* Settings editor - many padding fixes

* More space above level 2 label

* Fixing Cannot debug npm script using Yarn #55103

* Settings editor - show ellipsis when description overflows

* Settings editor - ... fix measuring around links, relayout

* Setting descriptions

* Settings editor - fix ... for some short lines, fix select container width

* Settings editor - overlay trees so scrollable shadow is full width

* Fix #54133 - missing extension settings after reload

* Settings color token description tweak

* Settings editor - disable overflow indicator temporarily, needs to be faster

* Added command to Run the selected npm script

* fixes #54452

* fixes #54929

* fixes #55248

* prefix command with extension name

* Contribute run selected to the context menu

* node-debug@1.26.6

* Allow terminal rendererType to be swapped out at runtime

Part of #53274
Fixes #55344

* Settings editor - fix not focusing search when restoring editor
setInput must be actually async. Will be fixed naturally when we aren't using winJS promises...

* Settings editor - TOC should only expand the section with a selected item

* Bump node-debug2

* Settings editor - Tree focus outlines

* Settings editor - don't blink the scrollbar when toc selection changes
And hide TOC correctly when the editor is narrow

* Settings editor - header rows should not be selectable

* fixes #54877

* change debug assignee to isi

* Settings sweep (#54690)

* workaround for #55051

* Settings sweep (#54690)

* settings sweep

#54690

* Don't try closing tags when you type > after another >

* Describe what implementation code lens does

Fixes #55370

* fix javadoc formatter setting description

* fixes #55325

* update to officical TS version

* Settings editor - Even more padding, use semibold instead of bold

* Fix #55357 - fix TOC twistie

* fixes #55288

* explorer: refresh on di change file system provider registration

fixes #53256

* Disable push to Linux repo to test standalone publisher

* New env var to notify log level to extensions #54001

* Disable snippets in extension search (when not in suggest dropdown) (#55281)

* Disable snippits in extension search (when not in suggest dropdown)

* Add monaco input contributions

* Fix bug preventing snippetSuggestions from taking effect in sub-editors

* Latest emmet helper to fix #52366

* Fix comment updates for threads within same file

* Allow extensions to log telemetry to log files #54001

* Pull latest css grammar

* files.exclude control - use same style for "add" vs "edit"

* files.exclude control - focus/keyboard behavior

* don't show menubar too early

* files.exclude - better styling

* Place cursor at end of extensions search box on autofill (#55254)

* Place cursor at end of extensions search box on autofill

* Use position instead of selection

* fix linux build issue (empty if block)

* Settings editor - fix extension category prefixes

* Settings editor - add simple ellipsis for first line that overflows, doesn't cover case when first line does not overflow but there is more text, TODO

* File/Text search provider docs

* Fixes #52655

* Include epoch (#55008)

* Fixes #53385

* Fixes #49480

*  VS Code Insiders (Users) not opening Fixes #55353

* Better handling of the case when the extension host fails to start

* Fixes #53966

*  Remove confusing Start from wordPartLeft commands ID

* vscode-xterm@3.6.0-beta12

Fixes #55488

* Initial size is set to infinity!! Fixes #55461

* Polish embeddedEditorBackground

* configuration service misses event

* Fix #55224 - fix duplicate results in multiroot workspace from splitting the diskseach query

* Select all not working in issue reporter on mac, fixes #55424

* Disable fuzzy matching for extensions autosuggest (#55498)

* Fix clipping of extensions search border in some third party themes (#55504)

* fixes #55538

* Fix bug causing an aria alert to not be shown the third time
 (and odd numbers thereafter)

* Settings editor - work around rendering glitch with webkit-line-clamp

* Settings editor - revert earlier '...' changes

* Settings editor - move enumDescription to its own div, because it disturbs -webkit-line-clamp for some reason

* Settings editor - better overflow indicator

* Don't show existing filters in autocomplete (#55495)

* Dont show existing filters in autocomplete

* Simplify

* Settings Editor: Add aria labels for input elements Fixes: #54836 (#55543)

* fixes #55223

* Update vscode-css-languageservice to 3.0.10-next.1

* Fix #55509 - settings navigation

* Fix #55519

* Fix #55520

* FIx #55524

* Fix #55556 - include wordSeparators in all search queries, so findTextInFiles can respect isWordMatch correctly

* oss updates for endgame

* Fix unit tests

* fixes #55522

* Avoid missing manifest error from bubbling up #54757

* Settings format crawl

* Search provider - Fix FileSearchProvider to return array, not progress

* Fix #55598

* Settings editor - fix NPE rendering settings with no description

* dont render inden guides in search box (#55600)

* fixes #55454

* More settings crawl

* Another change for #55598 - maxResults applies to FileSearch and TextSearch but not FileIndex

* Fix FileSearchProvider unit tests for progress change

* fixes #55561

* Settings description update for #54690

* Update setting descriptions for online services

* Minor edits

* fixes #55513

* fixes #55451

* Fix #55612 - fix findTextInFiles cancellation

* fixes #55539

* More setting description tweaks

* Setting to disable online experiments #54354

* fixes #55507

* fixes #55515

* Show online services action only in Insiders for now

* Settings editor - change toc behavior default to 'filter'

* Settings editor - nicer filter count style during search

* Fix #55617 - search viewlet icons

* Settings editor - better styling for element count indicator

* SearchProvider - fix NPE when searching extraFileResources

* Allow extends to work without json suffix

Fixes #16905

* Remove accessability options logic entirely

Follow up on #55451

* use latest version of DAP

* fixes #55490

* fixes #55122

* fixes #52332

* Avoid assumptions about git: URIs (fixes #36236)

* relative path for descriptions

* resourece: get rid of isFile context key

fixes #48275

* Register previous ids for compatibility (#53497)

* more tuning for #48275

* no need to always re-read "files explorer"

fixes #52003

* read out active composites properly

fixes #51967

* Update link colors for hc theme to meet color contrast ratio, fixes #55651

Also updated link color for `textLinkActiveForeground` to be the same as `textLinkForeground` as it wasn't properly updated

* detect 'winpty-agent.exe'; fixes #55672

* node-debug@1.26.7

* reset counter on new label

* Settings editor - fix multiple setting links in one description

* Settings editor - color code blocks in setting descriptions, fix #55532

* Settings editor - hover color in TOC

* Settings editor - fix navigation NPE

* Settings editor - fix text control width

* Settings editor - maybe fix #55684

* Fix bug causing cursor to not move on paste

* fixes #53582

* Use ctrlCmd instead of ctrl for go down from search box

* fixes #55264

* fixes #55456

* filter for spcaes before triggering search (#55611)

* Fix #55698 - don't lose filtered TOC counts when refreshing TOC

* fixes #55421

* fixes #28979

* fixes #55576

* only add check for updates to windows/linux help

* readonly files: append decoration to label

fixes #53022

* debug: do not show toolbar while initialising

fixes #55026

* Opening launch.json should not activate debug extensions

fixes #55029

* fixes #55435

* fixes #55434

* fixes #55439

* trigger menu only on altkey up

* Fix #50555 - fix settings editor memory leak

* Fix #55712 - no need to focus 'a' anymore when restoring control focus after tree render

* fixes #55335

* proper fix for readonly model

fixes #53022

* improve FoldingRangeKind spec (for #55686)

* Use class with static fields (fixes #55494)

* Fixes #53671

* fixes #54630

* [html] should disable ionic suggestions by default. Currently forces deprecated Ionic v1 suggestions in .html files while typing. Fixes #53324

* cleanup deps

* debug issues back to andre

* update electron for smoketest

* Fix #55757 - prevent settings tabs from overflowing

* Fix #53897 - revert setting menu defaults to old editor

* Add enum descriptions to `typescript.preferences.importModuleSpecifier`

* Fix #55767 - leaking style elements from settings editor

* Fix #55521 - prevent flashing when clicking in exclude control

* Update Git modified color for contrast ratio, fixes #53140

* Revert "Merge branch 'master' of github.com:Microsoft/vscode"

This reverts commit bf46b6bfbae0cab99c2863e1244a916181fa9fbc, reversing
changes made to e275a424483dfb4ed33b428c97d5e2c441d6b917.

* Revert "Revert "Merge branch 'master' of github.com:Microsoft/vscode""

This reverts commit 53949d963f39e40757557c6526332354a31d9154.

* don't ask to install an incomplete menu

* Fix NPE in terminal AccessibilityManager

Fixes #55744

* don't display fallback menu unless we've closed the last window

* fixes #55547

* Fix smoke tests for extension search box

* Update OSSREADME.json for Electron 2.0.5

* Update distro

Includes Chromium license changes

* fix #55455

* fix #55865

* fixes #55893

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation (#55805)

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation

* ONly show on @recommended or @recommended:workspace

* Make more consistant

* Fix #55911

* Understand json activity (#55926)

* Understand json file activity

* Refactoring

* adding composer.json

* Distro update for experiments

* use terminal.processId for auto-attach; fixes #55918

* Reject invalid URI with vscode.openFolder (for #55891)

* improve win32 setup system vs user detection

fixes #55840

fixes #55840

delay winreg import

related to #55840

show notification earlier

related to #55840

fix #55840

update inno setup message

related to #55840

* Fix #55593 - this code only operates on local paths, so use fsPath and Uri.file instead

* Bring back the old menu due to electron 2.0 issues (#55913)

* add the old menu back for native menus

* make menu labels match

* `vscode.openFolder`: treat missing URI schema gracefully (for #55891)

* delay EH reattach; fixes #55955

* Mark all json files under appSettingsHome as settings

* Use localized strings for telemetry opt-out

* Exception when saving file editor opened from remote file provider (fixes #55051)

* Remove terminal menu from stable

Fixes 56003

* VSCode Insiders crashes on open with TypeError: Cannot read property 'lastIndexOf' of undefined. Fixes #54933

* improve fix for #55891

* fix #55916

* Improve #55891

* increase EH debugging restart delay; fixes #55955

* Revert "Don't include non-resource entries in history quick pick"

This reverts commit 37209a838e9f7e9abe6dc53ed73cdf1e03b72060.

* Diff editor: horizontal scrollbar height is smaller (fixes #56062)

* improve openFolder uri fix (correctly treat backslashes)

* fixes #56116
repair ipc for native menubar keybindings

* Fix #56240 - Open the JSON settings editor instead of the UI editor

* Fix #55536

* uriDisplay: if no formatter is registered fall back to getPathlabel

fixes #56104

* VSCode hangs when opening python file. Fixes #56377

* VS Code Hangs When Opening Specific PowerShell File. Fixes #56430

* Fix #56433 - search extraFileResources even when no folders open

* Workaround #55649

* Fix in master #56371

* Fix tests #56371

* Fix in master #56317

* increase version to 1.26.1

* Fixes #56387: Handle SIGPIPE in extension host

* fixes #56185

* Fix merge issues (part 1)

* Fix build breaks (part 1)

* Build breaks (part 2)

* Build breaks (part 3)

* More build breaks (part 4)

* Fix build breaks (part 5)

* WIP

* Fix menus

* Render query result and message panels (#2363)

* Put back query editor hot exit changes

* Fix grid changes that broke profiler (#2365)

* Update APIs for saving query editor state

* Fix restore view state for profiler and edit data

* Updating custom default themes to support 4.5:1 contrast ratio

* Test updates

* Fix Extension Manager and Windows Setup

* Update license headers

* Add appveyor and travis files back

* Fix hidden modal dropdown issue
This commit is contained in:
Karl Burtram
2018-09-04 14:55:00 -07:00
committed by GitHub
parent 3763278366
commit 81329fa7fa
2638 changed files with 118456 additions and 64012 deletions

View File

@@ -8,7 +8,10 @@ import URI from 'vs/base/common/uri';
import * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands';
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
import { isWindows } from 'vs/base/common/platform';
// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
@@ -32,7 +35,7 @@ export class PreviewHTMLAPICommand {
public static execute(executor: ICommandsExecutor, uri: URI, position?: vscode.ViewColumn, label?: string, options?: any): Thenable<any> {
return executor.executeCommand('_workbench.previewHtml',
uri,
typeof position === 'number' && typeConverters.fromViewColumn(position),
typeof position === 'number' && typeConverters.ViewColumn.from(position),
label,
options
);
@@ -46,8 +49,13 @@ export class OpenFolderAPICommand {
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', forceNewWindow);
}
if (!uri.scheme || isWindows && uri.scheme.match(/^[a-zA-Z]$/)) {
// workaround for #55916 and #55891, will be removed in 1.28
console.warn(`'vscode.openFolder' command invoked with an invalid URI (file:// scheme missing): '${uri}'. Converted to a 'file://' URI.`);
uri = URI.file((uri.scheme ? uri.scheme + ':' : '') + uri.path);
}
return executor.executeCommand('_files.windowOpen', [uri.fsPath], forceNewWindow);
return executor.executeCommand('_files.windowOpen', [uri], forceNewWindow);
}
}
CommandsRegistry.registerCommand(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute));
@@ -59,8 +67,8 @@ export class DiffAPICommand {
left, right,
label,
undefined,
typeConverters.toTextEditorOptions(options),
options ? typeConverters.fromViewColumn(options.viewColumn) : undefined
typeConverters.TextEditorOptions.from(options),
options ? typeConverters.ViewColumn.from(options.viewColumn) : undefined
]);
}
}
@@ -70,21 +78,21 @@ export class OpenAPICommand {
public static ID = 'vscode.open';
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions): Thenable<any> {
let options: ITextEditorOptions;
let column: EditorPosition;
let position: EditorViewColumn;
if (columnOrOptions) {
if (typeof columnOrOptions === 'number') {
column = typeConverters.fromViewColumn(columnOrOptions);
position = typeConverters.ViewColumn.from(columnOrOptions);
} else {
options = typeConverters.toTextEditorOptions(columnOrOptions);
column = typeConverters.fromViewColumn(columnOrOptions.viewColumn);
options = typeConverters.TextEditorOptions.from(columnOrOptions);
position = typeConverters.ViewColumn.from(columnOrOptions.viewColumn);
}
}
return executor.executeCommand('_workbench.open', [
resource,
options,
column
position
]);
}
}
@@ -97,3 +105,11 @@ export class RemoveFromRecentlyOpenedAPICommand {
}
}
CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute));
export class SetEditorLayoutAPICommand {
public static ID = 'vscode.setEditorLayout';
public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Thenable<any> {
return executor.executeCommand('layoutEditorGroups', layout);
}
}
CommandsRegistry.registerCommand(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute));

View File

@@ -7,7 +7,7 @@
import { Emitter } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { score } from 'vs/editor/common/modes/languageSelector';
import * as Platform from 'vs/base/common/platform';
import * as platform from 'vs/base/common/platform';
import * as errors from 'vs/base/common/errors';
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
@@ -31,7 +31,7 @@ import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalSer
import { ExtHostMessageService } from 'vs/workbench/api/node/extHostMessageService';
import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors';
import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ExtHostLanguageFeatures, ISchemeTransformer } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
// {{SQL CARBON EDIT}}
@@ -47,18 +47,19 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as vscode from 'vscode';
import * as paths from 'vs/base/common/paths';
import * as files from 'vs/platform/files/common/files';
import { MainContext, ExtHostContext, IInitData, IExtHostContext } from './extHost.protocol';
import { MainContext, ExtHostContext, IInitData, IMainContext } from './extHost.protocol';
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs';
import { ExtHostFileSystem } from 'vs/workbench/api/node/extHostFileSystem';
import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations';
import { toGlobPattern, toLanguageSelector } from 'vs/workbench/api/node/extHostTypeConverters';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator';
import { OverviewRulerLane } from 'vs/editor/common/model';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview';
import { ExtHostComments } from './extHostComments';
import { ExtHostSearch } from './extHostSearch';
import { ExtHostUrls } from './extHostUrls';
@@ -80,7 +81,7 @@ function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
if (extension.enableProposedApi) {
return fn;
} else {
return <any>throwProposedApiError;
return throwProposedApiError.bind(null, extension);
}
}
@@ -89,13 +90,15 @@ function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
*/
export function createApiFactory(
initData: IInitData,
rpcProtocol: IExtHostContext,
rpcProtocol: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extensionService: ExtHostExtensionService,
extHostLogService: ExtHostLogService
): IExtensionApiFactory {
let schemeTransformer: ISchemeTransformer = null;
// Addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
@@ -108,26 +111,27 @@ export function createApiFactory(
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors)));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
// {{SQL CARBON EDIT}}
//const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(threadService, extHostWorkspace));
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics, extHostLogService));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
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));
// {{SQL CARBON EDIT}}
//const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration));
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const exthostCommentProviders = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands.converter, extHostDocuments));
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => ExtHostContext[key]);
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => (<any>ExtHostContext)[key]);
rpcProtocol.assertRegistered(expected);
// Other instances
@@ -138,7 +142,7 @@ export function createApiFactory(
const extHostLanguages = new ExtHostLanguages(rpcProtocol);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands, extHostTask);
ExtHostApiCommands.register(extHostCommands);
return function (extension: IExtensionDescription): typeof vscode {
@@ -148,7 +152,7 @@ export function createApiFactory(
// We only inform once, it is not a warning because we just want to raise awareness and because
// we cannot say if the extension is doing it right or wrong...
let checkSelector = (function () {
let done = initData.environment.extensionDevelopmentPath !== extension.extensionFolderPath;
let done = (!extension.isUnderDevelopment);
function informOnce(selector: vscode.DocumentSelector) {
if (!done) {
console.info(`Extension '${extension.id}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`);
@@ -222,7 +226,7 @@ export function createApiFactory(
const env: typeof vscode.env = Object.freeze({
get machineId() { return initData.telemetryInfo.machineId; },
get sessionId() { return initData.telemetryInfo.sessionId; },
get language() { return Platform.language; },
get language() { return platform.language; },
get appName() { return product.nameLong; },
get appRoot() { return initData.environment.appRoot; },
get logLevel() { return extHostLogService.getLevel(); }
@@ -250,17 +254,17 @@ export function createApiFactory(
get onDidChangeDiagnostics() {
return extHostDiagnostics.onDidChangeDiagnostics;
},
getDiagnostics: (resource?) => {
getDiagnostics: (resource?: vscode.Uri) => {
return <any>extHostDiagnostics.getDiagnostics(resource);
},
getLanguages(): TPromise<string[]> {
return extHostLanguages.getLanguages();
},
match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number {
return score(toLanguageSelector(selector), document.uri, document.languageId, true);
return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true);
},
registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, metadata);
return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, extension, metadata);
},
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(checkSelector(selector), provider);
@@ -287,7 +291,7 @@ export function createApiFactory(
return extHostLanguageFeatures.registerRenameProvider(checkSelector(selector), provider);
},
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentSymbolProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerDocumentSymbolProvider(checkSelector(selector), provider, extension);
},
registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
return extHostLanguageFeatures.registerWorkspaceSymbolProvider(provider);
@@ -329,8 +333,11 @@ export function createApiFactory(
get visibleTextEditors() {
return extHostEditors.getVisibleTextEditors();
},
get activeTerminal() {
return proposedApiFunction(extension, extHostTerminalService.activeTerminal);
},
get terminals() {
return proposedApiFunction(extension, extHostTerminalService.terminals);
return extHostTerminalService.terminals;
},
showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor> {
let documentPromise: TPromise<vscode.TextDocument>;
@@ -367,8 +374,11 @@ export function createApiFactory(
onDidCloseTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables);
},
onDidOpenTerminal: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
onDidOpenTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidOpenTerminal(listener, thisArg, disposables);
},
onDidChangeActiveTerminal: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables);
}),
get state() {
return extHostWindow.state;
@@ -416,8 +426,8 @@ export function createApiFactory(
createOutputChannel(name: string): vscode.OutputChannel {
return extHostOutputService.createOutputChannel(name);
},
createWebviewPanel(viewType: string, title: string, column: vscode.ViewColumn, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviews.createWebview(viewType, title, column, options, extension.extensionFolderPath);
createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviews.createWebview(extension.extensionLocation, viewType, title, showOptions, options);
},
createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
if (typeof nameOrOptions === 'object') {
@@ -425,12 +435,18 @@ export function createApiFactory(
}
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
},
createTerminalRenderer(name: string): vscode.TerminalRenderer {
return extHostTerminalService.createTerminalRenderer(name);
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider);
},
createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider<any> }): vscode.TreeView<any> {
return extHostTreeViews.createTreeView(viewId, options);
},
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer);
},
// proposed API
sampleFunction: proposedApiFunction(extension, () => {
return extHostMessageService.showMessage(extension, Severity.Info, 'Hello Proposed Api!', {}, []);
@@ -438,12 +454,15 @@ export function createApiFactory(
registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => {
return extHostDecorations.registerDecorationProvider(provider, extension.id);
}),
registerWebviewPanelSerializer: proposedApiFunction(extension, (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer);
}),
registerProtocolHandler: proposedApiFunction(extension, (handler: vscode.ProtocolHandler) => {
return extHostUrls.registerProtocolHandler(extension.id, handler);
})
registerUriHandler(handler: vscode.UriHandler) {
return extHostUrls.registerUriHandler(extension.id, handler);
},
createQuickPick<T extends vscode.QuickPickItem>(): vscode.QuickPick<T> {
return extHostQuickOpen.createQuickPick(extension.id);
},
createInputBox(): vscode.InputBox {
return extHostQuickOpen.createInputBox(extension.id);
},
};
// namespace: workspace
@@ -476,7 +495,22 @@ export function createApiFactory(
return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace);
},
findFiles: (include, exclude, maxResults?, token?) => {
return extHostWorkspace.findFiles(toGlobPattern(include), toGlobPattern(exclude), maxResults, extension.id, token);
return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.id, token);
},
findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback, callbackOrToken?, token?: vscode.CancellationToken) => {
let options: vscode.FindTextInFilesOptions;
let callback: (result: vscode.TextSearchResult) => void;
if (typeof optionsOrCallback === 'object') {
options = optionsOrCallback;
callback = callbackOrToken;
} else {
options = {};
callback = optionsOrCallback;
token = callbackOrToken;
}
return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.id, token);
},
saveAll: (includeUntitled?) => {
return extHostWorkspace.saveAll(includeUntitled);
@@ -485,7 +519,7 @@ export function createApiFactory(
return extHostEditors.applyWorkspaceEdit(edit);
},
createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => {
return extHostFileSystemEvent.createFileSystemWatcher(toGlobPattern(pattern), ignoreCreate, ignoreChange, ignoreDelete);
return extHostFileSystemEvent.createFileSystemWatcher(typeConverters.GlobPattern.from(pattern), ignoreCreate, ignoreChange, ignoreDelete);
},
get textDocuments() {
return extHostDocuments.getAllDocumentData().map(data => data.document);
@@ -542,29 +576,33 @@ export function createApiFactory(
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
return extHostTask.registerTaskProvider(extension, provider);
},
fetchTasks: proposedApiFunction(extension, (filter?: vscode.TaskFilter): Thenable<vscode.Task[]> => {
return extHostTask.fetchTasks(filter);
}),
executeTask: proposedApiFunction(extension, (task: vscode.Task): Thenable<vscode.TaskExecution> => {
return extHostTask.executeTask(extension, task);
}),
get taskExecutions(): vscode.TaskExecution[] {
return extHostTask.taskExecutions;
},
onDidStartTask: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidStartTask(listeners, thisArgs, disposables);
},
onDidEndTask: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidEndTask(listeners, thisArgs, disposables);
},
registerFileSystemProvider(scheme, provider, options) {
return extHostFileSystem.registerFileSystemProvider(scheme, provider, options);
},
registerDeprecatedFileSystemProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostFileSystem.registerDeprecatedFileSystemProvider(scheme, provider);
registerFileSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerFileSearchProvider(scheme, provider);
}),
registerSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerSearchProvider(scheme, provider);
registerSearchProvider: proposedApiFunction(extension, () => {
// Temp for live share in Insiders
return { dispose: () => { } };
}),
registerTextSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerTextSearchProvider(scheme, provider);
}),
registerFileIndexProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerFileIndexProvider(scheme, provider);
}),
registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => {
return exthostCommentProviders.registerDocumentCommentProvider(provider);
}),
registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => {
return exthostCommentProviders.registerWorkspaceCommentProvider(provider);
}),
onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
}),
onWillRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables);
})
};
@@ -580,6 +618,32 @@ export function createApiFactory(
// {{SQL CARBON EDIT}}
// delete namespace: debug
const tasks: typeof vscode.tasks = {
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
return extHostTask.registerTaskProvider(extension, provider);
},
fetchTasks: (filter?: vscode.TaskFilter): Thenable<vscode.Task[]> => {
return extHostTask.fetchTasks(filter);
},
executeTask: (task: vscode.Task): Thenable<vscode.TaskExecution> => {
return extHostTask.executeTask(extension, task);
},
get taskExecutions(): vscode.TaskExecution[] {
return extHostTask.taskExecutions;
},
onDidStartTask: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidStartTask(listeners, thisArgs, disposables);
},
onDidEndTask: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidEndTask(listeners, thisArgs, disposables);
},
onDidStartTaskProcess: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidStartTaskProcess(listeners, thisArgs, disposables);
},
onDidEndTaskProcess: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidEndTaskProcess(listeners, thisArgs, disposables);
}
};
return <typeof vscode>{
version: pkg.version,
@@ -593,6 +657,7 @@ export function createApiFactory(
scm,
// {{SQL CARBON EDIT}}
// debug,
tasks,
// types
Breakpoint: extHostTypes.Breakpoint,
CancellationTokenSource: CancellationTokenSource,
@@ -602,6 +667,7 @@ export function createApiFactory(
Color: extHostTypes.Color,
ColorPresentation: extHostTypes.ColorPresentation,
ColorInformation: extHostTypes.ColorInformation,
CodeActionTrigger: extHostTypes.CodeActionTrigger,
EndOfLine: extHostTypes.EndOfLine,
CompletionItem: extHostTypes.CompletionItem,
CompletionItemKind: extHostTypes.CompletionItemKind,
@@ -611,6 +677,7 @@ export function createApiFactory(
// DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
Diagnostic: extHostTypes.Diagnostic,
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
DiagnosticTag: extHostTypes.DiagnosticTag,
DiagnosticSeverity: extHostTypes.DiagnosticSeverity,
Disposable: extHostTypes.Disposable,
DocumentHighlight: extHostTypes.DocumentHighlight,
@@ -626,6 +693,7 @@ export function createApiFactory(
OverviewRulerLane: OverviewRulerLane,
ParameterInformation: extHostTypes.ParameterInformation,
Position: extHostTypes.Position,
QuickInputButtons: extHostTypes.QuickInputButtons,
Range: extHostTypes.Range,
Selection: extHostTypes.Selection,
SignatureHelp: extHostTypes.SignatureHelp,
@@ -634,12 +702,7 @@ export function createApiFactory(
SourceBreakpoint: extHostTypes.SourceBreakpoint,
StatusBarAlignment: extHostTypes.StatusBarAlignment,
SymbolInformation: extHostTypes.SymbolInformation,
HierarchicalSymbolInformation: class extends extHostTypes.HierarchicalSymbolInformation {
constructor(name, kind, keyof, range) {
checkProposedApiEnabled(extension);
super(name, kind, keyof, range);
}
},
DocumentSymbol: extHostTypes.DocumentSymbol,
SymbolKind: extHostTypes.SymbolKind,
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason,
@@ -669,17 +732,29 @@ export function createApiFactory(
ConfigurationTarget: extHostTypes.ConfigurationTarget,
RelativePattern: extHostTypes.RelativePattern,
DeprecatedFileChangeType: extHostTypes.DeprecatedFileChangeType,
DeprecatedFileType: extHostTypes.DeprecatedFileType,
FileChangeType: extHostTypes.FileChangeType,
FileType: files.FileType,
FileSystemError: extHostTypes.FileSystemError,
FoldingRange: extHostTypes.FoldingRange,
FoldingRangeKind: extHostTypes.FoldingRangeKind
FoldingRangeKind: extHostTypes.FoldingRangeKind,
CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState
};
};
}
/**
* Returns the original fs path (using the original casing for the drive letter)
*/
export function originalFSPath(uri: URI): string {
const result = uri.fsPath;
if (/^[a-zA-Z]:/.test(result) && uri.path.charAt(1).toLowerCase() === result.charAt(0)) {
// Restore original drive letter casing
return uri.path.charAt(1) + result.substr(1);
}
return result;
}
class Extension<T> implements vscode.Extension<T> {
private _extensionService: ExtHostExtensionService;
@@ -691,7 +766,7 @@ class Extension<T> implements vscode.Extension<T> {
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) {
this._extensionService = extensionService;
this.id = description.id;
this.extensionPath = paths.normalize(description.extensionFolderPath, true);
this.extensionPath = paths.normalize(originalFSPath(description.extensionLocation), true);
this.packageJSON = description;
}
@@ -720,13 +795,13 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request, parent, isMain) {
node_module._load = function load(request: string, parent: any, isMain: any) {
if (request !== 'vscode') {
return original.apply(this, arguments);
}
// get extension id from filename and api for extension
const ext = extensionPaths.findSubstr(parent.filename);
const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
if (ext) {
let apiImpl = extApiImpl.get(ext.id);
if (!apiImpl) {
@@ -738,6 +813,9 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
// fall back to a default implementation
if (!defaultApiImpl) {
let extensionPathsPretty = '';
extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.id}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
defaultApiImpl = factory(nullExtensionDescription);
}
return defaultApiImpl;
@@ -753,8 +831,9 @@ const nullExtensionDescription: IExtensionDescription = {
enableProposedApi: false,
engines: undefined,
extensionDependencies: undefined,
extensionFolderPath: undefined,
extensionLocation: undefined,
isBuiltin: false,
isUnderDevelopment: false,
main: undefined,
version: undefined
};

View File

@@ -4,57 +4,48 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createMainContextProxyIdentifier as createMainId, createExtHostContextProxyIdentifier as createExtId, ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import * as vscode from 'vscode';
import URI, { UriComponents } from 'vs/base/common/uri';
import { SerializedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { IConfig, IAdapterExecutable, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ITreeItem } from 'vs/workbench/common/views';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { SerializedError } from 'vs/base/common/errors';
import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileWriteOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { ILineMatch, IPatternInfo } from 'vs/platform/search/common/search';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files';
import { LogLevel } from 'vs/platform/log/common/log';
import { TaskExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO } from 'vs/workbench/api/shared/tasks';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { IPickOptions, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IPatternInfo, IQueryOptions, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats } from 'vs/platform/search/common/search';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { EndOfLine, IFileOperationOptions, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
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 { 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';
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol, ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/common/progress';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
appRoot: string;
appSettingsHome: string;
disableExtensions: boolean;
extensionDevelopmentPath: string;
extensionTestsPath: string;
}
@@ -102,6 +93,14 @@ export interface MainThreadCommandsShape extends IDisposable {
$getCommands(): Thenable<string[]>;
}
export interface MainThreadCommentsShape extends IDisposable {
$registerDocumentCommentProvider(handle: number): void;
$unregisterDocumentCommentProvider(handle: number): void;
$registerWorkspaceCommentProvider(handle: number): void;
$unregisterWorkspaceCommentProvider(handle: number): void;
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void;
}
export interface MainThreadConfigurationShape extends IDisposable {
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: UriComponents): TPromise<void>;
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: UriComponents): TPromise<void>;
@@ -181,7 +180,7 @@ export interface IApplyEditsOptions extends IUndoStopOptions {
}
export interface ITextDocumentShowOptions {
position?: EditorPosition;
position?: EditorViewColumn;
preserveFocus?: boolean;
pinned?: boolean;
selection?: IRange;
@@ -191,7 +190,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): TPromise<string>;
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
$removeTextEditorDecorationType(key: string): void;
$tryShowEditor(id: string, position: EditorPosition): TPromise<void>;
$tryShowEditor(id: string, position: EditorViewColumn): TPromise<void>;
$tryHideEditor(id: string): TPromise<void>;
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<void>;
$trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): TPromise<void>;
@@ -206,8 +205,8 @@ export interface MainThreadTextEditorsShape extends IDisposable {
export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): void;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options?: { select?: boolean }): TPromise<void>;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): TPromise<void>;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: { select: boolean, focus: boolean }): TPromise<void>;
}
export interface MainThreadErrorsShape extends IDisposable {
@@ -263,7 +262,7 @@ export interface ISerializedDocumentFilter {
export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): void;
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: string): void;
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void;
$emitCodeLensEvent(eventHandle: number, event?: any): void;
$registerDeclaractionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
@@ -316,26 +315,98 @@ export interface MainThreadProgressShape extends IDisposable {
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean): TPromise<number>;
$createTerminalRenderer(name: string): TPromise<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
$show(terminalId: number, preserveFocus: boolean): void;
$registerOnDataListener(terminalId: number): void;
// Process
$sendProcessTitle(terminalId: number, title: string): void;
$sendProcessData(terminalId: number, data: string): void;
$sendProcessPid(terminalId: number, pid: number): void;
$sendProcessExit(terminalId: number, exitCode: number): void;
// Renderer
$terminalRendererSetName(terminalId: number, name: string): void;
$terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void;
$terminalRendererWrite(terminalId: number, text: string): void;
$terminalRendererRegisterOnInputListener(terminalId: number): void;
}
export interface MyQuickPickItems extends IPickOpenEntry {
export interface TransferQuickPickItems extends IQuickPickItem {
handle: number;
}
export interface TransferQuickInputButton extends IQuickInputButton {
handle: number;
}
export type TransferQuickInput = TransferQuickPick | TransferInputBox;
export interface BaseTransferQuickInput {
id: number;
type?: 'quickPick' | 'inputBox';
enabled?: boolean;
busy?: boolean;
visible?: boolean;
}
export interface TransferQuickPick extends BaseTransferQuickInput {
type?: 'quickPick';
value?: string;
placeholder?: string;
buttons?: TransferQuickInputButton[];
items?: TransferQuickPickItems[];
activeItems?: number[];
selectedItems?: number[];
canSelectMany?: boolean;
ignoreFocusOut?: boolean;
matchOnDescription?: boolean;
matchOnDetail?: boolean;
}
export interface TransferInputBox extends BaseTransferQuickInput {
type?: 'inputBox';
value?: string;
placeholder?: string;
password?: boolean;
buttons?: TransferQuickInputButton[];
prompt?: string;
validationMessage?: string;
}
export interface MainThreadQuickOpenShape extends IDisposable {
$show(options: IPickOptions): TPromise<number | number[]>;
$setItems(items: MyQuickPickItems[]): TPromise<any>;
$show(options: IPickOptions<TransferQuickPickItems>): TPromise<number | number[]>;
$setItems(items: TransferQuickPickItems[]): TPromise<any>;
$setError(error: Error): TPromise<any>;
$input(options: vscode.InputBoxOptions, validateInput: boolean): TPromise<string>;
$createOrUpdate(params: TransferQuickInput): TPromise<void>;
$dispose(id: number): TPromise<void>;
}
export interface MainThreadStatusBarShape extends IDisposable {
@@ -354,29 +425,41 @@ export interface MainThreadTelemetryShape extends IDisposable {
export type WebviewPanelHandle = string;
export interface WebviewPanelShowOptions {
readonly viewColumn?: EditorViewColumn;
readonly preserveFocus?: boolean;
}
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, column: EditorPosition, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionFolderPath: string): void;
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionLocation: UriComponents): void;
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, column: EditorPosition | undefined): void;
$reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void;
$setTitle(handle: WebviewPanelHandle, value: string): void;
$setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void;
$setHtml(handle: WebviewPanelHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void;
$postMessage(handle: WebviewPanelHandle, value: any): Thenable<boolean>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
}
export interface WebviewPanelViewState {
readonly active: boolean;
readonly visible: boolean;
readonly position: EditorViewColumn;
}
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewPanelHandle, message: any): void;
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, active: boolean, position: EditorPosition): void;
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): void;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Thenable<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorPosition, options: vscode.WebviewOptions): Thenable<void>;
$serializeWebviewPanel(webviewHandle: WebviewPanelHandle): Thenable<any>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: vscode.WebviewOptions): Thenable<void>;
}
export interface MainThreadUrlsShape extends IDisposable {
$registerProtocolHandler(handle: number, extensionId: string): TPromise<void>;
$unregisterProtocolHandler(handle: number): TPromise<void>;
$registerUriHandler(handle: number, extensionId: string): TPromise<void>;
$unregisterUriHandler(handle: number): TPromise<void>;
}
export interface ExtHostUrlsShape {
@@ -384,7 +467,8 @@ export interface ExtHostUrlsShape {
}
export interface MainThreadWorkspaceShape extends IDisposable {
$startSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>;
$startFileSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>;
$startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number): TPromise<void>;
$cancelSearch(requestId: number): Thenable<boolean>;
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>;
@@ -402,17 +486,22 @@ export interface MainThreadFileSystemShape extends IDisposable {
}
export interface MainThreadSearchShape extends IDisposable {
$registerSearchProvider(handle: number, scheme: string): void;
$registerFileSearchProvider(handle: number, scheme: string): void;
$registerTextSearchProvider(handle: number, scheme: string): void;
$registerFileIndexProvider(handle: number, scheme: string): void;
$unregisterProvider(handle: number): void;
$handleFindMatch(handle: number, session, data: UriComponents | [UriComponents, ILineMatch]): void;
$handleFileMatch(handle: number, session: number, data: UriComponents[]): void;
$handleTextMatch(handle: number, session: number, data: IRawFileMatch2[]): void;
$handleTelemetry(eventName: string, data: any): void;
}
export interface MainThreadTaskShape extends IDisposable {
$registerTaskProvider(handle: number): TPromise<void>;
$unregisterTaskProvider(handle: number): TPromise<void>;
$fetchTasks(filter?: TaskFilterDTO): TPromise<TaskDTO[]>;
$executeTask(task: TaskHandleDTO | TaskDTO): TPromise<TaskExecutionDTO>;
$terminateTask(id: string): TPromise<void>;
$unregisterTaskProvider(handle: number): TPromise<void>;
$registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void;
}
export interface MainThreadExtensionServiceShape extends IDisposable {
@@ -482,10 +571,10 @@ export interface MainThreadSCMShape extends IDisposable {
export type DebugSessionUUID = string;
export interface MainThreadDebugServiceShape extends IDisposable {
$registerDebugTypes(debugTypes: string[]);
$acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage);
$acceptDAError(handle: number, name: string, message: string, stack: string);
$acceptDAExit(handle: number, code: number, signal: string);
$registerDebugTypes(debugTypes: string[]): void;
$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): TPromise<any>;
$unregisterDebugConfigurationProvider(handle: number): TPromise<any>;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean>;
@@ -546,10 +635,10 @@ export interface ITextEditorAddData {
options: IResolvedTextEditorConfiguration;
selections: ISelection[];
visibleRanges: IRange[];
editorPosition: EditorPosition;
editorPosition: EditorViewColumn;
}
export interface ITextEditorPositionData {
[id: string]: EditorPosition;
[id: string]: EditorViewColumn;
}
export interface IEditorPropertiesChangeData {
options: IResolvedTextEditorConfiguration | null;
@@ -580,10 +669,14 @@ export interface ExtHostDocumentsAndEditorsShape {
export interface ExtHostTreeViewsShape {
$getChildren(treeViewId: string, treeItemHandle?: string): TPromise<ITreeItem[]>;
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void;
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
$setVisible(treeViewId: string, visible: boolean): void;
}
export interface ExtHostWorkspaceShape {
$acceptWorkspaceData(workspace: IWorkspaceData): void;
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void;
}
export interface ExtHostFileSystemShape {
@@ -594,14 +687,15 @@ export interface ExtHostFileSystemShape {
$rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>;
$copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>;
$mkdir(handle: number, resource: UriComponents): TPromise<void>;
$delete(handle: number, resource: UriComponents): TPromise<void>;
$delete(handle: number, resource: UriComponents, opts: FileDeleteOptions): TPromise<void>;
$watch(handle: number, session: number, resource: UriComponents, opts: IWatchOptions): void;
$unwatch(handle: number, session: number): void;
}
export interface ExtHostSearchShape {
$provideFileSearchResults(handle: number, session: number, query: string): TPromise<void>;
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, options: { includes: string[], excludes: string[] }): TPromise<void>;
$provideFileSearchResults(handle: number, session: number, query: IRawSearchQuery): TPromise<ISearchCompleteStats>;
$clearCache(cacheKey: string): TPromise<void>;
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, query: IRawSearchQuery): TPromise<ISearchCompleteStats>;
}
export interface ExtHostExtensionServiceShape {
@@ -615,6 +709,8 @@ export interface FileSystemEvents {
}
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onFileRename(oldUri: UriComponents, newUri: UriComponents): void;
$onWillRename(oldUri: UriComponents, newUri: UriComponents): TPromise<any>;
}
export interface ObjectIdentifier {
@@ -664,7 +760,14 @@ export interface LocationDto {
range: IRange;
}
export interface SymbolInformationDto extends IdObject {
export interface DefinitionLinkDto {
origin?: IRange;
uri: UriComponents;
range: IRange;
selectionRange?: IRange;
}
export interface WorkspaceSymbolDto extends IdObject {
name: string;
containerName?: string;
kind: modes.SymbolKind;
@@ -672,12 +775,13 @@ export interface SymbolInformationDto extends IdObject {
}
export interface WorkspaceSymbolsDto extends IdObject {
symbols: SymbolInformationDto[];
symbols: WorkspaceSymbolDto[];
}
export interface ResourceFileEditDto {
oldUri: UriComponents;
newUri: UriComponents;
options: IFileOperationOptions;
}
export interface ResourceTextEditDto {
@@ -716,21 +820,21 @@ export interface CodeActionDto {
}
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents): TPromise<SymbolInformationDto[]>;
$provideDocumentSymbols(handle: number, resource: UriComponents): TPromise<modes.DocumentSymbol[]>;
$provideCodeLenses(handle: number, resource: UriComponents): TPromise<modes.ICodeLensSymbol[]>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol): TPromise<modes.ICodeLensSymbol>;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<LocationDto | LocationDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise<LocationDto | LocationDto[]>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<LocationDto | LocationDto[]>;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<DefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise<DefinitionLinkDto[]>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<DefinitionLinkDto[]>;
$provideHover(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Hover>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.DocumentHighlight[]>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext): TPromise<LocationDto[]>;
$provideCodeActions(handle: number, resource: UriComponents, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext): TPromise<CodeActionDto[]>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
$provideWorkspaceSymbols(handle: number, search: string): TPromise<WorkspaceSymbolsDto>;
$resolveWorkspaceSymbol(handle: number, symbol: SymbolInformationDto): TPromise<SymbolInformationDto>;
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto): TPromise<WorkspaceSymbolDto>;
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string): TPromise<WorkspaceEditDto>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.RenameLocation>;
@@ -748,6 +852,12 @@ export interface ExtHostLanguageFeaturesShape {
export interface ExtHostQuickOpenShape {
$onItemSelected(handle: number): void;
$validateInput(input: string): TPromise<string>;
$onDidChangeActive(sessionId: number, handles: number[]): void;
$onDidChangeSelection(sessionId: number, handles: number[]): void;
$onDidAccept(sessionId: number): void;
$onDidChangeValue(sessionId: number, value: string): void;
$onDidTriggerButton(sessionId: number, handle: number): void;
$onDidHide(sessionId: number): void;
}
export interface ShellLaunchConfigDto {
@@ -761,8 +871,11 @@ export interface ShellLaunchConfigDto {
export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalOpened(id: number, name: string): void;
$acceptActiveTerminalChanged(id: number | null): void;
$acceptTerminalProcessId(id: number, processId: number): void;
$acceptTerminalProcessData(id: number, data: string);
$acceptTerminalProcessData(id: number, data: string): void;
$acceptTerminalRendererInput(id: number, data: string): void;
$acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void;
$acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void;
@@ -777,9 +890,12 @@ export interface ExtHostSCMShape {
}
export interface ExtHostTaskShape {
$provideTasks(handle: number): TPromise<TaskSet>;
$taskStarted(execution: TaskExecutionDTO): void;
$taskEnded(execution: TaskExecutionDTO): void;
$provideTasks(handle: number, validTypes: { [key: string]: boolean; }): TPromise<TaskSet>;
$onDidStartTask(execution: TaskExecutionDTO): void;
$onDidStartTaskProcess(value: TaskProcessStartedDTO): void;
$onDidEndTaskProcess(value: TaskProcessEndedDTO): void;
$OnDidEndTask(execution: TaskExecutionDTO): void;
$resolveVariables(workspaceFolder: UriComponents, variables: string[]): TPromise<any>;
}
export interface IBreakpointDto {
@@ -828,7 +944,7 @@ export interface ISourceMultiBreakpointDto {
export interface ExtHostDebugServiceShape {
$substituteVariables(folder: UriComponents | undefined, config: IConfig): TPromise<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void>;
$startDASession(handle: number, debugType: string, adapterExecutableInfo: IAdapterExecutable | null): TPromise<void>;
$startDASession(handle: number, debugType: string, adapterExecutableInfo: IAdapterExecutable | null, debugPort: number): TPromise<void>;
$stopDASession(handle: number): TPromise<void>;
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise<void>;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): TPromise<IConfig>;
@@ -861,17 +977,25 @@ export interface ExtHostWindowShape {
}
export interface ExtHostLogServiceShape {
$setLevel(level: LogLevel);
$setLevel(level: LogLevel): void;
}
export interface ExtHostProgressShape {
$acceptProgressCanceled(handle: number): void;
}
export interface ExtHostCommentsShape {
$provideDocumentComments(handle: number, document: UriComponents): TPromise<modes.CommentInfo>;
$createNewCommentThread?(handle: number, document: UriComponents, range: IRange, text: string): TPromise<modes.CommentThread>;
$replyToCommentThread?(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): TPromise<modes.CommentThread>;
$provideWorkspaceComments(handle: number): TPromise<modes.CommentThread[]>;
}
// --- proxy identifiers
export const MainContext = {
MainThreadCommands: <ProxyIdentifier<MainThreadCommandsShape>>createMainId<MainThreadCommandsShape>('MainThreadCommands'),
MainThreadComments: createMainId<MainThreadCommentsShape>('MainThreadComments'),
MainThreadConfiguration: createMainId<MainThreadConfigurationShape>('MainThreadConfiguration'),
// {{SQL CARBON EDIT}}
// MainThreadDebugService: createMainId<MainThreadDebugServiceShape>('MainThreadDebugService'),
@@ -931,6 +1055,7 @@ export const ExtHostContext = {
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls'),
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress')
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments'),
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls')
};

View File

@@ -10,30 +10,28 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import * as types from 'vs/workbench/api/node/extHostTypes';
import { IRawColorInfo } from 'vs/workbench/api/node/extHost.protocol';
import { IRawColorInfo, WorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/parts/search/common/search';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ExtHostTask } from './extHostTask';
import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand } from './apiCommands';
import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands';
import { EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
export class ExtHostApiCommands {
static register(commands: ExtHostCommands, workspace: ExtHostTask) {
return new ExtHostApiCommands(commands, workspace).registerCommands();
static register(commands: ExtHostCommands) {
return new ExtHostApiCommands(commands).registerCommands();
}
private _commands: ExtHostCommands;
private _tasks: ExtHostTask;
private _disposables: IDisposable[] = [];
private constructor(commands: ExtHostCommands, task: ExtHostTask) {
private constructor(commands: ExtHostCommands) {
this._commands = commands;
this._tasks = task;
}
registerCommands() {
@@ -105,7 +103,7 @@ export class ExtHostApiCommands {
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
{ name: 'triggerCharacter', description: '(optional) Trigger signature help when the user types the character, like `,` or `(`', constraint: value => value === void 0 || typeof value === 'string' }
{ name: 'triggerCharacter', description: '(optional) Trigger signature help when the user types the character, like `,` or `(`', constraint: (value: any) => value === void 0 || typeof value === 'string' }
],
returns: 'A promise that resolves to SignatureHelp.'
});
@@ -121,8 +119,8 @@ export class ExtHostApiCommands {
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
{ name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: value => value === void 0 || typeof value === 'string' },
{ name: 'itemResolveCount', description: '(optional) Number of completions to resolve (too large numbers slow down completions)', constraint: value => value === void 0 || typeof value === 'number' }
{ name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: (value: any) => value === void 0 || typeof value === 'string' },
{ name: 'itemResolveCount', description: '(optional) Number of completions to resolve (too large numbers slow down completions)', constraint: (value: any) => value === void 0 || typeof value === 'number' }
],
returns: 'A promise that resolves to a CompletionList-instance.'
});
@@ -138,7 +136,7 @@ export class ExtHostApiCommands {
description: 'Execute CodeLens provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only retrun resolved lenses, will impact performance)', constraint: value => value === void 0 || typeof value === 'number' }
{ name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only retrun resolved lenses, will impact performance)', constraint: (value: any) => value === void 0 || typeof value === 'number' }
],
returns: 'A promise that resolves to an array of CodeLens-instances.'
});
@@ -176,11 +174,6 @@ export class ExtHostApiCommands {
],
returns: 'A promise that resolves to an array of DocumentLink-instances.'
});
this._register('vscode.executeTaskProvider', this._executeTaskProvider, {
description: 'Execute task provider',
args: [],
returns: 'An array of task handles'
});
this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, {
description: 'Execute document color provider.',
args: [
@@ -219,18 +212,18 @@ export class ExtHostApiCommands {
See [working with the HTML preview](https://code.visualstudio.com/docs/extensionAPI/vscode-api-commands#working-with-the-html-preview) for more information about the HTML preview's integration with the editor and for best practices for extension authors.
`,
args: [
{ name: 'uri', description: 'Uri of the resource to preview.', constraint: value => value instanceof URI || typeof value === 'string' },
{ name: 'column', description: '(optional) Column in which to preview.', constraint: value => typeof value === 'undefined' || (typeof value === 'number' && typeof types.ViewColumn[value] === 'string') },
{ name: 'label', description: '(optional) An human readable string that is used as title for the preview.', constraint: v => typeof v === 'string' || typeof v === 'undefined' },
{ name: 'options', description: '(optional) Options for controlling webview environment.', constraint: v => typeof v === 'object' || typeof v === 'undefined' }
{ name: 'uri', description: 'Uri of the resource to preview.', constraint: (value: any) => value instanceof URI || typeof value === 'string' },
{ name: 'column', description: '(optional) Column in which to preview.', constraint: (value: any) => typeof value === 'undefined' || (typeof value === 'number' && typeof types.ViewColumn[value] === 'string') },
{ name: 'label', description: '(optional) An human readable string that is used as title for the preview.', constraint: (v: any) => typeof v === 'string' || typeof v === 'undefined' },
{ name: 'options', description: '(optional) Options for controlling webview environment.', constraint: (v: any) => typeof v === 'object' || typeof v === 'undefined' }
]
});
this._register(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute), {
description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.',
args: [
{ name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: value => value === void 0 || value instanceof URI },
{ name: 'newWindow', description: '(optional) Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window.', constraint: value => value === void 0 || typeof value === 'boolean' }
{ name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === void 0 || value instanceof URI },
{ name: 'newWindow', description: '(optional) Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window.', constraint: (value: any) => value === void 0 || typeof value === 'boolean' }
]
});
@@ -239,7 +232,7 @@ export class ExtHostApiCommands {
args: [
{ name: 'left', description: 'Left-hand side resource of the diff editor', constraint: URI },
{ name: 'right', description: 'Right-hand side resource of the diff editor', constraint: URI },
{ name: 'title', description: '(optional) Human readable title for the diff editor', constraint: v => v === void 0 || typeof v === 'string' },
{ name: 'title', description: '(optional) Human readable title for the diff editor', constraint: (v: any) => v === void 0 || typeof v === 'string' },
{ name: 'options', description: '(optional) Editor options, see vscode.TextDocumentShowOptions' }
]
});
@@ -248,14 +241,21 @@ export class ExtHostApiCommands {
description: 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.',
args: [
{ name: 'resource', description: 'Resource to open', constraint: URI },
{ name: 'columnOrOptions', description: '(optional) Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', constraint: v => v === void 0 || typeof v === 'number' || typeof v === 'object' }
{ name: 'columnOrOptions', description: '(optional) Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', constraint: (v: any) => v === void 0 || typeof v === 'number' || typeof v === 'object' }
]
});
this._register(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute), {
description: 'Removes an entry with the given path from the recently opened list.',
args: [
{ name: 'path', description: 'Path to remove from recently opened.', constraint: value => typeof value === 'string' }
{ name: 'path', description: 'Path to remove from recently opened.', constraint: (value: any) => typeof value === 'string' }
]
});
this._register(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute), {
description: 'Sets the editor layout. The layout is described as object with an initial (optional) orientation (0 = horizontal, 1 = vertical) and an array of editor groups within. Each editor group can have a size and another array of editor groups that will be laid out orthogonal to the orientation. If editor group sizes are provided, their sum must be 1 to be applied per row or column. Example for a 2x2 grid: `{ orientation: 0, groups: [{ groups: [{}, {}], size: 0.5 }, { groups: [{}, {}], size: 0.5 }] }`',
args: [
{ name: 'layout', description: 'The editor layout to set.', constraint: (value: EditorGroupLayout) => typeof value === 'object' && Array.isArray(value.groups) }
]
});
}
@@ -274,11 +274,11 @@ export class ExtHostApiCommands {
* @return A promise that resolves to an array of symbol information.
*/
private _executeWorkspaceSymbolProvider(query: string): Thenable<types.SymbolInformation[]> {
return this._commands.executeCommand<[IWorkspaceSymbolProvider, modes.SymbolInformation[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => {
return this._commands.executeCommand<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => {
const result: types.SymbolInformation[] = [];
if (Array.isArray(value)) {
for (let tuple of value) {
result.push(...tuple[1].map(typeConverters.toSymbolInformation));
result.push(...tuple[1].map(typeConverters.WorkspaceSymbol.to));
}
}
return result;
@@ -288,7 +288,7 @@ export class ExtHostApiCommands {
private _executeDefinitionProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeDefinitionProvider', args)
.then(tryMapWith(typeConverters.location.to));
@@ -297,7 +297,7 @@ export class ExtHostApiCommands {
private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeTypeDefinitionProvider', args)
.then(tryMapWith(typeConverters.location.to));
@@ -306,7 +306,7 @@ export class ExtHostApiCommands {
private _executeImplementationProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeImplementationProvider', args)
.then(tryMapWith(typeConverters.location.to));
@@ -315,25 +315,25 @@ export class ExtHostApiCommands {
private _executeHoverProvider(resource: URI, position: types.Position): Thenable<types.Hover[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<modes.Hover[]>('_executeHoverProvider', args)
.then(tryMapWith(typeConverters.toHover));
.then(tryMapWith(typeConverters.Hover.to));
}
private _executeDocumentHighlights(resource: URI, position: types.Position): Thenable<types.DocumentHighlight[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<modes.DocumentHighlight[]>('_executeDocumentHighlights', args)
.then(tryMapWith(typeConverters.toDocumentHighlight));
.then(tryMapWith(typeConverters.DocumentHighlight.to));
}
private _executeReferenceProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeReferenceProvider', args)
.then(tryMapWith(typeConverters.location.to));
@@ -342,10 +342,10 @@ export class ExtHostApiCommands {
private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Thenable<types.WorkspaceEdit> {
const args = {
resource,
position: position && typeConverters.fromPosition(position),
position: position && typeConverters.Position.from(position),
newName
};
return this._commands.executeCommand<modes.WorkspaceEdit>('_executeDocumentRenameProvider', args).then(value => {
return this._commands.executeCommand<WorkspaceEditDto>('_executeDocumentRenameProvider', args).then(value => {
if (!value) {
return undefined;
}
@@ -359,7 +359,7 @@ export class ExtHostApiCommands {
private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable<types.SignatureHelp> {
const args = {
resource,
position: position && typeConverters.fromPosition(position),
position: position && typeConverters.Position.from(position),
triggerCharacter
};
return this._commands.executeCommand<modes.SignatureHelp>('_executeSignatureHelpProvider', args).then(value => {
@@ -373,7 +373,7 @@ export class ExtHostApiCommands {
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Thenable<types.CompletionList> {
const args = {
resource,
position: position && typeConverters.fromPosition(position),
position: position && typeConverters.Position.from(position),
triggerCharacter,
maxItemsToResolve
};
@@ -393,7 +393,7 @@ export class ExtHostApiCommands {
};
return this._commands.executeCommand<IRawColorInfo[]>('_executeDocumentColorProvider', args).then(result => {
if (result) {
return result.map(ci => ({ range: typeConverters.toRange(ci.range), color: typeConverters.Color.to(ci.color) }));
return result.map(ci => ({ range: typeConverters.Range.to(ci.range), color: typeConverters.Color.to(ci.color) }));
}
return [];
});
@@ -403,7 +403,7 @@ export class ExtHostApiCommands {
const args = {
resource: context.uri,
color: typeConverters.Color.from(color),
range: typeConverters.fromRange(context.range),
range: typeConverters.Range.from(context.range),
};
return this._commands.executeCommand<modes.IColorPresentation[]>('_executeColorPresentationProvider', args).then(result => {
if (result) {
@@ -413,22 +413,31 @@ export class ExtHostApiCommands {
});
}
private _executeDocumentSymbolProvider(resource: URI): Thenable<types.SymbolInformation[]> {
private _executeDocumentSymbolProvider(resource: URI): Thenable<vscode.SymbolInformation[]> {
const args = {
resource
};
return this._commands.executeCommand<modes.IOutline>('_executeDocumentSymbolProvider', args).then(value => {
if (value && Array.isArray(value.entries)) {
return value.entries.map(typeConverters.toSymbolInformation);
return this._commands.executeCommand<modes.DocumentSymbol[]>('_executeDocumentSymbolProvider', args).then(value => {
if (isFalsyOrEmpty(value)) {
return undefined;
}
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))
));
}
return result;
});
}
private _executeCodeActionProvider(resource: URI, range: types.Range): Thenable<(vscode.CodeAction | vscode.Command)[]> {
const args = {
resource,
range: typeConverters.fromRange(range)
range: typeConverters.Range.from(range)
};
return this._commands.executeCommand<CustomCodeAction[]>('_executeCodeActionProvider', args)
.then(tryMapWith(codeAction => {
@@ -455,7 +464,7 @@ export class ExtHostApiCommands {
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args)
.then(tryMapWith(item => {
return new types.CodeLens(
typeConverters.toRange(item.range),
typeConverters.Range.to(item.range),
this._commands.converter.fromInternal(item.command));
}));
@@ -467,38 +476,34 @@ export class ExtHostApiCommands {
options
};
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatDocumentProvider', args)
.then(tryMapWith(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)));
.then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text)));
}
private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
const args = {
resource,
range: typeConverters.fromRange(range),
range: typeConverters.Range.from(range),
options
};
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatRangeProvider', args)
.then(tryMapWith(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)));
.then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text)));
}
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
const args = {
resource,
position: typeConverters.fromPosition(position),
position: typeConverters.Position.from(position),
ch,
options
};
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatOnTypeProvider', args)
.then(tryMapWith(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)));
.then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text)));
}
private _executeDocumentLinkProvider(resource: URI): Thenable<vscode.DocumentLink[]> {
return this._commands.executeCommand<modes.ILink[]>('_executeLinkProvider', resource)
.then(tryMapWith(typeConverters.DocumentLink.to));
}
private _executeTaskProvider(): Thenable<vscode.Task[]> {
return this._tasks.fetchTasks();
}
}
function tryMapWith<T, R>(f: (x: T) => R) {

View File

@@ -92,10 +92,10 @@ export class ExtHostCommands implements ExtHostCommandsShape {
args = cloneAndChange(args, function (value) {
if (value instanceof extHostTypes.Position) {
return extHostTypeConverter.fromPosition(value);
return extHostTypeConverter.Position.from(value);
}
if (value instanceof extHostTypes.Range) {
return extHostTypeConverter.fromRange(value);
return extHostTypeConverter.Range.from(value);
}
if (value instanceof extHostTypes.Location) {
return extHostTypeConverter.location.from(value);
@@ -133,6 +133,8 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T> {
this._logService.trace('ExtHostCommands#$executeContributedCommand', id);
if (!this._commands.has(id)) {
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
return <any>Promise.reject(new Error(`Contributed command '${id}' does not exist.`));

View File

@@ -0,0 +1,179 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { asWinJsPromise } from 'vs/base/common/async';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import * as modes from 'vs/editor/common/modes';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters';
import * as vscode from 'vscode';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape } from './extHost.protocol';
import { CommandsConverter } from './extHostCommands';
import { IRange } from 'vs/editor/common/core/range';
export class ExtHostComments implements ExtHostCommentsShape {
private static handlePool = 0;
private _proxy: MainThreadCommentsShape;
private _documentProviders = new Map<number, vscode.DocumentCommentProvider>();
private _workspaceProviders = new Map<number, vscode.WorkspaceCommentProvider>();
constructor(
mainContext: IMainContext,
private readonly _commandsConverter: CommandsConverter,
private readonly _documents: ExtHostDocuments,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadComments);
}
registerWorkspaceCommentProvider(
provider: vscode.WorkspaceCommentProvider
): vscode.Disposable {
const handle = ExtHostComments.handlePool++;
this._workspaceProviders.set(handle, provider);
this._proxy.$registerWorkspaceCommentProvider(handle);
this.registerListeners(handle, provider);
return {
dispose: () => {
this._proxy.$unregisterWorkspaceCommentProvider(handle);
this._workspaceProviders.delete(handle);
}
};
}
registerDocumentCommentProvider(
provider: vscode.DocumentCommentProvider
): vscode.Disposable {
const handle = ExtHostComments.handlePool++;
this._documentProviders.set(handle, provider);
this._proxy.$registerDocumentCommentProvider(handle);
this.registerListeners(handle, provider);
return {
dispose: () => {
this._proxy.$unregisterDocumentCommentProvider(handle);
this._documentProviders.delete(handle);
}
};
}
$createNewCommentThread(handle: number, uri: UriComponents, range: IRange, text: string): TPromise<modes.CommentThread> {
const data = this._documents.getDocumentData(URI.revive(uri));
const ran = <vscode.Range>extHostTypeConverter.Range.to(range);
if (!data || !data.document) {
return TPromise.as(null);
}
return asWinJsPromise(token => {
let provider = this._documentProviders.get(handle);
return provider.createNewCommentThread(data.document, ran, text, token);
}).then(commentThread => commentThread ? convertToCommentThread(commentThread, this._commandsConverter) : null);
}
$replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): TPromise<modes.CommentThread> {
const data = this._documents.getDocumentData(URI.revive(uri));
const ran = <vscode.Range>extHostTypeConverter.Range.to(range);
if (!data || !data.document) {
return TPromise.as(null);
}
return asWinJsPromise(token => {
let provider = this._documentProviders.get(handle);
return provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, token);
}).then(commentThread => commentThread ? convertToCommentThread(commentThread, this._commandsConverter) : null);
}
$provideDocumentComments(handle: number, uri: UriComponents): TPromise<modes.CommentInfo> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
return TPromise.as(null);
}
return asWinJsPromise(token => {
let provider = this._documentProviders.get(handle);
return provider.provideDocumentComments(data.document, token);
})
.then(commentInfo => commentInfo ? convertCommentInfo(handle, commentInfo, this._commandsConverter) : null);
}
$provideWorkspaceComments(handle: number): TPromise<modes.CommentThread[]> {
const provider = this._workspaceProviders.get(handle);
if (!provider) {
return TPromise.as(null);
}
return asWinJsPromise(token => {
return provider.provideWorkspaceComments(token);
}).then(comments =>
comments.map(x => convertToCommentThread(x, this._commandsConverter)
));
}
private registerListeners(handle: number, provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider) {
provider.onDidChangeCommentThreads(event => {
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))
});
});
}
}
function convertCommentInfo(owner: number, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo {
return {
owner: owner,
threads: vscodeCommentInfo.threads.map(x => convertToCommentThread(x, commandsConverter)),
commentingRanges: vscodeCommentInfo.commentingRanges ? vscodeCommentInfo.commentingRanges.map(range => extHostTypeConverter.Range.from(range)) : []
};
}
function convertToCommentThread(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)),
collapsibleState: vscodeCommentThread.collapsibleState
};
}
function convertFromCommentThread(commentThread: modes.CommentThread): vscode.CommentThread {
return {
threadId: commentThread.threadId,
resource: URI.parse(commentThread.resource),
range: extHostTypeConverter.Range.to(commentThread.range),
comments: commentThread.comments.map(convertFromComment),
collapsibleState: commentThread.collapsibleState
};
}
function convertFromComment(comment: modes.Comment): vscode.Comment {
return {
commentId: comment.commentId,
body: extHostTypeConverter.MarkdownString.to(comment.body),
userName: comment.userName,
gravatar: comment.gravatar
};
}
function convertToComment(vscodeComment: vscode.Comment, commandsConverter: CommandsConverter): modes.Comment {
return {
commentId: vscodeComment.commentId,
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
userName: vscodeComment.userName,
gravatar: vscodeComment.gravatar,
command: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : null
};
}

View File

@@ -15,7 +15,7 @@ import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs
import { Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
import { ResourceMap } from 'vs/base/common/map';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { isObject } from 'vs/base/common/types';
declare var Proxy: any; // TODO@TypeScript
@@ -121,17 +121,17 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
}
return result;
},
set: (target: any, property: string, value: any) => {
set: (_target: any, property: string, value: any) => {
cloneTarget();
clonedTarget[property] = value;
return true;
},
deleteProperty: (target: any, property: string) => {
deleteProperty: (_target: any, property: string) => {
cloneTarget();
delete clonedTarget[property];
return true;
},
defineProperty: (target: any, property: string, descriptor: any) => {
defineProperty: (_target: any, property: string, descriptor: any) => {
cloneTarget();
Object.defineProperty(clonedTarget, property, descriptor);
return true;
@@ -179,10 +179,10 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
return isObject(target) ?
new Proxy(target, {
get: (target: any, property: string) => readonlyProxy(target[property]),
set: (target: any, property: string, value: any) => { throw new Error(`TypeError: Cannot assign to read only property '${property}' of object`); },
deleteProperty: (target: any, property: string) => { throw new Error(`TypeError: Cannot delete read only property '${property}' of object`); },
defineProperty: (target: any, property: string) => { throw new Error(`TypeError: Cannot define property '${property}' for a readonly object`); },
setPrototypeOf: (target: any) => { throw new Error(`TypeError: Cannot set prototype for a readonly object`); },
set: (_target: any, property: string, _value: any) => { throw new Error(`TypeError: Cannot assign to read only property '${property}' of object`); },
deleteProperty: (_target: any, property: string) => { throw new Error(`TypeError: Cannot delete read only property '${property}' of object`); },
defineProperty: (_target: any, property: string) => { throw new Error(`TypeError: Cannot define property '${property}' for a readonly object`); },
setPrototypeOf: (_target: any) => { throw new Error(`TypeError: Cannot set prototype for a readonly object`); },
isExtensible: () => false,
preventExtensions: () => true
}) : target;
@@ -191,7 +191,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
}
private _validateConfigurationAccess(key: string, resource: URI, extensionId: string): void {
const scope = this._configurationScopes[key];
const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes[key];
const extensionIdText = extensionId ? `[${extensionId}] ` : '';
if (ConfigurationScope.RESOURCE === scope) {
if (resource === void 0) {

View File

@@ -9,6 +9,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Event, Emitter } from 'vs/base/common/event';
import { asWinJsPromise } from 'vs/base/common/async';
import * as nls from 'vs/nls';
import {
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto
@@ -16,17 +17,19 @@ import {
import * as vscode from 'vscode';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint } from 'vs/workbench/api/node/extHostTypes';
import { generateUuid } from 'vs/base/common/uuid';
import { DebugAdapter, convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/node/debugAdapter';
import { DebugAdapter, StreamDebugAdapter, SocketDebugAdapter } 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 } from 'vs/workbench/parts/debug/common/debug';
import { getTerminalLauncher } from 'vs/workbench/parts/debug/node/terminals';
import { IAdapterExecutable, 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 { VariableResolver } from 'vs/workbench/services/configurationResolver/node/variableResolver';
import { IConfigurationResolverService } from '../../services/configurationResolver/common/configurationResolver';
import { IStringDictionary } from 'vs/base/common/collections';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver';
import { ExtHostConfiguration } from './extHostConfiguration';
import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/common/debugUtils';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
@@ -60,16 +63,20 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private readonly _onDidChangeBreakpoints: Emitter<vscode.BreakpointsChangeEvent>;
private _debugAdapters: Map<number, DebugAdapter>;
private _debugAdapters: Map<number, IDebugAdapter>;
private _variableResolver: IConfigurationResolverService;
private _integratedTerminalInstance: vscode.Terminal;
private _terminalDisposedListener: IDisposable;
constructor(mainContext: IMainContext,
private _workspace: ExtHostWorkspace,
private _workspaceService: ExtHostWorkspace,
private _extensionService: ExtHostExtensionService,
private _editorsService: ExtHostDocumentsAndEditors,
private _configurationService: ExtHostConfiguration
private _configurationService: ExtHostConfiguration,
private _terminalService: ExtHostTerminalService
) {
this._handleCounter = 0;
@@ -116,37 +123,109 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void> {
const terminalLauncher = getTerminalLauncher();
if (terminalLauncher) {
return terminalLauncher.runInTerminal(args, config);
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._workspace, this._editorsService, this._configurationService);
this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, this._configurationService);
}
const folder = <IWorkspaceFolder>this.getFolder(folderUri);
return asWinJsPromise(token => DebugAdapter.substituteVariables(folder, config, this._variableResolver));
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 asWinJsPromise(token => this._variableResolver.resolveAny(ws, config));
}
public $startDASession(handle: number, debugType: string, adpaterExecutable: IAdapterExecutable | null): TPromise<void> {
public $startDASession(handle: number, debugType: string, adpaterExecutable: IAdapterExecutable | null, debugPort: number): TPromise<void> {
const mythis = this;
const da = new class extends DebugAdapter {
let da: StreamDebugAdapter = null;
// 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);
}
if (debugPort > 0) {
da = new class extends SocketDebugAdapter {
}(debugType, adpaterExecutable, this._extensionService.getAllExtensionDescriptions());
// 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));
@@ -346,9 +425,9 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
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({
added: Object.freeze<vscode.Breakpoint[]>(added),
removed: Object.freeze<vscode.Breakpoint[]>(removed),
changed: Object.freeze<vscode.Breakpoint[]>(changed)
added,
removed,
changed,
}));
}
}
@@ -458,14 +537,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._onDidReceiveDebugSessionCustomEvent.fire(ee);
}
private getFolder(_folderUri: UriComponents | undefined) {
private getFolder(_folderUri: UriComponents | undefined): vscode.WorkspaceFolder | undefined {
if (_folderUri) {
const folderUriString = URI.revive(_folderUri).toString();
const folders = this._workspace.getWorkspaceFolders();
const found = folders.filter(f => f.uri.toString() === folderUriString);
if (found && found.length > 0) {
return found[0];
}
const folderURI = URI.revive(_folderUri);
return this._workspaceService.resolveWorkspaceFolder(folderURI);
}
return undefined;
}
@@ -525,5 +600,16 @@ export class ExtHostDebugConsole implements vscode.DebugConsole {
}
}
export class ExtHostVariableResolverService implements IConfigurationResolverService {
constructor(workspace: ExtHostWorkspace, editors: ExtHostDocumentsAndEditors, configuration: ExtHostConfiguration) {
this._variableResolver = new VariableResolver({
const folders = workspace.getWorkspaceFolders();
return workspace.getWorkspaceFolders().length;
return configuration.getConfiguration(undefined, folderUri).get<string>(section);
const activeEditor = editors.activeEditor();
const activeEditor = editors.activeEditor();
const activeEditor = editors.activeEditor();
}
}
// {{SQL CARBON EDIT}}
*/

View File

@@ -17,17 +17,19 @@ import { keys } from 'vs/base/common/map';
export class DiagnosticCollection implements vscode.DiagnosticCollection {
private static readonly _maxDiagnosticsPerFile: number = 250;
private readonly _name: string;
private readonly _owner: string;
private readonly _maxDiagnosticsPerFile: number;
private readonly _onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>;
private readonly _proxy: MainThreadDiagnosticsShape;
private _proxy: MainThreadDiagnosticsShape;
private _isDisposed = false;
private _data = new Map<string, vscode.Diagnostic[]>();
constructor(name: string, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
this._name = name;
this._owner = owner;
this._maxDiagnosticsPerFile = maxDiagnosticsPerFile;
this._proxy = proxy;
this._onDidChangeDiagnostics = onDidChangeDiagnostics;
}
@@ -35,8 +37,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
dispose(): void {
if (!this._isDisposed) {
this._onDidChangeDiagnostics.fire(keys(this._data));
this._proxy.$clear(this.name);
this._proxy = undefined;
this._proxy.$clear(this._owner);
this._data = undefined;
this._isDisposed = true;
}
@@ -109,15 +110,15 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
let diagnostics = this._data.get(uri.toString());
if (diagnostics) {
// no more than 250 diagnostics per file
if (diagnostics.length > DiagnosticCollection._maxDiagnosticsPerFile) {
// no more than N diagnostics per file
if (diagnostics.length > this._maxDiagnosticsPerFile) {
marker = [];
const order = [DiagnosticSeverity.Error, DiagnosticSeverity.Warning, DiagnosticSeverity.Information, DiagnosticSeverity.Hint];
orderLoop: for (let i = 0; i < 4; i++) {
for (let diagnostic of diagnostics) {
if (diagnostic.severity === order[i]) {
const len = marker.push(converter.fromDiagnostic(diagnostic));
if (len === DiagnosticCollection._maxDiagnosticsPerFile) {
const len = marker.push(converter.Diagnostic.from(diagnostic));
if (len === this._maxDiagnosticsPerFile) {
break orderLoop;
}
}
@@ -126,15 +127,15 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
// add 'signal' marker for showing omitted errors/warnings
marker.push({
severity: MarkerSeverity.Error,
message: localize({ key: 'limitHit', comment: ['amount of errors/warning skipped due to limits'] }, "Not showing {0} further errors and warnings.", diagnostics.length - DiagnosticCollection._maxDiagnosticsPerFile),
severity: MarkerSeverity.Info,
message: localize({ key: 'limitHit', comment: ['amount of errors/warning skipped due to limits'] }, "Not showing {0} further errors and warnings.", diagnostics.length - this._maxDiagnosticsPerFile),
startLineNumber: marker[marker.length - 1].startLineNumber,
startColumn: marker[marker.length - 1].startColumn,
endLineNumber: marker[marker.length - 1].endLineNumber,
endColumn: marker[marker.length - 1].endColumn
});
} else {
marker = diagnostics.map(converter.fromDiagnostic);
marker = diagnostics.map(converter.Diagnostic.from);
}
}
@@ -142,20 +143,21 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
}
this._onDidChangeDiagnostics.fire(toSync);
this._proxy.$changeMany(this.name, entries);
this._proxy.$changeMany(this._owner, entries);
}
delete(uri: vscode.Uri): void {
this._checkDisposed();
this._onDidChangeDiagnostics.fire([uri]);
this._data.delete(uri.toString());
this._proxy.$changeMany(this.name, [[uri, undefined]]);
this._proxy.$changeMany(this._owner, [[uri, undefined]]);
}
clear(): void {
this._checkDisposed();
this._onDidChangeDiagnostics.fire(keys(this._data));
this._data.clear();
this._proxy.$clear(this.name);
this._proxy.$clear(this._owner);
}
forEach(callback: (uri: URI, diagnostics: vscode.Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void {
@@ -200,9 +202,10 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
private static _idPool: number = 0;
private static readonly _maxDiagnosticsPerFile: number = 1000;
private readonly _proxy: MainThreadDiagnosticsShape;
private readonly _collections: DiagnosticCollection[] = [];
private readonly _collections = new Map<string, DiagnosticCollection>();
private readonly _onDidChangeDiagnostics = new Emitter<(vscode.Uri | string)[]>();
static _debouncer(last: (vscode.Uri | string)[], current: (vscode.Uri | string)[]): (vscode.Uri | string)[] {
@@ -240,22 +243,28 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
}
createDiagnosticCollection(name: string): vscode.DiagnosticCollection {
let { _collections, _proxy, _onDidChangeDiagnostics } = this;
let owner: string;
if (!name) {
name = '_generated_diagnostic_collection_name_#' + ExtHostDiagnostics._idPool++;
owner = name;
} else if (!_collections.has(name)) {
owner = name;
} else {
console.warn(`DiagnosticCollection with name '${name}' does already exist.`);
do {
owner = name + ExtHostDiagnostics._idPool++;
} while (_collections.has(owner));
}
const { _collections, _proxy, _onDidChangeDiagnostics } = this;
const result = new class extends DiagnosticCollection {
constructor() {
super(name, _proxy, _onDidChangeDiagnostics);
_collections.push(this);
super(name, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, _proxy, _onDidChangeDiagnostics);
_collections.set(owner, this);
}
dispose() {
super.dispose();
let idx = _collections.indexOf(this);
if (idx !== -1) {
_collections.splice(idx, 1);
}
_collections.delete(owner);
}
};
@@ -270,7 +279,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
} else {
let index = new Map<string, number>();
let res: [vscode.Uri, vscode.Diagnostic[]][] = [];
for (const collection of this._collections) {
this._collections.forEach(collection => {
collection.forEach((uri, diagnostics) => {
let idx = index.get(uri.toString());
if (typeof idx === 'undefined') {
@@ -280,18 +289,18 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
}
res[idx][1] = res[idx][1].concat(...diagnostics);
});
}
});
return res;
}
}
private _getDiagnostics(resource: vscode.Uri): vscode.Diagnostic[] {
let res: vscode.Diagnostic[] = [];
for (const collection of this._collections) {
this._collections.forEach(collection => {
if (collection.has(resource)) {
res = res.concat(collection.get(resource));
}
}
});
return res;
}
}

View File

@@ -10,7 +10,7 @@ import { sequence, always } from 'vs/base/common/async';
import { illegalState } from 'vs/base/common/errors';
import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { TextEdit } from 'vs/workbench/api/node/extHostTypes';
import { fromRange, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
@@ -78,32 +78,32 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
return Promise.resolve(false);
}
return this._deliverEventAsync(listener, thisArg, stubEvent).then(() => {
return this._deliverEventAsync(extension, listener, thisArg, stubEvent).then(() => {
// don't send result across the wire
return true;
}, err => {
this._logService.error('[onWillSaveTextDocument]', extension.id);
this._logService.error(`onWillSaveTextDocument-listener from extension '${extension.id}' threw ERROR`);
this._logService.error(err);
if (!(err instanceof Error) || (<Error>err).message !== 'concurrent_edits') {
const errors = this._badListeners.get(listener);
this._badListeners.set(listener, !errors ? 1 : errors + 1);
// todo@joh signal to the listener?
// if (errors === this._thresholds.errors) {
// console.warn('BAD onWillSaveTextDocumentEvent-listener is from now on being ignored');
// }
if (errors > this._thresholds.errors) {
this._logService.info(`onWillSaveTextDocument-listener from extension '${extension.id}' will now be IGNORED because of timeouts and/or errors`);
}
}
return false;
});
}
private _deliverEventAsync(listener: Function, thisArg: any, stubEvent: vscode.TextDocumentWillSaveEvent): Promise<any> {
private _deliverEventAsync(extension: IExtensionDescription, listener: Function, thisArg: any, stubEvent: vscode.TextDocumentWillSaveEvent): Promise<any> {
const promises: Promise<vscode.TextEdit[]>[] = [];
const t1 = Date.now();
const { document, reason } = stubEvent;
const { version } = document;
@@ -133,6 +133,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
const handle = setTimeout(() => reject(new Error('timeout')), this._thresholds.timeout);
return Promise.all(promises).then(edits => {
this._logService.debug(`onWillSaveTextDocument-listener from extension '${extension.id}' finished after ${(Date.now() - t1)}ms`);
clearTimeout(handle);
resolve(edits);
}).catch(err => {
@@ -151,7 +152,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
if (Array.isArray(value) && (<vscode.TextEdit[]>value).every(e => e instanceof TextEdit)) {
for (const { newText, newEol, range } of value) {
resourceEdit.edits.push({
range: range && fromRange(range),
range: range && Range.from(range),
text: newText,
eol: EndOfLine.from(newEol)
});

View File

@@ -136,7 +136,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
document: data.document,
contentChanges: events.changes.map((change) => {
return {
range: TypeConverters.toRange(change.range),
range: TypeConverters.Range.to(change.range),
rangeOffset: change.rangeOffset,
rangeLength: change.rangeLength,
text: change.text

View File

@@ -95,10 +95,10 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
this._mainContext.getProxy(MainContext.MainThreadTextEditors),
data.id,
documentData,
data.selections.map(typeConverters.toSelection),
data.selections.map(typeConverters.Selection.to),
data.options,
data.visibleRanges.map(typeConverters.toRange),
typeConverters.toViewColumn(data.editorPosition)
data.visibleRanges.map(typeConverters.Range.to),
typeConverters.ViewColumn.to(data.editorPosition)
);
this._editors.set(data.id, editor);
}

View File

@@ -15,7 +15,7 @@ import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
// {{SQL CARBON EDIT}}
import { createApiFactory, initializeExtensionApi } from 'sql/workbench/api/node/sqlExtHost.api.impl';
import { checkProposedApiEnabled } from 'vs/workbench/api/node/extHost.api.impl';
import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IExtHostContext } from './extHost.protocol';
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 { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
@@ -70,7 +70,7 @@ class ExtensionStoragePath {
private readonly _workspace: IWorkspaceData;
private readonly _environment: IEnvironment;
private readonly _ready: TPromise<string>;
private readonly _ready: Promise<string>;
private _value: string;
constructor(workspace: IWorkspaceData, environment: IEnvironment) {
@@ -79,7 +79,7 @@ class ExtensionStoragePath {
this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
}
get whenReady(): TPromise<any> {
get whenReady(): Promise<any> {
return this._ready;
}
@@ -90,7 +90,7 @@ class ExtensionStoragePath {
return undefined;
}
private async _getOrCreateWorkspaceStoragePath(): TPromise<string> {
private async _getOrCreateWorkspaceStoragePath(): Promise<string> {
if (!this._workspace) {
return TPromise.as(undefined);
}
@@ -138,7 +138,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
*/
constructor(initData: IInitData,
extHostContext: IExtHostContext,
extHostContext: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extHostLogService: ExtHostLogService
@@ -245,8 +245,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
if (!ext.main) {
return undefined;
}
return realpath(ext.extensionFolderPath).then(value => tree.set(value, ext));
return realpath(ext.extensionLocation.fsPath).then(value => tree.set(value, ext));
});
this._extensionPathIndex = TPromise.join(extensions).then(() => tree);
}
@@ -361,9 +360,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
globalState,
workspaceState,
subscriptions: [],
get extensionPath() { return extensionDescription.extensionFolderPath; },
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
storagePath: this._storagePath.value(extensionDescription),
asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionFolderPath, relativePath); },
asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionLocation.fsPath, relativePath); },
get logger() {
checkProposedApiEnabled(extensionDescription);
return that._extHostLogService.getExtLogger(extensionDescription.id);

View File

@@ -6,15 +6,13 @@
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { Event, mapEvent } from 'vs/base/common/event';
import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape, IFileChangeDto } from './extHost.protocol';
import * as vscode from 'vscode';
import * as files from 'vs/platform/files/common/files';
import * as path from 'path';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { asWinJsPromise } from 'vs/base/common/async';
import { values } from 'vs/base/common/map';
import { Range, DeprecatedFileType, DeprecatedFileChangeType, FileChangeType } from 'vs/workbench/api/node/extHostTypes';
import { Range, FileChangeType } from 'vs/workbench/api/node/extHostTypes';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { Schemas } from 'vs/base/common/network';
@@ -34,7 +32,7 @@ class FsLinkProvider implements vscode.DocumentLinkProvider {
}
}
provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.DocumentLink[]> {
provideDocumentLinks(document: vscode.TextDocument): vscode.ProviderResult<vscode.DocumentLink[]> {
if (this._schemes.size === 0) {
return undefined;
}
@@ -49,6 +47,9 @@ class FsLinkProvider implements vscode.DocumentLinkProvider {
let m: RegExpMatchArray;
while (m = this._regex.exec(textLine.text)) {
const target = URI.parse(m[0]);
if (target.path[0] !== '/') {
continue;
}
const range = new Range(line, this._regex.lastIndex - m[0].length, line, this._regex.lastIndex);
result.push({ target, range });
}
@@ -57,106 +58,6 @@ class FsLinkProvider implements vscode.DocumentLinkProvider {
}
}
class FileSystemProviderShim implements vscode.FileSystemProvider {
onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]>;
constructor(private readonly _delegate: vscode.DeprecatedFileSystemProvider) {
if (!this._delegate.onDidChange) {
this.onDidChangeFile = Event.None;
} else {
this.onDidChangeFile = mapEvent(this._delegate.onDidChange, old => old.map(FileSystemProviderShim._modernizeFileChange));
}
}
watch(uri: vscode.Uri, options: {}): vscode.Disposable {
// does nothing because in the old API there was no notion of
// watch and provider decide what file events to generate...
return { dispose() { } };
}
stat(resource: vscode.Uri): Thenable<vscode.FileStat> {
return this._delegate.stat(resource).then(stat => FileSystemProviderShim._modernizeFileStat(stat));
}
rename(oldUri: vscode.Uri, newUri: vscode.Uri): Thenable<void> {
return this._delegate.move(oldUri, newUri).then(stat => void 0);
}
readDirectory(resource: vscode.Uri): Thenable<[string, vscode.FileType][]> {
return this._delegate.readdir(resource).then(tuples => {
return tuples.map(tuple => <[string, vscode.FileType]>[path.posix.basename(tuple[0].path), FileSystemProviderShim._modernizeFileStat(tuple[1]).type]);
});
}
private static _modernizeFileStat(stat: vscode.DeprecatedFileStat): vscode.FileStat {
let { mtime, size, type } = stat;
let newType: files.FileType;
// no support for bitmask, effectively no support for symlinks
switch (type) {
case DeprecatedFileType.Dir:
newType = files.FileType.Directory;
break;
case DeprecatedFileType.File:
newType = files.FileType.File;
break;
case DeprecatedFileType.Symlink:
newType = files.FileType.File & files.FileType.SymbolicLink;
break;
}
return { type: newType, ctime: 0, mtime, size };
}
private static _modernizeFileChange(e: vscode.DeprecatedFileChange): vscode.FileChangeEvent {
let { resource, type } = e;
let newType: vscode.FileChangeType;
switch (type) {
case DeprecatedFileChangeType.Updated:
newType = FileChangeType.Changed;
break;
case DeprecatedFileChangeType.Added:
newType = FileChangeType.Created;
break;
case DeprecatedFileChangeType.Deleted:
newType = FileChangeType.Deleted;
break;
}
return { uri: resource, type: newType };
}
// --- delete/create file or folder
delete(resource: vscode.Uri): Thenable<void> {
return this._delegate.stat(resource).then(stat => {
if (stat.type === DeprecatedFileType.Dir) {
return this._delegate.rmdir(resource);
} else {
return this._delegate.unlink(resource);
}
});
}
createDirectory(resource: vscode.Uri): Thenable<void> {
return this._delegate.mkdir(resource).then(stat => void 0);
}
// --- read/write
readFile(resource: vscode.Uri): Thenable<Uint8Array> {
let chunks: Buffer[] = [];
return this._delegate.read(resource, 0, -1, {
report(data) {
chunks.push(Buffer.from(data));
}
}).then(() => {
return Buffer.concat(chunks);
});
}
writeFile(resource: vscode.Uri, content: Uint8Array, options: files.FileWriteOptions): Thenable<void> {
return this._delegate.write(resource, content);
}
}
export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _proxy: MainThreadFileSystemShape;
@@ -182,11 +83,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
extHostLanguageFeatures.registerDocumentLinkProvider('*', this._linkProvider);
}
registerDeprecatedFileSystemProvider(scheme: string, provider: vscode.DeprecatedFileSystemProvider) {
return this.registerFileSystemProvider(scheme, new FileSystemProviderShim(provider), { isCaseSensitive: false });
}
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean }) {
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) {
if (this._usedSchemes.has(scheme)) {
throw new Error(`a provider for the scheme '${scheme}' is already registered`);
@@ -201,6 +98,9 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
if (options.isCaseSensitive) {
capabilites += files.FileSystemProviderCapabilities.PathCaseSensitive;
}
if (options.isReadonly) {
capabilites += files.FileSystemProviderCapabilities.Readonly;
}
if (typeof provider.copy === 'function') {
capabilites += files.FileSystemProviderCapabilities.FileFolderCopy;
}
@@ -232,15 +132,13 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
this._proxy.$onFileSystemChange(handle, mapped);
});
return {
dispose: () => {
subscription.dispose();
this._linkProvider.delete(scheme);
this._usedSchemes.delete(scheme);
this._fsProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
}
};
return toDisposable(() => {
subscription.dispose();
this._linkProvider.delete(scheme);
this._usedSchemes.delete(scheme);
this._fsProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
private static _asIStat(stat: vscode.FileStat): files.IStat {
@@ -249,15 +147,15 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$stat(handle: number, resource: UriComponents): TPromise<files.IStat, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
return asWinJsPromise(() => this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
}
$readdir(handle: number, resource: UriComponents): TPromise<[string, files.FileType][], any> {
return asWinJsPromise(token => this._fsProvider.get(handle).readDirectory(URI.revive(resource)));
return asWinJsPromise(() => this._fsProvider.get(handle).readDirectory(URI.revive(resource)));
}
$readFile(handle: number, resource: UriComponents): TPromise<string> {
return asWinJsPromise(token => {
return asWinJsPromise(() => {
return this._fsProvider.get(handle).readFile(URI.revive(resource));
}).then(data => {
return Buffer.isBuffer(data) ? data.toString('base64') : Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('base64');
@@ -265,33 +163,33 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$writeFile(handle: number, resource: UriComponents, base64Content: string, opts: files.FileWriteOptions): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).writeFile(URI.revive(resource), Buffer.from(base64Content, 'base64'), opts));
return asWinJsPromise(() => this._fsProvider.get(handle).writeFile(URI.revive(resource), Buffer.from(base64Content, 'base64'), opts));
}
$delete(handle: number, resource: UriComponents): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).delete(URI.revive(resource), { recursive: true }));
$delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): TPromise<void, any> {
return asWinJsPromise(() => this._fsProvider.get(handle).delete(URI.revive(resource), opts));
}
$rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts));
return asWinJsPromise(() => this._fsProvider.get(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts));
}
$copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts));
return asWinJsPromise(() => this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts));
}
$mkdir(handle: number, resource: UriComponents): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).createDirectory(URI.revive(resource)));
return asWinJsPromise(() => this._fsProvider.get(handle).createDirectory(URI.revive(resource)));
}
$watch(handle: number, session: number, resource: UriComponents, opts: files.IWatchOptions): void {
asWinJsPromise(token => {
asWinJsPromise(() => {
let subscription = this._fsProvider.get(handle).watch(URI.revive(resource), opts);
this._watches.set(session, subscription);
});
}
$unwatch(handle: number, session: number): void {
$unwatch(session: number): void {
let subscription = this._watches.get(session);
if (subscription) {
subscription.dispose();

View File

@@ -4,18 +4,23 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from './extHostTypes';
import { parse, IRelativePattern } from 'vs/base/common/glob';
import { Uri, FileSystemWatcher as _FileSystemWatcher } from 'vscode';
import { FileSystemEvents, ExtHostFileSystemEventServiceShape } from './extHost.protocol';
import URI from 'vs/base/common/uri';
import { flatten } from 'vs/base/common/arrays';
import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event';
import { IRelativePattern, parse } from 'vs/base/common/glob';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as vscode from 'vscode';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, ResourceFileEditDto, ResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol';
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
class FileSystemWatcher implements _FileSystemWatcher {
class FileSystemWatcher implements vscode.FileSystemWatcher {
private _onDidCreate = new Emitter<Uri>();
private _onDidChange = new Emitter<Uri>();
private _onDidDelete = new Emitter<Uri>();
private _onDidCreate = new Emitter<vscode.Uri>();
private _onDidChange = new Emitter<vscode.Uri>();
private _onDidDelete = new Emitter<vscode.Uri>();
private _disposable: Disposable;
private _config: number;
@@ -80,31 +85,100 @@ class FileSystemWatcher implements _FileSystemWatcher {
this._disposable.dispose();
}
get onDidCreate(): Event<Uri> {
get onDidCreate(): Event<vscode.Uri> {
return this._onDidCreate.event;
}
get onDidChange(): Event<Uri> {
get onDidChange(): Event<vscode.Uri> {
return this._onDidChange.event;
}
get onDidDelete(): Event<Uri> {
get onDidDelete(): Event<vscode.Uri> {
return this._onDidDelete.event;
}
}
interface WillRenameListener {
extension: IExtensionDescription;
(e: vscode.FileWillRenameEvent): any;
}
export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape {
private _emitter = new Emitter<FileSystemEvents>();
private readonly _onFileEvent = new Emitter<FileSystemEvents>();
private readonly _onDidRenameFile = new Emitter<vscode.FileRenameEvent>();
private readonly _onWillRenameFile = new AsyncEmitter<vscode.FileWillRenameEvent>();
constructor() {
readonly onDidRenameFile: Event<vscode.FileRenameEvent> = this._onDidRenameFile.event;
constructor(
mainContext: IMainContext,
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = mainContext.getProxy(MainContext.MainThreadTextEditors)
) {
//
}
public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): _FileSystemWatcher {
return new FileSystemWatcher(this._emitter.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher {
return new FileSystemWatcher(this._onFileEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
}
$onFileEvent(events: FileSystemEvents) {
this._emitter.fire(events);
this._onFileEvent.fire(events);
}
$onFileRename(oldUri: UriComponents, newUri: UriComponents) {
this._onDidRenameFile.fire(Object.freeze({ oldUri: URI.revive(oldUri), newUri: URI.revive(newUri) }));
}
getOnWillRenameFileEvent(extension: IExtensionDescription): Event<vscode.FileWillRenameEvent> {
return (listener, thisArg, disposables) => {
let wrappedListener = <WillRenameListener><any>function () {
listener.apply(thisArg, arguments);
};
wrappedListener.extension = extension;
return this._onWillRenameFile.event(wrappedListener, undefined, disposables);
};
}
$onWillRename(oldUriDto: UriComponents, newUriDto: UriComponents): TPromise<any> {
const oldUri = URI.revive(oldUriDto);
const newUri = URI.revive(newUriDto);
const edits: WorkspaceEdit[] = [];
return TPromise.wrap(this._onWillRenameFile.fireAsync((bucket, listener) => {
return {
oldUri,
newUri,
waitUntil: (thenable: Thenable<vscode.WorkspaceEdit>): void => {
if (Object.isFrozen(bucket)) {
throw new TypeError('waitUntil cannot be called async');
}
const index = bucket.length;
const wrappedThenable = TPromise.as(thenable).then(result => {
// ignore all results except for WorkspaceEdits. Those
// are stored in a spare array
if (result instanceof WorkspaceEdit) {
edits[index] = result;
}
});
bucket.push(wrappedThenable);
}
};
}).then(() => {
if (edits.length === 0) {
return undefined;
}
// flatten all WorkspaceEdits collected via waitUntil-call
// and apply them in one go.
let allEdits = new Array<(ResourceFileEditDto | ResourceTextEditDto)[]>();
for (let edit of edits) {
if (edit) { // sparse array
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
allEdits.push(edits);
}
}
return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) });
}));
}
}

View File

@@ -8,8 +8,8 @@ import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { mixin } from 'vs/base/common/objects';
import * as vscode from 'vscode';
import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, HierarchicalSymbolInformation } from 'vs/workbench/api/node/extHostTypes';
import * as typeConvert from 'vs/workbench/api/node/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol } from 'vs/workbench/api/node/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
@@ -17,12 +17,15 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto } from './extHost.protocol';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { isObject } from 'vs/base/common/types';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
// --- adapter
@@ -36,35 +39,71 @@ class OutlineAdapter {
this._provider = provider;
}
provideDocumentSymbols(resource: URI): TPromise<SymbolInformationDto[]> {
provideDocumentSymbols(resource: URI): TPromise<modes.DocumentSymbol[]> {
let doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideDocumentSymbols(doc, token)).then(value => {
if (value instanceof HierarchicalSymbolInformation) {
value = HierarchicalSymbolInformation.toFlatSymbolInformation(value);
if (isFalsyOrEmpty(value)) {
return undefined;
}
if (Array.isArray(value)) {
return value.map(symbol => IdObject.mixin(TypeConverters.fromSymbolInformation(symbol)));
if (value[0] instanceof DocumentSymbol) {
return (<DocumentSymbol[]>value).map(typeConvert.DocumentSymbol.from);
} else {
return OutlineAdapter._asDocumentSymbolTree(resource, <SymbolInformation[]>value);
}
return undefined;
});
}
private static _asDocumentSymbolTree(resource: URI, info: SymbolInformation[]): modes.DocumentSymbol[] {
// first sort by start (and end) and then loop over all elements
// and build a tree based on containment.
info = info.slice(0).sort((a, b) => {
let res = a.location.range.start.compareTo(b.location.range.start);
if (res === 0) {
res = b.location.range.end.compareTo(a.location.range.end);
}
return res;
});
let res: modes.DocumentSymbol[] = [];
let parentStack: modes.DocumentSymbol[] = [];
for (let i = 0; i < info.length; i++) {
let element = <modes.DocumentSymbol>{
name: info[i].name,
kind: typeConvert.SymbolKind.from(info[i].kind),
containerName: info[i].containerName,
range: typeConvert.Range.from(info[i].location.range),
selectionRange: typeConvert.Range.from(info[i].location.range),
children: []
};
while (true) {
if (parentStack.length === 0) {
parentStack.push(element);
res.push(element);
break;
}
let parent = parentStack[parentStack.length - 1];
if (EditorRange.containsRange(parent.range, element.range) && !EditorRange.equalsRange(parent.range, element.range)) {
parent.children.push(element);
parentStack.push(element);
break;
}
parentStack.pop();
}
}
return res;
}
}
class CodeLensAdapter {
private static _badCmd: vscode.Command = { command: 'missing', title: '<<MISSING COMMAND>>' };
private _documents: ExtHostDocuments;
private _commands: CommandsConverter;
private _heapService: ExtHostHeapService;
private _provider: vscode.CodeLensProvider;
constructor(documents: ExtHostDocuments, commands: CommandsConverter, heapService: ExtHostHeapService, provider: vscode.CodeLensProvider) {
this._documents = documents;
this._commands = commands;
this._heapService = heapService;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
private readonly _heapService: ExtHostHeapService,
private readonly _provider: vscode.CodeLensProvider
) { }
provideCodeLenses(resource: URI): TPromise<modes.ICodeLensSymbol[]> {
const doc = this._documents.getDocumentData(resource).document;
@@ -74,7 +113,7 @@ class CodeLensAdapter {
return lenses.map(lens => {
const id = this._heapService.keep(lens);
return ObjectIdentifier.mixin({
range: TypeConverters.fromRange(lens.range),
range: typeConvert.Range.from(lens.range),
command: this._commands.toInternal(lens.command)
}, id);
});
@@ -105,89 +144,68 @@ class CodeLensAdapter {
}
}
class DefinitionAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.DefinitionProvider;
constructor(documents: ExtHostDocuments, provider: vscode.DefinitionProvider) {
this._documents = documents;
this._provider = provider;
function convertToDefinitionLinks(value: vscode.Definition): modes.DefinitionLink[] {
if (Array.isArray(value)) {
return (value as (vscode.DefinitionLink | vscode.Location)[]).map(typeConvert.DefinitionLink.from);
} else if (value) {
return [typeConvert.DefinitionLink.from(value)];
}
return undefined;
}
provideDefinition(resource: URI, position: IPosition): TPromise<modes.Definition> {
class DefinitionAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.DefinitionProvider
) { }
provideDefinition(resource: URI, position: IPosition): TPromise<modes.DefinitionLink[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = TypeConverters.toPosition(position);
return asWinJsPromise(token => this._provider.provideDefinition(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(TypeConverters.location.from);
} else if (value) {
return TypeConverters.location.from(value);
}
return undefined;
});
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideDefinition(doc, pos, token)).then(convertToDefinitionLinks);
}
}
class ImplementationAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.ImplementationProvider;
constructor(documents: ExtHostDocuments, provider: vscode.ImplementationProvider) {
this._documents = documents;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.ImplementationProvider
) { }
provideImplementation(resource: URI, position: IPosition): TPromise<modes.Definition> {
provideImplementation(resource: URI, position: IPosition): TPromise<modes.DefinitionLink[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = TypeConverters.toPosition(position);
return asWinJsPromise(token => this._provider.provideImplementation(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(TypeConverters.location.from);
} else if (value) {
return TypeConverters.location.from(value);
}
return undefined;
});
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideImplementation(doc, pos, token)).then(convertToDefinitionLinks);
}
}
class TypeDefinitionAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.TypeDefinitionProvider;
constructor(documents: ExtHostDocuments, provider: vscode.TypeDefinitionProvider) {
this._documents = documents;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.TypeDefinitionProvider
) { }
provideTypeDefinition(resource: URI, position: IPosition): TPromise<modes.Definition> {
provideTypeDefinition(resource: URI, position: IPosition): TPromise<modes.DefinitionLink[]> {
const doc = this._documents.getDocumentData(resource).document;
const pos = TypeConverters.toPosition(position);
return asWinJsPromise(token => this._provider.provideTypeDefinition(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(TypeConverters.location.from);
} else if (value) {
return TypeConverters.location.from(value);
}
return undefined;
});
const pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideTypeDefinition(doc, pos, token)).then(convertToDefinitionLinks);
}
}
class HoverAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.HoverProvider,
) {
//
}
) { }
public provideHover(resource: URI, position: IPosition): TPromise<modes.Hover> {
let doc = this._documents.getDocumentData(resource).document;
let pos = TypeConverters.toPosition(position);
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideHover(doc, pos, token)).then(value => {
if (!value || isFalsyOrEmpty(value.contents)) {
@@ -200,59 +218,46 @@ class HoverAdapter {
value.range = new Range(pos, pos);
}
return TypeConverters.fromHover(value);
return typeConvert.Hover.from(value);
});
}
}
class DocumentHighlightAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.DocumentHighlightProvider;
constructor(documents: ExtHostDocuments, provider: vscode.DocumentHighlightProvider) {
this._documents = documents;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.DocumentHighlightProvider
) { }
provideDocumentHighlights(resource: URI, position: IPosition): TPromise<modes.DocumentHighlight[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = TypeConverters.toPosition(position);
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideDocumentHighlights(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(DocumentHighlightAdapter._convertDocumentHighlight);
return value.map(typeConvert.DocumentHighlight.from);
}
return undefined;
});
}
private static _convertDocumentHighlight(documentHighlight: vscode.DocumentHighlight): modes.DocumentHighlight {
return {
range: TypeConverters.fromRange(documentHighlight.range),
kind: documentHighlight.kind
};
}
}
class ReferenceAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.ReferenceProvider;
constructor(documents: ExtHostDocuments, provider: vscode.ReferenceProvider) {
this._documents = documents;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.ReferenceProvider
) { }
provideReferences(resource: URI, position: IPosition, context: modes.ReferenceContext): TPromise<modes.Location[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = TypeConverters.toPosition(position);
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideReferences(doc, pos, context, token)).then(value => {
if (Array.isArray(value)) {
return value.map(TypeConverters.location.from);
return value.map(typeConvert.location.from);
}
return undefined;
});
@@ -265,27 +270,25 @@ export interface CustomCodeAction extends CodeActionDto {
class CodeActionAdapter {
private _documents: ExtHostDocuments;
private _commands: CommandsConverter;
private _diagnostics: ExtHostDiagnostics;
private _provider: vscode.CodeActionProvider;
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
private readonly _diagnostics: ExtHostDiagnostics,
private readonly _provider: vscode.CodeActionProvider,
private readonly _logService: ILogService,
private readonly _extensionId: string
) { }
constructor(documents: ExtHostDocuments, commands: CommandsConverter, diagnostics: ExtHostDiagnostics, provider: vscode.CodeActionProvider) {
this._documents = documents;
this._commands = commands;
this._diagnostics = diagnostics;
this._provider = provider;
}
provideCodeActions(resource: URI, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
const doc = this._documents.getDocumentData(resource).document;
const ran = <vscode.Range>TypeConverters.toRange(range);
const ran = Selection.isISelection(rangeOrSelection)
? <vscode.Selection>typeConvert.Selection.to(rangeOrSelection)
: <vscode.Range>typeConvert.Range.to(rangeOrSelection);
const allDiagnostics: vscode.Diagnostic[] = [];
for (const diagnostic of this._diagnostics.getDiagnostics(resource)) {
if (ran.contains(diagnostic.range)) {
if (ran.intersection(diagnostic.range)) {
allDiagnostics.push(diagnostic);
}
}
@@ -294,6 +297,7 @@ class CodeActionAdapter {
diagnostics: allDiagnostics,
only: context.only ? new CodeActionKind(context.only) : undefined
};
return asWinJsPromise(token =>
this._provider.provideCodeActions(doc, ran, codeActionContext, token)
).then(commandsOrActions => {
@@ -313,12 +317,20 @@ class CodeActionAdapter {
command: this._commands.toInternal(candidate),
});
} else {
if (codeActionContext.only) {
if (!candidate.kind) {
this._logService.warn(`${this._extensionId} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
} else if (!codeActionContext.only.contains(candidate.kind)) {
this._logService.warn(`${this._extensionId} -Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
}
}
// new school: convert code action
result.push({
title: candidate.title,
command: candidate.command && this._commands.toInternal(candidate.command),
diagnostics: candidate.diagnostics && candidate.diagnostics.map(TypeConverters.fromDiagnostic),
edit: candidate.edit && TypeConverters.WorkspaceEdit.from(candidate.edit),
diagnostics: candidate.diagnostics && candidate.diagnostics.map(typeConvert.Diagnostic.from),
edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit),
kind: candidate.kind && candidate.kind.value
});
}
@@ -335,13 +347,10 @@ class CodeActionAdapter {
class DocumentFormattingAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.DocumentFormattingEditProvider;
constructor(documents: ExtHostDocuments, provider: vscode.DocumentFormattingEditProvider) {
this._documents = documents;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.DocumentFormattingEditProvider
) { }
provideDocumentFormattingEdits(resource: URI, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
@@ -349,7 +358,7 @@ class DocumentFormattingAdapter {
return asWinJsPromise(token => this._provider.provideDocumentFormattingEdits(document, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(TypeConverters.TextEdit.from);
return value.map(typeConvert.TextEdit.from);
}
return undefined;
});
@@ -358,22 +367,19 @@ class DocumentFormattingAdapter {
class RangeFormattingAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.DocumentRangeFormattingEditProvider;
constructor(documents: ExtHostDocuments, provider: vscode.DocumentRangeFormattingEditProvider) {
this._documents = documents;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.DocumentRangeFormattingEditProvider
) { }
provideDocumentRangeFormattingEdits(resource: URI, range: IRange, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
const { document } = this._documents.getDocumentData(resource);
const ran = TypeConverters.toRange(range);
const ran = typeConvert.Range.to(range);
return asWinJsPromise(token => this._provider.provideDocumentRangeFormattingEdits(document, ran, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(TypeConverters.TextEdit.from);
return value.map(typeConvert.TextEdit.from);
}
return undefined;
});
@@ -382,24 +388,21 @@ class RangeFormattingAdapter {
class OnTypeFormattingAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.OnTypeFormattingEditProvider;
constructor(documents: ExtHostDocuments, provider: vscode.OnTypeFormattingEditProvider) {
this._documents = documents;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.OnTypeFormattingEditProvider
) { }
autoFormatTriggerCharacters: string[] = []; // not here
provideOnTypeFormattingEdits(resource: URI, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
const { document } = this._documents.getDocumentData(resource);
const pos = TypeConverters.toPosition(position);
const pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideOnTypeFormattingEdits(document, pos, ch, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(TypeConverters.TextEdit.from);
return value.map(typeConvert.TextEdit.from);
}
return undefined;
});
@@ -429,7 +432,7 @@ class NavigateTypeAdapter {
console.warn('INVALID SymbolInformation, lacks name', item);
continue;
}
const symbol = IdObject.mixin(TypeConverters.fromSymbolInformation(item));
const symbol = IdObject.mixin(typeConvert.WorkspaceSymbol.from(item));
this._symbolCache[symbol._id] = item;
result.symbols.push(symbol);
}
@@ -442,7 +445,7 @@ class NavigateTypeAdapter {
});
}
resolveWorkspaceSymbol(symbol: SymbolInformationDto): TPromise<SymbolInformationDto> {
resolveWorkspaceSymbol(symbol: WorkspaceSymbolDto): TPromise<WorkspaceSymbolDto> {
if (typeof this._provider.resolveWorkspaceSymbol !== 'function') {
return TPromise.as(symbol);
@@ -451,7 +454,7 @@ class NavigateTypeAdapter {
const item = this._symbolCache[symbol._id];
if (item) {
return asWinJsPromise(token => this._provider.resolveWorkspaceSymbol(item, token)).then(value => {
return value && mixin(symbol, TypeConverters.fromSymbolInformation(value), true);
return value && mixin(symbol, typeConvert.WorkspaceSymbol.from(value), true);
});
}
return undefined;
@@ -474,38 +477,35 @@ class RenameAdapter {
return typeof provider.prepareRename === 'function';
}
private _documents: ExtHostDocuments;
private _provider: vscode.RenameProvider;
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.RenameProvider
) { }
constructor(documents: ExtHostDocuments, provider: vscode.RenameProvider) {
this._documents = documents;
this._provider = provider;
}
provideRenameEdits(resource: URI, position: IPosition, newName: string): TPromise<modes.WorkspaceEdit> {
provideRenameEdits(resource: URI, position: IPosition, newName: string): TPromise<WorkspaceEditDto> {
let doc = this._documents.getDocumentData(resource).document;
let pos = TypeConverters.toPosition(position);
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideRenameEdits(doc, pos, newName, token)).then(value => {
if (!value) {
return undefined;
}
return TypeConverters.WorkspaceEdit.from(value);
return typeConvert.WorkspaceEdit.from(value);
}, err => {
if (typeof err === 'string') {
return <modes.WorkspaceEdit>{
return <WorkspaceEditDto>{
edits: undefined,
rejectReason: err
};
} else if (err instanceof Error && typeof err.message === 'string') {
return <modes.WorkspaceEdit>{
return <WorkspaceEditDto>{
edits: undefined,
rejectReason: err.message
};
} else {
// generic error
return TPromise.wrapError<modes.WorkspaceEdit>(err);
return TPromise.wrapError<WorkspaceEditDto>(err);
}
});
}
@@ -516,7 +516,7 @@ class RenameAdapter {
}
let doc = this._documents.getDocumentData(resource).document;
let pos = TypeConverters.toPosition(position);
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.prepareRename(doc, pos, token)).then(rangeOrLocation => {
@@ -539,7 +539,7 @@ class RenameAdapter {
console.warn('INVALID rename location: range must contain position');
return undefined;
}
return { range: TypeConverters.fromRange(range), text };
return { range: typeConvert.Range.from(range), text };
});
}
}
@@ -566,10 +566,10 @@ class SuggestAdapter {
provideCompletionItems(resource: URI, position: IPosition, context: modes.SuggestContext): TPromise<SuggestResultDto> {
const doc = this._documents.getDocumentData(resource).document;
const pos = TypeConverters.toPosition(position);
const pos = typeConvert.Position.to(position);
return asWinJsPromise<vscode.CompletionItem[] | vscode.CompletionList>(token => {
return this._provider.provideCompletionItems(doc, pos, token, TypeConverters.CompletionContext.from(context));
return this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.from(context));
}).then(value => {
const _id = this._idPool++;
@@ -629,7 +629,7 @@ class SuggestAdapter {
}
const doc = this._documents.getDocumentData(resource).document;
const pos = TypeConverters.toPosition(position);
const pos = typeConvert.Position.to(position);
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos)).with({ end: pos });
const newSuggestion = this._convertCompletionItem(resolvedItem, pos, wordRangeBeforePos, _id, _parentId);
if (newSuggestion) {
@@ -656,14 +656,15 @@ class SuggestAdapter {
_parentId,
//
label: item.label,
type: TypeConverters.CompletionItemKind.from(item.kind),
type: typeConvert.CompletionItemKind.from(item.kind),
detail: item.detail,
documentation: item.documentation,
filterText: item.filterText,
sortText: item.sortText,
preselect: item.preselect,
//
insertText: undefined,
additionalTextEdits: item.additionalTextEdits && item.additionalTextEdits.map(TypeConverters.TextEdit.from),
additionalTextEdits: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
command: this._commands.toInternal(item.command),
commitCharacters: item.commitCharacters
};
@@ -709,22 +710,19 @@ class SuggestAdapter {
class SignatureHelpAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.SignatureHelpProvider;
constructor(documents: ExtHostDocuments, provider: vscode.SignatureHelpProvider) {
this._documents = documents;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.SignatureHelpProvider
) { }
provideSignatureHelp(resource: URI, position: IPosition): TPromise<modes.SignatureHelp> {
const doc = this._documents.getDocumentData(resource).document;
const pos = TypeConverters.toPosition(position);
const pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideSignatureHelp(doc, pos, token)).then(value => {
if (value) {
return TypeConverters.SignatureHelp.from(value);
return typeConvert.SignatureHelp.from(value);
}
return undefined;
});
@@ -733,15 +731,11 @@ class SignatureHelpAdapter {
class LinkProviderAdapter {
private _documents: ExtHostDocuments;
private _heapService: ExtHostHeapService;
private _provider: vscode.DocumentLinkProvider;
constructor(documents: ExtHostDocuments, heapService: ExtHostHeapService, provider: vscode.DocumentLinkProvider) {
this._documents = documents;
this._heapService = heapService;
this._provider = provider;
}
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _heapService: ExtHostHeapService,
private readonly _provider: vscode.DocumentLinkProvider
) { }
provideLinks(resource: URI): TPromise<modes.ILink[]> {
const doc = this._documents.getDocumentData(resource).document;
@@ -752,7 +746,7 @@ class LinkProviderAdapter {
}
const result: modes.ILink[] = [];
for (const link of links) {
let data = TypeConverters.DocumentLink.from(link);
let data = typeConvert.DocumentLink.from(link);
let id = this._heapService.keep(link);
ObjectIdentifier.mixin(data, id);
result.push(data);
@@ -774,7 +768,7 @@ class LinkProviderAdapter {
return asWinJsPromise(token => this._provider.resolveDocumentLink(item, token)).then(value => {
if (value) {
return TypeConverters.DocumentLink.from(value);
return typeConvert.DocumentLink.from(value);
}
return undefined;
});
@@ -797,8 +791,8 @@ class ColorProviderAdapter {
const colorInfos: IRawColorInfo[] = colors.map(ci => {
return {
color: TypeConverters.Color.from(ci.color),
range: TypeConverters.fromRange(ci.range)
color: typeConvert.Color.from(ci.color),
range: typeConvert.Range.from(ci.range)
};
});
@@ -808,10 +802,10 @@ class ColorProviderAdapter {
provideColorPresentations(resource: URI, raw: IRawColorInfo): TPromise<modes.IColorPresentation[]> {
const document = this._documents.getDocumentData(resource).document;
const range = TypeConverters.toRange(raw.range);
const color = TypeConverters.Color.to(raw.color);
const range = typeConvert.Range.to(raw.range);
const color = typeConvert.Color.to(raw.color);
return asWinJsPromise(token => this._provider.provideColorPresentations(color, { document, range }, token)).then(value => {
return value.map(TypeConverters.ColorPresentation.from);
return value.map(typeConvert.ColorPresentation.from);
});
}
}
@@ -829,7 +823,7 @@ class FoldingProviderAdapter {
if (!Array.isArray(ranges)) {
return void 0;
}
return ranges.map(TypeConverters.FoldingRange.from);
return ranges.map(typeConvert.FoldingRange.from);
});
}
}
@@ -855,6 +849,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
private _heapService: ExtHostHeapService;
private _diagnostics: ExtHostDiagnostics;
private _adapter = new Map<number, Adapter>();
private readonly _logService: ILogService;
constructor(
mainContext: IMainContext,
@@ -862,7 +857,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
documents: ExtHostDocuments,
commands: ExtHostCommands,
heapMonitor: ExtHostHeapService,
diagnostics: ExtHostDiagnostics
diagnostics: ExtHostDiagnostics,
logService: ILogService
) {
this._schemeTransformer = schemeTransformer;
this._proxy = mainContext.getProxy(MainContext.MainThreadLanguageFeatures);
@@ -870,6 +866,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
this._commands = commands;
this._heapService = heapMonitor;
this._diagnostics = diagnostics;
this._logService = logService;
}
private _transformDocumentSelector(selector: vscode.DocumentSelector): ISerializedDocumentFilter[] {
@@ -935,13 +932,13 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- outline
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, extension?: IExtensionDescription): vscode.Disposable {
const handle = this._addNewAdapter(new OutlineAdapter(this._documents, provider));
this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector));
this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector), extension ? extension.displayName || extension.name : undefined);
return this._createDisposable(handle);
}
$provideDocumentSymbols(handle: number, resource: UriComponents): TPromise<SymbolInformationDto[]> {
$provideDocumentSymbols(handle: number, resource: UriComponents): TPromise<modes.DocumentSymbol[]> {
return this._withAdapter(handle, OutlineAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource)));
}
@@ -979,7 +976,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Definition> {
$provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.DefinitionLink[]> {
return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position));
}
@@ -989,7 +986,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Definition> {
$provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.DefinitionLink[]> {
return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position));
}
@@ -999,7 +996,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Definition> {
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.DefinitionLink[]> {
return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position));
}
@@ -1041,15 +1038,15 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- quick fix
registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider));
registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, extension?: IExtensionDescription, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension ? extension.id : ''));
this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), metadata && metadata.providedCodeActionKinds ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined);
return this._createDisposable(handle);
}
$provideCodeActions(handle: number, resource: UriComponents, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), range, context));
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context));
}
// --- formatting
@@ -1096,7 +1093,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search));
}
$resolveWorkspaceSymbol(handle: number, symbol: SymbolInformationDto): TPromise<SymbolInformationDto> {
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto): TPromise<WorkspaceSymbolDto> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol));
}
@@ -1112,7 +1109,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string): TPromise<modes.WorkspaceEdit> {
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string): TPromise<WorkspaceEditDto> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName));
}

View File

@@ -8,10 +8,11 @@ import { ProgressOptions } from 'vscode';
import { MainThreadProgressShape, ExtHostProgressShape } from './extHost.protocol';
import { ProgressLocation } from './extHostTypeConverters';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IProgressStep, Progress } from 'vs/platform/progress/common/progress';
import { Progress } from 'vs/platform/progress/common/progress';
import { localize } from 'vs/nls';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { debounce } from 'vs/base/common/decorators';
import { IProgressStep } from 'vs/workbench/services/progress/common/progress';
export class ExtHostProgress implements ExtHostProgressShape {
@@ -70,11 +71,14 @@ export class ExtHostProgress implements ExtHostProgressShape {
function mergeProgress(result: IProgressStep, currentValue: IProgressStep): IProgressStep {
result.message = currentValue.message;
if (typeof currentValue.increment === 'number' && typeof result.message === 'number') {
result.increment += currentValue.increment;
} else if (typeof currentValue.increment === 'number') {
result.increment = currentValue.increment;
if (typeof currentValue.increment === 'number') {
if (typeof result.increment === 'number') {
result.increment += currentValue.increment;
} else {
result.increment = currentValue.increment;
}
}
return result;
}

View File

@@ -4,13 +4,17 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { wireCancellationToken, asWinJsPromise } from 'vs/base/common/async';
import { asWinJsPromise, wireCancellationToken } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickPickOptions, QuickPickItem, InputBoxOptions, WorkspaceFolderPickOptions, WorkspaceFolder } from 'vscode';
import { MainContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, IMainContext } from './extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { Emitter } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode';
import { ExtHostQuickOpenShape, IMainContext, MainContext, MainThreadQuickOpenShape, TransferQuickPickItems, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol';
import URI from 'vs/base/common/uri';
import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/node/extHostTypes';
export type Item = string | QuickPickItem;
@@ -23,13 +27,15 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
private _onDidSelectItem: (handle: number) => void;
private _validateInput: (input: string) => string | Thenable<string>;
private _sessions = new Map<number, ExtHostQuickInput>();
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace, commands: ExtHostCommands) {
this._proxy = mainContext.getProxy(MainContext.MainThreadQuickOpen);
this._workspace = workspace;
this._commands = commands;
}
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options: QuickPickOptions & { canSelectMany: true; }, token?: CancellationToken): Thenable<QuickPickItem[] | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Thenable<QuickPickItem[] | undefined>;
showQuickPick(itemsOrItemsPromise: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | Item[] | undefined> {
@@ -40,12 +46,11 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
const itemsPromise = <TPromise<Item[]>>TPromise.wrap(itemsOrItemsPromise);
const quickPickWidget = this._proxy.$show({
autoFocus: { autoFocusFirstEntry: true },
placeHolder: options && options.placeHolder,
matchOnDescription: options && options.matchOnDescription,
matchOnDetail: options && options.matchOnDetail,
ignoreFocusLost: options && options.ignoreFocusOut,
canSelectMany: options && options.canPickMany
canPickMany: options && options.canPickMany
});
const promise = TPromise.any(<TPromise<number | Item[]>[]>[quickPickWidget, itemsPromise]).then(values => {
@@ -55,7 +60,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return itemsPromise.then(items => {
let pickItems: MyQuickPickItems[] = [];
let pickItems: TransferQuickPickItems[] = [];
for (let handle = 0; handle < items.length; handle++) {
let item = items[handle];
@@ -143,4 +148,443 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return this._workspace.getWorkspaceFolders().filter(folder => folder.uri.toString() === selectedFolder.uri.toString())[0];
});
}
// ---- QuickInput
createQuickPick<T extends QuickPickItem>(extensionId: string): QuickPick<T> {
const session = new ExtHostQuickPick(this._proxy, extensionId, () => this._sessions.delete(session._id));
this._sessions.set(session._id, session);
return session;
}
createInputBox(extensionId: string): InputBox {
const session = new ExtHostInputBox(this._proxy, extensionId, () => this._sessions.delete(session._id));
this._sessions.set(session._id, session);
return session;
}
$onDidChangeValue(sessionId: number, value: string): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidChangeValue(value);
}
}
$onDidAccept(sessionId: number): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidAccept();
}
}
$onDidChangeActive(sessionId: number, handles: number[]): void {
const session = this._sessions.get(sessionId);
if (session instanceof ExtHostQuickPick) {
session._fireDidChangeActive(handles);
}
}
$onDidChangeSelection(sessionId: number, handles: number[]): void {
const session = this._sessions.get(sessionId);
if (session instanceof ExtHostQuickPick) {
session._fireDidChangeSelection(handles);
}
}
$onDidTriggerButton(sessionId: number, handle: number): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidTriggerButton(handle);
}
}
$onDidHide(sessionId: number): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidHide();
}
}
}
class ExtHostQuickInput implements QuickInput {
private static _nextId = 1;
_id = ExtHostQuickPick._nextId++;
private _title: string;
private _steps: number;
private _totalSteps: number;
private _visible = false;
private _enabled = true;
private _busy = false;
private _ignoreFocusOut = true;
private _value = '';
private _placeholder: string;
private _buttons: QuickInputButton[] = [];
private _handlesToButtons = new Map<number, QuickInputButton>();
private _onDidAcceptEmitter = new Emitter<void>();
private _onDidChangeValueEmitter = new Emitter<string>();
private _onDidTriggerButtonEmitter = new Emitter<QuickInputButton>();
private _onDidHideEmitter = new Emitter<void>();
private _updateTimeout: number;
private _pendingUpdate: TransferQuickInput = { id: this._id };
private _disposed = false;
protected _disposables: IDisposable[] = [
this._onDidTriggerButtonEmitter,
this._onDidHideEmitter,
this._onDidAcceptEmitter,
this._onDidChangeValueEmitter
];
constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: string, private _onDidDispose: () => void) {
}
get title() {
return this._title;
}
set title(title: string) {
this._title = title;
this.update({ title });
}
get step() {
return this._steps;
}
set step(step: number) {
this._steps = step;
this.update({ step });
}
get totalSteps() {
return this._totalSteps;
}
set totalSteps(totalSteps: number) {
this._totalSteps = totalSteps;
this.update({ totalSteps });
}
get enabled() {
return this._enabled;
}
set enabled(enabled: boolean) {
this._enabled = enabled;
this.update({ enabled });
}
get busy() {
return this._busy;
}
set busy(busy: boolean) {
this._busy = busy;
this.update({ busy });
}
get ignoreFocusOut() {
return this._ignoreFocusOut;
}
set ignoreFocusOut(ignoreFocusOut: boolean) {
this._ignoreFocusOut = ignoreFocusOut;
this.update({ ignoreFocusOut });
}
get value() {
return this._value;
}
set value(value: string) {
this._value = value;
this.update({ value });
}
get placeholder() {
return this._placeholder;
}
set placeholder(placeholder: string) {
this._placeholder = placeholder;
this.update({ placeholder });
}
onDidChangeValue = this._onDidChangeValueEmitter.event;
onDidAccept = this._onDidAcceptEmitter.event;
get buttons() {
return this._buttons;
}
set buttons(buttons: QuickInputButton[]) {
this._buttons = buttons.slice();
this._handlesToButtons.clear();
buttons.forEach((button, i) => {
const handle = button === QuickInputButtons.Back ? -1 : i;
this._handlesToButtons.set(handle, button);
});
this.update({
buttons: buttons.map<TransferQuickInputButton>((button, i) => ({
iconPath: getIconUris(button.iconPath),
tooltip: button.tooltip,
handle: button === QuickInputButtons.Back ? -1 : i,
}))
});
}
onDidTriggerButton = this._onDidTriggerButtonEmitter.event;
show(): void {
this._visible = true;
this.update({ visible: true });
}
hide(): void {
this._visible = false;
this.update({ visible: false });
}
onDidHide = this._onDidHideEmitter.event;
_fireDidAccept() {
this._onDidAcceptEmitter.fire();
}
_fireDidChangeValue(value) {
this._value = value;
this._onDidChangeValueEmitter.fire(value);
}
_fireDidTriggerButton(handle: number) {
const button = this._handlesToButtons.get(handle);
this._onDidTriggerButtonEmitter.fire(button);
}
_fireDidHide() {
this._onDidHideEmitter.fire();
}
public dispose(): void {
if (this._disposed) {
return;
}
this._disposed = true;
this._fireDidHide();
this._disposables = dispose(this._disposables);
if (this._updateTimeout) {
clearTimeout(this._updateTimeout);
this._updateTimeout = undefined;
}
this._onDidDispose();
this._proxy.$dispose(this._id);
}
protected update(properties: Record<string, any>): void {
if (this._disposed) {
return;
}
for (const key of Object.keys(properties)) {
const value = properties[key];
this._pendingUpdate[key] = value === undefined ? null : value;
}
if ('visible' in this._pendingUpdate) {
if (this._updateTimeout) {
clearTimeout(this._updateTimeout);
this._updateTimeout = undefined;
}
this.dispatchUpdate();
} else if (this._visible && !this._updateTimeout) {
// Defer the update so that multiple changes to setters dont cause a redraw each
this._updateTimeout = setTimeout(() => {
this._updateTimeout = undefined;
this.dispatchUpdate();
}, 0);
}
}
private dispatchUpdate() {
this._proxy.$createOrUpdate(this._pendingUpdate);
this._pendingUpdate = { id: this._id };
}
}
function getIconUris(iconPath: QuickInputButton['iconPath']) {
const light = getLightIconUri(iconPath);
return { dark: getDarkIconUri(iconPath) || light, light };
}
function getLightIconUri(iconPath: QuickInputButton['iconPath']) {
if (iconPath && !(iconPath instanceof ThemeIcon)) {
if (typeof iconPath === 'string'
|| iconPath instanceof URI) {
return getIconUri(iconPath);
}
return getIconUri(iconPath['light']);
}
return undefined;
}
function getDarkIconUri(iconPath: QuickInputButton['iconPath']) {
if (iconPath && !(iconPath instanceof ThemeIcon) && iconPath['dark']) {
return getIconUri(iconPath['dark']);
}
return undefined;
}
function getIconUri(iconPath: string | URI) {
if (iconPath instanceof URI) {
return iconPath;
}
return URI.file(iconPath);
}
class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implements QuickPick<T> {
private _items: T[] = [];
private _handlesToItems = new Map<number, T>();
private _itemsToHandles = new Map<T, number>();
private _canSelectMany = false;
private _matchOnDescription = true;
private _matchOnDetail = true;
private _activeItems: T[] = [];
private _onDidChangeActiveEmitter = new Emitter<T[]>();
private _selectedItems: T[] = [];
private _onDidChangeSelectionEmitter = new Emitter<T[]>();
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, onDispose: () => void) {
super(proxy, extensionId, onDispose);
this._disposables.push(
this._onDidChangeActiveEmitter,
this._onDidChangeSelectionEmitter,
);
this.update({ type: 'quickPick' });
}
get items() {
return this._items;
}
set items(items: T[]) {
this._items = items.slice();
this._handlesToItems.clear();
this._itemsToHandles.clear();
items.forEach((item, i) => {
this._handlesToItems.set(i, item);
this._itemsToHandles.set(item, i);
});
this.update({
items: items.map((item, i) => ({
label: item.label,
description: item.description,
handle: i,
detail: item.detail,
picked: item.picked
}))
});
}
get canSelectMany() {
return this._canSelectMany;
}
set canSelectMany(canSelectMany: boolean) {
this._canSelectMany = canSelectMany;
this.update({ canSelectMany });
}
get matchOnDescription() {
return this._matchOnDescription;
}
set matchOnDescription(matchOnDescription: boolean) {
this._matchOnDescription = matchOnDescription;
this.update({ matchOnDescription });
}
get matchOnDetail() {
return this._matchOnDetail;
}
set matchOnDetail(matchOnDetail: boolean) {
this._matchOnDetail = matchOnDetail;
this.update({ matchOnDetail });
}
get activeItems() {
return this._activeItems;
}
set activeItems(activeItems: T[]) {
this._activeItems = activeItems.filter(item => this._itemsToHandles.has(item));
this.update({ activeItems: this._activeItems.map(item => this._itemsToHandles.get(item)) });
}
onDidChangeActive = this._onDidChangeActiveEmitter.event;
get selectedItems() {
return this._selectedItems;
}
set selectedItems(selectedItems: T[]) {
this._selectedItems = selectedItems.filter(item => this._itemsToHandles.has(item));
this.update({ selectedItems: this._selectedItems.map(item => this._itemsToHandles.get(item)) });
}
onDidChangeSelection = this._onDidChangeSelectionEmitter.event;
_fireDidChangeActive(handles: number[]) {
const items = handles.map(handle => this._handlesToItems.get(handle));
this._activeItems = items;
this._onDidChangeActiveEmitter.fire(items);
}
_fireDidChangeSelection(handles: number[]) {
const items = handles.map(handle => this._handlesToItems.get(handle));
this._selectedItems = items;
this._onDidChangeSelectionEmitter.fire(items);
}
}
class ExtHostInputBox extends ExtHostQuickInput implements InputBox {
private _password: boolean;
private _prompt: string;
private _validationMessage: string;
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, onDispose: () => void) {
super(proxy, extensionId, onDispose);
this.update({ type: 'inputBox' });
}
get password() {
return this._password;
}
set password(password: boolean) {
this._password = password;
this.update({ password });
}
get prompt() {
return this._prompt;
}
set prompt(prompt: string) {
this._prompt = prompt;
this.update({ prompt });
}
get validationMessage() {
return this._validationMessage;
}
set validationMessage(validationMessage: string) {
this._validationMessage = validationMessage;
this.update({ validationMessage });
}
}

View File

@@ -237,14 +237,14 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
return this._resourceStatesMap.get(handle);
}
async $executeResourceCommand(handle: number): TPromise<void> {
$executeResourceCommand(handle: number): TPromise<void> {
const command = this._resourceStatesCommandsMap.get(handle);
if (!command) {
return;
return TPromise.as(null);
}
await this._commands.executeCommand(command.command, ...command.arguments);
return asWinJsPromise(_ => this._commands.executeCommand(command.command, ...command.arguments));
}
_takeResourceStateSnapshot(): SCMRawResourceSplice[] {
@@ -568,25 +568,25 @@ export class ExtHostSCM implements ExtHostSCMShape {
return TPromise.as(null);
}
async $executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): TPromise<void> {
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): TPromise<void> {
this.logService.trace('ExtHostSCM#$executeResourceCommand', sourceControlHandle, groupHandle, handle);
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl) {
return;
return TPromise.as(null);
}
const group = sourceControl.getResourceGroup(groupHandle);
if (!group) {
return;
return TPromise.as(null);
}
await group.$executeResourceCommand(handle);
return group.$executeResourceCommand(handle);
}
async $validateInput(sourceControlHandle: number, value: string, cursorPosition: number): TPromise<[string, number] | undefined> {
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): TPromise<[string, number] | undefined> {
this.logService.trace('ExtHostSCM#$validateInput', sourceControlHandle);
const sourceControl = this._sourceControls.get(sourceControlHandle);
@@ -599,12 +599,12 @@ export class ExtHostSCM implements ExtHostSCMShape {
return TPromise.as(undefined);
}
const result = await sourceControl.inputBox.validateInput(value, cursorPosition);
return asWinJsPromise(_ => Promise.resolve(sourceControl.inputBox.validateInput(value, cursorPosition))).then(result => {
if (!result) {
return TPromise.as(undefined);
}
if (!result) {
return TPromise.as(undefined);
}
return [result.message, result.type];
return TPromise.as<[string, number]>([result.message, result.type]);
});
}
}

View File

@@ -0,0 +1,659 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import * as arrays from 'vs/base/common/arrays';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import * as glob from 'vs/base/common/glob';
import * as resources from 'vs/base/common/resources';
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 { IFileMatch, IFolderQuery, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search';
import * as vscode from 'vscode';
export interface IInternalFileMatch {
base: URI;
original?: URI;
relativePath?: string; // Not present for extraFiles or absolute path matches
basename: string;
size?: number;
}
/**
* Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns
*/
export function resolvePatternsForProvider(globalPattern: glob.IExpression, folderPattern: glob.IExpression): string[] {
const merged = {
...(globalPattern || {}),
...(folderPattern || {})
};
return Object.keys(merged)
.filter(key => {
const value = merged[key];
return typeof value === 'boolean' && value;
});
}
export class QueryGlobTester {
private _excludeExpression: glob.IExpression;
private _parsedExcludeExpression: glob.ParsedExpression;
private _parsedIncludeExpression: glob.ParsedExpression;
constructor(config: ISearchQuery, folderQuery: IFolderQuery) {
this._excludeExpression = {
...(config.excludePattern || {}),
...(folderQuery.excludePattern || {})
};
this._parsedExcludeExpression = glob.parse(this._excludeExpression);
// Empty includeExpression means include nothing, so no {} shortcuts
let includeExpression: glob.IExpression = config.includePattern;
if (folderQuery.includePattern) {
if (includeExpression) {
includeExpression = {
...includeExpression,
...folderQuery.includePattern
};
} else {
includeExpression = folderQuery.includePattern;
}
}
if (includeExpression) {
this._parsedIncludeExpression = glob.parse(includeExpression);
}
}
/**
* Guaranteed sync - siblingsFn should not return a promise.
*/
public includedInQuerySync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean {
if (this._parsedExcludeExpression && this._parsedExcludeExpression(testPath, basename, hasSibling)) {
return false;
}
if (this._parsedIncludeExpression && !this._parsedIncludeExpression(testPath, basename, hasSibling)) {
return false;
}
return true;
}
/**
* Guaranteed async.
*/
public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise<boolean>): TPromise<boolean> {
const excludeP = this._parsedExcludeExpression ?
TPromise.as(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
TPromise.wrap(false);
return excludeP.then(excluded => {
if (excluded) {
return false;
}
return this._parsedIncludeExpression ?
TPromise.as(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
TPromise.wrap(true);
}).then(included => {
return included;
});
}
public hasSiblingExcludeClauses(): boolean {
return hasSiblingClauses(this._excludeExpression);
}
}
function hasSiblingClauses(pattern: glob.IExpression): boolean {
for (let key in pattern) {
if (typeof pattern[key] !== 'boolean') {
return true;
}
}
return false;
}
export interface IDirectoryEntry {
base: URI;
relativePath: string;
basename: string;
}
export interface IDirectoryTree {
rootEntries: IDirectoryEntry[];
pathToEntries: { [relativePath: string]: IDirectoryEntry[] };
}
// ???
interface IInternalSearchComplete {
limitHit: boolean;
results: IInternalFileMatch[];
}
export class FileIndexSearchEngine {
private filePattern: string;
private normalizedFilePatternLowercase: string;
private includePattern: glob.ParsedExpression;
private maxResults: number;
private exists: boolean;
private isLimitHit: boolean;
private resultCount: number;
private isCanceled: boolean;
private activeCancellationTokens: Set<CancellationTokenSource>;
private globalExcludePattern: glob.ParsedExpression;
constructor(private config: ISearchQuery, private provider: vscode.FileIndexProvider) {
this.filePattern = config.filePattern;
this.includePattern = config.includePattern && glob.parse(config.includePattern);
this.maxResults = config.maxResults || null;
this.exists = config.exists;
this.resultCount = 0;
this.isLimitHit = false;
this.activeCancellationTokens = new Set<CancellationTokenSource>();
if (this.filePattern) {
this.normalizedFilePatternLowercase = strings.stripWildcards(this.filePattern).toLowerCase();
}
this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern);
}
public cancel(): void {
this.isCanceled = true;
this.activeCancellationTokens.forEach(t => t.cancel());
this.activeCancellationTokens = new Set();
}
public search(_onResult: (match: IInternalFileMatch) => void): TPromise<{ isLimitHit: boolean }> {
if (this.config.folderQueries.length !== 1) {
throw new Error('Searches just one folder');
}
const folderQuery = this.config.folderQueries[0];
return new TPromise<{ isLimitHit: boolean }>((resolve, reject) => {
const onResult = (match: IInternalFileMatch) => {
this.resultCount++;
_onResult(match);
};
if (this.isCanceled) {
return resolve({ isLimitHit: this.isLimitHit });
}
// For each extra file
if (this.config.extraFileResources) {
this.config.extraFileResources
.forEach(extraFile => {
const extraFileStr = extraFile.toString(); // ?
const basename = path.basename(extraFileStr);
if (this.globalExcludePattern && this.globalExcludePattern(extraFileStr, basename)) {
return; // excluded
}
// File: Check for match on file pattern and include pattern
this.matchFile(onResult, { base: extraFile, basename });
});
}
return this.searchInFolder(folderQuery, _onResult)
.then(() => {
resolve({ isLimitHit: this.isLimitHit });
}, (errs: Error[]) => {
const errMsg = errs
.map(err => toErrorMessage(err))
.filter(msg => !!msg)[0];
reject(new Error(errMsg));
});
});
}
private searchInFolder(fq: IFolderQuery<URI>, onResult: (match: IInternalFileMatch) => void): TPromise<void> {
let cancellation = new CancellationTokenSource();
return new TPromise((resolve, reject) => {
const options = this.getSearchOptionsForFolder(fq);
const tree = this.initDirectoryTree();
const queryTester = new QueryGlobTester(this.config, fq);
const noSiblingsClauses = !queryTester.hasSiblingExcludeClauses();
const onProviderResult = (uri: URI) => {
if (this.isCanceled) {
return;
}
// TODO@rob - ???
const relativePath = path.relative(fq.folder.path, uri.path);
if (noSiblingsClauses) {
const basename = path.basename(uri.path);
this.matchFile(onResult, { base: fq.folder, relativePath, basename, original: uri });
return;
}
// TODO: Optimize siblings clauses with ripgrep here.
this.addDirectoryEntries(tree, fq.folder, relativePath, onResult);
};
new TPromise(resolve => process.nextTick(resolve))
.then(() => {
this.activeCancellationTokens.add(cancellation);
return this.provider.provideFileIndex(options, cancellation.token);
})
.then(results => {
this.activeCancellationTokens.delete(cancellation);
if (this.isCanceled) {
return null;
}
results.forEach(onProviderResult);
this.matchDirectoryTree(tree, queryTester, onResult);
return null;
}).then(
() => {
cancellation.dispose();
resolve(undefined);
},
err => {
cancellation.dispose();
reject(err);
});
});
}
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): vscode.FileIndexOptions {
const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern);
const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern);
return {
folder: fq.folder,
excludes,
includes,
useIgnoreFiles: !this.config.disregardIgnoreFiles,
followSymlinks: !this.config.ignoreSymlinks
};
}
private initDirectoryTree(): IDirectoryTree {
const tree: IDirectoryTree = {
rootEntries: [],
pathToEntries: Object.create(null)
};
tree.pathToEntries['.'] = tree.rootEntries;
return tree;
}
private addDirectoryEntries({ pathToEntries }: IDirectoryTree, base: URI, relativeFile: string, onResult: (result: IInternalFileMatch) => void) {
// Support relative paths to files from a root resource (ignores excludes)
if (relativeFile === this.filePattern) {
const basename = path.basename(this.filePattern);
this.matchFile(onResult, { base: base, relativePath: this.filePattern, basename });
}
function add(relativePath: string) {
const basename = path.basename(relativePath);
const dirname = path.dirname(relativePath);
let entries = pathToEntries[dirname];
if (!entries) {
entries = pathToEntries[dirname] = [];
add(dirname);
}
entries.push({
base,
relativePath,
basename
});
}
add(relativeFile);
}
private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, queryTester: QueryGlobTester, onResult: (result: IInternalFileMatch) => void) {
const self = this;
const filePattern = this.filePattern;
function matchDirectory(entries: IDirectoryEntry[]) {
// self.directoriesWalked++;
for (let i = 0, n = entries.length; i < n; i++) {
const entry = entries[i];
const { relativePath, basename } = entry;
// Check exclude pattern
// If the user searches for the exact file name, we adjust the glob matching
// to ignore filtering by siblings because the user seems to know what she
// is searching for and we want to include the result in that case anyway
const hasSibling = glob.hasSiblingFn(() => entries.map(entry => entry.basename));
if (!queryTester.includedInQuerySync(relativePath, basename, filePattern !== basename ? hasSibling : undefined)) {
continue;
}
const sub = pathToEntries[relativePath];
if (sub) {
matchDirectory(sub);
} else {
// self.filesWalked++;
if (relativePath === filePattern) {
continue; // ignore file if its path matches with the file pattern because that is already matched above
}
self.matchFile(onResult, entry);
}
if (self.isLimitHit) {
break;
}
}
}
matchDirectory(rootEntries);
}
private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void {
if (this.isFilePatternMatch(candidate.relativePath) && (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename))) {
if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) {
this.isLimitHit = true;
this.cancel();
}
if (!this.isLimitHit) {
onResult(candidate);
}
}
}
private isFilePatternMatch(path: string): boolean {
// Check for search pattern
if (this.filePattern) {
if (this.filePattern === '*') {
return true; // support the all-matching wildcard
}
return strings.fuzzyContains(path, this.normalizedFilePatternLowercase);
}
// No patterns means we match all
return true;
}
}
export class FileIndexSearchManager {
private static readonly BATCH_SIZE = 512;
private caches: { [cacheKey: string]: Cache; } = Object.create(null);
private readonly folderCacheKeys = new Map<string, Set<string>>();
public fileSearch(config: ISearchQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void): TPromise<ISearchCompleteStats> {
if (config.sortByScore) {
let sortedSearch = this.trySortedSearchFromCache(config);
if (!sortedSearch) {
const engineConfig = config.maxResults ?
{
...config,
...{ maxResults: null }
} :
config;
const engine = new FileIndexSearchEngine(engineConfig, provider);
sortedSearch = this.doSortedSearch(engine, config);
}
return new TPromise<ISearchCompleteStats>((c, e) => {
sortedSearch.then(complete => {
this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE);
c(complete);
}, e, onBatch);
}, () => {
sortedSearch.cancel();
});
}
const engine = new FileIndexSearchEngine(config, provider);
return this.doSearch(engine)
.then(complete => {
this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE);
return <ISearchCompleteStats>{
limitHit: complete.limitHit
};
});
}
private getFolderCacheKey(config: ISearchQuery): string {
const uri = config.folderQueries[0].folder.toString();
const folderCacheKey = config.cacheKey && `${uri}_${config.cacheKey}`;
if (!this.folderCacheKeys.get(config.cacheKey)) {
this.folderCacheKeys.set(config.cacheKey, new Set());
}
this.folderCacheKeys.get(config.cacheKey).add(folderCacheKey);
return folderCacheKey;
}
private rawMatchToSearchItem(match: IInternalFileMatch): IFileMatch {
return {
resource: match.original || resources.joinPath(match.base, match.relativePath)
};
}
private doSortedSearch(engine: FileIndexSearchEngine, config: ISearchQuery): TPromise<IInternalSearchComplete> {
let searchPromise: TPromise<void>;
let allResultsPromise = new TPromise<IInternalSearchComplete>((c, e) => {
searchPromise = this.doSearch(engine).then(c, e);
}, () => {
searchPromise.cancel();
});
const folderCacheKey = this.getFolderCacheKey(config);
let cache: Cache;
if (folderCacheKey) {
cache = this.getOrCreateCache(folderCacheKey);
cache.resultsToSearchCache[config.filePattern] = allResultsPromise;
allResultsPromise.then(null, err => {
delete cache.resultsToSearchCache[config.filePattern];
});
allResultsPromise = this.preventCancellation(allResultsPromise);
}
let chained: TPromise<void>;
return new TPromise<IInternalSearchComplete>((c, e) => {
chained = allResultsPromise.then(complete => {
const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null);
return this.sortResults(config, complete.results, scorerCache)
.then(sortedResults => {
c({
limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults, // ??
results: sortedResults
});
});
}, e);
}, () => {
chained.cancel();
});
}
private getOrCreateCache(cacheKey: string): Cache {
const existing = this.caches[cacheKey];
if (existing) {
return existing;
}
return this.caches[cacheKey] = new Cache();
}
private trySortedSearchFromCache(config: ISearchQuery): TPromise<IInternalSearchComplete> {
const folderCacheKey = this.getFolderCacheKey(config);
const cache = folderCacheKey && this.caches[folderCacheKey];
if (!cache) {
return undefined;
}
const cached = this.getResultsFromCache(cache, config.filePattern);
if (cached) {
let chained: TPromise<void>;
return new TPromise<IInternalSearchComplete>((c, e) => {
chained = cached.then(complete => {
return this.sortResults(config, complete.results, cache.scorerCache)
.then(sortedResults => {
c({
limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults,
results: sortedResults
});
});
}, e);
}, () => {
chained.cancel();
});
}
return undefined;
}
private sortResults(config: IRawSearchQuery, results: IInternalFileMatch[], scorerCache: ScorerCache): 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
// exceeds config.maxResults.
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);
}
private sendAsBatches(rawMatches: IInternalFileMatch[], onBatch: (batch: IFileMatch[]) => void, batchSize: number) {
const serializedMatches = rawMatches.map(rawMatch => this.rawMatchToSearchItem(rawMatch));
if (batchSize && batchSize > 0) {
for (let i = 0; i < serializedMatches.length; i += batchSize) {
onBatch(serializedMatches.slice(i, i + batchSize));
}
} else {
onBatch(serializedMatches);
}
}
private getResultsFromCache(cache: Cache, searchValue: string): TPromise<IInternalSearchComplete> {
if (path.isAbsolute(searchValue)) {
return null; // bypass cache if user looks up an absolute path where matching goes directly on disk
}
// Find cache entries by prefix of search value
const hasPathSep = searchValue.indexOf(path.sep) >= 0;
let cached: TPromise<IInternalSearchComplete>;
for (let previousSearch in cache.resultsToSearchCache) {
// If we narrow down, we might be able to reuse the cached results
if (strings.startsWith(searchValue, previousSearch)) {
if (hasPathSep && previousSearch.indexOf(path.sep) < 0) {
continue; // since a path character widens the search for potential more matches, require it in previous search too
}
const c = cache.resultsToSearchCache[previousSearch];
cached = this.preventCancellation(c);
break;
}
}
if (!cached) {
return null;
}
return new TPromise<IInternalSearchComplete>((c, e) => {
cached.then(complete => {
// Pattern match on results
let results: IInternalFileMatch[] = [];
const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase();
for (let i = 0; i < complete.results.length; i++) {
let entry = complete.results[i];
// Check if this entry is a match for the search value
if (!strings.fuzzyContains(entry.relativePath, normalizedSearchValueLowercase)) {
continue;
}
results.push(entry);
}
c({
limitHit: complete.limitHit,
results
});
}, e);
}, () => {
cached.cancel();
});
}
private doSearch(engine: FileIndexSearchEngine): TPromise<IInternalSearchComplete> {
const results: IInternalFileMatch[] = [];
const onResult = match => results.push(match);
return new TPromise<IInternalSearchComplete>((c, e) => {
engine.search(onResult).then(result => {
c({
limitHit: result.isLimitHit,
results
});
}, e);
}, () => {
engine.cancel();
});
}
public clearCache(cacheKey: string): TPromise<void> {
if (!this.folderCacheKeys.has(cacheKey)) {
return TPromise.wrap(undefined);
}
const expandedKeys = this.folderCacheKeys.get(cacheKey);
expandedKeys.forEach(key => delete this.caches[key]);
this.folderCacheKeys.delete(cacheKey);
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.
});
}
}
class Cache {
public resultsToSearchCache: { [searchValue: string]: TPromise<IInternalSearchComplete>; } = Object.create(null);
public scorerCache: ScorerCache = Object.create(null);
}
const FileMatchItemAccessor = new class implements IItemAccessor<IInternalFileMatch> {
public getItemLabel(match: IInternalFileMatch): string {
return match.basename; // e.g. myFile.txt
}
public getItemDescription(match: IInternalFileMatch): string {
return match.relativePath.substr(0, match.relativePath.length - match.basename.length - 1); // e.g. some/path/to/file
}
public getItemPath(match: IInternalFileMatch): string {
return match.relativePath; // e.g. some/path/to/file/myFile.txt
}
};

View File

@@ -4,61 +4,711 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { asWinJsPromise } from 'vs/base/common/async';
import * as path from 'path';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import * as glob from 'vs/base/common/glob';
import * as resources from 'vs/base/common/resources';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IPatternInfo } from 'vs/platform/search/common/search';
import * as extfs from 'vs/base/node/extfs';
import { IFileMatch, IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search';
import * as vscode from 'vscode';
import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol';
import { toDisposable } from 'vs/base/common/lifecycle';
import { IInternalFileMatch, QueryGlobTester, resolvePatternsForProvider, IDirectoryTree, IDirectoryEntry, FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex';
export interface ISchemeTransformer {
transformOutgoing(scheme: string): string;
}
export class ExtHostSearch implements ExtHostSearchShape {
private readonly _proxy: MainThreadSearchShape;
private readonly _searchProvider = new Map<number, vscode.SearchProvider>();
private readonly _fileSearchProvider = new Map<number, vscode.FileSearchProvider>();
private readonly _textSearchProvider = new Map<number, vscode.TextSearchProvider>();
private readonly _fileIndexProvider = new Map<number, vscode.FileIndexProvider>();
private _handlePool: number = 0;
constructor(mainContext: IMainContext) {
private _fileSearchManager: FileSearchManager;
private _fileIndexSearchManager: FileIndexSearchManager;
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _extfs = extfs) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSearch);
this._fileSearchManager = new FileSearchManager();
this._fileIndexSearchManager = new FileIndexSearchManager();
}
registerSearchProvider(scheme: string, provider: vscode.SearchProvider) {
const handle = this._handlePool++;
this._searchProvider.set(handle, provider);
this._proxy.$registerSearchProvider(handle, scheme);
return {
dispose: () => {
this._searchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
}
};
}
$provideFileSearchResults(handle: number, session: number, query: string): TPromise<void> {
const provider = this._searchProvider.get(handle);
if (!provider.provideFileSearchResults) {
return TPromise.as(undefined);
private _transformScheme(scheme: string): string {
if (this._schemeTransformer) {
return this._schemeTransformer.transformOutgoing(scheme);
}
const progress = {
report: (uri) => {
this._proxy.$handleFindMatch(handle, session, uri);
}
};
return asWinJsPromise(token => provider.provideFileSearchResults(query, progress, token));
return scheme;
}
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, options: { includes: string[], excludes: string[] }): TPromise<void> {
const provider = this._searchProvider.get(handle);
registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider) {
const handle = this._handlePool++;
this._fileSearchProvider.set(handle, provider);
this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._fileSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider) {
const handle = this._handlePool++;
this._textSearchProvider.set(handle, provider);
this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._textSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
registerFileIndexProvider(scheme: string, provider: vscode.FileIndexProvider) {
const handle = this._handlePool++;
this._fileIndexProvider.set(handle, provider);
this._proxy.$registerFileIndexProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._fileSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle); // TODO@roblou - unregisterFileIndexProvider
});
}
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawSearchQuery): TPromise<ISearchCompleteStats> {
const provider = this._fileSearchProvider.get(handle);
const query = reviveQuery(rawQuery);
if (provider) {
return this._fileSearchManager.fileSearch(query, provider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
});
} else {
const indexProvider = this._fileIndexProvider.get(handle);
if (indexProvider) {
return this._fileIndexSearchManager.fileSearch(query, indexProvider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
});
} else {
throw new Error('something went wrong');
}
}
}
$clearCache(cacheKey: string): TPromise<void> {
// Actually called once per provider.
// Only relevant to file index search.
return this._fileIndexSearchManager.clearCache(cacheKey);
}
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, rawQuery: IRawSearchQuery): TPromise<ISearchCompleteStats> {
const provider = this._textSearchProvider.get(handle);
if (!provider.provideTextSearchResults) {
return TPromise.as(undefined);
}
const progress = {
report: (data: vscode.TextSearchResult) => {
this._proxy.$handleFindMatch(handle, session, [data.uri, {
lineNumber: data.range.start.line,
preview: data.preview.leading + data.preview.matching + data.preview.trailing,
offsetAndLengths: [[data.preview.leading.length, data.preview.matching.length]]
}]);
}
};
return asWinJsPromise(token => provider.provideTextSearchResults(pattern, options, progress, token));
const query = reviveQuery(rawQuery);
const engine = new TextSearchEngine(pattern, query, provider, this._extfs);
return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress));
}
}
function reviveQuery(rawQuery: IRawSearchQuery): ISearchQuery {
return {
...rawQuery,
...{
folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery),
extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components))
}
};
}
function reviveFolderQuery(rawFolderQuery: IFolderQuery<UriComponents>): IFolderQuery<URI> {
return {
...rawFolderQuery,
folder: URI.revive(rawFolderQuery.folder)
};
}
class TextSearchResultsCollector {
private _batchedCollector: BatchedCollector<IFileMatch>;
private _currentFolderIdx: number;
private _currentUri: URI;
private _currentFileMatch: IFileMatch;
constructor(private _onResult: (result: IFileMatch[]) => void) {
this._batchedCollector = new BatchedCollector<IFileMatch>(512, items => this.sendItems(items));
}
add(data: vscode.TextSearchResult, folderIdx: number): void {
// Collects TextSearchResults into IInternalFileMatches and collates using BatchedCollector.
// This is efficient for ripgrep which sends results back one file at a time. It wouldn't be efficient for other search
// providers that send results in random order. We could do this step afterwards instead.
if (this._currentFileMatch && (this._currentFolderIdx !== folderIdx || resources.isEqual(this._currentUri, data.uri))) {
this.pushToCollector();
this._currentFileMatch = null;
}
if (!this._currentFileMatch) {
this._currentFileMatch = {
resource: data.uri,
lineMatches: []
};
}
// TODO@roblou - line text is sent for every match
const matchRange = data.preview.match;
this._currentFileMatch.lineMatches.push({
lineNumber: data.range.start.line,
preview: data.preview.text,
offsetAndLengths: [[matchRange.start.character, matchRange.end.character - matchRange.start.character]]
});
}
private pushToCollector(): void {
const size = this._currentFileMatch ?
this._currentFileMatch.lineMatches.reduce((acc, match) => acc + match.offsetAndLengths.length, 0) :
0;
this._batchedCollector.addItem(this._currentFileMatch, size);
}
flush(): void {
this.pushToCollector();
this._batchedCollector.flush();
}
private sendItems(items: IFileMatch[]): void {
this._onResult(items);
}
}
/**
* Collects items that have a size - before the cumulative size of collected items reaches START_BATCH_AFTER_COUNT, the callback is called for every
* set of items collected.
* But after that point, the callback is called with batches of maxBatchSize.
* If the batch isn't filled within some time, the callback is also called.
*/
class BatchedCollector<T> {
private static readonly TIMEOUT = 4000;
// After START_BATCH_AFTER_COUNT items have been collected, stop flushing on timeout
private static readonly START_BATCH_AFTER_COUNT = 50;
private totalNumberCompleted = 0;
private batch: T[] = [];
private batchSize = 0;
private timeoutHandle: number;
constructor(private maxBatchSize: number, private cb: (items: T[]) => void) {
}
addItem(item: T, size: number): void {
if (!item) {
return;
}
this.addItemToBatch(item, size);
}
addItems(items: T[], size: number): void {
if (!items) {
return;
}
if (this.maxBatchSize > 0) {
this.addItemsToBatch(items, size);
} else {
this.cb(items);
}
}
private addItemToBatch(item: T, size: number): void {
this.batch.push(item);
this.batchSize += size;
this.onUpdate();
}
private addItemsToBatch(item: T[], size: number): void {
this.batch = this.batch.concat(item);
this.batchSize += size;
this.onUpdate();
}
private onUpdate(): void {
if (this.totalNumberCompleted < BatchedCollector.START_BATCH_AFTER_COUNT) {
// Flush because we aren't batching yet
this.flush();
} else if (this.batchSize >= this.maxBatchSize) {
// Flush because the batch is full
this.flush();
} else if (!this.timeoutHandle) {
// No timeout running, start a timeout to flush
this.timeoutHandle = setTimeout(() => {
this.flush();
}, BatchedCollector.TIMEOUT);
}
}
flush(): void {
if (this.batchSize) {
this.totalNumberCompleted += this.batchSize;
this.cb(this.batch);
this.batch = [];
this.batchSize = 0;
if (this.timeoutHandle) {
clearTimeout(this.timeoutHandle);
this.timeoutHandle = 0;
}
}
}
}
class TextSearchEngine {
private activeCancellationTokens = new Set<CancellationTokenSource>();
private collector: TextSearchResultsCollector;
private isLimitHit: boolean;
private resultCount = 0;
private isCanceled: boolean;
constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs) {
}
public cancel(): void {
this.isCanceled = true;
this.activeCancellationTokens.forEach(t => t.cancel());
this.activeCancellationTokens = new Set();
}
public search(onProgress: (matches: IFileMatch[]) => void): TPromise<{ limitHit: boolean }> {
const folderQueries = this.config.folderQueries;
return new TPromise<{ limitHit: boolean }>((resolve, reject) => {
this.collector = new TextSearchResultsCollector(onProgress);
const onResult = (match: vscode.TextSearchResult, folderIdx: number) => {
if (this.isCanceled) {
return;
}
if (this.resultCount >= this.config.maxResults) {
this.isLimitHit = true;
this.cancel();
}
if (!this.isLimitHit) {
this.resultCount++;
this.collector.add(match, folderIdx);
}
};
// For each root folder
TPromise.join(folderQueries.map((fq, i) => {
return this.searchInFolder(fq, r => onResult(r, i));
})).then(() => {
this.collector.flush();
resolve({ limitHit: this.isLimitHit });
}, (errs: Error[]) => {
const errMsg = errs
.map(err => toErrorMessage(err))
.filter(msg => !!msg)[0];
reject(new Error(errMsg));
});
});
}
private searchInFolder(folderQuery: IFolderQuery<URI>, onResult: (result: vscode.TextSearchResult) => void): TPromise<void> {
let cancellation = new CancellationTokenSource();
return new TPromise((resolve, reject) => {
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 searchOptions = this.getSearchOptionsForFolder(folderQuery);
new TPromise(resolve => process.nextTick(resolve))
.then(() => {
this.activeCancellationTokens.add(cancellation);
return this.provider.provideTextSearchResults(patternInfoToQuery(this.pattern), searchOptions, progress, cancellation.token);
})
.then(() => {
this.activeCancellationTokens.delete(cancellation);
return TPromise.join(testingPs);
})
.then(
() => {
cancellation.dispose();
resolve(null);
},
err => {
cancellation.dispose();
reject(err);
});
});
}
private readdir(dirname: string): TPromise<string[]> {
return new TPromise((resolve, reject) => {
this._extfs.readdir(dirname, (err, files) => {
if (err) {
return reject(err);
}
resolve(files);
});
});
}
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): vscode.TextSearchOptions {
const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern);
const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern);
return {
folder: URI.from(fq.folder),
excludes,
includes,
useIgnoreFiles: !this.config.disregardIgnoreFiles,
followSymlinks: !this.config.ignoreSymlinks,
encoding: this.config.fileEncoding,
maxFileSize: this.config.maxFileSize,
maxResults: this.config.maxResults
};
}
}
function patternInfoToQuery(patternInfo: IPatternInfo): vscode.TextSearchQuery {
return <vscode.TextSearchQuery>{
isCaseSensitive: patternInfo.isCaseSensitive || false,
isRegExp: patternInfo.isRegExp || false,
isWordMatch: patternInfo.isWordMatch || false,
pattern: patternInfo.pattern
};
}
class FileSearchEngine {
private filePattern: string;
private includePattern: glob.ParsedExpression;
private maxResults: number;
private exists: boolean;
private isLimitHit: boolean;
private resultCount: number;
private isCanceled: boolean;
private activeCancellationTokens: Set<CancellationTokenSource>;
private globalExcludePattern: glob.ParsedExpression;
constructor(private config: ISearchQuery, private provider: vscode.FileSearchProvider) {
this.filePattern = config.filePattern;
this.includePattern = config.includePattern && glob.parse(config.includePattern);
this.maxResults = config.maxResults || null;
this.exists = config.exists;
this.resultCount = 0;
this.isLimitHit = false;
this.activeCancellationTokens = new Set<CancellationTokenSource>();
this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern);
}
public cancel(): void {
this.isCanceled = true;
this.activeCancellationTokens.forEach(t => t.cancel());
this.activeCancellationTokens = new Set();
}
public search(_onResult: (match: IInternalFileMatch) => void): TPromise<IInternalSearchComplete> {
const folderQueries = this.config.folderQueries;
return new TPromise((resolve, reject) => {
const onResult = (match: IInternalFileMatch) => {
this.resultCount++;
_onResult(match);
};
// Support that the file pattern is a full path to a file that exists
if (this.isCanceled) {
return resolve({ limitHit: this.isLimitHit });
}
// For each extra file
if (this.config.extraFileResources) {
this.config.extraFileResources
.forEach(extraFile => {
const extraFileStr = extraFile.toString(); // ?
const basename = path.basename(extraFileStr);
if (this.globalExcludePattern && this.globalExcludePattern(extraFileStr, basename)) {
return; // excluded
}
// File: Check for match on file pattern and include pattern
this.matchFile(onResult, { base: extraFile, basename });
});
}
// For each root folder
TPromise.join(folderQueries.map(fq => {
return this.searchInFolder(fq, onResult);
})).then(() => {
resolve({ limitHit: this.isLimitHit });
}, (errs: Error[]) => {
const errMsg = errs
.map(err => toErrorMessage(err))
.filter(msg => !!msg)[0];
reject(new Error(errMsg));
});
});
}
private searchInFolder(fq: IFolderQuery<URI>, onResult: (match: IInternalFileMatch) => void): TPromise<void> {
let cancellation = new CancellationTokenSource();
return new TPromise((resolve, reject) => {
const options = this.getSearchOptionsForFolder(fq);
const tree = this.initDirectoryTree();
const queryTester = new QueryGlobTester(this.config, fq);
const noSiblingsClauses = !queryTester.hasSiblingExcludeClauses();
new TPromise(_resolve => process.nextTick(_resolve))
.then(() => {
this.activeCancellationTokens.add(cancellation);
return this.provider.provideFileSearchResults(
{
pattern: this.config.filePattern || ''
},
options,
cancellation.token);
})
.then(results => {
if (this.isCanceled) {
return;
}
if (results) {
results.forEach(result => {
const relativePath = path.relative(fq.folder.fsPath, result.fsPath);
if (noSiblingsClauses) {
const basename = path.basename(result.fsPath);
this.matchFile(onResult, { base: fq.folder, relativePath, basename });
return;
}
// TODO: Optimize siblings clauses with ripgrep here.
this.addDirectoryEntries(tree, fq.folder, relativePath, onResult);
});
}
this.activeCancellationTokens.delete(cancellation);
if (this.isCanceled) {
return null;
}
this.matchDirectoryTree(tree, queryTester, onResult);
return null;
}).then(
() => {
cancellation.dispose();
resolve(null);
},
err => {
cancellation.dispose();
reject(err);
});
});
}
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): vscode.FileSearchOptions {
const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern);
const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern);
return {
folder: fq.folder,
excludes,
includes,
useIgnoreFiles: !this.config.disregardIgnoreFiles,
followSymlinks: !this.config.ignoreSymlinks,
maxResults: this.config.maxResults
};
}
private initDirectoryTree(): IDirectoryTree {
const tree: IDirectoryTree = {
rootEntries: [],
pathToEntries: Object.create(null)
};
tree.pathToEntries['.'] = tree.rootEntries;
return tree;
}
private addDirectoryEntries({ pathToEntries }: IDirectoryTree, base: URI, relativeFile: string, onResult: (result: IInternalFileMatch) => void) {
// Support relative paths to files from a root resource (ignores excludes)
if (relativeFile === this.filePattern) {
const basename = path.basename(this.filePattern);
this.matchFile(onResult, { base: base, relativePath: this.filePattern, basename });
}
function add(relativePath: string) {
const basename = path.basename(relativePath);
const dirname = path.dirname(relativePath);
let entries = pathToEntries[dirname];
if (!entries) {
entries = pathToEntries[dirname] = [];
add(dirname);
}
entries.push({
base,
relativePath,
basename
});
}
add(relativeFile);
}
private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, queryTester: QueryGlobTester, onResult: (result: IInternalFileMatch) => void) {
const self = this;
const filePattern = this.filePattern;
function matchDirectory(entries: IDirectoryEntry[]) {
const hasSibling = glob.hasSiblingFn(() => entries.map(entry => entry.basename));
for (let i = 0, n = entries.length; i < n; i++) {
const entry = entries[i];
const { relativePath, basename } = entry;
// Check exclude pattern
// If the user searches for the exact file name, we adjust the glob matching
// to ignore filtering by siblings because the user seems to know what she
// is searching for and we want to include the result in that case anyway
if (!queryTester.includedInQuerySync(relativePath, basename, filePattern !== basename ? hasSibling : undefined)) {
continue;
}
const sub = pathToEntries[relativePath];
if (sub) {
matchDirectory(sub);
} else {
if (relativePath === filePattern) {
continue; // ignore file if its path matches with the file pattern because that is already matched above
}
self.matchFile(onResult, entry);
}
if (self.isLimitHit) {
break;
}
}
}
matchDirectory(rootEntries);
}
private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void {
if (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename)) {
if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) {
this.isLimitHit = true;
this.cancel();
}
if (!this.isLimitHit) {
onResult(candidate);
}
}
}
}
interface IInternalSearchComplete {
limitHit: boolean;
}
class FileSearchManager {
private static readonly BATCH_SIZE = 512;
fileSearch(config: ISearchQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void): TPromise<ISearchCompleteStats> {
let searchP: TPromise;
return new TPromise<ISearchCompleteStats>((c, e) => {
const engine = new FileSearchEngine(config, provider);
const onInternalResult = (batch: IInternalFileMatch[]) => {
onBatch(batch.map(m => this.rawMatchToSearchItem(m)));
};
searchP = this.doSearch(engine, FileSearchManager.BATCH_SIZE, onInternalResult).then(
result => {
c({
limitHit: result.limitHit
});
},
e);
}, () => {
if (searchP) {
searchP.cancel();
}
});
}
private rawMatchToSearchItem(match: IInternalFileMatch): IFileMatch {
if (match.relativePath) {
return {
resource: resources.joinPath(match.base, match.relativePath)
};
} else {
// extraFileResources
return {
resource: match.base
};
}
}
private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void): TPromise<IInternalSearchComplete> {
return new TPromise((c, e) => {
const _onResult = match => {
if (match) {
batch.push(match);
if (batchSize > 0 && batch.length >= batchSize) {
onResultBatch(batch);
batch = [];
}
}
};
let batch: IInternalFileMatch[] = [];
engine.search(_onResult).then(result => {
if (batch.length) {
onResultBatch(batch);
}
c(result);
}, error => {
if (batch.length) {
onResultBatch(batch);
}
e(error);
});
}, () => {
engine.cancel();
});
}
}

View File

@@ -12,7 +12,7 @@ import { asWinJsPromise } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as TaskSystem from 'vs/workbench/parts/tasks/common/tasks';
import * as tasks from 'vs/workbench/parts/tasks/common/tasks';
import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
@@ -21,10 +21,15 @@ import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import * as vscode from 'vscode';
import {
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO, ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO
ShellExecutionOptionsDTO, ShellExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO
} from '../shared/tasks';
export { TaskExecutionDTO };
// {{SQL CARBON EDIT}}
// import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
/*
namespace ProblemPattern {
@@ -197,46 +202,47 @@ namespace ProblemMatcher {
*/
namespace TaskRevealKind {
export function from(value: vscode.TaskRevealKind): TaskSystem.RevealKind {
export function from(value: vscode.TaskRevealKind): tasks.RevealKind {
if (value === void 0 || value === null) {
return TaskSystem.RevealKind.Always;
return tasks.RevealKind.Always;
}
switch (value) {
case types.TaskRevealKind.Silent:
return TaskSystem.RevealKind.Silent;
return tasks.RevealKind.Silent;
case types.TaskRevealKind.Never:
return TaskSystem.RevealKind.Never;
return tasks.RevealKind.Never;
}
return TaskSystem.RevealKind.Always;
return tasks.RevealKind.Always;
}
}
namespace TaskPanelKind {
export function from(value: vscode.TaskPanelKind): TaskSystem.PanelKind {
export function from(value: vscode.TaskPanelKind): tasks.PanelKind {
if (value === void 0 || value === null) {
return TaskSystem.PanelKind.Shared;
return tasks.PanelKind.Shared;
}
switch (value) {
case types.TaskPanelKind.Dedicated:
return TaskSystem.PanelKind.Dedicated;
return tasks.PanelKind.Dedicated;
case types.TaskPanelKind.New:
return TaskSystem.PanelKind.New;
return tasks.PanelKind.New;
default:
return TaskSystem.PanelKind.Shared;
return tasks.PanelKind.Shared;
}
}
}
namespace PresentationOptions {
export function from(value: vscode.TaskPresentationOptions): TaskSystem.PresentationOptions {
export function from(value: vscode.TaskPresentationOptions): tasks.PresentationOptions {
if (value === void 0 || value === null) {
return { reveal: TaskSystem.RevealKind.Always, echo: true, focus: false, panel: TaskSystem.PanelKind.Shared };
return { reveal: tasks.RevealKind.Always, echo: true, focus: false, panel: tasks.PanelKind.Shared, showReuseMessage: true };
}
return {
reveal: TaskRevealKind.from(value.reveal),
echo: value.echo === void 0 ? true : !!value.echo,
focus: !!value.focus,
panel: TaskPanelKind.from(value.panel)
panel: TaskPanelKind.from(value.panel),
showReuseMessage: value.showReuseMessage === void 0 ? true : !!value.showReuseMessage
};
}
}
@@ -259,11 +265,11 @@ namespace CommandOptions {
function isShellConfiguration(value: any): value is { executable: string; shellArgs?: string[] } {
return value && typeof value.executable === 'string';
}
export function from(value: vscode.ShellExecutionOptions | vscode.ProcessExecutionOptions): TaskSystem.CommandOptions {
export function from(value: vscode.ShellExecutionOptions | vscode.ProcessExecutionOptions): tasks.CommandOptions {
if (value === void 0 || value === null) {
return undefined;
}
let result: TaskSystem.CommandOptions = {
let result: tasks.CommandOptions = {
};
if (typeof value.cwd === 'string') {
result.cwd = value.cwd;
@@ -285,7 +291,7 @@ namespace CommandOptions {
}
namespace ShellQuoteOptions {
export function from(value: vscode.ShellQuotingOptions): TaskSystem.ShellQuotingOptions {
export function from(value: vscode.ShellQuotingOptions): tasks.ShellQuotingOptions {
if (value === void 0 || value === null) {
return undefined;
}
@@ -298,12 +304,12 @@ namespace ShellQuoteOptions {
}
namespace ShellConfiguration {
export function from(value: { executable?: string, shellArgs?: string[], quotes?: vscode.ShellQuotingOptions }): TaskSystem.ShellConfiguration {
export function from(value: { executable?: string, shellArgs?: string[], quotes?: vscode.ShellQuotingOptions }): tasks.ShellConfiguration {
if (value === void 0 || value === null || !value.executable) {
return undefined;
}
let result: TaskSystem.ShellConfiguration = {
let result: tasks.ShellConfiguration = {
executable: value.executable,
args: Strings.from(value.shellArgs),
quoting: ShellQuoteOptions.from(value.quotes)
@@ -313,7 +319,7 @@ namespace ShellConfiguration {
}
namespace ShellString {
export function from(value: (string | vscode.ShellQuotedString)[]): TaskSystem.CommandString[] {
export function from(value: (string | vscode.ShellQuotedString)[]): tasks.CommandString[] {
if (value === void 0 || value === null) {
return undefined;
}
@@ -323,11 +329,11 @@ namespace ShellString {
namespace Tasks {
export function from(tasks: vscode.Task[], rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): TaskSystem.ContributedTask[] {
export function from(tasks: vscode.Task[], rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): tasks.ContributedTask[] {
if (tasks === void 0 || tasks === null) {
return [];
}
let result: TaskSystem.ContributedTask[] = [];
let result: tasks.ContributedTask[] = [];
for (let task of tasks) {
let converted = fromSingle(task, rootFolder, extension);
if (converted) {
@@ -337,11 +343,11 @@ namespace Tasks {
return result;
}
function fromSingle(task: vscode.Task, rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): TaskSystem.ContributedTask {
function fromSingle(task: vscode.Task, rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): tasks.ContributedTask {
if (typeof task.name !== 'string') {
return undefined;
}
let command: TaskSystem.CommandConfiguration;
let command: tasks.CommandConfiguration;
let execution = task.execution;
if (execution instanceof types.ProcessExecution) {
command = getProcessCommand(execution);
@@ -357,21 +363,21 @@ namespace Tasks {
let taskScope: types.TaskScope.Global | types.TaskScope.Workspace | vscode.WorkspaceFolder | undefined = task.scope;
let workspaceFolder: vscode.WorkspaceFolder | undefined;
let scope: TaskSystem.TaskScope;
let scope: tasks.TaskScope;
// For backwards compatibility
if (taskScope === void 0) {
scope = TaskSystem.TaskScope.Folder;
scope = tasks.TaskScope.Folder;
workspaceFolder = rootFolder;
} else if (taskScope === types.TaskScope.Global) {
scope = TaskSystem.TaskScope.Global;
scope = tasks.TaskScope.Global;
} else if (taskScope === types.TaskScope.Workspace) {
scope = TaskSystem.TaskScope.Workspace;
scope = tasks.TaskScope.Workspace;
} else {
scope = TaskSystem.TaskScope.Folder;
scope = tasks.TaskScope.Folder;
workspaceFolder = taskScope;
}
let source: TaskSystem.ExtensionTaskSource = {
kind: TaskSystem.TaskSourceKind.Extension,
let source: tasks.ExtensionTaskSource = {
kind: tasks.TaskSourceKind.Extension,
label: typeof task.source === 'string' ? task.source : extension.name,
extension: extension.id,
scope: scope,
@@ -380,22 +386,17 @@ namespace Tasks {
// We can't transfer a workspace folder object from the extension host to main since they differ
// in shape and we don't have backwards converting function. So transfer the URI and resolve the
// workspace folder on the main side.
(source as any as TaskSystem.ExtensionTaskSourceTransfer).__workspaceFolder = workspaceFolder ? workspaceFolder.uri as URI : undefined;
(source as any as tasks.ExtensionTaskSourceTransfer).__workspaceFolder = workspaceFolder ? workspaceFolder.uri as URI : undefined;
(source as any as tasks.ExtensionTaskSourceTransfer).__definition = task.definition;
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
let key = (task as types.Task).definitionKey;
let kind = (task as types.Task).definition;
let id = `${extension.id}.${key}`;
let taskKind: TaskSystem.TaskIdentifier = {
_key: key,
type: kind.type
};
Objects.assign(taskKind, kind);
let result: TaskSystem.ContributedTask = {
_id: id, // uuidMap.getUUID(identifier),
// The definition id will be prefix on the main side since we compute it there.
let id = `${extension.id}`;
let result: tasks.ContributedTask = {
_id: id,
_source: source,
_label: label,
type: kind.type,
defines: taskKind,
type: task.definition.type,
defines: undefined,
name: task.name,
identifier: label,
group: task.group ? (task.group as types.TaskGroup).id : undefined,
@@ -407,14 +408,14 @@ namespace Tasks {
return result;
}
function getProcessCommand(value: vscode.ProcessExecution): TaskSystem.CommandConfiguration {
function getProcessCommand(value: vscode.ProcessExecution): tasks.CommandConfiguration {
if (typeof value.process !== 'string') {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
let result: tasks.CommandConfiguration = {
name: value.process,
args: Strings.from(value.args),
runtime: TaskSystem.RuntimeType.Process,
runtime: tasks.RuntimeType.Process,
suppressTaskName: true,
presentation: undefined
};
@@ -424,15 +425,15 @@ namespace Tasks {
return result;
}
function getShellCommand(value: vscode.ShellExecution): TaskSystem.CommandConfiguration {
function getShellCommand(value: vscode.ShellExecution): tasks.CommandConfiguration {
if (value.args) {
if (typeof value.command !== 'string' && typeof value.command.value !== 'string') {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
let result: tasks.CommandConfiguration = {
name: value.command,
args: ShellString.from(value.args),
runtime: TaskSystem.RuntimeType.Shell,
runtime: tasks.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
@@ -443,9 +444,9 @@ namespace Tasks {
if (typeof value.commandLine !== 'string') {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
let result: tasks.CommandConfiguration = {
name: value.commandLine,
runtime: TaskSystem.RuntimeType.Shell,
runtime: tasks.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
@@ -689,21 +690,28 @@ namespace TaskFilterDTO {
}
class TaskExecutionImpl implements vscode.TaskExecution {
constructor(readonly _id: string, private readonly _task: vscode.Task, private readonly _tasks: ExtHostTask) {
constructor(private readonly _tasks: ExtHostTask, readonly _id: string, private readonly _task: vscode.Task) {
}
get task(): vscode.Task {
public get task(): vscode.Task {
return this._task;
}
public terminate(): void {
this._tasks.terminateTask(this);
}
public fireDidStartProcess(value: TaskProcessStartedDTO): void {
}
public fireDidEndProcess(value: TaskProcessEndedDTO): void {
}
}
namespace TaskExecutionDTO {
export function to(value: TaskExecutionDTO, tasks: ExtHostTask): vscode.TaskExecution {
return new TaskExecutionImpl(value.id, TaskDTO.to(value.task, tasks.extHostWorkspace), tasks);
return new TaskExecutionImpl(tasks, value.id, TaskDTO.to(value.task, tasks.extHostWorkspace));
}
export function from(value: vscode.TaskExecution): TaskExecutionDTO {
return {
@@ -721,7 +729,9 @@ interface HandlerData {
export class ExtHostTask implements ExtHostTaskShape {
private _proxy: MainThreadTaskShape;
private _extHostWorkspace: ExtHostWorkspace;
private _workspaceService: ExtHostWorkspace;
private _editorService: ExtHostDocumentsAndEditors;
private _configurationService: ExtHostConfiguration;
private _handleCounter: number;
private _handlers: Map<number, HandlerData>;
private _taskExecutions: Map<string, TaskExecutionImpl>;
@@ -729,16 +739,21 @@ export class ExtHostTask implements ExtHostTaskShape {
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
constructor(mainContext: IMainContext, extHostWorkspace: ExtHostWorkspace) {
private readonly _onDidTaskProcessStarted: Emitter<vscode.TaskProcessStartEvent> = new Emitter<vscode.TaskProcessStartEvent>();
private readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
constructor(mainContext: IMainContext, workspaceService: ExtHostWorkspace, editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfiguration) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTask);
this._extHostWorkspace = extHostWorkspace;
this._workspaceService = workspaceService;
this._editorService = editorService;
this._configurationService = configurationService;
this._handleCounter = 0;
this._handlers = new Map<number, HandlerData>();
this._taskExecutions = new Map<string, TaskExecutionImpl>();
}
public get extHostWorkspace(): ExtHostWorkspace {
return this._extHostWorkspace;
return this._workspaceService;
}
public registerTaskProvider(extension: IExtensionDescription, provider: vscode.TaskProvider): vscode.Disposable {
@@ -754,11 +769,15 @@ export class ExtHostTask implements ExtHostTaskShape {
});
}
public registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void {
this._proxy.$registerTaskSystem(scheme, info);
}
public fetchTasks(filter?: vscode.TaskFilter): Thenable<vscode.Task[]> {
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then((values) => {
let result: vscode.Task[] = [];
for (let value of values) {
let task = TaskDTO.to(value, this._extHostWorkspace);
let task = TaskDTO.to(value, this._workspaceService);
if (task) {
result.push(task);
}
@@ -782,22 +801,12 @@ export class ExtHostTask implements ExtHostTaskShape {
}
}
public $taskStarted(execution: TaskExecutionDTO): void {
this._onDidExecuteTask.fire({
execution: this.getTaskExecution(execution)
});
}
get taskExecutions(): vscode.TaskExecution[] {
public get taskExecutions(): vscode.TaskExecution[] {
let result: vscode.TaskExecution[] = [];
this._taskExecutions.forEach(value => result.push(value));
return result;
}
get onDidStartTask(): Event<vscode.TaskStartEvent> {
return this._onDidExecuteTask.event;
}
public terminateTask(execution: vscode.TaskExecution): TPromise<void> {
if (!(execution instanceof TaskExecutionImpl)) {
throw new Error('No valid task execution provided');
@@ -805,7 +814,21 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id);
}
public $taskEnded(execution: TaskExecutionDTO): void {
public get onDidStartTask(): Event<vscode.TaskStartEvent> {
return this._onDidExecuteTask.event;
}
public $onDidStartTask(execution: TaskExecutionDTO): void {
this._onDidExecuteTask.fire({
execution: this.getTaskExecution(execution)
});
}
public get onDidEndTask(): Event<vscode.TaskEndEvent> {
return this._onDidTerminateTask.event;
}
public $OnDidEndTask(execution: TaskExecutionDTO): void {
const _execution = this.getTaskExecution(execution);
this._taskExecutions.delete(execution.id);
this._onDidTerminateTask.fire({
@@ -813,34 +836,92 @@ export class ExtHostTask implements ExtHostTaskShape {
});
}
get onDidEndTask(): Event<vscode.TaskEndEvent> {
return this._onDidTerminateTask.event;
public get onDidStartTaskProcess(): Event<vscode.TaskProcessStartEvent> {
return this._onDidTaskProcessStarted.event;
}
public $provideTasks(handle: number): TPromise<TaskSystem.TaskSet> {
public $onDidStartTaskProcess(value: TaskProcessStartedDTO): void {
const execution = this.getTaskExecution(value.id);
if (execution) {
this._onDidTaskProcessStarted.fire({
execution: execution,
processId: value.processId
});
}
}
public get onDidEndTaskProcess(): Event<vscode.TaskProcessEndEvent> {
return this._onDidTaskProcessEnded.event;
}
public $onDidEndTaskProcess(value: TaskProcessEndedDTO): void {
const execution = this.getTaskExecution(value.id);
if (execution) {
this._onDidTaskProcessEnded.fire({
execution: execution,
exitCode: value.exitCode
});
}
}
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): TPromise<tasks.TaskSet> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<TaskSystem.TaskSet>(new Error('no handler found'));
return TPromise.wrapError<tasks.TaskSet>(new Error('no handler found'));
}
return asWinJsPromise(token => handler.provider.provideTasks(token)).then(value => {
let workspaceFolders = this._extHostWorkspace.getWorkspaceFolders();
let sanitized: vscode.Task[] = [];
for (let task of value) {
if (task.definition && validTypes[task.definition.type] === true) {
sanitized.push(task);
} else {
sanitized.push(task);
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
}
}
let workspaceFolders = this._workspaceService.getWorkspaceFolders();
return {
tasks: Tasks.from(value, workspaceFolders && workspaceFolders.length > 0 ? workspaceFolders[0] : undefined, handler.extension),
tasks: Tasks.from(sanitized, workspaceFolders && workspaceFolders.length > 0 ? workspaceFolders[0] : undefined, handler.extension),
extension: handler.extension
};
});
}
// {{SQL CARBON EDIT}} disable debug related method
public $resolveVariables(uriComponents: UriComponents, variables: string[]): any {
// let uri: URI = URI.revive(uriComponents);
// let result: { [key: string]: string; } = Object.create(null);
// let workspaceFolder = this._workspaceService.resolveWorkspaceFolder(uri);
// let resolver = new ExtHostVariableResolverService(this._workspaceService, this._editorService, this._configurationService);
// let ws: IWorkspaceFolder = {
// uri: workspaceFolder.uri,
// name: workspaceFolder.name,
// index: workspaceFolder.index,
// toResource: () => {
// throw new Error('Not implemented');
// }
// };
// for (let variable of variables) {
// result[variable] = resolver.resolve(ws, variable);
// }
// return result;
return undefined;
}
private nextHandle(): number {
return this._handleCounter++;
}
private getTaskExecution(execution: TaskExecutionDTO, task?: vscode.Task): TaskExecutionImpl {
private getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): TaskExecutionImpl {
if (typeof execution === 'string') {
return this._taskExecutions.get(execution);
}
let result: TaskExecutionImpl = this._taskExecutions.get(execution.id);
if (result) {
return result;
}
result = new TaskExecutionImpl(execution.id, task ? task : TaskDTO.to(execution.task, this._extHostWorkspace), this);
result = new TaskExecutionImpl(this, execution.id, task ? task : TaskDTO.to(execution.task, this._workspaceService));
this._taskExecutions.set(execution.id, result);
return result;
}

View File

@@ -5,28 +5,77 @@
'use strict';
import * as vscode from 'vscode';
import * as cp from 'child_process';
import * as os from 'os';
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/parts/terminal/node/terminalEnvironment';
import Uri from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol';
import { IMessageFromTerminalProcess } from 'vs/workbench/parts/terminal/node/terminal';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ILogService } from 'vs/platform/log/common/log';
import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal';
import { TerminalProcess } from 'vs/workbench/parts/terminal/node/terminalProcess';
export class ExtHostTerminal implements vscode.Terminal {
private _name: string;
private _id: number;
private _proxy: MainThreadTerminalServiceShape;
private _disposed: boolean;
private _queuedRequests: ApiRequest[];
const RENDERER_NO_PROCESS_ID = -1;
export class BaseExtHostTerminal {
public _id: number;
protected _idPromise: Promise<number>;
private _idPromiseComplete: (value: number) => any;
private _disposed: boolean = false;
private _queuedRequests: ApiRequest[] = [];
constructor(
protected _proxy: MainThreadTerminalServiceShape,
id?: number
) {
this._idPromise = new Promise<number>(c => {
if (id !== undefined) {
this._id = id;
c(id);
} else {
this._idPromiseComplete = c;
}
});
}
public dispose(): void {
if (!this._disposed) {
this._disposed = true;
this._queueApiRequest(this._proxy.$dispose, []);
}
}
protected _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
}
}
protected _queueApiRequest(callback: (...args: any[]) => void, args: any[]): void {
const request: ApiRequest = new ApiRequest(callback, args);
if (!this._id) {
this._queuedRequests.push(request);
return;
}
request.run(this._proxy, this._id);
}
public _runQueuedRequests(id: number): void {
this._id = id;
this._idPromiseComplete(id);
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
});
this._queuedRequests.length = 0;
}
}
export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Terminal {
private _pidPromise: Promise<number>;
private _pidPromiseComplete: (value: number) => any;
private readonly _onData: Emitter<string> = new Emitter<string>();
public get onData(): Event<string> {
public get onDidWriteData(): Event<string> {
// Tell the main side to start sending data if it's not already
this._proxy.$registerOnDataListener(this._id);
return this._onData && this._onData.event;
@@ -34,17 +83,17 @@ export class ExtHostTerminal implements vscode.Terminal {
constructor(
proxy: MainThreadTerminalServiceShape,
name: string = '',
id?: number
private _name: string,
id?: number,
pid?: number
) {
this._proxy = proxy;
this._name = name;
if (id) {
this._id = id;
}
this._queuedRequests = [];
super(proxy, id);
this._pidPromise = new Promise<number>(c => {
this._pidPromiseComplete = c;
if (pid === RENDERER_NO_PROCESS_ID) {
c(undefined);
} else {
this._pidPromiseComplete = c;
}
});
}
@@ -56,11 +105,7 @@ export class ExtHostTerminal implements vscode.Terminal {
waitOnExit?: boolean
): void {
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit).then((id) => {
this._id = id;
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
});
this._queuedRequests = [];
this._runQueuedRequests(id);
});
}
@@ -87,13 +132,6 @@ export class ExtHostTerminal implements vscode.Terminal {
this._queueApiRequest(this._proxy.$hide, []);
}
public dispose(): void {
if (!this._disposed) {
this._disposed = true;
this._queueApiRequest(this._proxy.$dispose, []);
}
}
public _setProcessId(processId: number): void {
// The event may fire 2 times when the panel is restored
if (this._pidPromiseComplete) {
@@ -105,34 +143,99 @@ export class ExtHostTerminal implements vscode.Terminal {
public _fireOnData(data: string): void {
this._onData.fire(data);
}
}
private _queueApiRequest(callback: (...args: any[]) => void, args: any[]) {
let request: ApiRequest = new ApiRequest(callback, args);
if (!this._id) {
this._queuedRequests.push(request);
return;
}
request.run(this._proxy, this._id);
export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vscode.TerminalRenderer {
public get name(): string { return this._name; }
public set name(newName: string) {
this._name = newName;
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererSetName, [this._name]);
}
private _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
private readonly _onInput: Emitter<string> = new Emitter<string>();
public get onDidAcceptInput(): Event<string> {
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererRegisterOnInputListener, [this._id]);
// Tell the main side to start sending data if it's not already
// this._proxy.$terminalRendererRegisterOnDataListener(this._id);
return this._onInput && this._onInput.event;
}
private _dimensions: vscode.TerminalDimensions | undefined;
public get dimensions(): vscode.TerminalDimensions { return this._dimensions; }
public set dimensions(dimensions: vscode.TerminalDimensions) {
this._checkDisposed();
this._dimensions = dimensions;
this._queueApiRequest(this._proxy.$terminalRendererSetDimensions, [dimensions]);
}
private _maximumDimensions: vscode.TerminalDimensions;
public get maximumDimensions(): vscode.TerminalDimensions {
if (!this._maximumDimensions) {
return undefined;
}
return {
rows: this._maximumDimensions.rows,
columns: this._maximumDimensions.columns
};
}
private readonly _onDidChangeMaximumDimensions: Emitter<vscode.TerminalDimensions> = new Emitter<vscode.TerminalDimensions>();
public get onDidChangeMaximumDimensions(): Event<vscode.TerminalDimensions> {
return this._onDidChangeMaximumDimensions && this._onDidChangeMaximumDimensions.event;
}
public get terminal(): ExtHostTerminal {
return this._terminal;
}
constructor(
proxy: MainThreadTerminalServiceShape,
private _name: string,
private _terminal: ExtHostTerminal
) {
super(proxy);
this._proxy.$createTerminalRenderer(this._name).then(id => {
this._runQueuedRequests(id);
(<any>this._terminal)._runQueuedRequests(id);
});
}
public write(data: string): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererWrite, [data]);
}
public _fireOnInput(data: string): void {
this._onInput.fire(data);
}
public _setMaximumDimensions(columns: number, rows: number): void {
if (this._maximumDimensions && this._maximumDimensions.columns === columns && this._maximumDimensions.rows === rows) {
return;
}
this._maximumDimensions = { columns, rows };
this._onDidChangeMaximumDimensions.fire(this.maximumDimensions);
}
}
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _proxy: MainThreadTerminalServiceShape;
private _activeTerminal: ExtHostTerminal;
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: cp.ChildProcess } = {};
private _terminalProcesses: { [id: number]: TerminalProcess } = {};
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; }
public get terminals(): ExtHostTerminal[] { return this._terminals; }
private readonly _onDidCloseTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
public get onDidCloseTerminal(): Event<vscode.Terminal> { return this._onDidCloseTerminal && this._onDidCloseTerminal.event; }
private readonly _onDidOpenTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
public get onDidOpenTerminal(): Event<vscode.Terminal> { return this._onDidOpenTerminal && this._onDidOpenTerminal.event; }
private readonly _onDidChangeActiveTerminal: Emitter<vscode.Terminal | undefined> = new Emitter<vscode.Terminal | undefined>();
public get onDidChangeActiveTerminal(): Event<vscode.Terminal | undefined> { return this._onDidChangeActiveTerminal && this._onDidChangeActiveTerminal.event; }
constructor(
mainContext: IMainContext,
@@ -143,53 +246,108 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, name);
const terminal = new ExtHostTerminal(this._proxy, name);
terminal.create(shellPath, shellArgs);
this._terminals.push(terminal);
return terminal;
}
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, options.name);
const terminal = new ExtHostTerminal(this._proxy, options.name);
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env /*, options.waitOnExit*/);
this._terminals.push(terminal);
return terminal;
}
public $acceptTerminalProcessData(id: number, data: string): void {
let index = this._getTerminalIndexById(id);
if (index === null) {
return;
public createTerminalRenderer(name: string): vscode.TerminalRenderer {
const terminal = new ExtHostTerminal(this._proxy, name);
terminal._setProcessId(undefined);
this._terminals.push(terminal);
const renderer = new ExtHostTerminalRenderer(this._proxy, name, terminal);
this._terminalRenderers.push(renderer);
return renderer;
}
public $acceptActiveTerminalChanged(id: number | null): void {
const original = this._activeTerminal;
if (id === null) {
this._activeTerminal = undefined;
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
}
}
this._performTerminalIdAction(id, terminal => {
if (terminal) {
this._activeTerminal = terminal;
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
}
}
});
}
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);
}
}
public $acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void {
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._setMaximumDimensions(cols, rows);
}
}
public $acceptTerminalRendererInput(id: number, data: string): void {
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._fireOnInput(data);
}
const terminal = this._terminals[index];
terminal._fireOnData(data);
}
public $acceptTerminalClosed(id: number): void {
let index = this._getTerminalIndexById(id);
const index = this._getTerminalObjectIndexById(this.terminals, id);
if (index === null) {
return;
}
let terminal = this._terminals.splice(index, 1)[0];
const terminal = this._terminals.splice(index, 1)[0];
this._onDidCloseTerminal.fire(terminal);
}
public $acceptTerminalOpened(id: number, name: string): void {
let index = this._getTerminalIndexById(id);
const index = this._getTerminalObjectIndexById(this._terminals, id);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
this._onDidOpenTerminal.fire(this.terminals[index]);
return;
}
let terminal = new ExtHostTerminal(this._proxy, name, id);
const renderer = this._getTerminalRendererById(id);
const terminal = new ExtHostTerminal(this._proxy, name, id, renderer ? RENDERER_NO_PROCESS_ID : undefined);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
}
public $acceptTerminalProcessId(id: number, processId: number): void {
this._performTerminalIdAction(id, terminal => terminal._setProcessId(processId));
}
private _performTerminalIdAction(id: number, callback: (terminal: ExtHostTerminal) => void): void {
let terminal = this._getTerminalById(id);
if (terminal) {
terminal._setProcessId(processId);
callback(terminal);
} else {
// Retry one more time in case the terminal has not yet been initialized.
setTimeout(() => {
terminal = this._getTerminalById(id);
if (terminal) {
callback(terminal);
}
}, EXT_HOST_CREATION_DELAY);
}
}
@@ -199,7 +357,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
const terminalConfig = this._extHostConfiguration.getConfiguration('terminal.integrated');
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
if (!shellLaunchConfig.executable) {
// TODO: This duplicates some of TerminalConfigHelper.mergeDefaultShellPathAndArgs and should be merged
// this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
@@ -223,61 +380,48 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
// const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
// const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
// shellLaunchConfig.env = envFromShell;
// Merge process env with the env from config
const parentEnv = { ...process.env };
// terminalEnvironment.mergeEnvironments(parentEnv, envFromConfig);
const env = { ...process.env };
// terminalEnvironment.mergeEnvironments(env, envFromConfig);
terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, initialCwd, locale, cols, rows);
let cwd = Uri.parse(require.toUrl('../../parts/terminal/node')).fsPath;
const options = { env, cwd, execArgv: [] };
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
terminalEnvironment.addTerminalEnvironmentKeys(env, locale);
// Fork the process and listen for messages
this._logService.debug(`Terminal process launching on ext host`, options);
this._terminalProcesses[id] = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], options);
this._terminalProcesses[id].on('message', (message: IMessageFromTerminalProcess) => {
switch (message.type) {
case 'pid': this._proxy.$sendProcessPid(id, <number>message.content); break;
case 'title': this._proxy.$sendProcessTitle(id, <string>message.content); break;
case 'data': this._proxy.$sendProcessData(id, <string>message.content); break;
}
});
this._terminalProcesses[id].on('exit', (exitCode) => this._onProcessExit(id, exitCode));
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
this._terminalProcesses[id] = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env);
this._terminalProcesses[id].onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid));
this._terminalProcesses[id].onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title));
this._terminalProcesses[id].onProcessData(data => this._proxy.$sendProcessData(id, data));
this._terminalProcesses[id].onProcessExit((exitCode) => this._onProcessExit(id, exitCode));
}
public $acceptProcessInput(id: number, data: string): void {
if (this._terminalProcesses[id].connected) {
this._terminalProcesses[id].send({ event: 'input', data });
}
this._terminalProcesses[id].input(data);
}
public $acceptProcessResize(id: number, cols: number, rows: number): void {
if (this._terminalProcesses[id].connected) {
try {
this._terminalProcesses[id].send({ event: 'resize', cols, rows });
} catch (error) {
// We tried to write to a closed pipe / channel.
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
throw (error);
}
try {
this._terminalProcesses[id].resize(cols, rows);
} catch (error) {
// We tried to write to a closed pipe / channel.
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
throw (error);
}
}
}
public $acceptProcessShutdown(id: number): void {
if (this._terminalProcesses[id].connected) {
this._terminalProcesses[id].send({ event: 'shutdown' });
}
this._terminalProcesses[id].shutdown();
}
private _onProcessExit(id: number, exitCode: number): void {
// Remove listeners
const process = this._terminalProcesses[id];
process.removeAllListeners('message');
process.removeAllListeners('exit');
this._terminalProcesses[id].dispose();
// Remove process reference
delete this._terminalProcesses[id];
@@ -286,16 +430,43 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._proxy.$sendProcessExit(id, exitCode);
}
private _getTerminalById(id: number): ExtHostTerminal {
let index = this._getTerminalIndexById(id);
return index !== null ? this._terminals[index] : null;
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal> {
return new Promise(c => {
if (retries === 0) {
c(undefined);
return;
}
const terminal = this._getTerminalById(id);
if (terminal) {
c(terminal);
} 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);
}
});
}
private _getTerminalIndexById(id: number): number {
private _getTerminalById(id: number): ExtHostTerminal {
return this._getTerminalObjectById(this._terminals, id);
}
private _getTerminalRendererById(id: number): ExtHostTerminalRenderer {
return this._getTerminalObjectById(this._terminalRenderers, id);
}
private _getTerminalObjectById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): T {
const index = this._getTerminalObjectIndexById(array, id);
return index !== null ? array[index] : null;
}
private _getTerminalObjectIndexById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): number {
let index: number = null;
this._terminals.some((terminal, i) => {
// TODO: This shouldn't be cas
let thisId = (<any>terminal)._id;
array.some((item, i) => {
const thisId = item._id;
if (thisId === id) {
index = i;
return true;

View File

@@ -345,7 +345,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
}
@deprecated('TextEditor.show') show(column: vscode.ViewColumn) {
this._proxy.$tryShowEditor(this._id, TypeConverters.fromViewColumn(column));
this._proxy.$tryShowEditor(this._id, TypeConverters.ViewColumn.from(column));
}
@deprecated('TextEditor.hide') hide() {
@@ -467,14 +467,14 @@ export class ExtHostTextEditor implements vscode.TextEditor {
this._runOnProxy(
() => this._proxy.$tryRevealRange(
this._id,
TypeConverters.fromRange(range),
TypeConverters.Range.from(range),
(revealType || TextEditorRevealType.Default)
)
);
}
private _trySetSelection(): TPromise<vscode.TextEditor> {
let selection = this._selections.map(TypeConverters.fromSelection);
let selection = this._selections.map(TypeConverters.Selection.from);
return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection));
}
@@ -497,6 +497,11 @@ export class ExtHostTextEditor implements vscode.TextEditor {
private _applyEdit(editBuilder: TextEditorEdit): TPromise<boolean> {
let editData = editBuilder.finalize();
// return when there is nothing to do
if (editData.edits.length === 0 && !editData.setEndOfLine) {
return TPromise.wrap(true);
}
// check that the edits are not overlapping (i.e. illegal)
let editRanges = editData.edits.map(edit => edit.range);
@@ -530,7 +535,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
// prepare data for serialization
let edits: ISingleEditOperation[] = editData.edits.map((edit) => {
return {
range: TypeConverters.fromRange(edit.range),
range: TypeConverters.Range.from(edit.range),
text: edit.text,
forceMoveMarkers: edit.forceMoveMarkers
};
@@ -550,21 +555,21 @@ export class ExtHostTextEditor implements vscode.TextEditor {
let ranges: IRange[];
if (!where || (Array.isArray(where) && where.length === 0)) {
ranges = this._selections.map(TypeConverters.fromRange);
ranges = this._selections.map(TypeConverters.Range.from);
} else if (where instanceof Position) {
const { lineNumber, column } = TypeConverters.fromPosition(where);
const { lineNumber, column } = TypeConverters.Position.from(where);
ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
} else if (where instanceof Range) {
ranges = [TypeConverters.fromRange(where)];
ranges = [TypeConverters.Range.from(where)];
} else {
ranges = [];
for (const posOrRange of where) {
if (posOrRange instanceof Range) {
ranges.push(TypeConverters.fromRange(posOrRange));
ranges.push(TypeConverters.Range.from(posOrRange));
} else {
const { lineNumber, column } = TypeConverters.fromPosition(posOrRange);
const { lineNumber, column } = TypeConverters.Position.from(posOrRange);
ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
}
}

View File

@@ -11,8 +11,7 @@ import { TextEditorSelectionChangeKind } from './extHostTypes';
import * as TypeConverters from './extHostTypeConverters';
import { TextEditorDecorationType, ExtHostTextEditor } from './extHostTextEditor';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { MainContext, MainThreadTextEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IMainContext, WorkspaceEditDto, IEditorPropertiesChangeData } from './extHost.protocol';
import { MainContext, MainThreadTextEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IMainContext, IEditorPropertiesChangeData } from './extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostEditors implements ExtHostEditorsShape {
@@ -61,19 +60,18 @@ export class ExtHostEditors implements ExtHostEditorsShape {
let options: ITextDocumentShowOptions;
if (typeof columnOrOptions === 'number') {
options = {
position: TypeConverters.fromViewColumn(columnOrOptions),
position: TypeConverters.ViewColumn.from(columnOrOptions),
preserveFocus
};
} else if (typeof columnOrOptions === 'object') {
options = {
position: TypeConverters.fromViewColumn(columnOrOptions.viewColumn),
position: TypeConverters.ViewColumn.from(columnOrOptions.viewColumn),
preserveFocus: columnOrOptions.preserveFocus,
selection: typeof columnOrOptions.selection === 'object' ? TypeConverters.fromRange(columnOrOptions.selection) : undefined,
selection: typeof columnOrOptions.selection === 'object' ? TypeConverters.Range.from(columnOrOptions.selection) : undefined,
pinned: typeof columnOrOptions.preview === 'boolean' ? !columnOrOptions.preview : undefined
};
} else {
options = {
position: EditorPosition.ONE,
preserveFocus: false
};
}
@@ -93,23 +91,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
}
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
const dto: WorkspaceEditDto = { edits: [] };
for (let entry of edit.entries()) {
let [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
let doc = this._extHostDocumentsAndEditors.getDocument(uri.toString());
dto.edits.push({
resource: uri,
modelVersionId: doc && doc.version,
edits: uriOrEdits.map(TypeConverters.TextEdit.from)
});
// } else {
// dto.edits.push({ oldUri: uri, newUri: uriOrEdits });
}
}
const dto = TypeConverters.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
return this._proxy.$tryApplyWorkspaceEdit(dto);
}
@@ -123,11 +105,11 @@ export class ExtHostEditors implements ExtHostEditorsShape {
textEditor._acceptOptions(data.options);
}
if (data.selections) {
const selections = data.selections.selections.map(TypeConverters.toSelection);
const selections = data.selections.selections.map(TypeConverters.Selection.to);
textEditor._acceptSelections(selections);
}
if (data.visibleRanges) {
const visibleRanges = data.visibleRanges.map(TypeConverters.toRange);
const visibleRanges = data.visibleRanges.map(TypeConverters.Range.to);
textEditor._acceptVisibleRanges(visibleRanges);
}
@@ -140,7 +122,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
}
if (data.selections) {
const kind = TextEditorSelectionChangeKind.fromValue(data.selections.source);
const selections = data.selections.selections.map(TypeConverters.toSelection);
const selections = data.selections.selections.map(TypeConverters.Selection.to);
this._onDidChangeTextEditorSelection.fire({
textEditor,
selections,
@@ -148,7 +130,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
});
}
if (data.visibleRanges) {
const visibleRanges = data.visibleRanges.map(TypeConverters.toRange);
const visibleRanges = data.visibleRanges.map(TypeConverters.Range.to);
this._onDidChangeTextEditorVisibleRanges.fire({
textEditor,
visibleRanges
@@ -159,7 +141,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
$acceptEditorPositionData(data: ITextEditorPositionData): void {
for (let id in data) {
let textEditor = this._extHostDocumentsAndEditors.getEditor(id);
let viewColumn = TypeConverters.toViewColumn(data[id]);
let viewColumn = TypeConverters.ViewColumn.to(data[id]);
if (textEditor.viewColumn !== viewColumn) {
textEditor._acceptViewColumn(viewColumn);
this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn });

View File

@@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
import * as vscode from 'vscode';
import { basename } from 'vs/base/common/paths';
import URI from 'vs/base/common/uri';
import { debounceEvent } from 'vs/base/common/event';
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
@@ -17,6 +17,8 @@ import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHos
import { asWinJsPromise } from 'vs/base/common/async';
import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { equals } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
// {{SQL CARBON EDIT}}
export type TreeItemHandle = string;
@@ -27,7 +29,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
constructor(
private _proxy: MainThreadTreeViewsShape,
private commands: ExtHostCommands
private commands: ExtHostCommands,
private logService: ILogService
) {
commands.registerArgumentProcessor({
processArgument: arg => {
@@ -50,7 +53,13 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
}
const treeView = this.createExtHostTreeViewer(viewId, options.treeDataProvider);
return {
reveal: (element: T, options?: { select?: boolean }): Thenable<void> => {
get onDidCollapseElement() { return treeView.onDidCollapseElement; },
get onDidExpandElement() { return treeView.onDidExpandElement; },
get selection() { return treeView.selectedElements; },
get onDidChangeSelection() { return treeView.onDidChangeSelection; },
get visible() { return treeView.visible; },
get onDidChangeVisibility() { return treeView.onDidChangeVisibility; },
reveal: (element: T, options?: { select?: boolean, focus?: boolean }): Thenable<void> => {
return treeView.reveal(element, options);
},
dispose: () => {
@@ -68,8 +77,32 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
return treeView.getChildren(treeItemHandle);
}
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId));
}
treeView.setExpanded(treeItemHandle, expanded);
}
$setSelection(treeViewId: string, treeItemHandles: string[]): void {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId));
}
treeView.setSelection(treeItemHandles);
}
$setVisible(treeViewId: string, isVisible: boolean): void {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId));
}
treeView.setVisible(isVisible);
}
private createExtHostTreeViewer<T>(id: string, dataProvider: vscode.TreeDataProvider<T>): ExtHostTreeView<T> {
const treeView = new ExtHostTreeView<T>(id, dataProvider, this._proxy, this.commands.converter);
const treeView = new ExtHostTreeView<T>(id, dataProvider, this._proxy, this.commands.converter, this.logService);
this.treeViews.set(id, treeView);
return treeView;
}
@@ -98,14 +131,46 @@ export class ExtHostTreeView<T> extends Disposable {
// {{SQL CARBON EDIT}}
protected nodes: Map<T, TreeNode> = new Map<T, TreeNode>();
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
private _visible: boolean = false;
get visible(): boolean { return this._visible; }
private _selectedHandles: TreeItemHandle[] = [];
get selectedElements(): T[] { return this._selectedHandles.map(handle => this.getExtensionElement(handle)).filter(element => !isUndefinedOrNull(element)); }
private _onDidExpandElement: Emitter<vscode.TreeViewExpansionEvent<T>> = this._register(new Emitter<vscode.TreeViewExpansionEvent<T>>());
readonly onDidExpandElement: Event<vscode.TreeViewExpansionEvent<T>> = this._onDidExpandElement.event;
private _onDidCollapseElement: Emitter<vscode.TreeViewExpansionEvent<T>> = this._register(new Emitter<vscode.TreeViewExpansionEvent<T>>());
readonly onDidCollapseElement: Event<vscode.TreeViewExpansionEvent<T>> = this._onDidCollapseElement.event;
private _onDidChangeSelection: Emitter<vscode.TreeViewSelectionChangeEvent<T>> = this._register(new Emitter<vscode.TreeViewSelectionChangeEvent<T>>());
readonly onDidChangeSelection: Event<vscode.TreeViewSelectionChangeEvent<T>> = this._onDidChangeSelection.event;
private _onDidChangeVisibility: Emitter<vscode.TreeViewVisibilityChangeEvent> = this._register(new Emitter<vscode.TreeViewVisibilityChangeEvent>());
readonly onDidChangeVisibility: Event<vscode.TreeViewVisibilityChangeEvent> = this._onDidChangeVisibility.event;
private refreshPromise: TPromise<void> = TPromise.as(null);
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService) {
super();
// {{SQL CARBON EDIT}}
if (this.proxy) {
this.proxy.$registerTreeViewDataProvider(viewId);
}
if (this.dataProvider.onDidChangeTreeData) {
this._register(debounceEvent<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this.refresh(elements)));
let refreshingPromise, promiseCallback;
this._register(debounceEvent<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => {
if (!refreshingPromise) {
// New refresh has started
refreshingPromise = new TPromise((c, e) => promiseCallback = c);
this.refreshPromise = this.refreshPromise.then(() => refreshingPromise);
}
return last ? [...last, current] : [current];
}, 200)(elements => {
const _promiseCallback = promiseCallback;
refreshingPromise = null;
this.refresh(elements);
}));
}
}
@@ -125,13 +190,43 @@ export class ExtHostTreeView<T> extends Disposable {
return this.elements.get(treeItemHandle);
}
reveal(element: T, options?: { select?: boolean }): TPromise<void> {
reveal(element: T, options?: { select?: boolean, focus?: boolean }): TPromise<void> {
options = options ? options : { select: true, focus: false };
const select = isUndefinedOrNull(options.select) ? true : options.select;
const focus = isUndefinedOrNull(options.focus) ? false : options.focus;
if (typeof this.dataProvider.getParent !== 'function') {
return TPromise.wrapError(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' mehtod`));
return TPromise.wrapError(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`));
}
return this.resolveUnknownParentChain(element)
return this.refreshPromise
.then(() => this.resolveUnknownParentChain(element))
.then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1])
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), options)));
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus })), error => this.logService.error(error));
}
setExpanded(treeItemHandle: TreeItemHandle, expanded: boolean): void {
const element = this.getExtensionElement(treeItemHandle);
if (element) {
if (expanded) {
this._onDidExpandElement.fire(Object.freeze({ element }));
} else {
this._onDidCollapseElement.fire(Object.freeze({ element }));
}
}
}
setSelection(treeItemHandles: TreeItemHandle[]): void {
if (!equals(this._selectedHandles, treeItemHandles)) {
this._selectedHandles = treeItemHandles;
this._onDidChangeSelection.fire(Object.freeze({ selection: this.selectedElements }));
}
}
setVisible(visible: boolean): void {
if (visible !== this._visible) {
this._visible = visible;
this._onDidChangeVisibility.fire(Object.freeze({ visible: this._visible }));
}
}
// {{SQL CARBON EDIT}}
@@ -204,7 +299,7 @@ export class ExtHostTreeView<T> extends Disposable {
}
// {{SQL CARBON EDIT}}
protected refresh(elements: T[]): void {
private refresh(elements: T[]): void {
const hasRoot = elements.some(element => !element);
if (hasRoot) {
this.clearAll(); // clear cache
@@ -318,7 +413,7 @@ export class ExtHostTreeView<T> extends Disposable {
return item;
}
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode, first?: boolean): TreeItemHandle {
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode, returnFirst?: boolean): TreeItemHandle {
if (id) {
return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`;
}
@@ -329,14 +424,20 @@ export class ExtHostTreeView<T> extends Disposable {
const existingHandle = this.nodes.has(element) ? this.nodes.get(element).item.handle : void 0;
const childrenNodes = (this.getChildrenNodes(parent) || []);
for (let counter = 0; counter <= childrenNodes.length; counter++) {
const handle = `${prefix}/${counter}:${elementId}`;
if (first || !this.elements.has(handle) || existingHandle === handle) {
return handle;
let handle: TreeItemHandle;
let counter = 0;
do {
handle = `${prefix}/${counter}:${elementId}`;
if (returnFirst || !this.elements.has(handle) || existingHandle === handle) {
// Return first if asked for or
// Return if handle does not exist or
// Return if handle is being reused
break;
}
}
counter++;
} while (counter <= childrenNodes.length);
throw new Error('This should not be reached');
return handle;
}
private getLightIconPath(extensionTreeItem: vscode.TreeItem): string {
@@ -414,7 +515,7 @@ export class ExtHostTreeView<T> extends Disposable {
}
}
}
node.children = [];
node.children = void 0;
} else {
this.clearAll();
}

View File

@@ -6,21 +6,25 @@
import * as modes from 'vs/editor/common/modes';
import * as types from './extHostTypes';
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import * as search from 'vs/workbench/parts/search/common/search';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence } from 'vs/editor/common/model';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
import { ProgressLocation as MainProgressLocation } from 'vs/workbench/services/progress/common/progress';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
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 { LanguageSelector, LanguageFilter } from 'vs/editor/common/modes/languageSelector';
import { WorkspaceEditDto, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { MarkerSeverity, IRelatedInformation, IMarkerData } from 'vs/platform/markers/common/markers';
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';
export interface PositionLike {
line: number;
@@ -36,131 +40,145 @@ export interface SelectionLike extends RangeLike {
anchor: PositionLike;
active: PositionLike;
}
export namespace Selection {
export function toSelection(selection: ISelection): types.Selection {
let { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection;
let start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1);
let end = new types.Position(positionLineNumber - 1, positionColumn - 1);
return new types.Selection(start, end);
export function to(selection: ISelection): types.Selection {
let { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection;
let start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1);
let end = new types.Position(positionLineNumber - 1, positionColumn - 1);
return new types.Selection(start, end);
}
export function from(selection: SelectionLike): ISelection {
let { anchor, active } = selection;
return {
selectionStartLineNumber: anchor.line + 1,
selectionStartColumn: anchor.character + 1,
positionLineNumber: active.line + 1,
positionColumn: active.character + 1
};
}
}
export namespace Range {
export function from(range: RangeLike): IRange {
if (!range) {
return undefined;
}
let { start, end } = range;
return {
startLineNumber: start.line + 1,
startColumn: start.character + 1,
endLineNumber: end.line + 1,
endColumn: end.character + 1
};
}
export function to(range: IRange): types.Range {
if (!range) {
return undefined;
}
let { startLineNumber, startColumn, endLineNumber, endColumn } = range;
return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1);
}
}
export function fromSelection(selection: SelectionLike): ISelection {
let { anchor, active } = selection;
return {
selectionStartLineNumber: anchor.line + 1,
selectionStartColumn: anchor.character + 1,
positionLineNumber: active.line + 1,
positionColumn: active.character + 1
};
export namespace Position {
export function to(position: IPosition): types.Position {
return new types.Position(position.lineNumber - 1, position.column - 1);
}
export function from(position: types.Position): IPosition {
return { lineNumber: position.line + 1, column: position.character + 1 };
}
}
export function fromRange(range: RangeLike): IRange {
if (!range) {
export namespace DiagnosticTag {
export function from(value: vscode.DiagnosticTag): MarkerTag {
switch (value) {
case types.DiagnosticTag.Unnecessary:
return MarkerTag.Unnecessary;
}
return undefined;
}
let { start, end } = range;
return {
startLineNumber: start.line + 1,
startColumn: start.character + 1,
endLineNumber: end.line + 1,
endColumn: end.character + 1
};
}
export function toRange(range: IRange): types.Range {
if (!range) {
export namespace Diagnostic {
export function from(value: vscode.Diagnostic): IMarkerData {
return {
...Range.from(value.range),
message: value.message,
source: value.source,
code: String(value.code),
severity: DiagnosticSeverity.from(value.severity),
relatedInformation: value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.from),
tags: Array.isArray(value.tags) ? value.tags.map(DiagnosticTag.from) : undefined,
};
}
}
export namespace DiagnosticRelatedInformation {
export function from(value: types.DiagnosticRelatedInformation): IRelatedInformation {
return {
...Range.from(value.location.range),
message: value.message,
resource: value.location.uri
};
}
export function to(value: IRelatedInformation): types.DiagnosticRelatedInformation {
return new types.DiagnosticRelatedInformation(new types.Location(value.resource, Range.to(value)), value.message);
}
}
export namespace DiagnosticSeverity {
export function from(value: number): MarkerSeverity {
switch (value) {
case types.DiagnosticSeverity.Error:
return MarkerSeverity.Error;
case types.DiagnosticSeverity.Warning:
return MarkerSeverity.Warning;
case types.DiagnosticSeverity.Information:
return MarkerSeverity.Info;
case types.DiagnosticSeverity.Hint:
return MarkerSeverity.Hint;
}
return MarkerSeverity.Error;
}
export function to(value: MarkerSeverity): types.DiagnosticSeverity {
switch (value) {
case MarkerSeverity.Info:
return types.DiagnosticSeverity.Information;
case MarkerSeverity.Warning:
return types.DiagnosticSeverity.Warning;
case MarkerSeverity.Error:
return types.DiagnosticSeverity.Error;
case MarkerSeverity.Hint:
return types.DiagnosticSeverity.Hint;
}
return types.DiagnosticSeverity.Error;
}
}
export namespace ViewColumn {
export function from(column?: vscode.ViewColumn): EditorViewColumn {
if (typeof column === 'number' && column >= types.ViewColumn.One) {
return column - 1; // adjust zero index (ViewColumn.ONE => 0)
}
if (column === types.ViewColumn.Beside) {
return SIDE_GROUP;
}
return ACTIVE_GROUP; // default is always the active group
}
export function to(position?: EditorViewColumn): vscode.ViewColumn {
if (typeof position === 'number' && position >= 0) {
return position + 1; // adjust to index (ViewColumn.ONE => 1)
}
return undefined;
}
let { startLineNumber, startColumn, endLineNumber, endColumn } = range;
return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1);
}
export function toPosition(position: IPosition): types.Position {
return new types.Position(position.lineNumber - 1, position.column - 1);
}
export function fromPosition(position: types.Position): IPosition {
return { lineNumber: position.line + 1, column: position.character + 1 };
}
export function fromDiagnostic(value: vscode.Diagnostic): IMarkerData {
return {
...fromRange(value.range),
message: value.message,
source: value.source,
code: String(value.code),
severity: fromDiagnosticSeverity(value.severity),
relatedInformation: value.relatedInformation && value.relatedInformation.map(fromDiagnosticRelatedInformation)
};
}
export function fromDiagnosticRelatedInformation(value: types.DiagnosticRelatedInformation): IRelatedInformation {
return {
...fromRange(value.location.range),
message: value.message,
resource: value.location.uri
};
}
export function toDiagnosticRelatedInformation(value: IRelatedInformation): types.DiagnosticRelatedInformation {
return new types.DiagnosticRelatedInformation(new types.Location(value.resource, toRange(value)), value.message);
}
export function fromDiagnosticSeverity(value: number): MarkerSeverity {
switch (value) {
case types.DiagnosticSeverity.Error:
return MarkerSeverity.Error;
case types.DiagnosticSeverity.Warning:
return MarkerSeverity.Warning;
case types.DiagnosticSeverity.Information:
return MarkerSeverity.Info;
case types.DiagnosticSeverity.Hint:
return MarkerSeverity.Hint;
}
return MarkerSeverity.Error;
}
export function toDiagnosticSeverty(value: MarkerSeverity): types.DiagnosticSeverity {
switch (value) {
case MarkerSeverity.Info:
return types.DiagnosticSeverity.Information;
case MarkerSeverity.Warning:
return types.DiagnosticSeverity.Warning;
case MarkerSeverity.Error:
return types.DiagnosticSeverity.Error;
case MarkerSeverity.Hint:
return types.DiagnosticSeverity.Hint;
}
return types.DiagnosticSeverity.Error;
}
export function fromViewColumn(column?: vscode.ViewColumn): EditorPosition {
let editorColumn = EditorPosition.ONE;
if (typeof column !== 'number') {
// stick with ONE
} else if (column === <number>types.ViewColumn.Two) {
editorColumn = EditorPosition.TWO;
} else if (column === <number>types.ViewColumn.Three) {
editorColumn = EditorPosition.THREE;
} else if (column === <number>types.ViewColumn.Active) {
editorColumn = undefined;
}
return editorColumn;
}
export function toViewColumn(position?: EditorPosition): vscode.ViewColumn {
if (typeof position !== 'number') {
return undefined;
}
if (position === EditorPosition.ONE) {
return <number>types.ViewColumn.One;
} else if (position === EditorPosition.TWO) {
return <number>types.ViewColumn.Two;
} else if (position === EditorPosition.THREE) {
return <number>types.ViewColumn.Three;
}
return undefined;
}
function isDecorationOptions(something: any): something is vscode.DecorationOptions {
@@ -221,7 +239,7 @@ export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.Deco
if (isDecorationOptionsArr(ranges)) {
return ranges.map(r => {
return {
range: fromRange(r.range),
range: Range.from(r.range),
hoverMessage: Array.isArray(r.hoverMessage) ? MarkdownString.fromMany(r.hoverMessage) : r.hoverMessage && MarkdownString.from(r.hoverMessage),
renderOptions: <any> /* URI vs Uri */r.renderOptions
};
@@ -229,7 +247,7 @@ export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.Deco
} else {
return ranges.map((r): IDecorationOptions => {
return {
range: fromRange(r)
range: Range.from(r)
};
});
}
@@ -241,29 +259,30 @@ export const TextEdit = {
return <modes.TextEdit>{
text: edit.newText,
eol: EndOfLine.from(edit.newEol),
range: fromRange(edit.range)
range: Range.from(edit.range)
};
},
to(edit: modes.TextEdit): types.TextEdit {
let result = new types.TextEdit(toRange(edit.range), edit.text);
let result = new types.TextEdit(Range.to(edit.range), edit.text);
result.newEol = EndOfLine.to(edit.eol);
return result;
}
};
export namespace WorkspaceEdit {
export function from(value: vscode.WorkspaceEdit): modes.WorkspaceEdit {
const result: modes.WorkspaceEdit = {
export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors): WorkspaceEditDto {
const result: WorkspaceEditDto = {
edits: []
};
for (const entry of value.entries()) {
for (const entry of (value as types.WorkspaceEdit)._allEntries()) {
const [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
// text edits
result.edits.push({ resource: uri, edits: uriOrEdits.map(TextEdit.from) });
let doc = documents ? documents.getDocument(uri.toString()) : undefined;
result.edits.push(<ResourceTextEditDto>{ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) });
} else {
// resource edits
result.edits.push({ oldUri: uri, newUri: uriOrEdits });
result.edits.push(<ResourceFileEditDto>{ oldUri: uri, newUri: uriOrEdits, options: entry[2] });
}
}
return result;
@@ -277,11 +296,12 @@ export namespace WorkspaceEdit {
URI.revive((<ResourceTextEditDto>edit).resource),
<types.TextEdit[]>(<ResourceTextEditDto>edit).edits.map(TextEdit.to)
);
// } else {
// result.renameResource(
// URI.revive((<ResourceFileEditDto>edit).oldUri),
// URI.revive((<ResourceFileEditDto>edit).newUri)
// );
} else {
result.renameFile(
URI.revive((<ResourceFileEditDto>edit).oldUri),
URI.revive((<ResourceFileEditDto>edit).newUri),
(<ResourceFileEditDto>edit).options
);
}
}
return result;
@@ -333,50 +353,105 @@ export namespace SymbolKind {
}
}
export function fromSymbolInformation(info: vscode.SymbolInformation): modes.SymbolInformation {
return <modes.SymbolInformation>{
name: info.name,
kind: SymbolKind.from(info.kind),
containerName: info.containerName,
location: location.from(info.location)
};
export namespace WorkspaceSymbol {
export function from(info: vscode.SymbolInformation): search.IWorkspaceSymbol {
return <search.IWorkspaceSymbol>{
name: info.name,
kind: SymbolKind.from(info.kind),
containerName: info.containerName,
location: location.from(info.location)
};
}
export function to(info: search.IWorkspaceSymbol): types.SymbolInformation {
return new types.SymbolInformation(
info.name,
SymbolKind.to(info.kind),
info.containerName,
location.to(info.location)
);
}
}
export function toSymbolInformation(bearing: modes.SymbolInformation): types.SymbolInformation {
return new types.SymbolInformation(
bearing.name,
SymbolKind.to(bearing.kind),
bearing.containerName,
location.to(bearing.location)
);
export namespace DocumentSymbol {
export function from(info: vscode.DocumentSymbol): modes.DocumentSymbol {
let result: modes.DocumentSymbol = {
name: info.name,
detail: info.detail,
range: Range.from(info.range),
selectionRange: Range.from(info.selectionRange),
kind: SymbolKind.from(info.kind)
};
if (info.children) {
result.children = info.children.map(from);
}
return result;
}
export function to(info: modes.DocumentSymbol): vscode.DocumentSymbol {
let result = new types.DocumentSymbol(
info.name,
info.detail,
SymbolKind.to(info.kind),
Range.to(info.range),
Range.to(info.selectionRange),
);
if (info.children) {
result.children = info.children.map(to) as any;
}
return result;
}
}
export const location = {
from(value: vscode.Location): modes.Location {
return {
range: value.range && fromRange(value.range),
range: value.range && Range.from(value.range),
uri: value.uri
};
},
to(value: modes.Location): types.Location {
return new types.Location(value.uri, toRange(value.range));
return new types.Location(value.uri, Range.to(value.range));
}
};
export function fromHover(hover: vscode.Hover): modes.Hover {
return <modes.Hover>{
range: fromRange(hover.range),
contents: MarkdownString.fromMany(hover.contents)
};
export namespace DefinitionLink {
export function from(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink {
const definitionLink = <vscode.DefinitionLink>value;
const location = <vscode.Location>value;
return {
origin: definitionLink.originSelectionRange
? Range.from(definitionLink.originSelectionRange)
: undefined,
uri: definitionLink.targetUri ? definitionLink.targetUri : location.uri,
range: Range.from(definitionLink.targetRange ? definitionLink.targetRange : location.range),
selectionRange: definitionLink.targetSelectionRange
? Range.from(definitionLink.targetSelectionRange)
: undefined,
};
}
}
export function toHover(info: modes.Hover): types.Hover {
return new types.Hover(info.contents.map(MarkdownString.to), toRange(info.range));
}
export namespace Hover {
export function from(hover: vscode.Hover): modes.Hover {
return <modes.Hover>{
range: Range.from(hover.range),
contents: MarkdownString.fromMany(hover.contents)
};
}
export function toDocumentHighlight(occurrence: modes.DocumentHighlight): types.DocumentHighlight {
return new types.DocumentHighlight(toRange(occurrence.range), occurrence.kind);
export function to(info: modes.Hover): types.Hover {
return new types.Hover(info.contents.map(MarkdownString.to), Range.to(info.range));
}
}
export namespace DocumentHighlight {
export function from(documentHighlight: vscode.DocumentHighlight): modes.DocumentHighlight {
return {
range: Range.from(documentHighlight.range),
kind: documentHighlight.kind
};
}
export function to(occurrence: modes.DocumentHighlight): types.DocumentHighlight {
return new types.DocumentHighlight(Range.to(occurrence.range), occurrence.kind);
}
}
export namespace CompletionTriggerKind {
@@ -454,6 +529,7 @@ export namespace Suggest {
result.documentation = htmlContent.isMarkdownString(suggestion.documentation) ? MarkdownString.to(suggestion.documentation) : suggestion.documentation;
result.sortText = suggestion.sortText;
result.filterText = suggestion.filterText;
result.preselect = suggestion.preselect;
// 'overwrite[Before|After]'-logic
let overwriteBefore = (typeof suggestion.overwriteBefore === 'number') ? suggestion.overwriteBefore : 0;
@@ -535,13 +611,13 @@ export namespace DocumentLink {
export function from(link: vscode.DocumentLink): modes.ILink {
return {
range: fromRange(link.range),
range: Range.from(link.range),
url: link.target && link.target.toString()
};
}
export function to(link: modes.ILink): vscode.DocumentLink {
return new types.DocumentLink(toRange(link.range), link.url && URI.parse(link.url));
return new types.DocumentLink(Range.to(link.range), link.url && URI.parse(link.url));
}
}
@@ -649,57 +725,58 @@ export namespace FoldingRangeKind {
}
}
export function toTextEditorOptions(options?: vscode.TextDocumentShowOptions): ITextEditorOptions {
if (options) {
return {
pinned: typeof options.preview === 'boolean' ? !options.preview : undefined,
preserveFocus: options.preserveFocus,
selection: typeof options.selection === 'object' ? fromRange(options.selection) : undefined
} as ITextEditorOptions;
export namespace TextEditorOptions {
export function from(options?: vscode.TextDocumentShowOptions): ITextEditorOptions {
if (options) {
return {
pinned: typeof options.preview === 'boolean' ? !options.preview : undefined,
preserveFocus: options.preserveFocus,
selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined
} as ITextEditorOptions;
}
return undefined;
}
return undefined;
}
export function toGlobPattern(pattern: vscode.GlobPattern): string | IRelativePattern {
if (typeof pattern === 'string') {
return pattern;
export namespace GlobPattern {
export function from(pattern: vscode.GlobPattern): string | IRelativePattern {
if (typeof pattern === 'string') {
return pattern;
}
if (isRelativePattern(pattern)) {
return new types.RelativePattern(pattern.base, pattern.pattern);
}
return pattern; // preserve `undefined` and `null`
}
if (isRelativePattern(pattern)) {
return new types.RelativePattern(pattern.base, pattern.pattern);
function isRelativePattern(obj: any): obj is vscode.RelativePattern {
const rp = obj as vscode.RelativePattern;
return rp && typeof rp.base === 'string' && typeof rp.pattern === 'string';
}
return pattern; // preserve `undefined` and `null`
}
function isRelativePattern(obj: any): obj is vscode.RelativePattern {
const rp = obj as vscode.RelativePattern;
export namespace LanguageSelector {
return rp && typeof rp.base === 'string' && typeof rp.pattern === 'string';
}
export function toLanguageSelector(selector: vscode.DocumentSelector): LanguageSelector {
if (Array.isArray(selector)) {
return selector.map(sel => doToLanguageSelector(sel));
export function from(selector: vscode.DocumentSelector): languageSelector.LanguageSelector {
if (!selector) {
return undefined;
} else if (Array.isArray(selector)) {
return <languageSelector.LanguageSelector>selector.map(from);
} else if (typeof selector === 'string') {
return selector;
} else {
return <languageSelector.LanguageFilter>{
language: selector.language,
scheme: selector.scheme,
pattern: GlobPattern.from(selector.pattern),
exclusive: selector.exclusive
};
}
}
return doToLanguageSelector(selector);
}
function doToLanguageSelector(selector: string | vscode.DocumentFilter): string | LanguageFilter {
if (typeof selector === 'string') {
return selector;
}
if (selector) {
return {
language: selector.language,
scheme: selector.scheme,
pattern: toGlobPattern(selector.pattern),
exclusive: selector.exclusive
};
}
return undefined;
}

View File

@@ -13,6 +13,8 @@ import { isMarkdownString } from 'vs/base/common/htmlContent';
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';
export class Disposable {
@@ -492,38 +494,45 @@ export class TextEdit {
}
}
export interface IFileOperationOptions {
overwrite?: boolean;
ignoreIfExists?: boolean;
ignoreIfNotExists?: boolean;
recursive?: boolean;
}
export interface IFileOperation {
_type: 1;
from: URI;
to: URI;
options?: IFileOperationOptions;
}
export interface IFileTextEdit {
_type: 2;
uri: URI;
edit: TextEdit;
}
export class WorkspaceEdit implements vscode.WorkspaceEdit {
private _seqPool: number = 0;
private _edits = new Array<IFileOperation | IFileTextEdit>();
private _resourceEdits: { seq: number, from: URI, to: URI }[] = [];
private _textEdits = new Map<string, { seq: number, uri: URI, edits: TextEdit[] }>();
renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void {
this._edits.push({ _type: 1, from, to, options });
}
// createResource(uri: vscode.Uri): void {
// this.renameResource(undefined, uri);
// }
createFile(uri: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void {
this._edits.push({ _type: 1, from: undefined, to: uri, options });
}
// deleteResource(uri: vscode.Uri): void {
// this.renameResource(uri, undefined);
// }
// renameResource(from: vscode.Uri, to: vscode.Uri): void {
// this._resourceEdits.push({ seq: this._seqPool++, from, to });
// }
// resourceEdits(): [vscode.Uri, vscode.Uri][] {
// return this._resourceEdits.map(({ from, to }) => (<[vscode.Uri, vscode.Uri]>[from, to]));
// }
deleteFile(uri: vscode.Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean }): void {
this._edits.push({ _type: 1, from: uri, to: undefined, options });
}
replace(uri: URI, range: Range, newText: string): void {
let edit = new TextEdit(range, newText);
let array = this.get(uri);
if (array) {
array.push(edit);
} else {
array = [edit];
}
this.set(uri, array);
this._edits.push({ _type: 2, uri, edit: new TextEdit(range, newText) });
}
insert(resource: URI, position: Position, newText: string): void {
@@ -535,55 +544,76 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
}
has(uri: URI): boolean {
return this._textEdits.has(uri.toString());
for (const edit of this._edits) {
if (edit._type === 2 && edit.uri.toString() === uri.toString()) {
return true;
}
}
return false;
}
set(uri: URI, edits: TextEdit[]): void {
let data = this._textEdits.get(uri.toString());
if (!data) {
data = { seq: this._seqPool++, uri, edits: [] };
this._textEdits.set(uri.toString(), data);
}
if (!edits) {
data.edits = undefined;
// remove all text edits for `uri`
for (let i = 0; i < this._edits.length; i++) {
const element = this._edits[i];
if (element._type === 2 && element.uri.toString() === uri.toString()) {
this._edits[i] = undefined;
}
}
this._edits = coalesce(this._edits);
} else {
data.edits = edits.slice(0);
// append edit to the end
for (const edit of edits) {
if (edit) {
this._edits.push({ _type: 2, uri, edit });
}
}
}
}
get(uri: URI): TextEdit[] {
if (!this._textEdits.has(uri.toString())) {
let res: TextEdit[] = [];
for (let candidate of this._edits) {
if (candidate._type === 2 && candidate.uri.toString() === uri.toString()) {
res.push(candidate.edit);
}
}
if (res.length === 0) {
return undefined;
}
const { edits } = this._textEdits.get(uri.toString());
return edits ? edits.slice() : undefined;
return res;
}
entries(): [URI, TextEdit[]][] {
const res: [URI, TextEdit[]][] = [];
this._textEdits.forEach(value => res.push([value.uri, value.edits]));
return res.slice();
let textEdits = new Map<string, [URI, TextEdit[]]>();
for (let candidate of this._edits) {
if (candidate._type === 2) {
let textEdit = textEdits.get(candidate.uri.toString());
if (!textEdit) {
textEdit = [candidate.uri, []];
textEdits.set(candidate.uri.toString(), textEdit);
}
textEdit[1].push(candidate.edit);
}
}
return values(textEdits);
}
allEntries(): ([URI, TextEdit[]] | [URI, URI])[] {
return this.entries();
// // use the 'seq' the we have assigned when inserting
// // the operation and use that order in the resulting
// // array
// const res: ([URI, TextEdit[]] | [URI, URI])[] = [];
// this._textEdits.forEach(value => {
// const { seq, uri, edits } = value;
// res[seq] = [uri, edits];
// });
// this._resourceEdits.forEach(value => {
// const { seq, from, to } = value;
// res[seq] = [from, to];
// });
// return res;
_allEntries(): ([URI, TextEdit[]] | [URI, URI, IFileOperationOptions])[] {
let res: ([URI, TextEdit[]] | [URI, URI, IFileOperationOptions])[] = [];
for (let edit of this._edits) {
if (edit._type === 1) {
res.push([edit.from, edit.to, edit.options]);
} else {
res.push([edit.uri, [edit.edit]]);
}
}
return res;
}
get size(): number {
return this._textEdits.size + this._resourceEdits.length;
return this.entries().length;
}
toJSON(): any {
@@ -673,6 +703,10 @@ export class SnippetString {
}
}
export enum DiagnosticTag {
Unnecessary = 1,
}
export enum DiagnosticSeverity {
Hint = 3,
Information = 2,
@@ -747,6 +781,7 @@ export class Diagnostic {
code: string | number;
severity: DiagnosticSeverity;
relatedInformation: DiagnosticRelatedInformation[];
tags?: DiagnosticTag[];
constructor(range: Range, message: string, severity: DiagnosticSeverity = DiagnosticSeverity.Error) {
this.range = range;
@@ -876,36 +911,32 @@ export class SymbolInformation {
}
}
export class HierarchicalSymbolInformation {
export class DocumentSymbol {
name: string;
location: Location;
detail: string;
kind: SymbolKind;
range: Range;
children: HierarchicalSymbolInformation[];
selectionRange: Range;
children: DocumentSymbol[];
constructor(name: string, kind: SymbolKind, location: Location, range: Range) {
constructor(name: string, detail: string, kind: SymbolKind, range: Range, selectionRange: Range) {
this.name = name;
this.detail = detail;
this.kind = kind;
this.location = location;
this.range = range;
this.selectionRange = selectionRange;
this.children = [];
}
static toFlatSymbolInformation(info: HierarchicalSymbolInformation): SymbolInformation[] {
let result: SymbolInformation[] = [];
HierarchicalSymbolInformation._toFlatSymbolInformation(info, undefined, result);
return result;
}
private static _toFlatSymbolInformation(info: HierarchicalSymbolInformation, containerName: string, bucket: SymbolInformation[]): void {
bucket.push(new SymbolInformation(info.name, info.kind, containerName, new Location(info.location.uri, info.range)));
if (Array.isArray(info.children)) {
for (const child of info.children) {
HierarchicalSymbolInformation._toFlatSymbolInformation(child, info.name, bucket);
}
if (!this.range.contains(this.selectionRange)) {
throw new Error('selectionRange must be contained in fullRange');
}
}
}
export enum CodeActionTrigger {
Automatic = 1,
Manual = 2,
}
export class CodeAction {
@@ -1072,7 +1103,7 @@ export enum CompletionItemKind {
TypeParameter = 24
}
export class CompletionItem {
export class CompletionItem implements vscode.CompletionItem {
label: string;
kind: CompletionItemKind;
@@ -1080,6 +1111,7 @@ export class CompletionItem {
documentation: string | MarkdownString;
sortText: string;
filterText: string;
preselect: boolean;
insertText: string | SnippetString;
range: Range;
textEdit: TextEdit;
@@ -1099,6 +1131,7 @@ export class CompletionItem {
documentation: this.documentation,
sortText: this.sortText,
filterText: this.filterText,
preselect: this.preselect,
insertText: this.insertText,
textEdit: this.textEdit
};
@@ -1119,9 +1152,16 @@ export class CompletionList {
export enum ViewColumn {
Active = -1,
Beside = -2,
One = 1,
Two = 2,
Three = 3
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9
}
export enum StatusBarAlignment {
@@ -1494,7 +1534,6 @@ export class Task implements vscode.Task {
private __id: string;
private _definition: vscode.TaskDefinition;
private _definitionKey: string;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder;
private _name: string;
private _execution: ProcessExecution | ShellExecution;
@@ -1555,7 +1594,6 @@ export class Task implements vscode.Task {
}
this.__id = undefined;
this._scope = undefined;
this._definitionKey = undefined;
this._definition = undefined;
if (this._execution instanceof ProcessExecution) {
this._definition = {
@@ -1579,19 +1617,9 @@ export class Task implements vscode.Task {
throw illegalArgument('Kind can\'t be undefined or null');
}
this.clear();
this._definitionKey = undefined;
this._definition = value;
}
get definitionKey(): string {
if (!this._definitionKey) {
const hash = crypto.createHash('md5');
hash.update(JSON.stringify(this._definition));
this._definitionKey = hash.digest('hex');
}
return this._definitionKey;
}
get scope(): vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder {
return this._scope;
}
@@ -1848,12 +1876,6 @@ export enum LogLevel {
}
//#region file api
// todo@remote
export enum DeprecatedFileChangeType {
Updated = 0,
Added = 1,
Deleted = 2
}
export enum FileChangeType {
Changed = 1,
@@ -1861,12 +1883,6 @@ export enum FileChangeType {
Deleted = 3,
}
export enum DeprecatedFileType {
File = 0,
Dir = 1,
Symlink = 2
}
export class FileSystemError extends Error {
static FileExists(messageOrUri?: string | URI): FileSystemError {
@@ -1931,3 +1947,22 @@ export enum FoldingRangeKind {
}
//#endregion
export enum CommentThreadCollapsibleState {
/**
* Determines an item is collapsed
*/
Collapsed = 0,
/**
* Determines an item is expanded
*/
Expanded = 1
}
export class QuickInputButtons {
static readonly Back: vscode.QuickInputButton = { iconPath: 'back.svg' };
private constructor() { }
}

View File

@@ -8,6 +8,8 @@ import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { toDisposable } from 'vs/base/common/lifecycle';
import { asWinJsPromise } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
export class ExtHostUrls implements ExtHostUrlsShape {
@@ -15,7 +17,7 @@ export class ExtHostUrls implements ExtHostUrlsShape {
private readonly _proxy: MainThreadUrlsShape;
private handles = new Set<string>();
private handlers = new Map<number, vscode.ProtocolHandler>();
private handlers = new Map<number, vscode.UriHandler>();
constructor(
mainContext: IMainContext
@@ -23,7 +25,7 @@ export class ExtHostUrls implements ExtHostUrlsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadUrls);
}
registerProtocolHandler(extensionId: string, handler: vscode.ProtocolHandler): vscode.Disposable {
registerUriHandler(extensionId: string, handler: vscode.UriHandler): vscode.Disposable {
if (this.handles.has(extensionId)) {
throw new Error(`Protocol handler already registered for extension ${extensionId}`);
}
@@ -31,12 +33,12 @@ export class ExtHostUrls implements ExtHostUrlsShape {
const handle = ExtHostUrls.HandlePool++;
this.handles.add(extensionId);
this.handlers.set(handle, handler);
this._proxy.$registerProtocolHandler(handle, extensionId);
this._proxy.$registerUriHandler(handle, extensionId);
return toDisposable(() => {
this.handles.delete(extensionId);
this.handlers.delete(handle);
this._proxy.$unregisterProtocolHandler(handle);
this._proxy.$unregisterUriHandler(handle);
});
}
@@ -47,7 +49,9 @@ export class ExtHostUrls implements ExtHostUrlsShape {
return TPromise.as(null);
}
handler.handleUri(URI.revive(uri));
asWinJsPromise(_ => handler.handleUri(URI.revive(uri)))
.done(null, onUnexpectedError);
return TPromise.as(null);
}
}

View File

@@ -3,14 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadWebviewsShape, IMainContext, ExtHostWebviewsShape, WebviewPanelHandle } from './extHost.protocol';
import * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Position } from 'vs/platform/editor/common/editor';
import { Emitter, Event } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol';
import { Disposable } from './extHostTypes';
type IconPath = URI | { light: URI, dark: URI };
export class ExtHostWebview implements vscode.Webview {
private readonly _handle: WebviewPanelHandle;
private readonly _proxy: MainThreadWebviewsShape;
@@ -18,7 +21,7 @@ export class ExtHostWebview implements vscode.Webview {
private _options: vscode.WebviewOptions;
private _isDisposed: boolean = false;
readonly _onMessageEmitter = new Emitter<any>();
public readonly _onMessageEmitter = new Emitter<any>();
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
constructor(
@@ -31,16 +34,16 @@ export class ExtHostWebview implements vscode.Webview {
this._options = options;
}
dispose() {
public dispose() {
this._onMessageEmitter.dispose();
}
get html(): string {
public get html(): string {
this.assertNotDisposed();
return this._html;
}
set html(value: string) {
public set html(value: string) {
this.assertNotDisposed();
if (this._html !== value) {
this._html = value;
@@ -48,11 +51,17 @@ export class ExtHostWebview implements vscode.Webview {
}
}
get options(): vscode.WebviewOptions {
public get options(): vscode.WebviewOptions {
this.assertNotDisposed();
return this._options;
}
public set options(newOptions: vscode.WebviewOptions) {
this.assertNotDisposed();
this._proxy.$setOptions(this._handle, newOptions);
this._options = newOptions;
}
public postMessage(message: any): Thenable<boolean> {
this.assertNotDisposed();
return this._proxy.$postMessage(this._handle, message);
@@ -71,12 +80,14 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
private readonly _proxy: MainThreadWebviewsShape;
private readonly _viewType: string;
private _title: string;
private _iconPath: IconPath;
private readonly _options: vscode.WebviewPanelOptions;
private readonly _webview: ExtHostWebview;
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn;
private _visible: boolean = true;
private _active: boolean = true;
readonly _onDisposeEmitter = new Emitter<void>();
public readonly onDidDispose: Event<void> = this._onDisposeEmitter.event;
@@ -142,6 +153,20 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
}
}
get iconPath(): IconPath | undefined {
this.assertNotDisposed();
return this._iconPath;
}
set iconPath(value: IconPath | undefined) {
this.assertNotDisposed();
if (this._iconPath !== value) {
this._iconPath = value;
this._proxy.$setIconPath(this._handle, URI.isUri(value) ? { light: value, dark: value } : value);
}
}
get options() {
return this._options;
}
@@ -156,7 +181,17 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
this._viewColumn = value;
}
get visible(): boolean {
public get active(): boolean {
this.assertNotDisposed();
return this._active;
}
_setActive(value: boolean) {
this.assertNotDisposed();
this._active = value;
}
public get visible(): boolean {
this.assertNotDisposed();
return this._visible;
}
@@ -171,9 +206,12 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
return this._proxy.$postMessage(this._handle, message);
}
public reveal(viewColumn?: vscode.ViewColumn): void {
public reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void {
this.assertNotDisposed();
this._proxy.$reveal(this._handle, viewColumn ? typeConverters.fromViewColumn(viewColumn) : undefined);
this._proxy.$reveal(this._handle, {
viewColumn: viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined,
preserveFocus: !!preserveFocus
});
}
private assertNotDisposed() {
@@ -186,8 +224,11 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
export class ExtHostWebviews implements ExtHostWebviewsShape {
private static webviewHandlePool = 1;
private readonly _proxy: MainThreadWebviewsShape;
private static newHandle(): WebviewPanelHandle {
return ExtHostWebviews.webviewHandlePool++ + '';
}
private readonly _proxy: MainThreadWebviewsShape;
private readonly _webviewPanels = new Map<WebviewPanelHandle, ExtHostWebviewPanel>();
private readonly _serializers = new Map<string, vscode.WebviewPanelSerializer>();
@@ -197,16 +238,21 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews);
}
createWebview(
public createWebview(
extensionLocation: URI,
viewType: string,
title: string,
viewColumn: vscode.ViewColumn,
options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) | undefined,
extensionFolderPath: string
showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean },
options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) = {},
): vscode.WebviewPanel {
options = options || {};
const handle = ExtHostWebviews.webviewHandlePool++ + '';
this._proxy.$createWebviewPanel(handle, viewType, title, typeConverters.fromViewColumn(viewColumn), options, extensionFolderPath);
const viewColumn = typeof showOptions === 'object' ? showOptions.viewColumn : showOptions;
const webviewShowOptions = {
viewColumn: typeConverters.ViewColumn.from(viewColumn),
preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus
};
const handle = ExtHostWebviews.newHandle();
this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extensionLocation);
const webview = new ExtHostWebview(handle, this._proxy, options);
const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview);
@@ -214,7 +260,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
return panel;
}
registerWebviewPanelSerializer(
public registerWebviewPanelSerializer(
viewType: string,
serializer: vscode.WebviewPanelSerializer
): vscode.Disposable {
@@ -231,19 +277,26 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
});
}
$onMessage(handle: WebviewPanelHandle, message: any): void {
public $onMessage(
handle: WebviewPanelHandle,
message: any
): void {
const panel = this.getWebviewPanel(handle);
if (panel) {
panel.webview._onMessageEmitter.fire(message);
}
}
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, visible: boolean, position: Position): void {
public $onDidChangeWebviewPanelViewState(
handle: WebviewPanelHandle,
newState: WebviewPanelViewState
): void {
const panel = this.getWebviewPanel(handle);
if (panel) {
const viewColumn = typeConverters.toViewColumn(position);
if (panel.visible !== visible || panel.viewColumn !== viewColumn) {
panel._setVisible(visible);
const viewColumn = typeConverters.ViewColumn.to(newState.position);
if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) {
panel._setActive(newState.active);
panel._setVisible(newState.visible);
panel._setViewColumn(viewColumn);
panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel });
}
@@ -264,7 +317,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
viewType: string,
title: string,
state: any,
position: Position,
position: EditorViewColumn,
options: vscode.WebviewOptions & vscode.WebviewPanelOptions
): Thenable<void> {
const serializer = this._serializers.get(viewType);
@@ -273,28 +326,12 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
const webview = new ExtHostWebview(webviewHandle, this._proxy, options);
const revivedPanel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeConverters.toViewColumn(position), options, webview);
const revivedPanel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeConverters.ViewColumn.to(position), options, webview);
this._webviewPanels.set(webviewHandle, revivedPanel);
return serializer.deserializeWebviewPanel(revivedPanel, state);
}
$serializeWebviewPanel(
webviewHandle: WebviewPanelHandle
): Thenable<any> {
const panel = this.getWebviewPanel(webviewHandle);
if (!panel) {
return TPromise.as(undefined);
}
const serialzer = this._serializers.get(panel.viewType);
if (!serialzer) {
return TPromise.as(undefined);
}
return serialzer.serializeWebviewPanel(panel);
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewPanel | undefined {
return this._webviewPanels.get(handle);
}
}
}

View File

@@ -4,22 +4,25 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { normalize } from 'vs/base/common/paths';
import { posix, relative, join } from 'path';
import { delta as arrayDelta } from 'vs/base/common/arrays';
import { relative, posix } from 'path';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext, MainThreadMessageServiceShape } from './extHost.protocol';
import * as vscode from 'vscode';
import { compare } from 'vs/base/common/strings';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
import { normalize } from 'vs/base/common/paths';
import { isLinux } from 'vs/base/common/platform';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
import { compare } from 'vs/base/common/strings';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { Severity } from 'vs/platform/notification/common/notification';
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 { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
function isFolderEqual(folderA: URI, folderB: URI): boolean {
return isEqual(folderA, folderB, !isLinux);
@@ -145,6 +148,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
private readonly _activeSearchCallbacks = [];
constructor(
mainContext: IMainContext,
data: IWorkspaceData,
@@ -263,6 +268,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
if (folders.length === 0) {
return undefined;
}
// #54483 @Joh Why are we still using fsPath?
return folders[0].uri.fsPath;
}
@@ -359,13 +365,79 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
}
const result = this._proxy.$startSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, requestId);
const result = this._proxy.$startFileSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, requestId);
if (token) {
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
}
return result.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) {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`);
const requestId = ExtHostWorkspace._requestIdPool++;
const globPatternToString = (pattern: vscode.GlobPattern | string) => {
if (typeof pattern === 'string') {
return pattern;
}
return join(pattern.base, pattern.pattern);
};
const queryOptions: IQueryOptions = {
ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined,
disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined,
disregardExcludeSettings: options.exclude === null,
fileEncoding: options.encoding,
maxResults: options.maxResults,
includePattern: options.include && globPatternToString(options.include),
excludePattern: options.exclude && globPatternToString(options.exclude)
};
let isCanceled = false;
this._activeSearchCallbacks[requestId] = p => {
if (isCanceled) {
return;
}
p.lineMatches.forEach(lineMatch => {
lineMatch.offsetAndLengths.forEach(offsetAndLength => {
const range = new Range(lineMatch.lineNumber, offsetAndLength[0], lineMatch.lineNumber, offsetAndLength[0] + offsetAndLength[1]);
callback({
uri: URI.revive(p.resource),
preview: { text: lineMatch.preview, match: range },
range
});
});
});
};
if (token) {
token.onCancellationRequested(() => {
isCanceled = true;
this._proxy.$cancelSearch(requestId);
});
}
return this._proxy.$startTextSearch(query, queryOptions, requestId).then(
() => {
delete this._activeSearchCallbacks[requestId];
},
err => {
delete this._activeSearchCallbacks[requestId];
return TPromise.wrapError(err);
});
}
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void {
if (this._activeSearchCallbacks[requestId]) {
this._activeSearchCallbacks[requestId](result);
}
}
saveAll(includeUntitled?: boolean): Thenable<boolean> {
return this._proxy.$saveAll(includeUntitled);
}