registerDocumentLinkProvider
This commit is contained in:
parent
7c6e9672cd
commit
b5287d4621
9 changed files with 221 additions and 22 deletions
54
src/vs/vscode.d.ts
vendored
54
src/vs/vscode.d.ts
vendored
|
@ -2312,6 +2312,47 @@ declare namespace vscode {
|
|||
resolveCompletionItem?(item: CompletionItem, token: CancellationToken): CompletionItem | Thenable<CompletionItem>;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A document link is a range in a text document that links to an internal or external resource, like another
|
||||
* text document or a web site.
|
||||
*/
|
||||
export class DocumentLink {
|
||||
|
||||
/**
|
||||
* The range this link applies to.
|
||||
*/
|
||||
range: Range;
|
||||
|
||||
/**
|
||||
* The uri this link points to.
|
||||
*/
|
||||
target: Uri;
|
||||
|
||||
/**
|
||||
* Creates a new document link.
|
||||
*
|
||||
* @param range The range the document link applies to. Must not be empty.
|
||||
* @param target The uri the document link points to.
|
||||
*/
|
||||
constructor(range: Range, target: Uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* The document link provider defines the contract between extensions and feature of showing
|
||||
* links in the editor.
|
||||
*/
|
||||
export interface DocumentLinkProvider {
|
||||
|
||||
/**
|
||||
* @param document The document in which the command was invoked.
|
||||
* @param token A cancellation token.
|
||||
* @return An array of [document links](#DocumentLink) or a thenable that resolves to such. The lack of a result
|
||||
* can be signaled by returning `undefined`, `null`, or an empty array.
|
||||
*/
|
||||
provideDocumentLinks(document: TextDocument, token: CancellationToken): DocumentLink[] | Thenable<DocumentLink[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tuple of two characters, like a pair of
|
||||
* opening and closing brackets.
|
||||
|
@ -3753,6 +3794,19 @@ declare namespace vscode {
|
|||
*/
|
||||
export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable;
|
||||
|
||||
/**
|
||||
* Register a document link provider.
|
||||
*
|
||||
* Multiple providers can be registered for a language. In that case providers are asked in
|
||||
* parallel and the results are merged. A failing provider (rejected promise or exception) will
|
||||
* not cause a failure of the whole operation.
|
||||
*
|
||||
* @param selector A selector that defines the documents this provider is applicable to.
|
||||
* @param provider A document link provider.
|
||||
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
|
||||
*/
|
||||
export function registerDocumentLinkProvider(selector: DocumentSelector, provider: DocumentLinkProvider): Disposable;
|
||||
|
||||
/**
|
||||
* Set a [language configuration](#LanguageConfiguration) for a language.
|
||||
*
|
||||
|
|
|
@ -74,6 +74,7 @@ export class ExtHostAPIImplementation {
|
|||
CompletionItem: typeof vscode.CompletionItem;
|
||||
CompletionItemKind: typeof vscode.CompletionItemKind;
|
||||
CompletionList: typeof vscode.CompletionList;
|
||||
DocumentLink: typeof vscode.DocumentLink;
|
||||
IndentAction: typeof vscode.IndentAction;
|
||||
OverviewRulerLane: typeof vscode.OverviewRulerLane;
|
||||
TextEditorRevealType: typeof vscode.TextEditorRevealType;
|
||||
|
@ -150,6 +151,7 @@ export class ExtHostAPIImplementation {
|
|||
this.CompletionItem = extHostTypes.CompletionItem;
|
||||
this.CompletionItemKind = extHostTypes.CompletionItemKind;
|
||||
this.CompletionList = extHostTypes.CompletionList;
|
||||
this.DocumentLink = extHostTypes.DocumentLink;
|
||||
this.ViewColumn = extHostTypes.ViewColumn;
|
||||
this.StatusBarAlignment = extHostTypes.StatusBarAlignment;
|
||||
this.IndentAction = Modes.IndentAction;
|
||||
|
@ -369,6 +371,9 @@ export class ExtHostAPIImplementation {
|
|||
registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable {
|
||||
return languageFeatures.registerCompletionItemProvider(selector, provider, triggerCharacters);
|
||||
},
|
||||
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
|
||||
return languageFeatures.registerDocumentLinkProvider(selector, provider);
|
||||
},
|
||||
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration):vscode.Disposable => {
|
||||
return languageFeatures.setLanguageConfiguration(language, configuration);
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ export abstract class MainThreadLanguageFeaturesShape {
|
|||
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> { throw ni(); }
|
||||
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[]): TPromise<any> { throw ni(); }
|
||||
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise<any> { throw ni(); }
|
||||
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> { throw ni(); }
|
||||
$setLanguageConfiguration(handle: number, languageId:string, configuration: vscode.LanguageConfiguration): TPromise<any> { throw ni(); }
|
||||
}
|
||||
|
||||
|
@ -267,6 +268,8 @@ export abstract class ExtHostLanguageFeaturesShape {
|
|||
$provideCompletionItems(handle: number, resource: URI, position: editorCommon.IPosition): TPromise<modes.ISuggestResult[]> { throw ni(); }
|
||||
$resolveCompletionItem(handle: number, resource: URI, position: editorCommon.IPosition, suggestion: modes.ISuggestion): TPromise<modes.ISuggestion> { throw ni(); }
|
||||
$provideSignatureHelp(handle: number, resource: URI, position: editorCommon.IPosition): TPromise<modes.SignatureHelp> { throw ni(); }
|
||||
$providDocumentLinks(handle: number, resource: URI): TPromise<modes.ILink[]> { throw ni(); }
|
||||
|
||||
}
|
||||
|
||||
export abstract class ExtHostQuickOpenShape {
|
||||
|
|
|
@ -588,10 +588,31 @@ class SignatureHelpAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
class LinkProviderAdapter {
|
||||
|
||||
private _documents: ExtHostDocuments;
|
||||
private _provider: vscode.DocumentLinkProvider;
|
||||
|
||||
constructor(documents: ExtHostDocuments, provider: vscode.DocumentLinkProvider) {
|
||||
this._documents = documents;
|
||||
this._provider = provider;
|
||||
}
|
||||
|
||||
provideLinks(resource: URI): TPromise<modes.ILink[]> {
|
||||
const doc = this._documents.getDocumentData(resource).document;
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideDocumentLinks(doc, token)).then(links => {
|
||||
if (Array.isArray(links)) {
|
||||
return links.map(TypeConverters.DocumentLink.from);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
|
||||
| DocumentHighlightAdapter | ReferenceAdapter | QuickFixAdapter | DocumentFormattingAdapter
|
||||
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
|
||||
| SuggestAdapter | SignatureHelpAdapter;
|
||||
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter;
|
||||
|
||||
export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape {
|
||||
|
||||
|
@ -821,6 +842,19 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape {
|
|||
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(resource, position));
|
||||
}
|
||||
|
||||
// --- links
|
||||
|
||||
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
|
||||
const handle = this._nextHandle();
|
||||
this._adapter[handle] = new LinkProviderAdapter(this._documents, provider);
|
||||
this._proxy.$registerDocumentLinkProvider(handle, selector);
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
$providDocumentLinks(handle: number, resource: URI): TPromise<modes.ILink[]> {
|
||||
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(resource));
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
setLanguageConfiguration(languageId:string, configuration: vscode.LanguageConfiguration): vscode.Disposable {
|
||||
|
|
|
@ -318,6 +318,19 @@ export namespace SignatureHelp {
|
|||
}
|
||||
}
|
||||
|
||||
export namespace DocumentLink {
|
||||
|
||||
export function from(link: types.DocumentLink): modes.ILink {
|
||||
return {
|
||||
range: fromRange(link.range),
|
||||
url: link.target.toString()
|
||||
};
|
||||
}
|
||||
|
||||
export function to(link: modes.ILink):types.DocumentLink {
|
||||
return new types.DocumentLink(toRange(link.range), URI.parse(link.url));
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Command {
|
||||
|
||||
|
|
|
@ -206,6 +206,17 @@ export class Position {
|
|||
|
||||
export class Range {
|
||||
|
||||
static is(thing: any): thing is Range {
|
||||
if (thing instanceof Range) {
|
||||
return true;
|
||||
}
|
||||
if (!thing) {
|
||||
return false;
|
||||
}
|
||||
return Position.is((<Range>thing).start)
|
||||
&& Position.is((<Range>thing.end));
|
||||
}
|
||||
|
||||
protected _start: Position;
|
||||
protected _end: Position;
|
||||
|
||||
|
@ -769,3 +780,21 @@ export enum TextEditorRevealType {
|
|||
InCenter = 1,
|
||||
InCenterIfOutsideViewport = 2
|
||||
}
|
||||
|
||||
export class DocumentLink {
|
||||
|
||||
range: Range;
|
||||
|
||||
target: URI;
|
||||
|
||||
constructor(range: Range, target: URI) {
|
||||
if (!(target instanceof URI)) {
|
||||
throw illegalArgument('target');
|
||||
}
|
||||
if (!Range.is(range) || range.isEmpty) {
|
||||
throw illegalArgument('range');
|
||||
}
|
||||
this.range = range;
|
||||
this.target = target;
|
||||
}
|
||||
}
|
|
@ -201,6 +201,17 @@ export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape
|
|||
return undefined;
|
||||
}
|
||||
|
||||
// --- links
|
||||
|
||||
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
|
||||
this._registrations[handle] = modes.LinkProviderRegistry.register(selector, <modes.LinkProvider>{
|
||||
provideLinks: (model, token) => {
|
||||
return wireCancellationToken(token, this._proxy.$providDocumentLinks(handle, model.uri));
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
$setLanguageConfiguration(handle: number, languageId: string, configuration: vscode.LanguageConfiguration): TPromise<any> {
|
||||
|
|
|
@ -35,6 +35,7 @@ import {rename} from 'vs/editor/contrib/rename/common/rename';
|
|||
import {provideSignatureHelp} from 'vs/editor/contrib/parameterHints/common/parameterHints';
|
||||
import {provideSuggestionItems} from 'vs/editor/contrib/suggest/common/suggest';
|
||||
import {getDocumentFormattingEdits, getDocumentRangeFormattingEdits, getOnTypeFormattingEdits} from 'vs/editor/contrib/format/common/format';
|
||||
import {getLinks} from 'vs/editor/contrib/links/common/links';
|
||||
import {asWinJsPromise} from 'vs/base/common/async';
|
||||
import {MainContext, ExtHostContext} from 'vs/workbench/api/node/extHost.protocol';
|
||||
import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics';
|
||||
|
@ -949,4 +950,48 @@ suite('ExtHostLanguageFeatures', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Links, data conversion', function () {
|
||||
|
||||
disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, <vscode.DocumentLinkProvider>{
|
||||
provideDocumentLinks() {
|
||||
return [new types.DocumentLink(new types.Range(0, 0, 1, 1), types.Uri.parse('foo:bar#3'))];
|
||||
}
|
||||
}));
|
||||
|
||||
return threadService.sync().then(() => {
|
||||
return getLinks(model).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
let [first] = value;
|
||||
|
||||
assert.equal(first.url, 'foo:bar#3');
|
||||
assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Links, evil provider', function () {
|
||||
|
||||
disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, <vscode.DocumentLinkProvider>{
|
||||
provideDocumentLinks() {
|
||||
return [new types.DocumentLink(new types.Range(0, 0, 1, 1), types.Uri.parse('foo:bar#3'))];
|
||||
}
|
||||
}));
|
||||
|
||||
disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, <vscode.DocumentLinkProvider>{
|
||||
provideDocumentLinks(): any {
|
||||
throw new Error();
|
||||
}
|
||||
}));
|
||||
|
||||
return threadService.sync().then(() => {
|
||||
return getLinks(model).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
let [first] = value;
|
||||
|
||||
assert.equal(first.url, 'foo:bar#3');
|
||||
assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,9 +16,9 @@ function assertToJSON(a: any, expected: any) {
|
|||
assert.deepEqual(actual, expected);
|
||||
}
|
||||
|
||||
suite('ExtHostTypes', function() {
|
||||
suite('ExtHostTypes', function () {
|
||||
|
||||
test('URI, toJSON', function() {
|
||||
test('URI, toJSON', function () {
|
||||
|
||||
let uri = URI.parse('file:///path/test.file');
|
||||
let data = uri.toJSON();
|
||||
|
@ -34,7 +34,7 @@ suite('ExtHostTypes', function() {
|
|||
});
|
||||
});
|
||||
|
||||
test('Disposable', function() {
|
||||
test('Disposable', function () {
|
||||
|
||||
let count = 0;
|
||||
let d = new types.Disposable(() => {
|
||||
|
@ -61,7 +61,7 @@ suite('ExtHostTypes', function() {
|
|||
|
||||
});
|
||||
|
||||
test('Position', function() {
|
||||
test('Position', function () {
|
||||
assert.throws(() => new types.Position(-1, 0));
|
||||
assert.throws(() => new types.Position(0, -1));
|
||||
|
||||
|
@ -75,12 +75,12 @@ suite('ExtHostTypes', function() {
|
|||
assert.equal(character, 0);
|
||||
});
|
||||
|
||||
test('Position, toJSON', function() {
|
||||
test('Position, toJSON', function () {
|
||||
let pos = new types.Position(4, 2);
|
||||
assertToJSON(pos, { line: 4, character: 2 });
|
||||
});
|
||||
|
||||
test('Position, isBefore(OrEqual)?', function() {
|
||||
test('Position, isBefore(OrEqual)?', function () {
|
||||
let p1 = new types.Position(1, 3);
|
||||
let p2 = new types.Position(1, 2);
|
||||
let p3 = new types.Position(0, 4);
|
||||
|
@ -91,7 +91,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.ok(p3.isBefore(p2));
|
||||
});
|
||||
|
||||
test('Position, isAfter(OrEqual)?', function() {
|
||||
test('Position, isAfter(OrEqual)?', function () {
|
||||
let p1 = new types.Position(1, 3);
|
||||
let p2 = new types.Position(1, 2);
|
||||
let p3 = new types.Position(0, 4);
|
||||
|
@ -103,7 +103,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.ok(p1.isAfter(p3));
|
||||
});
|
||||
|
||||
test('Position, compareTo', function() {
|
||||
test('Position, compareTo', function () {
|
||||
let p1 = new types.Position(1, 3);
|
||||
let p2 = new types.Position(1, 2);
|
||||
let p3 = new types.Position(0, 4);
|
||||
|
@ -115,7 +115,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.equal(p1.compareTo(p3), 1);
|
||||
});
|
||||
|
||||
test('Position, translate', function() {
|
||||
test('Position, translate', function () {
|
||||
let p1 = new types.Position(1, 3);
|
||||
|
||||
assert.ok(p1.translate() === p1);
|
||||
|
@ -153,7 +153,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.throws(() => p1.translate(0, -4));
|
||||
});
|
||||
|
||||
test('Position, with', function() {
|
||||
test('Position, with', function () {
|
||||
let p1 = new types.Position(1, 3);
|
||||
|
||||
assert.ok(p1.with() === p1);
|
||||
|
@ -176,7 +176,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.throws(() => p1.with({ character: -1 }));
|
||||
});
|
||||
|
||||
test('Range', function() {
|
||||
test('Range', function () {
|
||||
assert.throws(() => new types.Range(-1, 0, 0, 0));
|
||||
assert.throws(() => new types.Range(0, -1, 0, 0));
|
||||
assert.throws(() => new types.Range(new types.Position(0, 0), undefined));
|
||||
|
@ -189,13 +189,13 @@ suite('ExtHostTypes', function() {
|
|||
assert.throws(() => range.start = new types.Position(0, 3));
|
||||
});
|
||||
|
||||
test('Range, toJSON', function() {
|
||||
test('Range, toJSON', function () {
|
||||
|
||||
let range = new types.Range(1, 2, 3, 4);
|
||||
assertToJSON(range, [{ line: 1, character: 2 }, { line: 3, character: 4 }]);
|
||||
});
|
||||
|
||||
test('Range, sorting', function() {
|
||||
test('Range, sorting', function () {
|
||||
// sorts start/end
|
||||
let range = new types.Range(1, 0, 0, 0);
|
||||
assert.equal(range.start.line, 0);
|
||||
|
@ -206,7 +206,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.equal(range.end.line, 1);
|
||||
});
|
||||
|
||||
test('Range, isEmpty|isSingleLine', function() {
|
||||
test('Range, isEmpty|isSingleLine', function () {
|
||||
let range = new types.Range(1, 0, 0, 0);
|
||||
assert.ok(!range.isEmpty);
|
||||
assert.ok(!range.isSingleLine);
|
||||
|
@ -224,7 +224,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.ok(!range.isSingleLine);
|
||||
});
|
||||
|
||||
test('Range, contains', function() {
|
||||
test('Range, contains', function () {
|
||||
let range = new types.Range(1, 1, 2, 11);
|
||||
|
||||
assert.ok(range.contains(range.start));
|
||||
|
@ -237,7 +237,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.ok(!range.contains(new types.Range(1, 1, 3, 11)));
|
||||
});
|
||||
|
||||
test('Range, intersection', function() {
|
||||
test('Range, intersection', function () {
|
||||
let range = new types.Range(1, 1, 2, 11);
|
||||
let res: types.Range;
|
||||
|
||||
|
@ -267,7 +267,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.throws(() => range.intersection(undefined));
|
||||
});
|
||||
|
||||
test('Range, union', function() {
|
||||
test('Range, union', function () {
|
||||
let ran1 = new types.Range(0, 0, 5, 5);
|
||||
assert.ok(ran1.union(new types.Range(0, 0, 1, 1)) === ran1);
|
||||
|
||||
|
@ -284,7 +284,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.equal(res.start.character, 0);
|
||||
});
|
||||
|
||||
test('Range, with', function() {
|
||||
test('Range, with', function () {
|
||||
let range = new types.Range(1, 1, 2, 11);
|
||||
|
||||
assert.ok(range.with(range.start) === range);
|
||||
|
@ -310,7 +310,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.equal(res.start.line, 1);
|
||||
assert.equal(res.start.character, 1);
|
||||
|
||||
res = range.with({ end: new types.Position(9, 8), start: new types.Position(2, 3)});
|
||||
res = range.with({ end: new types.Position(9, 8), start: new types.Position(2, 3) });
|
||||
assert.equal(res.end.line, 9);
|
||||
assert.equal(res.end.character, 8);
|
||||
assert.equal(res.start.line, 2);
|
||||
|
@ -320,7 +320,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.throws(() => range.with(undefined, null));
|
||||
});
|
||||
|
||||
test('TextEdit', function() {
|
||||
test('TextEdit', function () {
|
||||
|
||||
assert.throws(() => new types.TextEdit(null, 'far'));
|
||||
assert.throws(() => new types.TextEdit(undefined, 'far'));
|
||||
|
@ -337,7 +337,7 @@ suite('ExtHostTypes', function() {
|
|||
assert.equal(edit.newText, '');
|
||||
});
|
||||
|
||||
test('WorkspaceEdit', function() {
|
||||
test('WorkspaceEdit', function () {
|
||||
|
||||
let a = types.Uri.file('a.ts');
|
||||
let b = types.Uri.file('b.ts');
|
||||
|
@ -368,6 +368,11 @@ suite('ExtHostTypes', function() {
|
|||
|
||||
});
|
||||
|
||||
test('DocumentLink', function () {
|
||||
assert.throws(() => new types.DocumentLink(null, null));
|
||||
assert.throws(() => new types.DocumentLink(new types.Range(1, 1, 1, 1), null));
|
||||
});
|
||||
|
||||
test('toJSON & stringify', function() {
|
||||
|
||||
assertToJSON(new types.Selection(3, 4, 2, 1), { start: { line: 2, character: 1 }, end: { line: 3, character: 4 }, anchor: { line: 3, character: 4 }, active: { line: 2, character: 1 } });
|
||||
|
|
Loading…
Reference in a new issue