mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -23,6 +23,7 @@ import './mainThreadCredentials';
|
||||
// disable the debug service
|
||||
// import './mainThreadDebugService';
|
||||
|
||||
import './mainThreadDecorations';
|
||||
import './mainThreadDiagnostics';
|
||||
import './mainThreadDialogs';
|
||||
import './mainThreadDocumentContentProviders';
|
||||
@@ -32,6 +33,7 @@ import './mainThreadEditor';
|
||||
import './mainThreadEditors';
|
||||
import './mainThreadErrors';
|
||||
import './mainThreadExtensionService';
|
||||
import './mainThreadFileSystem';
|
||||
import './mainThreadFileSystemEventService';
|
||||
import './mainThreadHeapService';
|
||||
import './mainThreadLanguageFeatures';
|
||||
|
||||
@@ -21,9 +21,7 @@ export class MainThreadCommands implements MainThreadCommandsShape {
|
||||
extHostContext: IExtHostContext,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
) {
|
||||
if (extHostContext) {
|
||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostCommands);
|
||||
}
|
||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostCommands);
|
||||
|
||||
this._generateCommandsDocumentationRegistration = CommandsRegistry.registerCommand('_generateCommandsDocumentation', () => this._generateCommandsDocumentation());
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
|
||||
import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IWorkspaceConfigurationChangeEventData } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadConfiguration)
|
||||
export class MainThreadConfiguration implements MainThreadConfigurationShape {
|
||||
@@ -22,14 +22,13 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IConfigurationEditingService private readonly _configurationEditingService: IConfigurationEditingService,
|
||||
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
|
||||
@IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService
|
||||
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService
|
||||
) {
|
||||
const proxy = extHostContext.get(ExtHostContext.ExtHostConfiguration);
|
||||
|
||||
this._configurationListener = configurationService.onDidUpdateConfiguration(() => {
|
||||
proxy.$acceptConfigurationChanged(configurationService.getConfigurationData());
|
||||
this._configurationListener = configurationService.onDidChangeConfiguration(e => {
|
||||
proxy.$acceptConfigurationChanged(configurationService.getConfigurationData(), this.toConfigurationChangeEventData(e));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,16 +46,26 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
|
||||
|
||||
private writeConfiguration(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise<void> {
|
||||
target = target !== null && target !== undefined ? target : this.deriveConfigurationTarget(key, resource);
|
||||
return this._configurationEditingService.writeConfiguration(target, { key, value }, { donotNotifyError: true, scopes: { resource } });
|
||||
return this.configurationService.updateValue(key, value, { resource }, target, true);
|
||||
}
|
||||
|
||||
private deriveConfigurationTarget(key: string, resource: URI): ConfigurationTarget {
|
||||
if (resource && this._workspaceContextService.hasMultiFolderWorkspace()) {
|
||||
if (resource && this._workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
|
||||
if (configurationProperties[key] && configurationProperties[key].scope === ConfigurationScope.RESOURCE) {
|
||||
return ConfigurationTarget.FOLDER;
|
||||
return ConfigurationTarget.WORKSPACE_FOLDER;
|
||||
}
|
||||
}
|
||||
return ConfigurationTarget.WORKSPACE;
|
||||
}
|
||||
|
||||
private toConfigurationChangeEventData(event: IConfigurationChangeEvent): IWorkspaceConfigurationChangeEventData {
|
||||
return {
|
||||
changedConfiguration: event.changedConfiguration,
|
||||
changedConfigurationByResource: event.changedConfigurationByResource.keys().reduce((result, resource) => {
|
||||
result[resource.toString()] = event.changedConfigurationByResource.get(resource);
|
||||
return result;
|
||||
}, Object.create({}))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@ import URI from 'vs/base/common/uri';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IDebugService, IConfig, IDebugConfigurationProvider } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import severity from 'vs/base/common/severity';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadDebugService)
|
||||
export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
@@ -21,7 +23,8 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IDebugService private debugService: IDebugService
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
) {
|
||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostDebugService);
|
||||
this._toDispose = [];
|
||||
@@ -36,8 +39,10 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
}));
|
||||
this._toDispose.push(debugService.onDidCustomEvent(event => {
|
||||
if (event && event.sessionId) {
|
||||
const process = this.debugService.findProcessByUUID(event.sessionId);
|
||||
this._proxy.$acceptDebugSessionCustomEvent(event.sessionId, process.configuration.type, process.configuration.name, event);
|
||||
const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === event.sessionId).pop();
|
||||
if (process) {
|
||||
this._proxy.$acceptDebugSessionCustomEvent(event.sessionId, process.configuration.type, process.configuration.name, event);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -52,12 +57,12 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
type: debugType
|
||||
};
|
||||
if (hasProvide) {
|
||||
provider.provideDebugConfigurations = (folder: URI | undefined) => {
|
||||
provider.provideDebugConfigurations = folder => {
|
||||
return this._proxy.$provideDebugConfigurations(handle, folder);
|
||||
};
|
||||
}
|
||||
if (hasResolve) {
|
||||
provider.resolveDebugConfiguration = (folder: URI | undefined, debugConfiguration: any) => {
|
||||
provider.resolveDebugConfiguration = (folder, debugConfiguration) => {
|
||||
return this._proxy.$resolveDebugConfiguration(handle, folder, debugConfiguration);
|
||||
};
|
||||
}
|
||||
@@ -71,30 +76,17 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
return TPromise.as<void>(undefined);
|
||||
}
|
||||
|
||||
public $startDebugging(folderUri: URI | undefined, nameOrConfiguration: string | IConfig): TPromise<boolean> {
|
||||
return this.debugService.startDebugging(folderUri, nameOrConfiguration).then(x => {
|
||||
public $startDebugging(folderUri: uri | undefined, nameOrConfiguration: string | IConfig): TPromise<boolean> {
|
||||
const folder = folderUri ? this.contextService.getWorkspace().folders.filter(wf => wf.uri.toString() === folderUri.toString()).pop() : undefined;
|
||||
return this.debugService.startDebugging(folder, nameOrConfiguration).then(x => {
|
||||
return true;
|
||||
}, err => {
|
||||
return TPromise.wrapError(err && err.message ? err.message : 'cannot start debugging');
|
||||
});
|
||||
}
|
||||
|
||||
public $startDebugSession(folderUri: URI | undefined, configuration: IConfig): TPromise<DebugSessionUUID> {
|
||||
if (configuration.request !== 'launch' && configuration.request !== 'attach') {
|
||||
return TPromise.wrapError(new Error(`only 'launch' or 'attach' allowed for 'request' attribute`));
|
||||
}
|
||||
return this.debugService.createProcess(folderUri, configuration).then(process => {
|
||||
if (process) {
|
||||
return <DebugSessionUUID>process.getId();
|
||||
}
|
||||
return TPromise.wrapError<DebugSessionUUID>(new Error('cannot create debug session'));
|
||||
}, err => {
|
||||
return TPromise.wrapError(err && err.message ? err.message : 'cannot start debug session');
|
||||
});
|
||||
}
|
||||
|
||||
public $customDebugAdapterRequest(sessionId: DebugSessionUUID, request: string, args: any): TPromise<any> {
|
||||
const process = this.debugService.findProcessByUUID(sessionId);
|
||||
const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === sessionId).pop();
|
||||
if (process) {
|
||||
return process.session.custom(request, args).then(response => {
|
||||
if (response.success) {
|
||||
@@ -106,6 +98,12 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
|
||||
}
|
||||
return TPromise.wrapError(new Error('debug session not found'));
|
||||
}
|
||||
|
||||
public $appendDebugConsole(value: string): TPromise<any> {
|
||||
// Use warning as severity to get the orange color for messages coming from the debug extension
|
||||
this.debugService.logToRepl(value, severity.Warning);
|
||||
return TPromise.as<void>(undefined);
|
||||
}
|
||||
}
|
||||
// {{SQL CARBON EDIT}}
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 URI from 'vs/base/common/uri';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostContext, MainContext, IExtHostContext, MainThreadDecorationsShape, ExtHostDecorationsShape } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { IDecorationsService, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadDecorations)
|
||||
export class MainThreadDecorations implements MainThreadDecorationsShape {
|
||||
|
||||
private readonly _provider = new Map<number, [Emitter<URI[]>, IDisposable]>();
|
||||
private readonly _proxy: ExtHostDecorationsShape;
|
||||
|
||||
constructor(
|
||||
context: IExtHostContext,
|
||||
@IDecorationsService private readonly _decorationsService: IDecorationsService
|
||||
) {
|
||||
this._proxy = context.get(ExtHostContext.ExtHostDecorations);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._provider.forEach(value => dispose(value));
|
||||
this._provider.clear();
|
||||
}
|
||||
|
||||
$registerDecorationProvider(handle: number, label: string): void {
|
||||
let emitter = new Emitter<URI[]>();
|
||||
let registration = this._decorationsService.registerDecorationsProvider({
|
||||
label,
|
||||
onDidChange: emitter.event,
|
||||
provideDecorations: (uri) => {
|
||||
return this._proxy.$providerDecorations(handle, uri).then(data => {
|
||||
if (!data) {
|
||||
return undefined;
|
||||
}
|
||||
const [weight, bubble, tooltip, letter, themeColor, source] = data;
|
||||
return <IDecorationData>{
|
||||
weight: weight || 0,
|
||||
bubble: bubble || false,
|
||||
color: themeColor && themeColor.id,
|
||||
tooltip,
|
||||
letter,
|
||||
source,
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
this._provider.set(handle, [emitter, registration]);
|
||||
}
|
||||
|
||||
$onDidChange(handle: number, resources: URI[]): void {
|
||||
const [emitter] = this._provider.get(handle);
|
||||
emitter.fire(resources);
|
||||
}
|
||||
|
||||
$unregisterDecorationProvider(handle: number): void {
|
||||
if (this._provider.has(handle)) {
|
||||
dispose(this._provider.get(handle));
|
||||
this._provider.delete(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { MainThreadDiaglogsShape, MainContext, IExtHostContext, MainThreadDialogOptions } from '../node/extHost.protocol';
|
||||
import { MainThreadDiaglogsShape, MainContext, IExtHostContext, MainThreadDialogOpenOptions, MainThreadDialogSaveOptions } from '../node/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { forEach } from 'vs/base/common/collections';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadDialogs)
|
||||
export class MainThreadDialogs implements MainThreadDiaglogsShape {
|
||||
@@ -24,40 +25,75 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
|
||||
//
|
||||
}
|
||||
|
||||
$showOpenDialog(options: MainThreadDialogOptions): TPromise<string[]> {
|
||||
$showOpenDialog(options: MainThreadDialogOpenOptions): TPromise<string[]> {
|
||||
// TODO@joh what about remote dev setup?
|
||||
if (options.uri && options.uri.scheme !== 'file') {
|
||||
return TPromise.wrapError(new Error('bad path'));
|
||||
if (options.defaultUri && options.defaultUri.scheme !== 'file') {
|
||||
return TPromise.wrapError(new Error('Not supported - Open-dialogs can only be opened on `file`-uris.'));
|
||||
}
|
||||
return new TPromise<string[]>(resolve => {
|
||||
this._windowService.showOpenDialog(MainThreadDialogs._convertOptions(options), filenames => {
|
||||
resolve(isFalsyOrEmpty(filenames) ? undefined : filenames);
|
||||
});
|
||||
this._windowService.showOpenDialog(
|
||||
MainThreadDialogs._convertOpenOptions(options),
|
||||
filenames => resolve(isFalsyOrEmpty(filenames) ? undefined : filenames)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private static _convertOptions(options: MainThreadDialogOptions): Electron.OpenDialogOptions {
|
||||
$showSaveDialog(options: MainThreadDialogSaveOptions): TPromise<string> {
|
||||
// TODO@joh what about remote dev setup?
|
||||
if (options.defaultUri && options.defaultUri.scheme !== 'file') {
|
||||
return TPromise.wrapError(new Error('Not supported - Save-dialogs can only be opened on `file`-uris.'));
|
||||
}
|
||||
return new TPromise<string>(resolve => {
|
||||
this._windowService.showSaveDialog(
|
||||
MainThreadDialogs._convertSaveOptions(options),
|
||||
filename => resolve(!filename ? undefined : filename)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private static _convertOpenOptions(options: MainThreadDialogOpenOptions): Electron.OpenDialogOptions {
|
||||
const result: Electron.OpenDialogOptions = {
|
||||
properties: ['createDirectory']
|
||||
};
|
||||
if (options.openLabel) {
|
||||
result.buttonLabel = options.openLabel;
|
||||
}
|
||||
if (options.uri) {
|
||||
result.defaultPath = options.uri.fsPath;
|
||||
if (options.defaultUri) {
|
||||
result.defaultPath = options.defaultUri.fsPath;
|
||||
}
|
||||
if (!options.openFiles && !options.openFolders) {
|
||||
options.openFiles = true;
|
||||
if (!options.canSelectFiles && !options.canSelectFolders) {
|
||||
options.canSelectFiles = true;
|
||||
}
|
||||
if (options.openFiles) {
|
||||
if (options.canSelectFiles) {
|
||||
result.properties.push('openFile');
|
||||
}
|
||||
if (options.openFolders) {
|
||||
if (options.canSelectFolders) {
|
||||
result.properties.push('openDirectory');
|
||||
}
|
||||
if (options.openMany) {
|
||||
if (options.canSelectMany) {
|
||||
result.properties.push('multiSelections');
|
||||
}
|
||||
if (options.filters) {
|
||||
result.filters = [];
|
||||
forEach(options.filters, entry => result.filters.push({ name: entry.key, extensions: entry.value }));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _convertSaveOptions(options: MainThreadDialogSaveOptions): Electron.SaveDialogOptions {
|
||||
const result: Electron.SaveDialogOptions = {
|
||||
|
||||
};
|
||||
if (options.defaultUri) {
|
||||
result.defaultPath = options.defaultUri.fsPath;
|
||||
}
|
||||
if (options.saveLabel) {
|
||||
result.buttonLabel = options.saveLabel;
|
||||
}
|
||||
if (options.filters) {
|
||||
result.filters = [];
|
||||
forEach(options.filters, entry => result.filters.push({ name: entry.key, extensions: entry.value }));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModel, ICommonCodeEditor, isCommonCodeEditor, isCommonDiffEditor } from 'vs/editor/common/editorCommon';
|
||||
import { compare } from 'vs/base/common/strings';
|
||||
import { delta } from 'vs/base/common/arrays';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
@@ -26,28 +24,73 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
namespace cmp {
|
||||
export function compareModels(a: IModel, b: IModel): number {
|
||||
return compare(a.uri.toString(), b.uri.toString());
|
||||
namespace mapset {
|
||||
|
||||
export function newSet<E>(from: Set<E>): Set<E> {
|
||||
return new (<any>Set)(from);
|
||||
// let ret = new Set<E>();
|
||||
// from.forEach(ret.add, ret);
|
||||
// return ret;
|
||||
}
|
||||
export function compareEditors(a: EditorAndModel, b: EditorAndModel): number {
|
||||
let ret = compare(a.editor.getId(), b.editor.getId());
|
||||
if (ret === 0) {
|
||||
ret = compare(a.document.uri.toString(), b.document.uri.toString());
|
||||
}
|
||||
|
||||
export function setValues<T>(set: Set<T>): T[] {
|
||||
// return Array.from(set);
|
||||
let ret: T[] = [];
|
||||
set.forEach(v => ret.push(v));
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function mapValues<T>(map: Map<any, T>): T[] {
|
||||
// return Array.from(map.values());
|
||||
let ret: T[] = [];
|
||||
map.forEach(v => ret.push(v));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
class EditorAndModel {
|
||||
namespace delta {
|
||||
|
||||
export function ofSets<T>(before: Set<T>, after: Set<T>): { removed: T[], added: T[] } {
|
||||
const removed: T[] = [];
|
||||
const added: T[] = [];
|
||||
before.forEach(element => {
|
||||
if (!after.has(element)) {
|
||||
removed.push(element);
|
||||
}
|
||||
});
|
||||
after.forEach(element => {
|
||||
if (!before.has(element)) {
|
||||
added.push(element);
|
||||
}
|
||||
});
|
||||
return { removed, added };
|
||||
};
|
||||
|
||||
export function ofMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[], added: V[] } {
|
||||
const removed: V[] = [];
|
||||
const added: V[] = [];
|
||||
before.forEach((value, index) => {
|
||||
if (!after.has(index)) {
|
||||
removed.push(value);
|
||||
}
|
||||
});
|
||||
after.forEach((value, index) => {
|
||||
if (!before.has(index)) {
|
||||
added.push(value);
|
||||
}
|
||||
});
|
||||
return { removed, added };
|
||||
};
|
||||
}
|
||||
|
||||
class EditorSnapshot {
|
||||
|
||||
readonly id: string;
|
||||
|
||||
constructor(
|
||||
readonly editor: ICommonCodeEditor,
|
||||
readonly document: IModel,
|
||||
) {
|
||||
this.id = `${editor.getId()},${document.uri.toString()}`;
|
||||
this.id = `${editor.getId()},${editor.getModel().id}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +101,8 @@ class DocumentAndEditorStateDelta {
|
||||
constructor(
|
||||
readonly removedDocuments: IModel[],
|
||||
readonly addedDocuments: IModel[],
|
||||
readonly removedEditors: EditorAndModel[],
|
||||
readonly addedEditors: EditorAndModel[],
|
||||
readonly removedEditors: EditorSnapshot[],
|
||||
readonly addedEditors: EditorSnapshot[],
|
||||
readonly oldActiveEditor: string,
|
||||
readonly newActiveEditor: string,
|
||||
) {
|
||||
@@ -85,10 +128,14 @@ class DocumentAndEditorState {
|
||||
|
||||
static compute(before: DocumentAndEditorState, after: DocumentAndEditorState): DocumentAndEditorStateDelta {
|
||||
if (!before) {
|
||||
return new DocumentAndEditorStateDelta([], after.documents, [], after.editors, undefined, after.activeEditor);
|
||||
return new DocumentAndEditorStateDelta(
|
||||
[], mapset.setValues(after.documents),
|
||||
[], mapset.mapValues(after.editors),
|
||||
undefined, after.activeEditor
|
||||
);
|
||||
}
|
||||
const documentDelta = delta(before.documents, after.documents, cmp.compareModels);
|
||||
const editorDelta = delta(before.editors, after.editors, cmp.compareEditors);
|
||||
const documentDelta = delta.ofSets(before.documents, after.documents);
|
||||
const editorDelta = delta.ofMaps(before.editors, after.editors);
|
||||
const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
|
||||
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
|
||||
|
||||
@@ -100,12 +147,11 @@ class DocumentAndEditorState {
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly documents: IModel[],
|
||||
readonly editors: EditorAndModel[],
|
||||
readonly documents: Set<IModel>,
|
||||
readonly editors: Map<string, EditorSnapshot>,
|
||||
readonly activeEditor: string,
|
||||
) {
|
||||
this.documents = documents.sort(cmp.compareModels);
|
||||
this.editors = editors.sort(cmp.compareEditors);
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +167,7 @@ class MainThreadDocumentAndEditorStateComputer {
|
||||
@ICodeEditorService private _codeEditorService: ICodeEditorService,
|
||||
@IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService
|
||||
) {
|
||||
this._modelService.onModelAdded(this._updateState, this, this._toDispose);
|
||||
this._modelService.onModelAdded(this._updateStateOnModelAdd, this, this._toDispose);
|
||||
this._modelService.onModelRemoved(this._updateState, this, this._toDispose);
|
||||
|
||||
this._codeEditorService.onCodeEditorAdd(this._onDidAddEditor, this, this._toDispose);
|
||||
@@ -151,19 +197,45 @@ class MainThreadDocumentAndEditorStateComputer {
|
||||
}
|
||||
}
|
||||
|
||||
private _updateStateOnModelAdd(model: IModel): void {
|
||||
if (model.isTooLargeForHavingARichMode()) {
|
||||
// ignore
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._currentState) {
|
||||
// too early
|
||||
this._updateState();
|
||||
return;
|
||||
}
|
||||
|
||||
// small (fast) delta
|
||||
this._currentState = new DocumentAndEditorState(
|
||||
this._currentState.documents.add(model),
|
||||
this._currentState.editors,
|
||||
this._currentState.activeEditor
|
||||
);
|
||||
|
||||
this._onDidChangeState(new DocumentAndEditorStateDelta(
|
||||
[], [model],
|
||||
[], [],
|
||||
this._currentState.activeEditor, this._currentState.activeEditor
|
||||
));
|
||||
}
|
||||
|
||||
private _updateState(): void {
|
||||
|
||||
// models: ignore too large models
|
||||
const models = this._modelService.getModels();
|
||||
for (let i = 0; i < models.length; i++) {
|
||||
if (models[i].isTooLargeForHavingARichMode()) {
|
||||
models.splice(i, 1);
|
||||
i--;
|
||||
const models = new Set<IModel>();
|
||||
for (const model of this._modelService.getModels()) {
|
||||
if (!model.isTooLargeForHavingARichMode()) {
|
||||
models.add(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// editor: only take those that have a not too large model
|
||||
const editors: EditorAndModel[] = [];
|
||||
const editors = new Map<string, EditorSnapshot>();
|
||||
let activeEditor: string = null;
|
||||
|
||||
for (const editor of this._codeEditorService.listCodeEditors()) {
|
||||
@@ -172,8 +244,8 @@ class MainThreadDocumentAndEditorStateComputer {
|
||||
&& !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
|
||||
) {
|
||||
const apiEditor = new EditorAndModel(editor, model);
|
||||
editors.push(apiEditor);
|
||||
const apiEditor = new EditorSnapshot(editor);
|
||||
editors.set(apiEditor.id, apiEditor);
|
||||
if (editor.isFocused()) {
|
||||
activeEditor = apiEditor.id;
|
||||
}
|
||||
@@ -194,12 +266,11 @@ class MainThreadDocumentAndEditorStateComputer {
|
||||
candidate = workbenchEditorControl.getModifiedEditor();
|
||||
}
|
||||
if (candidate) {
|
||||
for (const { editor, id } of editors) {
|
||||
if (candidate === editor) {
|
||||
activeEditor = id;
|
||||
break;
|
||||
editors.forEach(snapshot => {
|
||||
if (candidate === snapshot.editor) {
|
||||
activeEditor = snapshot.id;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,7 +321,7 @@ export class MainThreadDocumentsAndEditors {
|
||||
const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService);
|
||||
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
|
||||
|
||||
const mainThreadEditors = new MainThreadEditors(this, extHostContext, codeEditorService, this._workbenchEditorService, editorGroupService, telemetryService);
|
||||
const mainThreadEditors = new MainThreadEditors(this, extHostContext, codeEditorService, this._workbenchEditorService, editorGroupService, telemetryService, textModelResolverService, fileService, this._modelService);
|
||||
extHostContext.set(MainContext.MainThreadEditors, mainThreadEditors);
|
||||
|
||||
// It is expected that the ctor of the state computer calls our `_onDelta`.
|
||||
@@ -282,7 +353,7 @@ export class MainThreadDocumentsAndEditors {
|
||||
|
||||
// added editors
|
||||
for (const apiEditor of delta.addedEditors) {
|
||||
const mainThreadEditor = new MainThreadTextEditor(apiEditor.id, apiEditor.document,
|
||||
const mainThreadEditor = new MainThreadTextEditor(apiEditor.id, apiEditor.editor.getModel(),
|
||||
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService);
|
||||
|
||||
this._editors[apiEditor.id] = mainThreadEditor;
|
||||
|
||||
@@ -244,6 +244,17 @@ export class MainThreadTextEditor {
|
||||
this._codeEditor.setDecorations(key, ranges);
|
||||
}
|
||||
|
||||
public setDecorationsFast(key: string, _ranges: number[]): void {
|
||||
if (!this._codeEditor) {
|
||||
return;
|
||||
}
|
||||
let ranges: Range[] = [];
|
||||
for (let i = 0, len = Math.floor(_ranges.length / 4); i < len; i++) {
|
||||
ranges[i] = new Range(_ranges[4 * i], _ranges[4 * i + 1], _ranges[4 * i + 2], _ranges[4 * i + 3]);
|
||||
}
|
||||
this._codeEditor.setDecorationsFast(key, ranges);
|
||||
}
|
||||
|
||||
public revealRange(range: IRange, revealType: TextEditorRevealType): void {
|
||||
if (!this._codeEditor) {
|
||||
return;
|
||||
|
||||
@@ -8,7 +8,7 @@ import URI from 'vs/base/common/uri';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { disposed } from 'vs/base/common/errors';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ISingleEditOperation, IDecorationRenderOptions, IDecorationOptions, ILineChange } from 'vs/editor/common/editorCommon';
|
||||
import { ISingleEditOperation, IDecorationRenderOptions, IDecorationOptions, ILineChange, ICommonCodeEditor, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
|
||||
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
@@ -18,9 +18,13 @@ import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOption
|
||||
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { equals as objectEquals } from 'vs/base/common/objects';
|
||||
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext, IWorkspaceResourceEdit } from '../node/extHost.protocol';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { bulkEdit, IResourceEdit } from 'vs/editor/common/services/bulkEdit';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
|
||||
export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
|
||||
@@ -31,6 +35,7 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
private _toDispose: IDisposable[];
|
||||
private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
|
||||
private _editorPositionData: ITextEditorPositionData;
|
||||
private _registeredDecorationTypes: { [decorationType: string]: boolean; };
|
||||
|
||||
constructor(
|
||||
documentsAndEditors: MainThreadDocumentsAndEditors,
|
||||
@@ -38,7 +43,10 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
@ICodeEditorService private _codeEditorService: ICodeEditorService,
|
||||
@IWorkbenchEditorService workbenchEditorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITelemetryService telemetryService: ITelemetryService
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@ITextModelService private readonly _textModelResolverService: ITextModelService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
) {
|
||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostEditors);
|
||||
this._documentsAndEditors = documentsAndEditors;
|
||||
@@ -52,7 +60,9 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
this._toDispose.push(documentsAndEditors.onTextEditorRemove(editors => editors.forEach(this._onTextEditorRemove, this)));
|
||||
|
||||
this._toDispose.push(editorGroupService.onEditorsChanged(() => this._updateActiveAndVisibleTextEditors()));
|
||||
this._toDispose.push(editorGroupService.onEditorsMoved(() => this._updateActiveAndVisibleTextEditors()));
|
||||
this._toDispose.push(editorGroupService.onEditorGroupMoved(() => this._updateActiveAndVisibleTextEditors()));
|
||||
|
||||
this._registeredDecorationTypes = Object.create(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -61,6 +71,10 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
});
|
||||
this._textEditorsListenersMap = Object.create(null);
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
for (let decorationType in this._registeredDecorationTypes) {
|
||||
this._codeEditorService.removeDecorationType(decorationType);
|
||||
}
|
||||
this._registeredDecorationTypes = Object.create(null);
|
||||
}
|
||||
|
||||
private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
|
||||
@@ -126,6 +140,11 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
|
||||
$tryShowEditor(id: string, position: EditorPosition): TPromise<void> {
|
||||
// check how often this is used
|
||||
/* __GDPR__
|
||||
"api.deprecated" : {
|
||||
"function" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.show' });
|
||||
|
||||
let mainThreadEditor = this._documentsAndEditors.getEditor(id);
|
||||
@@ -141,6 +160,11 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
|
||||
$tryHideEditor(id: string): TPromise<void> {
|
||||
// check how often this is used
|
||||
/* __GDPR__
|
||||
"api.deprecated" : {
|
||||
"function" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.hide' });
|
||||
|
||||
let mainThreadEditor = this._documentsAndEditors.getEditor(id);
|
||||
@@ -171,6 +195,14 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
$trySetDecorationsFast(id: string, key: string, ranges: string): TPromise<any> {
|
||||
if (!this._documentsAndEditors.getEditor(id)) {
|
||||
return TPromise.wrapError(disposed(`TextEditor(${id})`));
|
||||
}
|
||||
this._documentsAndEditors.getEditor(id).setDecorationsFast(key, /*TODO: marshaller is too slow*/JSON.parse(ranges));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any> {
|
||||
if (!this._documentsAndEditors.getEditor(id)) {
|
||||
return TPromise.wrapError(disposed(`TextEditor(${id})`));
|
||||
@@ -194,6 +226,52 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
return TPromise.as(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
|
||||
}
|
||||
|
||||
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean> {
|
||||
|
||||
// First check if loaded models were not changed in the meantime
|
||||
for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
|
||||
const workspaceResourceEdit = workspaceResourceEdits[i];
|
||||
if (workspaceResourceEdit.modelVersionId) {
|
||||
let model = this._modelService.getModel(workspaceResourceEdit.resource);
|
||||
if (model && model.getVersionId() !== workspaceResourceEdit.modelVersionId) {
|
||||
// model changed in the meantime
|
||||
return TPromise.as(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to shape expected by bulkEdit below
|
||||
let resourceEdits: IResourceEdit[] = [];
|
||||
for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
|
||||
const workspaceResourceEdit = workspaceResourceEdits[i];
|
||||
const uri = workspaceResourceEdit.resource;
|
||||
const edits = workspaceResourceEdit.edits;
|
||||
|
||||
for (let j = 0, lenJ = edits.length; j < lenJ; j++) {
|
||||
const edit = edits[j];
|
||||
|
||||
resourceEdits.push({
|
||||
resource: uri,
|
||||
newText: edit.newText,
|
||||
newEol: edit.newEol,
|
||||
range: edit.range
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let codeEditor: ICommonCodeEditor;
|
||||
let editor = this._workbenchEditorService.getActiveEditor();
|
||||
if (editor) {
|
||||
let candidate = editor.getControl();
|
||||
if (isCommonCodeEditor(candidate)) {
|
||||
codeEditor = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return bulkEdit(this._textModelResolverService, codeEditor, resourceEdits, this._fileService)
|
||||
.then(() => true);
|
||||
}
|
||||
|
||||
$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {
|
||||
if (!this._documentsAndEditors.getEditor(id)) {
|
||||
return TPromise.wrapError<boolean>(disposed(`TextEditor(${id})`));
|
||||
@@ -202,10 +280,12 @@ export class MainThreadEditors implements MainThreadEditorsShape {
|
||||
}
|
||||
|
||||
$registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {
|
||||
this._registeredDecorationTypes[key] = true;
|
||||
this._codeEditorService.registerDecorationType(key, options);
|
||||
}
|
||||
|
||||
$removeTextEditorDecorationType(key: string): void {
|
||||
delete this._registeredDecorationTypes[key];
|
||||
this._codeEditorService.removeDecorationType(key);
|
||||
}
|
||||
|
||||
|
||||
163
src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts
Normal file
163
src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 URI from 'vs/base/common/uri';
|
||||
import { TPromise, PPromise } from 'vs/base/common/winjs.base';
|
||||
import { ExtHostContext, MainContext, IExtHostContext, MainThreadFileSystemShape, ExtHostFileSystemShape } from '../node/extHost.protocol';
|
||||
import { IFileService, IFileSystemProvider, IStat, IFileChange } from 'vs/platform/files/common/files';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
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 } from 'vs/platform/search/common/search';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadFileSystem)
|
||||
export class MainThreadFileSystem implements MainThreadFileSystemShape {
|
||||
|
||||
private readonly _toDispose: IDisposable[] = [];
|
||||
private readonly _proxy: ExtHostFileSystemShape;
|
||||
private readonly _provider = new Map<number, RemoteFileSystemProvider>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@ISearchService private readonly _searchService: ISearchService,
|
||||
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService
|
||||
) {
|
||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostFileSystem);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._toDispose);
|
||||
}
|
||||
|
||||
$registerFileSystemProvider(handle: number, scheme: string): void {
|
||||
this._provider.set(handle, new RemoteFileSystemProvider(this._fileService, this._searchService, scheme, handle, this._proxy));
|
||||
}
|
||||
|
||||
$unregisterFileSystemProvider(handle: number): void {
|
||||
dispose(this._provider.get(handle));
|
||||
this._provider.delete(handle);
|
||||
}
|
||||
|
||||
$onDidAddFileSystemRoot(uri: URI): void {
|
||||
this._workspaceEditingService.addFolders([{ uri }], true).done(null, onUnexpectedError);
|
||||
}
|
||||
|
||||
$onFileSystemChange(handle: number, changes: IFileChange[]): void {
|
||||
this._provider.get(handle).$onFileSystemChange(changes);
|
||||
}
|
||||
|
||||
$reportFileChunk(handle: number, resource: URI, chunk: number[]): void {
|
||||
this._provider.get(handle).reportFileChunk(resource, chunk);
|
||||
}
|
||||
|
||||
// --- search
|
||||
|
||||
$handleSearchProgress(handle: number, session: number, resource: URI): void {
|
||||
this._provider.get(handle).handleSearchProgress(session, resource);
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProvider {
|
||||
|
||||
private readonly _onDidChange = new Emitter<IFileChange[]>();
|
||||
private readonly _reads = new Map<string, IProgress<Uint8Array>>();
|
||||
private readonly _registrations: IDisposable[];
|
||||
|
||||
readonly onDidChange: Event<IFileChange[]> = this._onDidChange.event;
|
||||
|
||||
|
||||
constructor(
|
||||
fileService: IFileService,
|
||||
searchService: ISearchService,
|
||||
scheme: string,
|
||||
private readonly _handle: number,
|
||||
private readonly _proxy: ExtHostFileSystemShape
|
||||
) {
|
||||
this._registrations = [
|
||||
fileService.registerProvider(scheme, this),
|
||||
searchService.registerSearchResultProvider(this),
|
||||
];
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._registrations);
|
||||
this._onDidChange.dispose();
|
||||
}
|
||||
|
||||
$onFileSystemChange(changes: IFileChange[]): void {
|
||||
this._onDidChange.fire(changes);
|
||||
}
|
||||
|
||||
// --- 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> {
|
||||
this._reads.set(resource.toString(), progress);
|
||||
return this._proxy.$read(this._handle, offset, count, resource);
|
||||
}
|
||||
reportFileChunk(resource: URI, chunk: number[]): void {
|
||||
this._reads.get(resource.toString()).report(Buffer.from(chunk));
|
||||
}
|
||||
write(resource: URI, content: Uint8Array): TPromise<void, any> {
|
||||
return this._proxy.$write(this._handle, resource, [].slice.call(content));
|
||||
}
|
||||
unlink(resource: URI): TPromise<void, any> {
|
||||
return this._proxy.$unlink(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> {
|
||||
return this._proxy.$mkdir(this._handle, resource);
|
||||
}
|
||||
readdir(resource: URI): TPromise<[URI, IStat][], any> {
|
||||
return this._proxy.$readdir(this._handle, resource);
|
||||
}
|
||||
rmdir(resource: URI): TPromise<void, any> {
|
||||
return this._proxy.$rmdir(this._handle, resource);
|
||||
}
|
||||
|
||||
// --- search
|
||||
|
||||
private _searches = new Map<number, (resource: URI) => void>();
|
||||
private _searchesIdPool = 0;
|
||||
|
||||
search(query: ISearchQuery): PPromise<ISearchComplete, ISearchProgressItem> {
|
||||
if (query.type === QueryType.Text) {
|
||||
return PPromise.as<ISearchComplete>({ results: [], stats: undefined });
|
||||
}
|
||||
const id = ++this._searchesIdPool;
|
||||
const matches: IFileMatch[] = [];
|
||||
return new PPromise((resolve, reject, report) => {
|
||||
this._proxy.$fileFiles(this._handle, id, query.filePattern).then(() => {
|
||||
this._searches.delete(id);
|
||||
resolve({
|
||||
results: matches,
|
||||
stats: undefined
|
||||
});
|
||||
}, reject);
|
||||
|
||||
this._searches.set(id, resource => {
|
||||
const match: IFileMatch = { resource };
|
||||
matches.push(match);
|
||||
report(match);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleSearchProgress(session: number, resource: URI): void {
|
||||
this._searches.get(session)(resource);
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,11 @@ 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, IRawColorFormatMap, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { IHeapService } from './mainThreadHeapService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ColorFormatter, CombinedColorFormatter } from 'vs/editor/contrib/colorPicker/common/colorFormatter';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
|
||||
@@ -30,7 +29,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
private _heapService: IHeapService;
|
||||
private _modeService: IModeService;
|
||||
private _registrations: { [handle: number]: IDisposable; } = Object.create(null);
|
||||
private _formatters: Map<number, ColorFormatter>;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@@ -40,7 +38,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostLanguageFeatures);
|
||||
this._heapService = heapService;
|
||||
this._modeService = modeService;
|
||||
this._formatters = new Map<number, ColorFormatter>();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -208,9 +205,17 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
// --- navigate type
|
||||
|
||||
$registerNavigateTypeSupport(handle: number): TPromise<any> {
|
||||
let lastResultId: number;
|
||||
this._registrations[handle] = WorkspaceSymbolProviderRegistry.register(<IWorkspaceSymbolProvider>{
|
||||
provideWorkspaceSymbols: (search: string): TPromise<modes.SymbolInformation[]> => {
|
||||
return this._heapService.trackRecursive(this._proxy.$provideWorkspaceSymbols(handle, search));
|
||||
|
||||
return this._proxy.$provideWorkspaceSymbols(handle, search).then(result => {
|
||||
if (lastResultId !== undefined) {
|
||||
this._proxy.$releaseWorkspaceSymbols(handle, lastResultId);
|
||||
}
|
||||
lastResultId = result._id;
|
||||
return result.symbols;
|
||||
});
|
||||
},
|
||||
resolveWorkspaceSymbol: (item: modes.SymbolInformation): TPromise<modes.SymbolInformation> => {
|
||||
return this._proxy.$resolveWorkspaceSymbol(handle, item);
|
||||
@@ -232,15 +237,25 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
// --- suggest
|
||||
|
||||
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[]): TPromise<any> {
|
||||
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[], supportsResolveDetails: boolean): TPromise<any> {
|
||||
|
||||
this._registrations[handle] = modes.SuggestRegistry.register(selector, <modes.ISuggestSupport>{
|
||||
triggerCharacters,
|
||||
provideCompletionItems: (model: IReadOnlyModel, position: EditorPosition, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position)));
|
||||
provideCompletionItems: (model: IReadOnlyModel, position: EditorPosition, context: modes.SuggestContext, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position, context)).then(result => {
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
return {
|
||||
suggestions: result.suggestions,
|
||||
incomplete: result.incomplete,
|
||||
dispose: () => this._proxy.$releaseCompletionItems(handle, result._id)
|
||||
};
|
||||
});
|
||||
},
|
||||
resolveCompletionItem: (model: IReadOnlyModel, position: EditorPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable<modes.ISuggestion> => {
|
||||
return wireCancellationToken(token, this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion));
|
||||
}
|
||||
resolveCompletionItem: supportsResolveDetails
|
||||
? (model, position, suggestion, token) => wireCancellationToken(token, this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion))
|
||||
: undefined
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
@@ -279,44 +294,37 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
const proxy = this._proxy;
|
||||
this._registrations[handle] = modes.ColorProviderRegistry.register(selector, <modes.DocumentColorProvider>{
|
||||
provideColorRanges: (model, token) => {
|
||||
provideDocumentColors: (model, token) => {
|
||||
return wireCancellationToken(token, proxy.$provideDocumentColors(handle, model.uri))
|
||||
.then(documentColors => {
|
||||
return documentColors.map(documentColor => {
|
||||
const formatters = documentColor.availableFormats.map(f => {
|
||||
if (typeof f === 'number') {
|
||||
return this._formatters.get(f);
|
||||
} else {
|
||||
return new CombinedColorFormatter(this._formatters.get(f[0]), this._formatters.get(f[1]));
|
||||
}
|
||||
});
|
||||
|
||||
const [red, green, blue, alpha] = documentColor.color;
|
||||
const color = {
|
||||
red: red / 255.0,
|
||||
green: green / 255.0,
|
||||
blue: blue / 255.0,
|
||||
red: red,
|
||||
green: green,
|
||||
blue: blue,
|
||||
alpha
|
||||
};
|
||||
|
||||
return {
|
||||
color,
|
||||
formatters,
|
||||
range: documentColor.range
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
provideColorPresentations: (model, colorInfo, token) => {
|
||||
return wireCancellationToken(token, proxy.$provideColorPresentations(handle, model.uri, {
|
||||
color: [colorInfo.color.red, colorInfo.color.green, colorInfo.color.blue, colorInfo.color.alpha],
|
||||
range: colorInfo.range
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
$registerColorFormats(formats: IRawColorFormatMap): TPromise<any> {
|
||||
formats.forEach(f => this._formatters.set(f[0], new ColorFormatter(f[1])));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
$setLanguageConfiguration(handle: number, languageId: string, _configuration: vscode.LanguageConfiguration): TPromise<any> {
|
||||
|
||||
@@ -10,23 +10,38 @@ import URI from 'vs/base/common/uri';
|
||||
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 } from 'vs/workbench/services/scm/common/scm';
|
||||
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, ISCMResourceCollection, ISCMResourceSplice } from 'vs/workbench/services/scm/common/scm';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResource, SCMGroupFeatures, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
|
||||
class MainThreadSCMResourceCollection implements ISCMResourceCollection {
|
||||
|
||||
readonly resources: ISCMResource[] = [];
|
||||
|
||||
private _onDidSplice = new Emitter<ISCMResourceSplice>();
|
||||
readonly onDidSplice = this._onDidSplice.event;
|
||||
|
||||
splice(start: number, deleteCount: number, resources: ISCMResource[]) {
|
||||
this.resources.splice(start, deleteCount, ...resources);
|
||||
this._onDidSplice.fire({ start, deleteCount, resources });
|
||||
}
|
||||
}
|
||||
|
||||
class MainThreadSCMResourceGroup implements ISCMResourceGroup {
|
||||
|
||||
readonly resourceCollection = new MainThreadSCMResourceCollection();
|
||||
get hideWhenEmpty(): boolean { return this.features.hideWhenEmpty; }
|
||||
|
||||
constructor(
|
||||
private sourceControlHandle: number,
|
||||
private handle: number,
|
||||
public provider: ISCMProvider,
|
||||
public features: SCMGroupFeatures,
|
||||
public label: string,
|
||||
public id: string,
|
||||
public resources: ISCMResource[]
|
||||
public id: string
|
||||
) { }
|
||||
|
||||
toJSON(): any {
|
||||
@@ -41,15 +56,19 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup {
|
||||
class MainThreadSCMResource implements ISCMResource {
|
||||
|
||||
constructor(
|
||||
private proxy: ExtHostSCMShape,
|
||||
private sourceControlHandle: number,
|
||||
private groupHandle: number,
|
||||
private handle: number,
|
||||
public sourceUri: URI,
|
||||
public command: Command | undefined,
|
||||
public resourceGroup: ISCMResourceGroup,
|
||||
public decorations: ISCMResourceDecorations
|
||||
) { }
|
||||
|
||||
open(): TPromise<void> {
|
||||
return this.proxy.$executeResourceCommand(this.sourceControlHandle, this.groupHandle, this.handle);
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
return {
|
||||
$mid: 3,
|
||||
@@ -71,42 +90,41 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
|
||||
get resources(): ISCMResourceGroup[] {
|
||||
return this._groups
|
||||
.filter(g => g.resources.length > 0 || !g.features.hideWhenEmpty);
|
||||
.filter(g => g.resourceCollection.resources.length > 0 || !g.features.hideWhenEmpty);
|
||||
}
|
||||
|
||||
private _onDidChange = new Emitter<void>();
|
||||
get onDidChange(): Event<void> { return this._onDidChange.event; }
|
||||
private _onDidChangeResources = new Emitter<void>();
|
||||
get onDidChangeResources(): Event<void> { return this._onDidChangeResources.event; }
|
||||
|
||||
private features: SCMProviderFeatures = {};
|
||||
|
||||
get handle(): number { return this._handle; }
|
||||
get label(): string { return this._label; }
|
||||
get rootUri(): URI | undefined { return this._rootUri; }
|
||||
get contextValue(): string { return this._contextValue; }
|
||||
|
||||
get commitTemplate(): string | undefined { return this.features.commitTemplate; }
|
||||
get acceptInputCommand(): Command | undefined { return this.features.acceptInputCommand; }
|
||||
get statusBarCommands(): Command[] | undefined { return this.features.statusBarCommands; }
|
||||
get count(): number | undefined { return this.features.count; }
|
||||
|
||||
private _onDidChangeCommitTemplate = new Emitter<string>();
|
||||
get onDidChangeCommitTemplate(): Event<string> { return this._onDidChangeCommitTemplate.event; }
|
||||
|
||||
private _count: number | undefined = undefined;
|
||||
get count(): number | undefined { return this._count; }
|
||||
private _onDidChange = new Emitter<void>();
|
||||
get onDidChange(): Event<void> { return this._onDidChange.event; }
|
||||
|
||||
constructor(
|
||||
private proxy: ExtHostSCMShape,
|
||||
private _handle: number,
|
||||
private _contextValue: string,
|
||||
private _label: string,
|
||||
private _rootUri: URI | undefined,
|
||||
@ISCMService scmService: ISCMService,
|
||||
@ICommandService private commandService: ICommandService
|
||||
) { }
|
||||
|
||||
$updateSourceControl(features: SCMProviderFeatures): void {
|
||||
if ('count' in features) {
|
||||
this._count = features.count;
|
||||
}
|
||||
|
||||
this.features = assign(this.features, features);
|
||||
this._onDidChange.fire();
|
||||
|
||||
@@ -122,8 +140,7 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
this,
|
||||
{},
|
||||
label,
|
||||
id,
|
||||
[]
|
||||
id
|
||||
);
|
||||
|
||||
this._groups.push(group);
|
||||
@@ -152,37 +169,50 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
$updateGroupResourceStates(groupHandle: number, resources: SCMRawResource[]): void {
|
||||
const group = this._groupsByHandle[groupHandle];
|
||||
$spliceGroupResourceStates(splices: SCMRawResourceSplices[]): void {
|
||||
for (const [groupHandle, groupSlices] of splices) {
|
||||
const group = this._groupsByHandle[groupHandle];
|
||||
|
||||
if (!group) {
|
||||
return;
|
||||
if (!group) {
|
||||
console.warn(`SCM group ${groupHandle} not found in provider ${this.label}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// reverse the splices sequence in order to apply them correctly
|
||||
groupSlices.reverse();
|
||||
|
||||
for (const [start, deleteCount, rawResources] of groupSlices) {
|
||||
const resources = rawResources.map(rawResource => {
|
||||
const [handle, sourceUri, icons, tooltip, strikeThrough, faded, source, letter, color] = rawResource;
|
||||
const icon = icons[0];
|
||||
const iconDark = icons[1] || icon;
|
||||
const decorations = {
|
||||
icon: icon && URI.parse(icon),
|
||||
iconDark: iconDark && URI.parse(iconDark),
|
||||
tooltip,
|
||||
strikeThrough,
|
||||
faded,
|
||||
source,
|
||||
letter,
|
||||
color: color && color.id
|
||||
};
|
||||
|
||||
return new MainThreadSCMResource(
|
||||
this.proxy,
|
||||
this.handle,
|
||||
groupHandle,
|
||||
handle,
|
||||
URI.parse(sourceUri),
|
||||
group,
|
||||
decorations
|
||||
);
|
||||
});
|
||||
|
||||
group.resourceCollection.splice(start, deleteCount, resources);
|
||||
}
|
||||
}
|
||||
|
||||
group.resources = resources.map(rawResource => {
|
||||
const [handle, sourceUri, command, icons, tooltip, strikeThrough, faded] = rawResource;
|
||||
const icon = icons[0];
|
||||
const iconDark = icons[1] || icon;
|
||||
const decorations = {
|
||||
icon: icon && URI.parse(icon),
|
||||
iconDark: iconDark && URI.parse(iconDark),
|
||||
tooltip,
|
||||
strikeThrough,
|
||||
faded
|
||||
};
|
||||
|
||||
return new MainThreadSCMResource(
|
||||
this.handle,
|
||||
groupHandle,
|
||||
handle,
|
||||
URI.parse(sourceUri),
|
||||
command,
|
||||
group,
|
||||
decorations
|
||||
);
|
||||
});
|
||||
|
||||
this._onDidChange.fire();
|
||||
this._onDidChangeResources.fire();
|
||||
}
|
||||
|
||||
$unregisterGroup(handle: number): void {
|
||||
@@ -245,8 +275,8 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
||||
this._disposables = dispose(this._disposables);
|
||||
}
|
||||
|
||||
$registerSourceControl(handle: number, id: string, label: string): void {
|
||||
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, this.scmService, this.commandService);
|
||||
$registerSourceControl(handle: number, id: string, label: string, rootUri: string | undefined): void {
|
||||
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri && URI.parse(rootUri), this.scmService, this.commandService);
|
||||
const repository = this.scmService.registerSCMProvider(provider);
|
||||
this._repositories[handle] = repository;
|
||||
|
||||
@@ -312,7 +342,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
||||
provider.$updateGroupLabel(groupHandle, label);
|
||||
}
|
||||
|
||||
$updateGroupResourceStates(sourceControlHandle: number, groupHandle: number, resources: SCMRawResource[]): void {
|
||||
$spliceResourceStates(sourceControlHandle: number, splices: SCMRawResourceSplices[]): void {
|
||||
const repository = this._repositories[sourceControlHandle];
|
||||
|
||||
if (!repository) {
|
||||
@@ -320,7 +350,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
||||
}
|
||||
|
||||
const provider = repository.provider as MainThreadSCMProvider;
|
||||
provider.$updateGroupResourceStates(groupHandle, resources);
|
||||
provider.$spliceGroupResourceStates(splices);
|
||||
}
|
||||
|
||||
$unregisterGroup(sourceControlHandle: number, handle: number): void {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textF
|
||||
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
|
||||
export interface INamedSaveParticpant extends ISaveParticipant {
|
||||
readonly name: string;
|
||||
@@ -41,7 +42,7 @@ class TrimWhitespaceParticipant implements INamedSaveParticpant {
|
||||
}
|
||||
|
||||
public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void {
|
||||
if (this.configurationService.lookup('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() }).value) {
|
||||
if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
|
||||
this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO);
|
||||
}
|
||||
}
|
||||
@@ -99,7 +100,7 @@ export class FinalNewLineParticipant implements INamedSaveParticpant {
|
||||
}
|
||||
|
||||
public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void {
|
||||
if (this.configurationService.lookup('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() }).value) {
|
||||
if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
|
||||
this.doInsertFinalNewLine(model.textEditorModel);
|
||||
}
|
||||
}
|
||||
@@ -127,12 +128,60 @@ export class FinalNewLineParticipant implements INamedSaveParticpant {
|
||||
}
|
||||
}
|
||||
|
||||
export class TrimFinalNewLinesParticipant implements INamedSaveParticpant {
|
||||
|
||||
readonly name = 'TrimFinalNewLinesParticipant';
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@ICodeEditorService private codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void {
|
||||
if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
|
||||
this.doTrimFinalNewLines(model.textEditorModel);
|
||||
}
|
||||
}
|
||||
|
||||
private doTrimFinalNewLines(model: IModel): void {
|
||||
const lineCount = model.getLineCount();
|
||||
|
||||
// Do not insert new line if file does not end with new line
|
||||
if (!lineCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
prevSelection = editor.getSelections();
|
||||
}
|
||||
|
||||
let currentLineNumber = model.getLineCount();
|
||||
let currentLine = model.getLineContent(currentLineNumber);
|
||||
let currentLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(currentLine) === -1;
|
||||
while (currentLineIsEmptyOrWhitespace) {
|
||||
currentLineNumber--;
|
||||
currentLine = model.getLineContent(currentLineNumber);
|
||||
currentLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(currentLine) === -1;
|
||||
}
|
||||
model.pushEditOperations(prevSelection, [EditOperation.delete(new Range(currentLineNumber + 1, 1, lineCount + 1, 1))], edits => prevSelection);
|
||||
|
||||
if (editor) {
|
||||
editor.setSelections(prevSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FormatOnSaveParticipant implements INamedSaveParticpant {
|
||||
|
||||
readonly name = 'FormatOnSaveParticipant';
|
||||
|
||||
constructor(
|
||||
@ICodeEditorService private _editorService: ICodeEditorService,
|
||||
@IEditorWorkerService private _editorWorkerService: IEditorWorkerService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService
|
||||
) {
|
||||
// Nothing
|
||||
@@ -142,7 +191,7 @@ class FormatOnSaveParticipant implements INamedSaveParticpant {
|
||||
|
||||
const model = editorModel.textEditorModel;
|
||||
if (env.reason === SaveReason.AUTO
|
||||
|| !this._configurationService.lookup('editor.formatOnSave', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() }).value) {
|
||||
|| !this._configurationService.getValue('editor.formatOnSave', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() })) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -151,7 +200,9 @@ class FormatOnSaveParticipant implements INamedSaveParticpant {
|
||||
|
||||
return new TPromise<ISingleEditOperation[]>((resolve, reject) => {
|
||||
setTimeout(reject, 750);
|
||||
getDocumentFormattingEdits(model, { tabSize, insertSpaces }).then(resolve, reject);
|
||||
getDocumentFormattingEdits(model, { tabSize, insertSpaces })
|
||||
.then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits))
|
||||
.then(resolve, reject);
|
||||
|
||||
}).then(edits => {
|
||||
if (edits && versionNow === model.getVersionId()) {
|
||||
@@ -230,13 +281,15 @@ export class SaveParticipant implements ISaveParticipant {
|
||||
@ITelemetryService private _telemetryService: ITelemetryService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService,
|
||||
@IEditorWorkerService editorWorkerService: IEditorWorkerService
|
||||
) {
|
||||
|
||||
this._saveParticipants = [
|
||||
new TrimWhitespaceParticipant(configurationService, codeEditorService),
|
||||
new FormatOnSaveParticipant(codeEditorService, configurationService),
|
||||
new FormatOnSaveParticipant(codeEditorService, editorWorkerService, configurationService),
|
||||
new FinalNewLineParticipant(configurationService, codeEditorService),
|
||||
new TrimFinalNewLinesParticipant(configurationService, codeEditorService),
|
||||
new ExtHostSaveParticipant(extHostContext)
|
||||
];
|
||||
|
||||
@@ -266,6 +319,20 @@ export class SaveParticipant implements ISaveParticipant {
|
||||
});
|
||||
|
||||
return sequence(promiseFactory).then(() => {
|
||||
/* __GDPR__
|
||||
"saveParticipantStats" : {
|
||||
"${wildcard}": [
|
||||
{
|
||||
"${prefix}": "Success-",
|
||||
"${classification}": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
|
||||
},
|
||||
{
|
||||
"${prefix}": "Failure-",
|
||||
"${classification}": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('saveParticipantStats', stats);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
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';
|
||||
@@ -19,7 +22,8 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ITaskService private _taskService: ITaskService
|
||||
@ITaskService private _taskService: ITaskService,
|
||||
@IWorkspaceContextService private _workspaceContextServer: IWorkspaceContextService
|
||||
) {
|
||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostTask);
|
||||
this._activeHandles = Object.create(null);
|
||||
@@ -35,7 +39,18 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
public $registerTaskProvider(handle: number): TPromise<void> {
|
||||
this._taskService.registerTaskProvider(handle, {
|
||||
provideTasks: () => {
|
||||
return this._proxy.$provideTasks(handle);
|
||||
return this._proxy.$provideTasks(handle).then((value) => {
|
||||
for (let task of value.tasks) {
|
||||
if (ContributedTask.is(task)) {
|
||||
let uri = (task._source as any as ExtensionTaskSourceTransfer).__workspaceFolder;
|
||||
if (uri) {
|
||||
delete (task._source as any as ExtensionTaskSourceTransfer).__workspaceFolder;
|
||||
(task._source as any).workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
});
|
||||
this._activeHandles[handle] = true;
|
||||
|
||||
@@ -25,6 +25,11 @@ export class MainThreadTelemetry implements MainThreadTelemetryShape {
|
||||
}
|
||||
|
||||
$publicLog(eventName: string, data: any = Object.create(null)): void {
|
||||
/* __GDPR__FRAGMENT__
|
||||
"MainThreadData" : {
|
||||
"pluginHostTelemetry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
data[MainThreadTelemetry._name] = true;
|
||||
this._telemetryService.publicLog(eventName, data);
|
||||
}
|
||||
|
||||
@@ -33,13 +33,14 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
// when the extension host process goes down ?
|
||||
}
|
||||
|
||||
public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], waitOnExit?: boolean): TPromise<number> {
|
||||
public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], env?: { [key: string]: string }, waitOnExit?: boolean): TPromise<number> {
|
||||
const shellLaunchConfig: IShellLaunchConfig = {
|
||||
name,
|
||||
executable: shellPath,
|
||||
args: shellArgs,
|
||||
waitOnExit,
|
||||
ignoreConfigurationCwd: true
|
||||
ignoreConfigurationCwd: true,
|
||||
env
|
||||
};
|
||||
return TPromise.as(this.terminalService.createInstance(shellLaunchConfig).id);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ 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 { IMessageService, Severity } from 'vs/platform/message/common/message';
|
||||
import { ViewsRegistry } from 'vs/workbench/parts/views/browser/viewsRegistry';
|
||||
import { ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState } from 'vs/workbench/parts/views/common/views';
|
||||
import { ViewsRegistry } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState } from 'vs/workbench/common/views';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
|
||||
|
||||
@@ -6,20 +6,17 @@
|
||||
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ISearchService, QueryType, ISearchQuery, ISearchProgressItem, ISearchComplete } from 'vs/platform/search/common/search';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ISearchService, QueryType, ISearchQuery, IFolderQuery, ISearchConfiguration } from 'vs/platform/search/common/search';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ICommonCodeEditor, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
|
||||
import { bulkEdit, IResourceEdit } from 'vs/editor/common/services/bulkEdit';
|
||||
import { TPromise, PPromise } from 'vs/base/common/winjs.base';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
|
||||
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@@ -33,12 +30,13 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@ISearchService private readonly _searchService: ISearchService,
|
||||
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
|
||||
@ITextFileService private readonly _textFileService: ITextFileService,
|
||||
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
|
||||
@ITextModelService private readonly _textModelResolverService: ITextModelService,
|
||||
@IFileService private readonly _fileService: IFileService
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IWorkspaceEditingService private _workspaceEditingService: IWorkspaceEditingService
|
||||
) {
|
||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostWorkspace);
|
||||
this._contextService.onDidChangeWorkspaceRoots(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -53,23 +51,42 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
// --- workspace ---
|
||||
|
||||
private _onDidChangeWorkspace(): void {
|
||||
this._proxy.$acceptWorkspaceData(this._contextService.getWorkspace());
|
||||
this._proxy.$acceptWorkspaceData(this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace());
|
||||
}
|
||||
|
||||
// --- search ---
|
||||
|
||||
$startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable<URI[]> {
|
||||
$startSearch(include: string | IRelativePattern, exclude: string | IRelativePattern, maxResults: number, requestId: number): Thenable<URI[]> {
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
if (!workspace) {
|
||||
if (!workspace.folders.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let folderQueries: IFolderQuery[];
|
||||
if (typeof include === 'string' || !include) {
|
||||
folderQueries = workspace.folders.map(folder => ({ folder: folder.uri })); // absolute pattern: search across all folders
|
||||
} else {
|
||||
folderQueries = [{ folder: URI.file(include.base) }]; // relative pattern: search only in base folder
|
||||
}
|
||||
|
||||
const useRipgrep = folderQueries.every(folderQuery => {
|
||||
const folderConfig = this._configurationService.getConfiguration<ISearchConfiguration>({ resource: folderQuery.folder });
|
||||
return folderConfig.search.useRipgrep;
|
||||
});
|
||||
|
||||
const ignoreSymlinks = folderQueries.every(folderQuery => {
|
||||
const folderConfig = this._configurationService.getConfiguration<ISearchConfiguration>({ resource: folderQuery.folder });
|
||||
return !folderConfig.search.followSymlinks;
|
||||
});
|
||||
|
||||
const query: ISearchQuery = {
|
||||
folderQueries: workspace.roots.map(root => ({ folder: root })),
|
||||
folderQueries,
|
||||
type: QueryType.File,
|
||||
maxResults,
|
||||
includePattern: { [include]: true },
|
||||
excludePattern: { [exclude]: true },
|
||||
includePattern: { [typeof include === 'string' ? include : !!include ? include.pattern : undefined]: true },
|
||||
excludePattern: { [typeof exclude === 'string' ? exclude : !!exclude ? exclude.pattern : undefined]: true },
|
||||
useRipgrep,
|
||||
ignoreSymlinks
|
||||
};
|
||||
this._searchService.extendQuery(query);
|
||||
|
||||
@@ -106,96 +123,5 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
return result.results.every(each => each.success === true);
|
||||
});
|
||||
}
|
||||
|
||||
$applyWorkspaceEdit(edits: IResourceEdit[]): TPromise<boolean> {
|
||||
|
||||
let codeEditor: ICommonCodeEditor;
|
||||
let editor = this._editorService.getActiveEditor();
|
||||
if (editor) {
|
||||
let candidate = editor.getControl();
|
||||
if (isCommonCodeEditor(candidate)) {
|
||||
codeEditor = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return bulkEdit(this._textModelResolverService, codeEditor, edits, this._fileService)
|
||||
.then(() => true);
|
||||
}
|
||||
|
||||
// --- EXPERIMENT: workspace provider
|
||||
|
||||
private _idPool: number = 0;
|
||||
private readonly _provider = new Map<number, [IDisposable, Emitter<URI>]>();
|
||||
private readonly _searchSessions = new Map<number, { resolve: (result: ISearchComplete) => void, reject: Function, progress: (item: ISearchProgressItem) => void, matches: URI[] }>();
|
||||
|
||||
$registerFileSystemProvider(handle: number, authority: string): void {
|
||||
if (!(this._fileService instanceof RemoteFileService)) {
|
||||
throw new Error();
|
||||
}
|
||||
const emitter = new Emitter<URI>();
|
||||
const provider = {
|
||||
onDidChange: emitter.event,
|
||||
resolve: (resource) => {
|
||||
return this._proxy.$resolveFile(handle, resource);
|
||||
},
|
||||
update: (resource, value) => {
|
||||
return this._proxy.$storeFile(handle, resource, value);
|
||||
}
|
||||
};
|
||||
const searchProvider = {
|
||||
search: (query) => {
|
||||
if (query.type !== QueryType.File) {
|
||||
return undefined;
|
||||
}
|
||||
const session = ++this._idPool;
|
||||
return new PPromise<any, any>((resolve, reject, progress) => {
|
||||
this._searchSessions.set(session, { resolve, reject, progress, matches: [] });
|
||||
this._proxy.$startSearch(handle, session, query.filePattern);
|
||||
}, () => {
|
||||
this._proxy.$cancelSearch(handle, session);
|
||||
});
|
||||
}
|
||||
};
|
||||
const registrations = combinedDisposable([
|
||||
this._fileService.registerProvider(authority, provider),
|
||||
this._searchService.registerSearchResultProvider(searchProvider),
|
||||
]);
|
||||
this._provider.set(handle, [registrations, emitter]);
|
||||
}
|
||||
|
||||
$unregisterFileSystemProvider(handle: number): void {
|
||||
if (this._provider.has(handle)) {
|
||||
dispose(this._provider.get(handle)[0]);
|
||||
this._provider.delete(handle);
|
||||
}
|
||||
}
|
||||
|
||||
$onFileSystemChange(handle: number, resource: URI) {
|
||||
const [, emitter] = this._provider.get(handle);
|
||||
emitter.fire(resource);
|
||||
};
|
||||
|
||||
$updateSearchSession(session: number, data: URI): void {
|
||||
if (this._searchSessions.has(session)) {
|
||||
this._searchSessions.get(session).progress({ resource: data });
|
||||
this._searchSessions.get(session).matches.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
$finishSearchSession(session: number, err?: any): void {
|
||||
if (this._searchSessions.has(session)) {
|
||||
const { matches, resolve, reject } = this._searchSessions.get(session);
|
||||
this._searchSessions.delete(session);
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({
|
||||
limitHit: false,
|
||||
stats: undefined,
|
||||
results: matches.map(resource => ({ resource }))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user