mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-25 02:58:56 +01:00
Split up ext host <-> main thread communication to separate files
This commit is contained in:
@@ -7,11 +7,10 @@
|
||||
import {Emitter} from 'vs/base/common/event';
|
||||
import {score} from 'vs/editor/common/modes/languageSelector';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import {regExpLeadsToEndlessLoop} from 'vs/base/common/strings';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import {ExtHostFileSystemEventService} from 'vs/workbench/api/node/extHostFileSystemEventService';
|
||||
import {ExtHostModelService, setWordDefinitionFor} from 'vs/workbench/api/node/extHostDocuments';
|
||||
import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments';
|
||||
import {ExtHostConfiguration} from 'vs/workbench/api/node/extHostConfiguration';
|
||||
import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics';
|
||||
import {ExtHostWorkspace} from 'vs/workbench/api/node/extHostWorkspace';
|
||||
@@ -27,22 +26,18 @@ import * as ExtHostTypeConverters from 'vs/workbench/api/node/extHostTypeConvert
|
||||
import {registerApiCommands} from 'vs/workbench/api/node/extHostApiCommands';
|
||||
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
|
||||
import Modes = require('vs/editor/common/modes');
|
||||
import {IModeService} from 'vs/editor/common/services/modeService';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import {IDisposable} from 'vs/base/common/lifecycle';
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import {IExtensionDescription} from 'vs/platform/extensions/common/extensions';
|
||||
import {ExtHostExtensionService} from 'vs/workbench/api/node/nativeExtensionService';
|
||||
import {ExtHostExtensionService} from 'vs/workbench/api/node/extHostExtensionService';
|
||||
import {ExtensionsRegistry} from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
|
||||
import {CancellationTokenSource} from 'vs/base/common/cancellation';
|
||||
import vscode = require('vscode');
|
||||
import {TextEditorRevealType} from 'vs/workbench/api/node/mainThreadEditors';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import {ITelemetryService, ITelemetryInfo} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {LanguageConfigurationRegistry} from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import {MainContext, ExtHostContext, InstanceCollection} from './extHostProtocol';
|
||||
|
||||
/**
|
||||
@@ -51,13 +46,6 @@ import {MainContext, ExtHostContext, InstanceCollection} from './extHostProtocol
|
||||
*/
|
||||
export class ExtHostAPIImplementation {
|
||||
|
||||
private static _LAST_REGISTER_TOKEN = 0;
|
||||
private static generateDisposeToken(): string {
|
||||
return String(++ExtHostAPIImplementation._LAST_REGISTER_TOKEN);
|
||||
}
|
||||
|
||||
private _proxy: MainProcessVSCodeAPIHelper;
|
||||
|
||||
version: typeof vscode.version;
|
||||
env: typeof vscode.env;
|
||||
Uri: typeof vscode.Uri;
|
||||
@@ -106,7 +94,7 @@ export class ExtHostAPIImplementation {
|
||||
// Addressable instances
|
||||
const col = new InstanceCollection();
|
||||
|
||||
const extHostDocuments = col.define(ExtHostContext.ExtHostModelService).set(new ExtHostModelService(threadService));
|
||||
const extHostDocuments = col.define(ExtHostContext.ExtHostDocuments).set(new ExtHostDocuments(threadService));
|
||||
const extHostEditors = col.define(ExtHostContext.ExtHostEditors).set(new ExtHostEditors(threadService, extHostDocuments));
|
||||
const extHostCommands = col.define(ExtHostContext.ExtHostCommands).set(new ExtHostCommands(threadService, extHostEditors));
|
||||
const extHostConfiguration = col.define(ExtHostContext.ExtHostConfiguration).set(new ExtHostConfiguration());
|
||||
@@ -118,11 +106,10 @@ export class ExtHostAPIImplementation {
|
||||
|
||||
col.finish(false, threadService);
|
||||
|
||||
|
||||
// Others
|
||||
this._proxy = threadService.get(MainContext.MainProcessVSCodeAPIHelper);
|
||||
const mainThreadErrors = threadService.get(MainContext.MainThreadErrors);
|
||||
errors.setUnexpectedErrorHandler((err) => {
|
||||
this._proxy.onUnexpectedExtHostError(errors.transformErrorForSerialization(err));
|
||||
mainThreadErrors.onUnexpectedExtHostError(errors.transformErrorForSerialization(err));
|
||||
});
|
||||
|
||||
const extHostMessageService = new ExtHostMessageService(threadService);
|
||||
@@ -167,7 +154,7 @@ export class ExtHostAPIImplementation {
|
||||
this.StatusBarAlignment = extHostTypes.StatusBarAlignment;
|
||||
this.IndentAction = Modes.IndentAction;
|
||||
this.OverviewRulerLane = EditorCommon.OverviewRulerLane;
|
||||
this.TextEditorRevealType = TextEditorRevealType;
|
||||
this.TextEditorRevealType = extHostTypes.TextEditorRevealType;
|
||||
this.EndOfLine = extHostTypes.EndOfLine;
|
||||
this.TextEditorCursorStyle = EditorCommon.TextEditorCursorStyle;
|
||||
|
||||
@@ -383,7 +370,7 @@ export class ExtHostAPIImplementation {
|
||||
return languageFeatures.registerCompletionItemProvider(selector, provider, triggerCharacters);
|
||||
},
|
||||
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration):vscode.Disposable => {
|
||||
return this._setLanguageConfiguration(language, configuration);
|
||||
return languageFeatures.setLanguageConfiguration(language, configuration);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -399,41 +386,6 @@ export class ExtHostAPIImplementation {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private _disposableFromToken(disposeToken:string): IDisposable {
|
||||
return new extHostTypes.Disposable(() => this._proxy.disposeByToken(disposeToken));
|
||||
}
|
||||
|
||||
private _setLanguageConfiguration(modeId: string, configuration: vscode.LanguageConfiguration): vscode.Disposable {
|
||||
|
||||
let {wordPattern} = configuration;
|
||||
|
||||
// check for a valid word pattern
|
||||
if (wordPattern && regExpLeadsToEndlessLoop(wordPattern)) {
|
||||
throw new Error(`Invalid language configuration: wordPattern '${wordPattern}' is not allowed to match the empty string.`);
|
||||
}
|
||||
|
||||
// word definition
|
||||
if (wordPattern) {
|
||||
setWordDefinitionFor(modeId, wordPattern);
|
||||
} else {
|
||||
setWordDefinitionFor(modeId, null);
|
||||
}
|
||||
|
||||
// backward compatibility, migrate deprecated setting
|
||||
if (configuration.__characterPairSupport && !configuration.autoClosingPairs) {
|
||||
configuration.autoClosingPairs = configuration.__characterPairSupport.autoClosingPairs;
|
||||
delete configuration.__characterPairSupport;
|
||||
}
|
||||
|
||||
return this.Modes_RichEditSupport_register(modeId, configuration);
|
||||
}
|
||||
|
||||
private Modes_RichEditSupport_register(modeId: string, configuration:vscode.LanguageConfiguration): IDisposable {
|
||||
let disposeToken = ExtHostAPIImplementation.generateDisposeToken();
|
||||
this._proxy.Modes_RichEditSupport_register(disposeToken, modeId, configuration);
|
||||
return this._disposableFromToken(disposeToken);
|
||||
}
|
||||
}
|
||||
|
||||
class Extension<T> implements vscode.Extension<T> {
|
||||
@@ -475,32 +427,3 @@ export function defineAPI(impl: typeof vscode) {
|
||||
};
|
||||
define('vscode', [], impl);
|
||||
}
|
||||
|
||||
export class MainProcessVSCodeAPIHelper {
|
||||
protected _modeService: IModeService;
|
||||
private _token2Dispose: {
|
||||
[token:string]: IDisposable;
|
||||
};
|
||||
|
||||
constructor(
|
||||
@IModeService modeService: IModeService
|
||||
) {
|
||||
this._modeService = modeService;
|
||||
this._token2Dispose = {};
|
||||
}
|
||||
|
||||
public onUnexpectedExtHostError(err: any): void {
|
||||
errors.onUnexpectedError(err);
|
||||
}
|
||||
|
||||
public disposeByToken(disposeToken:string): void {
|
||||
if (this._token2Dispose[disposeToken]) {
|
||||
this._token2Dispose[disposeToken].dispose();
|
||||
delete this._token2Dispose[disposeToken];
|
||||
}
|
||||
}
|
||||
|
||||
public Modes_RichEditSupport_register(disposeToken:string, modeId: string, configuration:vscode.LanguageConfiguration): void {
|
||||
this._token2Dispose[disposeToken] = LanguageConfigurationRegistry.register(modeId, configuration);
|
||||
}
|
||||
}
|
||||
@@ -10,29 +10,33 @@ import {Registry} from 'vs/platform/platform';
|
||||
import {IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions} from 'vs/workbench/common/contributions';
|
||||
import {IInstantiationService, IConstructorSignature0} from 'vs/platform/instantiation/common/instantiation';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {MainThreadDocuments} from 'vs/workbench/api/node/extHostDocuments';
|
||||
import {MainContext, InstanceCollection} from './extHostProtocol';
|
||||
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
// --- addressable
|
||||
import {MainThreadCommands} from './mainThreadCommands';
|
||||
import {MainThreadConfiguration} from './mainThreadConfiguration';
|
||||
import {MainThreadDiagnostics} from './mainThreadDiagnostics';
|
||||
import {MainThreadDocuments} from './mainThreadDocuments';
|
||||
import {MainThreadEditors} from './mainThreadEditors';
|
||||
import {MainThreadErrors} from './mainThreadErrors';
|
||||
import {MainThreadLanguageFeatures} from './mainThreadLanguageFeatures';
|
||||
import {MainThreadLanguages} from './mainThreadLanguages';
|
||||
import {MainThreadMessageService} from './mainThreadMessageService';
|
||||
import {MainThreadOutputService} from './mainThreadOutputService';
|
||||
import {MainThreadQuickOpen} from './mainThreadQuickOpen';
|
||||
import {MainThreadStatusBar} from './mainThreadStatusBar';
|
||||
import {MainThreadStorage} from './mainThreadStorage';
|
||||
import {MainThreadTelemetry} from './mainThreadTelemetry';
|
||||
import {MainThreadWorkspace} from './mainThreadWorkspace';
|
||||
import {MainProcessExtensionService} from './mainThreadExtensionService';
|
||||
import {MainThreadFileSystemEventService} from './mainThreadFileSystemEventService';
|
||||
|
||||
// --- other interested parties
|
||||
import {MainProcessTextMateSyntax} from 'vs/editor/node/textMate/TMSyntax';
|
||||
import {MainProcessTextMateSnippet} from 'vs/editor/node/textMate/TMSnippets';
|
||||
import {JSONValidationExtensionPoint} from 'vs/platform/jsonschemas/common/jsonValidationExtensionPoint';
|
||||
import {LanguageConfigurationFileHandler} from 'vs/editor/node/languageConfiguration';
|
||||
import {MainThreadFileSystemEventService} from 'vs/workbench/api/node/extHostFileSystemEventService';
|
||||
import {MainThreadQuickOpen} from 'vs/workbench/api/node/extHostQuickOpen';
|
||||
import {MainThreadStatusBar} from 'vs/workbench/api/node/extHostStatusBar';
|
||||
import {MainThreadCommands} from 'vs/workbench/api/node/extHostCommands';
|
||||
import {MainThreadTelemetry} from 'vs/workbench/api/node/extHostTelemetry';
|
||||
import {MainThreadDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics';
|
||||
import {MainThreadOutputService} from 'vs/workbench/api/node/extHostOutputService';
|
||||
import {MainThreadMessageService} from 'vs/workbench/api/node/extHostMessageService';
|
||||
import {MainThreadLanguages} from 'vs/workbench/api/node/extHostLanguages';
|
||||
import {MainThreadEditors} from 'vs/workbench/api/node/extHostEditors';
|
||||
import {MainThreadWorkspace} from 'vs/workbench/api/node/extHostWorkspace';
|
||||
import {MainThreadConfiguration} from 'vs/workbench/api/node/extHostConfiguration';
|
||||
import {MainThreadLanguageFeatures} from 'vs/workbench/api/node/extHostLanguageFeatures';
|
||||
import {MainThreadStorage} from 'vs/workbench/api/node/extHostStorage';
|
||||
import {MainProcessVSCodeAPIHelper} from 'vs/workbench/api/node/extHost.api.impl';
|
||||
import {MainContext, InstanceCollection} from './extHostProtocol';
|
||||
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
|
||||
import {MainProcessExtensionService} from './nativeExtensionService';
|
||||
|
||||
export class ExtHostContribution implements IWorkbenchContribution {
|
||||
|
||||
@@ -55,12 +59,12 @@ export class ExtHostContribution implements IWorkbenchContribution {
|
||||
|
||||
// Addressable instances
|
||||
const col = new InstanceCollection();
|
||||
col.define(MainContext.MainProcessVSCodeAPIHelper).set(create(MainProcessVSCodeAPIHelper));
|
||||
col.define(MainContext.MainThreadCommands).set(create(MainThreadCommands));
|
||||
col.define(MainContext.MainThreadConfiguration).set(create(MainThreadConfiguration));
|
||||
col.define(MainContext.MainThreadDiagnostics).set(create(MainThreadDiagnostics));
|
||||
col.define(MainContext.MainThreadDocuments).set(create(MainThreadDocuments));
|
||||
col.define(MainContext.MainThreadEditors).set(create(MainThreadEditors));
|
||||
col.define(MainContext.MainThreadErrors).set(create(MainThreadErrors));
|
||||
col.define(MainContext.MainThreadLanguageFeatures).set(create(MainThreadLanguageFeatures));
|
||||
col.define(MainContext.MainThreadLanguages).set(create(MainThreadLanguages));
|
||||
col.define(MainContext.MainThreadMessageService).set(create(MainThreadMessageService));
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {validateConstraint} from 'vs/base/common/types';
|
||||
import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import {IKeybindingService, ICommandHandlerDescription} from 'vs/platform/keybinding/common/keybindingService';
|
||||
import {ICommandHandlerDescription} from 'vs/platform/keybinding/common/keybindingService';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {ExtHostEditors} from 'vs/workbench/api/node/extHostEditors';
|
||||
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
|
||||
import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import {cloneAndChange} from 'vs/base/common/objects';
|
||||
import {MainContext, ExtHostContext} from './extHostProtocol';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadCommands} from './mainThreadCommands';
|
||||
|
||||
interface CommandHandler {
|
||||
callback: Function;
|
||||
@@ -126,95 +126,3 @@ export class ExtHostCommands {
|
||||
return TPromise.as(result);
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadCommands {
|
||||
|
||||
private _threadService: IThreadService;
|
||||
private _keybindingService: IKeybindingService;
|
||||
private _proxy: ExtHostCommands;
|
||||
|
||||
constructor(
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
this._threadService = threadService;
|
||||
this._keybindingService = keybindingService;
|
||||
this._proxy = this._threadService.get(ExtHostContext.ExtHostCommands);
|
||||
}
|
||||
|
||||
$registerCommand(id: string): TPromise<any> {
|
||||
|
||||
KeybindingsRegistry.registerCommandDesc({
|
||||
id,
|
||||
handler: (serviceAccessor, ...args: any[]) => {
|
||||
return this._proxy.$executeContributedCommand(id, ...args);
|
||||
},
|
||||
weight: undefined,
|
||||
when: undefined,
|
||||
win: undefined,
|
||||
mac: undefined,
|
||||
linux: undefined,
|
||||
primary: undefined,
|
||||
secondary: undefined
|
||||
});
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$executeCommand<T>(id: string, args: any[]): Thenable<T> {
|
||||
return this._keybindingService.executeCommand(id, ...args);
|
||||
}
|
||||
|
||||
$getCommands(): Thenable<string[]> {
|
||||
return TPromise.as(Object.keys(KeybindingsRegistry.getCommands()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- command doc
|
||||
|
||||
KeybindingsRegistry.registerCommandDesc({
|
||||
id: '_generateCommandsDocumentation',
|
||||
handler: function(accessor) {
|
||||
return accessor.get(IThreadService).get(ExtHostContext.ExtHostCommands).$getContributedCommandHandlerDescriptions().then(result => {
|
||||
|
||||
// add local commands
|
||||
const commands = KeybindingsRegistry.getCommands();
|
||||
for (let id in commands) {
|
||||
let {description} = commands[id];
|
||||
if (description) {
|
||||
result[id] = description;
|
||||
}
|
||||
}
|
||||
|
||||
// print all as markdown
|
||||
const all: string[] = [];
|
||||
for (let id in result) {
|
||||
all.push('`' + id + '` - ' + _generateMarkdown(result[id]));
|
||||
}
|
||||
console.log(all.join('\n'));
|
||||
});
|
||||
},
|
||||
when: undefined,
|
||||
weight: KeybindingsRegistry.WEIGHT.builtinExtension(0),
|
||||
primary: undefined
|
||||
});
|
||||
|
||||
function _generateMarkdown(description: string | ICommandHandlerDescription): string {
|
||||
if (typeof description === 'string') {
|
||||
return description;
|
||||
} else {
|
||||
let parts = [description.description];
|
||||
parts.push('\n\n');
|
||||
if (description.args) {
|
||||
for (let arg of description.args) {
|
||||
parts.push(`* _${arg.name}_ ${arg.description || ''}\n`);
|
||||
}
|
||||
}
|
||||
if (description.returns) {
|
||||
parts.push(`* _(returns)_ ${description.returns}`);
|
||||
}
|
||||
parts.push('\n\n');
|
||||
return parts.join('');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,8 @@
|
||||
|
||||
import {clone} from 'vs/base/common/objects';
|
||||
import {illegalState} from 'vs/base/common/errors';
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {WorkspaceConfiguration} from 'vscode';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
|
||||
export class ExtHostConfiguration {
|
||||
|
||||
@@ -76,24 +72,3 @@ export class ExtHostConfiguration {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadConfiguration {
|
||||
|
||||
private _configurationService: IConfigurationService;
|
||||
private _toDispose: IDisposable;
|
||||
private _proxy: ExtHostConfiguration;
|
||||
|
||||
constructor(@IConfigurationService configurationService: IConfigurationService,
|
||||
@IThreadService threadService: IThreadService) {
|
||||
|
||||
this._configurationService = configurationService;
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostConfiguration);
|
||||
|
||||
this._toDispose = this._configurationService.onDidUpdateConfiguration(event => this._proxy.$acceptConfigurationChanged(event.config));
|
||||
this._proxy.$acceptConfigurationChanged(this._configurationService.getConfiguration());
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,12 @@
|
||||
'use strict';
|
||||
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IMarkerService, IMarkerData} from 'vs/platform/markers/common/markers';
|
||||
import {IMarkerData} from 'vs/platform/markers/common/markers';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import * as vscode from 'vscode';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadDiagnostics} from './mainThreadDiagnostics';
|
||||
|
||||
export class DiagnosticCollection implements vscode.DiagnosticCollection {
|
||||
|
||||
@@ -216,24 +216,3 @@ export class ExtHostDiagnostics {
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadDiagnostics {
|
||||
|
||||
private _markerService: IMarkerService;
|
||||
|
||||
constructor(@IMarkerService markerService: IMarkerService) {
|
||||
this._markerService = markerService;
|
||||
}
|
||||
|
||||
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any> {
|
||||
for (let entry of entries) {
|
||||
let [uri, markers] = entry;
|
||||
this._markerService.changeOne(owner, uri, markers);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$clear(owner: string): TPromise<any> {
|
||||
this._markerService.changeAll(owner, undefined);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,34 +4,26 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {toErrorMessage, onUnexpectedError} from 'vs/base/common/errors';
|
||||
import {EmitterEvent} from 'vs/base/common/eventEmitter';
|
||||
import {IModelService} from 'vs/editor/common/services/modelService';
|
||||
import * as EditorCommon from 'vs/editor/common/editorCommon';
|
||||
import {onUnexpectedError} from 'vs/base/common/errors';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import {MirrorModel2} from 'vs/editor/common/model/mirrorModel2';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {IDisposable} from 'vs/base/common/lifecycle';
|
||||
import {Range, Position, Disposable} from 'vs/workbench/api/node/extHostTypes';
|
||||
import {IEventService} from 'vs/platform/event/common/event';
|
||||
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
|
||||
import {EventType as FileEventType, TextFileChangeEvent, ITextFileService} from 'vs/workbench/parts/files/common/files';
|
||||
import * as TypeConverters from './extHostTypeConverters';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import * as vscode from 'vscode';
|
||||
import {IFileService} from 'vs/platform/files/common/files';
|
||||
import {IModeService} from 'vs/editor/common/services/modeService';
|
||||
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import {ResourceEditorInput} from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import {asWinJsPromise} from 'vs/base/common/async';
|
||||
import {getWordAtText, ensureValidWordDefinition} from 'vs/editor/common/model/wordHelper';
|
||||
import {MainContext, ExtHostContext} from './extHostProtocol';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadDocuments} from './mainThreadDocuments';
|
||||
|
||||
export interface IModelAddedData {
|
||||
url: URI;
|
||||
versionId: number;
|
||||
value: EditorCommon.IRawText;
|
||||
value: editorCommon.IRawText;
|
||||
modeId: string;
|
||||
isDirty: boolean;
|
||||
}
|
||||
@@ -40,15 +32,15 @@ const _modeId2WordDefinition: {
|
||||
[modeId: string]: RegExp;
|
||||
} = Object.create(null);
|
||||
|
||||
export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
|
||||
function setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
|
||||
_modeId2WordDefinition[modeId] = wordDefinition;
|
||||
}
|
||||
|
||||
export function getWordDefinitionFor(modeId: string): RegExp {
|
||||
function getWordDefinitionFor(modeId: string): RegExp {
|
||||
return _modeId2WordDefinition[modeId];
|
||||
}
|
||||
|
||||
export class ExtHostModelService {
|
||||
export class ExtHostDocuments {
|
||||
|
||||
private static _handlePool: number = 0;
|
||||
|
||||
@@ -135,7 +127,7 @@ export class ExtHostModelService {
|
||||
throw new Error(`scheme '${scheme}' already registered`);
|
||||
}
|
||||
|
||||
const handle = ExtHostModelService._handlePool++;
|
||||
const handle = ExtHostDocuments._handlePool++;
|
||||
|
||||
this._documentContentProviders[handle] = provider;
|
||||
this._proxy.$registerTextContentProvider(handle, scheme);
|
||||
@@ -215,7 +207,7 @@ export class ExtHostModelService {
|
||||
data.dispose();
|
||||
}
|
||||
|
||||
public _acceptModelChanged(strURL: string, events: EditorCommon.IModelContentChangedEvent2[]): void {
|
||||
public _acceptModelChanged(strURL: string, events: editorCommon.IModelContentChangedEvent2[]): void {
|
||||
let data = this._documentData[strURL];
|
||||
data.onEvents(events);
|
||||
this._onDidChangeDocumentEventEmitter.fire({
|
||||
@@ -229,6 +221,10 @@ export class ExtHostModelService {
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
|
||||
setWordDefinitionFor(modeId, wordDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostDocumentData extends MirrorModel2 {
|
||||
@@ -436,242 +432,3 @@ export class ExtHostDocumentData extends MirrorModel2 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadDocuments {
|
||||
private _modelService: IModelService;
|
||||
private _modeService: IModeService;
|
||||
private _textFileService: ITextFileService;
|
||||
private _editorService: IWorkbenchEditorService;
|
||||
private _fileService: IFileService;
|
||||
private _untitledEditorService: IUntitledEditorService;
|
||||
private _toDispose: IDisposable[];
|
||||
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
|
||||
private _proxy: ExtHostModelService;
|
||||
private _modelIsSynced: { [modelId: string]: boolean; };
|
||||
private _resourceContentProvider: { [handle: number]: IDisposable };
|
||||
private _virtualDocumentSet: { [resource: string]: boolean };
|
||||
|
||||
constructor(
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IModeService modeService: IModeService,
|
||||
@IEventService eventService: IEventService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IUntitledEditorService untitledEditorService: IUntitledEditorService
|
||||
) {
|
||||
this._modelService = modelService;
|
||||
this._modeService = modeService;
|
||||
this._textFileService = textFileService;
|
||||
this._editorService = editorService;
|
||||
this._fileService = fileService;
|
||||
this._untitledEditorService = untitledEditorService;
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostModelService);
|
||||
this._modelIsSynced = {};
|
||||
|
||||
this._toDispose = [];
|
||||
modelService.onModelAdded(this._onModelAdded, this, this._toDispose);
|
||||
modelService.onModelRemoved(this._onModelRemoved, this, this._toDispose);
|
||||
modelService.onModelModeChanged(this._onModelModeChanged, this, this._toDispose);
|
||||
|
||||
this._toDispose.push(eventService.addListener2(FileEventType.FILE_SAVED, (e: TextFileChangeEvent) => {
|
||||
if (this._shouldHandleFileEvent(e)) {
|
||||
this._proxy._acceptModelSaved(e.resource.toString());
|
||||
}
|
||||
}));
|
||||
this._toDispose.push(eventService.addListener2(FileEventType.FILE_REVERTED, (e: TextFileChangeEvent) => {
|
||||
if (this._shouldHandleFileEvent(e)) {
|
||||
this._proxy._acceptModelReverted(e.resource.toString());
|
||||
}
|
||||
}));
|
||||
this._toDispose.push(eventService.addListener2(FileEventType.FILE_DIRTY, (e: TextFileChangeEvent) => {
|
||||
if (this._shouldHandleFileEvent(e)) {
|
||||
this._proxy._acceptModelDirty(e.resource.toString());
|
||||
}
|
||||
}));
|
||||
|
||||
const handle = setInterval(() => this._runDocumentCleanup(), 1000 * 60 * 3);
|
||||
this._toDispose.push({ dispose() { clearInterval(handle); } });
|
||||
|
||||
this._modelToDisposeMap = Object.create(null);
|
||||
this._resourceContentProvider = Object.create(null);
|
||||
this._virtualDocumentSet = Object.create(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
Object.keys(this._modelToDisposeMap).forEach((modelUrl) => {
|
||||
this._modelToDisposeMap[modelUrl].dispose();
|
||||
});
|
||||
this._modelToDisposeMap = Object.create(null);
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
|
||||
private _shouldHandleFileEvent(e: TextFileChangeEvent): boolean {
|
||||
const model = this._modelService.getModel(e.resource);
|
||||
return model && !model.isTooLargeForHavingARichMode();
|
||||
}
|
||||
|
||||
private _onModelAdded(model: EditorCommon.IModel): void {
|
||||
// Same filter as in mainThreadEditors
|
||||
if (model.isTooLargeForHavingARichMode()) {
|
||||
// don't synchronize too large models
|
||||
return null;
|
||||
}
|
||||
let modelUrl = model.uri;
|
||||
this._modelIsSynced[modelUrl.toString()] = true;
|
||||
this._modelToDisposeMap[modelUrl.toString()] = model.addBulkListener((events) => this._onModelEvents(modelUrl, events));
|
||||
this._proxy._acceptModelAdd({
|
||||
url: model.uri,
|
||||
versionId: model.getVersionId(),
|
||||
value: model.toRawText(),
|
||||
modeId: model.getMode().getId(),
|
||||
isDirty: this._textFileService.isDirty(modelUrl)
|
||||
});
|
||||
}
|
||||
|
||||
private _onModelModeChanged(event: { model: EditorCommon.IModel; oldModeId: string; }): void {
|
||||
let {model, oldModeId} = event;
|
||||
let modelUrl = model.uri;
|
||||
if (!this._modelIsSynced[modelUrl.toString()]) {
|
||||
return;
|
||||
}
|
||||
this._proxy._acceptModelModeChanged(model.uri.toString(), oldModeId, model.getMode().getId());
|
||||
}
|
||||
|
||||
private _onModelRemoved(model: EditorCommon.IModel): void {
|
||||
let modelUrl = model.uri;
|
||||
if (!this._modelIsSynced[modelUrl.toString()]) {
|
||||
return;
|
||||
}
|
||||
delete this._modelIsSynced[modelUrl.toString()];
|
||||
this._modelToDisposeMap[modelUrl.toString()].dispose();
|
||||
delete this._modelToDisposeMap[modelUrl.toString()];
|
||||
this._proxy._acceptModelRemoved(modelUrl.toString());
|
||||
}
|
||||
|
||||
private _onModelEvents(modelUrl: URI, events: EmitterEvent[]): void {
|
||||
let changedEvents: EditorCommon.IModelContentChangedEvent2[] = [];
|
||||
for (let i = 0, len = events.length; i < len; i++) {
|
||||
let e = events[i];
|
||||
switch (e.getType()) {
|
||||
case EditorCommon.EventType.ModelContentChanged2:
|
||||
changedEvents.push(<EditorCommon.IModelContentChangedEvent2>e.getData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changedEvents.length > 0) {
|
||||
this._proxy._acceptModelChanged(modelUrl.toString(), changedEvents);
|
||||
}
|
||||
}
|
||||
|
||||
// --- from extension host process
|
||||
|
||||
_trySaveDocument(uri: URI): TPromise<boolean> {
|
||||
return this._textFileService.save(uri);
|
||||
}
|
||||
|
||||
_tryOpenDocument(uri: URI): TPromise<any> {
|
||||
|
||||
if (!uri.scheme || !(uri.fsPath || uri.authority)) {
|
||||
return TPromise.wrapError(`Invalid uri. Scheme and authority or path must be set.`);
|
||||
}
|
||||
|
||||
let promise: TPromise<boolean>;
|
||||
switch (uri.scheme) {
|
||||
case 'untitled':
|
||||
promise = this._handleUnititledScheme(uri);
|
||||
break;
|
||||
case 'file':
|
||||
default:
|
||||
promise = this._handleAsResourceInput(uri);
|
||||
break;
|
||||
}
|
||||
|
||||
return promise.then(success => {
|
||||
if (!success) {
|
||||
return TPromise.wrapError('cannot open ' + uri.toString());
|
||||
}
|
||||
}, err => {
|
||||
return TPromise.wrapError('cannot open ' + uri.toString() + '. Detail: ' + toErrorMessage(err));
|
||||
});
|
||||
}
|
||||
|
||||
private _handleAsResourceInput(uri: URI): TPromise<boolean> {
|
||||
return this._editorService.resolveEditorModel({ resource: uri }).then(model => {
|
||||
return !!model;
|
||||
});
|
||||
}
|
||||
|
||||
private _handleUnititledScheme(uri: URI): TPromise<boolean> {
|
||||
let asFileUri = URI.file(uri.fsPath);
|
||||
return this._fileService.resolveFile(asFileUri).then(stats => {
|
||||
// don't create a new file ontop of an existing file
|
||||
return TPromise.wrapError<boolean>('file already exists on disk');
|
||||
}, err => {
|
||||
let input = this._untitledEditorService.createOrGet(asFileUri); // using file-uri makes it show in 'Working Files' section
|
||||
return input.resolve(true).then(model => {
|
||||
if (input.getResource().toString() !== uri.toString()) {
|
||||
throw new Error(`expected URI ${uri.toString() } BUT GOT ${input.getResource().toString() }`);
|
||||
}
|
||||
if (!this._modelIsSynced[uri.toString()]) {
|
||||
throw new Error(`expected URI ${uri.toString()} to have come to LIFE`);
|
||||
}
|
||||
return this._proxy._acceptModelDirty(uri.toString()); // mark as dirty
|
||||
}).then(() => {
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- virtual document logic
|
||||
|
||||
$registerTextContentProvider(handle:number, scheme: string): void {
|
||||
this._resourceContentProvider[handle] = ResourceEditorInput.registerResourceContentProvider(scheme, {
|
||||
provideTextContent: (uri: URI): TPromise<EditorCommon.IModel> => {
|
||||
return this._proxy.$provideTextDocumentContent(handle, uri).then(value => {
|
||||
if (typeof value === 'string') {
|
||||
this._virtualDocumentSet[uri.toString()] = true;
|
||||
const firstLineText = value.substr(0, 1 + value.search(/\r?\n/));
|
||||
const mode = this._modeService.getOrCreateModeByFilenameOrFirstLine(uri.fsPath, firstLineText);
|
||||
return this._modelService.createModel(value, mode, uri);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$unregisterTextContentProvider(handle: number): void {
|
||||
const registration = this._resourceContentProvider[handle];
|
||||
if (registration) {
|
||||
registration.dispose();
|
||||
delete this._resourceContentProvider[handle];
|
||||
}
|
||||
}
|
||||
|
||||
$onVirtualDocumentChange(uri: URI, value: string): void {
|
||||
const model = this._modelService.getModel(uri);
|
||||
if (model) {
|
||||
model.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
private _runDocumentCleanup(): void {
|
||||
|
||||
const toBeDisposed: URI[] = [];
|
||||
|
||||
TPromise.join(Object.keys(this._virtualDocumentSet).map(key => {
|
||||
let resource = URI.parse(key);
|
||||
return this._editorService.createInput({ resource }).then(input => {
|
||||
if (!this._editorService.isVisible(input, true)) {
|
||||
toBeDisposed.push(resource);
|
||||
}
|
||||
});
|
||||
})).then(() => {
|
||||
for (let resource of toBeDisposed) {
|
||||
this._modelService.destroyModel(resource);
|
||||
delete this._virtualDocumentSet[resource.toString()];
|
||||
}
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,25 +8,17 @@ import URI from 'vs/base/common/uri';
|
||||
import {readonly, illegalArgument} from 'vs/base/common/errors';
|
||||
import {IdGenerator} from 'vs/base/common/idGenerator';
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {ExtHostModelService, ExtHostDocumentData} from 'vs/workbench/api/node/extHostDocuments';
|
||||
import {Selection, Range, Position, EditorOptions, EndOfLine} from './extHostTypes';
|
||||
import {ISingleEditOperation, ISelection, IRange, IEditor, EditorType, ICommonCodeEditor, ICommonDiffEditor, IDecorationRenderOptions, IDecorationOptions} 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';
|
||||
import {ExtHostDocuments, ExtHostDocumentData} from 'vs/workbench/api/node/extHostDocuments';
|
||||
import {Selection, Range, Position, EditorOptions, EndOfLine, TextEditorRevealType} from './extHostTypes';
|
||||
import {ISingleEditOperation, ISelection} from 'vs/editor/common/editorCommon';
|
||||
import {Position as EditorPosition} from 'vs/platform/editor/common/editor';
|
||||
import {IModelService} from 'vs/editor/common/services/modelService';
|
||||
import {MainThreadEditorsTracker, TextEditorRevealType, MainThreadTextEditor, ITextEditorConfigurationUpdate, IResolvedTextEditorConfiguration} from 'vs/workbench/api/node/mainThreadEditors';
|
||||
import {IResolvedTextEditorConfiguration} from 'vs/workbench/api/node/mainThreadEditorsTracker';
|
||||
import * as TypeConverters from './extHostTypeConverters';
|
||||
import {TextDocument, TextEditorSelectionChangeEvent, TextEditorOptionsChangeEvent, TextEditorOptions, TextEditorViewColumnChangeEvent, ViewColumn} from 'vscode';
|
||||
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {IEventService} from 'vs/platform/event/common/event';
|
||||
import {equals as arrayEquals} from 'vs/base/common/arrays';
|
||||
import {equals as objectEquals} from 'vs/base/common/objects';
|
||||
import {MainContext, ExtHostContext} from './extHostProtocol';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadEditors} from './mainThreadEditors';
|
||||
|
||||
export interface ITextEditorAddData {
|
||||
id: string;
|
||||
@@ -54,13 +46,13 @@ export class ExtHostEditors {
|
||||
private _editors: { [id: string]: ExtHostTextEditor };
|
||||
private _proxy: MainThreadEditors;
|
||||
private _onDidChangeActiveTextEditor: Emitter<vscode.TextEditor>;
|
||||
private _modelService: ExtHostModelService;
|
||||
private _extHostDocuments: ExtHostDocuments;
|
||||
private _activeEditorId: string;
|
||||
private _visibleEditorIds: string[];
|
||||
|
||||
constructor(
|
||||
threadService: IThreadService,
|
||||
modelService: ExtHostModelService
|
||||
extHostDocuments: ExtHostDocuments
|
||||
) {
|
||||
this._onDidChangeTextEditorSelection = new Emitter<TextEditorSelectionChangeEvent>();
|
||||
this.onDidChangeTextEditorSelection = this._onDidChangeTextEditorSelection.event;
|
||||
@@ -71,7 +63,7 @@ export class ExtHostEditors {
|
||||
this._onDidChangeTextEditorViewColumn = new Emitter<TextEditorViewColumnChangeEvent>();
|
||||
this.onDidChangeTextEditorViewColumn = this._onDidChangeTextEditorViewColumn.event;
|
||||
|
||||
this._modelService = modelService;
|
||||
this._extHostDocuments = extHostDocuments;
|
||||
this._proxy = threadService.get(MainContext.MainThreadEditors);
|
||||
this._onDidChangeActiveTextEditor = new Emitter<vscode.TextEditor>();
|
||||
this._editors = Object.create(null);
|
||||
@@ -109,7 +101,7 @@ export class ExtHostEditors {
|
||||
// --- called from main thread
|
||||
|
||||
_acceptTextEditorAdd(data: ITextEditorAddData): void {
|
||||
let document = this._modelService.getDocumentData(data.document);
|
||||
let document = this._extHostDocuments.getDocumentData(data.document);
|
||||
let newEditor = new ExtHostTextEditor(this._proxy, data.id, document, data.selections.map(TypeConverters.toSelection), data.options, TypeConverters.toViewColumn(data.editorPosition));
|
||||
this._editors[data.id] = newEditor;
|
||||
}
|
||||
@@ -404,7 +396,7 @@ class ExtHostTextEditor implements vscode.TextEditor {
|
||||
() => this._proxy._tryRevealRange(
|
||||
this._id,
|
||||
TypeConverters.fromRange(range),
|
||||
(<TextEditorRevealType><any>revealType) || TextEditorRevealType.Default
|
||||
revealType || TextEditorRevealType.Default
|
||||
),
|
||||
true
|
||||
);
|
||||
@@ -453,278 +445,3 @@ class ExtHostTextEditor implements vscode.TextEditor {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadEditors {
|
||||
|
||||
private _proxy: ExtHostEditors;
|
||||
private _workbenchEditorService: IWorkbenchEditorService;
|
||||
private _telemetryService: ITelemetryService;
|
||||
private _editorTracker: MainThreadEditorsTracker;
|
||||
private _toDispose: IDisposable[];
|
||||
private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
|
||||
private _textEditorsMap: { [editorId: string]: MainThreadTextEditor; };
|
||||
private _activeTextEditor: string;
|
||||
private _visibleEditors: string[];
|
||||
private _editorPositionData: ITextEditorPositionData;
|
||||
|
||||
constructor(
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IWorkbenchEditorService workbenchEditorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@ICodeEditorService editorService: ICodeEditorService,
|
||||
@IEventService eventService: IEventService,
|
||||
@IModelService modelService: IModelService
|
||||
) {
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostEditors);
|
||||
this._workbenchEditorService = workbenchEditorService;
|
||||
this._telemetryService = telemetryService;
|
||||
this._toDispose = [];
|
||||
this._textEditorsListenersMap = Object.create(null);
|
||||
this._textEditorsMap = Object.create(null);
|
||||
this._activeTextEditor = null;
|
||||
this._visibleEditors = [];
|
||||
this._editorPositionData = null;
|
||||
|
||||
this._editorTracker = new MainThreadEditorsTracker(editorService, modelService);
|
||||
this._toDispose.push(this._editorTracker);
|
||||
|
||||
this._toDispose.push(this._editorTracker.onTextEditorAdd((textEditor) => this._onTextEditorAdd(textEditor)));
|
||||
this._toDispose.push(this._editorTracker.onTextEditorRemove((textEditor) => this._onTextEditorRemove(textEditor)));
|
||||
|
||||
this._toDispose.push(this._editorTracker.onDidUpdateTextEditors(() => this._updateActiveAndVisibleTextEditors()));
|
||||
this._toDispose.push(this._editorTracker.onChangedFocusedTextEditor((focusedTextEditorId) => this._updateActiveAndVisibleTextEditors()));
|
||||
this._toDispose.push(editorGroupService.onEditorsChanged(() => this._updateActiveAndVisibleTextEditors()));
|
||||
this._toDispose.push(editorGroupService.onEditorsMoved(() => this._updateActiveAndVisibleTextEditors()));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
Object.keys(this._textEditorsListenersMap).forEach((editorId) => {
|
||||
dispose(this._textEditorsListenersMap[editorId]);
|
||||
});
|
||||
this._textEditorsListenersMap = Object.create(null);
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
|
||||
private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
|
||||
let id = textEditor.getId();
|
||||
let toDispose: IDisposable[] = [];
|
||||
toDispose.push(textEditor.onConfigurationChanged((opts) => {
|
||||
this._proxy._acceptOptionsChanged(id, opts);
|
||||
}));
|
||||
toDispose.push(textEditor.onSelectionChanged((selection) => {
|
||||
this._proxy._acceptSelectionsChanged(id, selection);
|
||||
}));
|
||||
this._proxy._acceptTextEditorAdd({
|
||||
id: id,
|
||||
document: textEditor.getModel().uri,
|
||||
options: textEditor.getConfiguration(),
|
||||
selections: textEditor.getSelections(),
|
||||
editorPosition: this._findEditorPosition(textEditor)
|
||||
});
|
||||
|
||||
this._textEditorsListenersMap[id] = toDispose;
|
||||
this._textEditorsMap[id] = textEditor;
|
||||
}
|
||||
|
||||
private _onTextEditorRemove(textEditor: MainThreadTextEditor): void {
|
||||
let id = textEditor.getId();
|
||||
dispose(this._textEditorsListenersMap[id]);
|
||||
delete this._textEditorsListenersMap[id];
|
||||
delete this._textEditorsMap[id];
|
||||
this._proxy._acceptTextEditorRemove(id);
|
||||
}
|
||||
|
||||
private _updateActiveAndVisibleTextEditors(): void {
|
||||
|
||||
// active and visible editors
|
||||
let visibleEditors = this._editorTracker.getVisibleTextEditorIds();
|
||||
let activeEditor = this._findActiveTextEditorId();
|
||||
if (activeEditor !== this._activeTextEditor || !arrayEquals(this._visibleEditors, visibleEditors, (a, b) => a === b)) {
|
||||
this._activeTextEditor = activeEditor;
|
||||
this._visibleEditors = visibleEditors;
|
||||
this._proxy._acceptActiveEditorAndVisibleEditors(this._activeTextEditor, this._visibleEditors);
|
||||
}
|
||||
|
||||
// editor columns
|
||||
let editorPositionData = this._getTextEditorPositionData();
|
||||
if (!objectEquals(this._editorPositionData, editorPositionData)) {
|
||||
this._editorPositionData = editorPositionData;
|
||||
this._proxy._acceptEditorPositionData(this._editorPositionData);
|
||||
}
|
||||
}
|
||||
|
||||
private _findActiveTextEditorId(): string {
|
||||
let focusedTextEditorId = this._editorTracker.getFocusedTextEditorId();
|
||||
if (focusedTextEditorId) {
|
||||
return focusedTextEditorId;
|
||||
}
|
||||
|
||||
let activeEditor = this._workbenchEditorService.getActiveEditor();
|
||||
if (!activeEditor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let editor = <IEditor>activeEditor.getControl();
|
||||
// Substitute for (editor instanceof ICodeEditor)
|
||||
if (!editor || typeof editor.getEditorType !== 'function') {
|
||||
// Not a text editor...
|
||||
return null;
|
||||
}
|
||||
|
||||
if (editor.getEditorType() === EditorType.ICodeEditor) {
|
||||
return this._editorTracker.findTextEditorIdFor(<ICommonCodeEditor>editor);
|
||||
}
|
||||
|
||||
// Must be a diff editor => use the modified side
|
||||
return this._editorTracker.findTextEditorIdFor((<ICommonDiffEditor>editor).getModifiedEditor());
|
||||
}
|
||||
|
||||
private _findEditorPosition(editor: MainThreadTextEditor): EditorPosition {
|
||||
for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) {
|
||||
if (editor.matches(workbenchEditor)) {
|
||||
return workbenchEditor.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getTextEditorPositionData(): ITextEditorPositionData {
|
||||
let result: ITextEditorPositionData = Object.create(null);
|
||||
for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) {
|
||||
let editor = <IEditor>workbenchEditor.getControl();
|
||||
// Substitute for (editor instanceof ICodeEditor)
|
||||
if (!editor || typeof editor.getEditorType !== 'function') {
|
||||
// Not a text editor...
|
||||
continue;
|
||||
}
|
||||
if (editor.getEditorType() === EditorType.ICodeEditor) {
|
||||
let id = this._editorTracker.findTextEditorIdFor(<ICommonCodeEditor>editor);
|
||||
if (id) {
|
||||
result[id] = workbenchEditor.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// --- from extension host process
|
||||
|
||||
_tryShowTextDocument(resource: URI, position: EditorPosition, preserveFocus: boolean): TPromise<string> {
|
||||
|
||||
const input = {
|
||||
resource,
|
||||
options: { preserveFocus }
|
||||
};
|
||||
|
||||
return this._workbenchEditorService.openEditor(input, position).then(editor => {
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new TPromise<void>(c => {
|
||||
// not very nice but the way it is: changes to the editor state aren't
|
||||
// send to the ext host as they happen but stuff is delayed a little. in
|
||||
// order to provide the real editor on #openTextEditor we need to sync on
|
||||
// that update
|
||||
let subscription: IDisposable;
|
||||
let handle: number;
|
||||
function contd() {
|
||||
subscription.dispose();
|
||||
clearTimeout(handle);
|
||||
c(undefined);
|
||||
}
|
||||
subscription = this._editorTracker.onDidUpdateTextEditors(() => {
|
||||
contd();
|
||||
});
|
||||
handle = setTimeout(() => {
|
||||
contd();
|
||||
}, 1000);
|
||||
|
||||
}).then(() => {
|
||||
// find the editor we have just opened and return the
|
||||
// id we have assigned to it.
|
||||
for (let id in this._textEditorsMap) {
|
||||
if (this._textEditorsMap[id].matches(editor)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_tryShowEditor(id: string, position: EditorPosition): TPromise<void> {
|
||||
// check how often this is used
|
||||
this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.show' });
|
||||
|
||||
let mainThreadEditor = this._textEditorsMap[id];
|
||||
if (mainThreadEditor) {
|
||||
let model = mainThreadEditor.getModel();
|
||||
return this._workbenchEditorService.openEditor({
|
||||
resource: model.uri,
|
||||
options: { preserveFocus: false }
|
||||
}, position).then(() => { return; });
|
||||
}
|
||||
}
|
||||
|
||||
_tryHideEditor(id: string): TPromise<void> {
|
||||
// check how often this is used
|
||||
this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.hide' });
|
||||
|
||||
let mainThreadEditor = this._textEditorsMap[id];
|
||||
if (mainThreadEditor) {
|
||||
let editors = this._workbenchEditorService.getVisibleEditors();
|
||||
for (let editor of editors) {
|
||||
if (mainThreadEditor.matches(editor)) {
|
||||
return this._workbenchEditorService.closeEditor(editor.position, editor.input).then(() => { return; });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_trySetSelections(id: string, selections: ISelection[]): TPromise<any> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
this._textEditorsMap[id].setSelections(selections);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
_trySetDecorations(id: string, key: string, ranges: IDecorationOptions[]): TPromise<any> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
this._textEditorsMap[id].setDecorations(key, ranges);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
_tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
this._textEditorsMap[id].revealRange(range, revealType);
|
||||
}
|
||||
|
||||
_trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<any> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
this._textEditorsMap[id].setConfiguration(options);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
_tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], setEndOfLine:EndOfLine): TPromise<boolean> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
return TPromise.as(this._textEditorsMap[id].applyEdits(modelVersionId, edits, setEndOfLine));
|
||||
}
|
||||
|
||||
_registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {
|
||||
this._editorTracker.registerTextEditorDecorationType(key, options);
|
||||
}
|
||||
|
||||
_removeTextEditorDecorationType(key: string): void {
|
||||
this._editorTracker.removeTextEditorDecorationType(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,144 +10,16 @@ import * as paths from 'vs/base/common/paths';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {AbstractExtensionService, ActivatedExtension} from 'vs/platform/extensions/common/abstractExtensionService';
|
||||
import {IMessage, IExtensionDescription, IExtensionsStatus} from 'vs/platform/extensions/common/extensions';
|
||||
import {IMessage, IExtensionDescription} from 'vs/platform/extensions/common/extensions';
|
||||
import {ExtensionsRegistry} from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import {IMessageService} from 'vs/platform/message/common/message';
|
||||
import {ExtHostStorage} from 'vs/workbench/api/node/extHostStorage';
|
||||
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
|
||||
import {MainContext, ExtHostContext} from './extHostProtocol';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainProcessExtensionService} from './mainThreadExtensionService';
|
||||
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Represents a failed extension in the ext host.
|
||||
*/
|
||||
class MainProcessFailedExtension extends ActivatedExtension {
|
||||
constructor() {
|
||||
super(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an extension that was successfully loaded or an
|
||||
* empty extension in the ext host.
|
||||
*/
|
||||
class MainProcessSuccessExtension extends ActivatedExtension {
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
}
|
||||
|
||||
function messageWithSource(msg:IMessage): string {
|
||||
return (msg.source ? '[' + msg.source + ']: ' : '') + msg.message;
|
||||
}
|
||||
|
||||
export class MainProcessExtensionService extends AbstractExtensionService<ActivatedExtension> {
|
||||
|
||||
private _threadService: IThreadService;
|
||||
private _messageService: IMessageService;
|
||||
private _proxy: ExtHostExtensionService;
|
||||
private _isDev: boolean;
|
||||
private _extensionsStatus: { [id: string]: IExtensionsStatus };
|
||||
|
||||
/**
|
||||
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
|
||||
*/
|
||||
constructor(
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IMessageService messageService: IMessageService
|
||||
) {
|
||||
super(false);
|
||||
let config = contextService.getConfiguration();
|
||||
this._isDev = !config.env.isBuilt || !!config.env.extensionDevelopmentPath;
|
||||
|
||||
this._messageService = messageService;
|
||||
this._threadService = threadService;
|
||||
this._proxy = this._threadService.get(ExtHostContext.ExtHostExtensionService);
|
||||
this._extensionsStatus = {};
|
||||
|
||||
ExtensionsRegistry.handleExtensionPoints((msg) => this._handleMessage(msg));
|
||||
}
|
||||
|
||||
private _handleMessage(msg: IMessage) {
|
||||
this._showMessage(msg.type, messageWithSource(msg));
|
||||
|
||||
if (!this._extensionsStatus[msg.source]) {
|
||||
this._extensionsStatus[msg.source] = { messages: [] };
|
||||
}
|
||||
this._extensionsStatus[msg.source].messages.push(msg);
|
||||
}
|
||||
|
||||
public $localShowMessage(severity: Severity, msg: string): void {
|
||||
let messageShown = false;
|
||||
if (severity === Severity.Error || severity === Severity.Warning) {
|
||||
if (this._isDev) {
|
||||
// Only show nasty intrusive messages if doing extension development.
|
||||
this._messageService.show(severity, msg);
|
||||
messageShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!messageShown) {
|
||||
switch (severity) {
|
||||
case Severity.Error:
|
||||
console.error(msg);
|
||||
break;
|
||||
case Severity.Warning:
|
||||
console.warn(msg);
|
||||
break;
|
||||
default:
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- overwriting AbstractExtensionService
|
||||
|
||||
public getExtensionsStatus(): { [id: string]: IExtensionsStatus } {
|
||||
return this._extensionsStatus;
|
||||
}
|
||||
|
||||
protected _showMessage(severity: Severity, msg: string): void {
|
||||
this._proxy.$localShowMessage(severity, msg);
|
||||
this.$localShowMessage(severity, msg);
|
||||
}
|
||||
|
||||
protected _createFailedExtension(): ActivatedExtension {
|
||||
return new MainProcessFailedExtension();
|
||||
}
|
||||
|
||||
protected _actualActivateExtension(extensionDescription: IExtensionDescription): TPromise<ActivatedExtension> {
|
||||
|
||||
// redirect extension activation to the extension host
|
||||
return this._proxy.$activateExtension(extensionDescription).then(_ => {
|
||||
|
||||
// the extension host calls $onExtensionActivated, where we write to `_activatedExtensions`
|
||||
return this._activatedExtensions[extensionDescription.id];
|
||||
});
|
||||
}
|
||||
|
||||
// -- called by extension host
|
||||
|
||||
public $onExtensionHostReady(extensionDescriptions: IExtensionDescription[], messages: IMessage[]): TPromise<void> {
|
||||
ExtensionsRegistry.registerExtensions(extensionDescriptions);
|
||||
messages.forEach((entry) => this._handleMessage(entry));
|
||||
this._triggerOnReady();
|
||||
return;
|
||||
}
|
||||
|
||||
public $onExtensionActivated(extensionId: string): void {
|
||||
this._activatedExtensions[extensionId] = new MainProcessSuccessExtension();
|
||||
}
|
||||
|
||||
public $onExtensionActivationFailed(extensionId: string): void {
|
||||
this._activatedExtensions[extensionId] = new MainProcessFailedExtension();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the source code (module) of an extension.
|
||||
*/
|
||||
@@ -444,4 +316,4 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {FileChangesEvent, FileChangeType} from 'vs/platform/files/common/files';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {Disposable} from './extHostTypes';
|
||||
import {IEventService} from 'vs/platform/event/common/event';
|
||||
import {RunOnceScheduler} from 'vs/base/common/async';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {match} from 'vs/base/common/glob';
|
||||
import {Uri, FileSystemWatcher as _FileSystemWatcher} from 'vscode';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
|
||||
export interface FileSystemEvents {
|
||||
created: URI[];
|
||||
@@ -113,40 +108,3 @@ export class ExtHostFileSystemEventService {
|
||||
this._emitter.fire(events);
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadFileSystemEventService {
|
||||
|
||||
constructor( @IEventService eventService: IEventService, @IThreadService threadService: IThreadService) {
|
||||
|
||||
const proxy = threadService.get(ExtHostContext.ExtHostFileSystemEventService);
|
||||
const events: FileSystemEvents = {
|
||||
created: [],
|
||||
changed: [],
|
||||
deleted: []
|
||||
};
|
||||
|
||||
const scheduler = new RunOnceScheduler(() => {
|
||||
proxy._onFileEvent(events);
|
||||
events.created.length = 0;
|
||||
events.changed.length = 0;
|
||||
events.deleted.length = 0;
|
||||
}, 100);
|
||||
|
||||
eventService.addListener2('files:fileChanges', (event: FileChangesEvent) => {
|
||||
for (let change of event.changes) {
|
||||
switch (change.type) {
|
||||
case FileChangeType.ADDED:
|
||||
events.created.push(change.resource);
|
||||
break;
|
||||
case FileChangeType.UPDATED:
|
||||
events.changed.push(change.resource);
|
||||
break;
|
||||
case FileChangeType.DELETED:
|
||||
events.deleted.push(change.resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
scheduler.schedule();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,26 +11,25 @@ import {IThreadService} from 'vs/workbench/services/thread/common/threadService'
|
||||
import * as vscode from 'vscode';
|
||||
import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import {Range, Disposable, SignatureHelp, CompletionList} from 'vs/workbench/api/node/extHostTypes';
|
||||
import {IReadOnlyModel, IPosition, IRange, ISingleEditOperation} from 'vs/editor/common/editorCommon';
|
||||
import {IPosition, IRange, ISingleEditOperation} from 'vs/editor/common/editorCommon';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import {ExtHostModelService} from 'vs/workbench/api/node/extHostDocuments';
|
||||
import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments';
|
||||
import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands';
|
||||
import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics';
|
||||
import {NavigateTypesSupportRegistry, INavigateTypesSupport, ITypeBearing} from 'vs/workbench/parts/search/common/search';
|
||||
import {asWinJsPromise, ShallowCancelThenPromise, 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 {MainContext, ExtHostContext} from './extHostProtocol';
|
||||
import {INavigateTypesSupport, ITypeBearing} from 'vs/workbench/parts/search/common/search';
|
||||
import {asWinJsPromise, ShallowCancelThenPromise} from 'vs/base/common/async';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadLanguageFeatures} from './mainThreadLanguageFeatures';
|
||||
import {regExpLeadsToEndlessLoop} from 'vs/base/common/strings';
|
||||
|
||||
// --- adapter
|
||||
|
||||
class OutlineAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.DocumentSymbolProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.DocumentSymbolProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.DocumentSymbolProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -53,13 +52,13 @@ interface CachedCodeLens {
|
||||
|
||||
class CodeLensAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _commands: ExtHostCommands;
|
||||
private _provider: vscode.CodeLensProvider;
|
||||
|
||||
private _cache: { [uri: string]: { version: number; data: TPromise<CachedCodeLens>; } } = Object.create(null);
|
||||
|
||||
constructor(documents: ExtHostModelService, commands: ExtHostCommands, provider: vscode.CodeLensProvider) {
|
||||
constructor(documents: ExtHostDocuments, commands: ExtHostCommands, provider: vscode.CodeLensProvider) {
|
||||
this._documents = documents;
|
||||
this._commands = commands;
|
||||
this._provider = provider;
|
||||
@@ -157,10 +156,10 @@ class CodeLensAdapter {
|
||||
|
||||
class DefinitionAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.DefinitionProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.DefinitionProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.DefinitionProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -190,10 +189,10 @@ class DefinitionAdapter {
|
||||
|
||||
class HoverAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.HoverProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.HoverProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.HoverProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -221,10 +220,10 @@ class HoverAdapter {
|
||||
|
||||
class DocumentHighlightAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.DocumentHighlightProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.DocumentHighlightProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.DocumentHighlightProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -251,10 +250,10 @@ class DocumentHighlightAdapter {
|
||||
|
||||
class ReferenceAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.ReferenceProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.ReferenceProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.ReferenceProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -280,14 +279,14 @@ class ReferenceAdapter {
|
||||
|
||||
class QuickFixAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _commands: ExtHostCommands;
|
||||
private _diagnostics: ExtHostDiagnostics;
|
||||
private _provider: vscode.CodeActionProvider;
|
||||
|
||||
private _cachedCommands: IDisposable[] = [];
|
||||
|
||||
constructor(documents: ExtHostModelService, commands: ExtHostCommands, diagnostics: ExtHostDiagnostics, provider: vscode.CodeActionProvider) {
|
||||
constructor(documents: ExtHostDocuments, commands: ExtHostCommands, diagnostics: ExtHostDiagnostics, provider: vscode.CodeActionProvider) {
|
||||
this._documents = documents;
|
||||
this._commands = commands;
|
||||
this._diagnostics = diagnostics;
|
||||
@@ -328,10 +327,10 @@ class QuickFixAdapter {
|
||||
|
||||
class DocumentFormattingAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.DocumentFormattingEditProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.DocumentFormattingEditProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.DocumentFormattingEditProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -350,10 +349,10 @@ class DocumentFormattingAdapter {
|
||||
|
||||
class RangeFormattingAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.DocumentRangeFormattingEditProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.DocumentRangeFormattingEditProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.DocumentRangeFormattingEditProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -373,10 +372,10 @@ class RangeFormattingAdapter {
|
||||
|
||||
class OnTypeFormattingAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.OnTypeFormattingEditProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.OnTypeFormattingEditProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.OnTypeFormattingEditProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -415,10 +414,10 @@ class NavigateTypeAdapter implements INavigateTypesSupport {
|
||||
|
||||
class RenameAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.RenameProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.RenameProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.RenameProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -467,11 +466,11 @@ interface ISuggestion2 extends modes.ISuggestion {
|
||||
|
||||
class SuggestAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.CompletionItemProvider;
|
||||
private _cache: { [key: string]: CompletionList } = Object.create(null);
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.CompletionItemProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.CompletionItemProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -569,10 +568,10 @@ class SuggestAdapter {
|
||||
|
||||
class SignatureHelpAdapter {
|
||||
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.SignatureHelpProvider;
|
||||
|
||||
constructor(documents: ExtHostModelService, provider: vscode.SignatureHelpProvider) {
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.SignatureHelpProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
@@ -600,14 +599,14 @@ export class ExtHostLanguageFeatures {
|
||||
private static _handlePool: number = 0;
|
||||
|
||||
private _proxy: MainThreadLanguageFeatures;
|
||||
private _documents: ExtHostModelService;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _commands: ExtHostCommands;
|
||||
private _diagnostics: ExtHostDiagnostics;
|
||||
private _adapter: { [handle: number]: Adapter } = Object.create(null);
|
||||
|
||||
constructor(
|
||||
threadService: IThreadService,
|
||||
documents: ExtHostModelService,
|
||||
documents: ExtHostDocuments,
|
||||
commands: ExtHostCommands,
|
||||
diagnostics: ExtHostDiagnostics
|
||||
) {
|
||||
@@ -821,188 +820,32 @@ export class ExtHostLanguageFeatures {
|
||||
$provideSignatureHelp(handle: number, resource: URI, position: IPosition): TPromise<modes.SignatureHelp> {
|
||||
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(resource, position));
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadLanguageFeatures {
|
||||
// --- configuration
|
||||
|
||||
private _proxy: ExtHostLanguageFeatures;
|
||||
private _registrations: { [handle: number]: IDisposable; } = Object.create(null);
|
||||
setLanguageConfiguration(languageId:string, configuration: vscode.LanguageConfiguration): vscode.Disposable {
|
||||
let {wordPattern} = configuration;
|
||||
|
||||
constructor( @IThreadService threadService: IThreadService) {
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostLanguageFeatures);
|
||||
}
|
||||
|
||||
$unregister(handle: number): TPromise<any> {
|
||||
let registration = this._registrations[handle];
|
||||
if (registration) {
|
||||
registration.dispose();
|
||||
delete this._registrations[handle];
|
||||
// check for a valid word pattern
|
||||
if (wordPattern && regExpLeadsToEndlessLoop(wordPattern)) {
|
||||
throw new Error(`Invalid language configuration: wordPattern '${wordPattern}' is not allowed to match the empty string.`);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- outline
|
||||
// word definition
|
||||
if (wordPattern) {
|
||||
this._documents.setWordDefinitionFor(languageId, wordPattern);
|
||||
} else {
|
||||
this._documents.setWordDefinitionFor(languageId, null);
|
||||
}
|
||||
|
||||
$registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(selector, <modes.DocumentSymbolProvider>{
|
||||
provideDocumentSymbols: (model:IReadOnlyModel, token: CancellationToken): Thenable<modes.SymbolInformation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentSymbols(handle, model.uri));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
// backward compatibility, migrate deprecated setting
|
||||
if (configuration.__characterPairSupport && !configuration.autoClosingPairs) {
|
||||
configuration.autoClosingPairs = configuration.__characterPairSupport.autoClosingPairs;
|
||||
delete configuration.__characterPairSupport;
|
||||
}
|
||||
|
||||
// --- code lens
|
||||
|
||||
$registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.CodeLensProviderRegistry.register(selector, <modes.CodeLensProvider>{
|
||||
provideCodeLenses: (model:IReadOnlyModel, token: CancellationToken): modes.ICodeLensSymbol[] | Thenable<modes.ICodeLensSymbol[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideCodeLenses(handle, model.uri));
|
||||
},
|
||||
resolveCodeLens: (model:IReadOnlyModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): modes.ICodeLensSymbol | Thenable<modes.ICodeLensSymbol> => {
|
||||
return wireCancellationToken(token, this._proxy.$resolveCodeLens(handle, model.uri, codeLens));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- declaration
|
||||
|
||||
$registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DefinitionProviderRegistry.register(selector, <modes.DefinitionProvider>{
|
||||
provideDefinition: (model, position, token): Thenable<modes.Definition> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDefinition(handle, model.uri, position));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- extra info
|
||||
|
||||
$registerHoverProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.HoverProviderRegistry.register(selector, <modes.HoverProvider>{
|
||||
provideHover: (model:IReadOnlyModel, position:EditorPosition, token:CancellationToken): Thenable<modes.Hover> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideHover(handle, model.uri, position));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- occurrences
|
||||
|
||||
$registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DocumentHighlightProviderRegistry.register(selector, <modes.DocumentHighlightProvider>{
|
||||
provideDocumentHighlights: (model: IReadOnlyModel, position: EditorPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentHighlights(handle, model.uri, position));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- references
|
||||
|
||||
$registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.ReferenceProviderRegistry.register(selector, <modes.ReferenceProvider>{
|
||||
provideReferences: (model:IReadOnlyModel, position:EditorPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<modes.Location[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideReferences(handle, model.uri, position, context));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- quick fix
|
||||
|
||||
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
|
||||
provideCodeActions: (model:IReadOnlyModel, range:EditorRange, token: CancellationToken): Thenable<modes.CodeAction[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideCodeActions(handle, model.uri, range));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- formatting
|
||||
|
||||
$registerDocumentFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(selector, <modes.DocumentFormattingEditProvider>{
|
||||
provideDocumentFormattingEdits: (model: IReadOnlyModel, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$registerRangeFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(selector, <modes.DocumentRangeFormattingEditProvider>{
|
||||
provideDocumentRangeFormattingEdits: (model: IReadOnlyModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$registerOnTypeFormattingSupport(handle: number, selector: vscode.DocumentSelector, autoFormatTriggerCharacters: string[]): TPromise<any> {
|
||||
this._registrations[handle] = modes.OnTypeFormattingEditProviderRegistry.register(selector, <modes.OnTypeFormattingEditProvider>{
|
||||
|
||||
autoFormatTriggerCharacters,
|
||||
|
||||
provideOnTypeFormattingEdits: (model: IReadOnlyModel, position: EditorPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideOnTypeFormattingEdits(handle, model.uri, position, ch, options));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- navigate type
|
||||
|
||||
$registerNavigateTypeSupport(handle: number): TPromise<any> {
|
||||
this._registrations[handle] = NavigateTypesSupportRegistry.register(<INavigateTypesSupport>{
|
||||
getNavigateToItems: (search: string): TPromise<ITypeBearing[]> => {
|
||||
return this._proxy.$getNavigateToItems(handle, search);
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- rename
|
||||
|
||||
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.RenameProviderRegistry.register(selector, <modes.RenameProvider>{
|
||||
provideRenameEdits: (model:IReadOnlyModel, position:EditorPosition, newName: string, token: CancellationToken): Thenable<modes.WorkspaceEdit> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- suggest
|
||||
|
||||
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[]): TPromise<any> {
|
||||
this._registrations[handle] = modes.SuggestRegistry.register(selector, <modes.ISuggestSupport>{
|
||||
triggerCharacters: triggerCharacters,
|
||||
shouldAutotriggerSuggest: true,
|
||||
provideCompletionItems: (model:IReadOnlyModel, position:EditorPosition, token:CancellationToken): Thenable<modes.ISuggestResult[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position));
|
||||
},
|
||||
resolveCompletionItem: (model:IReadOnlyModel, position:EditorPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable<modes.ISuggestion> => {
|
||||
return wireCancellationToken(token, this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- parameter hints
|
||||
|
||||
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise<any> {
|
||||
this._registrations[handle] = modes.SignatureHelpProviderRegistry.register(selector, <modes.SignatureHelpProvider>{
|
||||
|
||||
signatureHelpTriggerCharacters: triggerCharacter,
|
||||
|
||||
provideSignatureHelp: (model:IReadOnlyModel, position:EditorPosition, token:CancellationToken): Thenable<modes.SignatureHelp> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideSignatureHelp(handle, model.uri, position));
|
||||
}
|
||||
|
||||
});
|
||||
return undefined;
|
||||
const handle = this._nextHandle();
|
||||
this._proxy.$setLanguageConfiguration(handle, languageId, configuration);
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IModeService} from 'vs/editor/common/services/modeService';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadLanguages} from './mainThreadLanguages';
|
||||
|
||||
export class ExtHostLanguages {
|
||||
|
||||
@@ -24,17 +24,3 @@ export class ExtHostLanguages {
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadLanguages {
|
||||
|
||||
private _modeService: IModeService;
|
||||
|
||||
constructor(
|
||||
@IModeService modeService: IModeService
|
||||
) {
|
||||
this._modeService = modeService;
|
||||
}
|
||||
|
||||
_getLanguages(): TPromise<string[]> {
|
||||
return TPromise.as(this._modeService.getRegisteredModes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IMessageService} from 'vs/platform/message/common/message';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import {Action} from 'vs/base/common/actions';
|
||||
import {TPromise as Promise} from 'vs/base/common/winjs.base';
|
||||
import vscode = require('vscode');
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadMessageService} from './mainThreadMessageService';
|
||||
|
||||
export class ExtHostMessageService {
|
||||
|
||||
@@ -44,51 +41,3 @@ export class ExtHostMessageService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadMessageService {
|
||||
|
||||
private _messageService: IMessageService;
|
||||
|
||||
constructor(@IMessageService messageService:IMessageService) {
|
||||
this._messageService = messageService;
|
||||
}
|
||||
|
||||
$showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number> {
|
||||
|
||||
return new Promise<number>(resolve => {
|
||||
|
||||
let messageHide: Function;
|
||||
let actions: MessageItemAction[] = [];
|
||||
let hasCloseAffordance = false;
|
||||
|
||||
class MessageItemAction extends Action {
|
||||
constructor(id: string, label: string, handle: number) {
|
||||
super(id, label, undefined, true, () => {
|
||||
resolve(handle);
|
||||
messageHide(); // triggers dispose! make sure promise is already resolved
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
dispose(): void {
|
||||
resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
commands.forEach(command => {
|
||||
if (command.isCloseAffordance === true) {
|
||||
hasCloseAffordance = true;
|
||||
}
|
||||
actions.push(new MessageItemAction('_extension_message_handle_' + command.handle, command.title, command.handle));
|
||||
});
|
||||
|
||||
if (!hasCloseAffordance) {
|
||||
actions.unshift(new MessageItemAction('__close', nls.localize('close', "Close"), undefined));
|
||||
}
|
||||
|
||||
messageHide = this._messageService.show(severity, {
|
||||
message,
|
||||
actions
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {Registry} from 'vs/platform/platform';
|
||||
import {IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry} from 'vs/workbench/parts/output/common/output';
|
||||
import {IPartService} from 'vs/workbench/services/part/common/partService';
|
||||
import {IPanelService} from 'vs/workbench/services/panel/common/panelService';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadOutputService} from './mainThreadOutputService';
|
||||
|
||||
export class ExtHostOutputChannel implements vscode.OutputChannel {
|
||||
|
||||
@@ -81,51 +77,3 @@ export class ExtHostOutputService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadOutputService {
|
||||
|
||||
private _outputService: IOutputService;
|
||||
private _partService: IPartService;
|
||||
private _panelService: IPanelService;
|
||||
|
||||
constructor(@IOutputService outputService: IOutputService,
|
||||
@IPartService partService: IPartService,
|
||||
@IPanelService panelService: IPanelService
|
||||
) {
|
||||
this._outputService = outputService;
|
||||
this._partService = partService;
|
||||
this._panelService = panelService;
|
||||
}
|
||||
|
||||
public append(channelId: string, label: string, value: string): TPromise<void> {
|
||||
this._getChannel(channelId, label).append(value);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public clear(channelId: string, label: string): TPromise<void> {
|
||||
this._getChannel(channelId, label).clear();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public reveal(channelId: string, label: string, preserveFocus: boolean): TPromise<void> {
|
||||
this._getChannel(channelId, label).show(preserveFocus);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _getChannel(channelId: string, label: string): IOutputChannel {
|
||||
if (Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannels().every(channel => channel.id !== channelId)) {
|
||||
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel(channelId, label);
|
||||
}
|
||||
|
||||
return this._outputService.getChannel(channelId);
|
||||
}
|
||||
|
||||
public close(channelId: string): TPromise<void> {
|
||||
const panel = this._panelService.getActivePanel();
|
||||
if (panel && panel.getId() === OUTPUT_PANEL_ID && channelId === this._outputService.getActiveChannel().id ) {
|
||||
this._partService.setPanelHidden(true);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,32 +9,43 @@ import {
|
||||
createExtHostContextProxyIdentifier as createExtId,
|
||||
ProxyIdentifier, IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
|
||||
import {MainProcessVSCodeAPIHelper} from './extHost.api.impl';
|
||||
import {ExtHostCommands, MainThreadCommands} from './extHostCommands';
|
||||
import {ExtHostConfiguration, MainThreadConfiguration} from './extHostConfiguration';
|
||||
import {ExtHostDiagnostics, MainThreadDiagnostics} from './extHostDiagnostics';
|
||||
import {ExtHostModelService, MainThreadDocuments} from './extHostDocuments';
|
||||
import {ExtHostEditors, MainThreadEditors} from './extHostEditors';
|
||||
// --- main thread addressable
|
||||
import {MainThreadCommands} from './mainThreadCommands';
|
||||
import {MainThreadConfiguration} from './mainThreadConfiguration';
|
||||
import {MainThreadDiagnostics} from './mainThreadDiagnostics';
|
||||
import {MainThreadDocuments} from './mainThreadDocuments';
|
||||
import {MainThreadEditors} from './mainThreadEditors';
|
||||
import {MainThreadErrors} from './mainThreadErrors';
|
||||
import {MainThreadLanguageFeatures} from './mainThreadLanguageFeatures';
|
||||
import {MainThreadLanguages} from './mainThreadLanguages';
|
||||
import {MainThreadMessageService} from './mainThreadMessageService';
|
||||
import {MainThreadOutputService} from './mainThreadOutputService';
|
||||
import {MainThreadQuickOpen} from './mainThreadQuickOpen';
|
||||
import {MainThreadStatusBar} from './mainThreadStatusBar';
|
||||
import {MainThreadStorage} from './mainThreadStorage';
|
||||
import {MainThreadTelemetry} from './mainThreadTelemetry';
|
||||
import {MainThreadWorkspace} from './mainThreadWorkspace';
|
||||
import {MainProcessExtensionService} from './mainThreadExtensionService';
|
||||
|
||||
// --- ext host addressable
|
||||
import {ExtHostCommands} from './extHostCommands';
|
||||
import {ExtHostConfiguration} from './extHostConfiguration';
|
||||
import {ExtHostDiagnostics} from './extHostDiagnostics';
|
||||
import {ExtHostDocuments} from './extHostDocuments';
|
||||
import {ExtHostEditors} from './extHostEditors';
|
||||
import {ExtHostFileSystemEventService} from './extHostFileSystemEventService';
|
||||
import {ExtHostLanguageFeatures, MainThreadLanguageFeatures} from './extHostLanguageFeatures';
|
||||
import {MainThreadLanguages} from './extHostLanguages';
|
||||
import {MainThreadMessageService} from './extHostMessageService';
|
||||
import {MainThreadOutputService} from './extHostOutputService';
|
||||
import {ExtHostQuickOpen, MainThreadQuickOpen} from './extHostQuickOpen';
|
||||
import {MainThreadStatusBar} from './extHostStatusBar';
|
||||
import {MainThreadStorage} from './extHostStorage';
|
||||
import {MainThreadTelemetry} from './extHostTelemetry';
|
||||
import {MainThreadWorkspace} from './extHostWorkspace';
|
||||
import {ExtHostExtensionService, MainProcessExtensionService} from './nativeExtensionService';
|
||||
import {ExtHostLanguageFeatures} from './extHostLanguageFeatures';
|
||||
import {ExtHostQuickOpen} from './extHostQuickOpen';
|
||||
import {ExtHostExtensionService} from './extHostExtensionService';
|
||||
|
||||
let mainCounter = 0;
|
||||
export const MainContext = {
|
||||
MainProcessVSCodeAPIHelper: createMainId<MainProcessVSCodeAPIHelper>(++mainCounter),
|
||||
MainThreadCommands: createMainId<MainThreadCommands>(++mainCounter),
|
||||
MainThreadConfiguration: createMainId<MainThreadConfiguration>(++mainCounter),
|
||||
MainThreadDiagnostics: createMainId<MainThreadDiagnostics>(++mainCounter),
|
||||
MainThreadDocuments: createMainId<MainThreadDocuments>(++mainCounter),
|
||||
MainThreadEditors: createMainId<MainThreadEditors>(++mainCounter),
|
||||
MainThreadErrors: createMainId<MainThreadErrors>(++mainCounter),
|
||||
MainThreadLanguageFeatures: createMainId<MainThreadLanguageFeatures>(++mainCounter),
|
||||
MainThreadLanguages: createMainId<MainThreadLanguages>(++mainCounter),
|
||||
MainThreadMessageService: createMainId<MainThreadMessageService>(++mainCounter),
|
||||
@@ -52,7 +63,7 @@ export const ExtHostContext = {
|
||||
ExtHostCommands: createExtId<ExtHostCommands>(++extCounter),
|
||||
ExtHostConfiguration: createExtId<ExtHostConfiguration>(++extCounter),
|
||||
ExtHostDiagnostics: createExtId<ExtHostDiagnostics>(++extCounter),
|
||||
ExtHostModelService: createExtId<ExtHostModelService>(++extCounter),
|
||||
ExtHostDocuments: createExtId<ExtHostDocuments>(++extCounter),
|
||||
ExtHostEditors: createExtId<ExtHostEditors>(++extCounter),
|
||||
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventService>(++extCounter),
|
||||
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeatures>(++extCounter),
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IQuickOpenService, IPickOpenEntry, IPickOptions, IInputOptions} from 'vs/workbench/services/quickopen/common/quickOpenService';
|
||||
import {IPickOpenEntry} from 'vs/workbench/services/quickopen/common/quickOpenService';
|
||||
import {QuickPickOptions, QuickPickItem, InputBoxOptions} from 'vscode';
|
||||
import {MainContext, ExtHostContext} from './extHostProtocol';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadQuickOpen} from './mainThreadQuickOpen';
|
||||
|
||||
export interface MyQuickPickItems extends IPickOpenEntry {
|
||||
handle: number;
|
||||
@@ -111,83 +112,3 @@ export class ExtHostQuickOpen {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadQuickOpen {
|
||||
|
||||
private _proxy: ExtHostQuickOpen;
|
||||
private _quickOpenService: IQuickOpenService;
|
||||
private _doSetItems: (items: MyQuickPickItems[]) => any;
|
||||
private _doSetError: (error: Error) => any;
|
||||
private _contents: TPromise<MyQuickPickItems[]>;
|
||||
private _token: number = 0;
|
||||
|
||||
constructor( @IThreadService threadService: IThreadService, @IQuickOpenService quickOpenService: IQuickOpenService) {
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostQuickOpen);
|
||||
this._quickOpenService = quickOpenService;
|
||||
}
|
||||
|
||||
$show(options: IPickOptions): Thenable<number> {
|
||||
|
||||
const myToken = ++this._token;
|
||||
|
||||
this._contents = new TPromise((c, e) => {
|
||||
this._doSetItems = (items) => {
|
||||
if (myToken === this._token) {
|
||||
c(items);
|
||||
}
|
||||
};
|
||||
|
||||
this._doSetError = (error) => {
|
||||
if (myToken === this._token) {
|
||||
e(error);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return this._quickOpenService.pick(this._contents, options).then(item => {
|
||||
if (item) {
|
||||
return item.handle;
|
||||
}
|
||||
}, undefined, progress => {
|
||||
if (progress) {
|
||||
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$setItems(items: MyQuickPickItems[]): Thenable<any> {
|
||||
if (this._doSetItems) {
|
||||
this._doSetItems(items);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$setError(error: Error): Thenable<any> {
|
||||
if (this._doSetError) {
|
||||
this._doSetError(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- input
|
||||
|
||||
$input(options: InputBoxOptions, validateInput: boolean): Thenable<string> {
|
||||
|
||||
const inputOptions: IInputOptions = Object.create(null);
|
||||
|
||||
if (options) {
|
||||
inputOptions.password = options.password;
|
||||
inputOptions.placeHolder = options.placeHolder;
|
||||
inputOptions.prompt = options.prompt;
|
||||
inputOptions.value = options.value;
|
||||
}
|
||||
|
||||
if (validateInput) {
|
||||
inputOptions.validateInput = (value) => {
|
||||
return this._proxy.$validateInput(value);
|
||||
};
|
||||
}
|
||||
|
||||
return this._quickOpenService.input(inputOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
'use strict';
|
||||
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment} from 'vs/platform/statusbar/common/statusbar';
|
||||
import {IDisposable} from 'vs/base/common/lifecycle';
|
||||
import {StatusbarAlignment as MainThreadStatusBarAlignment} from 'vs/platform/statusbar/common/statusbar';
|
||||
import {StatusBarAlignment as ExtHostStatusBarAlignment, Disposable} from './extHostTypes';
|
||||
import {StatusBarItem, StatusBarAlignment} from 'vscode';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadStatusBar} from './mainThreadStatusBar';
|
||||
|
||||
export class ExtHostStatusBarEntry implements StatusBarItem {
|
||||
private static ID_GEN = 0;
|
||||
@@ -188,32 +188,3 @@ export class ExtHostStatusBar {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadStatusBar {
|
||||
private mapIdToDisposable: { [id: number]: IDisposable };
|
||||
|
||||
constructor(
|
||||
@IStatusbarService private statusbarService: IStatusbarService
|
||||
) {
|
||||
this.mapIdToDisposable = Object.create(null);
|
||||
}
|
||||
|
||||
setEntry(id: number, text: string, tooltip: string, command: string, color: string, alignment: MainThreadStatusBarAlignment, priority: number): void {
|
||||
|
||||
// Dispose any old
|
||||
this.dispose(id);
|
||||
|
||||
// Add new
|
||||
let disposeable = this.statusbarService.addEntry({ text, tooltip, command, color }, alignment, priority);
|
||||
this.mapIdToDisposable[id] = disposeable;
|
||||
}
|
||||
|
||||
dispose(id: number) {
|
||||
let disposeable = this.mapIdToDisposable[id];
|
||||
if (disposeable) {
|
||||
disposeable.dispose();
|
||||
}
|
||||
|
||||
delete this.mapIdToDisposable[id];
|
||||
}
|
||||
}
|
||||
@@ -6,41 +6,8 @@
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
|
||||
export class MainThreadStorage {
|
||||
|
||||
private _storageService: IStorageService;
|
||||
|
||||
constructor( @IStorageService storageService: IStorageService) {
|
||||
this._storageService = storageService;
|
||||
}
|
||||
|
||||
getValue<T>(shared: boolean, key: string): TPromise<T> {
|
||||
let jsonValue = this._storageService.get(key, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
|
||||
if (!jsonValue) {
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
let value: T;
|
||||
try {
|
||||
value = JSON.parse(jsonValue);
|
||||
return TPromise.as(value);
|
||||
} catch (err) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
}
|
||||
|
||||
setValue(shared: boolean, key: string, value: any): TPromise<any> {
|
||||
let jsonValue: any;
|
||||
try {
|
||||
jsonValue = JSON.stringify(value);
|
||||
this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
|
||||
} catch (err) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
import {MainThreadStorage} from './mainThreadStorage';
|
||||
|
||||
export class ExtHostStorage {
|
||||
|
||||
|
||||
@@ -9,26 +9,7 @@ import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {ITelemetryService, ITelemetryInfo} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
|
||||
/**
|
||||
* Helper always instantiated in the main process to receive telemetry events from remote telemetry services
|
||||
*/
|
||||
export class MainThreadTelemetry {
|
||||
|
||||
private _telemetryService: ITelemetryService;
|
||||
|
||||
constructor( @ITelemetryService telemetryService: ITelemetryService) {
|
||||
this._telemetryService = telemetryService;
|
||||
}
|
||||
|
||||
public $publicLog(eventName: string, data?: any): void {
|
||||
this._telemetryService.publicLog(eventName, data);
|
||||
}
|
||||
|
||||
public $getTelemetryInfo(): TPromise<ITelemetryInfo> {
|
||||
return this._telemetryService.getTelemetryInfo();
|
||||
}
|
||||
}
|
||||
import {MainThreadTelemetry} from './mainThreadTelemetry';
|
||||
|
||||
export class RemoteTelemetryService implements ITelemetryService {
|
||||
|
||||
|
||||
@@ -764,3 +764,8 @@ export enum EndOfLine {
|
||||
CRLF = 2
|
||||
}
|
||||
|
||||
export enum TextEditorRevealType {
|
||||
Default = 0,
|
||||
InCenter = 1,
|
||||
InCenterIfOutsideViewport = 2
|
||||
}
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {isPromiseCanceledError} from 'vs/base/common/errors';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {ISearchService, QueryType} from 'vs/platform/search/common/search';
|
||||
import {IWorkspaceContextService, IWorkspace} from 'vs/platform/workspace/common/workspace';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IEventService} from 'vs/platform/event/common/event';
|
||||
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
|
||||
import {ITextFileService} from 'vs/workbench/parts/files/common/files';
|
||||
import {ICommonCodeEditor} from 'vs/editor/common/editorCommon';
|
||||
import {bulkEdit, IResourceEdit} from 'vs/editor/common/services/bulkEdit';
|
||||
import {IResourceEdit} from 'vs/editor/common/services/bulkEdit';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {fromRange} from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import {Uri, CancellationToken} from 'vscode';
|
||||
import {MainContext} from './extHostProtocol';
|
||||
import {MainThreadWorkspace} from './mainThreadWorkspace';
|
||||
|
||||
export class ExtHostWorkspace {
|
||||
|
||||
@@ -85,84 +79,3 @@ export class ExtHostWorkspace {
|
||||
return this._proxy.$applyWorkspaceEdit(resourceEdits);
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadWorkspace {
|
||||
|
||||
private _activeSearches: { [id: number]: TPromise<Uri[]> } = Object.create(null);
|
||||
private _searchService: ISearchService;
|
||||
private _workspace: IWorkspace;
|
||||
private _textFileService: ITextFileService;
|
||||
private _editorService:IWorkbenchEditorService;
|
||||
private _eventService:IEventService;
|
||||
|
||||
constructor( @ISearchService searchService: ISearchService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@ITextFileService textFileService,
|
||||
@IWorkbenchEditorService editorService,
|
||||
@IEventService eventService) {
|
||||
|
||||
this._searchService = searchService;
|
||||
this._workspace = contextService.getWorkspace();
|
||||
this._textFileService = textFileService;
|
||||
this._editorService = editorService;
|
||||
this._eventService = eventService;
|
||||
}
|
||||
|
||||
$startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable<Uri[]> {
|
||||
|
||||
if (!this._workspace) {
|
||||
return;
|
||||
}
|
||||
|
||||
const search = this._searchService.search({
|
||||
folderResources: [this._workspace.resource],
|
||||
type: QueryType.File,
|
||||
maxResults,
|
||||
includePattern: { [include]: true },
|
||||
excludePattern: { [exclude]: true },
|
||||
}).then(result => {
|
||||
return result.results.map(m => m.resource);
|
||||
}, err => {
|
||||
if (!isPromiseCanceledError(err)) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
});
|
||||
|
||||
this._activeSearches[requestId] = search;
|
||||
const onDone = () => delete this._activeSearches[requestId];
|
||||
search.done(onDone, onDone);
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
$cancelSearch(requestId: number): Thenable<boolean> {
|
||||
const search = this._activeSearches[requestId];
|
||||
if (search) {
|
||||
delete this._activeSearches[requestId];
|
||||
search.cancel();
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
$saveAll(includeUntitled?: boolean): Thenable<boolean> {
|
||||
return this._textFileService.saveAll(includeUntitled).then(result => {
|
||||
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 = <ICommonCodeEditor> editor.getControl();
|
||||
if (typeof candidate.getEditorType === 'function') {
|
||||
// enough proof
|
||||
codeEditor = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return bulkEdit(this._eventService, this._editorService, codeEditor, edits)
|
||||
.then(() => true);
|
||||
}
|
||||
}
|
||||
103
src/vs/workbench/api/node/mainThreadCommands.ts
Normal file
103
src/vs/workbench/api/node/mainThreadCommands.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import {IKeybindingService, ICommandHandlerDescription} from 'vs/platform/keybinding/common/keybindingService';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
import {ExtHostCommands} from './extHostCommands';
|
||||
|
||||
export class MainThreadCommands {
|
||||
|
||||
private _threadService: IThreadService;
|
||||
private _keybindingService: IKeybindingService;
|
||||
private _proxy: ExtHostCommands;
|
||||
|
||||
constructor(
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
this._threadService = threadService;
|
||||
this._keybindingService = keybindingService;
|
||||
this._proxy = this._threadService.get(ExtHostContext.ExtHostCommands);
|
||||
}
|
||||
|
||||
$registerCommand(id: string): TPromise<any> {
|
||||
|
||||
KeybindingsRegistry.registerCommandDesc({
|
||||
id,
|
||||
handler: (serviceAccessor, ...args: any[]) => {
|
||||
return this._proxy.$executeContributedCommand(id, ...args);
|
||||
},
|
||||
weight: undefined,
|
||||
when: undefined,
|
||||
win: undefined,
|
||||
mac: undefined,
|
||||
linux: undefined,
|
||||
primary: undefined,
|
||||
secondary: undefined
|
||||
});
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$executeCommand<T>(id: string, args: any[]): Thenable<T> {
|
||||
return this._keybindingService.executeCommand(id, ...args);
|
||||
}
|
||||
|
||||
$getCommands(): Thenable<string[]> {
|
||||
return TPromise.as(Object.keys(KeybindingsRegistry.getCommands()));
|
||||
}
|
||||
}
|
||||
|
||||
// --- command doc
|
||||
|
||||
KeybindingsRegistry.registerCommandDesc({
|
||||
id: '_generateCommandsDocumentation',
|
||||
handler: function(accessor) {
|
||||
return accessor.get(IThreadService).get(ExtHostContext.ExtHostCommands).$getContributedCommandHandlerDescriptions().then(result => {
|
||||
|
||||
// add local commands
|
||||
const commands = KeybindingsRegistry.getCommands();
|
||||
for (let id in commands) {
|
||||
let {description} = commands[id];
|
||||
if (description) {
|
||||
result[id] = description;
|
||||
}
|
||||
}
|
||||
|
||||
// print all as markdown
|
||||
const all: string[] = [];
|
||||
for (let id in result) {
|
||||
all.push('`' + id + '` - ' + _generateMarkdown(result[id]));
|
||||
}
|
||||
console.log(all.join('\n'));
|
||||
});
|
||||
},
|
||||
when: undefined,
|
||||
weight: KeybindingsRegistry.WEIGHT.builtinExtension(0),
|
||||
primary: undefined
|
||||
});
|
||||
|
||||
function _generateMarkdown(description: string | ICommandHandlerDescription): string {
|
||||
if (typeof description === 'string') {
|
||||
return description;
|
||||
} else {
|
||||
let parts = [description.description];
|
||||
parts.push('\n\n');
|
||||
if (description.args) {
|
||||
for (let arg of description.args) {
|
||||
parts.push(`* _${arg.name}_ ${arg.description || ''}\n`);
|
||||
}
|
||||
}
|
||||
if (description.returns) {
|
||||
parts.push(`* _(returns)_ ${description.returns}`);
|
||||
}
|
||||
parts.push('\n\n');
|
||||
return parts.join('');
|
||||
}
|
||||
}
|
||||
32
src/vs/workbench/api/node/mainThreadConfiguration.ts
Normal file
32
src/vs/workbench/api/node/mainThreadConfiguration.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
import {ExtHostConfiguration} from './extHostConfiguration';
|
||||
|
||||
export class MainThreadConfiguration {
|
||||
|
||||
private _configurationService: IConfigurationService;
|
||||
private _toDispose: IDisposable;
|
||||
private _proxy: ExtHostConfiguration;
|
||||
|
||||
constructor(@IConfigurationService configurationService: IConfigurationService,
|
||||
@IThreadService threadService: IThreadService) {
|
||||
|
||||
this._configurationService = configurationService;
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostConfiguration);
|
||||
|
||||
this._toDispose = this._configurationService.onDidUpdateConfiguration(event => this._proxy.$acceptConfigurationChanged(event.config));
|
||||
this._proxy.$acceptConfigurationChanged(this._configurationService.getConfiguration());
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
}
|
||||
31
src/vs/workbench/api/node/mainThreadDiagnostics.ts
Normal file
31
src/vs/workbench/api/node/mainThreadDiagnostics.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {IMarkerService, IMarkerData} from 'vs/platform/markers/common/markers';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
|
||||
export class MainThreadDiagnostics {
|
||||
|
||||
private _markerService: IMarkerService;
|
||||
|
||||
constructor(@IMarkerService markerService: IMarkerService) {
|
||||
this._markerService = markerService;
|
||||
}
|
||||
|
||||
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any> {
|
||||
for (let entry of entries) {
|
||||
let [uri, markers] = entry;
|
||||
this._markerService.changeOne(owner, uri, markers);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$clear(owner: string): TPromise<any> {
|
||||
this._markerService.changeAll(owner, undefined);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
262
src/vs/workbench/api/node/mainThreadDocuments.ts
Normal file
262
src/vs/workbench/api/node/mainThreadDocuments.ts
Normal file
@@ -0,0 +1,262 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {toErrorMessage, onUnexpectedError} from 'vs/base/common/errors';
|
||||
import {EmitterEvent} from 'vs/base/common/eventEmitter';
|
||||
import {IModelService} from 'vs/editor/common/services/modelService';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {IEventService} from 'vs/platform/event/common/event';
|
||||
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
|
||||
import {EventType as FileEventType, TextFileChangeEvent, ITextFileService} from 'vs/workbench/parts/files/common/files';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IFileService} from 'vs/platform/files/common/files';
|
||||
import {IModeService} from 'vs/editor/common/services/modeService';
|
||||
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import {ResourceEditorInput} from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
import {ExtHostDocuments} from './extHostDocuments';
|
||||
|
||||
export class MainThreadDocuments {
|
||||
private _modelService: IModelService;
|
||||
private _modeService: IModeService;
|
||||
private _textFileService: ITextFileService;
|
||||
private _editorService: IWorkbenchEditorService;
|
||||
private _fileService: IFileService;
|
||||
private _untitledEditorService: IUntitledEditorService;
|
||||
private _toDispose: IDisposable[];
|
||||
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
|
||||
private _proxy: ExtHostDocuments;
|
||||
private _modelIsSynced: { [modelId: string]: boolean; };
|
||||
private _resourceContentProvider: { [handle: number]: IDisposable };
|
||||
private _virtualDocumentSet: { [resource: string]: boolean };
|
||||
|
||||
constructor(
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IModeService modeService: IModeService,
|
||||
@IEventService eventService: IEventService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IUntitledEditorService untitledEditorService: IUntitledEditorService
|
||||
) {
|
||||
this._modelService = modelService;
|
||||
this._modeService = modeService;
|
||||
this._textFileService = textFileService;
|
||||
this._editorService = editorService;
|
||||
this._fileService = fileService;
|
||||
this._untitledEditorService = untitledEditorService;
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostDocuments);
|
||||
this._modelIsSynced = {};
|
||||
|
||||
this._toDispose = [];
|
||||
modelService.onModelAdded(this._onModelAdded, this, this._toDispose);
|
||||
modelService.onModelRemoved(this._onModelRemoved, this, this._toDispose);
|
||||
modelService.onModelModeChanged(this._onModelModeChanged, this, this._toDispose);
|
||||
|
||||
this._toDispose.push(eventService.addListener2(FileEventType.FILE_SAVED, (e: TextFileChangeEvent) => {
|
||||
if (this._shouldHandleFileEvent(e)) {
|
||||
this._proxy._acceptModelSaved(e.resource.toString());
|
||||
}
|
||||
}));
|
||||
this._toDispose.push(eventService.addListener2(FileEventType.FILE_REVERTED, (e: TextFileChangeEvent) => {
|
||||
if (this._shouldHandleFileEvent(e)) {
|
||||
this._proxy._acceptModelReverted(e.resource.toString());
|
||||
}
|
||||
}));
|
||||
this._toDispose.push(eventService.addListener2(FileEventType.FILE_DIRTY, (e: TextFileChangeEvent) => {
|
||||
if (this._shouldHandleFileEvent(e)) {
|
||||
this._proxy._acceptModelDirty(e.resource.toString());
|
||||
}
|
||||
}));
|
||||
|
||||
const handle = setInterval(() => this._runDocumentCleanup(), 1000 * 60 * 3);
|
||||
this._toDispose.push({ dispose() { clearInterval(handle); } });
|
||||
|
||||
this._modelToDisposeMap = Object.create(null);
|
||||
this._resourceContentProvider = Object.create(null);
|
||||
this._virtualDocumentSet = Object.create(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
Object.keys(this._modelToDisposeMap).forEach((modelUrl) => {
|
||||
this._modelToDisposeMap[modelUrl].dispose();
|
||||
});
|
||||
this._modelToDisposeMap = Object.create(null);
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
|
||||
private _shouldHandleFileEvent(e: TextFileChangeEvent): boolean {
|
||||
const model = this._modelService.getModel(e.resource);
|
||||
return model && !model.isTooLargeForHavingARichMode();
|
||||
}
|
||||
|
||||
private _onModelAdded(model: editorCommon.IModel): void {
|
||||
// Same filter as in mainThreadEditorsTracker
|
||||
if (model.isTooLargeForHavingARichMode()) {
|
||||
// don't synchronize too large models
|
||||
return null;
|
||||
}
|
||||
let modelUrl = model.uri;
|
||||
this._modelIsSynced[modelUrl.toString()] = true;
|
||||
this._modelToDisposeMap[modelUrl.toString()] = model.addBulkListener((events) => this._onModelEvents(modelUrl, events));
|
||||
this._proxy._acceptModelAdd({
|
||||
url: model.uri,
|
||||
versionId: model.getVersionId(),
|
||||
value: model.toRawText(),
|
||||
modeId: model.getMode().getId(),
|
||||
isDirty: this._textFileService.isDirty(modelUrl)
|
||||
});
|
||||
}
|
||||
|
||||
private _onModelModeChanged(event: { model: editorCommon.IModel; oldModeId: string; }): void {
|
||||
let {model, oldModeId} = event;
|
||||
let modelUrl = model.uri;
|
||||
if (!this._modelIsSynced[modelUrl.toString()]) {
|
||||
return;
|
||||
}
|
||||
this._proxy._acceptModelModeChanged(model.uri.toString(), oldModeId, model.getMode().getId());
|
||||
}
|
||||
|
||||
private _onModelRemoved(model: editorCommon.IModel): void {
|
||||
let modelUrl = model.uri;
|
||||
if (!this._modelIsSynced[modelUrl.toString()]) {
|
||||
return;
|
||||
}
|
||||
delete this._modelIsSynced[modelUrl.toString()];
|
||||
this._modelToDisposeMap[modelUrl.toString()].dispose();
|
||||
delete this._modelToDisposeMap[modelUrl.toString()];
|
||||
this._proxy._acceptModelRemoved(modelUrl.toString());
|
||||
}
|
||||
|
||||
private _onModelEvents(modelUrl: URI, events: EmitterEvent[]): void {
|
||||
let changedEvents: editorCommon.IModelContentChangedEvent2[] = [];
|
||||
for (let i = 0, len = events.length; i < len; i++) {
|
||||
let e = events[i];
|
||||
switch (e.getType()) {
|
||||
case editorCommon.EventType.ModelContentChanged2:
|
||||
changedEvents.push(<editorCommon.IModelContentChangedEvent2>e.getData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changedEvents.length > 0) {
|
||||
this._proxy._acceptModelChanged(modelUrl.toString(), changedEvents);
|
||||
}
|
||||
}
|
||||
|
||||
// --- from extension host process
|
||||
|
||||
_trySaveDocument(uri: URI): TPromise<boolean> {
|
||||
return this._textFileService.save(uri);
|
||||
}
|
||||
|
||||
_tryOpenDocument(uri: URI): TPromise<any> {
|
||||
|
||||
if (!uri.scheme || !(uri.fsPath || uri.authority)) {
|
||||
return TPromise.wrapError(`Invalid uri. Scheme and authority or path must be set.`);
|
||||
}
|
||||
|
||||
let promise: TPromise<boolean>;
|
||||
switch (uri.scheme) {
|
||||
case 'untitled':
|
||||
promise = this._handleUnititledScheme(uri);
|
||||
break;
|
||||
case 'file':
|
||||
default:
|
||||
promise = this._handleAsResourceInput(uri);
|
||||
break;
|
||||
}
|
||||
|
||||
return promise.then(success => {
|
||||
if (!success) {
|
||||
return TPromise.wrapError('cannot open ' + uri.toString());
|
||||
}
|
||||
}, err => {
|
||||
return TPromise.wrapError('cannot open ' + uri.toString() + '. Detail: ' + toErrorMessage(err));
|
||||
});
|
||||
}
|
||||
|
||||
private _handleAsResourceInput(uri: URI): TPromise<boolean> {
|
||||
return this._editorService.resolveEditorModel({ resource: uri }).then(model => {
|
||||
return !!model;
|
||||
});
|
||||
}
|
||||
|
||||
private _handleUnititledScheme(uri: URI): TPromise<boolean> {
|
||||
let asFileUri = URI.file(uri.fsPath);
|
||||
return this._fileService.resolveFile(asFileUri).then(stats => {
|
||||
// don't create a new file ontop of an existing file
|
||||
return TPromise.wrapError<boolean>('file already exists on disk');
|
||||
}, err => {
|
||||
let input = this._untitledEditorService.createOrGet(asFileUri); // using file-uri makes it show in 'Working Files' section
|
||||
return input.resolve(true).then(model => {
|
||||
if (input.getResource().toString() !== uri.toString()) {
|
||||
throw new Error(`expected URI ${uri.toString() } BUT GOT ${input.getResource().toString() }`);
|
||||
}
|
||||
if (!this._modelIsSynced[uri.toString()]) {
|
||||
throw new Error(`expected URI ${uri.toString()} to have come to LIFE`);
|
||||
}
|
||||
return this._proxy._acceptModelDirty(uri.toString()); // mark as dirty
|
||||
}).then(() => {
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- virtual document logic
|
||||
|
||||
$registerTextContentProvider(handle:number, scheme: string): void {
|
||||
this._resourceContentProvider[handle] = ResourceEditorInput.registerResourceContentProvider(scheme, {
|
||||
provideTextContent: (uri: URI): TPromise<editorCommon.IModel> => {
|
||||
return this._proxy.$provideTextDocumentContent(handle, uri).then(value => {
|
||||
if (typeof value === 'string') {
|
||||
this._virtualDocumentSet[uri.toString()] = true;
|
||||
const firstLineText = value.substr(0, 1 + value.search(/\r?\n/));
|
||||
const mode = this._modeService.getOrCreateModeByFilenameOrFirstLine(uri.fsPath, firstLineText);
|
||||
return this._modelService.createModel(value, mode, uri);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$unregisterTextContentProvider(handle: number): void {
|
||||
const registration = this._resourceContentProvider[handle];
|
||||
if (registration) {
|
||||
registration.dispose();
|
||||
delete this._resourceContentProvider[handle];
|
||||
}
|
||||
}
|
||||
|
||||
$onVirtualDocumentChange(uri: URI, value: string): void {
|
||||
const model = this._modelService.getModel(uri);
|
||||
if (model) {
|
||||
model.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
private _runDocumentCleanup(): void {
|
||||
|
||||
const toBeDisposed: URI[] = [];
|
||||
|
||||
TPromise.join(Object.keys(this._virtualDocumentSet).map(key => {
|
||||
let resource = URI.parse(key);
|
||||
return this._editorService.createInput({ resource }).then(input => {
|
||||
if (!this._editorService.isVisible(input, true)) {
|
||||
toBeDisposed.push(resource);
|
||||
}
|
||||
});
|
||||
})).then(() => {
|
||||
for (let resource of toBeDisposed) {
|
||||
this._modelService.destroyModel(resource);
|
||||
delete this._virtualDocumentSet[resource.toString()];
|
||||
}
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
@@ -4,656 +4,296 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {IEditor} from 'vs/platform/editor/common/editor';
|
||||
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
|
||||
import {IModelService} from 'vs/editor/common/services/modelService';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {RunOnceScheduler} from 'vs/base/common/async';
|
||||
import {IdGenerator} from 'vs/base/common/idGenerator';
|
||||
import {Range} from 'vs/editor/common/core/range';
|
||||
import {Selection} from 'vs/editor/common/core/selection';
|
||||
import {EndOfLine} from 'vs/workbench/api/node/extHostTypes';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {EndOfLine} from './extHostTypes';
|
||||
import {ISingleEditOperation, ISelection, IRange, IEditor, EditorType, ICommonCodeEditor, ICommonDiffEditor, IDecorationRenderOptions, IDecorationOptions} 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';
|
||||
import {Position as EditorPosition} from 'vs/platform/editor/common/editor';
|
||||
import {IModelService} from 'vs/editor/common/services/modelService';
|
||||
import {MainThreadEditorsTracker, TextEditorRevealType, MainThreadTextEditor, ITextEditorConfigurationUpdate} from 'vs/workbench/api/node/mainThreadEditorsTracker';
|
||||
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {IEventService} from 'vs/platform/event/common/event';
|
||||
import {equals as arrayEquals} from 'vs/base/common/arrays';
|
||||
import {equals as objectEquals} from 'vs/base/common/objects';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
import {ExtHostEditors, ITextEditorPositionData} from './extHostEditors';
|
||||
|
||||
export interface ITextEditorConfigurationUpdate {
|
||||
tabSize?: number | string;
|
||||
insertSpaces?: boolean | string;
|
||||
cursorStyle?: EditorCommon.TextEditorCursorStyle;
|
||||
}
|
||||
export interface IResolvedTextEditorConfiguration {
|
||||
tabSize: number;
|
||||
insertSpaces: boolean;
|
||||
cursorStyle: EditorCommon.TextEditorCursorStyle;
|
||||
}
|
||||
|
||||
function configurationsEqual(a:IResolvedTextEditorConfiguration, b:IResolvedTextEditorConfiguration) {
|
||||
if (a && !b || !a && b) {
|
||||
return false;
|
||||
}
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
a.tabSize === b.tabSize
|
||||
&& a.insertSpaces === b.insertSpaces
|
||||
);
|
||||
}
|
||||
|
||||
export interface IFocusTracker {
|
||||
onGainedFocus(): void;
|
||||
onLostFocus(): void;
|
||||
}
|
||||
|
||||
export enum TextEditorRevealType {
|
||||
Default,
|
||||
InCenter,
|
||||
InCenterIfOutsideViewport
|
||||
}
|
||||
|
||||
/**
|
||||
* Text Editor that is permanently bound to the same model.
|
||||
* It can be bound or not to a CodeEditor.
|
||||
*/
|
||||
export class MainThreadTextEditor {
|
||||
|
||||
private _id: string;
|
||||
private _model: EditorCommon.IModel;
|
||||
private _modelService: IModelService;
|
||||
private _modelListeners: IDisposable[];
|
||||
private _codeEditor: EditorCommon.ICommonCodeEditor;
|
||||
private _focusTracker: IFocusTracker;
|
||||
private _codeEditorListeners: IDisposable[];
|
||||
|
||||
private _lastSelection: Selection[];
|
||||
private _configuration: IResolvedTextEditorConfiguration;
|
||||
|
||||
private _onSelectionChanged: Emitter<Selection[]>;
|
||||
private _onConfigurationChanged: Emitter<IResolvedTextEditorConfiguration>;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
model:EditorCommon.IModel,
|
||||
codeEditor:EditorCommon.ICommonCodeEditor,
|
||||
focusTracker:IFocusTracker,
|
||||
modelService: IModelService
|
||||
) {
|
||||
this._id = id;
|
||||
this._model = model;
|
||||
this._codeEditor = null;
|
||||
this._focusTracker = focusTracker;
|
||||
this._modelService = modelService;
|
||||
this._codeEditorListeners = [];
|
||||
|
||||
this._onSelectionChanged = new Emitter<Selection[]>();
|
||||
this._onConfigurationChanged = new Emitter<IResolvedTextEditorConfiguration>();
|
||||
|
||||
this._lastSelection = [ new Selection(1,1,1,1) ];
|
||||
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
|
||||
this._modelListeners = [];
|
||||
this._modelListeners.push(this._model.onDidChangeOptions((e) => {
|
||||
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
|
||||
}));
|
||||
|
||||
this.setCodeEditor(codeEditor);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._model = null;
|
||||
this._modelListeners = dispose(this._modelListeners);
|
||||
this._codeEditor = null;
|
||||
this._codeEditorListeners = dispose(this._codeEditorListeners);
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public getModel(): EditorCommon.IModel {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public hasCodeEditor(codeEditor:EditorCommon.ICommonCodeEditor): boolean {
|
||||
return (this._codeEditor === codeEditor);
|
||||
}
|
||||
|
||||
public setCodeEditor(codeEditor:EditorCommon.ICommonCodeEditor): void {
|
||||
if (this.hasCodeEditor(codeEditor)) {
|
||||
// Nothing to do...
|
||||
return;
|
||||
}
|
||||
this._codeEditorListeners = dispose(this._codeEditorListeners);
|
||||
|
||||
this._codeEditor = codeEditor;
|
||||
if (this._codeEditor) {
|
||||
|
||||
// Catch early the case that this code editor gets a different model set and disassociate from this model
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidChangeModel(() => {
|
||||
this.setCodeEditor(null);
|
||||
}));
|
||||
|
||||
let forwardSelection = () => {
|
||||
this._lastSelection = this._codeEditor.getSelections();
|
||||
this._onSelectionChanged.fire(this._lastSelection);
|
||||
};
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidChangeCursorSelection(forwardSelection));
|
||||
if (!Selection.selectionsArrEqual(this._lastSelection, this._codeEditor.getSelections())) {
|
||||
forwardSelection();
|
||||
}
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidFocusEditor(() => {
|
||||
this._focusTracker.onGainedFocus();
|
||||
}));
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidBlurEditor(() => {
|
||||
this._focusTracker.onLostFocus();
|
||||
}));
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidChangeConfiguration(() => {
|
||||
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
|
||||
}));
|
||||
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
|
||||
}
|
||||
}
|
||||
|
||||
public isVisible(): boolean {
|
||||
return !!this._codeEditor;
|
||||
}
|
||||
|
||||
public get onSelectionChanged(): Event<Selection[]> {
|
||||
return this._onSelectionChanged.event;
|
||||
}
|
||||
|
||||
public get onConfigurationChanged(): Event<IResolvedTextEditorConfiguration> {
|
||||
return this._onConfigurationChanged.event;
|
||||
}
|
||||
|
||||
public getSelections(): Selection[] {
|
||||
if (this._codeEditor) {
|
||||
return this._codeEditor.getSelections();
|
||||
}
|
||||
return this._lastSelection;
|
||||
}
|
||||
|
||||
public setSelections(selections:EditorCommon.ISelection[]): void {
|
||||
if (this._codeEditor) {
|
||||
this._codeEditor.setSelections(selections);
|
||||
return;
|
||||
}
|
||||
this._lastSelection = selections.map(Selection.liftSelection);
|
||||
console.warn('setSelections on invisble editor');
|
||||
}
|
||||
|
||||
public getConfiguration(): IResolvedTextEditorConfiguration {
|
||||
return this._configuration;
|
||||
}
|
||||
|
||||
private _setIndentConfiguration(newConfiguration:ITextEditorConfigurationUpdate): void {
|
||||
if (newConfiguration.tabSize === 'auto' || newConfiguration.insertSpaces === 'auto') {
|
||||
// one of the options was set to 'auto' => detect indentation
|
||||
|
||||
let creationOpts = this._modelService.getCreationOptions();
|
||||
let insertSpaces = creationOpts.insertSpaces;
|
||||
let tabSize = creationOpts.tabSize;
|
||||
|
||||
if (newConfiguration.insertSpaces !== 'auto') {
|
||||
if (typeof newConfiguration.insertSpaces !== 'undefined') {
|
||||
insertSpaces = (newConfiguration.insertSpaces === 'false' ? false : Boolean(newConfiguration.insertSpaces));
|
||||
}
|
||||
}
|
||||
if (newConfiguration.tabSize !== 'auto') {
|
||||
if (typeof newConfiguration.tabSize !== 'undefined') {
|
||||
let parsedTabSize = parseInt(<string>newConfiguration.tabSize, 10);
|
||||
if (!isNaN(parsedTabSize)) {
|
||||
tabSize = parsedTabSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._model.detectIndentation(insertSpaces, tabSize);
|
||||
return;
|
||||
}
|
||||
|
||||
let newOpts: EditorCommon.ITextModelUpdateOptions = {};
|
||||
if (typeof newConfiguration.insertSpaces !== 'undefined') {
|
||||
newOpts.insertSpaces = (newConfiguration.insertSpaces === 'false' ? false : Boolean(newConfiguration.insertSpaces));
|
||||
}
|
||||
if (typeof newConfiguration.tabSize !== 'undefined') {
|
||||
let parsedTabSize = parseInt(<string>newConfiguration.tabSize, 10);
|
||||
if (!isNaN(parsedTabSize)) {
|
||||
newOpts.tabSize = parsedTabSize;
|
||||
}
|
||||
}
|
||||
this._model.updateOptions(newOpts);
|
||||
}
|
||||
|
||||
public setConfiguration(newConfiguration:ITextEditorConfigurationUpdate): void {
|
||||
this._setIndentConfiguration(newConfiguration);
|
||||
|
||||
if (newConfiguration.cursorStyle) {
|
||||
let newCursorStyle = EditorCommon.cursorStyleToString(newConfiguration.cursorStyle);
|
||||
|
||||
if (!this._codeEditor) {
|
||||
console.warn('setConfiguration on invisible editor');
|
||||
return;
|
||||
}
|
||||
|
||||
this._codeEditor.updateOptions({
|
||||
cursorStyle: newCursorStyle
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public setDecorations(key: string, ranges:EditorCommon.IDecorationOptions[]): void {
|
||||
if (!this._codeEditor) {
|
||||
console.warn('setDecorations on invisible editor');
|
||||
return;
|
||||
}
|
||||
this._codeEditor.setDecorations(key, ranges);
|
||||
}
|
||||
|
||||
public revealRange(range:EditorCommon.IRange, revealType:TextEditorRevealType): void {
|
||||
if (!this._codeEditor) {
|
||||
console.warn('revealRange on invisible editor');
|
||||
return;
|
||||
}
|
||||
if (revealType === TextEditorRevealType.Default) {
|
||||
this._codeEditor.revealRange(range);
|
||||
} else if (revealType === TextEditorRevealType.InCenter) {
|
||||
this._codeEditor.revealRangeInCenter(range);
|
||||
} else if (revealType === TextEditorRevealType.InCenterIfOutsideViewport) {
|
||||
this._codeEditor.revealRangeInCenterIfOutsideViewport(range);
|
||||
} else {
|
||||
console.warn('Unknown revealType');
|
||||
}
|
||||
}
|
||||
|
||||
private _readConfiguration(model:EditorCommon.IModel, codeEditor:EditorCommon.ICommonCodeEditor): IResolvedTextEditorConfiguration {
|
||||
if (model.isDisposed()) {
|
||||
// shutdown time
|
||||
return this._configuration;
|
||||
}
|
||||
let cursorStyle = this._configuration ? this._configuration.cursorStyle : EditorCommon.TextEditorCursorStyle.Line;
|
||||
if (codeEditor) {
|
||||
let codeEditorOpts = codeEditor.getConfiguration();
|
||||
cursorStyle = codeEditorOpts.viewInfo.cursorStyle;
|
||||
}
|
||||
|
||||
let indent = model.getOptions();
|
||||
return {
|
||||
insertSpaces: indent.insertSpaces,
|
||||
tabSize: indent.tabSize,
|
||||
cursorStyle: cursorStyle
|
||||
};
|
||||
}
|
||||
|
||||
private _setConfiguration(newConfiguration:IResolvedTextEditorConfiguration): void {
|
||||
if (configurationsEqual(this._configuration, newConfiguration)) {
|
||||
return;
|
||||
}
|
||||
this._configuration = newConfiguration;
|
||||
this._onConfigurationChanged.fire(this._configuration);
|
||||
}
|
||||
|
||||
public isFocused(): boolean {
|
||||
if (this._codeEditor) {
|
||||
return this._codeEditor.isFocused();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public matches(editor: IEditor): boolean {
|
||||
if (!editor) {
|
||||
return false;
|
||||
}
|
||||
return editor.getControl() === this._codeEditor;
|
||||
}
|
||||
|
||||
public applyEdits(versionIdCheck:number, edits:EditorCommon.ISingleEditOperation[], setEndOfLine:EndOfLine): boolean {
|
||||
if (this._model.getVersionId() !== versionIdCheck) {
|
||||
console.warn('Model has changed in the meantime!');
|
||||
// throw new Error('Model has changed in the meantime!');
|
||||
// model changed in the meantime
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._codeEditor) {
|
||||
if (setEndOfLine === EndOfLine.CRLF) {
|
||||
this._model.setEOL(EditorCommon.EndOfLineSequence.CRLF);
|
||||
} else if (setEndOfLine === EndOfLine.LF) {
|
||||
this._model.setEOL(EditorCommon.EndOfLineSequence.LF);
|
||||
}
|
||||
|
||||
let transformedEdits = edits.map((edit): EditorCommon.IIdentifiedSingleEditOperation => {
|
||||
return {
|
||||
identifier: null,
|
||||
range: Range.lift(edit.range),
|
||||
text: edit.text,
|
||||
forceMoveMarkers: edit.forceMoveMarkers
|
||||
};
|
||||
});
|
||||
return this._codeEditor.executeEdits('MainThreadTextEditor', transformedEdits) || true;
|
||||
}
|
||||
|
||||
console.warn('applyEdits on invisible editor');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps track of what goes on in the main thread and maps models => text editors
|
||||
*/
|
||||
export class MainThreadEditorsTracker {
|
||||
|
||||
private static _Ids = new IdGenerator('');
|
||||
export class MainThreadEditors {
|
||||
|
||||
private _proxy: ExtHostEditors;
|
||||
private _workbenchEditorService: IWorkbenchEditorService;
|
||||
private _telemetryService: ITelemetryService;
|
||||
private _editorTracker: MainThreadEditorsTracker;
|
||||
private _toDispose: IDisposable[];
|
||||
private _codeEditorService: ICodeEditorService;
|
||||
private _modelService: IModelService;
|
||||
private _updateMapping: RunOnceScheduler;
|
||||
private _editorModelChangeListeners: {[editorId:string]:IDisposable;};
|
||||
|
||||
private _model2TextEditors: {
|
||||
[modelUri:string]: MainThreadTextEditor[];
|
||||
};
|
||||
private _focusedTextEditorId: string;
|
||||
private _visibleTextEditorIds: string[];
|
||||
private _onTextEditorAdd: Emitter<MainThreadTextEditor>;
|
||||
private _onTextEditorRemove: Emitter<MainThreadTextEditor>;
|
||||
private _onDidChangeFocusedTextEditor: Emitter<string>;
|
||||
private _onDidUpdateTextEditors: Emitter<void>;
|
||||
|
||||
private _focusTracker: IFocusTracker;
|
||||
private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
|
||||
private _textEditorsMap: { [editorId: string]: MainThreadTextEditor; };
|
||||
private _activeTextEditor: string;
|
||||
private _visibleEditors: string[];
|
||||
private _editorPositionData: ITextEditorPositionData;
|
||||
|
||||
constructor(
|
||||
editorService:ICodeEditorService,
|
||||
modelService:IModelService
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IWorkbenchEditorService workbenchEditorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@ICodeEditorService editorService: ICodeEditorService,
|
||||
@IEventService eventService: IEventService,
|
||||
@IModelService modelService: IModelService
|
||||
) {
|
||||
this._codeEditorService = editorService;
|
||||
this._modelService = modelService;
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostEditors);
|
||||
this._workbenchEditorService = workbenchEditorService;
|
||||
this._telemetryService = telemetryService;
|
||||
this._toDispose = [];
|
||||
this._focusedTextEditorId = null;
|
||||
this._visibleTextEditorIds = [];
|
||||
this._editorModelChangeListeners = Object.create(null);
|
||||
this._model2TextEditors = Object.create(null);
|
||||
this._onTextEditorAdd = new Emitter<MainThreadTextEditor>();
|
||||
this._onTextEditorRemove = new Emitter<MainThreadTextEditor>();
|
||||
this._onDidUpdateTextEditors = new Emitter<void>();
|
||||
this._onDidChangeFocusedTextEditor = new Emitter<string>();
|
||||
this._focusTracker = {
|
||||
onGainedFocus: () => this._updateFocusedTextEditor(),
|
||||
onLostFocus: () => this._updateFocusedTextEditor()
|
||||
};
|
||||
this._textEditorsListenersMap = Object.create(null);
|
||||
this._textEditorsMap = Object.create(null);
|
||||
this._activeTextEditor = null;
|
||||
this._visibleEditors = [];
|
||||
this._editorPositionData = null;
|
||||
|
||||
this._modelService.onModelAdded(this._onModelAdded, this, this._toDispose);
|
||||
this._modelService.onModelRemoved(this._onModelRemoved, this, this._toDispose);
|
||||
this._editorTracker = new MainThreadEditorsTracker(editorService, modelService);
|
||||
this._toDispose.push(this._editorTracker);
|
||||
|
||||
this._codeEditorService.onCodeEditorAdd(this._onCodeEditorAdd, this, this._toDispose);
|
||||
this._codeEditorService.onCodeEditorRemove(this._onCodeEditorRemove, this, this._toDispose);
|
||||
this._toDispose.push(this._editorTracker.onTextEditorAdd((textEditor) => this._onTextEditorAdd(textEditor)));
|
||||
this._toDispose.push(this._editorTracker.onTextEditorRemove((textEditor) => this._onTextEditorRemove(textEditor)));
|
||||
|
||||
this._updateMapping = new RunOnceScheduler(() => this._doUpdateMapping(), 0);
|
||||
this._toDispose.push(this._updateMapping);
|
||||
this._toDispose.push(this._editorTracker.onDidUpdateTextEditors(() => this._updateActiveAndVisibleTextEditors()));
|
||||
this._toDispose.push(this._editorTracker.onChangedFocusedTextEditor((focusedTextEditorId) => this._updateActiveAndVisibleTextEditors()));
|
||||
this._toDispose.push(editorGroupService.onEditorsChanged(() => this._updateActiveAndVisibleTextEditors()));
|
||||
this._toDispose.push(editorGroupService.onEditorsMoved(() => this._updateActiveAndVisibleTextEditors()));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
Object.keys(this._textEditorsListenersMap).forEach((editorId) => {
|
||||
dispose(this._textEditorsListenersMap[editorId]);
|
||||
});
|
||||
this._textEditorsListenersMap = Object.create(null);
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
|
||||
private _onModelAdded(model: EditorCommon.IModel): void {
|
||||
this._updateMapping.schedule();
|
||||
}
|
||||
|
||||
private _onModelRemoved(model: EditorCommon.IModel): void {
|
||||
this._updateMapping.schedule();
|
||||
}
|
||||
|
||||
private _onCodeEditorAdd(codeEditor: EditorCommon.ICommonCodeEditor): void {
|
||||
this._editorModelChangeListeners[codeEditor.getId()] = codeEditor.onDidChangeModel(_ => this._updateMapping.schedule());
|
||||
this._updateMapping.schedule();
|
||||
}
|
||||
|
||||
private _onCodeEditorRemove(codeEditor: EditorCommon.ICommonCodeEditor): void {
|
||||
this._editorModelChangeListeners[codeEditor.getId()].dispose();
|
||||
delete this._editorModelChangeListeners[codeEditor.getId()];
|
||||
this._updateMapping.schedule();
|
||||
}
|
||||
|
||||
private _doUpdateMapping(): void {
|
||||
let allModels = this._modelService.getModels();
|
||||
// Same filter as in extHostDocuments
|
||||
allModels = allModels.filter((model) => !model.isTooLargeForHavingARichMode());
|
||||
let allModelsMap: { [modelUri:string]: EditorCommon.IModel; } = Object.create(null);
|
||||
allModels.forEach((model) => {
|
||||
allModelsMap[model.uri.toString()] = model;
|
||||
private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
|
||||
let id = textEditor.getId();
|
||||
let toDispose: IDisposable[] = [];
|
||||
toDispose.push(textEditor.onConfigurationChanged((opts) => {
|
||||
this._proxy._acceptOptionsChanged(id, opts);
|
||||
}));
|
||||
toDispose.push(textEditor.onSelectionChanged((selection) => {
|
||||
this._proxy._acceptSelectionsChanged(id, selection);
|
||||
}));
|
||||
this._proxy._acceptTextEditorAdd({
|
||||
id: id,
|
||||
document: textEditor.getModel().uri,
|
||||
options: textEditor.getConfiguration(),
|
||||
selections: textEditor.getSelections(),
|
||||
editorPosition: this._findEditorPosition(textEditor)
|
||||
});
|
||||
|
||||
// Remove text editors for models that no longer exist
|
||||
Object.keys(this._model2TextEditors).forEach((modelUri) => {
|
||||
if (allModelsMap[modelUri]) {
|
||||
// model still exists, will be updated below
|
||||
return;
|
||||
}
|
||||
|
||||
let textEditorsToRemove = this._model2TextEditors[modelUri];
|
||||
delete this._model2TextEditors[modelUri];
|
||||
|
||||
for (let i = 0; i < textEditorsToRemove.length; i++) {
|
||||
this._onTextEditorRemove.fire(textEditorsToRemove[i]);
|
||||
textEditorsToRemove[i].dispose();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle all visible models
|
||||
let visibleModels = this._getVisibleModels();
|
||||
Object.keys(visibleModels).forEach((modelUri) => {
|
||||
let model = visibleModels[modelUri].model;
|
||||
let codeEditors = visibleModels[modelUri].codeEditors;
|
||||
|
||||
if (!this._model2TextEditors[modelUri]) {
|
||||
this._model2TextEditors[modelUri] = [];
|
||||
}
|
||||
let existingTextEditors = this._model2TextEditors[modelUri];
|
||||
|
||||
// Remove text editors if more exist
|
||||
while (existingTextEditors.length > codeEditors.length) {
|
||||
let removedTextEditor = existingTextEditors.pop();
|
||||
this._onTextEditorRemove.fire(removedTextEditor);
|
||||
removedTextEditor.dispose();
|
||||
}
|
||||
|
||||
// Adjust remaining text editors
|
||||
for (let i = 0; i < existingTextEditors.length; i++) {
|
||||
existingTextEditors[i].setCodeEditor(codeEditors[i]);
|
||||
}
|
||||
|
||||
// Create new editors as needed
|
||||
for (let i = existingTextEditors.length; i < codeEditors.length; i++) {
|
||||
let newTextEditor = new MainThreadTextEditor(MainThreadEditorsTracker._Ids.nextId(), model, codeEditors[i], this._focusTracker, this._modelService);
|
||||
existingTextEditors.push(newTextEditor);
|
||||
this._onTextEditorAdd.fire(newTextEditor);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle all not visible models
|
||||
allModels.forEach((model) => {
|
||||
let modelUri = model.uri.toString();
|
||||
|
||||
if (visibleModels[modelUri]) {
|
||||
// model is visible, already handled above
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._model2TextEditors[modelUri]) {
|
||||
this._model2TextEditors[modelUri] = [];
|
||||
}
|
||||
let existingTextEditors = this._model2TextEditors[modelUri];
|
||||
|
||||
// Remove extra text editors
|
||||
while (existingTextEditors.length > 1) {
|
||||
let removedTextEditor = existingTextEditors.pop();
|
||||
this._onTextEditorRemove.fire(removedTextEditor);
|
||||
removedTextEditor.dispose();
|
||||
}
|
||||
|
||||
// Create new editor if needed or adjust it
|
||||
if (existingTextEditors.length === 0) {
|
||||
let newTextEditor = new MainThreadTextEditor(MainThreadEditorsTracker._Ids.nextId(), model, null, this._focusTracker, this._modelService);
|
||||
existingTextEditors.push(newTextEditor);
|
||||
this._onTextEditorAdd.fire(newTextEditor);
|
||||
} else {
|
||||
existingTextEditors[0].setCodeEditor(null);
|
||||
}
|
||||
});
|
||||
|
||||
this._printState();
|
||||
|
||||
this._visibleTextEditorIds = this._findVisibleTextEditorIds();
|
||||
|
||||
this._updateFocusedTextEditor();
|
||||
|
||||
// this is a sync event
|
||||
this._onDidUpdateTextEditors.fire(undefined);
|
||||
this._textEditorsListenersMap[id] = toDispose;
|
||||
this._textEditorsMap[id] = textEditor;
|
||||
}
|
||||
|
||||
private _updateFocusedTextEditor(): void {
|
||||
this._setFocusedTextEditorId(this._findFocusedTextEditorId());
|
||||
private _onTextEditorRemove(textEditor: MainThreadTextEditor): void {
|
||||
let id = textEditor.getId();
|
||||
dispose(this._textEditorsListenersMap[id]);
|
||||
delete this._textEditorsListenersMap[id];
|
||||
delete this._textEditorsMap[id];
|
||||
this._proxy._acceptTextEditorRemove(id);
|
||||
}
|
||||
|
||||
private _findFocusedTextEditorId(): string {
|
||||
let modelUris = Object.keys(this._model2TextEditors);
|
||||
for (let i = 0, len = modelUris.length; i < len; i++) {
|
||||
let editors = this._model2TextEditors[modelUris[i]];
|
||||
for (let j = 0, lenJ = editors.length; j < lenJ; j++) {
|
||||
if (editors[j].isFocused()) {
|
||||
return editors[j].getId();
|
||||
private _updateActiveAndVisibleTextEditors(): void {
|
||||
|
||||
// active and visible editors
|
||||
let visibleEditors = this._editorTracker.getVisibleTextEditorIds();
|
||||
let activeEditor = this._findActiveTextEditorId();
|
||||
if (activeEditor !== this._activeTextEditor || !arrayEquals(this._visibleEditors, visibleEditors, (a, b) => a === b)) {
|
||||
this._activeTextEditor = activeEditor;
|
||||
this._visibleEditors = visibleEditors;
|
||||
this._proxy._acceptActiveEditorAndVisibleEditors(this._activeTextEditor, this._visibleEditors);
|
||||
}
|
||||
|
||||
// editor columns
|
||||
let editorPositionData = this._getTextEditorPositionData();
|
||||
if (!objectEquals(this._editorPositionData, editorPositionData)) {
|
||||
this._editorPositionData = editorPositionData;
|
||||
this._proxy._acceptEditorPositionData(this._editorPositionData);
|
||||
}
|
||||
}
|
||||
|
||||
private _findActiveTextEditorId(): string {
|
||||
let focusedTextEditorId = this._editorTracker.getFocusedTextEditorId();
|
||||
if (focusedTextEditorId) {
|
||||
return focusedTextEditorId;
|
||||
}
|
||||
|
||||
let activeEditor = this._workbenchEditorService.getActiveEditor();
|
||||
if (!activeEditor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let editor = <IEditor>activeEditor.getControl();
|
||||
// Substitute for (editor instanceof ICodeEditor)
|
||||
if (!editor || typeof editor.getEditorType !== 'function') {
|
||||
// Not a text editor...
|
||||
return null;
|
||||
}
|
||||
|
||||
if (editor.getEditorType() === EditorType.ICodeEditor) {
|
||||
return this._editorTracker.findTextEditorIdFor(<ICommonCodeEditor>editor);
|
||||
}
|
||||
|
||||
// Must be a diff editor => use the modified side
|
||||
return this._editorTracker.findTextEditorIdFor((<ICommonDiffEditor>editor).getModifiedEditor());
|
||||
}
|
||||
|
||||
private _findEditorPosition(editor: MainThreadTextEditor): EditorPosition {
|
||||
for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) {
|
||||
if (editor.matches(workbenchEditor)) {
|
||||
return workbenchEditor.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getTextEditorPositionData(): ITextEditorPositionData {
|
||||
let result: ITextEditorPositionData = Object.create(null);
|
||||
for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) {
|
||||
let editor = <IEditor>workbenchEditor.getControl();
|
||||
// Substitute for (editor instanceof ICodeEditor)
|
||||
if (!editor || typeof editor.getEditorType !== 'function') {
|
||||
// Not a text editor...
|
||||
continue;
|
||||
}
|
||||
if (editor.getEditorType() === EditorType.ICodeEditor) {
|
||||
let id = this._editorTracker.findTextEditorIdFor(<ICommonCodeEditor>editor);
|
||||
if (id) {
|
||||
result[id] = workbenchEditor.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private _findVisibleTextEditorIds(): string[] {
|
||||
let result = [];
|
||||
let modelUris = Object.keys(this._model2TextEditors);
|
||||
for (let i = 0, len = modelUris.length; i < len; i++) {
|
||||
let editors = this._model2TextEditors[modelUris[i]];
|
||||
for (let j = 0, lenJ = editors.length; j < lenJ; j++) {
|
||||
if (editors[j].isVisible()) {
|
||||
result.push(editors[j].getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
result.sort();
|
||||
return result;
|
||||
}
|
||||
|
||||
private _setFocusedTextEditorId(focusedTextEditorId:string): void {
|
||||
if (this._focusedTextEditorId === focusedTextEditorId) {
|
||||
// no change
|
||||
return;
|
||||
}
|
||||
// --- from extension host process
|
||||
|
||||
this._focusedTextEditorId = focusedTextEditorId;
|
||||
this._printState();
|
||||
this._onDidChangeFocusedTextEditor.fire(this._focusedTextEditorId);
|
||||
}
|
||||
_tryShowTextDocument(resource: URI, position: EditorPosition, preserveFocus: boolean): TPromise<string> {
|
||||
|
||||
const input = {
|
||||
resource,
|
||||
options: { preserveFocus }
|
||||
};
|
||||
|
||||
private _printState(): void {
|
||||
// console.log('----------------------');
|
||||
// Object.keys(this._model2TextEditors).forEach((modelUri) => {
|
||||
// let editors = this._model2TextEditors[modelUri];
|
||||
return this._workbenchEditorService.openEditor(input, position).then(editor => {
|
||||
|
||||
// console.log(editors.map((e) => {
|
||||
// return e.getId() + " (" + (e.getId() === this._focusedTextEditorId ? 'FOCUSED, ': '') + modelUri + ")";
|
||||
// }).join('\n'));
|
||||
// });
|
||||
}
|
||||
|
||||
private _getVisibleModels(): IVisibleModels {
|
||||
let r: IVisibleModels = {};
|
||||
|
||||
let allCodeEditors = this._codeEditorService.listCodeEditors();
|
||||
|
||||
// Maintain a certain sorting such that the mapping doesn't change too much all the time
|
||||
allCodeEditors.sort((a, b) => strcmp(a.getId(), b.getId()));
|
||||
|
||||
allCodeEditors.forEach((codeEditor) => {
|
||||
let model = codeEditor.getModel();
|
||||
if (!model || model.isTooLargeForHavingARichMode()) {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let modelUri = model.uri.toString();
|
||||
r[modelUri] = r[modelUri] || {
|
||||
model: model,
|
||||
codeEditors: []
|
||||
};
|
||||
r[modelUri].codeEditors.push(codeEditor);
|
||||
return new TPromise<void>(c => {
|
||||
// not very nice but the way it is: changes to the editor state aren't
|
||||
// send to the ext host as they happen but stuff is delayed a little. in
|
||||
// order to provide the real editor on #openTextEditor we need to sync on
|
||||
// that update
|
||||
let subscription: IDisposable;
|
||||
let handle: number;
|
||||
function contd() {
|
||||
subscription.dispose();
|
||||
clearTimeout(handle);
|
||||
c(undefined);
|
||||
}
|
||||
subscription = this._editorTracker.onDidUpdateTextEditors(() => {
|
||||
contd();
|
||||
});
|
||||
handle = setTimeout(() => {
|
||||
contd();
|
||||
}, 1000);
|
||||
|
||||
}).then(() => {
|
||||
// find the editor we have just opened and return the
|
||||
// id we have assigned to it.
|
||||
for (let id in this._textEditorsMap) {
|
||||
if (this._textEditorsMap[id].matches(editor)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public getFocusedTextEditorId(): string {
|
||||
return this._focusedTextEditorId;
|
||||
_tryShowEditor(id: string, position: EditorPosition): TPromise<void> {
|
||||
// check how often this is used
|
||||
this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.show' });
|
||||
|
||||
let mainThreadEditor = this._textEditorsMap[id];
|
||||
if (mainThreadEditor) {
|
||||
let model = mainThreadEditor.getModel();
|
||||
return this._workbenchEditorService.openEditor({
|
||||
resource: model.uri,
|
||||
options: { preserveFocus: false }
|
||||
}, position).then(() => { return; });
|
||||
}
|
||||
}
|
||||
|
||||
public getVisibleTextEditorIds(): string[] {
|
||||
return this._visibleTextEditorIds;
|
||||
}
|
||||
_tryHideEditor(id: string): TPromise<void> {
|
||||
// check how often this is used
|
||||
this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.hide' });
|
||||
|
||||
public get onTextEditorAdd(): Event<MainThreadTextEditor> {
|
||||
return this._onTextEditorAdd.event;
|
||||
}
|
||||
|
||||
public get onTextEditorRemove(): Event<MainThreadTextEditor> {
|
||||
return this._onTextEditorRemove.event;
|
||||
}
|
||||
|
||||
public get onDidUpdateTextEditors(): Event<void> {
|
||||
return this._onDidUpdateTextEditors.event;
|
||||
}
|
||||
|
||||
public get onChangedFocusedTextEditor(): Event<string> {
|
||||
return this._onDidChangeFocusedTextEditor.event;
|
||||
}
|
||||
|
||||
public findTextEditorIdFor(codeEditor:EditorCommon.ICommonCodeEditor): string {
|
||||
let modelUris = Object.keys(this._model2TextEditors);
|
||||
for (let i = 0, len = modelUris.length; i < len; i++) {
|
||||
let editors = this._model2TextEditors[modelUris[i]];
|
||||
for (let j = 0, lenJ = editors.length; j < lenJ; j++) {
|
||||
if (editors[j].hasCodeEditor(codeEditor)) {
|
||||
return editors[j].getId();
|
||||
let mainThreadEditor = this._textEditorsMap[id];
|
||||
if (mainThreadEditor) {
|
||||
let editors = this._workbenchEditorService.getVisibleEditors();
|
||||
for (let editor of editors) {
|
||||
if (mainThreadEditor.matches(editor)) {
|
||||
return this._workbenchEditorService.closeEditor(editor.position, editor.input).then(() => { return; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public registerTextEditorDecorationType(key:string, options: EditorCommon.IDecorationRenderOptions): void {
|
||||
this._codeEditorService.registerDecorationType(key, options);
|
||||
_trySetSelections(id: string, selections: ISelection[]): TPromise<any> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
this._textEditorsMap[id].setSelections(selections);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public removeTextEditorDecorationType(key:string): void {
|
||||
this._codeEditorService.removeDecorationType(key);
|
||||
_trySetDecorations(id: string, key: string, ranges: IDecorationOptions[]): TPromise<any> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
this._textEditorsMap[id].setDecorations(key, ranges);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
_tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
this._textEditorsMap[id].revealRange(range, revealType);
|
||||
}
|
||||
|
||||
_trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<any> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
this._textEditorsMap[id].setConfiguration(options);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
_tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], setEndOfLine:EndOfLine): TPromise<boolean> {
|
||||
if (!this._textEditorsMap[id]) {
|
||||
return TPromise.wrapError('TextEditor disposed');
|
||||
}
|
||||
return TPromise.as(this._textEditorsMap[id].applyEdits(modelVersionId, edits, setEndOfLine));
|
||||
}
|
||||
|
||||
_registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {
|
||||
this._editorTracker.registerTextEditorDecorationType(key, options);
|
||||
}
|
||||
|
||||
_removeTextEditorDecorationType(key: string): void {
|
||||
this._editorTracker.removeTextEditorDecorationType(key);
|
||||
}
|
||||
}
|
||||
|
||||
interface IVisibleModels {
|
||||
[modelUri:string]: {
|
||||
model: EditorCommon.IModel;
|
||||
codeEditors: EditorCommon.ICommonCodeEditor[];
|
||||
};
|
||||
}
|
||||
|
||||
function strcmp(a:string, b:string): number {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
if (a > b) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
659
src/vs/workbench/api/node/mainThreadEditorsTracker.ts
Normal file
659
src/vs/workbench/api/node/mainThreadEditorsTracker.ts
Normal file
@@ -0,0 +1,659 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {IEditor} from 'vs/platform/editor/common/editor';
|
||||
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
|
||||
import {IModelService} from 'vs/editor/common/services/modelService';
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {RunOnceScheduler} from 'vs/base/common/async';
|
||||
import {IdGenerator} from 'vs/base/common/idGenerator';
|
||||
import {Range} from 'vs/editor/common/core/range';
|
||||
import {Selection} from 'vs/editor/common/core/selection';
|
||||
import {EndOfLine} from 'vs/workbench/api/node/extHostTypes';
|
||||
|
||||
export interface ITextEditorConfigurationUpdate {
|
||||
tabSize?: number | string;
|
||||
insertSpaces?: boolean | string;
|
||||
cursorStyle?: EditorCommon.TextEditorCursorStyle;
|
||||
}
|
||||
export interface IResolvedTextEditorConfiguration {
|
||||
tabSize: number;
|
||||
insertSpaces: boolean;
|
||||
cursorStyle: EditorCommon.TextEditorCursorStyle;
|
||||
}
|
||||
|
||||
function configurationsEqual(a:IResolvedTextEditorConfiguration, b:IResolvedTextEditorConfiguration) {
|
||||
if (a && !b || !a && b) {
|
||||
return false;
|
||||
}
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
a.tabSize === b.tabSize
|
||||
&& a.insertSpaces === b.insertSpaces
|
||||
);
|
||||
}
|
||||
|
||||
export interface IFocusTracker {
|
||||
onGainedFocus(): void;
|
||||
onLostFocus(): void;
|
||||
}
|
||||
|
||||
export enum TextEditorRevealType {
|
||||
Default = 0,
|
||||
InCenter = 1,
|
||||
InCenterIfOutsideViewport = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Text Editor that is permanently bound to the same model.
|
||||
* It can be bound or not to a CodeEditor.
|
||||
*/
|
||||
export class MainThreadTextEditor {
|
||||
|
||||
private _id: string;
|
||||
private _model: EditorCommon.IModel;
|
||||
private _modelService: IModelService;
|
||||
private _modelListeners: IDisposable[];
|
||||
private _codeEditor: EditorCommon.ICommonCodeEditor;
|
||||
private _focusTracker: IFocusTracker;
|
||||
private _codeEditorListeners: IDisposable[];
|
||||
|
||||
private _lastSelection: Selection[];
|
||||
private _configuration: IResolvedTextEditorConfiguration;
|
||||
|
||||
private _onSelectionChanged: Emitter<Selection[]>;
|
||||
private _onConfigurationChanged: Emitter<IResolvedTextEditorConfiguration>;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
model:EditorCommon.IModel,
|
||||
codeEditor:EditorCommon.ICommonCodeEditor,
|
||||
focusTracker:IFocusTracker,
|
||||
modelService: IModelService
|
||||
) {
|
||||
this._id = id;
|
||||
this._model = model;
|
||||
this._codeEditor = null;
|
||||
this._focusTracker = focusTracker;
|
||||
this._modelService = modelService;
|
||||
this._codeEditorListeners = [];
|
||||
|
||||
this._onSelectionChanged = new Emitter<Selection[]>();
|
||||
this._onConfigurationChanged = new Emitter<IResolvedTextEditorConfiguration>();
|
||||
|
||||
this._lastSelection = [ new Selection(1,1,1,1) ];
|
||||
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
|
||||
this._modelListeners = [];
|
||||
this._modelListeners.push(this._model.onDidChangeOptions((e) => {
|
||||
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
|
||||
}));
|
||||
|
||||
this.setCodeEditor(codeEditor);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._model = null;
|
||||
this._modelListeners = dispose(this._modelListeners);
|
||||
this._codeEditor = null;
|
||||
this._codeEditorListeners = dispose(this._codeEditorListeners);
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public getModel(): EditorCommon.IModel {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public hasCodeEditor(codeEditor:EditorCommon.ICommonCodeEditor): boolean {
|
||||
return (this._codeEditor === codeEditor);
|
||||
}
|
||||
|
||||
public setCodeEditor(codeEditor:EditorCommon.ICommonCodeEditor): void {
|
||||
if (this.hasCodeEditor(codeEditor)) {
|
||||
// Nothing to do...
|
||||
return;
|
||||
}
|
||||
this._codeEditorListeners = dispose(this._codeEditorListeners);
|
||||
|
||||
this._codeEditor = codeEditor;
|
||||
if (this._codeEditor) {
|
||||
|
||||
// Catch early the case that this code editor gets a different model set and disassociate from this model
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidChangeModel(() => {
|
||||
this.setCodeEditor(null);
|
||||
}));
|
||||
|
||||
let forwardSelection = () => {
|
||||
this._lastSelection = this._codeEditor.getSelections();
|
||||
this._onSelectionChanged.fire(this._lastSelection);
|
||||
};
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidChangeCursorSelection(forwardSelection));
|
||||
if (!Selection.selectionsArrEqual(this._lastSelection, this._codeEditor.getSelections())) {
|
||||
forwardSelection();
|
||||
}
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidFocusEditor(() => {
|
||||
this._focusTracker.onGainedFocus();
|
||||
}));
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidBlurEditor(() => {
|
||||
this._focusTracker.onLostFocus();
|
||||
}));
|
||||
this._codeEditorListeners.push(this._codeEditor.onDidChangeConfiguration(() => {
|
||||
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
|
||||
}));
|
||||
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
|
||||
}
|
||||
}
|
||||
|
||||
public isVisible(): boolean {
|
||||
return !!this._codeEditor;
|
||||
}
|
||||
|
||||
public get onSelectionChanged(): Event<Selection[]> {
|
||||
return this._onSelectionChanged.event;
|
||||
}
|
||||
|
||||
public get onConfigurationChanged(): Event<IResolvedTextEditorConfiguration> {
|
||||
return this._onConfigurationChanged.event;
|
||||
}
|
||||
|
||||
public getSelections(): Selection[] {
|
||||
if (this._codeEditor) {
|
||||
return this._codeEditor.getSelections();
|
||||
}
|
||||
return this._lastSelection;
|
||||
}
|
||||
|
||||
public setSelections(selections:EditorCommon.ISelection[]): void {
|
||||
if (this._codeEditor) {
|
||||
this._codeEditor.setSelections(selections);
|
||||
return;
|
||||
}
|
||||
this._lastSelection = selections.map(Selection.liftSelection);
|
||||
console.warn('setSelections on invisble editor');
|
||||
}
|
||||
|
||||
public getConfiguration(): IResolvedTextEditorConfiguration {
|
||||
return this._configuration;
|
||||
}
|
||||
|
||||
private _setIndentConfiguration(newConfiguration:ITextEditorConfigurationUpdate): void {
|
||||
if (newConfiguration.tabSize === 'auto' || newConfiguration.insertSpaces === 'auto') {
|
||||
// one of the options was set to 'auto' => detect indentation
|
||||
|
||||
let creationOpts = this._modelService.getCreationOptions();
|
||||
let insertSpaces = creationOpts.insertSpaces;
|
||||
let tabSize = creationOpts.tabSize;
|
||||
|
||||
if (newConfiguration.insertSpaces !== 'auto') {
|
||||
if (typeof newConfiguration.insertSpaces !== 'undefined') {
|
||||
insertSpaces = (newConfiguration.insertSpaces === 'false' ? false : Boolean(newConfiguration.insertSpaces));
|
||||
}
|
||||
}
|
||||
if (newConfiguration.tabSize !== 'auto') {
|
||||
if (typeof newConfiguration.tabSize !== 'undefined') {
|
||||
let parsedTabSize = parseInt(<string>newConfiguration.tabSize, 10);
|
||||
if (!isNaN(parsedTabSize)) {
|
||||
tabSize = parsedTabSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._model.detectIndentation(insertSpaces, tabSize);
|
||||
return;
|
||||
}
|
||||
|
||||
let newOpts: EditorCommon.ITextModelUpdateOptions = {};
|
||||
if (typeof newConfiguration.insertSpaces !== 'undefined') {
|
||||
newOpts.insertSpaces = (newConfiguration.insertSpaces === 'false' ? false : Boolean(newConfiguration.insertSpaces));
|
||||
}
|
||||
if (typeof newConfiguration.tabSize !== 'undefined') {
|
||||
let parsedTabSize = parseInt(<string>newConfiguration.tabSize, 10);
|
||||
if (!isNaN(parsedTabSize)) {
|
||||
newOpts.tabSize = parsedTabSize;
|
||||
}
|
||||
}
|
||||
this._model.updateOptions(newOpts);
|
||||
}
|
||||
|
||||
public setConfiguration(newConfiguration:ITextEditorConfigurationUpdate): void {
|
||||
this._setIndentConfiguration(newConfiguration);
|
||||
|
||||
if (newConfiguration.cursorStyle) {
|
||||
let newCursorStyle = EditorCommon.cursorStyleToString(newConfiguration.cursorStyle);
|
||||
|
||||
if (!this._codeEditor) {
|
||||
console.warn('setConfiguration on invisible editor');
|
||||
return;
|
||||
}
|
||||
|
||||
this._codeEditor.updateOptions({
|
||||
cursorStyle: newCursorStyle
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public setDecorations(key: string, ranges:EditorCommon.IDecorationOptions[]): void {
|
||||
if (!this._codeEditor) {
|
||||
console.warn('setDecorations on invisible editor');
|
||||
return;
|
||||
}
|
||||
this._codeEditor.setDecorations(key, ranges);
|
||||
}
|
||||
|
||||
public revealRange(range:EditorCommon.IRange, revealType:TextEditorRevealType): void {
|
||||
if (!this._codeEditor) {
|
||||
console.warn('revealRange on invisible editor');
|
||||
return;
|
||||
}
|
||||
if (revealType === TextEditorRevealType.Default) {
|
||||
this._codeEditor.revealRange(range);
|
||||
} else if (revealType === TextEditorRevealType.InCenter) {
|
||||
this._codeEditor.revealRangeInCenter(range);
|
||||
} else if (revealType === TextEditorRevealType.InCenterIfOutsideViewport) {
|
||||
this._codeEditor.revealRangeInCenterIfOutsideViewport(range);
|
||||
} else {
|
||||
console.warn('Unknown revealType');
|
||||
}
|
||||
}
|
||||
|
||||
private _readConfiguration(model:EditorCommon.IModel, codeEditor:EditorCommon.ICommonCodeEditor): IResolvedTextEditorConfiguration {
|
||||
if (model.isDisposed()) {
|
||||
// shutdown time
|
||||
return this._configuration;
|
||||
}
|
||||
let cursorStyle = this._configuration ? this._configuration.cursorStyle : EditorCommon.TextEditorCursorStyle.Line;
|
||||
if (codeEditor) {
|
||||
let codeEditorOpts = codeEditor.getConfiguration();
|
||||
cursorStyle = codeEditorOpts.viewInfo.cursorStyle;
|
||||
}
|
||||
|
||||
let indent = model.getOptions();
|
||||
return {
|
||||
insertSpaces: indent.insertSpaces,
|
||||
tabSize: indent.tabSize,
|
||||
cursorStyle: cursorStyle
|
||||
};
|
||||
}
|
||||
|
||||
private _setConfiguration(newConfiguration:IResolvedTextEditorConfiguration): void {
|
||||
if (configurationsEqual(this._configuration, newConfiguration)) {
|
||||
return;
|
||||
}
|
||||
this._configuration = newConfiguration;
|
||||
this._onConfigurationChanged.fire(this._configuration);
|
||||
}
|
||||
|
||||
public isFocused(): boolean {
|
||||
if (this._codeEditor) {
|
||||
return this._codeEditor.isFocused();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public matches(editor: IEditor): boolean {
|
||||
if (!editor) {
|
||||
return false;
|
||||
}
|
||||
return editor.getControl() === this._codeEditor;
|
||||
}
|
||||
|
||||
public applyEdits(versionIdCheck:number, edits:EditorCommon.ISingleEditOperation[], setEndOfLine:EndOfLine): boolean {
|
||||
if (this._model.getVersionId() !== versionIdCheck) {
|
||||
console.warn('Model has changed in the meantime!');
|
||||
// throw new Error('Model has changed in the meantime!');
|
||||
// model changed in the meantime
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._codeEditor) {
|
||||
if (setEndOfLine === EndOfLine.CRLF) {
|
||||
this._model.setEOL(EditorCommon.EndOfLineSequence.CRLF);
|
||||
} else if (setEndOfLine === EndOfLine.LF) {
|
||||
this._model.setEOL(EditorCommon.EndOfLineSequence.LF);
|
||||
}
|
||||
|
||||
let transformedEdits = edits.map((edit): EditorCommon.IIdentifiedSingleEditOperation => {
|
||||
return {
|
||||
identifier: null,
|
||||
range: Range.lift(edit.range),
|
||||
text: edit.text,
|
||||
forceMoveMarkers: edit.forceMoveMarkers
|
||||
};
|
||||
});
|
||||
return this._codeEditor.executeEdits('MainThreadTextEditor', transformedEdits) || true;
|
||||
}
|
||||
|
||||
console.warn('applyEdits on invisible editor');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps track of what goes on in the main thread and maps models => text editors
|
||||
*/
|
||||
export class MainThreadEditorsTracker {
|
||||
|
||||
private static _Ids = new IdGenerator('');
|
||||
|
||||
private _toDispose: IDisposable[];
|
||||
private _codeEditorService: ICodeEditorService;
|
||||
private _modelService: IModelService;
|
||||
private _updateMapping: RunOnceScheduler;
|
||||
private _editorModelChangeListeners: {[editorId:string]:IDisposable;};
|
||||
|
||||
private _model2TextEditors: {
|
||||
[modelUri:string]: MainThreadTextEditor[];
|
||||
};
|
||||
private _focusedTextEditorId: string;
|
||||
private _visibleTextEditorIds: string[];
|
||||
private _onTextEditorAdd: Emitter<MainThreadTextEditor>;
|
||||
private _onTextEditorRemove: Emitter<MainThreadTextEditor>;
|
||||
private _onDidChangeFocusedTextEditor: Emitter<string>;
|
||||
private _onDidUpdateTextEditors: Emitter<void>;
|
||||
|
||||
private _focusTracker: IFocusTracker;
|
||||
|
||||
constructor(
|
||||
editorService:ICodeEditorService,
|
||||
modelService:IModelService
|
||||
) {
|
||||
this._codeEditorService = editorService;
|
||||
this._modelService = modelService;
|
||||
this._toDispose = [];
|
||||
this._focusedTextEditorId = null;
|
||||
this._visibleTextEditorIds = [];
|
||||
this._editorModelChangeListeners = Object.create(null);
|
||||
this._model2TextEditors = Object.create(null);
|
||||
this._onTextEditorAdd = new Emitter<MainThreadTextEditor>();
|
||||
this._onTextEditorRemove = new Emitter<MainThreadTextEditor>();
|
||||
this._onDidUpdateTextEditors = new Emitter<void>();
|
||||
this._onDidChangeFocusedTextEditor = new Emitter<string>();
|
||||
this._focusTracker = {
|
||||
onGainedFocus: () => this._updateFocusedTextEditor(),
|
||||
onLostFocus: () => this._updateFocusedTextEditor()
|
||||
};
|
||||
|
||||
this._modelService.onModelAdded(this._onModelAdded, this, this._toDispose);
|
||||
this._modelService.onModelRemoved(this._onModelRemoved, this, this._toDispose);
|
||||
|
||||
this._codeEditorService.onCodeEditorAdd(this._onCodeEditorAdd, this, this._toDispose);
|
||||
this._codeEditorService.onCodeEditorRemove(this._onCodeEditorRemove, this, this._toDispose);
|
||||
|
||||
this._updateMapping = new RunOnceScheduler(() => this._doUpdateMapping(), 0);
|
||||
this._toDispose.push(this._updateMapping);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
|
||||
private _onModelAdded(model: EditorCommon.IModel): void {
|
||||
this._updateMapping.schedule();
|
||||
}
|
||||
|
||||
private _onModelRemoved(model: EditorCommon.IModel): void {
|
||||
this._updateMapping.schedule();
|
||||
}
|
||||
|
||||
private _onCodeEditorAdd(codeEditor: EditorCommon.ICommonCodeEditor): void {
|
||||
this._editorModelChangeListeners[codeEditor.getId()] = codeEditor.onDidChangeModel(_ => this._updateMapping.schedule());
|
||||
this._updateMapping.schedule();
|
||||
}
|
||||
|
||||
private _onCodeEditorRemove(codeEditor: EditorCommon.ICommonCodeEditor): void {
|
||||
this._editorModelChangeListeners[codeEditor.getId()].dispose();
|
||||
delete this._editorModelChangeListeners[codeEditor.getId()];
|
||||
this._updateMapping.schedule();
|
||||
}
|
||||
|
||||
private _doUpdateMapping(): void {
|
||||
let allModels = this._modelService.getModels();
|
||||
// Same filter as in extHostDocuments
|
||||
allModels = allModels.filter((model) => !model.isTooLargeForHavingARichMode());
|
||||
let allModelsMap: { [modelUri:string]: EditorCommon.IModel; } = Object.create(null);
|
||||
allModels.forEach((model) => {
|
||||
allModelsMap[model.uri.toString()] = model;
|
||||
});
|
||||
|
||||
// Remove text editors for models that no longer exist
|
||||
Object.keys(this._model2TextEditors).forEach((modelUri) => {
|
||||
if (allModelsMap[modelUri]) {
|
||||
// model still exists, will be updated below
|
||||
return;
|
||||
}
|
||||
|
||||
let textEditorsToRemove = this._model2TextEditors[modelUri];
|
||||
delete this._model2TextEditors[modelUri];
|
||||
|
||||
for (let i = 0; i < textEditorsToRemove.length; i++) {
|
||||
this._onTextEditorRemove.fire(textEditorsToRemove[i]);
|
||||
textEditorsToRemove[i].dispose();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle all visible models
|
||||
let visibleModels = this._getVisibleModels();
|
||||
Object.keys(visibleModels).forEach((modelUri) => {
|
||||
let model = visibleModels[modelUri].model;
|
||||
let codeEditors = visibleModels[modelUri].codeEditors;
|
||||
|
||||
if (!this._model2TextEditors[modelUri]) {
|
||||
this._model2TextEditors[modelUri] = [];
|
||||
}
|
||||
let existingTextEditors = this._model2TextEditors[modelUri];
|
||||
|
||||
// Remove text editors if more exist
|
||||
while (existingTextEditors.length > codeEditors.length) {
|
||||
let removedTextEditor = existingTextEditors.pop();
|
||||
this._onTextEditorRemove.fire(removedTextEditor);
|
||||
removedTextEditor.dispose();
|
||||
}
|
||||
|
||||
// Adjust remaining text editors
|
||||
for (let i = 0; i < existingTextEditors.length; i++) {
|
||||
existingTextEditors[i].setCodeEditor(codeEditors[i]);
|
||||
}
|
||||
|
||||
// Create new editors as needed
|
||||
for (let i = existingTextEditors.length; i < codeEditors.length; i++) {
|
||||
let newTextEditor = new MainThreadTextEditor(MainThreadEditorsTracker._Ids.nextId(), model, codeEditors[i], this._focusTracker, this._modelService);
|
||||
existingTextEditors.push(newTextEditor);
|
||||
this._onTextEditorAdd.fire(newTextEditor);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle all not visible models
|
||||
allModels.forEach((model) => {
|
||||
let modelUri = model.uri.toString();
|
||||
|
||||
if (visibleModels[modelUri]) {
|
||||
// model is visible, already handled above
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._model2TextEditors[modelUri]) {
|
||||
this._model2TextEditors[modelUri] = [];
|
||||
}
|
||||
let existingTextEditors = this._model2TextEditors[modelUri];
|
||||
|
||||
// Remove extra text editors
|
||||
while (existingTextEditors.length > 1) {
|
||||
let removedTextEditor = existingTextEditors.pop();
|
||||
this._onTextEditorRemove.fire(removedTextEditor);
|
||||
removedTextEditor.dispose();
|
||||
}
|
||||
|
||||
// Create new editor if needed or adjust it
|
||||
if (existingTextEditors.length === 0) {
|
||||
let newTextEditor = new MainThreadTextEditor(MainThreadEditorsTracker._Ids.nextId(), model, null, this._focusTracker, this._modelService);
|
||||
existingTextEditors.push(newTextEditor);
|
||||
this._onTextEditorAdd.fire(newTextEditor);
|
||||
} else {
|
||||
existingTextEditors[0].setCodeEditor(null);
|
||||
}
|
||||
});
|
||||
|
||||
this._printState();
|
||||
|
||||
this._visibleTextEditorIds = this._findVisibleTextEditorIds();
|
||||
|
||||
this._updateFocusedTextEditor();
|
||||
|
||||
// this is a sync event
|
||||
this._onDidUpdateTextEditors.fire(undefined);
|
||||
}
|
||||
|
||||
private _updateFocusedTextEditor(): void {
|
||||
this._setFocusedTextEditorId(this._findFocusedTextEditorId());
|
||||
}
|
||||
|
||||
private _findFocusedTextEditorId(): string {
|
||||
let modelUris = Object.keys(this._model2TextEditors);
|
||||
for (let i = 0, len = modelUris.length; i < len; i++) {
|
||||
let editors = this._model2TextEditors[modelUris[i]];
|
||||
for (let j = 0, lenJ = editors.length; j < lenJ; j++) {
|
||||
if (editors[j].isFocused()) {
|
||||
return editors[j].getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private _findVisibleTextEditorIds(): string[] {
|
||||
let result = [];
|
||||
let modelUris = Object.keys(this._model2TextEditors);
|
||||
for (let i = 0, len = modelUris.length; i < len; i++) {
|
||||
let editors = this._model2TextEditors[modelUris[i]];
|
||||
for (let j = 0, lenJ = editors.length; j < lenJ; j++) {
|
||||
if (editors[j].isVisible()) {
|
||||
result.push(editors[j].getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
result.sort();
|
||||
return result;
|
||||
}
|
||||
|
||||
private _setFocusedTextEditorId(focusedTextEditorId:string): void {
|
||||
if (this._focusedTextEditorId === focusedTextEditorId) {
|
||||
// no change
|
||||
return;
|
||||
}
|
||||
|
||||
this._focusedTextEditorId = focusedTextEditorId;
|
||||
this._printState();
|
||||
this._onDidChangeFocusedTextEditor.fire(this._focusedTextEditorId);
|
||||
}
|
||||
|
||||
|
||||
private _printState(): void {
|
||||
// console.log('----------------------');
|
||||
// Object.keys(this._model2TextEditors).forEach((modelUri) => {
|
||||
// let editors = this._model2TextEditors[modelUri];
|
||||
|
||||
// console.log(editors.map((e) => {
|
||||
// return e.getId() + " (" + (e.getId() === this._focusedTextEditorId ? 'FOCUSED, ': '') + modelUri + ")";
|
||||
// }).join('\n'));
|
||||
// });
|
||||
}
|
||||
|
||||
private _getVisibleModels(): IVisibleModels {
|
||||
let r: IVisibleModels = {};
|
||||
|
||||
let allCodeEditors = this._codeEditorService.listCodeEditors();
|
||||
|
||||
// Maintain a certain sorting such that the mapping doesn't change too much all the time
|
||||
allCodeEditors.sort((a, b) => strcmp(a.getId(), b.getId()));
|
||||
|
||||
allCodeEditors.forEach((codeEditor) => {
|
||||
let model = codeEditor.getModel();
|
||||
if (!model || model.isTooLargeForHavingARichMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let modelUri = model.uri.toString();
|
||||
r[modelUri] = r[modelUri] || {
|
||||
model: model,
|
||||
codeEditors: []
|
||||
};
|
||||
r[modelUri].codeEditors.push(codeEditor);
|
||||
});
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public getFocusedTextEditorId(): string {
|
||||
return this._focusedTextEditorId;
|
||||
}
|
||||
|
||||
public getVisibleTextEditorIds(): string[] {
|
||||
return this._visibleTextEditorIds;
|
||||
}
|
||||
|
||||
public get onTextEditorAdd(): Event<MainThreadTextEditor> {
|
||||
return this._onTextEditorAdd.event;
|
||||
}
|
||||
|
||||
public get onTextEditorRemove(): Event<MainThreadTextEditor> {
|
||||
return this._onTextEditorRemove.event;
|
||||
}
|
||||
|
||||
public get onDidUpdateTextEditors(): Event<void> {
|
||||
return this._onDidUpdateTextEditors.event;
|
||||
}
|
||||
|
||||
public get onChangedFocusedTextEditor(): Event<string> {
|
||||
return this._onDidChangeFocusedTextEditor.event;
|
||||
}
|
||||
|
||||
public findTextEditorIdFor(codeEditor:EditorCommon.ICommonCodeEditor): string {
|
||||
let modelUris = Object.keys(this._model2TextEditors);
|
||||
for (let i = 0, len = modelUris.length; i < len; i++) {
|
||||
let editors = this._model2TextEditors[modelUris[i]];
|
||||
for (let j = 0, lenJ = editors.length; j < lenJ; j++) {
|
||||
if (editors[j].hasCodeEditor(codeEditor)) {
|
||||
return editors[j].getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public registerTextEditorDecorationType(key:string, options: EditorCommon.IDecorationRenderOptions): void {
|
||||
this._codeEditorService.registerDecorationType(key, options);
|
||||
}
|
||||
|
||||
public removeTextEditorDecorationType(key:string): void {
|
||||
this._codeEditorService.removeDecorationType(key);
|
||||
}
|
||||
}
|
||||
|
||||
interface IVisibleModels {
|
||||
[modelUri:string]: {
|
||||
model: EditorCommon.IModel;
|
||||
codeEditors: EditorCommon.ICommonCodeEditor[];
|
||||
};
|
||||
}
|
||||
|
||||
function strcmp(a:string, b:string): number {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
if (a > b) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
15
src/vs/workbench/api/node/mainThreadErrors.ts
Normal file
15
src/vs/workbench/api/node/mainThreadErrors.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
|
||||
export class MainThreadErrors {
|
||||
|
||||
public onUnexpectedExtHostError(err: any): void {
|
||||
errors.onUnexpectedError(err);
|
||||
}
|
||||
|
||||
}
|
||||
143
src/vs/workbench/api/node/mainThreadExtensionService.ts
Normal file
143
src/vs/workbench/api/node/mainThreadExtensionService.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {AbstractExtensionService, ActivatedExtension} from 'vs/platform/extensions/common/abstractExtensionService';
|
||||
import {IMessage, IExtensionDescription, IExtensionsStatus} from 'vs/platform/extensions/common/extensions';
|
||||
import {ExtensionsRegistry} from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import {IMessageService} from 'vs/platform/message/common/message';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
import {ExtHostExtensionService} from './extHostExtensionService';
|
||||
|
||||
/**
|
||||
* Represents a failed extension in the ext host.
|
||||
*/
|
||||
class MainProcessFailedExtension extends ActivatedExtension {
|
||||
constructor() {
|
||||
super(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an extension that was successfully loaded or an
|
||||
* empty extension in the ext host.
|
||||
*/
|
||||
class MainProcessSuccessExtension extends ActivatedExtension {
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
}
|
||||
|
||||
function messageWithSource(msg:IMessage): string {
|
||||
return (msg.source ? '[' + msg.source + ']: ' : '') + msg.message;
|
||||
}
|
||||
|
||||
export class MainProcessExtensionService extends AbstractExtensionService<ActivatedExtension> {
|
||||
|
||||
private _threadService: IThreadService;
|
||||
private _messageService: IMessageService;
|
||||
private _proxy: ExtHostExtensionService;
|
||||
private _isDev: boolean;
|
||||
private _extensionsStatus: { [id: string]: IExtensionsStatus };
|
||||
|
||||
/**
|
||||
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
|
||||
*/
|
||||
constructor(
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IMessageService messageService: IMessageService
|
||||
) {
|
||||
super(false);
|
||||
let config = contextService.getConfiguration();
|
||||
this._isDev = !config.env.isBuilt || !!config.env.extensionDevelopmentPath;
|
||||
|
||||
this._messageService = messageService;
|
||||
this._threadService = threadService;
|
||||
this._proxy = this._threadService.get(ExtHostContext.ExtHostExtensionService);
|
||||
this._extensionsStatus = {};
|
||||
|
||||
ExtensionsRegistry.handleExtensionPoints((msg) => this._handleMessage(msg));
|
||||
}
|
||||
|
||||
private _handleMessage(msg: IMessage) {
|
||||
this._showMessage(msg.type, messageWithSource(msg));
|
||||
|
||||
if (!this._extensionsStatus[msg.source]) {
|
||||
this._extensionsStatus[msg.source] = { messages: [] };
|
||||
}
|
||||
this._extensionsStatus[msg.source].messages.push(msg);
|
||||
}
|
||||
|
||||
public $localShowMessage(severity: Severity, msg: string): void {
|
||||
let messageShown = false;
|
||||
if (severity === Severity.Error || severity === Severity.Warning) {
|
||||
if (this._isDev) {
|
||||
// Only show nasty intrusive messages if doing extension development.
|
||||
this._messageService.show(severity, msg);
|
||||
messageShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!messageShown) {
|
||||
switch (severity) {
|
||||
case Severity.Error:
|
||||
console.error(msg);
|
||||
break;
|
||||
case Severity.Warning:
|
||||
console.warn(msg);
|
||||
break;
|
||||
default:
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- overwriting AbstractExtensionService
|
||||
|
||||
public getExtensionsStatus(): { [id: string]: IExtensionsStatus } {
|
||||
return this._extensionsStatus;
|
||||
}
|
||||
|
||||
protected _showMessage(severity: Severity, msg: string): void {
|
||||
this._proxy.$localShowMessage(severity, msg);
|
||||
this.$localShowMessage(severity, msg);
|
||||
}
|
||||
|
||||
protected _createFailedExtension(): ActivatedExtension {
|
||||
return new MainProcessFailedExtension();
|
||||
}
|
||||
|
||||
protected _actualActivateExtension(extensionDescription: IExtensionDescription): TPromise<ActivatedExtension> {
|
||||
|
||||
// redirect extension activation to the extension host
|
||||
return this._proxy.$activateExtension(extensionDescription).then(_ => {
|
||||
|
||||
// the extension host calls $onExtensionActivated, where we write to `_activatedExtensions`
|
||||
return this._activatedExtensions[extensionDescription.id];
|
||||
});
|
||||
}
|
||||
|
||||
// -- called by extension host
|
||||
|
||||
public $onExtensionHostReady(extensionDescriptions: IExtensionDescription[], messages: IMessage[]): TPromise<void> {
|
||||
ExtensionsRegistry.registerExtensions(extensionDescriptions);
|
||||
messages.forEach((entry) => this._handleMessage(entry));
|
||||
this._triggerOnReady();
|
||||
return;
|
||||
}
|
||||
|
||||
public $onExtensionActivated(extensionId: string): void {
|
||||
this._activatedExtensions[extensionId] = new MainProcessSuccessExtension();
|
||||
}
|
||||
|
||||
public $onExtensionActivationFailed(extensionId: string): void {
|
||||
this._activatedExtensions[extensionId] = new MainProcessFailedExtension();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {FileChangesEvent, FileChangeType} from 'vs/platform/files/common/files';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IEventService} from 'vs/platform/event/common/event';
|
||||
import {RunOnceScheduler} from 'vs/base/common/async';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
import {FileSystemEvents} from './extHostFileSystemEventService';
|
||||
|
||||
export class MainThreadFileSystemEventService {
|
||||
|
||||
constructor( @IEventService eventService: IEventService, @IThreadService threadService: IThreadService) {
|
||||
|
||||
const proxy = threadService.get(ExtHostContext.ExtHostFileSystemEventService);
|
||||
const events: FileSystemEvents = {
|
||||
created: [],
|
||||
changed: [],
|
||||
deleted: []
|
||||
};
|
||||
|
||||
const scheduler = new RunOnceScheduler(() => {
|
||||
proxy._onFileEvent(events);
|
||||
events.created.length = 0;
|
||||
events.changed.length = 0;
|
||||
events.deleted.length = 0;
|
||||
}, 100);
|
||||
|
||||
eventService.addListener2('files:fileChanges', (event: FileChangesEvent) => {
|
||||
for (let change of event.changes) {
|
||||
switch (change.type) {
|
||||
case FileChangeType.ADDED:
|
||||
events.created.push(change.resource);
|
||||
break;
|
||||
case FileChangeType.UPDATED:
|
||||
events.changed.push(change.resource);
|
||||
break;
|
||||
case FileChangeType.DELETED:
|
||||
events.deleted.push(change.resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
scheduler.schedule();
|
||||
});
|
||||
}
|
||||
}
|
||||
212
src/vs/workbench/api/node/mainThreadLanguageFeatures.ts
Normal file
212
src/vs/workbench/api/node/mainThreadLanguageFeatures.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IDisposable} from 'vs/base/common/lifecycle';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import * as vscode from 'vscode';
|
||||
import {IReadOnlyModel, ISingleEditOperation} from 'vs/editor/common/editorCommon';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import {NavigateTypesSupportRegistry, INavigateTypesSupport, ITypeBearing} from 'vs/workbench/parts/search/common/search';
|
||||
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} from './extHostProtocol';
|
||||
import {ExtHostLanguageFeatures} from './extHostLanguageFeatures';
|
||||
import {LanguageConfigurationRegistry} from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
|
||||
export class MainThreadLanguageFeatures {
|
||||
|
||||
private _proxy: ExtHostLanguageFeatures;
|
||||
private _registrations: { [handle: number]: IDisposable; } = Object.create(null);
|
||||
|
||||
constructor( @IThreadService threadService: IThreadService) {
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostLanguageFeatures);
|
||||
}
|
||||
|
||||
$unregister(handle: number): TPromise<any> {
|
||||
let registration = this._registrations[handle];
|
||||
if (registration) {
|
||||
registration.dispose();
|
||||
delete this._registrations[handle];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- outline
|
||||
|
||||
$registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(selector, <modes.DocumentSymbolProvider>{
|
||||
provideDocumentSymbols: (model:IReadOnlyModel, token: CancellationToken): Thenable<modes.SymbolInformation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentSymbols(handle, model.uri));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- code lens
|
||||
|
||||
$registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.CodeLensProviderRegistry.register(selector, <modes.CodeLensProvider>{
|
||||
provideCodeLenses: (model:IReadOnlyModel, token: CancellationToken): modes.ICodeLensSymbol[] | Thenable<modes.ICodeLensSymbol[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideCodeLenses(handle, model.uri));
|
||||
},
|
||||
resolveCodeLens: (model:IReadOnlyModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): modes.ICodeLensSymbol | Thenable<modes.ICodeLensSymbol> => {
|
||||
return wireCancellationToken(token, this._proxy.$resolveCodeLens(handle, model.uri, codeLens));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- declaration
|
||||
|
||||
$registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DefinitionProviderRegistry.register(selector, <modes.DefinitionProvider>{
|
||||
provideDefinition: (model, position, token): Thenable<modes.Definition> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDefinition(handle, model.uri, position));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- extra info
|
||||
|
||||
$registerHoverProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.HoverProviderRegistry.register(selector, <modes.HoverProvider>{
|
||||
provideHover: (model:IReadOnlyModel, position:EditorPosition, token:CancellationToken): Thenable<modes.Hover> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideHover(handle, model.uri, position));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- occurrences
|
||||
|
||||
$registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DocumentHighlightProviderRegistry.register(selector, <modes.DocumentHighlightProvider>{
|
||||
provideDocumentHighlights: (model: IReadOnlyModel, position: EditorPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentHighlights(handle, model.uri, position));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- references
|
||||
|
||||
$registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.ReferenceProviderRegistry.register(selector, <modes.ReferenceProvider>{
|
||||
provideReferences: (model:IReadOnlyModel, position:EditorPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<modes.Location[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideReferences(handle, model.uri, position, context));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- quick fix
|
||||
|
||||
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
|
||||
provideCodeActions: (model:IReadOnlyModel, range:EditorRange, token: CancellationToken): Thenable<modes.CodeAction[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideCodeActions(handle, model.uri, range));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- formatting
|
||||
|
||||
$registerDocumentFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(selector, <modes.DocumentFormattingEditProvider>{
|
||||
provideDocumentFormattingEdits: (model: IReadOnlyModel, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$registerRangeFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(selector, <modes.DocumentRangeFormattingEditProvider>{
|
||||
provideDocumentRangeFormattingEdits: (model: IReadOnlyModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$registerOnTypeFormattingSupport(handle: number, selector: vscode.DocumentSelector, autoFormatTriggerCharacters: string[]): TPromise<any> {
|
||||
this._registrations[handle] = modes.OnTypeFormattingEditProviderRegistry.register(selector, <modes.OnTypeFormattingEditProvider>{
|
||||
|
||||
autoFormatTriggerCharacters,
|
||||
|
||||
provideOnTypeFormattingEdits: (model: IReadOnlyModel, position: EditorPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideOnTypeFormattingEdits(handle, model.uri, position, ch, options));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- navigate type
|
||||
|
||||
$registerNavigateTypeSupport(handle: number): TPromise<any> {
|
||||
this._registrations[handle] = NavigateTypesSupportRegistry.register(<INavigateTypesSupport>{
|
||||
getNavigateToItems: (search: string): TPromise<ITypeBearing[]> => {
|
||||
return this._proxy.$getNavigateToItems(handle, search);
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- rename
|
||||
|
||||
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.RenameProviderRegistry.register(selector, <modes.RenameProvider>{
|
||||
provideRenameEdits: (model:IReadOnlyModel, position:EditorPosition, newName: string, token: CancellationToken): Thenable<modes.WorkspaceEdit> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- suggest
|
||||
|
||||
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[]): TPromise<any> {
|
||||
this._registrations[handle] = modes.SuggestRegistry.register(selector, <modes.ISuggestSupport>{
|
||||
triggerCharacters: triggerCharacters,
|
||||
shouldAutotriggerSuggest: true,
|
||||
provideCompletionItems: (model:IReadOnlyModel, position:EditorPosition, token:CancellationToken): Thenable<modes.ISuggestResult[]> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position));
|
||||
},
|
||||
resolveCompletionItem: (model:IReadOnlyModel, position:EditorPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable<modes.ISuggestion> => {
|
||||
return wireCancellationToken(token, this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- parameter hints
|
||||
|
||||
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise<any> {
|
||||
this._registrations[handle] = modes.SignatureHelpProviderRegistry.register(selector, <modes.SignatureHelpProvider>{
|
||||
|
||||
signatureHelpTriggerCharacters: triggerCharacter,
|
||||
|
||||
provideSignatureHelp: (model:IReadOnlyModel, position:EditorPosition, token:CancellationToken): Thenable<modes.SignatureHelp> => {
|
||||
return wireCancellationToken(token, this._proxy.$provideSignatureHelp(handle, model.uri, position));
|
||||
}
|
||||
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
$setLanguageConfiguration(handle: number, languageId:string, configuration: vscode.LanguageConfiguration): TPromise<any> {
|
||||
this._registrations[handle] = LanguageConfigurationRegistry.register(languageId, configuration);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
23
src/vs/workbench/api/node/mainThreadLanguages.ts
Normal file
23
src/vs/workbench/api/node/mainThreadLanguages.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IModeService} from 'vs/editor/common/services/modeService';
|
||||
|
||||
export class MainThreadLanguages {
|
||||
|
||||
private _modeService: IModeService;
|
||||
|
||||
constructor(
|
||||
@IModeService modeService: IModeService
|
||||
) {
|
||||
this._modeService = modeService;
|
||||
}
|
||||
|
||||
_getLanguages(): TPromise<string[]> {
|
||||
return TPromise.as(this._modeService.getRegisteredModes());
|
||||
}
|
||||
}
|
||||
59
src/vs/workbench/api/node/mainThreadMessageService.ts
Normal file
59
src/vs/workbench/api/node/mainThreadMessageService.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import {IMessageService} from 'vs/platform/message/common/message';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import {Action} from 'vs/base/common/actions';
|
||||
import {TPromise as Promise} from 'vs/base/common/winjs.base';
|
||||
|
||||
export class MainThreadMessageService {
|
||||
|
||||
private _messageService: IMessageService;
|
||||
|
||||
constructor(@IMessageService messageService:IMessageService) {
|
||||
this._messageService = messageService;
|
||||
}
|
||||
|
||||
$showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number> {
|
||||
|
||||
return new Promise<number>(resolve => {
|
||||
|
||||
let messageHide: Function;
|
||||
let actions: MessageItemAction[] = [];
|
||||
let hasCloseAffordance = false;
|
||||
|
||||
class MessageItemAction extends Action {
|
||||
constructor(id: string, label: string, handle: number) {
|
||||
super(id, label, undefined, true, () => {
|
||||
resolve(handle);
|
||||
messageHide(); // triggers dispose! make sure promise is already resolved
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
dispose(): void {
|
||||
resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
commands.forEach(command => {
|
||||
if (command.isCloseAffordance === true) {
|
||||
hasCloseAffordance = true;
|
||||
}
|
||||
actions.push(new MessageItemAction('_extension_message_handle_' + command.handle, command.title, command.handle));
|
||||
});
|
||||
|
||||
if (!hasCloseAffordance) {
|
||||
actions.unshift(new MessageItemAction('__close', nls.localize('close', "Close"), undefined));
|
||||
}
|
||||
|
||||
messageHide = this._messageService.show(severity, {
|
||||
message,
|
||||
actions
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
59
src/vs/workbench/api/node/mainThreadOutputService.ts
Normal file
59
src/vs/workbench/api/node/mainThreadOutputService.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {Registry} from 'vs/platform/platform';
|
||||
import {IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry} from 'vs/workbench/parts/output/common/output';
|
||||
import {IPartService} from 'vs/workbench/services/part/common/partService';
|
||||
import {IPanelService} from 'vs/workbench/services/panel/common/panelService';
|
||||
|
||||
export class MainThreadOutputService {
|
||||
|
||||
private _outputService: IOutputService;
|
||||
private _partService: IPartService;
|
||||
private _panelService: IPanelService;
|
||||
|
||||
constructor(@IOutputService outputService: IOutputService,
|
||||
@IPartService partService: IPartService,
|
||||
@IPanelService panelService: IPanelService
|
||||
) {
|
||||
this._outputService = outputService;
|
||||
this._partService = partService;
|
||||
this._panelService = panelService;
|
||||
}
|
||||
|
||||
public append(channelId: string, label: string, value: string): TPromise<void> {
|
||||
this._getChannel(channelId, label).append(value);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public clear(channelId: string, label: string): TPromise<void> {
|
||||
this._getChannel(channelId, label).clear();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public reveal(channelId: string, label: string, preserveFocus: boolean): TPromise<void> {
|
||||
this._getChannel(channelId, label).show(preserveFocus);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _getChannel(channelId: string, label: string): IOutputChannel {
|
||||
if (Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannels().every(channel => channel.id !== channelId)) {
|
||||
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel(channelId, label);
|
||||
}
|
||||
|
||||
return this._outputService.getChannel(channelId);
|
||||
}
|
||||
|
||||
public close(channelId: string): TPromise<void> {
|
||||
const panel = this._panelService.getActivePanel();
|
||||
if (panel && panel.getId() === OUTPUT_PANEL_ID && channelId === this._outputService.getActiveChannel().id ) {
|
||||
this._partService.setPanelHidden(true);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
92
src/vs/workbench/api/node/mainThreadQuickOpen.ts
Normal file
92
src/vs/workbench/api/node/mainThreadQuickOpen.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {IQuickOpenService, IPickOptions, IInputOptions} from 'vs/workbench/services/quickopen/common/quickOpenService';
|
||||
import {InputBoxOptions} from 'vscode';
|
||||
import {ExtHostContext} from './extHostProtocol';
|
||||
import {ExtHostQuickOpen, MyQuickPickItems} from './extHostQuickOpen';
|
||||
|
||||
export class MainThreadQuickOpen {
|
||||
|
||||
private _proxy: ExtHostQuickOpen;
|
||||
private _quickOpenService: IQuickOpenService;
|
||||
private _doSetItems: (items: MyQuickPickItems[]) => any;
|
||||
private _doSetError: (error: Error) => any;
|
||||
private _contents: TPromise<MyQuickPickItems[]>;
|
||||
private _token: number = 0;
|
||||
|
||||
constructor( @IThreadService threadService: IThreadService, @IQuickOpenService quickOpenService: IQuickOpenService) {
|
||||
this._proxy = threadService.get(ExtHostContext.ExtHostQuickOpen);
|
||||
this._quickOpenService = quickOpenService;
|
||||
}
|
||||
|
||||
$show(options: IPickOptions): Thenable<number> {
|
||||
|
||||
const myToken = ++this._token;
|
||||
|
||||
this._contents = new TPromise((c, e) => {
|
||||
this._doSetItems = (items) => {
|
||||
if (myToken === this._token) {
|
||||
c(items);
|
||||
}
|
||||
};
|
||||
|
||||
this._doSetError = (error) => {
|
||||
if (myToken === this._token) {
|
||||
e(error);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return this._quickOpenService.pick(this._contents, options).then(item => {
|
||||
if (item) {
|
||||
return item.handle;
|
||||
}
|
||||
}, undefined, progress => {
|
||||
if (progress) {
|
||||
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$setItems(items: MyQuickPickItems[]): Thenable<any> {
|
||||
if (this._doSetItems) {
|
||||
this._doSetItems(items);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$setError(error: Error): Thenable<any> {
|
||||
if (this._doSetError) {
|
||||
this._doSetError(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- input
|
||||
|
||||
$input(options: InputBoxOptions, validateInput: boolean): Thenable<string> {
|
||||
|
||||
const inputOptions: IInputOptions = Object.create(null);
|
||||
|
||||
if (options) {
|
||||
inputOptions.password = options.password;
|
||||
inputOptions.placeHolder = options.placeHolder;
|
||||
inputOptions.prompt = options.prompt;
|
||||
inputOptions.value = options.value;
|
||||
}
|
||||
|
||||
if (validateInput) {
|
||||
inputOptions.validateInput = (value) => {
|
||||
return this._proxy.$validateInput(value);
|
||||
};
|
||||
}
|
||||
|
||||
return this._quickOpenService.input(inputOptions);
|
||||
}
|
||||
}
|
||||
37
src/vs/workbench/api/node/mainThreadStatusBar.ts
Normal file
37
src/vs/workbench/api/node/mainThreadStatusBar.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment} from 'vs/platform/statusbar/common/statusbar';
|
||||
import {IDisposable} from 'vs/base/common/lifecycle';
|
||||
|
||||
export class MainThreadStatusBar {
|
||||
private mapIdToDisposable: { [id: number]: IDisposable };
|
||||
|
||||
constructor(
|
||||
@IStatusbarService private statusbarService: IStatusbarService
|
||||
) {
|
||||
this.mapIdToDisposable = Object.create(null);
|
||||
}
|
||||
|
||||
setEntry(id: number, text: string, tooltip: string, command: string, color: string, alignment: MainThreadStatusBarAlignment, priority: number): void {
|
||||
|
||||
// Dispose any old
|
||||
this.dispose(id);
|
||||
|
||||
// Add new
|
||||
let disposeable = this.statusbarService.addEntry({ text, tooltip, command, color }, alignment, priority);
|
||||
this.mapIdToDisposable[id] = disposeable;
|
||||
}
|
||||
|
||||
dispose(id: number) {
|
||||
let disposeable = this.mapIdToDisposable[id];
|
||||
if (disposeable) {
|
||||
disposeable.dispose();
|
||||
}
|
||||
|
||||
delete this.mapIdToDisposable[id];
|
||||
}
|
||||
}
|
||||
41
src/vs/workbench/api/node/mainThreadStorage.ts
Normal file
41
src/vs/workbench/api/node/mainThreadStorage.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class MainThreadStorage {
|
||||
|
||||
private _storageService: IStorageService;
|
||||
|
||||
constructor( @IStorageService storageService: IStorageService) {
|
||||
this._storageService = storageService;
|
||||
}
|
||||
|
||||
getValue<T>(shared: boolean, key: string): TPromise<T> {
|
||||
let jsonValue = this._storageService.get(key, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
|
||||
if (!jsonValue) {
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
let value: T;
|
||||
try {
|
||||
value = JSON.parse(jsonValue);
|
||||
return TPromise.as(value);
|
||||
} catch (err) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
}
|
||||
|
||||
setValue(shared: boolean, key: string, value: any): TPromise<any> {
|
||||
let jsonValue: any;
|
||||
try {
|
||||
jsonValue = JSON.stringify(value);
|
||||
this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
|
||||
} catch (err) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/vs/workbench/api/node/mainThreadTelemetry.ts
Normal file
28
src/vs/workbench/api/node/mainThreadTelemetry.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {ITelemetryService, ITelemetryInfo} from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
/**
|
||||
* Helper always instantiated in the main process to receive telemetry events from remote telemetry services
|
||||
*/
|
||||
export class MainThreadTelemetry {
|
||||
|
||||
private _telemetryService: ITelemetryService;
|
||||
|
||||
constructor( @ITelemetryService telemetryService: ITelemetryService) {
|
||||
this._telemetryService = telemetryService;
|
||||
}
|
||||
|
||||
public $publicLog(eventName: string, data?: any): void {
|
||||
this._telemetryService.publicLog(eventName, data);
|
||||
}
|
||||
|
||||
public $getTelemetryInfo(): TPromise<ITelemetryInfo> {
|
||||
return this._telemetryService.getTelemetryInfo();
|
||||
}
|
||||
}
|
||||
97
src/vs/workbench/api/node/mainThreadWorkspace.ts
Normal file
97
src/vs/workbench/api/node/mainThreadWorkspace.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {isPromiseCanceledError} from 'vs/base/common/errors';
|
||||
import {ISearchService, QueryType} from 'vs/platform/search/common/search';
|
||||
import {IWorkspaceContextService, IWorkspace} from 'vs/platform/workspace/common/workspace';
|
||||
import {IEventService} from 'vs/platform/event/common/event';
|
||||
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
|
||||
import {ITextFileService} from 'vs/workbench/parts/files/common/files';
|
||||
import {ICommonCodeEditor} from 'vs/editor/common/editorCommon';
|
||||
import {bulkEdit, IResourceEdit} from 'vs/editor/common/services/bulkEdit';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {Uri} from 'vscode';
|
||||
|
||||
export class MainThreadWorkspace {
|
||||
|
||||
private _activeSearches: { [id: number]: TPromise<Uri[]> } = Object.create(null);
|
||||
private _searchService: ISearchService;
|
||||
private _workspace: IWorkspace;
|
||||
private _textFileService: ITextFileService;
|
||||
private _editorService:IWorkbenchEditorService;
|
||||
private _eventService:IEventService;
|
||||
|
||||
constructor( @ISearchService searchService: ISearchService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@ITextFileService textFileService,
|
||||
@IWorkbenchEditorService editorService,
|
||||
@IEventService eventService) {
|
||||
|
||||
this._searchService = searchService;
|
||||
this._workspace = contextService.getWorkspace();
|
||||
this._textFileService = textFileService;
|
||||
this._editorService = editorService;
|
||||
this._eventService = eventService;
|
||||
}
|
||||
|
||||
$startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable<Uri[]> {
|
||||
|
||||
if (!this._workspace) {
|
||||
return;
|
||||
}
|
||||
|
||||
const search = this._searchService.search({
|
||||
folderResources: [this._workspace.resource],
|
||||
type: QueryType.File,
|
||||
maxResults,
|
||||
includePattern: { [include]: true },
|
||||
excludePattern: { [exclude]: true },
|
||||
}).then(result => {
|
||||
return result.results.map(m => m.resource);
|
||||
}, err => {
|
||||
if (!isPromiseCanceledError(err)) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
});
|
||||
|
||||
this._activeSearches[requestId] = search;
|
||||
const onDone = () => delete this._activeSearches[requestId];
|
||||
search.done(onDone, onDone);
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
$cancelSearch(requestId: number): Thenable<boolean> {
|
||||
const search = this._activeSearches[requestId];
|
||||
if (search) {
|
||||
delete this._activeSearches[requestId];
|
||||
search.cancel();
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
$saveAll(includeUntitled?: boolean): Thenable<boolean> {
|
||||
return this._textFileService.saveAll(includeUntitled).then(result => {
|
||||
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 = <ICommonCodeEditor> editor.getControl();
|
||||
if (typeof candidate.getEditorType === 'function') {
|
||||
// enough proof
|
||||
codeEditor = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return bulkEdit(this._eventService, this._editorService, codeEditor, edits)
|
||||
.then(() => true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user