diff --git a/src/vs/workbench/buildfile.js b/src/vs/workbench/buildfile.js index d7ba5b7a778..d507ae0f931 100644 --- a/src/vs/workbench/buildfile.js +++ b/src/vs/workbench/buildfile.js @@ -29,6 +29,7 @@ exports.collectModules = function(excludes) { createModuleDescription('vs/workbench/parts/output/common/outputMode', languageMainExcludes), createModuleDescription('vs/workbench/parts/output/common/outputWorker', languageWorkerExcludes), + createModuleDescription('vs/workbench/parts/output/common/outputLinkComputer', ['vs/base/common/worker/simpleWorker', 'vs/editor/common/services/editorSimpleWorker']), createModuleDescription('vs/workbench/parts/output/browser/outputPanel', excludes), createModuleDescription('vs/workbench/parts/debug/browser/debugViewlet', excludes), diff --git a/src/vs/workbench/parts/output/browser/output.contribution.ts b/src/vs/workbench/parts/output/browser/output.contribution.ts index aa12c7a87fd..8ea674777e0 100644 --- a/src/vs/workbench/parts/output/browser/output.contribution.ts +++ b/src/vs/workbench/parts/output/browser/output.contribution.ts @@ -25,15 +25,22 @@ import {ContextKeyExpr} from 'vs/platform/contextkey/common/contextkey'; registerSingleton(IOutputService, OutputService); // Register Output Mode -ModesRegistry.registerCompatMode({ +ModesRegistry.registerLanguage({ id: OUTPUT_MODE_ID, extensions: [], aliases: [null], - mimetypes: [OUTPUT_MIME], - moduleId: 'vs/workbench/parts/output/common/outputMode', - ctorName: 'OutputMode' + mimetypes: [OUTPUT_MIME] }); +// ModesRegistry.registerCompatMode({ +// id: OUTPUT_MODE_ID, +// extensions: [], +// aliases: [null], +// mimetypes: [OUTPUT_MIME], +// moduleId: 'vs/workbench/parts/output/common/outputMode', +// ctorName: 'OutputMode' +// }); + // Register Output Panel (platform.Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( 'vs/workbench/parts/output/browser/outputPanel', diff --git a/src/vs/workbench/parts/output/browser/outputServices.ts b/src/vs/workbench/parts/output/browser/outputServices.ts index 2bc297f9335..370d2d82212 100644 --- a/src/vs/workbench/parts/output/browser/outputServices.ts +++ b/src/vs/workbench/parts/output/browser/outputServices.ts @@ -17,6 +17,9 @@ import {IOutputEvent, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_I import {OutputEditorInput} from 'vs/workbench/parts/output/browser/outputEditorInput'; import {OutputPanel} from 'vs/workbench/parts/output/browser/outputPanel'; import {IPanelService} from 'vs/workbench/services/panel/common/panelService'; +import {IModelService} from 'vs/editor/common/services/modelService'; +import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; +import {OutputLinkProvider} from 'vs/workbench/parts/output/common/outputLinkProvider'; const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; @@ -31,12 +34,16 @@ export class OutputService implements IOutputService { private _onOutputChannel: Emitter; private _onActiveOutputChannel: Emitter; + private _outputLinkDetector: OutputLinkProvider; + constructor( @IStorageService private storageService: IStorageService, @IInstantiationService private instantiationService: IInstantiationService, @IEventService private eventService: IEventService, @ILifecycleService private lifecycleService: ILifecycleService, - @IPanelService private panelService: IPanelService + @IPanelService private panelService: IPanelService, + @IWorkspaceContextService contextService:IWorkspaceContextService, + @IModelService modelService: IModelService ) { this._onOutput = new Emitter(); this._onOutputChannel = new Emitter(); @@ -46,6 +53,8 @@ export class OutputService implements IOutputService { const channels = Registry.as(Extensions.OutputChannels).getChannels(); this.activeChannelId = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, channels && channels.length > 0 ? channels[0].id : null); + + this._outputLinkDetector = new OutputLinkProvider(contextService, modelService); } public get onOutput(): Event { diff --git a/src/vs/workbench/parts/output/common/outputLinkComputer.ts b/src/vs/workbench/parts/output/common/outputLinkComputer.ts new file mode 100644 index 00000000000..1404962eeba --- /dev/null +++ b/src/vs/workbench/parts/output/common/outputLinkComputer.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * 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 {IMirrorModel, IWorkerContext} from 'vs/editor/common/services/editorSimpleWorker'; +import {OutputWorker, IResourceCreator} from 'vs/workbench/parts/output/common/outputWorker'; +import {ILink} from 'vs/editor/common/modes'; +import {TPromise} from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import paths = require('vs/base/common/paths'); + +export interface ICreateData { + workspaceResourceUri: string; +} + +export class OutputLinkComputer { + + private _ctx:IWorkerContext; + private _patterns: RegExp[]; + private _workspaceResource: URI; + + constructor(ctx:IWorkerContext, createData:ICreateData) { + this._ctx = ctx; + this._workspaceResource = URI.parse(createData.workspaceResourceUri); + this._patterns = OutputWorker.createPatterns(this._workspaceResource); + } + + private _getModel(uri:string): IMirrorModel { + let models = this._ctx.getMirrorModels(); + for (let i = 0; i < models.length; i++) { + let model = models[i]; + if (model.uri.toString() === uri) { + return model; + } + } + return null; + } + + public computeLinks(uri:string): TPromise { + let model = this._getModel(uri); + if (!model) { + return; + } + + let links: ILink[] = []; + + let resourceCreator: IResourceCreator = { + toResource: (workspaceRelativePath: string): URI => { + if (typeof workspaceRelativePath === 'string') { + return URI.file(paths.join(this._workspaceResource.fsPath, workspaceRelativePath)); + } + return null; + } + }; + + let lines = model.getValue().split(/\r\n|\r|\n/); + for (let i = 0, len = lines.length; i < len; i++) { + links.push(...OutputWorker.detectLinks(lines[i], i + 1, this._patterns, resourceCreator)); + } + + return TPromise.as(links); + } +} + +export function create(ctx:IWorkerContext, createData:ICreateData): OutputLinkComputer { + return new OutputLinkComputer(ctx, createData); +} diff --git a/src/vs/workbench/parts/output/common/outputLinkProvider.ts b/src/vs/workbench/parts/output/common/outputLinkProvider.ts new file mode 100644 index 00000000000..a15437c5bfe --- /dev/null +++ b/src/vs/workbench/parts/output/common/outputLinkProvider.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * 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 URI from 'vs/base/common/uri'; +import {RunOnceScheduler, wireCancellationToken} from 'vs/base/common/async'; +import {IModelService} from 'vs/editor/common/services/modelService'; +import {LinkProviderRegistry, ILink} from 'vs/editor/common/modes'; +import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; +import {OUTPUT_MODE_ID} from 'vs/workbench/parts/output/common/output'; +import {MonacoWebWorker, createWebWorker} from 'vs/editor/common/services/webWorker'; +import {ICreateData, OutputLinkComputer} from 'vs/workbench/parts/output/common/outputLinkComputer'; + +export class OutputLinkProvider { + + private static DISPOSE_WORKER_TIME = 3 * 60 * 1000; // dispose worker after 3 minutes of inactivity + + private _modelService: IModelService; + private _workspaceResource: URI; + + private _worker: MonacoWebWorker; + private _disposeWorker: RunOnceScheduler; + + constructor( + contextService:IWorkspaceContextService, + modelService: IModelService + ) { + let workspace = contextService.getWorkspace(); + + // Does not do anything unless there is a workspace... + if (workspace) { + this._modelService = modelService; + + this._workspaceResource = workspace.resource; + + LinkProviderRegistry.register(OUTPUT_MODE_ID, { + provideLinks: (model, token): Thenable => { + return wireCancellationToken(token, this._provideLinks(model.uri)); + } + }); + + this._worker = null; + this._disposeWorker = new RunOnceScheduler(() => { + if (this._worker) { + this._worker.dispose(); + this._worker = null; + } + }, OutputLinkProvider.DISPOSE_WORKER_TIME); + } + } + + private _getOrCreateWorker(): MonacoWebWorker { + this._disposeWorker.schedule(); + if (!this._worker) { + let createData:ICreateData = { + workspaceResourceUri: this._workspaceResource.toString() + }; + this._worker = createWebWorker(this._modelService, { + moduleId: 'vs/workbench/parts/output/common/outputLinkComputer', + createData: createData, + label: 'outputLinkComputer' + }); + } + return this._worker; + } + + private _provideLinks(modelUri:URI): TPromise { + return this._getOrCreateWorker().withSyncedResources([modelUri]).then((linkComputer) => { + return linkComputer.computeLinks(modelUri.toString()); + }); + } +}