diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index e69567ee792..d8e13128c0b 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -14,6 +14,14 @@ function isThenable(obj: any): obj is Thenable { return obj && typeof (>obj).then === 'function'; } +export function toThenable(arg: T | Thenable): Thenable { + if (isThenable(arg)) { + return arg; + } else { + return TPromise.as(arg); + } +} + export function asWinJsPromise(callback: (token: CancellationToken) => T | Thenable): TPromise { let source = new CancellationTokenSource(); return new TPromise((resolve, reject) => { @@ -28,6 +36,14 @@ export function asWinJsPromise(callback: (token: CancellationToken) => T | Th }); } +/** + * Hook a cancellation token to a WinJS Promise + */ +export function wireCancellationToken(token: CancellationToken, promise: TPromise): Thenable { + token.onCancellationRequested(() => promise.cancel()); + return promise; +} + export interface ITask { (): T; } diff --git a/src/vs/base/common/winjs.base.d.ts b/src/vs/base/common/winjs.base.d.ts index 860c2c9b9ac..2b29355a65b 100644 --- a/src/vs/base/common/winjs.base.d.ts +++ b/src/vs/base/common/winjs.base.d.ts @@ -95,6 +95,7 @@ export declare class TPromise { public static is(value: any): value is TPromise; public static timeout(delay:number):TPromise; public static join(promises:TPromise[]):TPromise; + public static join(promises:Thenable[]):Thenable; public static join(promises: {[n:string]:TPromise}):TPromise<{[n:string]:ValueType}>; public static any(promises:TPromise[]):TPromise<{ key:string; value:TPromise;}>; public static wrapError(error:any):TPromise; diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index b786c8fe5f2..27d03229491 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -157,9 +157,10 @@ class SimpleWorkerProtocol { export class SimpleWorkerClient extends Disposable { private _worker:IWorker; - private _onModuleLoaded:TPromise; + private _onModuleLoaded:TPromise; private _protocol: SimpleWorkerProtocol; private _proxy: T; + private _lazyProxy: TPromise; private _lastRequestTimestamp = -1; constructor(workerFactory:IWorkerFactory, moduleId:string, ctor:any) { @@ -190,13 +191,30 @@ export class SimpleWorkerClient extends Disposable { loaderConfiguration = (window).requirejs.s.contexts._.config; } + let lazyProxyFulfill : (v:T)=>void = null; + let lazyProxyReject: (err:any)=>void = null; + + this._lazyProxy = new TPromise((c, e, p) => { + lazyProxyFulfill = c; + lazyProxyReject = e; + }, () => { /* no cancel */ }); + // Send initialize message this._onModuleLoaded = this._protocol.sendMessage(INITIALIZE, [ this._worker.getId(), moduleId, loaderConfiguration ]); - this._onModuleLoaded.then(null, (e) => this._onError('Worker failed to load ' + moduleId, e)); + this._onModuleLoaded.then((availableMethods:string[]) => { + let proxy = {}; + for (let i = 0; i < availableMethods.length; i++) { + proxy[availableMethods[i]] = createProxyMethod(availableMethods[i], proxyMethodRequest); + } + lazyProxyFulfill(proxy); + }, (e) => { + lazyProxyReject(e); + this._onError('Worker failed to load ' + moduleId, e); + }); // Create proxy to loaded code let proxyMethodRequest = (method:string, args:any[]):TPromise => { @@ -211,10 +229,13 @@ export class SimpleWorkerClient extends Disposable { }; this._proxy = {}; - for (let prop in ctor.prototype) { - if (ctor.prototype.hasOwnProperty(prop)) { - if (typeof ctor.prototype[prop] === 'function') { - this._proxy[prop] = createProxyMethod(prop, proxyMethodRequest); + if (ctor) { + // console.warn('deprecated'); + for (let prop in ctor.prototype) { + if (ctor.prototype.hasOwnProperty(prop)) { + if (typeof ctor.prototype[prop] === 'function') { + this._proxy[prop] = createProxyMethod(prop, proxyMethodRequest); + } } } } @@ -224,6 +245,10 @@ export class SimpleWorkerClient extends Disposable { return this._proxy; } + public getProxyObject(): TPromise { + return this._lazyProxy; + } + public getLastRequestTimestamp(): number { return this._lastRequestTimestamp; } @@ -323,7 +348,15 @@ export class SimpleWorkerServer { require([moduleId], (...result:any[]) => { let handlerModule = result[0]; this._requestHandler = handlerModule.create(); - cc(null); + + let methods: string[] = []; + for (let prop in this._requestHandler) { + if (typeof this._requestHandler[prop] === 'function') { + methods.push(prop); + } + } + + cc(methods); }, ee); return r; diff --git a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts index 93b9f2d1504..9ad5c5a6d06 100644 --- a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts @@ -24,7 +24,7 @@ import {RemoteTelemetryServiceHelper} from 'vs/platform/telemetry/common/remoteT import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {DefaultConfig} from 'vs/editor/common/config/defaultConfig'; import {IActionDescriptor, ICodeEditorWidgetCreationOptions, IDiffEditorOptions, IModel, IModelChangedEvent, EventType} from 'vs/editor/common/editorCommon'; -import {IMode} from 'vs/editor/common/modes'; +import {IExtraInfoSupport, IMode} from 'vs/editor/common/modes'; import {ModesRegistry} from 'vs/editor/common/modes/modesRegistry'; import {ILanguage} from 'vs/editor/common/modes/monarch/monarchTypes'; import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService'; @@ -38,6 +38,11 @@ import {SimpleEditorService, StandaloneKeybindingService} from 'vs/editor/browse import {IEditorContextViewService, IEditorOverrideServices, ensureDynamicPlatformServices, ensureStaticPlatformServices, getOrCreateStaticServices} from 'vs/editor/browser/standalone/standaloneServices'; import {CodeEditorWidget} from 'vs/editor/browser/widget/codeEditorWidget'; import {DiffEditorWidget} from 'vs/editor/browser/widget/diffEditorWidget'; +import * as modes from 'vs/editor/common/modes'; +import {EditorModelManager} from 'vs/editor/common/services/editorWorkerServiceImpl'; +import {SimpleWorkerClient} from 'vs/base/common/worker/simpleWorker'; +import {DefaultWorkerFactory} from 'vs/base/worker/defaultWorkerFactory'; +import {StandaloneWorker} from 'vs/editor/browser/standalone/standaloneWorker'; // Set defaults for standalone editor DefaultConfig.editor.wrappingIndent = 'none'; @@ -457,6 +462,87 @@ export function createCustomMode(language:ILanguage): TPromise { return modeService.getOrCreateMode(modeId); } +export function registerTokensProvider(languageId:string, support:modes.ITokenizationSupport2): IDisposable { + startup.initStaticServicesIfNecessary(); + let staticPlatformServices = ensureStaticPlatformServices(null); + + return staticPlatformServices.modeService.registerTokenizationSupport2(languageId, support); +} + +export function registerHoverProvider(languageId:string, support:IExtraInfoSupport): IDisposable { + return modes.ExtraInfoRegistry.register(languageId, support); +} + +interface IMonacoWebWorkerState { + myProxy:StandaloneWorker; + foreignProxy:T; + modelMananger: EditorModelManager; +} + +export class MonacoWebWorker { + + private _loaded: TPromise>; + private _client: SimpleWorkerClient; + + constructor(modelService: IModelService, opts:IWebWorkerOptions) { + this._client = new SimpleWorkerClient(new DefaultWorkerFactory(), 'vs/editor/browser/standalone/standaloneWorker', null); + + this._loaded = this._client.getProxyObject().then((proxy) => { + + let proxyMethodRequest = (method:string, args:any[]): TPromise => { + return proxy.fmr(method, args); + }; + + let createProxyMethod = (method:string, proxyMethodRequest:(method:string, args:any[])=>TPromise): Function => { + return function () { + let args = Array.prototype.slice.call(arguments, 0); + return proxyMethodRequest(method, args); + }; + }; + + const manager = new EditorModelManager(proxy, modelService, true); + + return proxy.loadModule(opts.moduleId).then((foreignMethods): IMonacoWebWorkerState => { + + let foreignProxy = {}; + for (let i = 0; i < foreignMethods.length; i++) { + foreignProxy[foreignMethods[i]] = createProxyMethod(foreignMethods[i], proxyMethodRequest); + } + + return { + myProxy: proxy, + foreignProxy: foreignProxy, + modelMananger: manager + }; + }); + }); + } + + public dispose(): void { + console.log('TODO: I should dispose now'); + } + + public getProxy(): TPromise { + return this._loaded.then(data => data.foreignProxy); + } + + public withSyncedResources(resources: URI[]): TPromise { + return this._loaded.then(data => data.modelMananger.withSyncedResources(resources)); + } +} + +export interface IWebWorkerOptions { + moduleId: string; +} + +export function createWebWorker(opts:IWebWorkerOptions): MonacoWebWorker { + startup.initStaticServicesIfNecessary(); + let staticPlatformServices = ensureStaticPlatformServices(null); + let modelService = staticPlatformServices.modelService; + + return new MonacoWebWorker(modelService, opts); +} + export function registerMonarchStandaloneLanguage(language:ILanguageExtensionPoint, defModule:string): void { ModesRegistry.registerLanguage(language); diff --git a/src/vs/editor/browser/standalone/standaloneEditor.ts b/src/vs/editor/browser/standalone/standaloneEditor.ts index ff1ad8d6935..1a04ef24923 100644 --- a/src/vs/editor/browser/standalone/standaloneEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneEditor.ts @@ -7,6 +7,7 @@ import 'vs/editor/standalone-languages/all'; import './standaloneSchemas'; import 'vs/css!./media/standalone-tokens'; +import {Emitter} from 'vs/base/common/event'; import {IJSONSchema} from 'vs/base/common/jsonSchema'; import * as editorCommon from 'vs/editor/common/editorCommon'; import {ClassNames, ContentWidgetPositionPreference, OverlayWidgetPositionPreference} from 'vs/editor/browser/editorBrowser'; @@ -91,7 +92,10 @@ Monaco.Languages.onLanguage = (languageId:string, callback:()=>void) => { dispose: () => { isDisposed = true; } }; }; - +Monaco.createWebWorker = standaloneCodeEditor.createWebWorker; +Monaco.Languages.registerTokensProvider = standaloneCodeEditor.registerTokensProvider; +Monaco.Languages.registerHoverProvider = standaloneCodeEditor.registerHoverProvider; +Monaco.Emitter = Emitter; // let handlePlugin = (plugin) => { // if (Array.isArray(plugin.languages)) { // ModesRegistry.registerLanguages(plugin.languages); diff --git a/src/vs/editor/browser/standalone/standaloneWorker.ts b/src/vs/editor/browser/standalone/standaloneWorker.ts new file mode 100644 index 00000000000..5114dbb024f --- /dev/null +++ b/src/vs/editor/browser/standalone/standaloneWorker.ts @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * 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 URI from 'vs/base/common/uri'; +import {ErrorCallback, TPromise, ValueCallback} from 'vs/base/common/winjs.base'; +import {IRequestHandler} from 'vs/base/common/worker/simpleWorker'; +import {Range} from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import {MirrorModel2} from 'vs/editor/common/model/mirrorModel2'; +import {WordHelper} from 'vs/editor/common/model/textModelWithTokensHelpers'; +import {IRawModelData} from 'vs/editor/common/services/editorSimpleWorkerCommon'; +import {Emitter} from 'vs/base/common/event'; + +class MirrorModel extends MirrorModel2 { + + public get uri(): URI { + return this._uri; + } + + public get version(): number { + return this._versionId; + } + + public getLinesContent(): string[] { + return this._lines.slice(0); + } + + public getLineCount(): number { + return this._lines.length; + } + + public getLineContent(lineNumber:number): string { + return this._lines[lineNumber - 1]; + } + + public getWordAtPosition(position:editorCommon.IPosition, wordDefinition:RegExp): Range { + + let wordAtText = WordHelper._getWordAtText( + position.column, + WordHelper.ensureValidWordDefinition(wordDefinition), + this._lines[position.lineNumber - 1], + 0 + ); + + if (wordAtText) { + return new Range(position.lineNumber, wordAtText.startColumn, position.lineNumber, wordAtText.endColumn); + } + + return null; + } + + public getWordUntilPosition(position: editorCommon.IPosition, wordDefinition:RegExp): editorCommon.IWordAtPosition { + var wordAtPosition = this.getWordAtPosition(position, wordDefinition); + if (!wordAtPosition) { + return { + word: '', + startColumn: position.column, + endColumn: position.column + }; + } + return { + word: this._lines[position.lineNumber - 1].substring(wordAtPosition.startColumn - 1, position.column - 1), + startColumn: wordAtPosition.startColumn, + endColumn: position.column + }; + } + + private _getAllWords(wordDefinition:RegExp): string[] { + var result:string[] = []; + this._lines.forEach((line) => { + this._wordenize(line, wordDefinition).forEach((info) => { + result.push(line.substring(info.start, info.end)); + }); + }); + return result; + } + + public getAllUniqueWords(wordDefinition:RegExp, skipWordOnce?:string) : string[] { + var foundSkipWord = false; + var uniqueWords = {}; + return this._getAllWords(wordDefinition).filter((word) => { + if (skipWordOnce && !foundSkipWord && skipWordOnce === word) { + foundSkipWord = true; + return false; + } else if (uniqueWords[word]) { + return false; + } else { + uniqueWords[word] = true; + return true; + } + }); + } + +// // TODO@Joh, TODO@Alex - remove these and make sure the super-things work + private _wordenize(content:string, wordDefinition:RegExp): editorCommon.IWordRange[] { + var result:editorCommon.IWordRange[] = []; + var match:RegExpExecArray; + while (match = wordDefinition.exec(content)) { + if (match[0].length === 0) { + // it did match the empty string + break; + } + result.push({ start: match.index, end: match.index + match[0].length }); + } + return result; + } + + public getValueInRange(range:editorCommon.IRange): string { + if (range.startLineNumber === range.endLineNumber) { + return this._lines[range.startLineNumber - 1].substring(range.startColumn - 1, range.endColumn - 1); + } + + var lineEnding = this._eol, + startLineIndex = range.startLineNumber - 1, + endLineIndex = range.endLineNumber - 1, + resultLines:string[] = []; + + resultLines.push(this._lines[startLineIndex].substring(range.startColumn - 1)); + for (var i = startLineIndex + 1; i < endLineIndex; i++) { + resultLines.push(this._lines[i]); + } + resultLines.push(this._lines[endLineIndex].substring(0, range.endColumn - 1)); + + return resultLines.join(lineEnding); + } +} + +this.Monaco = this.Monaco || {}; +var Monaco = this.Monaco; + +this.monaco = this.monaco || {}; +var monaco = this.monaco; + +export class StandaloneWorker /*extends EditorSimpleWorker*/ implements IRequestHandler { + _requestHandlerTrait: any; + + private _models:{[uri:string]:MirrorModel;}; + private _foreignModule: any; + + constructor() { + // super(); + this._models = Object.create(null); + this._foreignModule = null; + + + Monaco.TPromise = TPromise; + Monaco.Emitter = Emitter; + + let that = this; + monaco.worker = { + get mirrorModels () { + let all: MirrorModel[] = []; + Object.keys(that._models).forEach((key) => all.push(that._models[key])); + return all; + } + }; + } + + public acceptNewModel(data:IRawModelData): void { + this._models[data.url] = new MirrorModel(URI.parse(data.url), data.value.lines, data.value.EOL, data.versionId); + } + + public acceptModelChanged(strURL: string, events: editorCommon.IModelContentChangedEvent2[]): void { + if (!this._models[strURL]) { + return; + } + let model = this._models[strURL]; + model.onEvents(events); + } + + public acceptRemovedModel(strURL: string): void { + if (!this._models[strURL]) { + return; + } + delete this._models[strURL]; + } + + public loadModule(moduleId:string): TPromise { + let cc: ValueCallback; + let ee: ErrorCallback; + let r = new TPromise((c, e, p) => { + cc = c; + ee = e; + }); + + require([moduleId], (foreignModule) => { + this._foreignModule = foreignModule.create(); + + let methods: string[] = []; + for (let prop in this._foreignModule) { + if (typeof this._foreignModule[prop] === 'function') { + methods.push(prop); + } + } + + cc(methods); + + }, ee); + + return r; + } + + // foreign method request + public fmr(method:string, args:any[]): TPromise { + if (!this._foreignModule || typeof this._foreignModule[method] !== 'function') { + return TPromise.wrapError(new Error('Missing requestHandler or method: ' + method)); + } + + try { + return TPromise.as(this._foreignModule[method].apply(this._foreignModule, args)); + } catch (e) { + return TPromise.wrapError(e); + } + } +} + +/** + * Called on the worker side + */ +export function create(): IRequestHandler { + return new StandaloneWorker(); +} diff --git a/src/vs/editor/common/editorCommonExtensions.ts b/src/vs/editor/common/editorCommonExtensions.ts index f9ff6b55175..df24d32b930 100644 --- a/src/vs/editor/common/editorCommonExtensions.ts +++ b/src/vs/editor/common/editorCommonExtensions.ts @@ -102,7 +102,7 @@ export module CommonEditorRegistry { }); } - export function registerDefaultLanguageCommand(id: string, handler: (model: editorCommon.IModel, position: editorCommon.IPosition, args: { [n: string]: any }) => any) { + export function registerDefaultLanguageCommand(id: string, handler: (model: editorCommon.IModel, position: editorCommon.IEditorPosition, args: { [n: string]: any }) => any) { registerLanguageCommand(id, function(accessor, args) { const {resource, position} = args; @@ -115,7 +115,9 @@ export module CommonEditorRegistry { throw illegalArgument(); } - return handler(model, position, args); + const editorPosition = Position.lift(position); + + return handler(model, editorPosition, args); }); } } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index d1862a9eed8..1fcc47db825 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -12,6 +12,7 @@ import {IFilter} from 'vs/base/common/filters'; import * as editorCommon from 'vs/editor/common/editorCommon'; import {ModeTransition} from 'vs/editor/common/core/modeTransition'; import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; +import {CancellationToken} from 'vs/base/common/cancellation'; export interface ITokenizationResult { type?:string; @@ -322,16 +323,25 @@ export interface ITokenizationSupport2 { } /** - * Interface used to get extra info for a symbol + * A hover represents additional information for a symbol or word. Hovers are + * rendered in a tooltip-like widget. */ -export interface IComputeExtraInfoResult { +export interface Hover { + /** + * The contents of this hover. + */ + htmlContent: IHTMLContentElement[]; + + /** + * The range to which this hover applies. When missing, the + * editor will use the range at the current position or the + * current position itself. + */ range: editorCommon.IRange; - value?: string; - htmlContent?: IHTMLContentElement[]; - className?: string; } + export interface IExtraInfoSupport { - computeInfo(resource:URI, position:editorCommon.IPosition):TPromise; + provideHover(model:editorCommon.IModel, position:editorCommon.IEditorPosition, cancellationToken:CancellationToken): Hover | Thenable; } export type SuggestionType = 'method' diff --git a/src/vs/editor/contrib/hover/browser/hoverOperation.ts b/src/vs/editor/contrib/hover/browser/hoverOperation.ts index 7e8051647f9..155525863f8 100644 --- a/src/vs/editor/contrib/hover/browser/hoverOperation.ts +++ b/src/vs/editor/contrib/hover/browser/hoverOperation.ts @@ -6,7 +6,7 @@ import {RunOnceScheduler} from 'vs/base/common/async'; import {onUnexpectedError} from 'vs/base/common/errors'; -import {TPromise} from 'vs/base/common/winjs.base'; +import {CancellationToken, CancellationTokenSource} from 'vs/base/common/cancellation'; export interface IHoverComputer { @@ -18,7 +18,7 @@ export interface IHoverComputer { /** * This is called after half the hover time */ - computeAsync?: () => TPromise; + computeAsync?: (cancellationToken:CancellationToken) => Thenable; /** * This is called after all the hover time @@ -56,7 +56,7 @@ export class HoverOperation { private _firstWaitScheduler: RunOnceScheduler; private _secondWaitScheduler: RunOnceScheduler; private _loadingMessageScheduler: RunOnceScheduler; - private _asyncComputationPromise:TPromise; + private _asyncComputationCancellationTokenSource: CancellationTokenSource; private _asyncComputationPromiseDone:boolean; private _completeCallback:(r:Result)=>void; @@ -71,7 +71,7 @@ export class HoverOperation { this._secondWaitScheduler = new RunOnceScheduler(() => this._triggerSyncComputation(), this._getHoverTimeMillis() / 2); this._loadingMessageScheduler = new RunOnceScheduler(() => this._showLoadingMessage(), 3 * this._getHoverTimeMillis()); - this._asyncComputationPromise = null; + this._asyncComputationCancellationTokenSource = new CancellationTokenSource(); this._asyncComputationPromiseDone = false; this._completeCallback = success; @@ -96,12 +96,12 @@ export class HoverOperation { if (this._computer.computeAsync) { this._asyncComputationPromiseDone = false; - this._asyncComputationPromise = this._computer.computeAsync(); - - this._asyncComputationPromise.then((asyncResult: Result) => { + this._asyncComputationCancellationTokenSource.dispose(); + this._asyncComputationCancellationTokenSource = new CancellationTokenSource(); + this._computer.computeAsync(this._asyncComputationCancellationTokenSource.token).then((asyncResult: Result) => { this._asyncComputationPromiseDone = true; this._withAsyncResult(asyncResult); - }).done(null, () => this._onError); + }).then(null, () => this._onError); } else { this._asyncComputationPromiseDone = true; } @@ -173,14 +173,10 @@ export class HoverOperation { } if (this._state === ComputeHoverOperationState.SECOND_WAIT) { this._secondWaitScheduler.cancel(); - if (this._asyncComputationPromise) { - this._asyncComputationPromise.cancel(); - } + this._asyncComputationCancellationTokenSource.cancel(); } if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION) { - if (this._asyncComputationPromise) { - this._asyncComputationPromise.cancel(); - } + this._asyncComputationCancellationTokenSource.cancel(); } this._state = ComputeHoverOperationState.IDLE; } diff --git a/src/vs/editor/contrib/hover/browser/modesContentHover.ts b/src/vs/editor/contrib/hover/browser/modesContentHover.ts index 9203fc05854..8d2e788909b 100644 --- a/src/vs/editor/contrib/hover/browser/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/browser/modesContentHover.ts @@ -11,18 +11,20 @@ import {TPromise} from 'vs/base/common/winjs.base'; import {renderHtml} from 'vs/base/browser/htmlContentRenderer'; import {IOpenerService, NullOpenerService} from 'vs/platform/opener/common/opener'; import {Range} from 'vs/editor/common/core/range'; +import {Position} from 'vs/editor/common/core/position'; import {IEditorRange, IRange} from 'vs/editor/common/editorCommon'; -import {ExtraInfoRegistry, IComputeExtraInfoResult, IMode} from 'vs/editor/common/modes'; +import {ExtraInfoRegistry, Hover, IMode} from 'vs/editor/common/modes'; import {tokenizeToString} from 'vs/editor/common/modes/textToHtmlTokenizer'; import {ICodeEditor} from 'vs/editor/browser/editorBrowser'; import {getExtraInfoAtPosition} from '../common/hover'; import {HoverOperation, IHoverComputer} from './hoverOperation'; import {ContentHoverWidget} from './hoverWidgets'; +import {CancellationToken} from 'vs/base/common/cancellation'; -class ModesContentComputer implements IHoverComputer { +class ModesContentComputer implements IHoverComputer { private _editor: ICodeEditor; - private _result: IComputeExtraInfoResult[]; + private _result: Hover[]; private _range: IEditorRange; constructor(editor: ICodeEditor) { @@ -39,21 +41,21 @@ class ModesContentComputer implements IHoverComputer this._result = []; } - public computeAsync(): TPromise { + public computeAsync(cancellationToken:CancellationToken): Thenable { let model = this._editor.getModel(); if (!ExtraInfoRegistry.has(model)) { return TPromise.as(null); } - return getExtraInfoAtPosition(model, { - lineNumber: this._range.startLineNumber, - column: this._range.startColumn - }); + return getExtraInfoAtPosition(model, new Position( + this._range.startLineNumber, + this._range.startColumn + ), cancellationToken); } - public computeSync(): IComputeExtraInfoResult[] { - var result:IComputeExtraInfoResult[] = []; + public computeSync(): Hover[] { + var result:Hover[] = []; var lineNumber = this._range.startLineNumber; if (lineNumber > this._editor.getModel().getLineCount()) { @@ -68,12 +70,15 @@ class ModesContentComputer implements IHoverComputer var endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn; if (startColumn <= this._range.startColumn && this._range.endColumn <= endColumn && (d.options.hoverMessage || (d.options.htmlMessage && d.options.htmlMessage.length > 0))) { - var obj:IComputeExtraInfoResult = { - value: d.options.hoverMessage, + var obj:Hover = { + htmlContent: [], range: new Range(this._range.startLineNumber, startColumn, this._range.startLineNumber, endColumn) }; - if(d.options.htmlMessage) { - obj.htmlContent = d.options.htmlMessage; + if (d.options.hoverMessage) { + obj.htmlContent.push({ text: d.options.hoverMessage }); + } + if (d.options.htmlMessage) { + obj.htmlContent = obj.htmlContent.concat(d.options.htmlMessage); } result.push(obj); } @@ -81,7 +86,7 @@ class ModesContentComputer implements IHoverComputer return result; } - public onResult(result: IComputeExtraInfoResult[], isFromSynchronousComputation: boolean): void { + public onResult(result: Hover[], isFromSynchronousComputation: boolean): void { // Always put synchronous messages before asynchronous ones if (isFromSynchronousComputation) { this._result = result.concat(this._result); @@ -90,15 +95,15 @@ class ModesContentComputer implements IHoverComputer } } - public getResult(): IComputeExtraInfoResult[] { + public getResult(): Hover[] { return this._result.slice(0); } - public getResultWithLoadingMessage(): IComputeExtraInfoResult[] { + public getResultWithLoadingMessage(): Hover[] { return this._result.slice(0).concat([this._getLoadingMessage()]); } - private _getLoadingMessage(): IComputeExtraInfoResult { + private _getLoadingMessage(): Hover { return { range: this._range, htmlContent: [{ @@ -115,10 +120,10 @@ class ModesContentComputer implements IHoverComputer export class ModesContentHoverWidget extends ContentHoverWidget { static ID = 'editor.contrib.modesContentHoverWidget'; - private _messages: IComputeExtraInfoResult[]; + private _messages: Hover[]; private _lastRange: IEditorRange; private _computer: ModesContentComputer; - private _hoverOperation: HoverOperation; + private _hoverOperation: HoverOperation; private _highlightDecorations:string[]; private _isChangingDecorations: boolean; private _openerService: IOpenerService; @@ -134,7 +139,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._hoverOperation = new HoverOperation( this._computer, - (result:IComputeExtraInfoResult[]) => this._withResult(result, true), + (result:Hover[]) => this._withResult(result, true), null, (result:any) => this._withResult(result, false) ); @@ -170,7 +175,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { if (this._showAtPosition.lineNumber !== range.startLineNumber) { this.hide(); } else { - var filteredMessages: IComputeExtraInfoResult[] = []; + var filteredMessages: Hover[] = []; for (var i = 0, len = this._messages.length; i < len; i++) { var msg = this._messages[i]; var rng = msg.range; @@ -201,7 +206,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._isChangingDecorations = false; } - public _withResult(result: IComputeExtraInfoResult[], complete:boolean): void { + public _withResult(result: Hover[], complete:boolean): void { this._messages = result; if (this._lastRange && this._messages.length > 0) { @@ -211,8 +216,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { } } - // TODO@Alex: pull this out into a common utility class - private _renderMessages(renderRange: IRange, messages: IComputeExtraInfoResult[]): void { + private _renderMessages(renderRange: IRange, messages: Hover[]): void { // update column from which to show var renderColumn = Number.MAX_VALUE, @@ -228,17 +232,9 @@ export class ModesContentHoverWidget extends ContentHoverWidget { highlightRange = Range.plusRange(highlightRange, msg.range); var row:HTMLElement = document.createElement('div'); - var span:HTMLElement = null; var container = row; - if (msg.className) { - span = document.createElement('span'); - span.className = msg.className; - container = span; - row.appendChild(span); - } - - if(msg.htmlContent && msg.htmlContent.length > 0) { + if (msg.htmlContent && msg.htmlContent.length > 0) { msg.htmlContent.forEach((content) => { container.appendChild(renderHtml(content, { actionCallback: (content) => { @@ -254,8 +250,6 @@ export class ModesContentHoverWidget extends ContentHoverWidget { } })); }); - } else { - container.textContent = msg.value; } fragment.appendChild(row); diff --git a/src/vs/editor/contrib/hover/common/hover.ts b/src/vs/editor/contrib/hover/common/hover.ts index a6c4f2fa6a4..7e90d58d16c 100644 --- a/src/vs/editor/contrib/hover/common/hover.ts +++ b/src/vs/editor/contrib/hover/common/hover.ts @@ -8,23 +8,23 @@ import {coalesce} from 'vs/base/common/arrays'; import {onUnexpectedError} from 'vs/base/common/errors'; import {TPromise} from 'vs/base/common/winjs.base'; -import {IModel, IPosition} from 'vs/editor/common/editorCommon'; +import {IModel, IEditorPosition} from 'vs/editor/common/editorCommon'; import {CommonEditorRegistry} from 'vs/editor/common/editorCommonExtensions'; -import {IComputeExtraInfoResult, ExtraInfoRegistry} from 'vs/editor/common/modes'; +import {Hover, ExtraInfoRegistry} from 'vs/editor/common/modes'; +import {CancellationToken} from 'vs/base/common/cancellation'; +import {toThenable} from 'vs/base/common/async'; -export function getExtraInfoAtPosition(model: IModel, position: IPosition): TPromise { +export function getExtraInfoAtPosition(model: IModel, position: IEditorPosition, cancellationToken = CancellationToken.None): Thenable { - const resource = model.getAssociatedResource(); const supports = ExtraInfoRegistry.ordered(model); - const values: IComputeExtraInfoResult[] = []; + const values: Hover[] = []; const promises = supports.map((support, idx) => { - return support.computeInfo(resource, position).then(result => { + return toThenable(support.provideHover(model, position, cancellationToken)).then(result => { if (result) { let hasRange = (typeof result.range !== 'undefined'); - let hasValue = (typeof result.value !== 'undefined'); let hasHtmlContent = (typeof result.htmlContent !== 'undefined' && result.htmlContent && result.htmlContent.length > 0); - if (hasRange && (hasValue || hasHtmlContent)) { + if (hasRange && hasHtmlContent) { values[idx] = result; } } diff --git a/src/vs/languages/css/common/css.ts b/src/vs/languages/css/common/css.ts index f3a4b1c69ad..88727a58fd6 100644 --- a/src/vs/languages/css/common/css.ts +++ b/src/vs/languages/css/common/css.ts @@ -20,6 +20,8 @@ import {IThreadService, ThreadAffinity} from 'vs/platform/thread/common/thread'; import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; import {TokenizationSupport} from 'vs/editor/common/modes/supports/tokenizationSupport'; import {SuggestSupport} from 'vs/editor/common/modes/supports/suggestSupport'; +import {CancellationToken} from 'vs/base/common/cancellation'; +import {wireCancellationToken} from 'vs/base/common/async'; export enum States { Selector, @@ -401,9 +403,13 @@ export class CSSMode extends AbstractMode { return this._worker((w) => w.findDeclaration(resource, position)); } - static $computeInfo = OneWorkerAttr(CSSMode, CSSMode.prototype.computeInfo); - public computeInfo(resource:URI, position:EditorCommon.IPosition): WinJS.TPromise { - return this._worker((w) => w.computeInfo(resource, position)); + public provideHover(model:EditorCommon.IModel, position:EditorCommon.IEditorPosition, cancellationToken:CancellationToken): Thenable { + return wireCancellationToken(cancellationToken, this._provideHover(model.getAssociatedResource(), position)); + } + + static $_provideHover = OneWorkerAttr(CSSMode, CSSMode.prototype._provideHover); + private _provideHover(resource:URI, position:EditorCommon.IPosition): WinJS.TPromise { + return this._worker((w) => w.provideHover(resource, position)); } static $findReferences = OneWorkerAttr(CSSMode, CSSMode.prototype.findReferences); diff --git a/src/vs/languages/css/common/cssWorker.ts b/src/vs/languages/css/common/cssWorker.ts index ad63cbabc9a..a31ba6fff1e 100644 --- a/src/vs/languages/css/common/cssWorker.ts +++ b/src/vs/languages/css/common/cssWorker.ts @@ -257,7 +257,7 @@ export class CSSWorker { }); } - public computeInfo(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { + public provideHover(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { return this.languageService.join().then(() => { diff --git a/src/vs/languages/html/common/html.ts b/src/vs/languages/html/common/html.ts index cb3679a0562..28889b2b4cd 100644 --- a/src/vs/languages/html/common/html.ts +++ b/src/vs/languages/html/common/html.ts @@ -23,6 +23,8 @@ import {ReferenceSupport} from 'vs/editor/common/modes/supports/referenceSupport import {ParameterHintsSupport} from 'vs/editor/common/modes/supports/parameterHintsSupport'; import {SuggestSupport} from 'vs/editor/common/modes/supports/suggestSupport'; import {IThreadService} from 'vs/platform/thread/common/thread'; +import {CancellationToken} from 'vs/base/common/cancellation'; +import {wireCancellationToken} from 'vs/base/common/async'; export { htmlTokenTypes }; // export to be used by Razor. We are the main module, so Razor should get it from us. export { EMPTY_ELEMENTS }; // export to be used by Razor. We are the main module, so Razor should get it from us. @@ -483,9 +485,13 @@ export class HTMLMode extends AbstractMode impl return this._worker((w) => w.format(resource, range, options)); } - static $computeInfo = OneWorkerAttr(HTMLMode, HTMLMode.prototype.computeInfo); - public computeInfo(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { - return this._worker((w) => w.computeInfo(resource, position)); + public provideHover(model:EditorCommon.IModel, position:EditorCommon.IEditorPosition, cancellationToken:CancellationToken): Thenable { + return wireCancellationToken(cancellationToken, this._provideHover(model.getAssociatedResource(), position)); + } + + static $_provideHover = OneWorkerAttr(HTMLMode, HTMLMode.prototype._provideHover); + public _provideHover(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { + return this._worker((w) => w.provideHover(resource, position)); } static $findReferences = OneWorkerAttr(HTMLMode, HTMLMode.prototype.findReferences); diff --git a/src/vs/languages/html/common/htmlWorker.ts b/src/vs/languages/html/common/htmlWorker.ts index 2685061bc75..73206d12270 100644 --- a/src/vs/languages/html/common/htmlWorker.ts +++ b/src/vs/languages/html/common/htmlWorker.ts @@ -21,6 +21,7 @@ import {isTag, DELIM_END, DELIM_START, DELIM_ASSIGN, ATTRIB_NAME, ATTRIB_VALUE} import {isEmptyElement} from 'vs/languages/html/common/htmlEmptyTagsShared'; import {filterSuggestions} from 'vs/editor/common/modes/supports/suggestSupport'; import paths = require('vs/base/common/paths'); +import {asWinJsPromise} from 'vs/base/common/async'; enum LinkDetectionState { LOOKING_FOR_HREF_OR_SRC = 1, @@ -150,10 +151,12 @@ export class HTMLWorker { return callback(model.getAllEmbedded()); } - public computeInfo(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { + public provideHover(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { return this._delegateToModeAtPosition(resource, position, (isEmbeddedMode, model) => { if (isEmbeddedMode && model.getMode().extraInfoSupport) { - return model.getMode().extraInfoSupport.computeInfo(model.getAssociatedResource(), position); + return asWinJsPromise((token) => { + return model.getMode().extraInfoSupport.provideHover(/*TODO*/model, Position.lift(position), token); + }); } }); } diff --git a/src/vs/languages/json/common/json.ts b/src/vs/languages/json/common/json.ts index 95fb6f4e32f..8a641c0ecdd 100644 --- a/src/vs/languages/json/common/json.ts +++ b/src/vs/languages/json/common/json.ts @@ -19,6 +19,8 @@ import {IJSONContributionRegistry, Extensions, ISchemaContributions} from 'vs/pl import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; import {SuggestSupport} from 'vs/editor/common/modes/supports/suggestSupport'; +import {CancellationToken} from 'vs/base/common/cancellation'; +import {wireCancellationToken} from 'vs/base/common/async'; export class JSONMode extends AbstractMode implements Modes.IExtraInfoSupport, Modes.IOutlineSupport { @@ -148,9 +150,13 @@ export class JSONMode extends AbstractMode implements Modes.IExtraInfoSupport, M return this._worker((w) => w.suggest(resource, position)); } - static $computeInfo = OneWorkerAttr(JSONMode, JSONMode.prototype.computeInfo); - public computeInfo(resource:URI, position:EditorCommon.IPosition): WinJS.TPromise { - return this._worker((w) => w.computeInfo(resource, position)); + public provideHover(model:EditorCommon.IModel, position:EditorCommon.IEditorPosition, cancellationToken:CancellationToken): Thenable { + return wireCancellationToken(cancellationToken, this._provideHover(model.getAssociatedResource(), position)); + } + + static $_provideHover = OneWorkerAttr(JSONMode, JSONMode.prototype._provideHover); + public _provideHover(resource:URI, position:EditorCommon.IPosition): WinJS.TPromise { + return this._worker((w) => w.provideHover(resource, position)); } static $getOutline = OneWorkerAttr(JSONMode, JSONMode.prototype.getOutline); diff --git a/src/vs/languages/json/common/jsonWorker.ts b/src/vs/languages/json/common/jsonWorker.ts index 9e6bf88f97c..2a6415ef103 100644 --- a/src/vs/languages/json/common/jsonWorker.ts +++ b/src/vs/languages/json/common/jsonWorker.ts @@ -68,7 +68,7 @@ export interface IJSONWorkerContribution { collectDefaultSuggestions(resource: URI, result: ISuggestionsCollector): WinJS.Promise; } -export class JSONWorker implements Modes.IExtraInfoSupport { +export class JSONWorker { private schemaService: SchemaService.IJSONSchemaService; private requestService: IRequestService; @@ -275,7 +275,7 @@ export class JSONWorker implements Modes.IExtraInfoSupport { return this.jsonIntellisense.doSuggest(resource, modelMirror, position); } - public computeInfo(resource:URI, position:EditorCommon.IPosition): WinJS.TPromise { + public provideHover(resource:URI, position:EditorCommon.IPosition): WinJS.TPromise { var modelMirror = this.resourceService.get(resource); @@ -333,11 +333,10 @@ export class JSONWorker implements Modes.IExtraInfoSupport { }); } - private createInfoResult(htmlContent : HtmlContent.IHTMLContentElement[], node: Parser.ASTNode, modelMirror: EditorCommon.IMirrorModel) : Modes.IComputeExtraInfoResult { + private createInfoResult(htmlContent : HtmlContent.IHTMLContentElement[], node: Parser.ASTNode, modelMirror: EditorCommon.IMirrorModel) : Modes.Hover { var range = modelMirror.getRangeFromOffsetAndLength(node.start, node.end - node.start); - var result:Modes.IComputeExtraInfoResult = { - value: '', + var result:Modes.Hover = { htmlContent: htmlContent, range: range }; diff --git a/src/vs/languages/json/test/common/jsonworker.test.ts b/src/vs/languages/json/test/common/jsonworker.test.ts index 66d05cc17ab..3fbc5519946 100644 --- a/src/vs/languages/json/test/common/jsonworker.test.ts +++ b/src/vs/languages/json/test/common/jsonworker.test.ts @@ -62,11 +62,11 @@ suite('JSON - Worker', () => { return env.worker.suggest(url, position).then(result => result[0]); }; - function testComputeInfo(content:string, schema:jsonSchema.IJSONSchema, position:EditorCommon.IPosition):WinJS.TPromise { + function testComputeInfo(content:string, schema:jsonSchema.IJSONSchema, position:EditorCommon.IPosition):WinJS.TPromise { var url = URI.parse('test://test.json'); var env = mockWorkerEnv(url, content); prepareSchemaServer(schema, env.worker); - return env.worker.computeInfo(url, position); + return env.worker.provideHover(url, position); } var testValueSetFor = function(value:string, schema:jsonSchema.IJSONSchema, selection:string, selectionLength: number, up: boolean):WinJS.TPromise { diff --git a/src/vs/languages/less/common/less.ts b/src/vs/languages/less/common/less.ts index 3888b763c89..a901b9ac59b 100644 --- a/src/vs/languages/less/common/less.ts +++ b/src/vs/languages/less/common/less.ts @@ -23,6 +23,8 @@ import {DeclarationSupport} from 'vs/editor/common/modes/supports/declarationSup import {ReferenceSupport} from 'vs/editor/common/modes/supports/referenceSupport'; import {SuggestSupport} from 'vs/editor/common/modes/supports/suggestSupport'; import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService'; +import {CancellationToken} from 'vs/base/common/cancellation'; +import {wireCancellationToken} from 'vs/base/common/async'; export var language: Types.ILanguage = { displayName: 'LESS', @@ -265,9 +267,13 @@ export class LESSMode extends Monarch.MonarchMode implements Modes.IExtraInfoSup return this._worker((w) => w.suggest(resource, position)); } - static $computeInfo = OneWorkerAttr(LESSMode, LESSMode.prototype.computeInfo); - public computeInfo(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { - return this._worker((w) => w.computeInfo(resource, position)); + public provideHover(model:EditorCommon.IModel, position:EditorCommon.IEditorPosition, cancellationToken:CancellationToken): Thenable { + return wireCancellationToken(cancellationToken, this._provideHover(model.getAssociatedResource(), position)); + } + + static $_provideHover = OneWorkerAttr(LESSMode, LESSMode.prototype._provideHover); + public _provideHover(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { + return this._worker((w) => w.provideHover(resource, position)); } static $getOutline = OneWorkerAttr(LESSMode, LESSMode.prototype.getOutline); diff --git a/src/vs/languages/sass/common/sass.ts b/src/vs/languages/sass/common/sass.ts index 4d265627143..0838d88695a 100644 --- a/src/vs/languages/sass/common/sass.ts +++ b/src/vs/languages/sass/common/sass.ts @@ -23,6 +23,8 @@ import {DeclarationSupport} from 'vs/editor/common/modes/supports/declarationSup import {ReferenceSupport} from 'vs/editor/common/modes/supports/referenceSupport'; import {SuggestSupport} from 'vs/editor/common/modes/supports/suggestSupport'; import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService'; +import {CancellationToken} from 'vs/base/common/cancellation'; +import {wireCancellationToken} from 'vs/base/common/async'; export var language = { displayName: 'Sass', @@ -367,9 +369,13 @@ export class SASSMode extends Monarch.MonarchMode implements Modes.IExtraInfoSup return this._worker((w) => w.suggest(resource, position)); } - static $computeInfo = OneWorkerAttr(SASSMode, SASSMode.prototype.computeInfo); - public computeInfo(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { - return this._worker((w) => w.computeInfo(resource, position)); + public provideHover(model:EditorCommon.IModel, position:EditorCommon.IEditorPosition, cancellationToken:CancellationToken): Thenable { + return wireCancellationToken(cancellationToken, this._provideHover(model.getAssociatedResource(), position)); + } + + static $_provideHover = OneWorkerAttr(SASSMode, SASSMode.prototype._provideHover); + public _provideHover(resource:URI, position:EditorCommon.IPosition): winjs.TPromise { + return this._worker((w) => w.provideHover(resource, position)); } static $getOutline = OneWorkerAttr(SASSMode, SASSMode.prototype.getOutline); diff --git a/src/vs/languages/typescript/common/languageFeatures.ts b/src/vs/languages/typescript/common/languageFeatures.ts index 95faecdaf18..dbd2b1a13aa 100644 --- a/src/vs/languages/typescript/common/languageFeatures.ts +++ b/src/vs/languages/typescript/common/languageFeatures.ts @@ -15,6 +15,7 @@ import {IMarkerService, IMarkerData} from 'vs/platform/markers/common/markers'; import {IModelService} from 'vs/editor/common/services/modelService'; import {TypeScriptWorkerProtocol, LanguageServiceDefaults} from 'vs/languages/typescript/common/typescript'; import * as ts from 'vs/languages/typescript/common/lib/typescriptServices'; +import {CancellationToken} from 'vs/base/common/cancellation'; export function register(modelService: IModelService, markerService: IMarkerService, selector: string, defaults:LanguageServiceDefaults, worker: (first: URI, ...more: URI[]) => TPromise): lifecycle.IDisposable { @@ -301,16 +302,18 @@ class ParameterHintsAdapter extends Adapter implements modes.IParameterHintsSupp class QuickInfoAdapter extends Adapter implements modes.IExtraInfoSupport { - computeInfo(resource: URI, position: editor.IPosition): TPromise { + provideHover(model:editor.IModel, position:editor.IEditorPosition, cancellationToken:CancellationToken): TPromise { + let resource = model.getAssociatedResource(); + return this._worker(resource).then(worker => { return worker.getQuickInfoAtPosition(resource.toString(), this._positionToOffset(resource, position)); }).then(info => { if (!info) { return; } - return { + return { range: this._textSpanToRange(resource, info.textSpan), - value: ts.displayPartsToString(info.displayParts) + htmlContent: [{ text: ts.displayPartsToString(info.displayParts) }] }; }); } diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 666c57b577e..25bc8c2d8f5 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -248,7 +248,7 @@ class ExtHostApiCommands { resource, position: position && typeConverters.fromPosition(position) }; - return this._commands.executeCommand('_executeHoverProvider', args).then(value => { + return this._commands.executeCommand('_executeHoverProvider', args).then(value => { if (Array.isArray(value)) { return value.map(typeConverters.toHover); } diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index affd9c899d9..54413f99f38 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -11,13 +11,14 @@ import {Remotable, IThreadService} from 'vs/platform/thread/common/thread'; import * as vscode from 'vscode'; import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import {Range, DocumentHighlightKind, Disposable, SignatureHelp, CompletionList} from 'vs/workbench/api/node/extHostTypes'; -import {IPosition, IRange, ISingleEditOperation} from 'vs/editor/common/editorCommon'; +import {IModel, IEditorPosition, 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 {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} from 'vs/base/common/async'; +import {asWinJsPromise, ShallowCancelThenPromise, wireCancellationToken} from 'vs/base/common/async'; +import {CancellationToken} from 'vs/base/common/cancellation'; // --- adapter @@ -188,7 +189,7 @@ class DeclarationAdapter implements modes.IDeclarationSupport { } } -class ExtraInfoAdapter implements modes.IExtraInfoSupport { +class ExtraInfoAdapter { private _documents: ExtHostModelService; private _provider: vscode.HoverProvider; @@ -198,7 +199,7 @@ class ExtraInfoAdapter implements modes.IExtraInfoSupport { this._provider = provider; } - computeInfo(resource: URI, position: IPosition): TPromise { + public provideHover(resource: URI, position: IPosition): TPromise { let doc = this._documents.getDocumentData(resource).document; let pos = TypeConverters.toPosition(position); @@ -710,8 +711,8 @@ export class ExtHostLanguageFeatures { return this._createDisposable(handle); } - $computeInfo(handle: number, resource: URI, position: IPosition): TPromise { - return this._withAdapter(handle, ExtraInfoAdapter, adpater => adpater.computeInfo(resource, position)); + $provideHover(handle: number, resource: URI, position: IPosition): TPromise { + return this._withAdapter(handle, ExtraInfoAdapter, adpater => adpater.provideHover(resource, position)); } // --- occurrences @@ -911,8 +912,8 @@ export class MainThreadLanguageFeatures { $registerExtraInfoSupport(handle: number, selector: vscode.DocumentSelector): TPromise { this._registrations[handle] = modes.ExtraInfoRegistry.register(selector, { - computeInfo: (resource: URI, position: IPosition): TPromise => { - return this._proxy.$computeInfo(handle, resource, position); + provideHover: (model:IModel, position:IEditorPosition, cancellationToken:CancellationToken): Thenable => { + return wireCancellationToken(cancellationToken, this._proxy.$provideHover(handle, model.getAssociatedResource(), position)); } }); return undefined; diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 82350281925..b12567eda9b 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -338,14 +338,14 @@ export const location = { } }; -export function fromHover(hover: vscode.Hover): modes.IComputeExtraInfoResult { - return { +export function fromHover(hover: vscode.Hover): modes.Hover { + return { range: fromRange(hover.range), htmlContent: hover.contents.map(fromFormattedString) }; } -export function toHover(info: modes.IComputeExtraInfoResult): types.Hover { +export function toHover(info: modes.Hover): types.Hover { return new types.Hover(info.htmlContent.map(toFormattedString), toRange(info.range)); } diff --git a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts index aa24632ea5a..a16530a2271 100644 --- a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts @@ -11,6 +11,7 @@ import URI from 'vs/base/common/uri'; import * as types from 'vs/workbench/api/node/extHostTypes'; import * as EditorCommon from 'vs/editor/common/editorCommon'; import {Model as EditorModel} from 'vs/editor/common/model/model'; +import {Position as EditorPosition} from 'vs/editor/common/core/position'; import {TestThreadService} from './testThreadService'; import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection'; import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService'; @@ -359,7 +360,7 @@ suite('ExtHostLanguageFeatures', function() { threadService.sync().then(() => { - getExtraInfoAtPosition(model, { lineNumber: 1, column: 1 }).then(value => { + getExtraInfoAtPosition(model, new EditorPosition(1, 1)).then(value => { assert.equal(value.length, 1); let [entry] = value; @@ -379,7 +380,7 @@ suite('ExtHostLanguageFeatures', function() { threadService.sync().then(() => { - getExtraInfoAtPosition(model, { lineNumber: 1, column: 1 }).then(value => { + getExtraInfoAtPosition(model, new EditorPosition(1, 1)).then(value => { assert.equal(value.length, 1); let [entry] = value; assert.deepEqual(entry.range, { startLineNumber: 4, startColumn: 1, endLineNumber: 9, endColumn: 8 }); @@ -405,7 +406,7 @@ suite('ExtHostLanguageFeatures', function() { threadService.sync().then(() => { - getExtraInfoAtPosition(model, { lineNumber: 1, column: 1 }).then(value => { + getExtraInfoAtPosition(model, new EditorPosition(1, 1)).then(value => { assert.equal(value.length, 2); let [first, second] = value; assert.equal(first.htmlContent[0].markdown, 'registered second'); @@ -433,7 +434,7 @@ suite('ExtHostLanguageFeatures', function() { threadService.sync().then(() => { - getExtraInfoAtPosition(model, { lineNumber: 1, column: 1 }).then(value => { + getExtraInfoAtPosition(model, new EditorPosition(1, 1)).then(value => { assert.equal(value.length, 1); done();