diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts
index 5f2f9fbeba463524efa4abe1e76de89ab542fb8d..c758efe59c89badf2e56e47a5e531d64518f1ff6 100644
--- a/src/vs/base/common/htmlContent.ts
+++ b/src/vs/base/common/htmlContent.ts
@@ -6,6 +6,7 @@
'use strict';
import { equals } from 'vs/base/common/arrays';
+import { marked } from 'vs/base/common/marked/marked';
/**
* MarkedString can be used to render human readable text. It is either a markdown string
@@ -38,3 +39,16 @@ export function removeMarkdownEscapes(text: string): string {
}
return text.replace(/\\([\\`*_{}[\]()#+\-.!])/g, '$1');
}
+
+export function containsCommandLink(value: MarkedString): boolean {
+ let uses = false;
+ const renderer = new marked.Renderer();
+ renderer.link = (href, title, text): string => {
+ if (href.match(/^command:/i)) {
+ uses = true;
+ }
+ return 'link';
+ };
+ marked(value, { renderer });
+ return uses;
+}
diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts
index b18258e2c68d7de8701564792011c50e76b32526..1df16a4716fd053c2de9319e97150cca680d09bf 100644
--- a/src/vs/workbench/api/node/extHost.api.impl.ts
+++ b/src/vs/workbench/api/node/extHost.api.impl.ts
@@ -242,7 +242,7 @@ export function createApiFactory(
return languageFeatures.registerTypeDefinitionProvider(selector, provider);
},
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
- return languageFeatures.registerHoverProvider(selector, provider);
+ return languageFeatures.registerHoverProvider(selector, provider, extension.id);
},
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
return languageFeatures.registerDocumentHighlightProvider(selector, provider);
diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts
index 8e0c521684c2b20c004a6041953b3b2bdab64f74..f6049cdab1758e4ad07f56881c848079f3123c9f 100644
--- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts
+++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts
@@ -18,10 +18,13 @@ import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHos
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
import { asWinJsPromise } from 'vs/base/common/async';
-import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IRawColorFormatMap, IMainContext } from './extHost.protocol';
+import { MainContext, MainThreadTelemetryShape, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IRawColorFormatMap, IMainContext } from './extHost.protocol';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
+import { containsCommandLink } from 'vs/base/common/htmlContent';
+import { isFalsyOrEmpty } from 'vs/base/common/arrays';
+import { once } from 'vs/base/common/functional';
// --- adapter
@@ -173,12 +176,12 @@ class TypeDefinitionAdapter {
class HoverAdapter {
- private _documents: ExtHostDocuments;
- private _provider: vscode.HoverProvider;
-
- constructor(documents: ExtHostDocuments, provider: vscode.HoverProvider) {
- this._documents = documents;
- this._provider = provider;
+ constructor(
+ private readonly _documents: ExtHostDocuments,
+ private readonly _provider: vscode.HoverProvider,
+ private readonly _telemetryLog: (name: string, data: object) => void,
+ ) {
+ //
}
public provideHover(resource: URI, position: IPosition): TPromise {
@@ -187,7 +190,7 @@ class HoverAdapter {
let pos = TypeConverters.toPosition(position);
return asWinJsPromise(token => this._provider.provideHover(doc, pos, token)).then(value => {
- if (!value) {
+ if (!value || isFalsyOrEmpty(value.contents)) {
return undefined;
}
if (!value.range) {
@@ -197,7 +200,14 @@ class HoverAdapter {
value.range = new Range(pos, pos);
}
- return TypeConverters.fromHover(value);
+ const result = TypeConverters.fromHover(value);
+
+ // we wanna know which extension uses command links
+ // because that is a potential trick-attack on users
+ if (result.contents.some(containsCommandLink)) {
+ this._telemetryLog('usesCommandLink', { from: 'hover' });
+ }
+ return result;
});
}
}
@@ -744,6 +754,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
private static _handlePool: number = 0;
private _proxy: MainThreadLanguageFeaturesShape;
+ private _telemetry: MainThreadTelemetryShape;
private _documents: ExtHostDocuments;
private _commands: ExtHostCommands;
private _heapService: ExtHostHeapService;
@@ -759,6 +770,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
diagnostics: ExtHostDiagnostics
) {
this._proxy = mainContext.get(MainContext.MainThreadLanguageFeatures);
+ this._telemetry = mainContext.get(MainContext.MainThreadTelemetry);
this._documents = documents;
this._commands = commands;
this._heapService = heapMonitor;
@@ -860,9 +872,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- extra info
- registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
+ registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable {
const handle = this._nextHandle();
- this._adapter.set(handle, new HoverAdapter(this._documents, provider));
+ this._adapter.set(handle, new HoverAdapter(this._documents, provider, once((name, data) => {
+ data['extension'] = extensionId;
+ this._telemetry.$publicLog(name, data);
+ })));
this._proxy.$registerHoverProvider(handle, selector);
return this._createDisposable(handle);
}