diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts index 9983c542be3..032bd82375b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts @@ -144,4 +144,5 @@ suite('languages namespace tests', () => { assert.ok(ran); assert.equal(result!.items[0].label, 'foo'); }); + }); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 7d6e36ba985..b864e628aa8 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -983,7 +983,7 @@ export interface IInplaceReplaceSupportResult { */ export interface ILink { range: IRange; - url?: string; + url?: URI | string; } /** * A provider of links. diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index 5a75d3db2e5..933fd3f3121 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -33,14 +33,18 @@ export class Link implements ILink { return this._link.range; } - get url(): string | undefined { + get url(): URI | string | undefined { return this._link.url; } resolve(token: CancellationToken): Promise { if (this._link.url) { try { - return Promise.resolve(URI.parse(this._link.url)); + if (typeof this._link.url === 'string') { + return Promise.resolve(URI.parse(this._link.url)); + } else { + return Promise.resolve(this._link.url); + } } catch (e) { return Promise.reject(new Error('invalid')); } diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index d224f2254ad..2117d69f0ed 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -111,7 +111,7 @@ class LinkOccurrence { } private static _getOptions(link: Link, useMetaKey: boolean, isActive: boolean): ModelDecorationOptions { - if (link.url && /^command:/i.test(link.url)) { + if (link.url && /^command:/i.test(link.url.toString())) { if (useMetaKey) { return (isActive ? decoration.metaCommandActive : decoration.metaCommand); } else { @@ -341,7 +341,7 @@ class LinkDetector implements editorCommon.IEditorContribution { }, err => { // different error cases if (err === 'invalid') { - this.notificationService.warn(nls.localize('invalid.url', 'Failed to open this link because it is not well-formed: {0}', link.url)); + this.notificationService.warn(nls.localize('invalid.url', 'Failed to open this link because it is not well-formed: {0}', link.url.toString())); } else if (err === 'missing') { this.notificationService.warn(nls.localize('missing.url', 'Failed to open this link because its target is missing.')); } else { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 004d345c019..64578dc4b1d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5209,7 +5209,7 @@ declare namespace monaco.languages { */ export interface ILink { range: IRange; - url?: string; + url?: Uri | string; } /** diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 5e2b2f62843..73dbc49ae4e 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Position as EditorPosition } from 'vs/editor/common/core/position'; import { Range as EditorRange } from 'vs/editor/common/core/range'; -import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto } from '../node/extHost.protocol'; +import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto } from '../node/extHost.protocol'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; import { IHeapService } from './mainThreadHeapService'; @@ -106,6 +106,13 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return data; } + private static _reviveLinkDTO(data: LinkDto): modes.ILink { + if (data.url && typeof data.url !== 'string') { + data.url = URI.revive(data.url); + } + return data; + } + //#endregion // --- outline @@ -376,12 +383,18 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha this._registrations[handle] = modes.LinkProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { provideLinks: (model, token) => { return this._proxy.$provideDocumentLinks(handle, model.uri, token).then(dto => { - if (dto) { dto.forEach(obj => this._heapService.trackObject(obj)); } + if (dto) { + dto.forEach(obj => { + MainThreadLanguageFeatures._reviveLinkDTO(obj); + this._heapService.trackObject(obj); + }); + } return dto; }); }, resolveLink: (link, token) => { return this._proxy.$resolveDocumentLink(handle, link, token).then(obj => { + MainThreadLanguageFeatures._reviveLinkDTO(obj); this._heapService.trackObject(obj); return obj; }); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 5fe3aa05406..24b5c1b024a 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -890,7 +890,10 @@ export interface CodeActionDto { isPreferred?: boolean; } -export type LinkDto = ObjectIdentifier & modes.ILink; +export interface LinkDto extends ObjectIdentifier { + range: IRange; + url?: string | UriComponents; +} export interface CodeLensDto extends ObjectIdentifier { range: IRange; diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index a1e8edc9204..cb8b9b548f2 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -8,7 +8,7 @@ import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystem import * as vscode from 'vscode'; import * as files from 'vs/platform/files/common/files'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; -import { FileChangeType, DocumentLink } from 'vs/workbench/api/node/extHostTypes'; +import { FileChangeType } from 'vs/workbench/api/node/extHostTypes'; import * as typeConverter from 'vs/workbench/api/node/extHostTypeConverters'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; import { Schemas } from 'vs/base/common/network'; @@ -94,11 +94,9 @@ class FsLinkProvider { }, this._stateMachine); for (const link of links) { - try { - let uri = URI.parse(link.url, true); - result.push(new DocumentLink(typeConverter.Range.to(link.range), uri)); - } catch (err) { - // ignore + let docLink = typeConverter.DocumentLink.to(link); + if (docLink.target) { + result.push(docLink); } } return result; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 284df80be28..fc0a93e2f08 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -1364,11 +1364,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise { + $provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise { return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token)); } - $resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Promise { + $resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Promise { return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token)); } diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 43e16f8e559..6b98bb052ba 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -807,12 +807,20 @@ export namespace DocumentLink { export function from(link: vscode.DocumentLink): modes.ILink { return { range: Range.from(link.range), - url: link.target && link.target.toString() + url: link.target }; } export function to(link: modes.ILink): vscode.DocumentLink { - return new types.DocumentLink(Range.to(link.range), link.url ? URI.parse(link.url) : undefined); + let target = undefined; + if (link.url) { + try { + target = typeof link.url === 'string' ? URI.parse(link.url, true) : URI.revive(link.url); + } catch (err) { + // ignore + } + } + return new types.DocumentLink(Range.to(link.range), target); } }