mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 17:23:40 -05:00
Merge VS Code 1.23.1 (#1520)
This commit is contained in:
@@ -16,6 +16,7 @@ import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorEx
|
||||
import { LanguageConfigurationFileHandler } from 'vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint';
|
||||
|
||||
// --- mainThread participants
|
||||
import 'vs/workbench/api/node/apiCommands';
|
||||
import './mainThreadCommands';
|
||||
import './mainThreadConfiguration';
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -42,6 +43,7 @@ import './mainThreadOutputService';
|
||||
import './mainThreadProgress';
|
||||
import './mainThreadQuickOpen';
|
||||
import './mainThreadSCM';
|
||||
import './mainThreadSearch';
|
||||
import './mainThreadSaveParticipant';
|
||||
import './mainThreadStatusBar';
|
||||
import './mainThreadStorage';
|
||||
@@ -51,6 +53,7 @@ import './mainThreadTerminalService';
|
||||
import './mainThreadTreeViews';
|
||||
import './mainThreadLogService';
|
||||
import './mainThreadWebview';
|
||||
import './mainThreadUrls';
|
||||
import './mainThreadWindow';
|
||||
import './mainThreadWorkspace';
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IAdapterExecutable, ITerminalSettings, IDebugAdapter, IDebugAdapterProvider } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import {
|
||||
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
|
||||
@@ -17,13 +17,20 @@ import {
|
||||
} from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { AbstractDebugAdapter, convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/node/debugAdapter';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadDebugService)
|
||||
export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterProvider {
|
||||
|
||||
private _proxy: ExtHostDebugServiceShape;
|
||||
private _toDispose: IDisposable[];
|
||||
private _breakpointEventsActive: boolean;
|
||||
private _debugAdapters: Map<number, ExtensionHostDebugAdapter>;
|
||||
private _debugAdaptersHandleCounter = 1;
|
||||
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@@ -49,6 +56,26 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
}
|
||||
}
|
||||
}));
|
||||
this._debugAdapters = new Map<number, ExtensionHostDebugAdapter>();
|
||||
}
|
||||
|
||||
public $registerDebugTypes(debugTypes: string[]) {
|
||||
this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterProvider(debugTypes, this));
|
||||
}
|
||||
|
||||
createDebugAdapter(debugType: string, adapterInfo): IDebugAdapter {
|
||||
const handle = this._debugAdaptersHandleCounter++;
|
||||
const da = new ExtensionHostDebugAdapter(handle, this._proxy, debugType, adapterInfo);
|
||||
this._debugAdapters.set(handle, da);
|
||||
return da;
|
||||
}
|
||||
|
||||
substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise<IConfig> {
|
||||
return this._proxy.$substituteVariables(folder.uri, config);
|
||||
}
|
||||
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void> {
|
||||
return this._proxy.$runInTerminal(args, config);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -102,9 +129,10 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
id: l.id,
|
||||
enabled: l.enabled,
|
||||
lineNumber: l.line + 1,
|
||||
column: l.character > 0 ? l.character + 1 : 0,
|
||||
column: l.character > 0 ? l.character + 1 : undefined, // a column value of 0 results in an omitted column attribute; see #46784
|
||||
condition: l.condition,
|
||||
hitCondition: l.hitCondition
|
||||
hitCondition: l.hitCondition,
|
||||
logMessage: l.logMessage
|
||||
}
|
||||
);
|
||||
this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps);
|
||||
@@ -121,7 +149,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
private convertToDto(bps: (IBreakpoint | IFunctionBreakpoint)[]): (ISourceBreakpointDto | IFunctionBreakpointDto)[] {
|
||||
private convertToDto(bps: (ReadonlyArray<IBreakpoint | IFunctionBreakpoint>)): (ISourceBreakpointDto | IFunctionBreakpointDto)[] {
|
||||
return bps.map(bp => {
|
||||
if ('name' in bp) {
|
||||
const fbp = <IFunctionBreakpoint>bp;
|
||||
@@ -142,6 +170,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
enabled: sbp.enabled,
|
||||
condition: sbp.condition,
|
||||
hitCondition: sbp.hitCondition,
|
||||
logMessage: sbp.logMessage,
|
||||
uri: sbp.uri,
|
||||
line: sbp.lineNumber > 0 ? sbp.lineNumber - 1 : 0,
|
||||
character: (typeof sbp.column === 'number' && sbp.column > 0) ? sbp.column - 1 : 0,
|
||||
@@ -209,6 +238,67 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
this.debugService.logToRepl(value, severity.Warning);
|
||||
return TPromise.wrap<void>(undefined);
|
||||
}
|
||||
|
||||
public $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage) {
|
||||
|
||||
convertToVSCPaths(message, source => {
|
||||
if (typeof source.path === 'object') {
|
||||
source.path = uri.revive(source.path).toString();
|
||||
}
|
||||
});
|
||||
|
||||
this._debugAdapters.get(handle).acceptMessage(message);
|
||||
}
|
||||
|
||||
public $acceptDAError(handle: number, name: string, message: string, stack: string) {
|
||||
this._debugAdapters.get(handle).fireError(handle, new Error(`${name}: ${message}\n${stack}`));
|
||||
}
|
||||
|
||||
public $acceptDAExit(handle: number, code: number, signal: string) {
|
||||
this._debugAdapters.get(handle).fireExit(handle, code, signal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DebugAdapter that communicates via extension protocol with another debug adapter.
|
||||
*/
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
/*
|
||||
class ExtensionHostDebugAdapter extends AbstractDebugAdapter {
|
||||
|
||||
constructor(private _handle: number, private _proxy: ExtHostDebugServiceShape, private _debugType: string, private _adapterExecutable: IAdapterExecutable | null) {
|
||||
super();
|
||||
}
|
||||
|
||||
public fireError(handle: number, err: Error) {
|
||||
this._onError.fire(err);
|
||||
}
|
||||
|
||||
public fireExit(handle: number, code: number, signal: string) {
|
||||
this._onExit.fire(code);
|
||||
}
|
||||
|
||||
public startSession(): TPromise<void> {
|
||||
return this._proxy.$startDASession(this._handle, this._debugType, this._adapterExecutable);
|
||||
}
|
||||
|
||||
public sendMessage(message: DebugProtocol.ProtocolMessage): void {
|
||||
|
||||
convertToDAPaths(message, source => {
|
||||
if (paths.isAbsolute(source.path)) {
|
||||
(<any>source).path = uri.file(source.path);
|
||||
} else {
|
||||
(<any>source).path = uri.parse(source.path);
|
||||
}
|
||||
});
|
||||
|
||||
this._proxy.$sendDAMessage(this._handle, message);
|
||||
}
|
||||
|
||||
public stopSession(): TPromise<void> {
|
||||
return this._proxy.$stopDASession(this._handle);
|
||||
}
|
||||
}
|
||||
// {{SQL CARBON EDIT}}
|
||||
*/
|
||||
|
||||
@@ -10,11 +10,13 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostContext, MainContext, IExtHostContext, MainThreadDecorationsShape, ExtHostDecorationsShape, DecorationData, DecorationRequest } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { IDecorationsService, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
|
||||
class DecorationRequestsQueue {
|
||||
|
||||
private _idPool = 0;
|
||||
private _requests: DecorationRequest[] = [];
|
||||
private _requests: { [id: number]: DecorationRequest } = Object.create(null);
|
||||
private _resolver: { [id: number]: Function } = Object.create(null);
|
||||
|
||||
private _timer: number;
|
||||
@@ -25,12 +27,15 @@ class DecorationRequestsQueue {
|
||||
//
|
||||
}
|
||||
|
||||
enqueue(handle: number, uri: URI): Thenable<DecorationData> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = ++this._idPool;
|
||||
this._requests.push({ id, handle, uri });
|
||||
enqueue(handle: number, uri: URI): TPromise<DecorationData> {
|
||||
const id = ++this._idPool;
|
||||
return new TPromise((resolve, reject) => {
|
||||
this._requests[id] = { id, handle, uri };
|
||||
this._resolver[id] = resolve;
|
||||
this._processQueue();
|
||||
}, () => {
|
||||
delete this._requests[id];
|
||||
delete this._resolver[id];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,7 +48,7 @@ class DecorationRequestsQueue {
|
||||
// make request
|
||||
const requests = this._requests;
|
||||
const resolver = this._resolver;
|
||||
this._proxy.$provideDecorations(requests).then(data => {
|
||||
this._proxy.$provideDecorations(values(requests)).then(data => {
|
||||
for (const id in resolver) {
|
||||
resolver[id](data[id]);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,15 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
|
||||
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
|
||||
for (let entry of entries) {
|
||||
let [uri, markers] = entry;
|
||||
if (markers) {
|
||||
for (const marker of markers) {
|
||||
if (marker.relatedInformation) {
|
||||
for (const relatedInformation of marker.relatedInformation) {
|
||||
relatedInformation.resource = URI.revive(relatedInformation.resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._markerService.changeOne(owner, URI.revive(uri), markers);
|
||||
}
|
||||
this._activeOwners.add(owner);
|
||||
|
||||
@@ -28,6 +28,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
|
||||
$showOpenDialog(options: MainThreadDialogOpenOptions): Promise<string[]> {
|
||||
// TODO@joh what about remote dev setup?
|
||||
if (options.defaultUri && options.defaultUri.scheme !== 'file') {
|
||||
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
|
||||
return <any>Promise.reject(new Error('Not supported - Open-dialogs can only be opened on `file`-uris.'));
|
||||
}
|
||||
return new Promise<string[]>(resolve => {
|
||||
@@ -40,6 +41,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
|
||||
$showSaveDialog(options: MainThreadDialogSaveOptions): Promise<string> {
|
||||
// TODO@joh what about remote dev setup?
|
||||
if (options.defaultUri && options.defaultUri.scheme !== 'file') {
|
||||
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
|
||||
return <any>Promise.reject(new Error('Not supported - Save-dialogs can only be opened on `file`-uris.'));
|
||||
}
|
||||
return new Promise<string>(resolve => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import URI, { UriComponents } from 'vs/base/common/uri';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
|
||||
import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle';
|
||||
import { TextFileModelChangeEvent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
@@ -132,12 +132,12 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
|
||||
private _shouldHandleFileEvent(e: TextFileModelChangeEvent): boolean {
|
||||
const model = this._modelService.getModel(e.resource);
|
||||
return model && !model.isTooLargeForHavingARichMode();
|
||||
return model && shouldSynchronizeModel(model);
|
||||
}
|
||||
|
||||
private _onModelAdded(model: ITextModel): void {
|
||||
// Same filter as in mainThreadEditorsTracker
|
||||
if (model.isTooLargeForHavingARichMode()) {
|
||||
if (!shouldSynchronizeModel(model)) {
|
||||
// don't synchronize too large models
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostContext, ExtHostDocumentsAndEditorsShape, IModelAddedData, ITextEditorAddData, IDocumentsAndEditorsDelta, IExtHostContext, MainContext } from '../node/extHost.protocol';
|
||||
import { MainThreadTextEditor } from './mainThreadEditor';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
@@ -192,7 +192,7 @@ class MainThreadDocumentAndEditorStateComputer {
|
||||
}
|
||||
|
||||
private _updateStateOnModelAdd(model: ITextModel): void {
|
||||
if (model.isTooLargeForHavingARichMode()) {
|
||||
if (!shouldSynchronizeModel(model)) {
|
||||
// ignore
|
||||
return;
|
||||
}
|
||||
@@ -222,7 +222,7 @@ class MainThreadDocumentAndEditorStateComputer {
|
||||
// models: ignore too large models
|
||||
const models = new Set<ITextModel>();
|
||||
for (const model of this._modelService.getModels()) {
|
||||
if (!model.isTooLargeForHavingARichMode()) {
|
||||
if (shouldSynchronizeModel(model)) {
|
||||
models.add(model);
|
||||
}
|
||||
}
|
||||
@@ -233,8 +233,11 @@ class MainThreadDocumentAndEditorStateComputer {
|
||||
let activeEditor: string = null;
|
||||
|
||||
for (const editor of this._codeEditorService.listCodeEditors()) {
|
||||
if (editor.isSimpleWidget) {
|
||||
continue;
|
||||
}
|
||||
const model = editor.getModel();
|
||||
if (model && !model.isTooLargeForHavingARichMode()
|
||||
if (model && shouldSynchronizeModel(model)
|
||||
&& !model.isDisposed() // model disposed
|
||||
&& Boolean(this._modelService.getModel(model.uri)) // model disposing, the flag didn't flip yet but the model service already removed it
|
||||
) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'use strict';
|
||||
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IEditor } from 'vs/platform/editor/common/editor';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
@@ -185,7 +185,7 @@ export class MainThreadTextEditor {
|
||||
private _codeEditorListeners: IDisposable[];
|
||||
|
||||
private _properties: MainThreadTextEditorProperties;
|
||||
private _onPropertiesChanged: Emitter<IEditorPropertiesChangeData>;
|
||||
private readonly _onPropertiesChanged: Emitter<IEditorPropertiesChangeData>;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -322,7 +322,7 @@ export class MainThreadTextEditor {
|
||||
if (newConfiguration.tabSize === 'auto' || newConfiguration.insertSpaces === 'auto') {
|
||||
// one of the options was set to 'auto' => detect indentation
|
||||
|
||||
let creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri);
|
||||
let creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri, this._model.isForSimpleWidget);
|
||||
let insertSpaces = creationOpts.insertSpaces;
|
||||
let tabSize = creationOpts.tabSize;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export class MainThreadErrors implements MainThreadErrorsShape {
|
||||
}
|
||||
|
||||
$onUnexpectedError(err: any | SerializedError): void {
|
||||
if (err.$isError) {
|
||||
if (err && err.$isError) {
|
||||
const { name, message, stack } = err;
|
||||
err = new Error();
|
||||
err.message = message;
|
||||
|
||||
@@ -4,114 +4,63 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import URI, { UriComponents } from 'vs/base/common/uri';
|
||||
import { TPromise, PPromise } from 'vs/base/common/winjs.base';
|
||||
import { ExtHostContext, MainContext, IExtHostContext, MainThreadFileSystemShape, ExtHostFileSystemShape, IFileChangeDto } from '../node/extHost.protocol';
|
||||
import { IFileService, IFileSystemProvider, IStat, IFileChange } from 'vs/platform/files/common/files';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { ISearchResultProvider, ISearchQuery, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchService, ILineMatch } from 'vs/platform/search/common/search';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../node/extHost.protocol';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadFileSystem)
|
||||
export class MainThreadFileSystem implements MainThreadFileSystemShape {
|
||||
|
||||
private readonly _proxy: ExtHostFileSystemShape;
|
||||
private readonly _provider = new Map<number, RemoteFileSystemProvider>();
|
||||
private readonly _fileProvider = new Map<number, RemoteFileSystemProvider>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@ISearchService private readonly _searchService: ISearchService,
|
||||
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService
|
||||
@IFileService private readonly _fileService: IFileService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._provider.forEach(value => dispose());
|
||||
this._provider.clear();
|
||||
this._fileProvider.forEach(value => value.dispose());
|
||||
this._fileProvider.clear();
|
||||
}
|
||||
|
||||
$registerFileSystemProvider(handle: number, scheme: string): void {
|
||||
this._provider.set(handle, new RemoteFileSystemProvider(this._fileService, this._searchService, scheme, handle, this._proxy));
|
||||
$registerFileSystemProvider(handle: number, scheme: string, capabilities: FileSystemProviderCapabilities): void {
|
||||
this._fileProvider.set(handle, new RemoteFileSystemProvider(this._fileService, scheme, capabilities, handle, this._proxy));
|
||||
}
|
||||
|
||||
$unregisterFileSystemProvider(handle: number): void {
|
||||
dispose(this._provider.get(handle));
|
||||
this._provider.delete(handle);
|
||||
}
|
||||
|
||||
$onDidAddFileSystemRoot(data: UriComponents): void {
|
||||
this._workspaceEditingService.addFolders([{ uri: URI.revive(data) }], true).done(null, onUnexpectedError);
|
||||
$unregisterProvider(handle: number): void {
|
||||
dispose(this._fileProvider.get(handle));
|
||||
this._fileProvider.delete(handle);
|
||||
}
|
||||
|
||||
$onFileSystemChange(handle: number, changes: IFileChangeDto[]): void {
|
||||
this._provider.get(handle).$onFileSystemChange(changes);
|
||||
}
|
||||
|
||||
$reportFileChunk(handle: number, session: number, chunk: number[]): void {
|
||||
this._provider.get(handle).reportFileChunk(session, chunk);
|
||||
}
|
||||
|
||||
// --- search
|
||||
|
||||
$handleFindMatch(handle: number, session, data: UriComponents | [UriComponents, ILineMatch]): void {
|
||||
this._provider.get(handle).handleFindMatch(session, data);
|
||||
this._fileProvider.get(handle).$onFileSystemChange(changes);
|
||||
}
|
||||
}
|
||||
|
||||
class FileReadOperation {
|
||||
|
||||
private static _idPool = 0;
|
||||
|
||||
constructor(
|
||||
readonly progress: IProgress<Uint8Array>,
|
||||
readonly id: number = ++FileReadOperation._idPool
|
||||
) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
class SearchOperation {
|
||||
|
||||
private static _idPool = 0;
|
||||
|
||||
constructor(
|
||||
readonly progress: (match: IFileMatch) => any,
|
||||
readonly id: number = ++SearchOperation._idPool,
|
||||
readonly matches = new Map<string, IFileMatch>()
|
||||
) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProvider {
|
||||
class RemoteFileSystemProvider implements IFileSystemProvider {
|
||||
|
||||
private readonly _onDidChange = new Emitter<IFileChange[]>();
|
||||
private readonly _registrations: IDisposable[];
|
||||
private readonly _reads = new Map<number, FileReadOperation>();
|
||||
private readonly _searches = new Map<number, SearchOperation>();
|
||||
|
||||
readonly onDidChange: Event<IFileChange[]> = this._onDidChange.event;
|
||||
|
||||
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChange.event;
|
||||
readonly capabilities: FileSystemProviderCapabilities;
|
||||
|
||||
constructor(
|
||||
fileService: IFileService,
|
||||
searchService: ISearchService,
|
||||
private readonly _scheme: string,
|
||||
scheme: string,
|
||||
capabilities: FileSystemProviderCapabilities,
|
||||
private readonly _handle: number,
|
||||
private readonly _proxy: ExtHostFileSystemShape
|
||||
) {
|
||||
this._registrations = [
|
||||
fileService.registerProvider(_scheme, this),
|
||||
searchService.registerSearchResultProvider(this),
|
||||
];
|
||||
this.capabilities = capabilities;
|
||||
this._registrations = [fileService.registerProvider(scheme, this)];
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -119,6 +68,16 @@ class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProv
|
||||
this._onDidChange.dispose();
|
||||
}
|
||||
|
||||
watch(resource: URI, opts: IWatchOptions) {
|
||||
const session = Math.random();
|
||||
this._proxy.$watch(this._handle, session, resource, opts);
|
||||
return {
|
||||
dispose: () => {
|
||||
this._proxy.$unwatch(this._handle, session);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$onFileSystemChange(changes: IFileChangeDto[]): void {
|
||||
this._onDidChange.fire(changes.map(RemoteFileSystemProvider._createFileChange));
|
||||
}
|
||||
@@ -129,98 +88,42 @@ class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProv
|
||||
|
||||
// --- forwarding calls
|
||||
|
||||
utimes(resource: URI, mtime: number, atime: number): TPromise<IStat, any> {
|
||||
return this._proxy.$utimes(this._handle, resource, mtime, atime);
|
||||
}
|
||||
stat(resource: URI): TPromise<IStat, any> {
|
||||
return this._proxy.$stat(this._handle, resource);
|
||||
}
|
||||
read(resource: URI, offset: number, count: number, progress: IProgress<Uint8Array>): TPromise<number, any> {
|
||||
const read = new FileReadOperation(progress);
|
||||
this._reads.set(read.id, read);
|
||||
return this._proxy.$read(this._handle, read.id, offset, count, resource).then(value => {
|
||||
this._reads.delete(read.id);
|
||||
return value;
|
||||
stat(resource: URI): TPromise<IStat> {
|
||||
return this._proxy.$stat(this._handle, resource).then(undefined, err => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
reportFileChunk(session: number, chunk: number[]): void {
|
||||
this._reads.get(session).progress.report(Buffer.from(chunk));
|
||||
|
||||
readFile(resource: URI): TPromise<Uint8Array, any> {
|
||||
return this._proxy.$readFile(this._handle, resource).then(encoded => {
|
||||
return Buffer.from(encoded, 'base64');
|
||||
});
|
||||
}
|
||||
write(resource: URI, content: Uint8Array): TPromise<void, any> {
|
||||
return this._proxy.$write(this._handle, resource, [].slice.call(content));
|
||||
|
||||
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise<void, any> {
|
||||
let encoded = Buffer.isBuffer(content)
|
||||
? content.toString('base64')
|
||||
: Buffer.from(content.buffer, content.byteOffset, content.byteLength).toString('base64');
|
||||
return this._proxy.$writeFile(this._handle, resource, encoded, opts);
|
||||
}
|
||||
unlink(resource: URI): TPromise<void, any> {
|
||||
return this._proxy.$unlink(this._handle, resource);
|
||||
|
||||
delete(resource: URI): TPromise<void, any> {
|
||||
return this._proxy.$delete(this._handle, resource);
|
||||
}
|
||||
move(resource: URI, target: URI): TPromise<IStat, any> {
|
||||
return this._proxy.$move(this._handle, resource, target);
|
||||
}
|
||||
mkdir(resource: URI): TPromise<IStat, any> {
|
||||
|
||||
mkdir(resource: URI): TPromise<void, any> {
|
||||
return this._proxy.$mkdir(this._handle, resource);
|
||||
}
|
||||
readdir(resource: URI): TPromise<[URI, IStat][], any> {
|
||||
return this._proxy.$readdir(this._handle, resource).then(data => {
|
||||
return data.map(tuple => <[URI, IStat]>[URI.revive(tuple[0]), tuple[1]]);
|
||||
});
|
||||
}
|
||||
rmdir(resource: URI): TPromise<void, any> {
|
||||
return this._proxy.$rmdir(this._handle, resource);
|
||||
|
||||
readdir(resource: URI): TPromise<[string, FileType][], any> {
|
||||
return this._proxy.$readdir(this._handle, resource);
|
||||
}
|
||||
|
||||
// --- search
|
||||
|
||||
search(query: ISearchQuery): PPromise<ISearchComplete, ISearchProgressItem> {
|
||||
|
||||
if (isFalsyOrEmpty(query.folderQueries)) {
|
||||
return PPromise.as(undefined);
|
||||
}
|
||||
|
||||
let includes = { ...query.includePattern };
|
||||
let excludes = { ...query.excludePattern };
|
||||
|
||||
for (const folderQuery of query.folderQueries) {
|
||||
if (folderQuery.folder.scheme === this._scheme) {
|
||||
includes = { ...includes, ...folderQuery.includePattern };
|
||||
excludes = { ...excludes, ...folderQuery.excludePattern };
|
||||
}
|
||||
}
|
||||
|
||||
return new PPromise((resolve, reject, report) => {
|
||||
|
||||
const search = new SearchOperation(report);
|
||||
this._searches.set(search.id, search);
|
||||
|
||||
const promise = query.type === QueryType.File
|
||||
? this._proxy.$findFiles(this._handle, search.id, query.filePattern)
|
||||
: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, { excludes: Object.keys(excludes), includes: Object.keys(includes) });
|
||||
|
||||
promise.then(() => {
|
||||
this._searches.delete(search.id);
|
||||
resolve(({ results: values(search.matches), stats: undefined }));
|
||||
}, err => {
|
||||
this._searches.delete(search.id);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
rename(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise<void, any> {
|
||||
return this._proxy.$rename(this._handle, resource, target, opts);
|
||||
}
|
||||
|
||||
handleFindMatch(session: number, dataOrUri: UriComponents | [UriComponents, ILineMatch]): void {
|
||||
let resource: URI;
|
||||
let match: ILineMatch;
|
||||
|
||||
if (Array.isArray(dataOrUri)) {
|
||||
resource = URI.revive(dataOrUri[0]);
|
||||
match = dataOrUri[1];
|
||||
} else {
|
||||
resource = URI.revive(dataOrUri);
|
||||
}
|
||||
|
||||
const { matches } = this._searches.get(session);
|
||||
if (!matches.has(resource.toString())) {
|
||||
matches.set(resource.toString(), { resource, lineMatches: [] });
|
||||
}
|
||||
if (match) {
|
||||
matches.get(resource.toString()).lineMatches.push(match);
|
||||
}
|
||||
copy(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise<void, any> {
|
||||
return this._proxy.$copy(this._handle, resource, target, opts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { ExtHostContext, ObjectIdentifier, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { isThenable } from 'vs/base/common/async';
|
||||
@@ -32,7 +32,7 @@ export class HeapService implements IHeapService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private _onGarbageCollection: Emitter<number[]> = new Emitter<number[]>();
|
||||
private readonly _onGarbageCollection: Emitter<number[]> = new Emitter<number[]>();
|
||||
public readonly onGarbageCollection: Event<number[]> = this._onGarbageCollection.event;
|
||||
|
||||
private _activeSignals = new WeakMap<any, object>();
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import * as vscode from 'vscode';
|
||||
import { ITextModel, ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { WorkspaceSymbolProviderRegistry, IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
|
||||
@@ -15,7 +14,7 @@ import { wireCancellationToken } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Position as EditorPosition } from 'vs/editor/common/core/position';
|
||||
import { Range as EditorRange } from 'vs/editor/common/core/range';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, CodeActionDto, reviveWorkspaceEditDto } from '../node/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter } from '../node/extHost.protocol';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { IHeapService } from './mainThreadHeapService';
|
||||
@@ -97,7 +96,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- outline
|
||||
|
||||
$registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentSymbolProvider>{
|
||||
provideDocumentSymbols: (model: ITextModel, token: CancellationToken): Thenable<modes.SymbolInformation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentSymbols(handle, model.uri)).then(MainThreadLanguageFeatures._reviveSymbolInformationDto);
|
||||
@@ -107,7 +106,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- code lens
|
||||
|
||||
$registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): void {
|
||||
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void {
|
||||
|
||||
const provider = <modes.CodeLensProvider>{
|
||||
provideCodeLenses: (model: ITextModel, token: CancellationToken): modes.ICodeLensSymbol[] | Thenable<modes.ICodeLensSymbol[]> => {
|
||||
@@ -136,7 +135,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- declaration
|
||||
|
||||
$registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerDeclaractionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.DefinitionProviderRegistry.register(toLanguageSelector(selector), <modes.DefinitionProvider>{
|
||||
provideDefinition: (model, position, token): Thenable<modes.Definition> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto);
|
||||
@@ -144,7 +143,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
});
|
||||
}
|
||||
|
||||
$registerImplementationSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.ImplementationProviderRegistry.register(toLanguageSelector(selector), <modes.ImplementationProvider>{
|
||||
provideImplementation: (model, position, token): Thenable<modes.Definition> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideImplementation(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto);
|
||||
@@ -152,7 +151,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
});
|
||||
}
|
||||
|
||||
$registerTypeDefinitionSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.TypeDefinitionProviderRegistry.register(toLanguageSelector(selector), <modes.TypeDefinitionProvider>{
|
||||
provideTypeDefinition: (model, position, token): Thenable<modes.Definition> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideTypeDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto);
|
||||
@@ -162,7 +161,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- extra info
|
||||
|
||||
$registerHoverProvider(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.HoverProviderRegistry.register(toLanguageSelector(selector), <modes.HoverProvider>{
|
||||
provideHover: (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.Hover> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideHover(handle, model.uri, position));
|
||||
@@ -172,7 +171,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- occurrences
|
||||
|
||||
$registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.DocumentHighlightProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentHighlightProvider>{
|
||||
provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentHighlights(handle, model.uri, position));
|
||||
@@ -182,7 +181,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- references
|
||||
|
||||
$registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerReferenceSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.ReferenceProviderRegistry.register(toLanguageSelector(selector), <modes.ReferenceProvider>{
|
||||
provideReferences: (model: ITextModel, position: EditorPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<modes.Location[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideReferences(handle, model.uri, position, context)).then(MainThreadLanguageFeatures._reviveLocationDto);
|
||||
@@ -192,17 +191,18 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- quick fix
|
||||
|
||||
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], providedCodeActionKinds?: string[]): void {
|
||||
this._registrations[handle] = modes.CodeActionProviderRegistry.register(toLanguageSelector(selector), <modes.CodeActionProvider>{
|
||||
provideCodeActions: (model: ITextModel, range: EditorRange, context: modes.CodeActionContext, token: CancellationToken): Thenable<modes.CodeAction[]> => {
|
||||
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCodeActions(handle, model.uri, range, context))).then(MainThreadLanguageFeatures._reviveCodeActionDto);
|
||||
}
|
||||
},
|
||||
providedCodeActionKinds
|
||||
});
|
||||
}
|
||||
|
||||
// --- formatting
|
||||
|
||||
$registerDocumentFormattingSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentFormattingEditProvider>{
|
||||
provideDocumentFormattingEdits: (model: ITextModel, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options));
|
||||
@@ -210,7 +210,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
});
|
||||
}
|
||||
|
||||
$registerRangeFormattingSupport(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentRangeFormattingEditProvider>{
|
||||
provideDocumentRangeFormattingEdits: (model: ITextModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options));
|
||||
@@ -218,7 +218,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
});
|
||||
}
|
||||
|
||||
$registerOnTypeFormattingSupport(handle: number, selector: vscode.DocumentSelector, autoFormatTriggerCharacters: string[]): void {
|
||||
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[]): void {
|
||||
this._registrations[handle] = modes.OnTypeFormattingEditProviderRegistry.register(toLanguageSelector(selector), <modes.OnTypeFormattingEditProvider>{
|
||||
|
||||
autoFormatTriggerCharacters,
|
||||
@@ -251,20 +251,21 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- rename
|
||||
|
||||
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector, supportsResolveInitialValues: boolean): void {
|
||||
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportResolveLocation: boolean): void {
|
||||
|
||||
this._registrations[handle] = modes.RenameProviderRegistry.register(toLanguageSelector(selector), <modes.RenameProvider>{
|
||||
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken): Thenable<modes.WorkspaceEdit> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName)).then(reviveWorkspaceEditDto);
|
||||
},
|
||||
resolveInitialRenameValue: supportsResolveInitialValues
|
||||
? (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.RenameInitialValue> => wireCancellationToken(token, this._proxy.$resolveInitialRenameValue(handle, model.uri, position))
|
||||
resolveRenameLocation: supportResolveLocation
|
||||
? (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.RenameLocation> => wireCancellationToken(token, this._proxy.$resolveRenameLocation(handle, model.uri, position))
|
||||
: undefined
|
||||
});
|
||||
}
|
||||
|
||||
// --- suggest
|
||||
|
||||
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[], supportsResolveDetails: boolean): void {
|
||||
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void {
|
||||
this._registrations[handle] = modes.SuggestRegistry.register(toLanguageSelector(selector), <modes.ISuggestSupport>{
|
||||
triggerCharacters,
|
||||
provideCompletionItems: (model: ITextModel, position: EditorPosition, context: modes.SuggestContext, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
@@ -287,7 +288,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- parameter hints
|
||||
|
||||
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): void {
|
||||
$registerSignatureHelpProvider(handle: number, selector: ISerializedDocumentFilter[], triggerCharacter: string[]): void {
|
||||
this._registrations[handle] = modes.SignatureHelpProviderRegistry.register(toLanguageSelector(selector), <modes.SignatureHelpProvider>{
|
||||
|
||||
signatureHelpTriggerCharacters: triggerCharacter,
|
||||
@@ -301,7 +302,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- links
|
||||
|
||||
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
this._registrations[handle] = modes.LinkProviderRegistry.register(toLanguageSelector(selector), <modes.LinkProvider>{
|
||||
provideLinks: (model, token) => {
|
||||
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideDocumentLinks(handle, model.uri)));
|
||||
@@ -314,7 +315,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- colors
|
||||
|
||||
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
const proxy = this._proxy;
|
||||
this._registrations[handle] = modes.ColorProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentColorProvider>{
|
||||
provideDocumentColors: (model, token) => {
|
||||
@@ -348,11 +349,11 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- folding
|
||||
|
||||
$registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void {
|
||||
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
|
||||
const proxy = this._proxy;
|
||||
this._registrations[handle] = modes.FoldingProviderRegistry.register(toLanguageSelector(selector), <modes.FoldingProvider>{
|
||||
provideFoldingRanges: (model, token) => {
|
||||
return wireCancellationToken(token, proxy.$provideFoldingRanges(handle, model.uri));
|
||||
this._registrations[handle] = modes.FoldingRangeProviderRegistry.register(toLanguageSelector(selector), <modes.FoldingRangeProvider>{
|
||||
provideFoldingRanges: (model, context, token) => {
|
||||
return wireCancellationToken(token, proxy.$provideFoldingRanges(handle, model.uri, context));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import * as nls from 'vs/nls';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { MainThreadMessageServiceShape, MainContext, IExtHostContext, MainThreadMessageOptions } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadMessageService)
|
||||
export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
@@ -24,7 +24,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
extHostContext: IExtHostContext,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IChoiceService private readonly _choiceService: IChoiceService,
|
||||
@IDialogService private readonly _dialogService: IDialogService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService
|
||||
) {
|
||||
//
|
||||
@@ -71,11 +71,11 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
|
||||
let source: string;
|
||||
if (extension) {
|
||||
source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
|
||||
source = nls.localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
|
||||
}
|
||||
|
||||
if (!source) {
|
||||
source = localize('defaultSource', "Extension");
|
||||
source = nls.localize('defaultSource', "Extension");
|
||||
}
|
||||
|
||||
const secondaryActions: IAction[] = [];
|
||||
@@ -93,6 +93,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
// if promise has not been resolved yet, now is the time to ensure a return value
|
||||
// otherwise if already resolved it means the user clicked one of the buttons
|
||||
once(messageHandle.onDidClose)(() => {
|
||||
dispose(...primaryActions, ...secondaryActions);
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
@@ -101,7 +102,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
private _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number> {
|
||||
let cancelId: number | undefined = void 0;
|
||||
|
||||
const options = commands.map((command, index) => {
|
||||
const buttons = commands.map((command, index) => {
|
||||
if (command.isCloseAffordance === true) {
|
||||
cancelId = index;
|
||||
}
|
||||
@@ -110,16 +111,16 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
});
|
||||
|
||||
if (cancelId === void 0) {
|
||||
if (options.length > 0) {
|
||||
options.push(nls.localize('cancel', "Cancel"));
|
||||
if (buttons.length > 0) {
|
||||
buttons.push(nls.localize('cancel', "Cancel"));
|
||||
} else {
|
||||
options.push(nls.localize('ok', "OK"));
|
||||
buttons.push(nls.localize('ok', "OK"));
|
||||
}
|
||||
|
||||
cancelId = options.length - 1;
|
||||
cancelId = buttons.length - 1;
|
||||
}
|
||||
|
||||
return this._choiceService.choose(severity, message, options, cancelId, true)
|
||||
return this._dialogService.show(severity, message, buttons, { cancelId })
|
||||
.then(result => result === commands.length ? undefined : commands[result].handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'use strict';
|
||||
|
||||
import { IProgressService2, IProgress, IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
|
||||
import { MainThreadProgressShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { MainThreadProgressShape, MainContext, IExtHostContext, ExtHostProgressShape, ExtHostContext } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadProgress)
|
||||
@@ -13,11 +13,13 @@ export class MainThreadProgress implements MainThreadProgressShape {
|
||||
|
||||
private _progressService: IProgressService2;
|
||||
private _progress = new Map<number, { resolve: Function, progress: IProgress<IProgressStep> }>();
|
||||
private _proxy: ExtHostProgressShape;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IProgressService2 progressService: IProgressService2
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostProgress);
|
||||
this._progressService = progressService;
|
||||
}
|
||||
|
||||
@@ -28,7 +30,8 @@ export class MainThreadProgress implements MainThreadProgressShape {
|
||||
|
||||
$startProgress(handle: number, options: IProgressOptions): void {
|
||||
const task = this._createTask(handle);
|
||||
this._progressService.withProgress(options, task);
|
||||
|
||||
this._progressService.withProgress(options, task, () => this._proxy.$acceptProgressCanceled(handle));
|
||||
}
|
||||
|
||||
$progressReport(handle: number, message: IProgressStep): void {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { IQuickOpenService, IPickOptions, IInputOptions } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { InputBoxOptions } from 'vscode';
|
||||
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
@@ -16,6 +17,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
|
||||
private _proxy: ExtHostQuickOpenShape;
|
||||
private _quickOpenService: IQuickOpenService;
|
||||
private _quickInputService: IQuickInputService;
|
||||
private _doSetItems: (items: MyQuickPickItems[]) => any;
|
||||
private _doSetError: (error: Error) => any;
|
||||
private _contents: TPromise<MyQuickPickItems[]>;
|
||||
@@ -23,16 +25,18 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService,
|
||||
@IQuickInputService quickInputService: IQuickInputService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostQuickOpen);
|
||||
this._quickOpenService = quickOpenService;
|
||||
this._quickInputService = quickInputService;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
}
|
||||
|
||||
$show(options: IPickOptions): TPromise<number> {
|
||||
$show(options: IPickOptions): TPromise<number | number[]> {
|
||||
|
||||
const myToken = ++this._token;
|
||||
|
||||
@@ -50,16 +54,29 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
};
|
||||
});
|
||||
|
||||
return asWinJsPromise(token => this._quickOpenService.pick(this._contents, options, token)).then(item => {
|
||||
if (item) {
|
||||
return item.handle;
|
||||
}
|
||||
return undefined;
|
||||
}, undefined, progress => {
|
||||
if (progress) {
|
||||
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
|
||||
}
|
||||
});
|
||||
if (options.canSelectMany) {
|
||||
return asWinJsPromise(token => this._quickInputService.pick(this._contents, options, token)).then(items => {
|
||||
if (items) {
|
||||
return items.map(item => item.handle);
|
||||
}
|
||||
return undefined;
|
||||
}, undefined, progress => {
|
||||
if (progress) {
|
||||
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return asWinJsPromise(token => this._quickOpenService.pick(this._contents, options, token)).then(item => {
|
||||
if (item) {
|
||||
return item.handle;
|
||||
}
|
||||
return undefined;
|
||||
}, undefined, progress => {
|
||||
if (progress) {
|
||||
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$setItems(items: MyQuickPickItems[]): TPromise<any> {
|
||||
@@ -97,6 +114,6 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
};
|
||||
}
|
||||
|
||||
return asWinJsPromise(token => this._quickOpenService.input(inputOptions, token));
|
||||
return asWinJsPromise(token => this._quickInputService.input(inputOptions, token));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI, { UriComponents } from 'vs/base/common/uri';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation } from 'vs/workbench/services/scm/common/scm';
|
||||
|
||||
@@ -28,6 +28,16 @@ import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common
|
||||
import { localize } from 'vs/nls';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
|
||||
import { CodeAction } from 'vs/editor/common/modes';
|
||||
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
|
||||
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { ICodeActionsOnSaveOptions } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
export interface ISaveParticipantParticipant extends ISaveParticipant {
|
||||
// progressMessage: string;
|
||||
@@ -49,7 +59,7 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
|
||||
}
|
||||
|
||||
private doTrimTrailingWhitespace(model: ITextModel, isAutoSaved: boolean): void {
|
||||
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
|
||||
let prevSelection: Selection[] = [];
|
||||
const cursors: Position[] = [];
|
||||
|
||||
let editor = findEditor(model, this.codeEditorService);
|
||||
@@ -59,6 +69,12 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
|
||||
prevSelection = editor.getSelections();
|
||||
if (isAutoSaved) {
|
||||
cursors.push(...prevSelection.map(s => new Position(s.positionLineNumber, s.positionColumn)));
|
||||
const snippetsRange = SnippetController2.get(editor).getSessionEnclosingRange();
|
||||
if (snippetsRange) {
|
||||
for (let lineNumber = snippetsRange.startLineNumber; lineNumber <= snippetsRange.endLineNumber; lineNumber++) {
|
||||
cursors.push(new Position(lineNumber, model.getLineMaxColumn(lineNumber)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +129,7 @@ export class FinalNewLineParticipant implements ISaveParticipantParticipant {
|
||||
return;
|
||||
}
|
||||
|
||||
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
|
||||
let prevSelection: Selection[] = [];
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
prevSelection = editor.getSelections();
|
||||
@@ -150,7 +166,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant
|
||||
return;
|
||||
}
|
||||
|
||||
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
|
||||
let prevSelection: Selection[] = [];
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
prevSelection = editor.getSelections();
|
||||
@@ -165,7 +181,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant
|
||||
currentLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(currentLine) === -1;
|
||||
}
|
||||
|
||||
const deletionRange = new Range(currentLineNumber + 1, 1, lineCount + 1, 1);
|
||||
const deletionRange = model.validateRange(new Range(currentLineNumber + 1, 1, lineCount + 1, 1));
|
||||
if (!deletionRange.isEmpty()) {
|
||||
model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], edits => prevSelection);
|
||||
}
|
||||
@@ -197,8 +213,10 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
const versionNow = model.getVersionId();
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
|
||||
const timeout = this._configurationService.getValue('editor.formatOnSaveTimeout', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() });
|
||||
|
||||
return new Promise<ISingleEditOperation[]>((resolve, reject) => {
|
||||
setTimeout(reject, 750);
|
||||
setTimeout(() => reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout)), timeout);
|
||||
getDocumentFormattingEdits(model, { tabSize, insertSpaces })
|
||||
.then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits))
|
||||
.then(resolve, err => {
|
||||
@@ -222,7 +240,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
}
|
||||
|
||||
private _editsWithEditor(editor: ICodeEditor, edits: ISingleEditOperation[]): void {
|
||||
EditOperationsCommand.execute(editor, edits, false);
|
||||
EditOperationsCommand.execute(editor, edits);
|
||||
}
|
||||
|
||||
private _editWithModel(model: ITextModel, edits: ISingleEditOperation[]): void {
|
||||
@@ -249,6 +267,59 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
}
|
||||
}
|
||||
|
||||
class CodeActionOnParticipant implements ISaveParticipant {
|
||||
|
||||
constructor(
|
||||
@ITextModelService private readonly _textModelService: ITextModelService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) { }
|
||||
|
||||
async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
|
||||
if (env.reason === SaveReason.AUTO) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const model = editorModel.textEditorModel;
|
||||
const editor = findEditor(model, this._codeEditorService);
|
||||
if (!editor) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() };
|
||||
const setting = this._configurationService.getValue<ICodeActionsOnSaveOptions>('editor.codeActionsOnSave', settingsOverrides);
|
||||
if (!setting) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const codeActionsOnSave = Object.keys(setting).filter(x => setting[x]).map(x => new CodeActionKind(x));
|
||||
if (!codeActionsOnSave.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const timeout = this._configurationService.getValue<number>('editor.codeActionsOnSaveTimeout', settingsOverrides);
|
||||
|
||||
return new Promise<CodeAction[]>((resolve, reject) => {
|
||||
setTimeout(() => reject(localize('codeActionsOnSave.didTimeout', "Aborted codeActionsOnSave after {0}ms", timeout)), timeout);
|
||||
this.getActionsToRun(model, codeActionsOnSave).then(resolve);
|
||||
}).then(actionsToRun => this.applyCodeActions(actionsToRun, editor));
|
||||
}
|
||||
|
||||
private async applyCodeActions(actionsToRun: CodeAction[], editor: ICodeEditor) {
|
||||
for (const action of actionsToRun) {
|
||||
await applyCodeAction(action, this._textModelService, this._fileService, this._commandService, editor);
|
||||
}
|
||||
}
|
||||
|
||||
private async getActionsToRun(model: ITextModel, codeActionsOnSave: CodeActionKind[]) {
|
||||
const actions = await getCodeActions(model, model.getFullModelRange(), { kind: CodeActionKind.Source, includeSourceActions: true });
|
||||
const actionsToRun = actions.filter(returnedAction => returnedAction.kind && codeActionsOnSave.some(onSaveKind => onSaveKind.contains(returnedAction.kind)));
|
||||
return actionsToRun;
|
||||
}
|
||||
}
|
||||
|
||||
class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
private _proxy: ExtHostDocumentSaveParticipantShape;
|
||||
@@ -259,14 +330,14 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
|
||||
|
||||
if (editorModel.textEditorModel.isTooLargeForHavingARichMode()) {
|
||||
if (!shouldSynchronizeModel(editorModel.textEditorModel)) {
|
||||
// the model never made it to the extension
|
||||
// host meaning we cannot participate in its save
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
setTimeout(reject, 1750);
|
||||
setTimeout(() => reject(localize('timeout.onWillSave', "Aborted onWillSaveTextDocument-event after 1750ms")), 1750);
|
||||
this._proxy.$participateInSave(editorModel.getResource(), env.reason).then(values => {
|
||||
for (const success of values) {
|
||||
if (!success) {
|
||||
@@ -293,6 +364,7 @@ export class SaveParticipant implements ISaveParticipant {
|
||||
) {
|
||||
this._saveParticipants = [
|
||||
instantiationService.createInstance(TrimWhitespaceParticipant),
|
||||
instantiationService.createInstance(CodeActionOnParticipant),
|
||||
instantiationService.createInstance(FormatOnSaveParticipant),
|
||||
instantiationService.createInstance(FinalNewLineParticipant),
|
||||
instantiationService.createInstance(TrimFinalNewLinesParticipant),
|
||||
@@ -312,7 +384,7 @@ export class SaveParticipant implements ISaveParticipant {
|
||||
const promiseFactory = this._saveParticipants.map(p => () => {
|
||||
return Promise.resolve(p.participate(model, env));
|
||||
});
|
||||
return sequence(promiseFactory).then(() => { }, err => this._logService.error(err));
|
||||
return sequence(promiseFactory).then(() => { }, err => this._logService.warn(err));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
148
src/vs/workbench/api/electron-browser/mainThreadSearch.ts
Normal file
148
src/vs/workbench/api/electron-browser/mainThreadSearch.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import URI, { UriComponents } from 'vs/base/common/uri';
|
||||
import { PPromise, TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IFileMatch, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, QueryType } from 'vs/platform/search/common/search';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { ExtHostContext, ExtHostSearchShape, IExtHostContext, MainContext, MainThreadSearchShape } from '../node/extHost.protocol';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadSearch)
|
||||
export class MainThreadSearch implements MainThreadSearchShape {
|
||||
|
||||
private readonly _proxy: ExtHostSearchShape;
|
||||
private readonly _searchProvider = new Map<number, RemoteSearchProvider>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ISearchService private readonly _searchService: ISearchService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSearch);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._searchProvider.forEach(value => dispose());
|
||||
this._searchProvider.clear();
|
||||
}
|
||||
|
||||
$registerSearchProvider(handle: number, scheme: string): void {
|
||||
this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, scheme, handle, this._proxy));
|
||||
}
|
||||
|
||||
$unregisterProvider(handle: number): void {
|
||||
dispose(this._searchProvider.get(handle));
|
||||
this._searchProvider.delete(handle);
|
||||
}
|
||||
|
||||
$handleFindMatch(handle: number, session, data: UriComponents | [UriComponents, ILineMatch]): void {
|
||||
this._searchProvider.get(handle).handleFindMatch(session, data);
|
||||
}
|
||||
}
|
||||
|
||||
class SearchOperation {
|
||||
|
||||
private static _idPool = 0;
|
||||
|
||||
constructor(
|
||||
readonly progress: (match: IFileMatch) => any,
|
||||
readonly id: number = ++SearchOperation._idPool,
|
||||
readonly matches = new Map<string, IFileMatch>()
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
addMatch(resource: URI, match: ILineMatch): void {
|
||||
if (!this.matches.has(resource.toString())) {
|
||||
this.matches.set(resource.toString(), { resource, lineMatches: [] });
|
||||
}
|
||||
if (match) {
|
||||
this.matches.get(resource.toString()).lineMatches.push(match);
|
||||
}
|
||||
this.progress(this.matches.get(resource.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteSearchProvider implements ISearchResultProvider {
|
||||
|
||||
private readonly _registrations: IDisposable[];
|
||||
private readonly _searches = new Map<number, SearchOperation>();
|
||||
|
||||
|
||||
constructor(
|
||||
searchService: ISearchService,
|
||||
private readonly _scheme: string,
|
||||
private readonly _handle: number,
|
||||
private readonly _proxy: ExtHostSearchShape
|
||||
) {
|
||||
this._registrations = [searchService.registerSearchResultProvider(this)];
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._registrations);
|
||||
}
|
||||
|
||||
search(query: ISearchQuery): PPromise<ISearchComplete, ISearchProgressItem> {
|
||||
|
||||
if (isFalsyOrEmpty(query.folderQueries)) {
|
||||
return PPromise.as(undefined);
|
||||
}
|
||||
|
||||
let includes = { ...query.includePattern };
|
||||
let excludes = { ...query.excludePattern };
|
||||
|
||||
for (const folderQuery of query.folderQueries) {
|
||||
if (folderQuery.folder.scheme === this._scheme) {
|
||||
includes = { ...includes, ...folderQuery.includePattern };
|
||||
excludes = { ...excludes, ...folderQuery.excludePattern };
|
||||
}
|
||||
}
|
||||
|
||||
let outer: TPromise;
|
||||
|
||||
return new PPromise((resolve, reject, report) => {
|
||||
|
||||
const search = new SearchOperation(report);
|
||||
this._searches.set(search.id, search);
|
||||
|
||||
outer = query.type === QueryType.File
|
||||
? this._proxy.$provideFileSearchResults(this._handle, search.id, query.filePattern)
|
||||
: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, { excludes: Object.keys(excludes), includes: Object.keys(includes) });
|
||||
|
||||
outer.then(() => {
|
||||
this._searches.delete(search.id);
|
||||
resolve(({ results: values(search.matches), stats: undefined }));
|
||||
}, err => {
|
||||
this._searches.delete(search.id);
|
||||
reject(err);
|
||||
});
|
||||
}, () => {
|
||||
if (outer) {
|
||||
outer.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleFindMatch(session: number, dataOrUri: UriComponents | [UriComponents, ILineMatch]): void {
|
||||
if (!this._searches.has(session)) {
|
||||
// ignore...
|
||||
return;
|
||||
}
|
||||
let resource: URI;
|
||||
let match: ILineMatch;
|
||||
|
||||
if (Array.isArray(dataOrUri)) {
|
||||
resource = URI.revive(dataOrUri[0]);
|
||||
match = dataOrUri[1];
|
||||
} else {
|
||||
resource = URI.revive(dataOrUri);
|
||||
}
|
||||
|
||||
this._searches.get(session).addMatch(resource, match);
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,337 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { ContributedTask, ExtensionTaskSourceTransfer } from 'vs/workbench/parts/tasks/common/tasks';
|
||||
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
|
||||
|
||||
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
|
||||
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
import {
|
||||
ContributedTask, ExtensionTaskSourceTransfer, TaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind,
|
||||
PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RevealKind, PanelKind
|
||||
} from 'vs/workbench/parts/tasks/common/tasks';
|
||||
import { ITaskService, TaskFilter } from 'vs/workbench/parts/tasks/common/taskService';
|
||||
|
||||
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import {
|
||||
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
|
||||
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO
|
||||
} from 'vs/workbench/api/shared/tasks';
|
||||
|
||||
export { TaskDTO, TaskHandleDTO, TaskExecutionDTO, TaskFilterDTO };
|
||||
|
||||
namespace TaskExecutionDTO {
|
||||
export function from(value: TaskExecution): TaskExecutionDTO {
|
||||
return {
|
||||
id: value.id,
|
||||
task: TaskDTO.from(value.task)
|
||||
};
|
||||
}
|
||||
export function to(value: TaskExecutionDTO, workspace: IWorkspaceContextService): TaskExecution {
|
||||
return {
|
||||
id: value.id,
|
||||
task: TaskDTO.to(value.task, workspace)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskDefinitionDTO {
|
||||
export function from(value: TaskIdentifier): TaskDefinitionDTO {
|
||||
let result = Objects.assign(Object.create(null), value);
|
||||
delete result._key;
|
||||
return result;
|
||||
}
|
||||
export function to(value: TaskDefinitionDTO): TaskIdentifier {
|
||||
const hash = crypto.createHash('md5');
|
||||
hash.update(JSON.stringify(value));
|
||||
let result = Objects.assign(Object.create(null), value);
|
||||
result._key = hash.digest('hex');
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskPresentationOptionsDTO {
|
||||
export function from(value: PresentationOptions): TaskPresentationOptionsDTO {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
}
|
||||
export function to(value: TaskPresentationOptionsDTO): PresentationOptions {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProcessExecutionOptionsDTO {
|
||||
export function from(value: CommandOptions): ProcessExecutionOptionsDTO {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
cwd: value.cwd,
|
||||
env: value.env
|
||||
};
|
||||
}
|
||||
export function to(value: ProcessExecutionOptionsDTO): CommandOptions {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
cwd: value.cwd,
|
||||
env: value.env
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProcessExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
|
||||
let candidate = value as ProcessExecutionDTO;
|
||||
return candidate && !!candidate.process;
|
||||
}
|
||||
export function from(value: CommandConfiguration): ProcessExecutionDTO {
|
||||
let process: string = Types.isString(value.name) ? value.name : value.name.value;
|
||||
let args: string[] = value.args ? value.args.map(value => Types.isString(value) ? value : value.value) : [];
|
||||
let result: ProcessExecutionDTO = {
|
||||
process: process,
|
||||
args: args
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ProcessExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ProcessExecutionDTO): CommandConfiguration {
|
||||
let result: CommandConfiguration = {
|
||||
runtime: RuntimeType.Process,
|
||||
name: value.process,
|
||||
args: value.args,
|
||||
presentation: undefined
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ProcessExecutionOptionsDTO.to(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellExecutionOptionsDTO {
|
||||
export function from(value: CommandOptions): ShellExecutionOptionsDTO {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let result: ShellExecutionOptionsDTO = {
|
||||
cwd: value.cwd,
|
||||
env: value.env
|
||||
};
|
||||
if (value.shell) {
|
||||
result.executable = value.shell.executable;
|
||||
result.shellArgs = value.shell.args;
|
||||
result.shellQuoting = value.shell.quoting;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ShellExecutionOptionsDTO): CommandOptions {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let result: CommandOptions = {
|
||||
cwd: value.cwd,
|
||||
env: value.env
|
||||
};
|
||||
if (value.executable) {
|
||||
result.shell = {
|
||||
executable: value.executable
|
||||
};
|
||||
if (value.shellArgs) {
|
||||
result.shell.args = value.shellArgs;
|
||||
}
|
||||
if (value.shellQuoting) {
|
||||
result.shell.quoting = value.shellQuoting;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO {
|
||||
let candidate = value as ShellExecutionDTO;
|
||||
return candidate && (!!candidate.commandLine || !!candidate.command);
|
||||
}
|
||||
export function from(value: CommandConfiguration): ShellExecutionDTO {
|
||||
let result: ShellExecutionDTO = {};
|
||||
if (value.name && Types.isString(value.name) && (value.args === void 0 || value.args === null || value.args.length === 0)) {
|
||||
result.commandLine = value.name;
|
||||
} else {
|
||||
result.command = value.name;
|
||||
result.args = value.args;
|
||||
}
|
||||
if (value.options) {
|
||||
result.options = ShellExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ShellExecutionDTO): CommandConfiguration {
|
||||
let result: CommandConfiguration = {
|
||||
runtime: RuntimeType.Shell,
|
||||
name: value.commandLine ? value.commandLine : value.command,
|
||||
args: value.args,
|
||||
presentation: undefined
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ShellExecutionOptionsDTO.to(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskSourceDTO {
|
||||
export function from(value: TaskSource): TaskSourceDTO {
|
||||
let result: TaskSourceDTO = {
|
||||
label: value.label
|
||||
};
|
||||
if (value.kind === TaskSourceKind.Extension) {
|
||||
result.extensionId = value.extension;
|
||||
if (value.workspaceFolder) {
|
||||
result.scope = value.workspaceFolder.uri;
|
||||
} else {
|
||||
result.scope = value.scope;
|
||||
}
|
||||
} else if (value.kind === TaskSourceKind.Workspace) {
|
||||
result.extensionId = '$core';
|
||||
result.scope = value.config.workspaceFolder.uri;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: TaskSourceDTO, workspace: IWorkspaceContextService): ExtensionTaskSource {
|
||||
let scope: TaskScope;
|
||||
let workspaceFolder: IWorkspaceFolder;
|
||||
if (value.scope === void 0) {
|
||||
if (workspace.getWorkspace().folders.length === 0) {
|
||||
scope = TaskScope.Global;
|
||||
workspaceFolder = undefined;
|
||||
} else {
|
||||
scope = TaskScope.Folder;
|
||||
workspaceFolder = workspace.getWorkspace().folders[0];
|
||||
}
|
||||
} else if (typeof value.scope === 'number') {
|
||||
scope = value.scope;
|
||||
} else {
|
||||
scope = TaskScope.Folder;
|
||||
workspaceFolder = workspace.getWorkspaceFolder(URI.revive(value.scope));
|
||||
}
|
||||
let result: ExtensionTaskSource = {
|
||||
kind: TaskSourceKind.Extension,
|
||||
label: value.label,
|
||||
extension: value.extensionId,
|
||||
scope,
|
||||
workspaceFolder
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskHandleDTO {
|
||||
export function is(value: any): value is TaskHandleDTO {
|
||||
let candidate: TaskHandleDTO = value;
|
||||
return candidate && Types.isString(candidate.id) && !!candidate.workspaceFolder;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskDTO {
|
||||
export function from(task: Task): TaskDTO {
|
||||
if (task === void 0 || task === null || (!CustomTask.is(task) && !ContributedTask.is(task))) {
|
||||
return undefined;
|
||||
}
|
||||
let result: TaskDTO = {
|
||||
_id: task._id,
|
||||
name: task.name,
|
||||
definition: TaskDefinitionDTO.from(Task.getTaskDefinition(task)),
|
||||
source: TaskSourceDTO.from(task._source),
|
||||
execution: undefined,
|
||||
presentationOptions: task.command ? TaskPresentationOptionsDTO.from(task.command.presentation) : undefined,
|
||||
isBackground: task.isBackground,
|
||||
problemMatchers: [],
|
||||
hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false
|
||||
};
|
||||
if (task.group) {
|
||||
result.group = task.group;
|
||||
}
|
||||
if (task.command) {
|
||||
if (task.command.runtime === RuntimeType.Process) {
|
||||
result.execution = ProcessExecutionDTO.from(task.command);
|
||||
} else if (task.command.runtime === RuntimeType.Shell) {
|
||||
result.execution = ShellExecutionDTO.from(task.command);
|
||||
}
|
||||
}
|
||||
if (task.problemMatchers) {
|
||||
for (let matcher of task.problemMatchers) {
|
||||
if (Types.isString(matcher)) {
|
||||
result.problemMatchers.push(matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function to(task: TaskDTO, workspace: IWorkspaceContextService): Task {
|
||||
if (typeof task.name !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
let command: CommandConfiguration;
|
||||
if (ShellExecutionDTO.is(task.execution)) {
|
||||
command = ShellExecutionDTO.to(task.execution);
|
||||
} else if (ProcessExecutionDTO.is(task.execution)) {
|
||||
command = ProcessExecutionDTO.to(task.execution);
|
||||
}
|
||||
if (!command) {
|
||||
return undefined;
|
||||
}
|
||||
command.presentation = TaskPresentationOptionsDTO.to(task.presentationOptions);
|
||||
command.presentation = Objects.assign(command.presentation || {}, { echo: true, reveal: RevealKind.Always, focus: false, panel: PanelKind.Shared });
|
||||
|
||||
let source = TaskSourceDTO.to(task.source, workspace);
|
||||
|
||||
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
|
||||
let definition = TaskDefinitionDTO.to(task.definition);
|
||||
let id = `${task.source.extensionId}.${definition._key}`;
|
||||
let result: ContributedTask = {
|
||||
_id: id, // uuidMap.getUUID(identifier),
|
||||
_source: source,
|
||||
_label: label,
|
||||
type: definition.type,
|
||||
defines: definition,
|
||||
name: task.name,
|
||||
identifier: label,
|
||||
group: task.group,
|
||||
command: command,
|
||||
isBackground: !!task.isBackground,
|
||||
problemMatchers: task.problemMatchers.slice(),
|
||||
hasDefinedMatchers: task.hasDefinedMatchers
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskFilterDTO {
|
||||
export function from(value: TaskFilter): TaskFilterDTO {
|
||||
return value;
|
||||
}
|
||||
export function to(value: TaskFilterDTO): TaskFilter {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTask)
|
||||
export class MainThreadTask implements MainThreadTaskShape {
|
||||
@@ -28,6 +349,14 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask);
|
||||
this._activeHandles = Object.create(null);
|
||||
this._taskService.onDidStateChange((event: TaskEvent) => {
|
||||
let task = event.__task;
|
||||
if (event.kind === TaskEventKind.Start) {
|
||||
this._proxy.$taskStarted(TaskExecutionDTO.from(Task.getTaskExecution(task)));
|
||||
} else if (event.kind === TaskEventKind.End) {
|
||||
this._proxy.$taskEnded(TaskExecutionDTO.from(Task.getTaskExecution(task)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -63,4 +392,61 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
delete this._activeHandles[handle];
|
||||
return TPromise.wrap<void>(undefined);
|
||||
}
|
||||
|
||||
public $fetchTasks(filter?: TaskFilterDTO): TPromise<TaskDTO[]> {
|
||||
return this._taskService.tasks(TaskFilterDTO.to(filter)).then((tasks) => {
|
||||
let result: TaskDTO[] = [];
|
||||
for (let task of tasks) {
|
||||
let item = TaskDTO.from(task);
|
||||
if (item) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public $executeTask(value: TaskHandleDTO | TaskDTO): TPromise<TaskExecutionDTO> {
|
||||
return new TPromise<TaskExecutionDTO>((resolve, reject) => {
|
||||
if (TaskHandleDTO.is(value)) {
|
||||
let workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
|
||||
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => {
|
||||
this._taskService.run(task);
|
||||
let result: TaskExecutionDTO = {
|
||||
id: value.id,
|
||||
task: TaskDTO.from(task)
|
||||
};
|
||||
resolve(result);
|
||||
}, (error) => {
|
||||
reject(new Error('Task not found'));
|
||||
});
|
||||
} else {
|
||||
let task = TaskDTO.to(value, this._workspaceContextServer);
|
||||
this._taskService.run(task);
|
||||
let result: TaskExecutionDTO = {
|
||||
id: task._id,
|
||||
task: TaskDTO.from(task)
|
||||
};
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public $terminateTask(id: string): TPromise<void> {
|
||||
return new TPromise<void>((resolve, reject) => {
|
||||
this._taskService.getActiveTasks().then((tasks) => {
|
||||
for (let task of tasks) {
|
||||
if (id === task._id) {
|
||||
this._taskService.terminate(task).then((value) => {
|
||||
resolve(undefined);
|
||||
}, (error) => {
|
||||
reject(undefined);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
reject(new Error('Task to terminate not found'));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class MainThreadTelemetry implements MainThreadTelemetryShape {
|
||||
}
|
||||
|
||||
$publicLog(eventName: string, data: any = Object.create(null)): void {
|
||||
// __GDPR__COMMON__ "pluginHostTelemetry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
// __GDPR__COMMON__ "pluginHostTelemetry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
data[MainThreadTelemetry._name] = true;
|
||||
this._telemetryService.publicLog(eventName, data);
|
||||
}
|
||||
|
||||
@@ -5,25 +5,39 @@
|
||||
'use strict';
|
||||
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
|
||||
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
|
||||
|
||||
private _proxy: ExtHostTerminalServiceShape;
|
||||
private _toDispose: IDisposable[];
|
||||
private _toDispose: IDisposable[] = [];
|
||||
private _terminalProcesses: { [id: number]: ITerminalProcessExtHostProxy } = {};
|
||||
private _dataListeners: { [id: number]: IDisposable } = {};
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ITerminalService private terminalService: ITerminalService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
|
||||
this._toDispose = [];
|
||||
this._toDispose.push(terminalService.onInstanceDisposed((terminalInstance) => this._onTerminalDisposed(terminalInstance)));
|
||||
this._toDispose.push(terminalService.onInstanceProcessIdReady((terminalInstance) => this._onTerminalProcessIdReady(terminalInstance)));
|
||||
this._toDispose.push(terminalService.onInstanceCreated((terminalInstance) => {
|
||||
// Delay this message so the TerminalInstance constructor has a chance to finish and
|
||||
// return the ID normally to the extension host. The ID that is passed here will be used
|
||||
// to register non-extension API terminals in the extension host.
|
||||
setTimeout(() => this._onTerminalOpened(terminalInstance), 100);
|
||||
}));
|
||||
this._toDispose.push(terminalService.onInstanceDisposed(terminalInstance => this._onTerminalDisposed(terminalInstance)));
|
||||
this._toDispose.push(terminalService.onInstanceProcessIdReady(terminalInstance => this._onTerminalProcessIdReady(terminalInstance)));
|
||||
this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
|
||||
|
||||
// Set initial ext host state
|
||||
this.terminalService.terminalInstances.forEach(t => {
|
||||
this._onTerminalOpened(t);
|
||||
t.processReady.then(() => this._onTerminalProcessIdReady(t));
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -43,7 +57,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
ignoreConfigurationCwd: true,
|
||||
env
|
||||
};
|
||||
return TPromise.as(this.terminalService.createInstance(shellLaunchConfig).id);
|
||||
return TPromise.as(this.terminalService.createTerminal(shellLaunchConfig).id);
|
||||
}
|
||||
|
||||
public $show(terminalId: number, preserveFocus: boolean): void {
|
||||
@@ -74,11 +88,59 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
}
|
||||
}
|
||||
|
||||
public $registerOnDataListener(terminalId: number): void {
|
||||
let terminalInstance = this.terminalService.getInstanceFromId(terminalId);
|
||||
if (terminalInstance) {
|
||||
this._dataListeners[terminalId] = terminalInstance.onData(data => this._onTerminalData(terminalId, data));
|
||||
terminalInstance.onDisposed(instance => delete this._dataListeners[terminalId]);
|
||||
}
|
||||
}
|
||||
|
||||
private _onTerminalData(terminalId: number, data: string): void {
|
||||
this._proxy.$acceptTerminalProcessData(terminalId, data);
|
||||
}
|
||||
|
||||
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalClosed(terminalInstance.id);
|
||||
}
|
||||
|
||||
private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title);
|
||||
}
|
||||
|
||||
private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
|
||||
}
|
||||
|
||||
private _onTerminalRequestExtHostProcess(request: ITerminalProcessExtHostRequest): void {
|
||||
this._terminalProcesses[request.proxy.terminalId] = request.proxy;
|
||||
const shellLaunchConfigDto: ShellLaunchConfigDto = {
|
||||
name: request.shellLaunchConfig.name,
|
||||
executable: request.shellLaunchConfig.executable,
|
||||
args: request.shellLaunchConfig.args,
|
||||
cwd: request.shellLaunchConfig.cwd,
|
||||
env: request.shellLaunchConfig.env
|
||||
};
|
||||
this._proxy.$createProcess(request.proxy.terminalId, shellLaunchConfigDto, request.cols, request.rows);
|
||||
request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data));
|
||||
request.proxy.onResize((cols, rows) => this._proxy.$acceptProcessResize(request.proxy.terminalId, cols, rows));
|
||||
request.proxy.onShutdown(() => this._proxy.$acceptProcessShutdown(request.proxy.terminalId));
|
||||
}
|
||||
|
||||
public $sendProcessTitle(terminalId: number, title: string): void {
|
||||
this._terminalProcesses[terminalId].emitTitle(title);
|
||||
}
|
||||
|
||||
public $sendProcessData(terminalId: number, data: string): void {
|
||||
this._terminalProcesses[terminalId].emitData(data);
|
||||
}
|
||||
|
||||
public $sendProcessPid(terminalId: number, pid: number): void {
|
||||
this._terminalProcesses[terminalId].emitPid(pid);
|
||||
}
|
||||
|
||||
public $sendProcessExit(terminalId: number, exitCode: number): void {
|
||||
this._terminalProcesses[terminalId].emitExit(exitCode);
|
||||
delete this._terminalProcesses[terminalId];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { ITreeViewDataProvider, ITreeItem, ICustomViewsService } from 'vs/workbench/common/views';
|
||||
import { ITreeViewDataProvider, ITreeItem, IViewsService } from 'vs/workbench/common/views';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -21,7 +21,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ICustomViewsService private viewsService: ICustomViewsService,
|
||||
@IViewsService private viewsService: IViewsService,
|
||||
@INotificationService private notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
@@ -31,7 +31,12 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
$registerTreeViewDataProvider(treeViewId: string): void {
|
||||
const dataProvider = this._register(new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService));
|
||||
this._dataProviders.set(treeViewId, dataProvider);
|
||||
this.viewsService.getTreeViewer(treeViewId).dataProvider = dataProvider;
|
||||
const treeViewer = this.viewsService.getTreeViewer(treeViewId);
|
||||
if (treeViewer) {
|
||||
treeViewer.dataProvider = dataProvider;
|
||||
} else {
|
||||
this.notificationService.error('No view is registered with id: ' + treeViewId);
|
||||
}
|
||||
}
|
||||
|
||||
$reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options?: { select?: boolean }): TPromise<void> {
|
||||
@@ -59,10 +64,10 @@ type TreeItemHandle = string;
|
||||
|
||||
class TreeViewDataProvider implements ITreeViewDataProvider {
|
||||
|
||||
private _onDidChange: Emitter<ITreeItem[] | undefined | null> = new Emitter<ITreeItem[] | undefined | null>();
|
||||
private readonly _onDidChange: Emitter<ITreeItem[] | undefined | null> = new Emitter<ITreeItem[] | undefined | null>();
|
||||
readonly onDidChange: Event<ITreeItem[] | undefined | null> = this._onDidChange.event;
|
||||
|
||||
private _onDispose: Emitter<void> = new Emitter<void>();
|
||||
private readonly _onDispose: Emitter<void> = new Emitter<void>();
|
||||
readonly onDispose: Event<void> = this._onDispose.event;
|
||||
|
||||
private itemsMap: Map<TreeItemHandle, ITreeItem> = new Map<TreeItemHandle, ITreeItem>();
|
||||
@@ -87,8 +92,8 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
|
||||
}
|
||||
|
||||
refresh(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }) {
|
||||
const itemsToRefresh: ITreeItem[] = [];
|
||||
if (itemsToRefreshByHandle) {
|
||||
const itemsToRefresh: ITreeItem[] = [];
|
||||
for (const treeItemHandle of Object.keys(itemsToRefreshByHandle)) {
|
||||
const currentTreeItem = this.itemsMap.get(treeItemHandle);
|
||||
if (currentTreeItem) { // Refresh only if the item exists
|
||||
@@ -101,13 +106,16 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
|
||||
// Update maps when handle is changed and refresh parent
|
||||
this.itemsMap.delete(treeItemHandle);
|
||||
this.itemsMap.set(currentTreeItem.handle, currentTreeItem);
|
||||
itemsToRefresh.push(this.itemsMap.get(treeItem.parentHandle));
|
||||
const parent = treeItem.parentHandle ? this.itemsMap.get(treeItem.parentHandle) : null;
|
||||
if (parent) {
|
||||
itemsToRefresh.push(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (itemsToRefresh.length) {
|
||||
this._onDidChange.fire(itemsToRefresh);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (itemsToRefresh.length) {
|
||||
this._onDidChange.fire(itemsToRefresh);
|
||||
} else {
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
75
src/vs/workbench/api/electron-browser/mainThreadUrls.ts
Normal file
75
src/vs/workbench/api/electron-browser/mainThreadUrls.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExtHostContext, IExtHostContext, MainContext, MainThreadUrlsShape, ExtHostUrlsShape } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from './extHostCustomers';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtensionUrlHandler } from 'vs/platform/url/electron-browser/inactiveExtensionUrlHandler';
|
||||
|
||||
class ExtensionUrlHandler implements IURLHandler {
|
||||
|
||||
constructor(
|
||||
private readonly proxy: ExtHostUrlsShape,
|
||||
private readonly handle: number,
|
||||
readonly extensionId: string
|
||||
) { }
|
||||
|
||||
handleURL(uri: URI): TPromise<boolean> {
|
||||
if (uri.authority !== this.extensionId) {
|
||||
return TPromise.as(false);
|
||||
}
|
||||
|
||||
return this.proxy.$handleExternalUri(this.handle, uri).then(() => true);
|
||||
}
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadUrls)
|
||||
export class MainThreadUrls implements MainThreadUrlsShape {
|
||||
|
||||
private readonly proxy: ExtHostUrlsShape;
|
||||
private handlers = new Map<number, { extensionId: string, disposable: IDisposable }>();
|
||||
|
||||
constructor(
|
||||
context: IExtHostContext,
|
||||
@IURLService private urlService: IURLService,
|
||||
@IExtensionUrlHandler private inactiveExtensionUrlHandler: IExtensionUrlHandler
|
||||
) {
|
||||
this.proxy = context.getProxy(ExtHostContext.ExtHostUrls);
|
||||
}
|
||||
|
||||
$registerProtocolHandler(handle: number, extensionId: string): TPromise<void> {
|
||||
const handler = new ExtensionUrlHandler(this.proxy, handle, extensionId);
|
||||
const disposable = this.urlService.registerHandler(handler);
|
||||
|
||||
this.handlers.set(handle, { extensionId, disposable });
|
||||
this.inactiveExtensionUrlHandler.registerExtensionHandler(extensionId, handler);
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
$unregisterProtocolHandler(handle: number): TPromise<void> {
|
||||
const tuple = this.handlers.get(handle);
|
||||
|
||||
if (!tuple) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
const { extensionId, disposable } = tuple;
|
||||
|
||||
this.inactiveExtensionUrlHandler.unregisterExtensionHandler(extensionId);
|
||||
this.handlers.delete(handle);
|
||||
disposable.dispose();
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.handlers.forEach(({ disposable }) => disposable.dispose());
|
||||
this.handlers.clear();
|
||||
}
|
||||
}
|
||||
@@ -2,501 +2,117 @@
|
||||
* 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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import * as map from 'vs/base/common/map';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { MainThreadWebviewsShape, MainContext, IExtHostContext, ExtHostContext, ExtHostWebviewsShape, WebviewHandle } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { extHostNamedCustomer } from './extHostCustomers';
|
||||
import { EditorInput, EditorModel, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { IEditorModel, Position } from 'vs/platform/editor/common/editor';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { WebviewEditor as BaseWebviewEditor, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from 'vs/workbench/parts/html/browser/webviewEditor';
|
||||
import { Builder, Dimension } from 'vs/base/browser/builder';
|
||||
import { Webview } from 'vs/workbench/parts/html/browser/webview';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import * as vscode from 'vscode';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
|
||||
interface WebviewEvents {
|
||||
onMessage(message: any): void;
|
||||
onDidChangePosition(newPosition: Position): void;
|
||||
onDispose(): void;
|
||||
onDidClickLink(link: URI, options: vscode.WebviewOptions): void;
|
||||
}
|
||||
|
||||
class WebviewInput extends EditorInput {
|
||||
private static handlePool = 0;
|
||||
|
||||
private readonly _resource: URI;
|
||||
private _name: string;
|
||||
private _options: vscode.WebviewOptions;
|
||||
private _html: string;
|
||||
private _currentWebviewHtml: string = '';
|
||||
private _events: WebviewEvents | undefined;
|
||||
private _container: HTMLElement;
|
||||
private _webview: Webview | undefined;
|
||||
private _webviewOwner: any;
|
||||
private _webviewDisposables: IDisposable[] = [];
|
||||
private _position: Position;
|
||||
|
||||
|
||||
public static create(
|
||||
resource: URI,
|
||||
name: string,
|
||||
position: Position,
|
||||
options: vscode.WebviewOptions,
|
||||
html: string,
|
||||
events: WebviewEvents,
|
||||
partService: IPartService
|
||||
): WebviewInput {
|
||||
const id = WebviewInput.handlePool++;
|
||||
const webviewContainer = document.createElement('div');
|
||||
webviewContainer.id = `webview-${id}`;
|
||||
|
||||
partService.getContainer(Parts.EDITOR_PART).appendChild(webviewContainer);
|
||||
|
||||
return new WebviewInput(resource, name, position, options, html, events, webviewContainer, undefined);
|
||||
}
|
||||
|
||||
constructor(
|
||||
resource: URI,
|
||||
name: string,
|
||||
position: Position,
|
||||
options: vscode.WebviewOptions,
|
||||
html: string,
|
||||
events: WebviewEvents,
|
||||
container: HTMLElement,
|
||||
webview: Webview | undefined
|
||||
) {
|
||||
super();
|
||||
this._resource = resource;
|
||||
this._name = name;
|
||||
this._position = position;
|
||||
this._options = options;
|
||||
this._html = html;
|
||||
this._events = events;
|
||||
|
||||
this._container = container;
|
||||
this._webview = webview;
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
return 'webview';
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.disposeWebview();
|
||||
|
||||
if (this._container) {
|
||||
this._container.remove();
|
||||
this._container = undefined;
|
||||
}
|
||||
|
||||
if (this._events) {
|
||||
this._events.onDispose();
|
||||
this._events = undefined;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
return this._resource;
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public setName(value: string): void {
|
||||
this._name = value;
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
|
||||
public get position(): Position {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
public get html(): string {
|
||||
return this._html;
|
||||
}
|
||||
|
||||
public setHtml(value: string): void {
|
||||
if (value === this._currentWebviewHtml) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._html = value;
|
||||
|
||||
if (this._webview) {
|
||||
this._webview.contents = value;
|
||||
this._currentWebviewHtml = value;
|
||||
}
|
||||
}
|
||||
|
||||
public get options(): vscode.WebviewOptions {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
public set options(value: vscode.WebviewOptions) {
|
||||
this._options = value;
|
||||
}
|
||||
|
||||
public resolve(refresh?: boolean): TPromise<IEditorModel, any> {
|
||||
return TPromise.as(new EditorModel());
|
||||
}
|
||||
|
||||
public supportsSplitEditor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public get container(): HTMLElement {
|
||||
return this._container;
|
||||
}
|
||||
|
||||
public get webview(): Webview | undefined {
|
||||
return this._webview;
|
||||
}
|
||||
|
||||
public set webview(value: Webview) {
|
||||
this._webviewDisposables = dispose(this._webviewDisposables);
|
||||
|
||||
this._webview = value;
|
||||
|
||||
this._webview.onDidClickLink(link => {
|
||||
if (this._events) {
|
||||
this._events.onDidClickLink(link, this._options);
|
||||
}
|
||||
}, null, this._webviewDisposables);
|
||||
|
||||
this._webview.onMessage(message => {
|
||||
if (this._events) {
|
||||
this._events.onMessage(message);
|
||||
}
|
||||
}, null, this._webviewDisposables);
|
||||
}
|
||||
|
||||
public claimWebview(owner: any) {
|
||||
this._webviewOwner = owner;
|
||||
}
|
||||
|
||||
public releaseWebview(owner: any) {
|
||||
if (this._webviewOwner === owner) {
|
||||
this._webviewOwner = undefined;
|
||||
if (this._options.retainContextWhenHidden) {
|
||||
this.container.style.visibility = 'hidden';
|
||||
} else {
|
||||
this.disposeWebview();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public disposeWebview() {
|
||||
// The input owns the webview and its parent
|
||||
if (this._webview) {
|
||||
this._webview.dispose();
|
||||
this._webview = undefined;
|
||||
}
|
||||
|
||||
this._webviewDisposables = dispose(this._webviewDisposables);
|
||||
|
||||
this._webviewOwner = undefined;
|
||||
this.container.style.visibility = 'hidden';
|
||||
|
||||
this._currentWebviewHtml = '';
|
||||
}
|
||||
|
||||
public onDidChangePosition(position: Position) {
|
||||
if (this._events) {
|
||||
this._events.onDidChangePosition(position);
|
||||
}
|
||||
this._position = position;
|
||||
}
|
||||
}
|
||||
|
||||
class WebviewEditor extends BaseWebviewEditor {
|
||||
|
||||
public static readonly ID = 'WebviewEditor';
|
||||
|
||||
private editorFrame: HTMLElement;
|
||||
private webviewContent: HTMLElement;
|
||||
private _onDidFocusWebview: Emitter<void>;
|
||||
private _webviewFocusTracker?: DOM.IFocusTracker;
|
||||
private _webviewFocusListenerDisposable?: IDisposable;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextKeyService private _contextKeyService: IContextKeyService,
|
||||
@IPartService private readonly _partService: IPartService,
|
||||
@IContextViewService private readonly _contextViewService: IContextViewService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(WebviewEditor.ID, telemetryService, themeService, storageService, _contextKeyService);
|
||||
|
||||
this._onDidFocusWebview = new Emitter<void>();
|
||||
}
|
||||
|
||||
protected createEditor(parent: Builder): void {
|
||||
this.editorFrame = parent.getHTMLElement();
|
||||
this.content = document.createElement('div');
|
||||
parent.append(this.content);
|
||||
}
|
||||
|
||||
private doUpdateContainer() {
|
||||
const webviewContainer = this.input && (this.input as WebviewInput).container;
|
||||
if (webviewContainer) {
|
||||
const frameRect = this.editorFrame.getBoundingClientRect();
|
||||
const containerRect = webviewContainer.parentElement.getBoundingClientRect();
|
||||
|
||||
webviewContainer.style.position = 'absolute';
|
||||
webviewContainer.style.top = `${frameRect.top - containerRect.top}px`;
|
||||
webviewContainer.style.left = `${frameRect.left - containerRect.left}px`;
|
||||
webviewContainer.style.width = `${frameRect.width}px`;
|
||||
webviewContainer.style.height = `${frameRect.height}px`;
|
||||
}
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
if (this._webview) {
|
||||
this.doUpdateContainer();
|
||||
}
|
||||
super.layout(dimension);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
// Let the editor input dispose of the webview.
|
||||
this._webview = undefined;
|
||||
this.webviewContent = undefined;
|
||||
|
||||
this._onDidFocusWebview.dispose();
|
||||
|
||||
if (this._webviewFocusTracker) {
|
||||
this._webviewFocusTracker.dispose();
|
||||
}
|
||||
|
||||
if (this._webviewFocusListenerDisposable) {
|
||||
this._webviewFocusListenerDisposable.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public sendMessage(data: any): void {
|
||||
if (this._webview) {
|
||||
this._webview.sendMessage(data);
|
||||
}
|
||||
}
|
||||
|
||||
public get onDidFocus(): Event<any> {
|
||||
return this._onDidFocusWebview.event;
|
||||
}
|
||||
|
||||
protected setEditorVisible(visible: boolean, position?: Position): void {
|
||||
if (this.input && this.input instanceof WebviewInput) {
|
||||
if (visible) {
|
||||
this.input.claimWebview(this);
|
||||
} else {
|
||||
this.input.releaseWebview(this);
|
||||
}
|
||||
|
||||
this.updateWebview(this.input as WebviewInput);
|
||||
}
|
||||
|
||||
if (this.webviewContent) {
|
||||
if (visible) {
|
||||
this.webviewContent.style.visibility = 'visible';
|
||||
this.doUpdateContainer();
|
||||
} else {
|
||||
this.webviewContent.style.visibility = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
super.setEditorVisible(visible, position);
|
||||
}
|
||||
|
||||
public clearInput() {
|
||||
if (this.input && this.input instanceof WebviewInput) {
|
||||
this.input.releaseWebview(this);
|
||||
}
|
||||
|
||||
this._webview = undefined;
|
||||
this.webviewContent = undefined;
|
||||
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
async setInput(input: WebviewInput, options: EditorOptions): TPromise<void> {
|
||||
if (this.input && this.input.matches(input)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.input && this.input.getResource().fsPath !== input.getResource().fsPath) {
|
||||
(this.input as WebviewInput).releaseWebview(this);
|
||||
this._webview = undefined;
|
||||
this.webviewContent = undefined;
|
||||
}
|
||||
|
||||
await super.setInput(input, options);
|
||||
|
||||
input.onDidChangePosition(this.position);
|
||||
this.updateWebview(input);
|
||||
}
|
||||
|
||||
private updateWebview(input: WebviewInput) {
|
||||
const webview = this.getWebview(input);
|
||||
input.claimWebview(this);
|
||||
webview.options = {
|
||||
allowScripts: input.options.enableScripts,
|
||||
enableWrappedPostMessage: true,
|
||||
useSameOriginForRoot: false,
|
||||
localResourceRoots: (input && input.options.localResourceRoots) || this._contextService.getWorkspace().folders.map(x => x.uri)
|
||||
};
|
||||
input.setHtml(input.html);
|
||||
this.webviewContent.style.visibility = 'visible';
|
||||
this.doUpdateContainer();
|
||||
}
|
||||
|
||||
private getWebview(input: WebviewInput): Webview {
|
||||
if (this._webview) {
|
||||
return this._webview;
|
||||
}
|
||||
|
||||
this.webviewContent = input.container;
|
||||
const existing = input.webview;
|
||||
if (existing) {
|
||||
this._webview = existing;
|
||||
return existing;
|
||||
}
|
||||
|
||||
this._webviewFocusTracker = DOM.trackFocus(this.webviewContent);
|
||||
this._webviewFocusListenerDisposable = this._webviewFocusTracker.onDidFocus(() => {
|
||||
this._onDidFocusWebview.fire();
|
||||
});
|
||||
|
||||
this._contextKeyService = this._contextKeyService.createScoped(this.webviewContent);
|
||||
this.contextKey = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS.bindTo(this._contextKeyService);
|
||||
this.findInputFocusContextKey = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED.bindTo(this._contextKeyService);
|
||||
this.findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService);
|
||||
|
||||
this._webview = new Webview(
|
||||
this.webviewContent,
|
||||
this._partService.getContainer(Parts.EDITOR_PART),
|
||||
this.themeService,
|
||||
this._environmentService,
|
||||
this._contextViewService,
|
||||
this.contextKey,
|
||||
this.findInputFocusContextKey,
|
||||
{
|
||||
enableWrappedPostMessage: true,
|
||||
useSameOriginForRoot: false
|
||||
});
|
||||
input.webview = this._webview;
|
||||
|
||||
this.content.setAttribute('aria-flowto', this.webviewContent.id);
|
||||
|
||||
this.doUpdateContainer();
|
||||
return this._webview;
|
||||
}
|
||||
}
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Position } from 'vs/platform/editor/common/editor';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { WebviewEditor } from 'vs/workbench/parts/webview/electron-browser/webviewEditor';
|
||||
import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput';
|
||||
import { IWebviewEditorService, WebviewInputOptions, WebviewReviver } from 'vs/workbench/parts/webview/electron-browser/webviewEditorService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { extHostNamedCustomer } from './extHostCustomers';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadWebviews)
|
||||
export class MainThreadWebviews implements MainThreadWebviewsShape {
|
||||
export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver {
|
||||
|
||||
private static readonly serializeTimeout = 500; // ms
|
||||
|
||||
private static readonly viewType = 'mainThreadWebview';
|
||||
|
||||
private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto'];
|
||||
|
||||
private _toDispose: Disposable[] = [];
|
||||
private static revivalPool = 0;
|
||||
|
||||
private _toDispose: IDisposable[] = [];
|
||||
|
||||
private readonly _proxy: ExtHostWebviewsShape;
|
||||
private readonly _webviews = new Map<WebviewHandle, WebviewInput>();
|
||||
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput>();
|
||||
private readonly _revivers = new Set<string>();
|
||||
|
||||
private _activeWebview: WebviewInput | undefined = undefined;
|
||||
private _activeWebview: WebviewPanelHandle | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
context: IExtHostContext,
|
||||
@IContextKeyService _contextKeyService: IContextKeyService,
|
||||
@IPartService private readonly _partService: IPartService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService private readonly _editorGroupService: IEditorGroupService,
|
||||
@IOpenerService private readonly _openerService: IOpenerService
|
||||
@IWebviewEditorService private readonly _webviewService: IWebviewEditorService,
|
||||
@IOpenerService private readonly _openerService: IOpenerService,
|
||||
@IExtensionService private readonly _extensionService: IExtensionService,
|
||||
|
||||
) {
|
||||
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
|
||||
_editorGroupService.onEditorsChanged(this.onEditorsChanged, this, this._toDispose);
|
||||
editorGroupService.onEditorsChanged(this.onEditorsChanged, this, this._toDispose);
|
||||
editorGroupService.onEditorGroupMoved(this.onEditorGroupMoved, this, this._toDispose);
|
||||
|
||||
this._toDispose.push(_webviewService.registerReviver(MainThreadWebviews.viewType, this));
|
||||
|
||||
lifecycleService.onWillShutdown(e => {
|
||||
e.veto(this._onWillShutdown());
|
||||
}, this, this._toDispose);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
|
||||
$createWebview(handle: WebviewHandle, uri: URI, title: string, column: Position, options: vscode.WebviewOptions): void {
|
||||
const webviewInput = WebviewInput.create(URI.revive(uri), title, column, options, '', {
|
||||
onMessage: message => this._proxy.$onMessage(handle, message),
|
||||
onDidChangePosition: position => this._proxy.$onDidChangePosition(handle, position),
|
||||
onDispose: () => {
|
||||
this._proxy.$onDidDisposeWeview(handle).then(() => {
|
||||
this._webviews.delete(handle);
|
||||
});
|
||||
},
|
||||
onDidClickLink: (link, options) => this.onDidClickLink(link, options)
|
||||
}, this._partService);
|
||||
$createWebviewPanel(
|
||||
handle: WebviewPanelHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
column: Position,
|
||||
options: WebviewInputOptions,
|
||||
extensionFolderPath: string
|
||||
): void {
|
||||
const webview = this._webviewService.createWebview(MainThreadWebviews.viewType, title, column, options, extensionFolderPath, this.createWebviewEventDelegate(handle));
|
||||
webview.state = {
|
||||
viewType: viewType,
|
||||
state: undefined
|
||||
};
|
||||
|
||||
this._webviews.set(handle, webviewInput);
|
||||
|
||||
this._editorService.openEditor(webviewInput, { pinned: true }, column);
|
||||
this._webviews.set(handle, webview);
|
||||
this._activeWebview = handle;
|
||||
}
|
||||
|
||||
$disposeWebview(handle: WebviewHandle): void {
|
||||
$disposeWebview(handle: WebviewPanelHandle): void {
|
||||
const webview = this.getWebview(handle);
|
||||
if (webview) {
|
||||
this._editorService.closeEditor(webview.position, webview);
|
||||
}
|
||||
webview.dispose();
|
||||
}
|
||||
|
||||
$setTitle(handle: WebviewHandle, value: string): void {
|
||||
$setTitle(handle: WebviewPanelHandle, value: string): void {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.setName(value);
|
||||
}
|
||||
|
||||
$setHtml(handle: WebviewHandle, value: string): void {
|
||||
$setHtml(handle: WebviewPanelHandle, value: string): void {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.setHtml(value);
|
||||
webview.html = value;
|
||||
}
|
||||
|
||||
$show(handle: WebviewHandle, column: Position): void {
|
||||
const webviewInput = this.getWebview(handle);
|
||||
if (webviewInput.position === column) {
|
||||
this._editorService.openEditor(webviewInput, { preserveFocus: true }, column);
|
||||
} else {
|
||||
this._editorGroupService.moveEditor(webviewInput, webviewInput.position, column, { preserveFocus: true });
|
||||
$reveal(handle: WebviewPanelHandle, column: Position | null): void {
|
||||
const webview = this.getWebview(handle);
|
||||
if (webview.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._webviewService.revealWebview(webview, column);
|
||||
}
|
||||
|
||||
async $sendMessage(handle: WebviewHandle, message: any): Promise<boolean> {
|
||||
const webviewInput = this.getWebview(handle);
|
||||
async $postMessage(handle: WebviewPanelHandle, message: any): TPromise<boolean> {
|
||||
const webview = this.getWebview(handle);
|
||||
const editors = this._editorService.getVisibleEditors()
|
||||
.filter(e => e instanceof WebviewEditor)
|
||||
.map(e => e as WebviewEditor)
|
||||
.filter(e => e.input.matches(webviewInput));
|
||||
.filter(e => e.input.matches(webview));
|
||||
|
||||
for (const editor of editors) {
|
||||
editor.sendMessage(message);
|
||||
@@ -505,18 +121,91 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
|
||||
return (editors.length > 0);
|
||||
}
|
||||
|
||||
private getWebview(handle: number): WebviewInput {
|
||||
const webviewInput = this._webviews.get(handle);
|
||||
if (!webviewInput) {
|
||||
$registerSerializer(viewType: string): void {
|
||||
this._revivers.add(viewType);
|
||||
}
|
||||
|
||||
$unregisterSerializer(viewType: string): void {
|
||||
this._revivers.delete(viewType);
|
||||
}
|
||||
|
||||
reviveWebview(webview: WebviewEditorInput): TPromise<void> {
|
||||
const viewType = webview.state.viewType;
|
||||
return this._extensionService.activateByEvent(`onView:${viewType}`).then(() => {
|
||||
const handle = 'revival-' + MainThreadWebviews.revivalPool++;
|
||||
this._webviews.set(handle, webview);
|
||||
webview._events = this.createWebviewEventDelegate(handle);
|
||||
|
||||
return this._proxy.$deserializeWebviewPanel(handle, webview.state.viewType, webview.getTitle(), webview.state.state, webview.position, webview.options)
|
||||
.then(undefined, () => {
|
||||
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
canRevive(webview: WebviewEditorInput): boolean {
|
||||
if (webview.isDisposed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._revivers.has(webview.viewType) || webview.reviver !== null;
|
||||
}
|
||||
|
||||
private _onWillShutdown(): TPromise<boolean> {
|
||||
const toRevive: WebviewPanelHandle[] = [];
|
||||
this._webviews.forEach((view, key) => {
|
||||
if (this.canRevive(view)) {
|
||||
toRevive.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
const reviveResponses = toRevive.map(handle =>
|
||||
TPromise.any([
|
||||
this._proxy.$serializeWebviewPanel(handle).then(
|
||||
state => ({ handle, state }),
|
||||
() => ({ handle, state: null })),
|
||||
TPromise.timeout(MainThreadWebviews.serializeTimeout).then(() => ({ handle, state: null }))
|
||||
]).then(x => x.value));
|
||||
|
||||
return TPromise.join(reviveResponses).then(results => {
|
||||
for (const result of results) {
|
||||
const view = this._webviews.get(result.handle);
|
||||
if (view) {
|
||||
if (result.state) {
|
||||
view.state.state = result.state;
|
||||
} else {
|
||||
view.state = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // Don't veto shutdown
|
||||
});
|
||||
}
|
||||
|
||||
private createWebviewEventDelegate(handle: WebviewPanelHandle) {
|
||||
return {
|
||||
onDidClickLink: uri => this.onDidClickLink(handle, uri),
|
||||
onMessage: message => this._proxy.$onMessage(handle, message),
|
||||
onDispose: () => {
|
||||
this._proxy.$onDidDisposeWebviewPanel(handle).then(
|
||||
() => this._webviews.delete(handle),
|
||||
() => this._webviews.delete(handle));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private getWebview(handle: WebviewPanelHandle): WebviewEditorInput {
|
||||
const webview = this._webviews.get(handle);
|
||||
if (!webview) {
|
||||
throw new Error('Unknown webview handle:' + handle);
|
||||
}
|
||||
return webviewInput;
|
||||
return webview;
|
||||
}
|
||||
|
||||
private onEditorsChanged() {
|
||||
const activeEditor = this._editorService.getActiveEditor();
|
||||
let newActiveWebview: { input: WebviewInput, handle: WebviewHandle } | undefined = undefined;
|
||||
if (activeEditor && activeEditor.input instanceof WebviewInput) {
|
||||
let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined;
|
||||
if (activeEditor && activeEditor.input instanceof WebviewEditorInput) {
|
||||
for (const handle of map.keys(this._webviews)) {
|
||||
const input = this._webviews.get(handle);
|
||||
if (input.matches(activeEditor.input)) {
|
||||
@@ -526,33 +215,64 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
|
||||
}
|
||||
}
|
||||
|
||||
if (newActiveWebview && newActiveWebview.handle === this._activeWebview) {
|
||||
// Webview itself unchanged but position may have changed
|
||||
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, true, newActiveWebview.input.position);
|
||||
return;
|
||||
}
|
||||
|
||||
// Broadcast view state update for currently active
|
||||
if (typeof this._activeWebview !== 'undefined') {
|
||||
const oldActiveWebview = this._webviews.get(this._activeWebview);
|
||||
if (oldActiveWebview) {
|
||||
this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, false, oldActiveWebview.position);
|
||||
}
|
||||
}
|
||||
|
||||
// Then for newly active
|
||||
if (newActiveWebview) {
|
||||
if (!this._activeWebview || !newActiveWebview.input.matches(this._activeWebview)) {
|
||||
this._proxy.$onDidChangeActiveWeview(newActiveWebview.handle);
|
||||
this._activeWebview = newActiveWebview.input;
|
||||
}
|
||||
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, true, activeEditor.position);
|
||||
this._activeWebview = newActiveWebview.handle;
|
||||
} else {
|
||||
if (this._activeWebview) {
|
||||
this._proxy.$onDidChangeActiveWeview(undefined);
|
||||
this._activeWebview = undefined;
|
||||
}
|
||||
this._activeWebview = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private onDidClickLink(link: URI, options: vscode.WebviewOptions): void {
|
||||
private onEditorGroupMoved(): void {
|
||||
for (const workbenchEditor of this._editorService.getVisibleEditors()) {
|
||||
if (!workbenchEditor.input) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._webviews.forEach((input, handle) => {
|
||||
if (workbenchEditor.input.matches(input) && input.position !== workbenchEditor.position) {
|
||||
input.updatePosition(workbenchEditor.position);
|
||||
this._proxy.$onDidChangeWebviewPanelViewState(handle, handle === this._activeWebview, workbenchEditor.position);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
|
||||
if (!link) {
|
||||
return;
|
||||
}
|
||||
|
||||
const enableCommandUris = options.enableCommandUris;
|
||||
const webview = this.getWebview(handle);
|
||||
const enableCommandUris = webview.options.enableCommandUris;
|
||||
if (MainThreadWebviews.standardSupportedLinkSchemes.indexOf(link.scheme) >= 0 || enableCommandUris && link.scheme === 'command') {
|
||||
this._openerService.open(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(<IEditorRegistry>Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor(
|
||||
WebviewEditor,
|
||||
WebviewEditor.ID,
|
||||
localize('webview.editor.label', "webview editor")),
|
||||
[new SyncDescriptor(WebviewInput)]);
|
||||
private static getDeserializationFailedContents(viewType: string) {
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="https://code.visualstudio.com/raw/">
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src vscode-core-resource: https: 'unsafe-inline'; child-src 'none'; frame-src 'none';">
|
||||
</head>
|
||||
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)}</body>
|
||||
</html>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { MainThreadWindowShape, ExtHostWindowShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { latch } from 'vs/base/common/event';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadWindow)
|
||||
export class MainThreadWindow implements MainThreadWindowShape {
|
||||
@@ -22,7 +23,8 @@ export class MainThreadWindow implements MainThreadWindowShape {
|
||||
) {
|
||||
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostWindow);
|
||||
|
||||
windowService.onDidChangeFocus(this.proxy.$onDidChangeWindowFocus, this.proxy, this.disposables);
|
||||
latch(windowService.onDidChangeFocus)
|
||||
(this.proxy.$onDidChangeWindowFocus, this.proxy, this.disposables);
|
||||
}
|
||||
|
||||
$getWindowVisibility(): TPromise<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user