From 8476ff1a9ded4dcbb2ef06a42b5f45e4192eb26d Mon Sep 17 00:00:00 2001 From: kingwl Date: Tue, 22 Dec 2020 18:34:34 +0800 Subject: [PATCH 01/36] WIP --- build/azure-pipelines/common/symbols.js | 185 ++++++++++++++++++ .../editor/browser/widget/codeEditorWidget.ts | 4 + src/vs/editor/common/editorContextKeys.ts | 1 + src/vs/editor/common/modes.ts | 22 ++- src/vs/vscode.d.ts | 18 ++ .../api/browser/mainThreadLanguageFeatures.ts | 10 + .../workbench/api/common/extHost.protocol.ts | 13 ++ .../api/common/extHostLanguageFeatures.ts | 25 +++ .../api/common/extHostTypeConverters.ts | 10 + src/vs/workbench/api/common/extHostTypes.ts | 28 +++ 10 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 build/azure-pipelines/common/symbols.js diff --git a/build/azure-pipelines/common/symbols.js b/build/azure-pipelines/common/symbols.js new file mode 100644 index 0000000000000..5a436897e66cb --- /dev/null +++ b/build/azure-pipelines/common/symbols.js @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +const request = require("request"); +const fs_1 = require("fs"); +const github = require("github-releases"); +const path_1 = require("path"); +const os_1 = require("os"); +const util_1 = require("util"); +const BASE_URL = 'https://rink.hockeyapp.net/api/2/'; +const HOCKEY_APP_TOKEN_HEADER = 'X-HockeyAppToken'; +var Platform; +(function (Platform) { + Platform["WIN_32"] = "win32-ia32"; + Platform["WIN_64"] = "win32-x64"; + Platform["LINUX_64"] = "linux-x64"; + Platform["MAC_OS"] = "darwin-x64"; +})(Platform || (Platform = {})); +function symbolsZipName(platform, electronVersion, insiders) { + return `${insiders ? 'insiders' : 'stable'}-symbols-v${electronVersion}-${platform}.zip`; +} +const SEED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +async function tmpFile(name) { + let res = ''; + for (let i = 0; i < 8; i++) { + res += SEED.charAt(Math.floor(Math.random() * SEED.length)); + } + const tmpParent = path_1.join(os_1.tmpdir(), res); + await util_1.promisify(fs_1.mkdir)(tmpParent); + return path_1.join(tmpParent, name); +} +function getVersions(accessor) { + return asyncRequest({ + url: `${BASE_URL}/apps/${accessor.appId}/app_versions`, + method: 'GET', + headers: { + [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken + } + }); +} +function createVersion(accessor, version) { + return asyncRequest({ + url: `${BASE_URL}/apps/${accessor.appId}/app_versions/new`, + method: 'POST', + headers: { + [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken + }, + formData: { + bundle_version: version + } + }); +} +function updateVersion(accessor, symbolsPath) { + return asyncRequest({ + url: `${BASE_URL}/apps/${accessor.appId}/app_versions/${accessor.id}`, + method: 'PUT', + headers: { + [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken + }, + formData: { + dsym: fs_1.createReadStream(symbolsPath) + } + }); +} +function asyncRequest(options) { + return new Promise((resolve, reject) => { + request(options, (error, _response, body) => { + if (error) { + reject(error); + } + else { + resolve(JSON.parse(body)); + } + }); + }); +} +function downloadAsset(repository, assetName, targetPath, electronVersion) { + return new Promise((resolve, reject) => { + repository.getReleases({ tag_name: `v${electronVersion}` }, (err, releases) => { + if (err) { + reject(err); + } + else { + const asset = releases[0].assets.filter((asset) => asset.name === assetName)[0]; + if (!asset) { + reject(new Error(`Asset with name ${assetName} not found`)); + } + else { + repository.downloadAsset(asset, (err, reader) => { + if (err) { + reject(err); + } + else { + const writer = fs_1.createWriteStream(targetPath); + writer.on('error', reject); + writer.on('close', resolve); + reader.on('error', reject); + reader.pipe(writer); + } + }); + } + } + }); + }); +} +async function ensureVersionAndSymbols(options) { + // Check version does not exist + console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`); + const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }); + if (!Array.isArray(versions.app_versions)) { + throw new Error(`Unexpected response: ${JSON.stringify(versions)}`); + } + if (versions.app_versions.some(v => v.version === options.versions.code)) { + console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`); + return; + } + // Download symbols for platform and electron version + const symbolsName = symbolsZipName(options.platform, options.versions.electron, options.versions.insiders); + const symbolsPath = await tmpFile('symbols.zip'); + console.log(`HockeyApp: downloading symbols ${symbolsName} for electron ${options.versions.electron} (${options.platform}) into ${symbolsPath}`); + await downloadAsset(new github({ repo: options.repository, token: options.access.githubToken }), symbolsName, symbolsPath, options.versions.electron); + // Create version + console.log(`HockeyApp: creating new version ${options.versions.code} (${options.platform})`); + const version = await createVersion({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, options.versions.code); + // Upload symbols + console.log(`HockeyApp: uploading symbols for version ${options.versions.code} (${options.platform})`); + await updateVersion({ id: String(version.id), accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, symbolsPath); + // Cleanup + await util_1.promisify(fs_1.unlink)(symbolsPath); +} +// Environment +const pakage = require('../../../package.json'); +const product = require('../../../product.json'); +const repository = product.electronRepository; +const electronVersion = require('../../lib/electron').getElectronVersion(); +const insiders = product.quality !== 'stable'; +let codeVersion = pakage.version; +if (insiders) { + codeVersion = `${codeVersion}-insider`; +} +const githubToken = process.argv[2]; +const hockeyAppToken = process.argv[3]; +const is64 = process.argv[4] === 'x64'; +const hockeyAppId = process.argv[5]; +if (process.argv.length !== 6) { + throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`); +} +let platform; +if (process.platform === 'darwin') { + platform = Platform.MAC_OS; +} +else if (process.platform === 'win32') { + platform = is64 ? Platform.WIN_64 : Platform.WIN_32; +} +else { + platform = Platform.LINUX_64; +} +// Create version and upload symbols in HockeyApp +if (repository && codeVersion && electronVersion && (product.quality === 'stable' || product.quality === 'insider')) { + ensureVersionAndSymbols({ + repository, + platform, + versions: { + code: codeVersion, + insiders, + electron: electronVersion + }, + access: { + githubToken, + hockeyAppToken, + hockeyAppId + } + }).then(() => { + console.log('HockeyApp: done'); + }).catch(error => { + console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`); + return process.exit(1); + }); +} +else { + console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`); +} diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 4c87ce2bb2d35..f18591d2a5920 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1834,6 +1834,7 @@ export class EditorModeContext extends Disposable { private readonly _hasMultipleDocumentFormattingProvider: IContextKey; private readonly _hasMultipleDocumentSelectionFormattingProvider: IContextKey; private readonly _hasSignatureHelpProvider: IContextKey; + private readonly _hasSignatureArgumentsLabelProvider: IContextKey; private readonly _isInWalkThrough: IContextKey; constructor( @@ -1856,6 +1857,7 @@ export class EditorModeContext extends Disposable { this._hasReferenceProvider = EditorContextKeys.hasReferenceProvider.bindTo(_contextKeyService); this._hasRenameProvider = EditorContextKeys.hasRenameProvider.bindTo(_contextKeyService); this._hasSignatureHelpProvider = EditorContextKeys.hasSignatureHelpProvider.bindTo(_contextKeyService); + this._hasSignatureArgumentsLabelProvider = EditorContextKeys.hasSignatureArgumentsLabelProvider.bindTo(_contextKeyService); this._hasDocumentFormattingProvider = EditorContextKeys.hasDocumentFormattingProvider.bindTo(_contextKeyService); this._hasDocumentSelectionFormattingProvider = EditorContextKeys.hasDocumentSelectionFormattingProvider.bindTo(_contextKeyService); this._hasMultipleDocumentFormattingProvider = EditorContextKeys.hasMultipleDocumentFormattingProvider.bindTo(_contextKeyService); @@ -1884,6 +1886,7 @@ export class EditorModeContext extends Disposable { this._register(modes.DocumentFormattingEditProviderRegistry.onDidChange(update)); this._register(modes.DocumentRangeFormattingEditProviderRegistry.onDidChange(update)); this._register(modes.SignatureHelpProviderRegistry.onDidChange(update)); + this._register(modes.SignatureArgumentsLabelProviderRegistry.onDidChange(update)); update(); } @@ -1935,6 +1938,7 @@ export class EditorModeContext extends Disposable { this._hasReferenceProvider.set(modes.ReferenceProviderRegistry.has(model)); this._hasRenameProvider.set(modes.RenameProviderRegistry.has(model)); this._hasSignatureHelpProvider.set(modes.SignatureHelpProviderRegistry.has(model)); + this._hasSignatureArgumentsLabelProvider.set(modes.SignatureArgumentsLabelProviderRegistry.has(model)); this._hasDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.has(model) || modes.DocumentRangeFormattingEditProviderRegistry.has(model)); this._hasDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.has(model)); this._hasMultipleDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.all(model).length + modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1); diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index a3a3b3de1dc6a..23e1d43150bc9 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -61,6 +61,7 @@ export namespace EditorContextKeys { export const hasReferenceProvider = new RawContextKey('editorHasReferenceProvider', false); export const hasRenameProvider = new RawContextKey('editorHasRenameProvider', false); export const hasSignatureHelpProvider = new RawContextKey('editorHasSignatureHelpProvider', false); + export const hasSignatureArgumentsLabelProvider = new RawContextKey('editorHasSignatureArgumentsLabelProvider', false); // -- mode context keys: formatting export const hasDocumentFormattingProvider = new RawContextKey('editorHasDocumentFormattingProvider', false); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index fe0d7335d09a0..233ecd95eac9f 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { Position } from 'vs/editor/common/core/position'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; @@ -1659,6 +1659,22 @@ export interface CodeLensProvider { resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; } +export interface SignautreArguments { + name: string + positions: IPosition[] +} +export interface SignatureArgumentsSignature { + arguments: SignautreArguments[] +} + +export interface SignatureArgumentsLabelList { + signatures: SignatureArgumentsSignature[] +} + +export interface SignatureArgumentsLabelProvider { + provideSignatureArgumentsLabels(model: model.ITextModel, token: CancellationToken): ProviderResult; +} + export interface SemanticTokensLegend { readonly tokenTypes: string[]; readonly tokenModifiers: string[]; @@ -1763,6 +1779,10 @@ export const TypeDefinitionProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const SignatureArgumentsLabelProviderRegistry = new LanguageFeatureRegistry(); /** * @internal diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 0def9b1af794a..028a0430040de 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3755,6 +3755,24 @@ declare module 'vscode' { provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; } + export interface SignautreArguments { + name: string + positions: Position[] + } + export interface SignatureArgumentsSignature { + arguments: SignautreArguments[] + } + + export interface SignatureArgumentsLabelList { + signatures: SignatureArgumentsSignature[] + dispose(): void; + } + + + export interface SignatureArgumentsLabelProvider { + provideSignatureArgumentsLabels(model: TextDocument, token: CancellationToken): ProviderResult; + } + /** * Metadata about a registered [`SignatureHelpProvider`](#SignatureHelpProvider). */ diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index ddea8e0e9d161..a0f4b2def792d 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -497,6 +497,16 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha })); } + // --- parameter labels + + $registerSignatureArgumentsLabelSupport(handle: number, selector: IDocumentFilterDto[]): void { + this._registrations.set(handle, modes.SignatureArgumentsLabelProviderRegistry.register(selector, { + provideSignatureArgumentsLabels: async (model: ITextModel, token: CancellationToken): Promise => { + return this._proxy.$provideSignatureArgumentsLabel(handle, model.uri, token); + } + })); + } + // --- links $registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 77016c491b099..e133a331a3c10 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1269,6 +1269,18 @@ export interface ISignatureHelpContextDto { readonly activeSignatureHelp?: ISignatureHelpDto; } +export interface ISignautreArgumentsDto { + name: string + positions: IPosition[] +} +export interface ISignatureArgumentsSignatureDto { + arguments: ISignautreArgumentsDto[] +} + +export interface ISignatureArgumentsLabelDto { + signatures: ISignatureArgumentsSignatureDto[] +} + export interface ILocationDto { uri: UriComponents; range: IRange; @@ -1458,6 +1470,7 @@ export interface ExtHostLanguageFeaturesShape { $releaseCompletionItems(handle: number, id: number): void; $provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise; $releaseSignatureHelp(handle: number, id: number): void; + $provideSignatureArgumentsLabel(handle: number, resource: UriComponents, token: CancellationToken): Promise $provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise; $resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise; $releaseDocumentLinks(handle: number, id: number): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 49cde88e19c1d..086811373a629 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1062,6 +1062,26 @@ class SignatureHelpAdapter { } } +class SignatureArgumentsLabelAdapter { + private readonly _cache = new Cache('SignatureArgumentsLabel'); + + constructor( + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.SignatureArgumentsLabelProvider, + ) { } + + provideSignatureArgumentsLabels(resource: URI, token: CancellationToken): Promise { + const doc = this._documents.getDocument(resource); + return asPromise(() => this._provider.provideSignatureArgumentsLabels(doc, token)).then(value => { + if (value) { + const id = this._cache.add([value]); + return { ...typeConvert.SignatrueArgumentsLabelList.from(value), id }; + } + return undefined; + }); + } +} + class LinkProviderAdapter { private _cache = new Cache('DocumentLink'); @@ -1770,6 +1790,11 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.releaseSignatureHelp(id), undefined); } + // --- arguments labels + $provideSignatureArgumentsLabel(handle: number, resource: UriComponents, token: CancellationToken): Promise { + return this._withAdapter(handle, SignatureArgumentsLabelAdapter, adapter => adapter.provideSignatureArgumentsLabels(URI.revive(resource), token), undefined); + } + // --- links registerDocumentLinkProvider(extension: IExtensionDescription | undefined, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index ee4a7197a3cb6..9ce93ea7dd513 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1015,6 +1015,16 @@ export namespace SignatureHelp { } } +export namespace SignatrueArgumentsLabelList { + export function from(labels: vscode.SignatureArgumentsLabelList): modes.SignatureArgumentsLabelList { + + } + + export function to(labels: modes.SignatureArgumentsLabelList): vscode.SignatureArgumentsLabelList { + + } +} + export namespace DocumentLink { export function from(link: vscode.DocumentLink): modes.ILink { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index e01c9270d395d..815330cb6186e 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1373,6 +1373,34 @@ export enum SignatureHelpTriggerKind { ContentChange = 3, } +@es5ClassCompat +export class SignautreArguments { + name: string; + positions: Position[]; + + constructor(name: string, positions: Position[]) { + this.name = name; + this.positions = positions; + } +} + +@es5ClassCompat +export class SignatureArgumentsSignature { + arguments: SignautreArguments[]; + + constructor() { + this.arguments = []; + } +} + +@es5ClassCompat +export class SignatureArgumentsLabelList { + signatures: SignatureArgumentsSignature[]; + constructor() { + this.signatures = []; + } +} + export enum CompletionTriggerKind { Invoke = 0, TriggerCharacter = 1, From 741a568bfd583f0b21907d07800a3e152fcae484 Mon Sep 17 00:00:00 2001 From: kingwl Date: Tue, 22 Dec 2020 18:38:06 +0800 Subject: [PATCH 02/36] Avoid generated files --- build/azure-pipelines/common/symbols.js | 185 ------------------------ 1 file changed, 185 deletions(-) delete mode 100644 build/azure-pipelines/common/symbols.js diff --git a/build/azure-pipelines/common/symbols.js b/build/azure-pipelines/common/symbols.js deleted file mode 100644 index 5a436897e66cb..0000000000000 --- a/build/azure-pipelines/common/symbols.js +++ /dev/null @@ -1,185 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const request = require("request"); -const fs_1 = require("fs"); -const github = require("github-releases"); -const path_1 = require("path"); -const os_1 = require("os"); -const util_1 = require("util"); -const BASE_URL = 'https://rink.hockeyapp.net/api/2/'; -const HOCKEY_APP_TOKEN_HEADER = 'X-HockeyAppToken'; -var Platform; -(function (Platform) { - Platform["WIN_32"] = "win32-ia32"; - Platform["WIN_64"] = "win32-x64"; - Platform["LINUX_64"] = "linux-x64"; - Platform["MAC_OS"] = "darwin-x64"; -})(Platform || (Platform = {})); -function symbolsZipName(platform, electronVersion, insiders) { - return `${insiders ? 'insiders' : 'stable'}-symbols-v${electronVersion}-${platform}.zip`; -} -const SEED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -async function tmpFile(name) { - let res = ''; - for (let i = 0; i < 8; i++) { - res += SEED.charAt(Math.floor(Math.random() * SEED.length)); - } - const tmpParent = path_1.join(os_1.tmpdir(), res); - await util_1.promisify(fs_1.mkdir)(tmpParent); - return path_1.join(tmpParent, name); -} -function getVersions(accessor) { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions`, - method: 'GET', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - } - }); -} -function createVersion(accessor, version) { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions/new`, - method: 'POST', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - }, - formData: { - bundle_version: version - } - }); -} -function updateVersion(accessor, symbolsPath) { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions/${accessor.id}`, - method: 'PUT', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - }, - formData: { - dsym: fs_1.createReadStream(symbolsPath) - } - }); -} -function asyncRequest(options) { - return new Promise((resolve, reject) => { - request(options, (error, _response, body) => { - if (error) { - reject(error); - } - else { - resolve(JSON.parse(body)); - } - }); - }); -} -function downloadAsset(repository, assetName, targetPath, electronVersion) { - return new Promise((resolve, reject) => { - repository.getReleases({ tag_name: `v${electronVersion}` }, (err, releases) => { - if (err) { - reject(err); - } - else { - const asset = releases[0].assets.filter((asset) => asset.name === assetName)[0]; - if (!asset) { - reject(new Error(`Asset with name ${assetName} not found`)); - } - else { - repository.downloadAsset(asset, (err, reader) => { - if (err) { - reject(err); - } - else { - const writer = fs_1.createWriteStream(targetPath); - writer.on('error', reject); - writer.on('close', resolve); - reader.on('error', reject); - reader.pipe(writer); - } - }); - } - } - }); - }); -} -async function ensureVersionAndSymbols(options) { - // Check version does not exist - console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`); - const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }); - if (!Array.isArray(versions.app_versions)) { - throw new Error(`Unexpected response: ${JSON.stringify(versions)}`); - } - if (versions.app_versions.some(v => v.version === options.versions.code)) { - console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`); - return; - } - // Download symbols for platform and electron version - const symbolsName = symbolsZipName(options.platform, options.versions.electron, options.versions.insiders); - const symbolsPath = await tmpFile('symbols.zip'); - console.log(`HockeyApp: downloading symbols ${symbolsName} for electron ${options.versions.electron} (${options.platform}) into ${symbolsPath}`); - await downloadAsset(new github({ repo: options.repository, token: options.access.githubToken }), symbolsName, symbolsPath, options.versions.electron); - // Create version - console.log(`HockeyApp: creating new version ${options.versions.code} (${options.platform})`); - const version = await createVersion({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, options.versions.code); - // Upload symbols - console.log(`HockeyApp: uploading symbols for version ${options.versions.code} (${options.platform})`); - await updateVersion({ id: String(version.id), accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, symbolsPath); - // Cleanup - await util_1.promisify(fs_1.unlink)(symbolsPath); -} -// Environment -const pakage = require('../../../package.json'); -const product = require('../../../product.json'); -const repository = product.electronRepository; -const electronVersion = require('../../lib/electron').getElectronVersion(); -const insiders = product.quality !== 'stable'; -let codeVersion = pakage.version; -if (insiders) { - codeVersion = `${codeVersion}-insider`; -} -const githubToken = process.argv[2]; -const hockeyAppToken = process.argv[3]; -const is64 = process.argv[4] === 'x64'; -const hockeyAppId = process.argv[5]; -if (process.argv.length !== 6) { - throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`); -} -let platform; -if (process.platform === 'darwin') { - platform = Platform.MAC_OS; -} -else if (process.platform === 'win32') { - platform = is64 ? Platform.WIN_64 : Platform.WIN_32; -} -else { - platform = Platform.LINUX_64; -} -// Create version and upload symbols in HockeyApp -if (repository && codeVersion && electronVersion && (product.quality === 'stable' || product.quality === 'insider')) { - ensureVersionAndSymbols({ - repository, - platform, - versions: { - code: codeVersion, - insiders, - electron: electronVersion - }, - access: { - githubToken, - hockeyAppToken, - hockeyAppId - } - }).then(() => { - console.log('HockeyApp: done'); - }).catch(error => { - console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`); - return process.exit(1); - }); -} -else { - console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`); -} From 52c1cce25976a8aefb2cf4d5fe8f9ee848acd94f Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 23 Dec 2020 01:00:58 +0800 Subject: [PATCH 03/36] Add controller --- src/vs/editor/common/config/editorOptions.ts | 1 + src/vs/editor/common/modes.ts | 1 + .../signatureArgumentsLabel.ts | 135 ++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 79397349a6546..14914608be85f 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -3759,6 +3759,7 @@ export const enum EditorOption { wrappingIndent, wrappingStrategy, showDeprecated, + signatureArgumentsLabel, // Leave these at the end (because they have dependencies!) editorClassName, diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 233ecd95eac9f..193e26cbf8711 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1669,6 +1669,7 @@ export interface SignatureArgumentsSignature { export interface SignatureArgumentsLabelList { signatures: SignatureArgumentsSignature[] + dispose(): void; } export interface SignatureArgumentsLabelProvider { diff --git a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts new file mode 100644 index 0000000000000..c70d89279cfbc --- /dev/null +++ b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts @@ -0,0 +1,135 @@ +import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { SignatureArgumentsLabelProviderRegistry, SignatureArgumentsLabelProvider, SignatureArgumentsSignature, SignatureArgumentsLabelList } from 'vs/editor/common/modes'; +import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; + +export interface SignatureArgumentsLabelItem { + signature: SignatureArgumentsSignature; + provider: SignatureArgumentsLabelProvider; +} + + +class SignatureArgumentsLabelModel { + signatures: SignatureArgumentsLabelItem[] = []; + + private readonly _disposables = new DisposableStore(); + + dispose(): void { + this._disposables.dispose(); + } + + add(list: SignatureArgumentsLabelList, provider: SignatureArgumentsLabelProvider): void { + this._disposables.add(list); + for (const signature of list.signatures) { + this.signatures.push({ signature, provider }); + } + } +} + +async function getSignatureArgumentsLabelModel(model: ITextModel, token: CancellationToken) { + const provider = SignatureArgumentsLabelProviderRegistry.ordered(model); + const providerRanks = new Map(); + const result = new SignatureArgumentsLabelModel(); + + const promises = provider.map(async (provider, i) => { + + providerRanks.set(provider, i); + + try { + const list = await Promise.resolve(provider.provideSignatureArgumentsLabels(model, token)); + if (list) { + result.add(list, provider); + } + } catch (err) { + onUnexpectedExternalError(err); + } + }); + + await Promise.all(promises); + return result; +} + +class SignatureArgumentsLabelController implements IEditorContribution { + + public static readonly ID = 'editor.contrib.signatureArgumentsLabel'; + + static get(editor: ICodeEditor): SignatureArgumentsLabelController { + return editor.getContribution(SignatureArgumentsLabelController.ID); + } + + private readonly _disposables = new DisposableStore(); + + private readonly _getSignatureArgumentsLabelModelDelays = new LanguageFeatureRequestDelays(SignatureArgumentsLabelProviderRegistry, 250, 2500); + private _getSignatureArgumentsLabelModelPromise: CancelablePromise | undefined; + private _oldSignatureArgumentsLabelModels = new DisposableStore(); + private _currentSignatureArgumentsLabelModel: SignatureArgumentsLabelModel | undefined; + + constructor( + private readonly _editor: ICodeEditor + ) { + this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange())); + this._disposables.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); + this._disposables.add(this._editor.onDidChangeConfiguration((e) => { + + })); + this._disposables.add(SignatureArgumentsLabelProviderRegistry.onDidChange(this._onModelChange, this)); + this._onModelChange(); + } + + dispose(): void { + this._localDispose(); + this._disposables.dispose(); + } + + private _localDispose(): void { + + } + + private _onModelChange(): void { + this._localDispose(); + + const model = this._editor.getModel(); + if (!model) { + return; + } + + if (!this._editor.getOption(EditorOption.signatureArgumentsLabel)) { + return; + } + + const scheduler = new RunOnceScheduler(() => { + const t1 = Date.now(); + + this._getSignatureArgumentsLabelModelPromise?.cancel(); + this._getSignatureArgumentsLabelModelPromise = createCancelablePromise(token => getSignatureArgumentsLabelModel(model, token)); + + this._getSignatureArgumentsLabelModelPromise.then(result => { + if (this._currentSignatureArgumentsLabelModel) { + this._oldSignatureArgumentsLabelModels.add(this._currentSignatureArgumentsLabelModel); + } + this._currentSignatureArgumentsLabelModel = result; + + // update moving average + const newDelay = this._getSignatureArgumentsLabelModelDelays.update(model, Date.now() - t1); + scheduler.delay = newDelay; + + // render lenses + this._renderSignatureArgumentsLabels(result); + }, onUnexpectedError); + + }, this._getSignatureArgumentsLabelModelDelays.get(model)); + } + + _renderSignatureArgumentsLabels(result: SignatureArgumentsLabelModel) { + + } +} + +registerEditorContribution(SignatureArgumentsLabelController.ID, SignatureArgumentsLabelController); From 434f63192e87724bb02ab88a7cac983a0d193090 Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 23 Dec 2020 13:14:55 +0800 Subject: [PATCH 04/36] Revert "Avoid generated files" This reverts commit 741a568bfd583f0b21907d07800a3e152fcae484. --- build/azure-pipelines/common/symbols.js | 185 ++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 build/azure-pipelines/common/symbols.js diff --git a/build/azure-pipelines/common/symbols.js b/build/azure-pipelines/common/symbols.js new file mode 100644 index 0000000000000..5a436897e66cb --- /dev/null +++ b/build/azure-pipelines/common/symbols.js @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +const request = require("request"); +const fs_1 = require("fs"); +const github = require("github-releases"); +const path_1 = require("path"); +const os_1 = require("os"); +const util_1 = require("util"); +const BASE_URL = 'https://rink.hockeyapp.net/api/2/'; +const HOCKEY_APP_TOKEN_HEADER = 'X-HockeyAppToken'; +var Platform; +(function (Platform) { + Platform["WIN_32"] = "win32-ia32"; + Platform["WIN_64"] = "win32-x64"; + Platform["LINUX_64"] = "linux-x64"; + Platform["MAC_OS"] = "darwin-x64"; +})(Platform || (Platform = {})); +function symbolsZipName(platform, electronVersion, insiders) { + return `${insiders ? 'insiders' : 'stable'}-symbols-v${electronVersion}-${platform}.zip`; +} +const SEED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +async function tmpFile(name) { + let res = ''; + for (let i = 0; i < 8; i++) { + res += SEED.charAt(Math.floor(Math.random() * SEED.length)); + } + const tmpParent = path_1.join(os_1.tmpdir(), res); + await util_1.promisify(fs_1.mkdir)(tmpParent); + return path_1.join(tmpParent, name); +} +function getVersions(accessor) { + return asyncRequest({ + url: `${BASE_URL}/apps/${accessor.appId}/app_versions`, + method: 'GET', + headers: { + [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken + } + }); +} +function createVersion(accessor, version) { + return asyncRequest({ + url: `${BASE_URL}/apps/${accessor.appId}/app_versions/new`, + method: 'POST', + headers: { + [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken + }, + formData: { + bundle_version: version + } + }); +} +function updateVersion(accessor, symbolsPath) { + return asyncRequest({ + url: `${BASE_URL}/apps/${accessor.appId}/app_versions/${accessor.id}`, + method: 'PUT', + headers: { + [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken + }, + formData: { + dsym: fs_1.createReadStream(symbolsPath) + } + }); +} +function asyncRequest(options) { + return new Promise((resolve, reject) => { + request(options, (error, _response, body) => { + if (error) { + reject(error); + } + else { + resolve(JSON.parse(body)); + } + }); + }); +} +function downloadAsset(repository, assetName, targetPath, electronVersion) { + return new Promise((resolve, reject) => { + repository.getReleases({ tag_name: `v${electronVersion}` }, (err, releases) => { + if (err) { + reject(err); + } + else { + const asset = releases[0].assets.filter((asset) => asset.name === assetName)[0]; + if (!asset) { + reject(new Error(`Asset with name ${assetName} not found`)); + } + else { + repository.downloadAsset(asset, (err, reader) => { + if (err) { + reject(err); + } + else { + const writer = fs_1.createWriteStream(targetPath); + writer.on('error', reject); + writer.on('close', resolve); + reader.on('error', reject); + reader.pipe(writer); + } + }); + } + } + }); + }); +} +async function ensureVersionAndSymbols(options) { + // Check version does not exist + console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`); + const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }); + if (!Array.isArray(versions.app_versions)) { + throw new Error(`Unexpected response: ${JSON.stringify(versions)}`); + } + if (versions.app_versions.some(v => v.version === options.versions.code)) { + console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`); + return; + } + // Download symbols for platform and electron version + const symbolsName = symbolsZipName(options.platform, options.versions.electron, options.versions.insiders); + const symbolsPath = await tmpFile('symbols.zip'); + console.log(`HockeyApp: downloading symbols ${symbolsName} for electron ${options.versions.electron} (${options.platform}) into ${symbolsPath}`); + await downloadAsset(new github({ repo: options.repository, token: options.access.githubToken }), symbolsName, symbolsPath, options.versions.electron); + // Create version + console.log(`HockeyApp: creating new version ${options.versions.code} (${options.platform})`); + const version = await createVersion({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, options.versions.code); + // Upload symbols + console.log(`HockeyApp: uploading symbols for version ${options.versions.code} (${options.platform})`); + await updateVersion({ id: String(version.id), accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, symbolsPath); + // Cleanup + await util_1.promisify(fs_1.unlink)(symbolsPath); +} +// Environment +const pakage = require('../../../package.json'); +const product = require('../../../product.json'); +const repository = product.electronRepository; +const electronVersion = require('../../lib/electron').getElectronVersion(); +const insiders = product.quality !== 'stable'; +let codeVersion = pakage.version; +if (insiders) { + codeVersion = `${codeVersion}-insider`; +} +const githubToken = process.argv[2]; +const hockeyAppToken = process.argv[3]; +const is64 = process.argv[4] === 'x64'; +const hockeyAppId = process.argv[5]; +if (process.argv.length !== 6) { + throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`); +} +let platform; +if (process.platform === 'darwin') { + platform = Platform.MAC_OS; +} +else if (process.platform === 'win32') { + platform = is64 ? Platform.WIN_64 : Platform.WIN_32; +} +else { + platform = Platform.LINUX_64; +} +// Create version and upload symbols in HockeyApp +if (repository && codeVersion && electronVersion && (product.quality === 'stable' || product.quality === 'insider')) { + ensureVersionAndSymbols({ + repository, + platform, + versions: { + code: codeVersion, + insiders, + electron: electronVersion + }, + access: { + githubToken, + hockeyAppToken, + hockeyAppId + } + }).then(() => { + console.log('HockeyApp: done'); + }).catch(error => { + console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`); + return process.exit(1); + }); +} +else { + console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`); +} From 9f5cfc246bfd1c508d3aefab118ef50ea235709e Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 23 Dec 2020 15:07:33 +0800 Subject: [PATCH 05/36] Add dummy support --- extensions/search-result/src/extension.ts | 4 +- .../signatureArgumentsLabel.ts | 57 ++++ .../src/languageProvider.ts | 1 + src/vs/editor/common/config/editorOptions.ts | 2 - .../signatureArgumentsLabel.ts | 292 ++++++++++++------ src/vs/editor/editor.all.ts | 1 + src/vs/monaco.d.ts | 18 ++ src/vs/vscode.d.ts | 2 + .../api/browser/mainThreadLanguageFeatures.ts | 14 +- .../workbench/api/common/extHost.api.impl.ts | 3 + .../workbench/api/common/extHost.protocol.ts | 1 + .../api/common/extHostLanguageFeatures.ts | 10 +- .../api/common/extHostTypeConverters.ts | 15 +- 13 files changed, 318 insertions(+), 102 deletions(-) create mode 100644 extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 6bc5a573c95ff..d84455a5671e6 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -98,7 +98,7 @@ export function activate(context: vscode.ExtensionContext) { }), vscode.languages.registerDocumentLinkProvider(SEARCH_RESULT_SELECTOR, { - async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { return parseSearchResults(document, token) .filter(isFileLine) .map(({ location }) => ({ range: location.originSelectionRange!, target: location.targetUri })); @@ -141,7 +141,7 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u } const uriFromFolderWithPath = (folder: vscode.WorkspaceFolder, path: string): vscode.Uri => - vscode.Uri.joinPath(folder.uri, path); + (vscode.Uri as any).joinPath(folder.uri, path); if (vscode.workspace.workspaceFolders) { const multiRootFormattedPath = /^(.*) • (.*)$/.exec(path); diff --git a/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts b/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts new file mode 100644 index 0000000000000..4870f30fd20f8 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts @@ -0,0 +1,57 @@ +import * as vscode from 'vscode'; +import { DocumentSelector } from '../utils/documentSelector'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import FileConfigurationManager from './fileConfigurationManager'; +import { Position } from 'vscode'; + +class TypeScriptSginatureArgumentsLabelProvider implements vscode.SignatureArgumentsLabelProvider { + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly fileConfigurationManager: FileConfigurationManager + ) { + if (this.client && this.fileConfigurationManager) { + // nothing + } + vscode.window.showInformationMessage("Loaded") + } + + provideSignatureArgumentsLabels(model: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult { + if (model && token) { + // void + } + + vscode.window.showInformationMessage("Provided") + return { + signatures: [ + { + arguments: [ + { + name: 'foo', + positions: [ + new Position(1, 1) + ] + } + ] + } + ], + dispose() { + // nothing + } + } + } + +} + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, +) { + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerSignatureArgumentsLabelProvider(selector.semantic, + new TypeScriptSginatureArgumentsLabelProvider(client, fileConfigurationManager)); + }); +} diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 92ab84be30892..94c460058ae7c 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -82,6 +82,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/signatureArgumentsLabel').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), ]); } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 14914608be85f..c9b464dd296d2 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -3759,8 +3759,6 @@ export const enum EditorOption { wrappingIndent, wrappingStrategy, showDeprecated, - signatureArgumentsLabel, - // Leave these at the end (because they have dependencies!) editorClassName, pixelRatio, diff --git a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts index c70d89279cfbc..a10494465db99 100644 --- a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts +++ b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts @@ -1,135 +1,253 @@ -import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancelablePromise, TimeoutTimer, createCancelablePromise } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { hash } from 'vs/base/common/hash'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; -import { SignatureArgumentsLabelProviderRegistry, SignatureArgumentsLabelProvider, SignatureArgumentsSignature, SignatureArgumentsLabelList } from 'vs/editor/common/modes'; -import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; +import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { SignatureArgumentsLabelProvider, SignatureArgumentsLabelProviderRegistry, SignautreArguments } from 'vs/editor/common/modes'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { CancellationToken } from 'vscode'; +import { flatten } from 'vs/base/common/arrays';; +import { INotificationService } from 'vs/platform/notification/common/notification'; -export interface SignatureArgumentsLabelItem { - signature: SignatureArgumentsSignature; +const MAX_DECORATORS = 500; + +export interface SignatureArgumentsLabelData { + arguments: SignautreArguments[]; provider: SignatureArgumentsLabelProvider; } +export function getSignatures(model: ITextModel, token: CancellationToken): Promise { + const datas: SignatureArgumentsLabelData[] = []; + const providers = SignatureArgumentsLabelProviderRegistry.ordered(model).reverse(); + const promises = providers.map(provider => Promise.resolve(provider.provideSignatureArgumentsLabels(model, token)).then(result => { + if (result) { + for (let signature of result.signatures) { + datas.push({ arguments: signature.arguments, provider }); + } + } + })); -class SignatureArgumentsLabelModel { - signatures: SignatureArgumentsLabelItem[] = []; + return Promise.all(promises).then(() => datas); +} - private readonly _disposables = new DisposableStore(); +export class SignatureArgumentsLabelDetector extends Disposable implements IEditorContribution { - dispose(): void { - this._disposables.dispose(); - } + public static readonly ID: string = 'editor.contrib.signatureArgumentsLabel'; - add(list: SignatureArgumentsLabelList, provider: SignatureArgumentsLabelProvider): void { - this._disposables.add(list); - for (const signature of list.signatures) { - this.signatures.push({ signature, provider }); - } - } -} + static readonly RECOMPUTE_TIME = 1000; // ms + + private readonly _localToDispose = this._register(new DisposableStore()); + private _computePromise: CancelablePromise | null; + private _timeoutTimer: TimeoutTimer | null; -async function getSignatureArgumentsLabelModel(model: ITextModel, token: CancellationToken) { - const provider = SignatureArgumentsLabelProviderRegistry.ordered(model); - const providerRanks = new Map(); - const result = new SignatureArgumentsLabelModel(); + private _decorationsIds: string[] = []; + private _labelDatas = new Map(); - const promises = provider.map(async (provider, i) => { + private _labelDecoratorIds: string[] = []; + private readonly _decorationsTypes = new Set(); - providerRanks.set(provider, i); + private _isEnabled: boolean; - try { - const list = await Promise.resolve(provider.provideSignatureArgumentsLabels(model, token)); - if (list) { - result.add(list, provider); + constructor(private readonly _editor: ICodeEditor, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, + @INotificationService private readonly _notificationService: INotificationService, + ) { + super(); + this._notificationService.info("wtf"); + this._register(_editor.onDidChangeModel(() => { + this._isEnabled = this.isEnabled(); + this.onModelChanged(); + })); + this._register(_editor.onDidChangeModelLanguage(() => this.onModelChanged())); + this._register(SignatureArgumentsLabelProviderRegistry.onDidChange(() => this.onModelChanged())); + this._register(_editor.onDidChangeConfiguration(() => { + let prevIsEnabled = this._isEnabled; + this._isEnabled = this.isEnabled(); + if (prevIsEnabled !== this._isEnabled) { + if (this._isEnabled) { + this.onModelChanged(); + } else { + this.removeAllDecorations(); + } } - } catch (err) { - onUnexpectedExternalError(err); - } - }); + })); - await Promise.all(promises); - return result; -} + this._timeoutTimer = null; + this._computePromise = null; + this._isEnabled = this.isEnabled(); + this.onModelChanged(); + } -class SignatureArgumentsLabelController implements IEditorContribution { + isEnabled(): boolean { + const model = this._editor.getModel(); + if (!model) { + return false; + } - public static readonly ID = 'editor.contrib.signatureArgumentsLabel'; + return this._editor.getOption(EditorOption.colorDecorators); + } - static get(editor: ICodeEditor): SignatureArgumentsLabelController { - return editor.getContribution(SignatureArgumentsLabelController.ID); + static get(editor: ICodeEditor): SignatureArgumentsLabelDetector { + return editor.getContribution(this.ID); } - private readonly _disposables = new DisposableStore(); + dispose(): void { + this.stop(); + this.removeAllDecorations(); + super.dispose(); + } - private readonly _getSignatureArgumentsLabelModelDelays = new LanguageFeatureRequestDelays(SignatureArgumentsLabelProviderRegistry, 250, 2500); - private _getSignatureArgumentsLabelModelPromise: CancelablePromise | undefined; - private _oldSignatureArgumentsLabelModels = new DisposableStore(); - private _currentSignatureArgumentsLabelModel: SignatureArgumentsLabelModel | undefined; + private onModelChanged(): void { + this.stop(); - constructor( - private readonly _editor: ICodeEditor - ) { - this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange())); - this._disposables.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); - this._disposables.add(this._editor.onDidChangeConfiguration((e) => { + if (!this._isEnabled) { + return; + } + const model = this._editor.getModel(); + if (!model || !SignatureArgumentsLabelProviderRegistry.has(model)) { + return; + } + + this._localToDispose.add(this._editor.onDidChangeModelContent(() => { + if (!this._timeoutTimer) { + this._timeoutTimer = new TimeoutTimer(); + this._timeoutTimer.cancelAndSet(() => { + this._timeoutTimer = null; + + this.beginCompute(); + }, SignatureArgumentsLabelDetector.RECOMPUTE_TIME); + } })); - this._disposables.add(SignatureArgumentsLabelProviderRegistry.onDidChange(this._onModelChange, this)); - this._onModelChange(); + this.beginCompute(); } - dispose(): void { - this._localDispose(); - this._disposables.dispose(); + private beginCompute(): void { + this._computePromise = createCancelablePromise(token => { + const model = this._editor.getModel(); + if (!model) { + return Promise.resolve([]); + } + return getSignatures(model, token); + }); + this._computePromise.then((labelData) => { + this.updateDecorations(labelData); + this.updateLabelDecorators(labelData); + this._computePromise = null; + }, onUnexpectedError); } - private _localDispose(): void { + private stop(): void { + if (this._timeoutTimer) { + this._timeoutTimer.cancel(); + this._timeoutTimer = null; + } + if (this._computePromise) { + this._computePromise.cancel(); + this._computePromise = null; + } + this._localToDispose.clear(); + } + + private updateDecorations(labelData: SignatureArgumentsLabelData[]): void { + + this._notificationService.info("updateDecorations"); + + const decorations = flatten(flatten(labelData.map(args => args.arguments.map(arg => { + return arg.positions.map(pos => { + return { + range: { + startLineNumber: pos.lineNumber, + startColumn: pos.column, + endLineNumber: pos.lineNumber, + endColumn: pos.column + }, + options: ModelDecorationOptions.EMPTY + } + }) + })))); + + this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations); + this._labelDatas = new Map(); + this._decorationsIds.forEach((id, i) => this._labelDatas.set(id, labelData[i])); } - private _onModelChange(): void { - this._localDispose(); + private updateLabelDecorators(signatures: SignatureArgumentsLabelData[]): void { - const model = this._editor.getModel(); - if (!model) { - return; - } + this._notificationService.info("updateLabelDecorators"); - if (!this._editor.getOption(EditorOption.signatureArgumentsLabel)) { - return; - } + let decorations: IModelDeltaDecoration[] = []; + let newDecorationsTypes: { [key: string]: boolean } = {}; + + for (let i = 0; i < signatures.length; i++) { + const args = signatures[i].arguments; + for (let j = 0; j < args.length && decorations.length < MAX_DECORATORS; j++) { + const { name, positions } = args[j] - const scheduler = new RunOnceScheduler(() => { - const t1 = Date.now(); + const subKey = hash(name).toString(16); + let key = 'signatureArgumentsLabel-' + subKey; - this._getSignatureArgumentsLabelModelPromise?.cancel(); - this._getSignatureArgumentsLabelModelPromise = createCancelablePromise(token => getSignatureArgumentsLabelModel(model, token)); + if (!this._decorationsTypes.has(key) && !newDecorationsTypes[key]) { + this._codeEditorService.registerDecorationType(key, { + before: { + contentText: `${name}:`, + border: 'solid 1px gray', + backgroundColor: '#333', + margin: '1px', + color: 'white' + } + }, undefined, this._editor); + } + + newDecorationsTypes[key] = true; - this._getSignatureArgumentsLabelModelPromise.then(result => { - if (this._currentSignatureArgumentsLabelModel) { - this._oldSignatureArgumentsLabelModels.add(this._currentSignatureArgumentsLabelModel); + + for (let k = 0; k < positions.length && decorations.length < MAX_DECORATORS; ++k) { + const pos = positions[k] + decorations.push({ + range: { + startLineNumber: pos.lineNumber, + startColumn: pos.column, + endLineNumber: pos.lineNumber, + endColumn: pos.column + }, + options: this._codeEditorService.resolveDecorationOptions(key, true) + }); } - this._currentSignatureArgumentsLabelModel = result; + } + } + - // update moving average - const newDelay = this._getSignatureArgumentsLabelModelDelays.update(model, Date.now() - t1); - scheduler.delay = newDelay; + this._notificationService.info(`${decorations.length} length`) - // render lenses - this._renderSignatureArgumentsLabels(result); - }, onUnexpectedError); + this._decorationsTypes.forEach(subType => { + if (!newDecorationsTypes[subType]) { + this._codeEditorService.removeDecorationType(subType); + } + }); - }, this._getSignatureArgumentsLabelModelDelays.get(model)); + this._labelDecoratorIds = this._editor.deltaDecorations(this._labelDecoratorIds, decorations); } - _renderSignatureArgumentsLabels(result: SignatureArgumentsLabelModel) { + private removeAllDecorations(): void { + this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []); + this._labelDecoratorIds = this._editor.deltaDecorations(this._labelDecoratorIds, []); + this._decorationsTypes.forEach(subType => { + this._codeEditorService.removeDecorationType(subType); + }); } } -registerEditorContribution(SignatureArgumentsLabelController.ID, SignatureArgumentsLabelController); +registerEditorContribution(SignatureArgumentsLabelDetector.ID, SignatureArgumentsLabelDetector); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 904283213acc8..19b3b7637f390 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -46,6 +46,7 @@ import 'vs/editor/contrib/viewportSemanticTokens/viewportSemanticTokens'; import 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import 'vs/editor/contrib/wordOperations/wordOperations'; import 'vs/editor/contrib/wordPartOperations/wordPartOperations'; +import 'vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel'; // Load up these strings even in VSCode, even if they are not used // in order to get them translated diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 64ddb94e92bc5..8f9294e6996ac 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6361,6 +6361,24 @@ declare namespace monaco.languages { resolveCodeLens?(model: editor.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; } + export interface SignautreArguments { + name: string; + positions: IPosition[]; + } + + export interface SignatureArgumentsSignature { + arguments: SignautreArguments[]; + } + + export interface SignatureArgumentsLabelList { + signatures: SignatureArgumentsSignature[]; + dispose(): void; + } + + export interface SignatureArgumentsLabelProvider { + provideSignatureArgumentsLabels(model: editor.ITextModel, token: CancellationToken): ProviderResult; + } + export interface SemanticTokensLegend { readonly tokenTypes: string[]; readonly tokenModifiers: string[]; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 028a0430040de..a1609c987d048 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -10853,6 +10853,8 @@ declare module 'vscode' { export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; + export function registerSignatureArgumentsLabelProvider(selector: DocumentSelector, provider: SignatureArgumentsLabelProvider): Disposable; + /** * Register a document link provider. * diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index a0f4b2def792d..451605681d084 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -499,10 +499,20 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- parameter labels - $registerSignatureArgumentsLabelSupport(handle: number, selector: IDocumentFilterDto[]): void { + $registerSignatureArgumentsLabelProvider(handle: number, selector: IDocumentFilterDto[]): void { this._registrations.set(handle, modes.SignatureArgumentsLabelProviderRegistry.register(selector, { provideSignatureArgumentsLabels: async (model: ITextModel, token: CancellationToken): Promise => { - return this._proxy.$provideSignatureArgumentsLabel(handle, model.uri, token); + const result = await this._proxy.$provideSignatureArgumentsLabel(handle, model.uri, token); + + if (!result) { + return undefined + } + return { + signatures: result.signatures, + dispose: () => { + + } + } } })); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 6d6bdd4bd2d37..35e00f2bb1ebd 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -482,6 +482,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I getTokenInformationAtPosition(doc: vscode.TextDocument, pos: vscode.Position) { checkProposedApiEnabled(extension); return extHostLanguages.tokenAtPosition(doc, pos); + }, + registerSignatureArgumentsLabelProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureArgumentsLabelProvider): vscode.Disposable { + return extHostLanguageFeatures.registerSignatureArgumentsLabelProvider(extension, selector, provider) } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e133a331a3c10..9dbc36a5b1ca5 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -399,6 +399,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void; $registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; + $registerSignatureArgumentsLabelProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void; $registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 086811373a629..d57bbcd508cce 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1075,7 +1075,7 @@ class SignatureArgumentsLabelAdapter { return asPromise(() => this._provider.provideSignatureArgumentsLabels(doc, token)).then(value => { if (value) { const id = this._cache.add([value]); - return { ...typeConvert.SignatrueArgumentsLabelList.from(value), id }; + return { signatures: value.signatures.map(typeConvert.SignatureArgumentsSignature.from), id }; } return undefined; }); @@ -1340,7 +1340,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter - | LinkedEditingRangeAdapter; + | LinkedEditingRangeAdapter | SignatureArgumentsLabelAdapter; class AdapterData { constructor( @@ -1782,6 +1782,12 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._createDisposable(handle); } + registerSignatureArgumentsLabelProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SignatureArgumentsLabelProvider): vscode.Disposable { + const handle = this._addNewAdapter(new SignatureArgumentsLabelAdapter(this._documents, provider), extension); + this._proxy.$registerSignatureArgumentsLabelProvider(handle, this._transformDocumentSelector(selector)); + return this._createDisposable(handle); + } + $provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: extHostProtocol.ISignatureHelpContextDto, token: CancellationToken): Promise { return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token), undefined); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 9ce93ea7dd513..53f078dfd7a81 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1015,13 +1015,14 @@ export namespace SignatureHelp { } } -export namespace SignatrueArgumentsLabelList { - export function from(labels: vscode.SignatureArgumentsLabelList): modes.SignatureArgumentsLabelList { - - } - - export function to(labels: modes.SignatureArgumentsLabelList): vscode.SignatureArgumentsLabelList { - +export namespace SignatureArgumentsSignature { + export function from(signature: vscode.SignatureArgumentsSignature): modes.SignatureArgumentsSignature { + return { + arguments: signature.arguments.map(arg => ({ + name: arg.name, + positions: arg.positions.map(pos => Position.from(pos)) + })) + } } } From 3de96fd23725b7046330322dcc6461d6c1b1d718 Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 23 Dec 2020 18:51:15 +0800 Subject: [PATCH 06/36] Simplify code --- .../signatureArgumentsLabel.ts | 55 ++++++------- .../src/languageProvider.ts | 2 +- .../src/typescriptService.ts | 20 +++++ src/vs/editor/common/config/editorOptions.ts | 6 ++ src/vs/editor/common/modes.ts | 14 +--- .../common/standalone/standaloneEnums.ts | 11 +-- .../signatureArgumentsLabel.ts | 82 +++++++------------ src/vs/monaco.d.ts | 28 +++---- src/vs/vscode.d.ts | 15 +--- .../api/browser/mainThreadLanguageFeatures.ts | 12 +-- .../workbench/api/common/extHost.api.impl.ts | 1 + .../workbench/api/common/extHost.protocol.ts | 9 +- .../api/common/extHostLanguageFeatures.ts | 4 +- .../api/common/extHostTypeConverters.ts | 10 +-- src/vs/workbench/api/common/extHostTypes.ts | 25 +----- 15 files changed, 122 insertions(+), 172 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts b/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts index 4870f30fd20f8..8e55cc315c6e0 100644 --- a/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts +++ b/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts @@ -2,56 +2,47 @@ import * as vscode from 'vscode'; import { DocumentSelector } from '../utils/documentSelector'; import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; -import FileConfigurationManager from './fileConfigurationManager'; import { Position } from 'vscode'; +const dummy = [new vscode.SignautreArgumentsLabel('foo', new Position(1, 1))]; + class TypeScriptSginatureArgumentsLabelProvider implements vscode.SignatureArgumentsLabelProvider { constructor( - private readonly client: ITypeScriptServiceClient, - private readonly fileConfigurationManager: FileConfigurationManager - ) { - if (this.client && this.fileConfigurationManager) { - // nothing - } - vscode.window.showInformationMessage("Loaded") - } + private readonly client: ITypeScriptServiceClient + ) { } - provideSignatureArgumentsLabels(model: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult { - if (model && token) { - // void + async provideSignatureArgumentsLabels(model: vscode.TextDocument, token: vscode.CancellationToken): Promise { + const filepath = this.client.toOpenedFilePath(model); + if (!filepath) { + return []; } - vscode.window.showInformationMessage("Provided") - return { - signatures: [ - { - arguments: [ - { - name: 'foo', - positions: [ - new Position(1, 1) - ] - } - ] - } - ], - dispose() { - // nothing + try { + const response = await this.client.execute('provideSignatureArgumentsLabel', { file: filepath }, token); + if (response.type !== 'response' || !response.success || !response.body) { + return dummy; } + + const labels = response.body.map(label => { + return new vscode.SignautreArgumentsLabel(label.name, new vscode.Position( + label.position.line, label.position.offset + )) + }); + return labels + } catch { + return dummy } } - } export function register( selector: DocumentSelector, - client: ITypeScriptServiceClient, - fileConfigurationManager: FileConfigurationManager, + client: ITypeScriptServiceClient ) { return conditionalRegistration([ requireSomeCapability(client, ClientCapability.Semantic), ], () => { return vscode.languages.registerSignatureArgumentsLabelProvider(selector.semantic, - new TypeScriptSginatureArgumentsLabelProvider(client, fileConfigurationManager)); + new TypeScriptSginatureArgumentsLabelProvider(client)); }); } diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 94c460058ae7c..0cf4cc12298c5 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -82,7 +82,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), - import('./languageFeatures/signatureArgumentsLabel').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), + import('./languageFeatures/signatureArgumentsLabel').then(provider => this._register(provider.register(selector, this.client))), ]); } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 588925ec6db70..abefd8af71a56 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -13,6 +13,25 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import { PluginManager } from './utils/plugins'; import { TelemetryReporter } from './utils/telemetry'; +export namespace Experimental { + export const enum CommandTypes { + ProvideSignatureArgumentsLabel = 'provideSignatureArgumentsLabel' + } + + export interface ProvideSignatureArgumentsLabelRequest extends Proto.FileRequest { + command: CommandTypes.ProvideSignatureArgumentsLabel; + } + + interface LabelItem { + name: string + position: Proto.Location + } + + export interface ProvideSignatureArgumentsLabelResponse extends Proto.Response { + body?: LabelItem[]; + } +} + export enum ServerType { Syntax = 'syntax', Semantic = 'semantic', @@ -68,6 +87,7 @@ interface StandardTsServerRequests { 'prepareCallHierarchy': [Proto.FileLocationRequestArgs, Proto.PrepareCallHierarchyResponse]; 'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse]; 'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse]; + 'provideSignatureArgumentsLabel': [Proto.FileRequestArgs, Experimental.ProvideSignatureArgumentsLabelResponse]; } interface NoResponseTsServerRequests { diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index c9b464dd296d2..3208c58414f43 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -625,6 +625,7 @@ export interface IEditorOptions { * Controls strikethrough deprecated variables. */ showDeprecated?: boolean; + showSignatureArgumentsLabel?: boolean } /** @@ -3759,6 +3760,7 @@ export const enum EditorOption { wrappingIndent, wrappingStrategy, showDeprecated, + showSignatureArgumentsLabel, // Leave these at the end (because they have dependencies!) editorClassName, pixelRatio, @@ -4262,6 +4264,10 @@ export const EditorOptions = { EditorOption.showDeprecated, 'showDeprecated', true, { description: nls.localize('showDeprecated', "Controls strikethrough deprecated variables.") } )), + showSignatureArgumentsLabel: register(new EditorBooleanOption( + EditorOption.showSignatureArgumentsLabel, 'showSignatureArgumentsLabel', true, + { description: nls.localize('showSignatureArgumentsLabel', "Controls show signature arguments label.") } + )), snippetSuggestions: register(new EditorStringEnumOption( EditorOption.snippetSuggestions, 'snippetSuggestions', 'inline' as 'top' | 'bottom' | 'inline' | 'none', diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 193e26cbf8711..1d23234fec73d 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1659,21 +1659,13 @@ export interface CodeLensProvider { resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; } -export interface SignautreArguments { +export interface SignautreArgumentsLabel { name: string - positions: IPosition[] -} -export interface SignatureArgumentsSignature { - arguments: SignautreArguments[] -} - -export interface SignatureArgumentsLabelList { - signatures: SignatureArgumentsSignature[] - dispose(): void; + position: IPosition } export interface SignatureArgumentsLabelProvider { - provideSignatureArgumentsLabels(model: model.ITextModel, token: CancellationToken): ProviderResult; + provideSignatureArgumentsLabels(model: model.ITextModel, token: CancellationToken): ProviderResult; } export interface SemanticTokensLegend { diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 8c772e3c39603..ccb944f83e5d3 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -287,11 +287,12 @@ export enum EditorOption { wrappingIndent = 117, wrappingStrategy = 118, showDeprecated = 119, - editorClassName = 120, - pixelRatio = 121, - tabFocusMode = 122, - layoutInfo = 123, - wrappingInfo = 124 + showSignatureArgumentsLabel = 120, + editorClassName = 121, + pixelRatio = 122, + tabFocusMode = 123, + layoutInfo = 124, + wrappingInfo = 125 } /** diff --git a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts index a10494465db99..07120934a4062 100644 --- a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts +++ b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts @@ -13,16 +13,15 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { SignatureArgumentsLabelProvider, SignatureArgumentsLabelProviderRegistry, SignautreArguments } from 'vs/editor/common/modes'; +import { SignatureArgumentsLabelProvider, SignatureArgumentsLabelProviderRegistry, SignautreArgumentsLabel } from 'vs/editor/common/modes'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { CancellationToken } from 'vscode'; import { flatten } from 'vs/base/common/arrays';; -import { INotificationService } from 'vs/platform/notification/common/notification'; const MAX_DECORATORS = 500; export interface SignatureArgumentsLabelData { - arguments: SignautreArguments[]; + list: SignautreArgumentsLabel[]; provider: SignatureArgumentsLabelProvider; } @@ -31,9 +30,8 @@ export function getSignatures(model: ITextModel, token: CancellationToken): Prom const providers = SignatureArgumentsLabelProviderRegistry.ordered(model).reverse(); const promises = providers.map(provider => Promise.resolve(provider.provideSignatureArgumentsLabels(model, token)).then(result => { if (result) { - for (let signature of result.signatures) { - datas.push({ arguments: signature.arguments, provider }); - } + datas.push({ list: result, provider }); + } })); @@ -59,11 +57,9 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit private _isEnabled: boolean; constructor(private readonly _editor: ICodeEditor, - @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, - @INotificationService private readonly _notificationService: INotificationService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService ) { super(); - this._notificationService.info("wtf"); this._register(_editor.onDidChangeModel(() => { this._isEnabled = this.isEnabled(); this.onModelChanged(); @@ -94,7 +90,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit return false; } - return this._editor.getOption(EditorOption.colorDecorators); + return this._editor.getOption(EditorOption.showSignatureArgumentsLabel); } static get(editor: ICodeEditor): SignatureArgumentsLabelDetector { @@ -160,22 +156,17 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit } private updateDecorations(labelData: SignatureArgumentsLabelData[]): void { - - this._notificationService.info("updateDecorations"); - - const decorations = flatten(flatten(labelData.map(args => args.arguments.map(arg => { - return arg.positions.map(pos => { - return { - range: { - startLineNumber: pos.lineNumber, - startColumn: pos.column, - endLineNumber: pos.lineNumber, - endColumn: pos.column - }, - options: ModelDecorationOptions.EMPTY - } - }) - })))); + const decorations = flatten(labelData.map(labels => labels.list.map(label => { + return { + range: { + startLineNumber: label.position.lineNumber, + startColumn: label.position.column, + endLineNumber: label.position.lineNumber, + endColumn: label.position.column + }, + options: ModelDecorationOptions.EMPTY + } + }))); this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations); @@ -183,17 +174,14 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._decorationsIds.forEach((id, i) => this._labelDatas.set(id, labelData[i])); } - private updateLabelDecorators(signatures: SignatureArgumentsLabelData[]): void { - - this._notificationService.info("updateLabelDecorators"); - + private updateLabelDecorators(labelData: SignatureArgumentsLabelData[]): void { let decorations: IModelDeltaDecoration[] = []; let newDecorationsTypes: { [key: string]: boolean } = {}; - for (let i = 0; i < signatures.length; i++) { - const args = signatures[i].arguments; - for (let j = 0; j < args.length && decorations.length < MAX_DECORATORS; j++) { - const { name, positions } = args[j] + for (let i = 0; i < labelData.length; i++) { + const label = labelData[i].list; + for (let j = 0; j < label.length && decorations.length < MAX_DECORATORS; j++) { + const { name, position } = label[j] const subKey = hash(name).toString(16); let key = 'signatureArgumentsLabel-' + subKey; @@ -211,26 +199,18 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit } newDecorationsTypes[key] = true; - - - for (let k = 0; k < positions.length && decorations.length < MAX_DECORATORS; ++k) { - const pos = positions[k] - decorations.push({ - range: { - startLineNumber: pos.lineNumber, - startColumn: pos.column, - endLineNumber: pos.lineNumber, - endColumn: pos.column - }, - options: this._codeEditorService.resolveDecorationOptions(key, true) - }); - } + decorations.push({ + range: { + startLineNumber: position.lineNumber, + startColumn: position.column, + endLineNumber: position.lineNumber, + endColumn: position.column + }, + options: this._codeEditorService.resolveDecorationOptions(key, true) + }); } } - - this._notificationService.info(`${decorations.length} length`) - this._decorationsTypes.forEach(subType => { if (!newDecorationsTypes[subType]) { this._codeEditorService.removeDecorationType(subType); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8f9294e6996ac..9317729f74a75 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3176,6 +3176,7 @@ declare namespace monaco.editor { * Controls strikethrough deprecated variables. */ showDeprecated?: boolean; + showSignatureArgumentsLabel?: boolean; } /** @@ -4028,11 +4029,12 @@ declare namespace monaco.editor { wrappingIndent = 117, wrappingStrategy = 118, showDeprecated = 119, - editorClassName = 120, - pixelRatio = 121, - tabFocusMode = 122, - layoutInfo = 123, - wrappingInfo = 124 + showSignatureArgumentsLabel = 120, + editorClassName = 121, + pixelRatio = 122, + tabFocusMode = 123, + layoutInfo = 124, + wrappingInfo = 125 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -4133,6 +4135,7 @@ declare namespace monaco.editor { showFoldingControls: IEditorOption; showUnused: IEditorOption; showDeprecated: IEditorOption; + showSignatureArgumentsLabel: IEditorOption; snippetSuggestions: IEditorOption; smartSelect: IEditorOption; smoothScrolling: IEditorOption; @@ -6361,22 +6364,13 @@ declare namespace monaco.languages { resolveCodeLens?(model: editor.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; } - export interface SignautreArguments { + export interface SignautreArgumentsLabel { name: string; - positions: IPosition[]; - } - - export interface SignatureArgumentsSignature { - arguments: SignautreArguments[]; - } - - export interface SignatureArgumentsLabelList { - signatures: SignatureArgumentsSignature[]; - dispose(): void; + position: IPosition; } export interface SignatureArgumentsLabelProvider { - provideSignatureArgumentsLabels(model: editor.ITextModel, token: CancellationToken): ProviderResult; + provideSignatureArgumentsLabels(model: editor.ITextModel, token: CancellationToken): ProviderResult; } export interface SemanticTokensLegend { diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index a1609c987d048..bc9eed05d13f0 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3755,22 +3755,15 @@ declare module 'vscode' { provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; } - export interface SignautreArguments { + export class SignautreArgumentsLabel { name: string - positions: Position[] - } - export interface SignatureArgumentsSignature { - arguments: SignautreArguments[] - } + position: Position - export interface SignatureArgumentsLabelList { - signatures: SignatureArgumentsSignature[] - dispose(): void; + constructor(name: string, position: Position) } - export interface SignatureArgumentsLabelProvider { - provideSignatureArgumentsLabels(model: TextDocument, token: CancellationToken): ProviderResult; + provideSignatureArgumentsLabels(model: TextDocument, token: CancellationToken): ProviderResult; } /** diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 451605681d084..90835ef534e7d 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -497,22 +497,16 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha })); } - // --- parameter labels + // --- signature arguments labels $registerSignatureArgumentsLabelProvider(handle: number, selector: IDocumentFilterDto[]): void { this._registrations.set(handle, modes.SignatureArgumentsLabelProviderRegistry.register(selector, { - provideSignatureArgumentsLabels: async (model: ITextModel, token: CancellationToken): Promise => { + provideSignatureArgumentsLabels: async (model: ITextModel, token: CancellationToken): Promise => { const result = await this._proxy.$provideSignatureArgumentsLabel(handle, model.uri, token); - if (!result) { return undefined } - return { - signatures: result.signatures, - dispose: () => { - - } - } + return result.labels } })); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 35e00f2bb1ebd..19a40b1f10137 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1188,6 +1188,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I SignatureHelp: extHostTypes.SignatureHelp, SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind, SignatureInformation: extHostTypes.SignatureInformation, + SignautreArgumentsLabel: extHostTypes.SignautreArgumentsLabel, SnippetString: extHostTypes.SnippetString, SourceBreakpoint: extHostTypes.SourceBreakpoint, StandardTokenType: extHostTypes.StandardTokenType, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9dbc36a5b1ca5..e0f162102db7f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1270,16 +1270,13 @@ export interface ISignatureHelpContextDto { readonly activeSignatureHelp?: ISignatureHelpDto; } -export interface ISignautreArgumentsDto { +export interface ISignautreArgumentsLabelDto { name: string - positions: IPosition[] -} -export interface ISignatureArgumentsSignatureDto { - arguments: ISignautreArgumentsDto[] + position: IPosition } export interface ISignatureArgumentsLabelDto { - signatures: ISignatureArgumentsSignatureDto[] + labels: ISignautreArgumentsLabelDto[] } export interface ILocationDto { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index d57bbcd508cce..8c7eb051a309c 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1063,7 +1063,7 @@ class SignatureHelpAdapter { } class SignatureArgumentsLabelAdapter { - private readonly _cache = new Cache('SignatureArgumentsLabel'); + private readonly _cache = new Cache('SignatureArgumentsLabel'); constructor( private readonly _documents: ExtHostDocuments, @@ -1075,7 +1075,7 @@ class SignatureArgumentsLabelAdapter { return asPromise(() => this._provider.provideSignatureArgumentsLabels(doc, token)).then(value => { if (value) { const id = this._cache.add([value]); - return { signatures: value.signatures.map(typeConvert.SignatureArgumentsSignature.from), id }; + return { labels: value.map(typeConvert.SignatureArgumentsLabelList.from), id }; } return undefined; }); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 53f078dfd7a81..20d4f30593b19 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1015,13 +1015,11 @@ export namespace SignatureHelp { } } -export namespace SignatureArgumentsSignature { - export function from(signature: vscode.SignatureArgumentsSignature): modes.SignatureArgumentsSignature { +export namespace SignatureArgumentsLabelList { + export function from(label: vscode.SignautreArgumentsLabel): modes.SignautreArgumentsLabel { return { - arguments: signature.arguments.map(arg => ({ - name: arg.name, - positions: arg.positions.map(pos => Position.from(pos)) - })) + name: label.name, + position: Position.from(label.position) } } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 815330cb6186e..a8a096970ce1c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1374,30 +1374,13 @@ export enum SignatureHelpTriggerKind { } @es5ClassCompat -export class SignautreArguments { +export class SignautreArgumentsLabel { name: string; - positions: Position[]; + position: Position; - constructor(name: string, positions: Position[]) { + constructor(name: string, position: Position) { this.name = name; - this.positions = positions; - } -} - -@es5ClassCompat -export class SignatureArgumentsSignature { - arguments: SignautreArguments[]; - - constructor() { - this.arguments = []; - } -} - -@es5ClassCompat -export class SignatureArgumentsLabelList { - signatures: SignatureArgumentsSignature[]; - constructor() { - this.signatures = []; + this.position = position; } } From a6fd2cc10887ebe6534734b6d325ccdca0fe0329 Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 24 Dec 2020 12:28:11 +0800 Subject: [PATCH 07/36] Use utils type converter --- .../signatureArgumentsLabel.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts b/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts index 8e55cc315c6e0..6673feba039f8 100644 --- a/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts +++ b/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts @@ -1,10 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + import * as vscode from 'vscode'; import { DocumentSelector } from '../utils/documentSelector'; import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; -import { Position } from 'vscode'; - -const dummy = [new vscode.SignautreArgumentsLabel('foo', new Position(1, 1))]; +import { Position } from '../utils/typeConverters'; class TypeScriptSginatureArgumentsLabelProvider implements vscode.SignatureArgumentsLabelProvider { constructor( @@ -20,17 +23,15 @@ class TypeScriptSginatureArgumentsLabelProvider implements vscode.SignatureArgum try { const response = await this.client.execute('provideSignatureArgumentsLabel', { file: filepath }, token); if (response.type !== 'response' || !response.success || !response.body) { - return dummy; + return []; } const labels = response.body.map(label => { - return new vscode.SignautreArgumentsLabel(label.name, new vscode.Position( - label.position.line, label.position.offset - )) + return new vscode.SignautreArgumentsLabel(label.name, Position.fromLocation(label.position)); }); - return labels - } catch { - return dummy + return labels; + } catch (e) { + return []; } } } From bf4e2371d4ec424c1c4f33f4a0714a5570a8a70a Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 24 Dec 2020 15:19:31 +0800 Subject: [PATCH 08/36] Avoid generated file --- build/azure-pipelines/common/symbols.js | 185 ------------------------ 1 file changed, 185 deletions(-) delete mode 100644 build/azure-pipelines/common/symbols.js diff --git a/build/azure-pipelines/common/symbols.js b/build/azure-pipelines/common/symbols.js deleted file mode 100644 index 5a436897e66cb..0000000000000 --- a/build/azure-pipelines/common/symbols.js +++ /dev/null @@ -1,185 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const request = require("request"); -const fs_1 = require("fs"); -const github = require("github-releases"); -const path_1 = require("path"); -const os_1 = require("os"); -const util_1 = require("util"); -const BASE_URL = 'https://rink.hockeyapp.net/api/2/'; -const HOCKEY_APP_TOKEN_HEADER = 'X-HockeyAppToken'; -var Platform; -(function (Platform) { - Platform["WIN_32"] = "win32-ia32"; - Platform["WIN_64"] = "win32-x64"; - Platform["LINUX_64"] = "linux-x64"; - Platform["MAC_OS"] = "darwin-x64"; -})(Platform || (Platform = {})); -function symbolsZipName(platform, electronVersion, insiders) { - return `${insiders ? 'insiders' : 'stable'}-symbols-v${electronVersion}-${platform}.zip`; -} -const SEED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -async function tmpFile(name) { - let res = ''; - for (let i = 0; i < 8; i++) { - res += SEED.charAt(Math.floor(Math.random() * SEED.length)); - } - const tmpParent = path_1.join(os_1.tmpdir(), res); - await util_1.promisify(fs_1.mkdir)(tmpParent); - return path_1.join(tmpParent, name); -} -function getVersions(accessor) { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions`, - method: 'GET', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - } - }); -} -function createVersion(accessor, version) { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions/new`, - method: 'POST', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - }, - formData: { - bundle_version: version - } - }); -} -function updateVersion(accessor, symbolsPath) { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions/${accessor.id}`, - method: 'PUT', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - }, - formData: { - dsym: fs_1.createReadStream(symbolsPath) - } - }); -} -function asyncRequest(options) { - return new Promise((resolve, reject) => { - request(options, (error, _response, body) => { - if (error) { - reject(error); - } - else { - resolve(JSON.parse(body)); - } - }); - }); -} -function downloadAsset(repository, assetName, targetPath, electronVersion) { - return new Promise((resolve, reject) => { - repository.getReleases({ tag_name: `v${electronVersion}` }, (err, releases) => { - if (err) { - reject(err); - } - else { - const asset = releases[0].assets.filter((asset) => asset.name === assetName)[0]; - if (!asset) { - reject(new Error(`Asset with name ${assetName} not found`)); - } - else { - repository.downloadAsset(asset, (err, reader) => { - if (err) { - reject(err); - } - else { - const writer = fs_1.createWriteStream(targetPath); - writer.on('error', reject); - writer.on('close', resolve); - reader.on('error', reject); - reader.pipe(writer); - } - }); - } - } - }); - }); -} -async function ensureVersionAndSymbols(options) { - // Check version does not exist - console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`); - const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }); - if (!Array.isArray(versions.app_versions)) { - throw new Error(`Unexpected response: ${JSON.stringify(versions)}`); - } - if (versions.app_versions.some(v => v.version === options.versions.code)) { - console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`); - return; - } - // Download symbols for platform and electron version - const symbolsName = symbolsZipName(options.platform, options.versions.electron, options.versions.insiders); - const symbolsPath = await tmpFile('symbols.zip'); - console.log(`HockeyApp: downloading symbols ${symbolsName} for electron ${options.versions.electron} (${options.platform}) into ${symbolsPath}`); - await downloadAsset(new github({ repo: options.repository, token: options.access.githubToken }), symbolsName, symbolsPath, options.versions.electron); - // Create version - console.log(`HockeyApp: creating new version ${options.versions.code} (${options.platform})`); - const version = await createVersion({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, options.versions.code); - // Upload symbols - console.log(`HockeyApp: uploading symbols for version ${options.versions.code} (${options.platform})`); - await updateVersion({ id: String(version.id), accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, symbolsPath); - // Cleanup - await util_1.promisify(fs_1.unlink)(symbolsPath); -} -// Environment -const pakage = require('../../../package.json'); -const product = require('../../../product.json'); -const repository = product.electronRepository; -const electronVersion = require('../../lib/electron').getElectronVersion(); -const insiders = product.quality !== 'stable'; -let codeVersion = pakage.version; -if (insiders) { - codeVersion = `${codeVersion}-insider`; -} -const githubToken = process.argv[2]; -const hockeyAppToken = process.argv[3]; -const is64 = process.argv[4] === 'x64'; -const hockeyAppId = process.argv[5]; -if (process.argv.length !== 6) { - throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`); -} -let platform; -if (process.platform === 'darwin') { - platform = Platform.MAC_OS; -} -else if (process.platform === 'win32') { - platform = is64 ? Platform.WIN_64 : Platform.WIN_32; -} -else { - platform = Platform.LINUX_64; -} -// Create version and upload symbols in HockeyApp -if (repository && codeVersion && electronVersion && (product.quality === 'stable' || product.quality === 'insider')) { - ensureVersionAndSymbols({ - repository, - platform, - versions: { - code: codeVersion, - insiders, - electron: electronVersion - }, - access: { - githubToken, - hockeyAppToken, - hockeyAppId - } - }).then(() => { - console.log('HockeyApp: done'); - }).catch(error => { - console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`); - return process.exit(1); - }); -} -else { - console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`); -} From ff8ae0b93fffdee603bdca4d44a0008d73da2d24 Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 24 Dec 2020 15:20:55 +0800 Subject: [PATCH 09/36] Avoid changes --- extensions/search-result/src/extension.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index d84455a5671e6..6bc5a573c95ff 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -98,7 +98,7 @@ export function activate(context: vscode.ExtensionContext) { }), vscode.languages.registerDocumentLinkProvider(SEARCH_RESULT_SELECTOR, { - async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { return parseSearchResults(document, token) .filter(isFileLine) .map(({ location }) => ({ range: location.originSelectionRange!, target: location.targetUri })); @@ -141,7 +141,7 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u } const uriFromFolderWithPath = (folder: vscode.WorkspaceFolder, path: string): vscode.Uri => - (vscode.Uri as any).joinPath(folder.uri, path); + vscode.Uri.joinPath(folder.uri, path); if (vscode.workspace.workspaceFolders) { const multiRootFormattedPath = /^(.*) • (.*)$/.exec(path); From d24ab0a201a90dbaa43e53e5872999b1f6767502 Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 24 Dec 2020 15:28:27 +0800 Subject: [PATCH 10/36] Improve comments and code style --- src/vs/editor/common/config/editorOptions.ts | 3 ++ .../signatureArgumentsLabel.ts | 8 ++-- src/vs/monaco.d.ts | 3 ++ src/vs/vscode.d.ts | 42 +++++++++++++++++-- .../api/browser/mainThreadLanguageFeatures.ts | 5 +-- .../workbench/api/common/extHost.api.impl.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 3 +- .../api/common/extHostTypeConverters.ts | 2 +- 8 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 3208c58414f43..ec7a5adb96a0c 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -625,6 +625,9 @@ export interface IEditorOptions { * Controls strikethrough deprecated variables. */ showDeprecated?: boolean; + /** + * Controls show signature arguments label. + */ showSignatureArgumentsLabel?: boolean } diff --git a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts index 07120934a4062..85c540dbba3d6 100644 --- a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts +++ b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts @@ -15,8 +15,8 @@ import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { SignatureArgumentsLabelProvider, SignatureArgumentsLabelProviderRegistry, SignautreArgumentsLabel } from 'vs/editor/common/modes'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { CancellationToken } from 'vscode'; -import { flatten } from 'vs/base/common/arrays';; +import { flatten } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; const MAX_DECORATORS = 500; @@ -165,7 +165,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit endColumn: label.position.column }, options: ModelDecorationOptions.EMPTY - } + }; }))); this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations); @@ -181,7 +181,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit for (let i = 0; i < labelData.length; i++) { const label = labelData[i].list; for (let j = 0; j < label.length && decorations.length < MAX_DECORATORS; j++) { - const { name, position } = label[j] + const { name, position } = label[j]; const subKey = hash(name).toString(16); let key = 'signatureArgumentsLabel-' + subKey; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 9317729f74a75..966eb36b74d2f 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3176,6 +3176,9 @@ declare namespace monaco.editor { * Controls strikethrough deprecated variables. */ showDeprecated?: boolean; + /** + * Controls show signature arguments label. + */ showSignatureArgumentsLabel?: boolean; } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index bc9eed05d13f0..ce2fd979d280b 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3755,14 +3755,39 @@ declare module 'vscode' { provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; } + /** + * Argument label information of signature. + */ export class SignautreArgumentsLabel { - name: string - position: Position + /** + * The name of the parameter. + */ + name: string; + /** + * The position of the argument. + */ + position: Position; - constructor(name: string, position: Position) + /** + * Creates a new signature arguments label information object. + * + * @param name A name of the parameter. + * @param position The position of the argument. + */ + constructor(name: string, position: Position); } + /** + * The document formatting provider interface defines the contract between extensions and + * the arguments label feature. + */ export interface SignatureArgumentsLabelProvider { + /** + * @param model The document in which the command was invoked. + * @param token A cancellation token. + * + * @return A list of arguments labels or a thenable that resolves to such. + */ provideSignatureArgumentsLabels(model: TextDocument, token: CancellationToken): ProviderResult; } @@ -10846,6 +10871,17 @@ declare module 'vscode' { export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; + /** + * Register a signature arguments label provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their [score](#languages.match) and the best-matching provider is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An on type signature arguments label provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ export function registerSignatureArgumentsLabelProvider(selector: DocumentSelector, provider: SignatureArgumentsLabelProvider): Disposable; /** diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 90835ef534e7d..f62beca38c113 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -503,10 +503,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha this._registrations.set(handle, modes.SignatureArgumentsLabelProviderRegistry.register(selector, { provideSignatureArgumentsLabels: async (model: ITextModel, token: CancellationToken): Promise => { const result = await this._proxy.$provideSignatureArgumentsLabel(handle, model.uri, token); - if (!result) { - return undefined - } - return result.labels + return result?.labels; } })); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 19a40b1f10137..74848b69f72c6 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -484,7 +484,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguages.tokenAtPosition(doc, pos); }, registerSignatureArgumentsLabelProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureArgumentsLabelProvider): vscode.Disposable { - return extHostLanguageFeatures.registerSignatureArgumentsLabelProvider(extension, selector, provider) + return extHostLanguageFeatures.registerSignatureArgumentsLabelProvider(extension, selector, provider); } }; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 8c7eb051a309c..06b7f1a5cfe58 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1796,7 +1796,8 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.releaseSignatureHelp(id), undefined); } - // --- arguments labels + // --- signature arguments labels + $provideSignatureArgumentsLabel(handle: number, resource: UriComponents, token: CancellationToken): Promise { return this._withAdapter(handle, SignatureArgumentsLabelAdapter, adapter => adapter.provideSignatureArgumentsLabels(URI.revive(resource), token), undefined); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 20d4f30593b19..2dd57a79011e8 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1020,7 +1020,7 @@ export namespace SignatureArgumentsLabelList { return { name: label.name, position: Position.from(label.position) - } + }; } } From 1954e9385044b5519ead5ff2e1d6a84c4cf94504 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 00:35:37 +0800 Subject: [PATCH 11/36] Adjust hint label styles --- .../browser/services/codeEditorServiceImpl.ts | 4 +- src/vs/editor/common/editorCommon.ts | 3 + .../signatureArgumentsLabel.ts | 64 +++++++++++-------- src/vs/platform/theme/common/colorRegistry.ts | 2 + src/vs/vscode.d.ts | 8 +++ .../api/common/extHostTypeConverters.ts | 2 + 6 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index c961bfd795664..b4d276cce497b 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -347,6 +347,7 @@ const _CSS_MAP: { [prop: string]: string; } = { fontStyle: 'font-style:{0};', fontWeight: 'font-weight:{0};', + fontSize: 'font-size:{0};', textDecoration: 'text-decoration:{0};', cursor: 'cursor:{0};', letterSpacing: 'letter-spacing:{0};', @@ -357,6 +358,7 @@ const _CSS_MAP: { [prop: string]: string; } = { contentText: 'content:\'{0}\';', contentIconPath: 'content:{0};', margin: 'margin:{0};', + padding: 'padding:{0};', width: 'width:{0};', height: 'height:{0};' }; @@ -529,7 +531,7 @@ class DecorationCSSRules { cssTextArr.push(strings.format(_CSS_MAP.contentText, escaped)); } - this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'textDecoration', 'color', 'opacity', 'backgroundColor', 'margin'], cssTextArr); + this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'fontSize', 'textDecoration', 'color', 'opacity', 'backgroundColor', 'margin', 'padding'], cssTextArr); if (this.collectCSSText(opts, ['width', 'height'], cssTextArr)) { cssTextArr.push('display:inline-block;'); } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 94c4a3c48df79..c711e1b94cc63 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -605,6 +605,7 @@ export interface IThemeDecorationRenderOptions { fontStyle?: string; fontWeight?: string; + fontSize?: string; textDecoration?: string; cursor?: string; color?: string | ThemeColor; @@ -631,11 +632,13 @@ export interface IContentDecorationRenderOptions { borderColor?: string | ThemeColor; fontStyle?: string; fontWeight?: string; + fontSize?: string; textDecoration?: string; color?: string | ThemeColor; backgroundColor?: string | ThemeColor; margin?: string; + padding?: string; width?: string; height?: string; } diff --git a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts index 85c540dbba3d6..f776e294e0929 100644 --- a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts +++ b/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts @@ -16,7 +16,9 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { SignatureArgumentsLabelProvider, SignatureArgumentsLabelProviderRegistry, SignautreArgumentsLabel } from 'vs/editor/common/modes'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { flatten } from 'vs/base/common/arrays'; +import { inlineHintForeground, inlineHintBackground } from 'vs/platform/theme/common/colorRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; const MAX_DECORATORS = 500; @@ -40,7 +42,7 @@ export function getSignatures(model: ITextModel, token: CancellationToken): Prom export class SignatureArgumentsLabelDetector extends Disposable implements IEditorContribution { - public static readonly ID: string = 'editor.contrib.signatureArgumentsLabel'; + static readonly ID: string = 'editor.contrib.signatureArgumentsLabel'; static readonly RECOMPUTE_TIME = 1000; // ms @@ -57,23 +59,24 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit private _isEnabled: boolean; constructor(private readonly _editor: ICodeEditor, - @ICodeEditorService private readonly _codeEditorService: ICodeEditorService + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, + @IThemeService private readonly _themeService: IThemeService, ) { super(); this._register(_editor.onDidChangeModel(() => { this._isEnabled = this.isEnabled(); - this.onModelChanged(); + this._onModelChanged(); })); - this._register(_editor.onDidChangeModelLanguage(() => this.onModelChanged())); - this._register(SignatureArgumentsLabelProviderRegistry.onDidChange(() => this.onModelChanged())); + this._register(_editor.onDidChangeModelLanguage(() => this._onModelChanged())); + this._register(SignatureArgumentsLabelProviderRegistry.onDidChange(() => this._onModelChanged())); this._register(_editor.onDidChangeConfiguration(() => { let prevIsEnabled = this._isEnabled; this._isEnabled = this.isEnabled(); if (prevIsEnabled !== this._isEnabled) { if (this._isEnabled) { - this.onModelChanged(); + this._onModelChanged(); } else { - this.removeAllDecorations(); + this._removeAllDecorations(); } } })); @@ -81,7 +84,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._timeoutTimer = null; this._computePromise = null; this._isEnabled = this.isEnabled(); - this.onModelChanged(); + this._onModelChanged(); } isEnabled(): boolean { @@ -98,13 +101,13 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit } dispose(): void { - this.stop(); - this.removeAllDecorations(); + this._stop(); + this._removeAllDecorations(); super.dispose(); } - private onModelChanged(): void { - this.stop(); + private _onModelChanged(): void { + this._stop(); if (!this._isEnabled) { return; @@ -121,14 +124,14 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._timeoutTimer.cancelAndSet(() => { this._timeoutTimer = null; - this.beginCompute(); + this._beginCompute(); }, SignatureArgumentsLabelDetector.RECOMPUTE_TIME); } })); - this.beginCompute(); + this._beginCompute(); } - private beginCompute(): void { + private _beginCompute(): void { this._computePromise = createCancelablePromise(token => { const model = this._editor.getModel(); if (!model) { @@ -137,13 +140,13 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit return getSignatures(model, token); }); this._computePromise.then((labelData) => { - this.updateDecorations(labelData); - this.updateLabelDecorators(labelData); + this._updateDecorations(labelData); + this._updateLabelDecorators(labelData); this._computePromise = null; }, onUnexpectedError); } - private stop(): void { + private _stop(): void { if (this._timeoutTimer) { this._timeoutTimer.cancel(); this._timeoutTimer = null; @@ -155,7 +158,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._localToDispose.clear(); } - private updateDecorations(labelData: SignatureArgumentsLabelData[]): void { + private _updateDecorations(labelData: SignatureArgumentsLabelData[]): void { const decorations = flatten(labelData.map(labels => labels.list.map(label => { return { range: { @@ -174,9 +177,12 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._decorationsIds.forEach((id, i) => this._labelDatas.set(id, labelData[i])); } - private updateLabelDecorators(labelData: SignatureArgumentsLabelData[]): void { + private _updateLabelDecorators(labelData: SignatureArgumentsLabelData[]): void { let decorations: IModelDeltaDecoration[] = []; let newDecorationsTypes: { [key: string]: boolean } = {}; + const { fontSize } = this._getLayoutInfo(); + const backgroundColor = this._themeService.getColorTheme().getColor(inlineHintBackground); + const fontColor = this._themeService.getColorTheme().getColor(inlineHintForeground); for (let i = 0; i < labelData.length; i++) { const label = labelData[i].list; @@ -189,11 +195,12 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit if (!this._decorationsTypes.has(key) && !newDecorationsTypes[key]) { this._codeEditorService.registerDecorationType(key, { before: { - contentText: `${name}:`, - border: 'solid 1px gray', - backgroundColor: '#333', - margin: '1px', - color: 'white' + contentText: name, + backgroundColor: `${backgroundColor}`, + color: `${fontColor}`, + margin: '0px 5px 0px 0px', + fontSize: `${fontSize}px`, + padding: '0px 2px' } }, undefined, this._editor); } @@ -220,7 +227,12 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._labelDecoratorIds = this._editor.deltaDecorations(this._labelDecoratorIds, decorations); } - private removeAllDecorations(): void { + private _getLayoutInfo() { + const fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; + return { fontSize }; + } + + private _removeAllDecorations(): void { this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []); this._labelDecoratorIds = this._editor.deltaDecorations(this._labelDecoratorIds, []); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index b5239353a96b3..219bec51d2b3c 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -260,6 +260,8 @@ export const editorHintBorder = registerColor('editorHint.border', { dark: null, export const sashHoverBorder = registerColor('sash.hoverBorder', { dark: null, light: null, hc: null }, nls.localize('sashActiveBorder', "Border color of active sashes.")); +export const inlineHintForeground = registerColor('inlineHint.foreground', { dark: '#A7A6A5', light: '#A7A6A5', hc: null }, nls.localize('inlineHintForeground', 'Foreground color of inline hints')); +export const inlineHintBackground = registerColor('inlineHint.background', { dark: '#3A3A3A', light: '#3A3A3A', hc: null }, nls.localize('inlineHintBackground', 'Background color of inline hints')); /** * Editor background color. * Because of bug https://monacotools.visualstudio.com/DefaultCollection/Monaco/_workitems/edit/13254 diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index ce2fd979d280b..151114cba1959 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -983,6 +983,10 @@ declare module 'vscode' { * CSS styling property that will be applied to the decoration attachment. */ fontWeight?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontSize?: string; /** * CSS styling property that will be applied to the decoration attachment. */ @@ -999,6 +1003,10 @@ declare module 'vscode' { * CSS styling property that will be applied to the decoration attachment. */ margin?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + padding?: string; /** * CSS styling property that will be applied to the decoration attachment. */ diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 2dd57a79011e8..a72e598f476ac 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -394,10 +394,12 @@ export namespace ThemableDecorationAttachmentRenderOptions { borderColor: options.borderColor, fontStyle: options.fontStyle, fontWeight: options.fontWeight, + fontSize: options.fontSize, textDecoration: options.textDecoration, color: options.color, backgroundColor: options.backgroundColor, margin: options.margin, + padding: options.padding, width: options.width, height: options.height, }; From 2db89c75e6f3956ba7270ec2d76a60e5ac775214 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 01:15:16 +0800 Subject: [PATCH 12/36] rename to inline hints --- ...natureArgumentsLabel.ts => inlineHints.ts} | 12 ++--- .../src/languageProvider.ts | 2 +- .../src/typescriptService.ts | 16 +++--- .../editor/browser/widget/codeEditorWidget.ts | 8 +-- src/vs/editor/common/config/editorOptions.ts | 12 ++--- src/vs/editor/common/editorContextKeys.ts | 2 +- src/vs/editor/common/modes.ts | 10 ++-- .../common/standalone/standaloneEnums.ts | 2 +- .../inlineHintsController.ts} | 52 +++++++++---------- src/vs/editor/editor.all.ts | 2 +- src/vs/monaco.d.ts | 16 +++--- src/vs/vscode.d.ts | 28 +++++----- .../api/browser/mainThreadLanguageFeatures.ts | 12 ++--- .../workbench/api/common/extHost.api.impl.ts | 6 +-- .../workbench/api/common/extHost.protocol.ts | 12 ++--- .../api/common/extHostLanguageFeatures.ts | 26 +++++----- .../api/common/extHostTypeConverters.ts | 6 +-- src/vs/workbench/api/common/extHostTypes.ts | 8 +-- 18 files changed, 116 insertions(+), 116 deletions(-) rename extensions/typescript-language-features/src/languageFeatures/{signatureArgumentsLabel.ts => inlineHints.ts} (68%) rename src/vs/editor/contrib/{signatureArgumentsLabel/signatureArgumentsLabel.ts => inlineHints/inlineHintsController.ts} (78%) diff --git a/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts similarity index 68% rename from extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts rename to extensions/typescript-language-features/src/languageFeatures/inlineHints.ts index 6673feba039f8..4f10c8c697521 100644 --- a/extensions/typescript-language-features/src/languageFeatures/signatureArgumentsLabel.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts @@ -9,25 +9,25 @@ import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; import { Position } from '../utils/typeConverters'; -class TypeScriptSginatureArgumentsLabelProvider implements vscode.SignatureArgumentsLabelProvider { +class TypeScriptInlineHintsProvider implements vscode.InlineHintsProvider { constructor( private readonly client: ITypeScriptServiceClient ) { } - async provideSignatureArgumentsLabels(model: vscode.TextDocument, token: vscode.CancellationToken): Promise { + async provideInlineHints(model: vscode.TextDocument, token: vscode.CancellationToken): Promise { const filepath = this.client.toOpenedFilePath(model); if (!filepath) { return []; } try { - const response = await this.client.execute('provideSignatureArgumentsLabel', { file: filepath }, token); + const response = await this.client.execute('provideInlineHints', { file: filepath }, token); if (response.type !== 'response' || !response.success || !response.body) { return []; } const labels = response.body.map(label => { - return new vscode.SignautreArgumentsLabel(label.name, Position.fromLocation(label.position)); + return new vscode.InlineHint(label.text, Position.fromLocation(label.position)); }); return labels; } catch (e) { @@ -43,7 +43,7 @@ export function register( return conditionalRegistration([ requireSomeCapability(client, ClientCapability.Semantic), ], () => { - return vscode.languages.registerSignatureArgumentsLabelProvider(selector.semantic, - new TypeScriptSginatureArgumentsLabelProvider(client)); + return vscode.languages.registerInlineHintsProvider(selector.semantic, + new TypeScriptInlineHintsProvider(client)); }); } diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 0cf4cc12298c5..7e39520184466 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -82,7 +82,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), - import('./languageFeatures/signatureArgumentsLabel').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/inlineHints').then(provider => this._register(provider.register(selector, this.client))), ]); } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index abefd8af71a56..64358348349f8 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -15,20 +15,20 @@ import { TelemetryReporter } from './utils/telemetry'; export namespace Experimental { export const enum CommandTypes { - ProvideSignatureArgumentsLabel = 'provideSignatureArgumentsLabel' + ProvideInlineHints = 'ProvideInlineHints' } - export interface ProvideSignatureArgumentsLabelRequest extends Proto.FileRequest { - command: CommandTypes.ProvideSignatureArgumentsLabel; + export interface ProvideInlineHintsRequest extends Proto.FileRequest { + command: CommandTypes.ProvideInlineHints; } - interface LabelItem { - name: string + interface HintItem { + text: string position: Proto.Location } - export interface ProvideSignatureArgumentsLabelResponse extends Proto.Response { - body?: LabelItem[]; + export interface ProvideInlineHintsResponse extends Proto.Response { + body?: HintItem[]; } } @@ -87,7 +87,7 @@ interface StandardTsServerRequests { 'prepareCallHierarchy': [Proto.FileLocationRequestArgs, Proto.PrepareCallHierarchyResponse]; 'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse]; 'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse]; - 'provideSignatureArgumentsLabel': [Proto.FileRequestArgs, Experimental.ProvideSignatureArgumentsLabelResponse]; + 'provideInlineHints': [Proto.FileRequestArgs, Experimental.ProvideInlineHintsResponse]; } interface NoResponseTsServerRequests { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index f18591d2a5920..5523bd74b94a9 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1834,7 +1834,7 @@ export class EditorModeContext extends Disposable { private readonly _hasMultipleDocumentFormattingProvider: IContextKey; private readonly _hasMultipleDocumentSelectionFormattingProvider: IContextKey; private readonly _hasSignatureHelpProvider: IContextKey; - private readonly _hasSignatureArgumentsLabelProvider: IContextKey; + private readonly _hasInlineHintsProvider: IContextKey; private readonly _isInWalkThrough: IContextKey; constructor( @@ -1857,7 +1857,7 @@ export class EditorModeContext extends Disposable { this._hasReferenceProvider = EditorContextKeys.hasReferenceProvider.bindTo(_contextKeyService); this._hasRenameProvider = EditorContextKeys.hasRenameProvider.bindTo(_contextKeyService); this._hasSignatureHelpProvider = EditorContextKeys.hasSignatureHelpProvider.bindTo(_contextKeyService); - this._hasSignatureArgumentsLabelProvider = EditorContextKeys.hasSignatureArgumentsLabelProvider.bindTo(_contextKeyService); + this._hasInlineHintsProvider = EditorContextKeys.hasInlineHintsProvider.bindTo(_contextKeyService); this._hasDocumentFormattingProvider = EditorContextKeys.hasDocumentFormattingProvider.bindTo(_contextKeyService); this._hasDocumentSelectionFormattingProvider = EditorContextKeys.hasDocumentSelectionFormattingProvider.bindTo(_contextKeyService); this._hasMultipleDocumentFormattingProvider = EditorContextKeys.hasMultipleDocumentFormattingProvider.bindTo(_contextKeyService); @@ -1886,7 +1886,7 @@ export class EditorModeContext extends Disposable { this._register(modes.DocumentFormattingEditProviderRegistry.onDidChange(update)); this._register(modes.DocumentRangeFormattingEditProviderRegistry.onDidChange(update)); this._register(modes.SignatureHelpProviderRegistry.onDidChange(update)); - this._register(modes.SignatureArgumentsLabelProviderRegistry.onDidChange(update)); + this._register(modes.InlineHintsProviderRegistry.onDidChange(update)); update(); } @@ -1938,7 +1938,7 @@ export class EditorModeContext extends Disposable { this._hasReferenceProvider.set(modes.ReferenceProviderRegistry.has(model)); this._hasRenameProvider.set(modes.RenameProviderRegistry.has(model)); this._hasSignatureHelpProvider.set(modes.SignatureHelpProviderRegistry.has(model)); - this._hasSignatureArgumentsLabelProvider.set(modes.SignatureArgumentsLabelProviderRegistry.has(model)); + this._hasInlineHintsProvider.set(modes.InlineHintsProviderRegistry.has(model)); this._hasDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.has(model) || modes.DocumentRangeFormattingEditProviderRegistry.has(model)); this._hasDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.has(model)); this._hasMultipleDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.all(model).length + modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index ec7a5adb96a0c..8edd81d125e28 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -626,9 +626,9 @@ export interface IEditorOptions { */ showDeprecated?: boolean; /** - * Controls show signature arguments label. + * Controls show inline hints. */ - showSignatureArgumentsLabel?: boolean + showInlineHints?: boolean } /** @@ -3763,7 +3763,7 @@ export const enum EditorOption { wrappingIndent, wrappingStrategy, showDeprecated, - showSignatureArgumentsLabel, + showInlineHints, // Leave these at the end (because they have dependencies!) editorClassName, pixelRatio, @@ -4267,9 +4267,9 @@ export const EditorOptions = { EditorOption.showDeprecated, 'showDeprecated', true, { description: nls.localize('showDeprecated', "Controls strikethrough deprecated variables.") } )), - showSignatureArgumentsLabel: register(new EditorBooleanOption( - EditorOption.showSignatureArgumentsLabel, 'showSignatureArgumentsLabel', true, - { description: nls.localize('showSignatureArgumentsLabel', "Controls show signature arguments label.") } + showInlineHints: register(new EditorBooleanOption( + EditorOption.showInlineHints, 'showInlineHints', true, + { description: nls.localize('showInlineHints', "Controls show inline hints.") } )), snippetSuggestions: register(new EditorStringEnumOption( EditorOption.snippetSuggestions, 'snippetSuggestions', diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index 23e1d43150bc9..2f24e31188725 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -61,7 +61,7 @@ export namespace EditorContextKeys { export const hasReferenceProvider = new RawContextKey('editorHasReferenceProvider', false); export const hasRenameProvider = new RawContextKey('editorHasRenameProvider', false); export const hasSignatureHelpProvider = new RawContextKey('editorHasSignatureHelpProvider', false); - export const hasSignatureArgumentsLabelProvider = new RawContextKey('editorHasSignatureArgumentsLabelProvider', false); + export const hasInlineHintsProvider = new RawContextKey('editorHasInlineHintsProvider', false); // -- mode context keys: formatting export const hasDocumentFormattingProvider = new RawContextKey('editorHasDocumentFormattingProvider', false); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 1d23234fec73d..a0132bd749841 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1659,13 +1659,13 @@ export interface CodeLensProvider { resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; } -export interface SignautreArgumentsLabel { - name: string +export interface InlineHint { + text: string position: IPosition } -export interface SignatureArgumentsLabelProvider { - provideSignatureArgumentsLabels(model: model.ITextModel, token: CancellationToken): ProviderResult; +export interface InlineHintsProvider { + provideInlineHints(model: model.ITextModel, token: CancellationToken): ProviderResult; } export interface SemanticTokensLegend { @@ -1775,7 +1775,7 @@ export const CodeLensProviderRegistry = new LanguageFeatureRegistry(); +export const InlineHintsProviderRegistry = new LanguageFeatureRegistry(); /** * @internal diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index ccb944f83e5d3..fc751367d9847 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -287,7 +287,7 @@ export enum EditorOption { wrappingIndent = 117, wrappingStrategy = 118, showDeprecated = 119, - showSignatureArgumentsLabel = 120, + showInlineHints = 120, editorClassName = 121, pixelRatio = 122, tabFocusMode = 123, diff --git a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts similarity index 78% rename from src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts rename to src/vs/editor/contrib/inlineHints/inlineHintsController.ts index f776e294e0929..2e89fec5a000b 100644 --- a/src/vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -13,7 +13,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { SignatureArgumentsLabelProvider, SignatureArgumentsLabelProviderRegistry, SignautreArgumentsLabel } from 'vs/editor/common/modes'; +import { InlineHintsProvider, InlineHintsProviderRegistry, InlineHint } from 'vs/editor/common/modes'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { flatten } from 'vs/base/common/arrays'; import { inlineHintForeground, inlineHintBackground } from 'vs/platform/theme/common/colorRegistry'; @@ -22,15 +22,15 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; const MAX_DECORATORS = 500; -export interface SignatureArgumentsLabelData { - list: SignautreArgumentsLabel[]; - provider: SignatureArgumentsLabelProvider; +export interface InlineHintsData { + list: InlineHint[]; + provider: InlineHintsProvider; } -export function getSignatures(model: ITextModel, token: CancellationToken): Promise { - const datas: SignatureArgumentsLabelData[] = []; - const providers = SignatureArgumentsLabelProviderRegistry.ordered(model).reverse(); - const promises = providers.map(provider => Promise.resolve(provider.provideSignatureArgumentsLabels(model, token)).then(result => { +export function getSignatures(model: ITextModel, token: CancellationToken): Promise { + const datas: InlineHintsData[] = []; + const providers = InlineHintsProviderRegistry.ordered(model).reverse(); + const promises = providers.map(provider => Promise.resolve(provider.provideInlineHints(model, token)).then(result => { if (result) { datas.push({ list: result, provider }); @@ -40,18 +40,18 @@ export function getSignatures(model: ITextModel, token: CancellationToken): Prom return Promise.all(promises).then(() => datas); } -export class SignatureArgumentsLabelDetector extends Disposable implements IEditorContribution { +export class InlineHintsDetector extends Disposable implements IEditorContribution { - static readonly ID: string = 'editor.contrib.signatureArgumentsLabel'; + static readonly ID: string = 'editor.contrib.InlineHints'; static readonly RECOMPUTE_TIME = 1000; // ms private readonly _localToDispose = this._register(new DisposableStore()); - private _computePromise: CancelablePromise | null; + private _computePromise: CancelablePromise | null; private _timeoutTimer: TimeoutTimer | null; private _decorationsIds: string[] = []; - private _labelDatas = new Map(); + private _labelDatas = new Map(); private _labelDecoratorIds: string[] = []; private readonly _decorationsTypes = new Set(); @@ -68,7 +68,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._onModelChanged(); })); this._register(_editor.onDidChangeModelLanguage(() => this._onModelChanged())); - this._register(SignatureArgumentsLabelProviderRegistry.onDidChange(() => this._onModelChanged())); + this._register(InlineHintsProviderRegistry.onDidChange(() => this._onModelChanged())); this._register(_editor.onDidChangeConfiguration(() => { let prevIsEnabled = this._isEnabled; this._isEnabled = this.isEnabled(); @@ -93,11 +93,11 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit return false; } - return this._editor.getOption(EditorOption.showSignatureArgumentsLabel); + return this._editor.getOption(EditorOption.showInlineHints); } - static get(editor: ICodeEditor): SignatureArgumentsLabelDetector { - return editor.getContribution(this.ID); + static get(editor: ICodeEditor): InlineHintsDetector { + return editor.getContribution(this.ID); } dispose(): void { @@ -114,7 +114,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit } const model = this._editor.getModel(); - if (!model || !SignatureArgumentsLabelProviderRegistry.has(model)) { + if (!model || !InlineHintsProviderRegistry.has(model)) { return; } @@ -125,7 +125,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._timeoutTimer = null; this._beginCompute(); - }, SignatureArgumentsLabelDetector.RECOMPUTE_TIME); + }, InlineHintsDetector.RECOMPUTE_TIME); } })); this._beginCompute(); @@ -158,7 +158,7 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._localToDispose.clear(); } - private _updateDecorations(labelData: SignatureArgumentsLabelData[]): void { + private _updateDecorations(labelData: InlineHintsData[]): void { const decorations = flatten(labelData.map(labels => labels.list.map(label => { return { range: { @@ -173,11 +173,11 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations); - this._labelDatas = new Map(); + this._labelDatas = new Map(); this._decorationsIds.forEach((id, i) => this._labelDatas.set(id, labelData[i])); } - private _updateLabelDecorators(labelData: SignatureArgumentsLabelData[]): void { + private _updateLabelDecorators(labelData: InlineHintsData[]): void { let decorations: IModelDeltaDecoration[] = []; let newDecorationsTypes: { [key: string]: boolean } = {}; const { fontSize } = this._getLayoutInfo(); @@ -187,15 +187,15 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit for (let i = 0; i < labelData.length; i++) { const label = labelData[i].list; for (let j = 0; j < label.length && decorations.length < MAX_DECORATORS; j++) { - const { name, position } = label[j]; + const { text, position } = label[j]; - const subKey = hash(name).toString(16); - let key = 'signatureArgumentsLabel-' + subKey; + const subKey = hash(text).toString(16); + let key = 'inlineHints-' + subKey; if (!this._decorationsTypes.has(key) && !newDecorationsTypes[key]) { this._codeEditorService.registerDecorationType(key, { before: { - contentText: name, + contentText: text, backgroundColor: `${backgroundColor}`, color: `${fontColor}`, margin: '0px 5px 0px 0px', @@ -242,4 +242,4 @@ export class SignatureArgumentsLabelDetector extends Disposable implements IEdit } } -registerEditorContribution(SignatureArgumentsLabelDetector.ID, SignatureArgumentsLabelDetector); +registerEditorContribution(InlineHintsDetector.ID, InlineHintsDetector); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 19b3b7637f390..16b089649b915 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -46,7 +46,7 @@ import 'vs/editor/contrib/viewportSemanticTokens/viewportSemanticTokens'; import 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import 'vs/editor/contrib/wordOperations/wordOperations'; import 'vs/editor/contrib/wordPartOperations/wordPartOperations'; -import 'vs/editor/contrib/signatureArgumentsLabel/signatureArgumentsLabel'; +import 'vs/editor/contrib/inlineHints/inlineHintsController'; // Load up these strings even in VSCode, even if they are not used // in order to get them translated diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 966eb36b74d2f..d50be4ff7a9a1 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3177,9 +3177,9 @@ declare namespace monaco.editor { */ showDeprecated?: boolean; /** - * Controls show signature arguments label. + * Controls show inline hints. */ - showSignatureArgumentsLabel?: boolean; + showInlineHints?: boolean; } /** @@ -4032,7 +4032,7 @@ declare namespace monaco.editor { wrappingIndent = 117, wrappingStrategy = 118, showDeprecated = 119, - showSignatureArgumentsLabel = 120, + showInlineHints = 120, editorClassName = 121, pixelRatio = 122, tabFocusMode = 123, @@ -4138,7 +4138,7 @@ declare namespace monaco.editor { showFoldingControls: IEditorOption; showUnused: IEditorOption; showDeprecated: IEditorOption; - showSignatureArgumentsLabel: IEditorOption; + showInlineHints: IEditorOption; snippetSuggestions: IEditorOption; smartSelect: IEditorOption; smoothScrolling: IEditorOption; @@ -6367,13 +6367,13 @@ declare namespace monaco.languages { resolveCodeLens?(model: editor.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; } - export interface SignautreArgumentsLabel { - name: string; + export interface InlineHint { + text: string; position: IPosition; } - export interface SignatureArgumentsLabelProvider { - provideSignatureArgumentsLabels(model: editor.ITextModel, token: CancellationToken): ProviderResult; + export interface InlineHintsProvider { + provideInlineHints(model: editor.ITextModel, token: CancellationToken): ProviderResult; } export interface SemanticTokensLegend { diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 151114cba1959..12514b7d8a5d8 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3764,39 +3764,39 @@ declare module 'vscode' { } /** - * Argument label information of signature. + * Inline hint information. */ - export class SignautreArgumentsLabel { + export class InlineHint { /** - * The name of the parameter. + * The text of the hint. */ - name: string; + text: string; /** - * The position of the argument. + * The position of the hint. */ position: Position; /** - * Creates a new signature arguments label information object. + * Creates a new inline hint information object. * - * @param name A name of the parameter. + * @param text The text of the parameter. * @param position The position of the argument. */ - constructor(name: string, position: Position); + constructor(text: string, position: Position); } /** * The document formatting provider interface defines the contract between extensions and - * the arguments label feature. + * the inline hints feature. */ - export interface SignatureArgumentsLabelProvider { + export interface InlineHintsProvider { /** * @param model The document in which the command was invoked. * @param token A cancellation token. * * @return A list of arguments labels or a thenable that resolves to such. */ - provideSignatureArgumentsLabels(model: TextDocument, token: CancellationToken): ProviderResult; + provideInlineHints(model: TextDocument, token: CancellationToken): ProviderResult; } /** @@ -10880,17 +10880,17 @@ declare module 'vscode' { export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; /** - * Register a signature arguments label provider. + * Register a inline hints provider. * * Multiple providers can be registered for a language. In that case providers are sorted * by their [score](#languages.match) and the best-matching provider is used. Failure * of the selected provider will cause a failure of the whole operation. * * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An on type signature arguments label provider. + * @param provider An on type inline hints provider. * @return A [disposable](#Disposable) that unregisters this provider when being disposed. */ - export function registerSignatureArgumentsLabelProvider(selector: DocumentSelector, provider: SignatureArgumentsLabelProvider): Disposable; + export function registerInlineHintsProvider(selector: DocumentSelector, provider: InlineHintsProvider): Disposable; /** * Register a document link provider. diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index f62beca38c113..48fbc4032d341 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -497,13 +497,13 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha })); } - // --- signature arguments labels + // --- inline hints - $registerSignatureArgumentsLabelProvider(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, modes.SignatureArgumentsLabelProviderRegistry.register(selector, { - provideSignatureArgumentsLabels: async (model: ITextModel, token: CancellationToken): Promise => { - const result = await this._proxy.$provideSignatureArgumentsLabel(handle, model.uri, token); - return result?.labels; + $registerInlineHintsProvider(handle: number, selector: IDocumentFilterDto[]): void { + this._registrations.set(handle, modes.InlineHintsProviderRegistry.register(selector, { + provideInlineHints: async (model: ITextModel, token: CancellationToken): Promise => { + const result = await this._proxy.$provideInlineHints(handle, model.uri, token); + return result?.hints; } })); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 74848b69f72c6..0785de594abcf 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -483,8 +483,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostLanguages.tokenAtPosition(doc, pos); }, - registerSignatureArgumentsLabelProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureArgumentsLabelProvider): vscode.Disposable { - return extHostLanguageFeatures.registerSignatureArgumentsLabelProvider(extension, selector, provider); + registerInlineHintsProvider(selector: vscode.DocumentSelector, provider: vscode.InlineHintsProvider): vscode.Disposable { + return extHostLanguageFeatures.registerInlineHintsProvider(extension, selector, provider); } }; @@ -1166,6 +1166,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I FunctionBreakpoint: extHostTypes.FunctionBreakpoint, Hover: extHostTypes.Hover, IndentAction: languageConfiguration.IndentAction, + InlineHint: extHostTypes.InlineHint, Location: extHostTypes.Location, MarkdownString: extHostTypes.MarkdownString, OverviewRulerLane: OverviewRulerLane, @@ -1188,7 +1189,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I SignatureHelp: extHostTypes.SignatureHelp, SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind, SignatureInformation: extHostTypes.SignatureInformation, - SignautreArgumentsLabel: extHostTypes.SignautreArgumentsLabel, SnippetString: extHostTypes.SnippetString, SourceBreakpoint: extHostTypes.SourceBreakpoint, StandardTokenType: extHostTypes.StandardTokenType, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e0f162102db7f..6caa96cc3931a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -399,7 +399,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void; $registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; - $registerSignatureArgumentsLabelProvider(handle: number, selector: IDocumentFilterDto[]): void; + $registerInlineHintsProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void; $registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; @@ -1270,13 +1270,13 @@ export interface ISignatureHelpContextDto { readonly activeSignatureHelp?: ISignatureHelpDto; } -export interface ISignautreArgumentsLabelDto { - name: string +export interface IInlineHintDto { + text: string position: IPosition } -export interface ISignatureArgumentsLabelDto { - labels: ISignautreArgumentsLabelDto[] +export interface IInlineHintsDto { + hints: IInlineHintDto[] } export interface ILocationDto { @@ -1468,7 +1468,7 @@ export interface ExtHostLanguageFeaturesShape { $releaseCompletionItems(handle: number, id: number): void; $provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise; $releaseSignatureHelp(handle: number, id: number): void; - $provideSignatureArgumentsLabel(handle: number, resource: UriComponents, token: CancellationToken): Promise + $provideInlineHints(handle: number, resource: UriComponents, token: CancellationToken): Promise $provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise; $resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise; $releaseDocumentLinks(handle: number, id: number): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 06b7f1a5cfe58..f46f214ebc532 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1062,20 +1062,20 @@ class SignatureHelpAdapter { } } -class SignatureArgumentsLabelAdapter { - private readonly _cache = new Cache('SignatureArgumentsLabel'); +class InlineHintsAdapter { + private readonly _cache = new Cache('InlineHints'); constructor( private readonly _documents: ExtHostDocuments, - private readonly _provider: vscode.SignatureArgumentsLabelProvider, + private readonly _provider: vscode.InlineHintsProvider, ) { } - provideSignatureArgumentsLabels(resource: URI, token: CancellationToken): Promise { + provideInlineHints(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - return asPromise(() => this._provider.provideSignatureArgumentsLabels(doc, token)).then(value => { + return asPromise(() => this._provider.provideInlineHints(doc, token)).then(value => { if (value) { const id = this._cache.add([value]); - return { labels: value.map(typeConvert.SignatureArgumentsLabelList.from), id }; + return { hints: value.map(typeConvert.InlineHint.from), id }; } return undefined; }); @@ -1340,7 +1340,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter - | LinkedEditingRangeAdapter | SignatureArgumentsLabelAdapter; + | LinkedEditingRangeAdapter | InlineHintsAdapter; class AdapterData { constructor( @@ -1782,9 +1782,9 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._createDisposable(handle); } - registerSignatureArgumentsLabelProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SignatureArgumentsLabelProvider): vscode.Disposable { - const handle = this._addNewAdapter(new SignatureArgumentsLabelAdapter(this._documents, provider), extension); - this._proxy.$registerSignatureArgumentsLabelProvider(handle, this._transformDocumentSelector(selector)); + registerInlineHintsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineHintsProvider): vscode.Disposable { + const handle = this._addNewAdapter(new InlineHintsAdapter(this._documents, provider), extension); + this._proxy.$registerInlineHintsProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1796,10 +1796,10 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.releaseSignatureHelp(id), undefined); } - // --- signature arguments labels + // --- inline hints - $provideSignatureArgumentsLabel(handle: number, resource: UriComponents, token: CancellationToken): Promise { - return this._withAdapter(handle, SignatureArgumentsLabelAdapter, adapter => adapter.provideSignatureArgumentsLabels(URI.revive(resource), token), undefined); + $provideInlineHints(handle: number, resource: UriComponents, token: CancellationToken): Promise { + return this._withAdapter(handle, InlineHintsAdapter, adapter => adapter.provideInlineHints(URI.revive(resource), token), undefined); } // --- links diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index a72e598f476ac..0eaf2dd535f92 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1017,10 +1017,10 @@ export namespace SignatureHelp { } } -export namespace SignatureArgumentsLabelList { - export function from(label: vscode.SignautreArgumentsLabel): modes.SignautreArgumentsLabel { +export namespace InlineHint { + export function from(label: vscode.InlineHint): modes.InlineHint { return { - name: label.name, + text: label.text, position: Position.from(label.position) }; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index a8a096970ce1c..03290e70dfd2c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1374,12 +1374,12 @@ export enum SignatureHelpTriggerKind { } @es5ClassCompat -export class SignautreArgumentsLabel { - name: string; +export class InlineHint { + text: string; position: Position; - constructor(name: string, position: Position) { - this.name = name; + constructor(text: string, position: Position) { + this.text = text; this.position = position; } } From 2697a42ee718faf6aa8dd7be86e4b3bb1791a589 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 01:37:42 +0800 Subject: [PATCH 13/36] add range WIP --- .../src/languageFeatures/inlineHints.ts | 7 ++- .../src/typescriptService.ts | 16 +++++- src/vs/editor/common/modes.ts | 2 +- .../inlineHints/inlineHintsController.ts | 55 ++++++++++--------- src/vs/monaco.d.ts | 2 +- src/vs/vscode.d.ts | 2 +- .../api/browser/mainThreadLanguageFeatures.ts | 4 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 8 +-- 9 files changed, 59 insertions(+), 39 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts index 4f10c8c697521..66668b254c020 100644 --- a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts @@ -14,14 +14,17 @@ class TypeScriptInlineHintsProvider implements vscode.InlineHintsProvider { private readonly client: ITypeScriptServiceClient ) { } - async provideInlineHints(model: vscode.TextDocument, token: vscode.CancellationToken): Promise { + async provideInlineHints(model: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise { const filepath = this.client.toOpenedFilePath(model); if (!filepath) { return []; } + const start = model.offsetAt(range.start); + const length = model.offsetAt(range.end) - start; + try { - const response = await this.client.execute('provideInlineHints', { file: filepath }, token); + const response = await this.client.execute('provideInlineHints', { file: filepath, start, length }, token); if (response.type !== 'response' || !response.success || !response.body) { return []; } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 64358348349f8..07d0f0dfd1190 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -18,8 +18,20 @@ export namespace Experimental { ProvideInlineHints = 'ProvideInlineHints' } - export interface ProvideInlineHintsRequest extends Proto.FileRequest { + export interface ProvideInlineHintsArgs extends Proto.FileRequestArgs { + /** + * Start position of the span. + */ + start: number; + /** + * Length of the span. + */ + length: number; + } + + export interface ProvideInlineHintsRequest extends Proto.Request { command: CommandTypes.ProvideInlineHints; + arguments: ProvideInlineHintsArgs; } interface HintItem { @@ -87,7 +99,7 @@ interface StandardTsServerRequests { 'prepareCallHierarchy': [Proto.FileLocationRequestArgs, Proto.PrepareCallHierarchyResponse]; 'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse]; 'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse]; - 'provideInlineHints': [Proto.FileRequestArgs, Experimental.ProvideInlineHintsResponse]; + 'provideInlineHints': [Experimental.ProvideInlineHintsArgs, Experimental.ProvideInlineHintsResponse]; } interface NoResponseTsServerRequests { diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index a0132bd749841..f4b5f14b9dae1 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1665,7 +1665,7 @@ export interface InlineHint { } export interface InlineHintsProvider { - provideInlineHints(model: model.ITextModel, token: CancellationToken): ProviderResult; + provideInlineHints(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult; } export interface SemanticTokensLegend { diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 2e89fec5a000b..582f129af7812 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -19,6 +19,7 @@ import { flatten } from 'vs/base/common/arrays'; import { inlineHintForeground, inlineHintBackground } from 'vs/platform/theme/common/colorRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Range } from 'vs/editor/common/core/range'; const MAX_DECORATORS = 500; @@ -27,15 +28,14 @@ export interface InlineHintsData { provider: InlineHintsProvider; } -export function getSignatures(model: ITextModel, token: CancellationToken): Promise { +export function getSignatures(model: ITextModel, ranges: Range[], token: CancellationToken): Promise { const datas: InlineHintsData[] = []; const providers = InlineHintsProviderRegistry.ordered(model).reverse(); - const promises = providers.map(provider => Promise.resolve(provider.provideInlineHints(model, token)).then(result => { + const promises = flatten(providers.map(provider => ranges.map(range => Promise.resolve(provider.provideInlineHints(model, range, token)).then(result => { if (result) { datas.push({ list: result, provider }); - } - })); + })))); return Promise.all(promises).then(() => datas); } @@ -51,9 +51,9 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi private _timeoutTimer: TimeoutTimer | null; private _decorationsIds: string[] = []; - private _labelDatas = new Map(); + private _hintsDatas = new Map(); - private _labelDecoratorIds: string[] = []; + private _hintsDecoratorIds: string[] = []; private readonly _decorationsTypes = new Set(); private _isEnabled: boolean; @@ -80,6 +80,9 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi } } })); + this._register(_editor.onDidScrollChange(() => { + this._onModelChanged(); + })) this._timeoutTimer = null; this._computePromise = null; @@ -137,11 +140,13 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi if (!model) { return Promise.resolve([]); } - return getSignatures(model, token); + + const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow(); + return getSignatures(model, visibleRanges, token); }); - this._computePromise.then((labelData) => { - this._updateDecorations(labelData); - this._updateLabelDecorators(labelData); + this._computePromise.then((hintsData) => { + this._updateDecorations(hintsData); + this._updateHintsDecorators(hintsData); this._computePromise = null; }, onUnexpectedError); } @@ -158,14 +163,14 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi this._localToDispose.clear(); } - private _updateDecorations(labelData: InlineHintsData[]): void { - const decorations = flatten(labelData.map(labels => labels.list.map(label => { + private _updateDecorations(hintsData: InlineHintsData[]): void { + const decorations = flatten(hintsData.map(hints => hints.list.map(hint => { return { range: { - startLineNumber: label.position.lineNumber, - startColumn: label.position.column, - endLineNumber: label.position.lineNumber, - endColumn: label.position.column + startLineNumber: hint.position.lineNumber, + startColumn: hint.position.column, + endLineNumber: hint.position.lineNumber, + endColumn: hint.position.column }, options: ModelDecorationOptions.EMPTY }; @@ -173,21 +178,21 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations); - this._labelDatas = new Map(); - this._decorationsIds.forEach((id, i) => this._labelDatas.set(id, labelData[i])); + this._hintsDatas = new Map(); + this._decorationsIds.forEach((id, i) => this._hintsDatas.set(id, hintsData[i])); } - private _updateLabelDecorators(labelData: InlineHintsData[]): void { + private _updateHintsDecorators(hintsData: InlineHintsData[]): void { let decorations: IModelDeltaDecoration[] = []; let newDecorationsTypes: { [key: string]: boolean } = {}; const { fontSize } = this._getLayoutInfo(); const backgroundColor = this._themeService.getColorTheme().getColor(inlineHintBackground); const fontColor = this._themeService.getColorTheme().getColor(inlineHintForeground); - for (let i = 0; i < labelData.length; i++) { - const label = labelData[i].list; - for (let j = 0; j < label.length && decorations.length < MAX_DECORATORS; j++) { - const { text, position } = label[j]; + for (let i = 0; i < hintsData.length; i++) { + const hint = hintsData[i].list; + for (let j = 0; j < hint.length && decorations.length < MAX_DECORATORS; j++) { + const { text, position } = hint[j]; const subKey = hash(text).toString(16); let key = 'inlineHints-' + subKey; @@ -224,7 +229,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi } }); - this._labelDecoratorIds = this._editor.deltaDecorations(this._labelDecoratorIds, decorations); + this._hintsDecoratorIds = this._editor.deltaDecorations(this._hintsDecoratorIds, decorations); } private _getLayoutInfo() { @@ -234,7 +239,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi private _removeAllDecorations(): void { this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []); - this._labelDecoratorIds = this._editor.deltaDecorations(this._labelDecoratorIds, []); + this._hintsDecoratorIds = this._editor.deltaDecorations(this._hintsDecoratorIds, []); this._decorationsTypes.forEach(subType => { this._codeEditorService.removeDecorationType(subType); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d50be4ff7a9a1..b303f446422b3 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6373,7 +6373,7 @@ declare namespace monaco.languages { } export interface InlineHintsProvider { - provideInlineHints(model: editor.ITextModel, token: CancellationToken): ProviderResult; + provideInlineHints(model: editor.ITextModel, range: Range, token: CancellationToken): ProviderResult; } export interface SemanticTokensLegend { diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 12514b7d8a5d8..51ad8dd93aa22 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3796,7 +3796,7 @@ declare module 'vscode' { * * @return A list of arguments labels or a thenable that resolves to such. */ - provideInlineHints(model: TextDocument, token: CancellationToken): ProviderResult; + provideInlineHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; } /** diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 48fbc4032d341..2ef2c83bcea76 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -501,8 +501,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerInlineHintsProvider(handle: number, selector: IDocumentFilterDto[]): void { this._registrations.set(handle, modes.InlineHintsProviderRegistry.register(selector, { - provideInlineHints: async (model: ITextModel, token: CancellationToken): Promise => { - const result = await this._proxy.$provideInlineHints(handle, model.uri, token); + provideInlineHints: async (model: ITextModel, range: EditorRange, token: CancellationToken): Promise => { + const result = await this._proxy.$provideInlineHints(handle, model.uri, range, token); return result?.hints; } })); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6caa96cc3931a..22a591a26e0aa 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1468,7 +1468,7 @@ export interface ExtHostLanguageFeaturesShape { $releaseCompletionItems(handle: number, id: number): void; $provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise; $releaseSignatureHelp(handle: number, id: number): void; - $provideInlineHints(handle: number, resource: UriComponents, token: CancellationToken): Promise + $provideInlineHints(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise $provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise; $resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise; $releaseDocumentLinks(handle: number, id: number): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index f46f214ebc532..7a38d2d70385a 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1070,9 +1070,9 @@ class InlineHintsAdapter { private readonly _provider: vscode.InlineHintsProvider, ) { } - provideInlineHints(resource: URI, token: CancellationToken): Promise { + provideInlineHints(resource: URI, range: Range, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - return asPromise(() => this._provider.provideInlineHints(doc, token)).then(value => { + return asPromise(() => this._provider.provideInlineHints(doc, range, token)).then(value => { if (value) { const id = this._cache.add([value]); return { hints: value.map(typeConvert.InlineHint.from), id }; @@ -1798,8 +1798,8 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- inline hints - $provideInlineHints(handle: number, resource: UriComponents, token: CancellationToken): Promise { - return this._withAdapter(handle, InlineHintsAdapter, adapter => adapter.provideInlineHints(URI.revive(resource), token), undefined); + $provideInlineHints(handle: number, resource: UriComponents, range: Range, token: CancellationToken): Promise { + return this._withAdapter(handle, InlineHintsAdapter, adapter => adapter.provideInlineHints(URI.revive(resource), range, token), undefined); } // --- links From d26dbae6cdd3643f48a8744ff6028c781270dad4 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 12:08:30 +0800 Subject: [PATCH 14/36] Adjust request schedule --- .../src/languageFeatures/inlineHints.ts | 3 +- .../inlineHints/inlineHintsController.ts | 77 ++++++++----------- .../api/common/extHostLanguageFeatures.ts | 6 +- src/vs/workbench/api/common/extHostTypes.ts | 4 +- 4 files changed, 38 insertions(+), 52 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts index 66668b254c020..89d82ef9b1b1c 100644 --- a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts @@ -29,10 +29,9 @@ class TypeScriptInlineHintsProvider implements vscode.InlineHintsProvider { return []; } - const labels = response.body.map(label => { + return response.body.map(label => { return new vscode.InlineHint(label.text, Position.fromLocation(label.position)); }); - return labels; } catch (e) { return []; } diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 582f129af7812..6857c37b41323 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancelablePromise, TimeoutTimer, createCancelablePromise } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { hash } from 'vs/base/common/hash'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -20,6 +20,7 @@ import { inlineHintForeground, inlineHintBackground } from 'vs/platform/theme/co import { CancellationToken } from 'vs/base/common/cancellation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Range } from 'vs/editor/common/core/range'; +import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; const MAX_DECORATORS = 500; @@ -28,7 +29,7 @@ export interface InlineHintsData { provider: InlineHintsProvider; } -export function getSignatures(model: ITextModel, ranges: Range[], token: CancellationToken): Promise { +export function getInlineHints(model: ITextModel, ranges: Range[], token: CancellationToken): Promise { const datas: InlineHintsData[] = []; const providers = InlineHintsProviderRegistry.ordered(model).reverse(); const promises = flatten(providers.map(provider => ranges.map(range => Promise.resolve(provider.provideInlineHints(model, range, token)).then(result => { @@ -43,20 +44,14 @@ export function getSignatures(model: ITextModel, ranges: Range[], token: Cancell export class InlineHintsDetector extends Disposable implements IEditorContribution { static readonly ID: string = 'editor.contrib.InlineHints'; - - static readonly RECOMPUTE_TIME = 1000; // ms - private readonly _localToDispose = this._register(new DisposableStore()); - private _computePromise: CancelablePromise | null; - private _timeoutTimer: TimeoutTimer | null; - private _decorationsIds: string[] = []; private _hintsDatas = new Map(); - private _hintsDecoratorIds: string[] = []; private readonly _decorationsTypes = new Set(); - private _isEnabled: boolean; + private _getInlineHintsPromise: CancelablePromise | undefined; + private readonly _getInlineHintsDelays = new LanguageFeatureRequestDelays(InlineHintsProviderRegistry, 250, 2500); constructor(private readonly _editor: ICodeEditor, @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @@ -82,10 +77,8 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi })); this._register(_editor.onDidScrollChange(() => { this._onModelChanged(); - })) + })); - this._timeoutTimer = null; - this._computePromise = null; this._isEnabled = this.isEnabled(); this._onModelChanged(); } @@ -121,44 +114,38 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi return; } - this._localToDispose.add(this._editor.onDidChangeModelContent(() => { - if (!this._timeoutTimer) { - this._timeoutTimer = new TimeoutTimer(); - this._timeoutTimer.cancelAndSet(() => { - this._timeoutTimer = null; + const scheduler = new RunOnceScheduler(() => { + const t1 = Date.now(); - this._beginCompute(); - }, InlineHintsDetector.RECOMPUTE_TIME); - } - })); - this._beginCompute(); - } + this._getInlineHintsPromise?.cancel(); + this._getInlineHintsPromise = createCancelablePromise(token => { + const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow(); + return getInlineHints(model, visibleRanges, token); + }); - private _beginCompute(): void { - this._computePromise = createCancelablePromise(token => { - const model = this._editor.getModel(); - if (!model) { - return Promise.resolve([]); - } + this._getInlineHintsPromise.then(result => { + // update moving average + const newDelay = this._getInlineHintsDelays.update(model, Date.now() - t1); + scheduler.delay = newDelay; - const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow(); - return getSignatures(model, visibleRanges, token); - }); - this._computePromise.then((hintsData) => { - this._updateDecorations(hintsData); - this._updateHintsDecorators(hintsData); - this._computePromise = null; - }, onUnexpectedError); + // render lenses + this._updateDecorations(result); + this._updateHintsDecorators(result); + }, onUnexpectedError); + + }, this._getInlineHintsDelays.get(model)); + + this._localToDispose.add(scheduler); + this._localToDispose.add(this._editor.onDidChangeModelContent(() => { + scheduler.schedule(); + })); + scheduler.schedule(); } private _stop(): void { - if (this._timeoutTimer) { - this._timeoutTimer.cancel(); - this._timeoutTimer = null; - } - if (this._computePromise) { - this._computePromise.cancel(); - this._computePromise = null; + if (this._getInlineHintsPromise) { + this._getInlineHintsPromise.cancel(); + this._getInlineHintsPromise = undefined; } this._localToDispose.clear(); } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 7a38d2d70385a..a1f4502b354d4 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1070,9 +1070,9 @@ class InlineHintsAdapter { private readonly _provider: vscode.InlineHintsProvider, ) { } - provideInlineHints(resource: URI, range: Range, token: CancellationToken): Promise { + provideInlineHints(resource: URI, range: IRange, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - return asPromise(() => this._provider.provideInlineHints(doc, range, token)).then(value => { + return asPromise(() => this._provider.provideInlineHints(doc, typeConvert.Range.to(range), token)).then(value => { if (value) { const id = this._cache.add([value]); return { hints: value.map(typeConvert.InlineHint.from), id }; @@ -1798,7 +1798,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- inline hints - $provideInlineHints(handle: number, resource: UriComponents, range: Range, token: CancellationToken): Promise { + $provideInlineHints(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise { return this._withAdapter(handle, InlineHintsAdapter, adapter => adapter.provideInlineHints(URI.revive(resource), range, token), undefined); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 03290e70dfd2c..6753c8e8b49b2 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1261,11 +1261,11 @@ export class CallHierarchyOutgoingCall { @es5ClassCompat export class CodeLens { - range: Range; + range: vscode.Range; command: vscode.Command | undefined; - constructor(range: Range, command?: vscode.Command) { + constructor(range: vscode.Range, command?: vscode.Command) { this.range = range; this.command = command; } From 7222b357f754987384f287bee8b5d47f494e4dfc Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 14:01:07 +0800 Subject: [PATCH 15/36] Fix cr issues --- .../src/languageFeatures/inlineHints.ts | 53 +++++++++++++++-- .../src/typescriptService.ts | 32 ---------- src/vs/vscode.d.ts | 57 ------------------ src/vs/vscode.proposed.d.ts | 59 +++++++++++++++++++ 4 files changed, 108 insertions(+), 93 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts index 89d82ef9b1b1c..6647b654f3ea8 100644 --- a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts @@ -4,11 +4,56 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as Proto from '../protocol'; import { DocumentSelector } from '../utils/documentSelector'; -import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient, ServerResponse, ExecConfig } from '../typescriptService'; import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; import { Position } from '../utils/typeConverters'; +export namespace ExperimentalProto { + export const enum CommandTypes { + ProvideInlineHints = 'ProvideInlineHints' + } + + export interface ProvideInlineHintsArgs extends Proto.FileRequestArgs { + /** + * Start position of the span. + */ + start: number; + /** + * Length of the span. + */ + length: number; + } + + export interface ProvideInlineHintsRequest extends Proto.Request { + command: CommandTypes.ProvideInlineHints; + arguments: ProvideInlineHintsArgs; + } + + interface HintItem { + text: string + position: Proto.Location + } + + export interface ProvideInlineHintsResponse extends Proto.Response { + body?: HintItem[]; + } + + export interface IExtendedTypeScriptServiceClient { + execute( + command: K, + args: ExtendedTsServerRequests[K][0], + token: vscode.CancellationToken, + config?: ExecConfig + ): Promise>; + } + + export interface ExtendedTsServerRequests { + 'provideInlineHints': [ProvideInlineHintsArgs, ProvideInlineHintsResponse]; + } +} + class TypeScriptInlineHintsProvider implements vscode.InlineHintsProvider { constructor( private readonly client: ITypeScriptServiceClient @@ -24,13 +69,13 @@ class TypeScriptInlineHintsProvider implements vscode.InlineHintsProvider { const length = model.offsetAt(range.end) - start; try { - const response = await this.client.execute('provideInlineHints', { file: filepath, start, length }, token); + const response = await (this.client as ExperimentalProto.IExtendedTypeScriptServiceClient).execute('provideInlineHints', { file: filepath, start, length }, token); if (response.type !== 'response' || !response.success || !response.body) { return []; } - return response.body.map(label => { - return new vscode.InlineHint(label.text, Position.fromLocation(label.position)); + return response.body.map(hint => { + return new vscode.InlineHint(hint.text, Position.fromLocation(hint.position)); }); } catch (e) { return []; diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 07d0f0dfd1190..588925ec6db70 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -13,37 +13,6 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import { PluginManager } from './utils/plugins'; import { TelemetryReporter } from './utils/telemetry'; -export namespace Experimental { - export const enum CommandTypes { - ProvideInlineHints = 'ProvideInlineHints' - } - - export interface ProvideInlineHintsArgs extends Proto.FileRequestArgs { - /** - * Start position of the span. - */ - start: number; - /** - * Length of the span. - */ - length: number; - } - - export interface ProvideInlineHintsRequest extends Proto.Request { - command: CommandTypes.ProvideInlineHints; - arguments: ProvideInlineHintsArgs; - } - - interface HintItem { - text: string - position: Proto.Location - } - - export interface ProvideInlineHintsResponse extends Proto.Response { - body?: HintItem[]; - } -} - export enum ServerType { Syntax = 'syntax', Semantic = 'semantic', @@ -99,7 +68,6 @@ interface StandardTsServerRequests { 'prepareCallHierarchy': [Proto.FileLocationRequestArgs, Proto.PrepareCallHierarchyResponse]; 'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse]; 'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse]; - 'provideInlineHints': [Experimental.ProvideInlineHintsArgs, Experimental.ProvideInlineHintsResponse]; } interface NoResponseTsServerRequests { diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 51ad8dd93aa22..0def9b1af794a 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -983,10 +983,6 @@ declare module 'vscode' { * CSS styling property that will be applied to the decoration attachment. */ fontWeight?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - fontSize?: string; /** * CSS styling property that will be applied to the decoration attachment. */ @@ -1003,10 +999,6 @@ declare module 'vscode' { * CSS styling property that will be applied to the decoration attachment. */ margin?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - padding?: string; /** * CSS styling property that will be applied to the decoration attachment. */ @@ -3763,42 +3755,6 @@ declare module 'vscode' { provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; } - /** - * Inline hint information. - */ - export class InlineHint { - /** - * The text of the hint. - */ - text: string; - /** - * The position of the hint. - */ - position: Position; - - /** - * Creates a new inline hint information object. - * - * @param text The text of the parameter. - * @param position The position of the argument. - */ - constructor(text: string, position: Position); - } - - /** - * The document formatting provider interface defines the contract between extensions and - * the inline hints feature. - */ - export interface InlineHintsProvider { - /** - * @param model The document in which the command was invoked. - * @param token A cancellation token. - * - * @return A list of arguments labels or a thenable that resolves to such. - */ - provideInlineHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; - } - /** * Metadata about a registered [`SignatureHelpProvider`](#SignatureHelpProvider). */ @@ -10879,19 +10835,6 @@ declare module 'vscode' { export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; - /** - * Register a inline hints provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and the best-matching provider is used. Failure - * of the selected provider will cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An on type inline hints provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerInlineHintsProvider(selector: DocumentSelector, provider: InlineHintsProvider): Disposable; - /** * Register a document link provider. * diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7d642e3580014..1acb48712c19d 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1969,6 +1969,18 @@ declare module 'vscode' { export namespace languages { export function getTokenInformationAtPosition(document: TextDocument, position: Position): Promise; + /** + * Register a inline hints provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their [score](#languages.match) and the best-matching provider is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An on type inline hints provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerInlineHintsProvider(selector: DocumentSelector, provider: InlineHintsProvider): Disposable; } //#endregion @@ -2362,4 +2374,51 @@ declare module 'vscode' { } //#endregion + + export interface ThemableDecorationAttachmentRenderOptions { + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontSize?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + padding?: string; + } + + /** + * Inline hint information. + */ + export class InlineHint { + /** + * The text of the hint. + */ + text: string; + /** + * The position of the hint. + */ + position: Position; + + /** + * Creates a new inline hint information object. + * + * @param text The text of the parameter. + * @param position The position of the argument. + */ + constructor(text: string, position: Position); + } + + /** + * The document formatting provider interface defines the contract between extensions and + * the inline hints feature. + */ + export interface InlineHintsProvider { + /** + * @param model The document in which the command was invoked. + * @param token A cancellation token. + * + * @return A list of arguments labels or a thenable that resolves to such. + */ + provideInlineHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; + } } From 4b3d54ceb32fc2a13b88c19261c079d844317470 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 14:09:42 +0800 Subject: [PATCH 16/36] Avoid changes --- src/vs/workbench/api/common/extHostTypes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 6753c8e8b49b2..03290e70dfd2c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1261,11 +1261,11 @@ export class CallHierarchyOutgoingCall { @es5ClassCompat export class CodeLens { - range: vscode.Range; + range: Range; command: vscode.Command | undefined; - constructor(range: vscode.Range, command?: vscode.Command) { + constructor(range: Range, command?: vscode.Command) { this.range = range; this.command = command; } From d20f8ed37d4bf61e3f75123498721d42e9561e73 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 17:18:24 +0800 Subject: [PATCH 17/36] Add style controls --- .../browser/services/codeEditorServiceImpl.ts | 3 ++- src/vs/editor/common/config/editorOptions.ts | 21 +++++++++++++++++- src/vs/editor/common/editorCommon.ts | 1 + .../common/standalone/standaloneEnums.ts | 12 +++++----- .../inlineHints/inlineHintsController.ts | 13 +++++++---- src/vs/monaco.d.ts | 22 ++++++++++++++----- src/vs/vscode.proposed.d.ts | 4 ++++ .../api/common/extHostTypeConverters.ts | 1 + 8 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index b4d276cce497b..952b317c64016 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -348,6 +348,7 @@ const _CSS_MAP: { [prop: string]: string; } = { fontStyle: 'font-style:{0};', fontWeight: 'font-weight:{0};', fontSize: 'font-size:{0};', + fontFamily: 'font-family:{0};', textDecoration: 'text-decoration:{0};', cursor: 'cursor:{0};', letterSpacing: 'letter-spacing:{0};', @@ -531,7 +532,7 @@ class DecorationCSSRules { cssTextArr.push(strings.format(_CSS_MAP.contentText, escaped)); } - this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'fontSize', 'textDecoration', 'color', 'opacity', 'backgroundColor', 'margin', 'padding'], cssTextArr); + this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'textDecoration', 'color', 'opacity', 'backgroundColor', 'margin', 'padding'], cssTextArr); if (this.collectCSSText(opts, ['width', 'height'], cssTextArr)) { cssTextArr.push('display:inline-block;'); } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 8edd81d125e28..c244fb4c45a95 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -628,7 +628,15 @@ export interface IEditorOptions { /** * Controls show inline hints. */ - showInlineHints?: boolean + showInlineHints?: boolean; + /** + * Inline hints font size. Default to 90% of the editor font size. + */ + inlineHintsFontSize?: number; + /** + * Inline hints font family. Defaults to editor font family. + */ + inlineHintsFontFamily?: string } /** @@ -3764,6 +3772,8 @@ export const enum EditorOption { wrappingStrategy, showDeprecated, showInlineHints, + inlineHintsFontSize, + inlineHintsFontFamily, // Leave these at the end (because they have dependencies!) editorClassName, pixelRatio, @@ -4271,6 +4281,15 @@ export const EditorOptions = { EditorOption.showInlineHints, 'showInlineHints', true, { description: nls.localize('showInlineHints', "Controls show inline hints.") } )), + inlineHintsFontSize: register(new EditorIntOption( + EditorOption.inlineHintsFontSize, 'inlineHintsFontSize', + 0, 0, 1000, + { markdownDescription: nls.localize('inlineHintsFontSize', "Font size for the inline hints. When set to `0`, the value of `#editor.fontSize#` is used.") } + )), + inlineHintsFontFamily: register(new EditorStringOption( + EditorOption.inlineHintsFontFamily, 'inlineHintsFontFamily', EDITOR_FONT_DEFAULTS.fontFamily, + { description: nls.localize('inlineHintsFontFamily', "Controls inline hints font family.") } + )), snippetSuggestions: register(new EditorStringEnumOption( EditorOption.snippetSuggestions, 'snippetSuggestions', 'inline' as 'top' | 'bottom' | 'inline' | 'none', diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index c711e1b94cc63..97c835b84ef6f 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -633,6 +633,7 @@ export interface IContentDecorationRenderOptions { fontStyle?: string; fontWeight?: string; fontSize?: string; + fontFamily?: string; textDecoration?: string; color?: string | ThemeColor; backgroundColor?: string | ThemeColor; diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index fc751367d9847..431f74b302e55 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -288,11 +288,13 @@ export enum EditorOption { wrappingStrategy = 118, showDeprecated = 119, showInlineHints = 120, - editorClassName = 121, - pixelRatio = 122, - tabFocusMode = 123, - layoutInfo = 124, - wrappingInfo = 125 + inlineHintsFontSize = 121, + inlineHintsFontFamily = 122, + editorClassName = 123, + pixelRatio = 124, + tabFocusMode = 125, + layoutInfo = 126, + wrappingInfo = 127 } /** diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 6857c37b41323..65dfb12ac755b 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -128,7 +128,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi const newDelay = this._getInlineHintsDelays.update(model, Date.now() - t1); scheduler.delay = newDelay; - // render lenses + // render hints this._updateDecorations(result); this._updateHintsDecorators(result); }, onUnexpectedError); @@ -172,7 +172,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi private _updateHintsDecorators(hintsData: InlineHintsData[]): void { let decorations: IModelDeltaDecoration[] = []; let newDecorationsTypes: { [key: string]: boolean } = {}; - const { fontSize } = this._getLayoutInfo(); + const { fontSize, fontFamily } = this._getLayoutInfo(); const backgroundColor = this._themeService.getColorTheme().getColor(inlineHintBackground); const fontColor = this._themeService.getColorTheme().getColor(inlineHintForeground); @@ -192,6 +192,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi color: `${fontColor}`, margin: '0px 5px 0px 0px', fontSize: `${fontSize}px`, + fontFamily: fontFamily, padding: '0px 2px' } }, undefined, this._editor); @@ -220,8 +221,12 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi } private _getLayoutInfo() { - const fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; - return { fontSize }; + let fontSize = this._editor.getOption(EditorOption.inlineHintsFontSize); + if (!fontSize || fontSize < 5) { + fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; + } + const fontFamily = this._editor.getOption(EditorOption.inlineHintsFontFamily); + return { fontSize, fontFamily }; } private _removeAllDecorations(): void { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b303f446422b3..544b862e082ef 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3180,6 +3180,14 @@ declare namespace monaco.editor { * Controls show inline hints. */ showInlineHints?: boolean; + /** + * Inline hints font size. Default to 90% of the editor font size. + */ + inlineHintsFontSize?: number; + /** + * Inline hints font family. Defaults to editor font family. + */ + inlineHintsFontFamily?: string; } /** @@ -4033,11 +4041,13 @@ declare namespace monaco.editor { wrappingStrategy = 118, showDeprecated = 119, showInlineHints = 120, - editorClassName = 121, - pixelRatio = 122, - tabFocusMode = 123, - layoutInfo = 124, - wrappingInfo = 125 + inlineHintsFontSize = 121, + inlineHintsFontFamily = 122, + editorClassName = 123, + pixelRatio = 124, + tabFocusMode = 125, + layoutInfo = 126, + wrappingInfo = 127 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -4139,6 +4149,8 @@ declare namespace monaco.editor { showUnused: IEditorOption; showDeprecated: IEditorOption; showInlineHints: IEditorOption; + inlineHintsFontSize: IEditorOption; + inlineHintsFontFamily: IEditorOption; snippetSuggestions: IEditorOption; smartSelect: IEditorOption; smoothScrolling: IEditorOption; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 1acb48712c19d..4ae6a70ab32be 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2380,6 +2380,10 @@ declare module 'vscode' { * CSS styling property that will be applied to the decoration attachment. */ fontSize?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontFamily?: string; /** * CSS styling property that will be applied to the decoration attachment. */ diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 0eaf2dd535f92..fdd9da982d1ab 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -395,6 +395,7 @@ export namespace ThemableDecorationAttachmentRenderOptions { fontStyle: options.fontStyle, fontWeight: options.fontWeight, fontSize: options.fontSize, + fontFamily: options.fontFamily, textDecoration: options.textDecoration, color: options.color, backgroundColor: options.backgroundColor, From 2b380bf8c3482b4164fc5270d0c05851511e8107 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 17:30:32 +0800 Subject: [PATCH 18/36] Support whitespace options --- .../src/languageFeatures/inlineHints.ts | 8 +++++--- src/vs/editor/common/modes.ts | 6 ++++-- .../contrib/inlineHints/inlineHintsController.ts | 6 ++++-- src/vs/monaco.d.ts | 2 ++ src/vs/vscode.proposed.d.ts | 12 +++++++++++- src/vs/workbench/api/common/extHostTypeConverters.ts | 8 +++++--- src/vs/workbench/api/common/extHostTypes.ts | 6 +++++- 7 files changed, 36 insertions(+), 12 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts index 6647b654f3ea8..8607fb92c894a 100644 --- a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts @@ -32,8 +32,10 @@ export namespace ExperimentalProto { } interface HintItem { - text: string - position: Proto.Location + text: string; + position: Proto.Location; + whitespaceBefore?: boolean; + whitespaceAfter?: boolean; } export interface ProvideInlineHintsResponse extends Proto.Response { @@ -75,7 +77,7 @@ class TypeScriptInlineHintsProvider implements vscode.InlineHintsProvider { } return response.body.map(hint => { - return new vscode.InlineHint(hint.text, Position.fromLocation(hint.position)); + return new vscode.InlineHint(hint.text, Position.fromLocation(hint.position), hint.whitespaceBefore, hint.whitespaceAfter); }); } catch (e) { return []; diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index f4b5f14b9dae1..926a250c33890 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1660,8 +1660,10 @@ export interface CodeLensProvider { } export interface InlineHint { - text: string - position: IPosition + text: string; + position: IPosition; + whiteSpaceBefore?: boolean; + whiteSpaceAfter?: boolean; } export interface InlineHintsProvider { diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 65dfb12ac755b..34a5d9bfd37e9 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -179,7 +179,9 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi for (let i = 0; i < hintsData.length; i++) { const hint = hintsData[i].list; for (let j = 0; j < hint.length && decorations.length < MAX_DECORATORS; j++) { - const { text, position } = hint[j]; + const { text, position, whiteSpaceBefore, whiteSpaceAfter } = hint[j]; + const marginBefore = whiteSpaceBefore ? 5 : 0; + const marginAfter = whiteSpaceAfter ? 5 : 0; const subKey = hash(text).toString(16); let key = 'inlineHints-' + subKey; @@ -190,7 +192,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi contentText: text, backgroundColor: `${backgroundColor}`, color: `${fontColor}`, - margin: '0px 5px 0px 0px', + margin: `0px ${marginAfter}px 0px ${marginBefore}px`, fontSize: `${fontSize}px`, fontFamily: fontFamily, padding: '0px 2px' diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 544b862e082ef..f8911a522ebff 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6382,6 +6382,8 @@ declare namespace monaco.languages { export interface InlineHint { text: string; position: IPosition; + whitespaceBefore?: boolean; + whitespaceAfter?: boolean; } export interface InlineHintsProvider { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 4ae6a70ab32be..08e74740b13e3 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2402,14 +2402,24 @@ declare module 'vscode' { * The position of the hint. */ position: Position; + /** + * Whitespace before the hint. + */ + whitespaceBefore?: boolean; + /** + * Whitespace after the hint. + */ + whitespaceAfter?: boolean; /** * Creates a new inline hint information object. * * @param text The text of the parameter. * @param position The position of the argument. + * @param whitespaceBefore Whitespace before the hint. + * @param whitespaceAfter TWhitespace after the hint. */ - constructor(text: string, position: Position); + constructor(text: string, position: Position, whitespaceBefore?: boolean, whitespaceAfter?: boolean); } /** diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index fdd9da982d1ab..574703048d1b0 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1019,10 +1019,12 @@ export namespace SignatureHelp { } export namespace InlineHint { - export function from(label: vscode.InlineHint): modes.InlineHint { + export function from(hint: vscode.InlineHint): modes.InlineHint { return { - text: label.text, - position: Position.from(label.position) + text: hint.text, + position: Position.from(hint.position), + whiteSpaceBefore: hint.whitespaceBefore, + whiteSpaceAfter: hint.whitespaceAfter }; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 03290e70dfd2c..7c8cff3d68d28 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1377,10 +1377,14 @@ export enum SignatureHelpTriggerKind { export class InlineHint { text: string; position: Position; + whitespaceBefore?: boolean; + whitespaceAfter?: boolean; - constructor(text: string, position: Position) { + constructor(text: string, position: Position, whitespaceBefore?: boolean, whitespaceAfter?: boolean) { this.text = text; this.position = position; + this.whitespaceBefore = whitespaceBefore; + this.whitespaceAfter = whitespaceAfter; } } From b4789ecf84993dcf42bd5b82df041e3219141f27 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 17:39:39 +0800 Subject: [PATCH 19/36] Fix typo --- src/vs/editor/common/modes.ts | 4 ++-- src/vs/editor/contrib/inlineHints/inlineHintsController.ts | 6 +++--- src/vs/workbench/api/common/extHostTypeConverters.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 926a250c33890..45e542de90ea5 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1662,8 +1662,8 @@ export interface CodeLensProvider { export interface InlineHint { text: string; position: IPosition; - whiteSpaceBefore?: boolean; - whiteSpaceAfter?: boolean; + whitespaceBefore?: boolean; + whitespaceAfter?: boolean; } export interface InlineHintsProvider { diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 34a5d9bfd37e9..a5e12ea2f0428 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -179,9 +179,9 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi for (let i = 0; i < hintsData.length; i++) { const hint = hintsData[i].list; for (let j = 0; j < hint.length && decorations.length < MAX_DECORATORS; j++) { - const { text, position, whiteSpaceBefore, whiteSpaceAfter } = hint[j]; - const marginBefore = whiteSpaceBefore ? 5 : 0; - const marginAfter = whiteSpaceAfter ? 5 : 0; + const { text, position, whitespaceBefore, whitespaceAfter } = hint[j]; + const marginBefore = whitespaceBefore ? 5 : 0; + const marginAfter = whitespaceAfter ? 5 : 0; const subKey = hash(text).toString(16); let key = 'inlineHints-' + subKey; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 574703048d1b0..c6a344891d194 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1023,8 +1023,8 @@ export namespace InlineHint { return { text: hint.text, position: Position.from(hint.position), - whiteSpaceBefore: hint.whitespaceBefore, - whiteSpaceAfter: hint.whitespaceAfter + whitespaceBefore: hint.whitespaceBefore, + whitespaceAfter: hint.whitespaceAfter }; } } From 413b5d4705782466dc30e8c134a283141cdb6b5f Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 25 Dec 2020 17:40:10 +0800 Subject: [PATCH 20/36] Avoid ts changes --- .../src/languageFeatures/inlineHints.ts | 98 ------------------- .../src/languageProvider.ts | 1 - 2 files changed, 99 deletions(-) delete mode 100644 extensions/typescript-language-features/src/languageFeatures/inlineHints.ts diff --git a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts deleted file mode 100644 index 8607fb92c894a..0000000000000 --- a/extensions/typescript-language-features/src/languageFeatures/inlineHints.ts +++ /dev/null @@ -1,98 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as Proto from '../protocol'; -import { DocumentSelector } from '../utils/documentSelector'; -import { ClientCapability, ITypeScriptServiceClient, ServerResponse, ExecConfig } from '../typescriptService'; -import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; -import { Position } from '../utils/typeConverters'; - -export namespace ExperimentalProto { - export const enum CommandTypes { - ProvideInlineHints = 'ProvideInlineHints' - } - - export interface ProvideInlineHintsArgs extends Proto.FileRequestArgs { - /** - * Start position of the span. - */ - start: number; - /** - * Length of the span. - */ - length: number; - } - - export interface ProvideInlineHintsRequest extends Proto.Request { - command: CommandTypes.ProvideInlineHints; - arguments: ProvideInlineHintsArgs; - } - - interface HintItem { - text: string; - position: Proto.Location; - whitespaceBefore?: boolean; - whitespaceAfter?: boolean; - } - - export interface ProvideInlineHintsResponse extends Proto.Response { - body?: HintItem[]; - } - - export interface IExtendedTypeScriptServiceClient { - execute( - command: K, - args: ExtendedTsServerRequests[K][0], - token: vscode.CancellationToken, - config?: ExecConfig - ): Promise>; - } - - export interface ExtendedTsServerRequests { - 'provideInlineHints': [ProvideInlineHintsArgs, ProvideInlineHintsResponse]; - } -} - -class TypeScriptInlineHintsProvider implements vscode.InlineHintsProvider { - constructor( - private readonly client: ITypeScriptServiceClient - ) { } - - async provideInlineHints(model: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise { - const filepath = this.client.toOpenedFilePath(model); - if (!filepath) { - return []; - } - - const start = model.offsetAt(range.start); - const length = model.offsetAt(range.end) - start; - - try { - const response = await (this.client as ExperimentalProto.IExtendedTypeScriptServiceClient).execute('provideInlineHints', { file: filepath, start, length }, token); - if (response.type !== 'response' || !response.success || !response.body) { - return []; - } - - return response.body.map(hint => { - return new vscode.InlineHint(hint.text, Position.fromLocation(hint.position), hint.whitespaceBefore, hint.whitespaceAfter); - }); - } catch (e) { - return []; - } - } -} - -export function register( - selector: DocumentSelector, - client: ITypeScriptServiceClient -) { - return conditionalRegistration([ - requireSomeCapability(client, ClientCapability.Semantic), - ], () => { - return vscode.languages.registerInlineHintsProvider(selector.semantic, - new TypeScriptInlineHintsProvider(client)); - }); -} diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 7e39520184466..92ab84be30892 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -82,7 +82,6 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), - import('./languageFeatures/inlineHints').then(provider => this._register(provider.register(selector, this.client))), ]); } From f93a2b62ef070aeebbdde309d57888e2a5f4d38c Mon Sep 17 00:00:00 2001 From: kingwl Date: Mon, 4 Jan 2021 21:15:59 +0800 Subject: [PATCH 21/36] fix cr issues --- src/vs/editor/common/config/editorOptions.ts | 98 ++++++++++++++----- .../common/standalone/standaloneEnums.ts | 14 ++- .../inlineHints/inlineHintsController.ts | 12 ++- src/vs/monaco.d.ts | 53 ++++++---- .../workbench/api/common/extHost.api.impl.ts | 1 + .../api/common/extHostLanguageFeatures.ts | 8 +- .../api/common/extHostTypeConverters.ts | 10 ++ 7 files changed, 129 insertions(+), 67 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index c244fb4c45a95..bce85536162d3 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -626,17 +626,9 @@ export interface IEditorOptions { */ showDeprecated?: boolean; /** - * Controls show inline hints. + * Control the behavior and rendering of the inline hints. */ - showInlineHints?: boolean; - /** - * Inline hints font size. Default to 90% of the editor font size. - */ - inlineHintsFontSize?: number; - /** - * Inline hints font family. Defaults to editor font family. - */ - inlineHintsFontFamily?: string + inlineHints?: IEditorInlineHintsOptions; } /** @@ -2381,6 +2373,74 @@ class EditorLightbulb extends BaseEditorOption>; + +class EditorInlineHints extends BaseEditorOption { + + constructor() { + const defaults: EditorInlineHintsOptions = { enabled: true, fontSize: 0, fontFamily: EDITOR_FONT_DEFAULTS.fontFamily }; + super( + EditorOption.inlineHints, 'inlineHints', defaults, + { + 'editor.inlineHints.enabled': { + type: 'boolean', + default: defaults.enabled, + description: nls.localize('inlineHints.enable', "Enables the inline hints in the editor.") + }, + 'editor.inlineHints.fontSize': { + type: 'number', + default: defaults.fontSize, + description: nls.localize('inlineHints.fontSize', "Controls font size of inline hints in the editor. When set to `0`, the 90% of `#editor.fontSize#` is used.") + }, + 'editor.inlineHints.fontFamily': { + type: 'string', + default: defaults.fontFamily, + description: nls.localize('inlineHints.fontFamily', "Controls font family of inline hints in the editor.") + }, + } + ); + } + + public validate(_input: any): EditorInlineHintsOptions { + if (!_input || typeof _input !== 'object') { + return this.defaultValue; + } + const input = _input as IEditorInlineHintsOptions; + return { + enabled: boolean(input.enabled, this.defaultValue.enabled), + fontSize: EditorIntOption.clampedInt(input.fontSize, this.defaultValue.fontSize, 0, 100), + fontFamily: EditorStringOption.string(input.fontFamily, this.defaultValue.fontFamily) + }; + } +} + +//#endregion + //#region lineHeight class EditorLineHeight extends EditorIntOption { @@ -3771,9 +3831,7 @@ export const enum EditorOption { wrappingIndent, wrappingStrategy, showDeprecated, - showInlineHints, - inlineHintsFontSize, - inlineHintsFontFamily, + inlineHints, // Leave these at the end (because they have dependencies!) editorClassName, pixelRatio, @@ -4277,19 +4335,7 @@ export const EditorOptions = { EditorOption.showDeprecated, 'showDeprecated', true, { description: nls.localize('showDeprecated', "Controls strikethrough deprecated variables.") } )), - showInlineHints: register(new EditorBooleanOption( - EditorOption.showInlineHints, 'showInlineHints', true, - { description: nls.localize('showInlineHints', "Controls show inline hints.") } - )), - inlineHintsFontSize: register(new EditorIntOption( - EditorOption.inlineHintsFontSize, 'inlineHintsFontSize', - 0, 0, 1000, - { markdownDescription: nls.localize('inlineHintsFontSize', "Font size for the inline hints. When set to `0`, the value of `#editor.fontSize#` is used.") } - )), - inlineHintsFontFamily: register(new EditorStringOption( - EditorOption.inlineHintsFontFamily, 'inlineHintsFontFamily', EDITOR_FONT_DEFAULTS.fontFamily, - { description: nls.localize('inlineHintsFontFamily', "Controls inline hints font family.") } - )), + inlineHints: register(new EditorInlineHints()), snippetSuggestions: register(new EditorStringEnumOption( EditorOption.snippetSuggestions, 'snippetSuggestions', 'inline' as 'top' | 'bottom' | 'inline' | 'none', diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 431f74b302e55..ad85fc86c5b25 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -287,14 +287,12 @@ export enum EditorOption { wrappingIndent = 117, wrappingStrategy = 118, showDeprecated = 119, - showInlineHints = 120, - inlineHintsFontSize = 121, - inlineHintsFontFamily = 122, - editorClassName = 123, - pixelRatio = 124, - tabFocusMode = 125, - layoutInfo = 126, - wrappingInfo = 127 + inlineHints = 120, + editorClassName = 121, + pixelRatio = 122, + tabFocusMode = 123, + layoutInfo = 124, + wrappingInfo = 125 } /** diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index a5e12ea2f0428..67bd5470c4628 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -89,7 +89,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi return false; } - return this._editor.getOption(EditorOption.showInlineHints); + return this._editor.getOption(EditorOption.inlineHints).enabled; } static get(editor: ICodeEditor): InlineHintsDetector { @@ -180,8 +180,8 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi const hint = hintsData[i].list; for (let j = 0; j < hint.length && decorations.length < MAX_DECORATORS; j++) { const { text, position, whitespaceBefore, whitespaceAfter } = hint[j]; - const marginBefore = whitespaceBefore ? 5 : 0; - const marginAfter = whitespaceAfter ? 5 : 0; + const marginBefore = whitespaceBefore ? fontSize / 3 : 0; + const marginAfter = whitespaceAfter ? fontSize / 3 : 0; const subKey = hash(text).toString(16); let key = 'inlineHints-' + subKey; @@ -223,11 +223,13 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi } private _getLayoutInfo() { - let fontSize = this._editor.getOption(EditorOption.inlineHintsFontSize); + const options = this._editor.getOption(EditorOption.inlineHints); + let fontSize = options.fontSize; if (!fontSize || fontSize < 5) { fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; } - const fontFamily = this._editor.getOption(EditorOption.inlineHintsFontFamily); + + const fontFamily = options.fontFamily; return { fontSize, fontFamily }; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index f8911a522ebff..99e17ca6ed003 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3177,17 +3177,9 @@ declare namespace monaco.editor { */ showDeprecated?: boolean; /** - * Controls show inline hints. + * Control the behavior and rendering of the inline hints. */ - showInlineHints?: boolean; - /** - * Inline hints font size. Default to 90% of the editor font size. - */ - inlineHintsFontSize?: number; - /** - * Inline hints font family. Defaults to editor font family. - */ - inlineHintsFontFamily?: string; + inlineHints?: IEditorInlineHintsOptions; } /** @@ -3539,6 +3531,29 @@ declare namespace monaco.editor { export type EditorLightbulbOptions = Readonly>; + /** + * Configuration options for editor inlineHints + */ + export interface IEditorInlineHintsOptions { + /** + * Enable the inline hints. + * Defaults to true. + */ + enabled?: boolean; + /** + * Font size of inline hints. + * Default to 90% of the editor font size. + */ + fontSize?: number; + /** + * Font family of inline hints. + * Defaults to editor font family. + */ + fontFamily?: string; + } + + export type EditorInlineHintsOptions = Readonly>; + /** * Configuration options for editor minimap */ @@ -4040,14 +4055,12 @@ declare namespace monaco.editor { wrappingIndent = 117, wrappingStrategy = 118, showDeprecated = 119, - showInlineHints = 120, - inlineHintsFontSize = 121, - inlineHintsFontFamily = 122, - editorClassName = 123, - pixelRatio = 124, - tabFocusMode = 125, - layoutInfo = 126, - wrappingInfo = 127 + inlineHints = 120, + editorClassName = 121, + pixelRatio = 122, + tabFocusMode = 123, + layoutInfo = 124, + wrappingInfo = 125 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -4148,9 +4161,7 @@ declare namespace monaco.editor { showFoldingControls: IEditorOption; showUnused: IEditorOption; showDeprecated: IEditorOption; - showInlineHints: IEditorOption; - inlineHintsFontSize: IEditorOption; - inlineHintsFontFamily: IEditorOption; + inlineHints: IEditorOption; snippetSuggestions: IEditorOption; smartSelect: IEditorOption; smoothScrolling: IEditorOption; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 0785de594abcf..2af404f8d0f7b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -484,6 +484,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguages.tokenAtPosition(doc, pos); }, registerInlineHintsProvider(selector: vscode.DocumentSelector, provider: vscode.InlineHintsProvider): vscode.Disposable { + checkProposedApiEnabled(extension); return extHostLanguageFeatures.registerInlineHintsProvider(extension, selector, provider); } }; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index a1f4502b354d4..861a010f41255 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1063,8 +1063,6 @@ class SignatureHelpAdapter { } class InlineHintsAdapter { - private readonly _cache = new Cache('InlineHints'); - constructor( private readonly _documents: ExtHostDocuments, private readonly _provider: vscode.InlineHintsProvider, @@ -1073,11 +1071,7 @@ class InlineHintsAdapter { provideInlineHints(resource: URI, range: IRange, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); return asPromise(() => this._provider.provideInlineHints(doc, typeConvert.Range.to(range), token)).then(value => { - if (value) { - const id = this._cache.add([value]); - return { hints: value.map(typeConvert.InlineHint.from), id }; - } - return undefined; + return value ? { hints: value.map(typeConvert.InlineHint.from) } : undefined; }); } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index c6a344891d194..dec42ed93e378 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1019,6 +1019,7 @@ export namespace SignatureHelp { } export namespace InlineHint { + export function from(hint: vscode.InlineHint): modes.InlineHint { return { text: hint.text, @@ -1027,6 +1028,15 @@ export namespace InlineHint { whitespaceAfter: hint.whitespaceAfter }; } + + export function to(hint: modes.InlineHint): vscode.InlineHint { + return { + text: hint.text, + position: Position.to(hint.position), + whitespaceBefore: hint.whitespaceBefore, + whitespaceAfter: hint.whitespaceAfter + }; + } } export namespace DocumentLink { From 73b4dabb2d4c6401d0b99754782711a6f545f90a Mon Sep 17 00:00:00 2001 From: kingwl Date: Mon, 11 Jan 2021 19:40:08 +0800 Subject: [PATCH 22/36] Make lint happy --- src/vs/vscode.proposed.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 12578cdad1922..e365a95fb962a 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2430,7 +2430,8 @@ declare module 'vscode' { * @return A list of arguments labels or a thenable that resolves to such. */ provideInlineHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; - } + } + /** * Represents a storage utility for secrets, information that is * sensitive. From 9853c8fe6d7e221fd5c87c3f6b753b8ebdcc32a9 Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 13 Jan 2021 19:21:51 +0800 Subject: [PATCH 23/36] Fix cr issues --- src/vs/editor/common/modes.ts | 4 +- .../inlineHints/inlineHintsController.ts | 24 ++-- src/vs/monaco.d.ts | 2 +- src/vs/platform/theme/common/colorRegistry.ts | 4 +- src/vs/vscode.proposed.d.ts | 128 +++++++++--------- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 12 +- .../api/common/extHostTypeConverters.ts | 4 +- src/vs/workbench/api/common/extHostTypes.ts | 6 +- 9 files changed, 96 insertions(+), 90 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index d0cb01f2976b8..f65b17c31b001 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IPosition, Position } from 'vs/editor/common/core/position'; +import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; @@ -1661,7 +1661,7 @@ export interface CodeLensProvider { export interface InlineHint { text: string; - position: IPosition; + range: IRange; whitespaceBefore?: boolean; whitespaceAfter?: boolean; } diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 67bd5470c4628..1fcc1c2d62567 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -16,7 +16,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { InlineHintsProvider, InlineHintsProviderRegistry, InlineHint } from 'vs/editor/common/modes'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { flatten } from 'vs/base/common/arrays'; -import { inlineHintForeground, inlineHintBackground } from 'vs/platform/theme/common/colorRegistry'; +import { editorInlineHintForeground, editorInlineHintBackground } from 'vs/platform/theme/common/colorRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Range } from 'vs/editor/common/core/range'; @@ -154,10 +154,10 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi const decorations = flatten(hintsData.map(hints => hints.list.map(hint => { return { range: { - startLineNumber: hint.position.lineNumber, - startColumn: hint.position.column, - endLineNumber: hint.position.lineNumber, - endColumn: hint.position.column + startLineNumber: hint.range.startLineNumber, + startColumn: hint.range.startColumn, + endLineNumber: hint.range.endLineNumber, + endColumn: hint.range.endColumn }, options: ModelDecorationOptions.EMPTY }; @@ -173,13 +173,13 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi let decorations: IModelDeltaDecoration[] = []; let newDecorationsTypes: { [key: string]: boolean } = {}; const { fontSize, fontFamily } = this._getLayoutInfo(); - const backgroundColor = this._themeService.getColorTheme().getColor(inlineHintBackground); - const fontColor = this._themeService.getColorTheme().getColor(inlineHintForeground); + const backgroundColor = this._themeService.getColorTheme().getColor(editorInlineHintBackground); + const fontColor = this._themeService.getColorTheme().getColor(editorInlineHintForeground); for (let i = 0; i < hintsData.length; i++) { const hint = hintsData[i].list; for (let j = 0; j < hint.length && decorations.length < MAX_DECORATORS; j++) { - const { text, position, whitespaceBefore, whitespaceAfter } = hint[j]; + const { text, range, whitespaceBefore, whitespaceAfter } = hint[j]; const marginBefore = whitespaceBefore ? fontSize / 3 : 0; const marginAfter = whitespaceAfter ? fontSize / 3 : 0; @@ -203,10 +203,10 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi newDecorationsTypes[key] = true; decorations.push({ range: { - startLineNumber: position.lineNumber, - startColumn: position.column, - endLineNumber: position.lineNumber, - endColumn: position.column + startLineNumber: range.startLineNumber, + startColumn: range.startColumn, + endLineNumber: range.endLineNumber, + endColumn: range.endColumn }, options: this._codeEditorService.resolveDecorationOptions(key, true) }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 09863393a3164..aae108b813cd7 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6405,7 +6405,7 @@ declare namespace monaco.languages { export interface InlineHint { text: string; - position: IPosition; + range: IRange; whitespaceBefore?: boolean; whitespaceAfter?: boolean; } diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 219bec51d2b3c..df2c3894198df 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -260,8 +260,8 @@ export const editorHintBorder = registerColor('editorHint.border', { dark: null, export const sashHoverBorder = registerColor('sash.hoverBorder', { dark: null, light: null, hc: null }, nls.localize('sashActiveBorder', "Border color of active sashes.")); -export const inlineHintForeground = registerColor('inlineHint.foreground', { dark: '#A7A6A5', light: '#A7A6A5', hc: null }, nls.localize('inlineHintForeground', 'Foreground color of inline hints')); -export const inlineHintBackground = registerColor('inlineHint.background', { dark: '#3A3A3A', light: '#3A3A3A', hc: null }, nls.localize('inlineHintBackground', 'Background color of inline hints')); +export const editorInlineHintForeground = registerColor('editorInlineHint.foreground', { dark: '#A7A6A5', light: '#A7A6A5', hc: null }, nls.localize('editorInlineHintForeground', 'Foreground color of inline hints')); +export const editorInlineHintBackground = registerColor('editorInlineHint.background', { dark: '#3A3A3A', light: '#3A3A3A', hc: null }, nls.localize('editorInlineHintBackground', 'Background color of inline hints')); /** * Editor background color. * Because of bug https://monacotools.visualstudio.com/DefaultCollection/Monaco/_workitems/edit/13254 diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index e365a95fb962a..5e8463ccb5db3 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1952,6 +1952,13 @@ declare module 'vscode' { export namespace languages { export function getTokenInformationAtPosition(document: TextDocument, position: Position): Promise; + } + + //#endregion + + //@region https://github.com/microsoft/vscode/issues/16221 + + export namespace languages { /** * Register a inline hints provider. * @@ -1966,6 +1973,66 @@ declare module 'vscode' { export function registerInlineHintsProvider(selector: DocumentSelector, provider: InlineHintsProvider): Disposable; } + export interface ThemableDecorationAttachmentRenderOptions { + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontSize?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontFamily?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + padding?: string; + } + + /** + * Inline hint information. + */ + export class InlineHint { + /** + * The text of the hint. + */ + text: string; + /** + * The position of the hint. + */ + range: Range; + /** + * Whitespace before the hint. + */ + whitespaceBefore?: boolean; + /** + * Whitespace after the hint. + */ + whitespaceAfter?: boolean; + + /** + * Creates a new inline hint information object. + * + * @param text The text of the hint. + * @param range The range of the hint. + * @param whitespaceBefore Whitespace before the hint. + * @param whitespaceAfter TWhitespace after the hint. + */ + constructor(text: string, range: Range, whitespaceBefore?: boolean, whitespaceAfter?: boolean); + } + + /** + * The document formatting provider interface defines the contract between extensions and + * the inline hints feature. + */ + export interface InlineHintsProvider { + /** + * @param model The document in which the command was invoked. + * @param token A cancellation token. + * + * @return A list of arguments labels or a thenable that resolves to such. + */ + provideInlineHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; + } //#endregion //#region https://github.com/microsoft/vscode/issues/104436 @@ -2371,67 +2438,6 @@ declare module 'vscode' { //#endregion - export interface ThemableDecorationAttachmentRenderOptions { - /** - * CSS styling property that will be applied to the decoration attachment. - */ - fontSize?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - fontFamily?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - padding?: string; - } - - /** - * Inline hint information. - */ - export class InlineHint { - /** - * The text of the hint. - */ - text: string; - /** - * The position of the hint. - */ - position: Position; - /** - * Whitespace before the hint. - */ - whitespaceBefore?: boolean; - /** - * Whitespace after the hint. - */ - whitespaceAfter?: boolean; - - /** - * Creates a new inline hint information object. - * - * @param text The text of the parameter. - * @param position The position of the argument. - * @param whitespaceBefore Whitespace before the hint. - * @param whitespaceAfter TWhitespace after the hint. - */ - constructor(text: string, position: Position, whitespaceBefore?: boolean, whitespaceAfter?: boolean); - } - - /** - * The document formatting provider interface defines the contract between extensions and - * the inline hints feature. - */ - export interface InlineHintsProvider { - /** - * @param model The document in which the command was invoked. - * @param token A cancellation token. - * - * @return A list of arguments labels or a thenable that resolves to such. - */ - provideInlineHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; - } - /** * Represents a storage utility for secrets, information that is * sensitive. diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f08cced19422e..b0144d0f78110 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1288,7 +1288,7 @@ export interface ISignatureHelpContextDto { export interface IInlineHintDto { text: string - position: IPosition + range: IRange } export interface IInlineHintsDto { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 861a010f41255..3fe5e063c0d86 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1776,12 +1776,6 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._createDisposable(handle); } - registerInlineHintsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineHintsProvider): vscode.Disposable { - const handle = this._addNewAdapter(new InlineHintsAdapter(this._documents, provider), extension); - this._proxy.$registerInlineHintsProvider(handle, this._transformDocumentSelector(selector)); - return this._createDisposable(handle); - } - $provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: extHostProtocol.ISignatureHelpContextDto, token: CancellationToken): Promise { return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token), undefined); } @@ -1792,6 +1786,12 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- inline hints + registerInlineHintsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineHintsProvider): vscode.Disposable { + const handle = this._addNewAdapter(new InlineHintsAdapter(this._documents, provider), extension); + this._proxy.$registerInlineHintsProvider(handle, this._transformDocumentSelector(selector)); + return this._createDisposable(handle); + } + $provideInlineHints(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise { return this._withAdapter(handle, InlineHintsAdapter, adapter => adapter.provideInlineHints(URI.revive(resource), range, token), undefined); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index dec42ed93e378..e0d50f643bf3e 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1023,7 +1023,7 @@ export namespace InlineHint { export function from(hint: vscode.InlineHint): modes.InlineHint { return { text: hint.text, - position: Position.from(hint.position), + range: Range.from(hint.range), whitespaceBefore: hint.whitespaceBefore, whitespaceAfter: hint.whitespaceAfter }; @@ -1032,7 +1032,7 @@ export namespace InlineHint { export function to(hint: modes.InlineHint): vscode.InlineHint { return { text: hint.text, - position: Position.to(hint.position), + range: Range.to(hint.range), whitespaceBefore: hint.whitespaceBefore, whitespaceAfter: hint.whitespaceAfter }; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 7c8cff3d68d28..4e8fe79acfdb9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1376,13 +1376,13 @@ export enum SignatureHelpTriggerKind { @es5ClassCompat export class InlineHint { text: string; - position: Position; + range: Range; whitespaceBefore?: boolean; whitespaceAfter?: boolean; - constructor(text: string, position: Position, whitespaceBefore?: boolean, whitespaceAfter?: boolean) { + constructor(text: string, range: Range, whitespaceBefore?: boolean, whitespaceAfter?: boolean) { this.text = text; - this.position = position; + this.range = range; this.whitespaceBefore = whitespaceBefore; this.whitespaceAfter = whitespaceAfter; } From 9af036b8274d6cbbe488faa19997bbd25d48ddde Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 13 Jan 2021 19:38:10 +0800 Subject: [PATCH 24/36] Fix comments --- src/vs/vscode.proposed.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5e8463ccb5db3..572fe27ceed61 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1997,7 +1997,7 @@ declare module 'vscode' { */ text: string; /** - * The position of the hint. + * The range of the hint. */ range: Range; /** From fcc00b29f5e2d5265f76e3881a5a057787bb8651 Mon Sep 17 00:00:00 2001 From: Wenlu Wang Date: Mon, 18 Jan 2021 17:24:23 +0800 Subject: [PATCH 25/36] Avoid ts changes (#2) * Add ts server for inline hints * Add some feature related configure * Add more config * Rename all options * Support range * Add new contribution point to allow specific context menu of inline hints. * Fix filename * Pass range into command * Support trigger position and prefix/postfix * Support hover message and contextValue * Add event listener * Avoid demo command * Avoid ts changes --- src/vs/editor/common/modes.ts | 8 +- .../inlineHints/inlineHintsContribution.ts | 130 ++++++++++++++++++ .../inlineHints/inlineHintsController.ts | 64 +++++++-- src/vs/editor/editor.all.ts | 2 +- src/vs/monaco.d.ts | 6 + src/vs/platform/actions/common/actions.ts | 1 + src/vs/vscode.proposed.d.ts | 33 ++++- .../api/browser/mainThreadLanguageFeatures.ts | 21 ++- .../workbench/api/common/extHost.protocol.ts | 14 +- .../api/common/extHostLanguageFeatures.ts | 13 +- .../api/common/extHostTypeConverters.ts | 10 ++ src/vs/workbench/api/common/extHostTypes.ts | 12 +- .../api/common/menusExtensionPoint.ts | 5 + 13 files changed, 294 insertions(+), 25 deletions(-) create mode 100644 src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index f65b17c31b001..405432b1237ee 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { Position } from 'vs/editor/common/core/position'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; @@ -1662,11 +1662,17 @@ export interface CodeLensProvider { export interface InlineHint { text: string; range: IRange; + triggerPosition: IPosition; + prefix?: string; + postfix?: string; + contextValue?: string; + hoverMessage?: string; whitespaceBefore?: boolean; whitespaceAfter?: boolean; } export interface InlineHintsProvider { + onDidChangeInlineHints?: Event | undefined; provideInlineHints(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult; } diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts b/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts new file mode 100644 index 0000000000000..cfc76319eb4c0 --- /dev/null +++ b/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { InlineHintsController } from 'vs/editor/contrib/inlineHints/inlineHintsController'; +import { Disposable, dispose } from 'vs/base/common/lifecycle'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { MenuId, IMenuService, IMenu } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IAction } from 'vs/base/common/actions'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IPosition } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { ModesHoverController } from 'vs/editor/contrib/hover/hover'; +import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; + +interface InlineHintsMenuCommandArgs { + range: Range; + triggerPosition: IPosition; + contextValue?: string; +} + +export class InlineHintsContribution extends Disposable implements IEditorContribution { + + public static readonly ID: string = 'editor.contrib.inlineHintsContribution'; + + private menu: IMenu; + + constructor(private readonly _editor: ICodeEditor, + @IMenuService private readonly _menuService: IMenuService, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + ) { + super(); + this.menu = this._menuService.createMenu(MenuId.InlineHintContext, this._contextKeyService); + this._register(this.menu); + this._register(this._editor.onContextMenu(e => this.onContextMenu(e))); + this._register(this._editor.onMouseMove(e => this.onMouseMove(e))); + } + + dispose(): void { + super.dispose(); + } + + private getTriggerDecorationAndMetadataFromEvent(mouseEvent: IEditorMouseEvent) { + const targetType = mouseEvent.target.type; + + if (targetType !== MouseTargetType.CONTENT_TEXT) { + return; + } + + const isInlineHintsClassName = (className: string | null | undefined) => !!className?.startsWith('ced-inlineHints'); + const hoverOnInlineHints = [...mouseEvent.target.element?.classList.values() || []].find(isInlineHintsClassName); + if (!hoverOnInlineHints) { + return; + } + + const mouseRange = mouseEvent.target.range; + if (!mouseRange) { + return; + } + + const model = this._editor.getModel(); + if (!model) { + return; + } + + const decorations = model.getDecorationsInRange(mouseRange); + if (!decorations?.length) { + return; + } + + const currentDecoration = decorations.find(decoration => isInlineHintsClassName(decoration.options.beforeContentClassName)); + if (!currentDecoration) { + return; + } + + const inlineHintsController = this._editor.getContribution(InlineHintsController.ID); + if (!InlineHintsController) { + return; + } + + const metadata = inlineHintsController.getMetadata(currentDecoration.id); + if (!metadata) { + return; + } + + return [currentDecoration, metadata] as const; + } + + private onMouseMove(mouseEvent: IEditorMouseEvent) { + const decorationAndMetadata = this.getTriggerDecorationAndMetadataFromEvent(mouseEvent); + if (!decorationAndMetadata) { + return; + } + + const [currentDecoration] = decorationAndMetadata; + const hoverController = this._editor.getContribution(ModesHoverController.ID); + hoverController.showContentHover(currentDecoration.range, HoverStartMode.Immediate, false); + } + + private onContextMenu(mouseEvent: IEditorMouseEvent) { + const decorationAndMetadata = this.getTriggerDecorationAndMetadataFromEvent(mouseEvent); + if (!decorationAndMetadata) { + return; + } + + const [currentDecoration, metadata] = decorationAndMetadata; + + const actions: IAction[] = []; + const arg: InlineHintsMenuCommandArgs = { + range: currentDecoration.range, + triggerPosition: metadata.triggerPosition, + contextValue: metadata.contextValue || null! + }; + + const actionsDisposable = createAndFillInContextMenuActions(this.menu, { arg, shouldForwardArgs: false }, actions); + const anchor = { x: mouseEvent.event.posx, width: 0, y: mouseEvent.event.posy, height: 0 }; + this._contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => actions, + onHide: () => dispose(actionsDisposable) + }); + } +} + +registerEditorContribution(InlineHintsContribution.ID, InlineHintsContribution); diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 1fcc1c2d62567..43aae6d671811 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -6,7 +6,7 @@ import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { hash } from 'vs/base/common/hash'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -21,6 +21,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Range } from 'vs/editor/common/core/range'; import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; +import { IPosition } from 'vs/editor/common/core/position'; +import { MarkdownString } from 'vs/base/common/htmlContent'; const MAX_DECORATORS = 500; @@ -29,6 +31,11 @@ export interface InlineHintsData { provider: InlineHintsProvider; } +export interface InlineHintsMetadata { + triggerPosition: IPosition; + contextValue?: string; +} + export function getInlineHints(model: ITextModel, ranges: Range[], token: CancellationToken): Promise { const datas: InlineHintsData[] = []; const providers = InlineHintsProviderRegistry.ordered(model).reverse(); @@ -41,18 +48,21 @@ export function getInlineHints(model: ITextModel, ranges: Range[], token: Cancel return Promise.all(promises).then(() => datas); } -export class InlineHintsDetector extends Disposable implements IEditorContribution { +export class InlineHintsController extends Disposable implements IEditorContribution { static readonly ID: string = 'editor.contrib.InlineHints'; private readonly _localToDispose = this._register(new DisposableStore()); private _decorationsIds: string[] = []; private _hintsDatas = new Map(); + private _inlineHintsMetadataMap = new Map(); private _hintsDecoratorIds: string[] = []; private readonly _decorationsTypes = new Set(); private _isEnabled: boolean; private _getInlineHintsPromise: CancelablePromise | undefined; private readonly _getInlineHintsDelays = new LanguageFeatureRequestDelays(InlineHintsProviderRegistry, 250, 2500); + private _inlineHintsProvidersChangeListeners: IDisposable[] = []; + constructor(private readonly _editor: ICodeEditor, @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IThemeService private readonly _themeService: IThemeService, @@ -64,10 +74,8 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi })); this._register(_editor.onDidChangeModelLanguage(() => this._onModelChanged())); this._register(InlineHintsProviderRegistry.onDidChange(() => this._onModelChanged())); - this._register(_editor.onDidChangeConfiguration(() => { - let prevIsEnabled = this._isEnabled; - this._isEnabled = this.isEnabled(); - if (prevIsEnabled !== this._isEnabled) { + this._register(_editor.onDidChangeConfiguration((e) => { + if (e.hasChanged(EditorOption.inlineHints)) { if (this._isEnabled) { this._onModelChanged(); } else { @@ -92,8 +100,8 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi return this._editor.getOption(EditorOption.inlineHints).enabled; } - static get(editor: ICodeEditor): InlineHintsDetector { - return editor.getContribution(this.ID); + static get(editor: ICodeEditor): InlineHintsController { + return editor.getContribution(this.ID); } dispose(): void { @@ -102,6 +110,10 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi super.dispose(); } + getMetadata(id: string) { + return this._inlineHintsMetadataMap.get(id); + } + private _onModelChanged(): void { this._stop(); @@ -136,6 +148,17 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi }, this._getInlineHintsDelays.get(model)); this._localToDispose.add(scheduler); + + const bindInlineHintsChangeListeners = () => { + dispose(this._inlineHintsProvidersChangeListeners); + this._inlineHintsProvidersChangeListeners = []; + for (const provider of InlineHintsProviderRegistry.all(model)) { + if (typeof provider.onDidChangeInlineHints === 'function') { + this._inlineHintsProvidersChangeListeners.push(provider.onDidChangeInlineHints(() => scheduler.schedule())); + } + } + }; + bindInlineHintsChangeListeners(); this._localToDispose.add(this._editor.onDidChangeModelContent(() => { scheduler.schedule(); })); @@ -171,7 +194,8 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi private _updateHintsDecorators(hintsData: InlineHintsData[]): void { let decorations: IModelDeltaDecoration[] = []; - let newDecorationsTypes: { [key: string]: boolean } = {}; + const newDecorationsTypes: { [key: string]: boolean } = {}; + const newHintsMetadats: InlineHintsMetadata[] = []; const { fontSize, fontFamily } = this._getLayoutInfo(); const backgroundColor = this._themeService.getColorTheme().getColor(editorInlineHintBackground); const fontColor = this._themeService.getColorTheme().getColor(editorInlineHintForeground); @@ -179,7 +203,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi for (let i = 0; i < hintsData.length; i++) { const hint = hintsData[i].list; for (let j = 0; j < hint.length && decorations.length < MAX_DECORATORS; j++) { - const { text, range, whitespaceBefore, whitespaceAfter } = hint[j]; + const { text, range, triggerPosition, prefix = '', postfix = '', contextValue, hoverMessage, whitespaceBefore, whitespaceAfter } = hint[j]; const marginBefore = whitespaceBefore ? fontSize / 3 : 0; const marginAfter = whitespaceAfter ? fontSize / 3 : 0; @@ -189,7 +213,7 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi if (!this._decorationsTypes.has(key) && !newDecorationsTypes[key]) { this._codeEditorService.registerDecorationType(key, { before: { - contentText: text, + contentText: `${prefix}${text}${postfix}`, backgroundColor: `${backgroundColor}`, color: `${fontColor}`, margin: `0px ${marginAfter}px 0px ${marginBefore}px`, @@ -201,6 +225,11 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi } newDecorationsTypes[key] = true; + const options = this._codeEditorService.resolveDecorationOptions(key, true); + if (hoverMessage) { + options.hoverMessage = new MarkdownString().appendText(hoverMessage); + } + decorations.push({ range: { startLineNumber: range.startLineNumber, @@ -208,7 +237,11 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi endLineNumber: range.endLineNumber, endColumn: range.endColumn }, - options: this._codeEditorService.resolveDecorationOptions(key, true) + options + }); + newHintsMetadats.push({ + triggerPosition, + contextValue }); } } @@ -220,6 +253,11 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi }); this._hintsDecoratorIds = this._editor.deltaDecorations(this._hintsDecoratorIds, decorations); + const newInlineHintsMetadataMap = new Map(); + this._hintsDecoratorIds.forEach((decorationId, idx) => { + newInlineHintsMetadataMap.set(decorationId, newHintsMetadats[idx]); + }); + this._inlineHintsMetadataMap = newInlineHintsMetadataMap; } private _getLayoutInfo() { @@ -243,4 +281,4 @@ export class InlineHintsDetector extends Disposable implements IEditorContributi } } -registerEditorContribution(InlineHintsDetector.ID, InlineHintsDetector); +registerEditorContribution(InlineHintsController.ID, InlineHintsController); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index dcbdf97a7595f..2d8d9af65ee76 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -46,7 +46,7 @@ import 'vs/editor/contrib/viewportSemanticTokens/viewportSemanticTokens'; import 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import 'vs/editor/contrib/wordOperations/wordOperations'; import 'vs/editor/contrib/wordPartOperations/wordPartOperations'; -import 'vs/editor/contrib/inlineHints/inlineHintsController'; +import 'vs/editor/contrib/inlineHints/inlineHintsContribution'; // Load up these strings even in VSCode, even if they are not used // in order to get them translated diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index aae108b813cd7..f55c0f16a94d6 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6406,11 +6406,17 @@ declare namespace monaco.languages { export interface InlineHint { text: string; range: IRange; + triggerPosition: IPosition; + prefix?: string; + postfix?: string; + contextValue?: string; + hoverMessage?: string; whitespaceBefore?: boolean; whitespaceAfter?: boolean; } export interface InlineHintsProvider { + onDidChangeInlineHints?: IEvent | undefined; provideInlineHints(model: editor.ITextModel, range: Range, token: CancellationToken): ProviderResult; } diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index ec818b2bb31ec..1ae73f445a2c5 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -148,6 +148,7 @@ export class MenuId { static readonly AccountsContext = new MenuId('AccountsContext'); static readonly PanelTitle = new MenuId('PanelTitle'); static readonly PanelTitleContext = new MenuId('PanelTitleContext'); + static readonly InlineHintContext = new MenuId('InlineHintContext'); readonly id: number; readonly _debugName: string; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 572fe27ceed61..4c8ce36135a73 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2000,6 +2000,26 @@ declare module 'vscode' { * The range of the hint. */ range: Range; + /** + * The position of hint trigger. + */ + triggerPosition: Position; + /** + * Prefix of hint text. + */ + prefix?: string; + /** + * Postfix of hint text. + */ + postfix?: string; + /** + * Context for the hint. + */ + contextValue?: string; + /** + * Tooltip when hover on the hint. + */ + hoverMessage?: string; /** * Whitespace before the hint. */ @@ -2014,10 +2034,15 @@ declare module 'vscode' { * * @param text The text of the hint. * @param range The range of the hint. + * @param triggerPosition The position of hint trigger. + * @param prefix Prefix of hint text. + * @param postfix Postfix of hint text. + * @param contextValue Context for the hint. + * @param hoverInfo Tooltip when hover on the hint. * @param whitespaceBefore Whitespace before the hint. * @param whitespaceAfter TWhitespace after the hint. */ - constructor(text: string, range: Range, whitespaceBefore?: boolean, whitespaceAfter?: boolean); + constructor(text: string, range: Range, triggerPosition: Position, prefix?: string, postfix?: string, contextValue?: string, hoverInfo?: string, whitespaceBefore?: boolean, whitespaceAfter?: boolean); } /** @@ -2025,6 +2050,12 @@ declare module 'vscode' { * the inline hints feature. */ export interface InlineHintsProvider { + + /** + * An optional event to signal that inline hints have changed. + * @see [EventEmitter](#EventEmitter) + */ + onDidChangeInlineHints?: Event; /** * @param model The document in which the command was invoked. * @param token A cancellation token. diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 2ef2c83bcea76..ba619a53f6bdc 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -499,13 +499,28 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- inline hints - $registerInlineHintsProvider(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, modes.InlineHintsProviderRegistry.register(selector, { + $registerInlineHintsProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { + const provider = { provideInlineHints: async (model: ITextModel, range: EditorRange, token: CancellationToken): Promise => { const result = await this._proxy.$provideInlineHints(handle, model.uri, range, token); return result?.hints; } - })); + }; + + if (typeof eventHandle === 'number') { + const emitter = new Emitter(); + this._registrations.set(eventHandle, emitter); + provider.onDidChangeInlineHints = emitter.event; + } + + this._registrations.set(handle, modes.InlineHintsProviderRegistry.register(selector, provider)); + } + + $emitInlineHintsEvent(eventHandle: number, event?: any): void { + const obj = this._registrations.get(eventHandle); + if (obj instanceof Emitter) { + obj.fire(event); + } } // --- links diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b0144d0f78110..1cfe5be69cfc7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -401,7 +401,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void; $registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; - $registerInlineHintsProvider(handle: number, selector: IDocumentFilterDto[]): void; + $registerInlineHintsProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; + $emitInlineHintsEvent(eventHandle: number, event?: any): void; $registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void; $registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; @@ -1287,8 +1288,15 @@ export interface ISignatureHelpContextDto { } export interface IInlineHintDto { - text: string - range: IRange + text: string; + range: IRange; + triggerPosition: IPosition; + prefix?: string; + postfix?: string; + contextValue?: string; + hoverMessage?: string; + whitespaceBefore?: boolean; + whitespaceAfter?: boolean; } export interface IInlineHintsDto { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 3fe5e063c0d86..254db1a4991cf 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1787,9 +1787,18 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- inline hints registerInlineHintsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineHintsProvider): vscode.Disposable { + + const eventHandle = typeof provider.onDidChangeInlineHints === 'function' ? this._nextHandle() : undefined; const handle = this._addNewAdapter(new InlineHintsAdapter(this._documents, provider), extension); - this._proxy.$registerInlineHintsProvider(handle, this._transformDocumentSelector(selector)); - return this._createDisposable(handle); + + this._proxy.$registerInlineHintsProvider(handle, this._transformDocumentSelector(selector), eventHandle); + let result = this._createDisposable(handle); + + if (eventHandle !== undefined) { + const subscription = provider.onDidChangeInlineHints!(_ => this._proxy.$emitInlineHintsEvent(eventHandle)); + result = Disposable.from(result, subscription); + } + return result; } $provideInlineHints(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index e0d50f643bf3e..8ba349e9202ec 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1024,6 +1024,11 @@ export namespace InlineHint { return { text: hint.text, range: Range.from(hint.range), + triggerPosition: Position.from(hint.triggerPosition), + prefix: hint.prefix, + postfix: hint.postfix, + contextValue: hint.contextValue, + hoverMessage: hint.hoverMessage, whitespaceBefore: hint.whitespaceBefore, whitespaceAfter: hint.whitespaceAfter }; @@ -1033,6 +1038,11 @@ export namespace InlineHint { return { text: hint.text, range: Range.to(hint.range), + triggerPosition: Position.to(hint.triggerPosition), + prefix: hint.prefix, + postfix: hint.postfix, + contextValue: hint.contextValue, + hoverMessage: hint.hoverMessage, whitespaceBefore: hint.whitespaceBefore, whitespaceAfter: hint.whitespaceAfter }; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4e8fe79acfdb9..0cb0a690f66be 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1377,12 +1377,22 @@ export enum SignatureHelpTriggerKind { export class InlineHint { text: string; range: Range; + triggerPosition: Position; + prefix?: string; + postfix?: string; + contextValue?: string; + hoverMessage?: string; whitespaceBefore?: boolean; whitespaceAfter?: boolean; - constructor(text: string, range: Range, whitespaceBefore?: boolean, whitespaceAfter?: boolean) { + constructor(text: string, range: Range, triggerPosition: Position, prefix?: string, postfix?: string, contextValue?: string, hoverMessage?: string, whitespaceBefore?: boolean, whitespaceAfter?: boolean) { this.text = text; this.range = range; + this.triggerPosition = triggerPosition; + this.prefix = prefix; + this.postfix = postfix; + this.contextValue = contextValue; + this.hoverMessage = hoverMessage; this.whitespaceBefore = whitespaceBefore; this.whitespaceAfter = whitespaceAfter; } diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 32027cbe2528b..4327328b40223 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -176,6 +176,11 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.TimelineItemContext, description: localize('view.timelineContext', "The Timeline view item context menu") }, + { + key: 'inlineHints/context', + id: MenuId.InlineHintContext, + description: localize('menus.inlineHintsContext', "The inline hints context menu") + } ]; namespace schema { From ebac10e0a5651544e014ac8fd2e07104a771847c Mon Sep 17 00:00:00 2001 From: Wenlu Wang Date: Mon, 18 Jan 2021 17:39:31 +0800 Subject: [PATCH 26/36] Avoid conflict error --- src/vs/platform/actions/common/actions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 709183d04f463..10d9371243c12 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -148,7 +148,6 @@ export class MenuId { static readonly TimelineTitleContext = new MenuId('TimelineTitleContext'); static readonly AccountsContext = new MenuId('AccountsContext'); static readonly PanelTitle = new MenuId('PanelTitle'); - static readonly PanelTitleContext = new MenuId('PanelTitleContext'); static readonly InlineHintContext = new MenuId('InlineHintContext'); readonly id: number; From 1c131cf2657b4d27955b4a4af68eb0580a3fcc5f Mon Sep 17 00:00:00 2001 From: Wenlu Wang Date: Mon, 18 Jan 2021 17:48:43 +0800 Subject: [PATCH 27/36] Avoid extra fields --- src/vs/editor/common/modes.ts | 6 +-- .../inlineHints/inlineHintsContribution.ts | 37 ++++--------------- .../inlineHints/inlineHintsController.ts | 25 +------------ src/vs/monaco.d.ts | 4 -- src/vs/vscode.proposed.d.ts | 25 ++----------- .../workbench/api/common/extHost.protocol.ts | 4 -- .../api/common/extHostTypeConverters.ts | 8 ---- src/vs/workbench/api/common/extHostTypes.ts | 10 +---- 8 files changed, 15 insertions(+), 104 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 405432b1237ee..51353fcdea526 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IPosition, Position } from 'vs/editor/common/core/position'; +import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; @@ -1662,10 +1662,6 @@ export interface CodeLensProvider { export interface InlineHint { text: string; range: IRange; - triggerPosition: IPosition; - prefix?: string; - postfix?: string; - contextValue?: string; hoverMessage?: string; whitespaceBefore?: boolean; whitespaceAfter?: boolean; diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts b/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts index cfc76319eb4c0..e7943d317d6bd 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts @@ -2,7 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { InlineHintsController } from 'vs/editor/contrib/inlineHints/inlineHintsController'; +import 'vs/editor/contrib/inlineHints/inlineHintsController'; + import { Disposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -12,15 +13,12 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction } from 'vs/base/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IPosition } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ModesHoverController } from 'vs/editor/contrib/hover/hover'; import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; interface InlineHintsMenuCommandArgs { range: Range; - triggerPosition: IPosition; - contextValue?: string; } export class InlineHintsContribution extends Disposable implements IEditorContribution { @@ -45,7 +43,7 @@ export class InlineHintsContribution extends Disposable implements IEditorContri super.dispose(); } - private getTriggerDecorationAndMetadataFromEvent(mouseEvent: IEditorMouseEvent) { + private getTriggerDecorationFromEvent(mouseEvent: IEditorMouseEvent) { const targetType = mouseEvent.target.type; if (targetType !== MouseTargetType.CONTENT_TEXT) { @@ -73,48 +71,29 @@ export class InlineHintsContribution extends Disposable implements IEditorContri return; } - const currentDecoration = decorations.find(decoration => isInlineHintsClassName(decoration.options.beforeContentClassName)); - if (!currentDecoration) { - return; - } - - const inlineHintsController = this._editor.getContribution(InlineHintsController.ID); - if (!InlineHintsController) { - return; - } - - const metadata = inlineHintsController.getMetadata(currentDecoration.id); - if (!metadata) { - return; - } - - return [currentDecoration, metadata] as const; + return decorations.find(decoration => isInlineHintsClassName(decoration.options.beforeContentClassName)); } private onMouseMove(mouseEvent: IEditorMouseEvent) { - const decorationAndMetadata = this.getTriggerDecorationAndMetadataFromEvent(mouseEvent); + const decorationAndMetadata = this.getTriggerDecorationFromEvent(mouseEvent); if (!decorationAndMetadata) { return; } - const [currentDecoration] = decorationAndMetadata; + const currentDecoration = decorationAndMetadata; const hoverController = this._editor.getContribution(ModesHoverController.ID); hoverController.showContentHover(currentDecoration.range, HoverStartMode.Immediate, false); } private onContextMenu(mouseEvent: IEditorMouseEvent) { - const decorationAndMetadata = this.getTriggerDecorationAndMetadataFromEvent(mouseEvent); + const decorationAndMetadata = this.getTriggerDecorationFromEvent(mouseEvent); if (!decorationAndMetadata) { return; } - const [currentDecoration, metadata] = decorationAndMetadata; - const actions: IAction[] = []; const arg: InlineHintsMenuCommandArgs = { - range: currentDecoration.range, - triggerPosition: metadata.triggerPosition, - contextValue: metadata.contextValue || null! + range: decorationAndMetadata.range }; const actionsDisposable = createAndFillInContextMenuActions(this.menu, { arg, shouldForwardArgs: false }, actions); diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 43aae6d671811..5a25e3bab967b 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -21,7 +21,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Range } from 'vs/editor/common/core/range'; import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; -import { IPosition } from 'vs/editor/common/core/position'; import { MarkdownString } from 'vs/base/common/htmlContent'; const MAX_DECORATORS = 500; @@ -31,11 +30,6 @@ export interface InlineHintsData { provider: InlineHintsProvider; } -export interface InlineHintsMetadata { - triggerPosition: IPosition; - contextValue?: string; -} - export function getInlineHints(model: ITextModel, ranges: Range[], token: CancellationToken): Promise { const datas: InlineHintsData[] = []; const providers = InlineHintsProviderRegistry.ordered(model).reverse(); @@ -54,7 +48,6 @@ export class InlineHintsController extends Disposable implements IEditorContribu private readonly _localToDispose = this._register(new DisposableStore()); private _decorationsIds: string[] = []; private _hintsDatas = new Map(); - private _inlineHintsMetadataMap = new Map(); private _hintsDecoratorIds: string[] = []; private readonly _decorationsTypes = new Set(); private _isEnabled: boolean; @@ -110,10 +103,6 @@ export class InlineHintsController extends Disposable implements IEditorContribu super.dispose(); } - getMetadata(id: string) { - return this._inlineHintsMetadataMap.get(id); - } - private _onModelChanged(): void { this._stop(); @@ -195,7 +184,6 @@ export class InlineHintsController extends Disposable implements IEditorContribu private _updateHintsDecorators(hintsData: InlineHintsData[]): void { let decorations: IModelDeltaDecoration[] = []; const newDecorationsTypes: { [key: string]: boolean } = {}; - const newHintsMetadats: InlineHintsMetadata[] = []; const { fontSize, fontFamily } = this._getLayoutInfo(); const backgroundColor = this._themeService.getColorTheme().getColor(editorInlineHintBackground); const fontColor = this._themeService.getColorTheme().getColor(editorInlineHintForeground); @@ -203,7 +191,7 @@ export class InlineHintsController extends Disposable implements IEditorContribu for (let i = 0; i < hintsData.length; i++) { const hint = hintsData[i].list; for (let j = 0; j < hint.length && decorations.length < MAX_DECORATORS; j++) { - const { text, range, triggerPosition, prefix = '', postfix = '', contextValue, hoverMessage, whitespaceBefore, whitespaceAfter } = hint[j]; + const { text, range, hoverMessage, whitespaceBefore, whitespaceAfter } = hint[j]; const marginBefore = whitespaceBefore ? fontSize / 3 : 0; const marginAfter = whitespaceAfter ? fontSize / 3 : 0; @@ -213,7 +201,7 @@ export class InlineHintsController extends Disposable implements IEditorContribu if (!this._decorationsTypes.has(key) && !newDecorationsTypes[key]) { this._codeEditorService.registerDecorationType(key, { before: { - contentText: `${prefix}${text}${postfix}`, + contentText: text, backgroundColor: `${backgroundColor}`, color: `${fontColor}`, margin: `0px ${marginAfter}px 0px ${marginBefore}px`, @@ -239,10 +227,6 @@ export class InlineHintsController extends Disposable implements IEditorContribu }, options }); - newHintsMetadats.push({ - triggerPosition, - contextValue - }); } } @@ -253,11 +237,6 @@ export class InlineHintsController extends Disposable implements IEditorContribu }); this._hintsDecoratorIds = this._editor.deltaDecorations(this._hintsDecoratorIds, decorations); - const newInlineHintsMetadataMap = new Map(); - this._hintsDecoratorIds.forEach((decorationId, idx) => { - newInlineHintsMetadataMap.set(decorationId, newHintsMetadats[idx]); - }); - this._inlineHintsMetadataMap = newInlineHintsMetadataMap; } private _getLayoutInfo() { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index f55c0f16a94d6..1c0703a1c3cd0 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6406,10 +6406,6 @@ declare namespace monaco.languages { export interface InlineHint { text: string; range: IRange; - triggerPosition: IPosition; - prefix?: string; - postfix?: string; - contextValue?: string; hoverMessage?: string; whitespaceBefore?: boolean; whitespaceAfter?: boolean; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index ea5e2697bf959..45f9ee22eb50f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1976,22 +1976,6 @@ declare module 'vscode' { * The range of the hint. */ range: Range; - /** - * The position of hint trigger. - */ - triggerPosition: Position; - /** - * Prefix of hint text. - */ - prefix?: string; - /** - * Postfix of hint text. - */ - postfix?: string; - /** - * Context for the hint. - */ - contextValue?: string; /** * Tooltip when hover on the hint. */ @@ -2010,15 +1994,11 @@ declare module 'vscode' { * * @param text The text of the hint. * @param range The range of the hint. - * @param triggerPosition The position of hint trigger. - * @param prefix Prefix of hint text. - * @param postfix Postfix of hint text. - * @param contextValue Context for the hint. - * @param hoverInfo Tooltip when hover on the hint. + * @param hoverMessage Tooltip when hover on the hint. * @param whitespaceBefore Whitespace before the hint. * @param whitespaceAfter TWhitespace after the hint. */ - constructor(text: string, range: Range, triggerPosition: Position, prefix?: string, postfix?: string, contextValue?: string, hoverInfo?: string, whitespaceBefore?: boolean, whitespaceAfter?: boolean); + constructor(text: string, range: Range, hoverMessage?: string, whitespaceBefore?: boolean, whitespaceAfter?: boolean); } /** @@ -2526,4 +2506,5 @@ declare module 'vscode' { } //#endregion + } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 86c394bd34297..79b2c4d1cd815 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1292,10 +1292,6 @@ export interface ISignatureHelpContextDto { export interface IInlineHintDto { text: string; range: IRange; - triggerPosition: IPosition; - prefix?: string; - postfix?: string; - contextValue?: string; hoverMessage?: string; whitespaceBefore?: boolean; whitespaceAfter?: boolean; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 8ba349e9202ec..1200911d09c00 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1024,10 +1024,6 @@ export namespace InlineHint { return { text: hint.text, range: Range.from(hint.range), - triggerPosition: Position.from(hint.triggerPosition), - prefix: hint.prefix, - postfix: hint.postfix, - contextValue: hint.contextValue, hoverMessage: hint.hoverMessage, whitespaceBefore: hint.whitespaceBefore, whitespaceAfter: hint.whitespaceAfter @@ -1038,10 +1034,6 @@ export namespace InlineHint { return { text: hint.text, range: Range.to(hint.range), - triggerPosition: Position.to(hint.triggerPosition), - prefix: hint.prefix, - postfix: hint.postfix, - contextValue: hint.contextValue, hoverMessage: hint.hoverMessage, whitespaceBefore: hint.whitespaceBefore, whitespaceAfter: hint.whitespaceAfter diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index d451f74e22d19..3f626f4e4d6a1 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1377,21 +1377,13 @@ export enum SignatureHelpTriggerKind { export class InlineHint { text: string; range: Range; - triggerPosition: Position; - prefix?: string; - postfix?: string; - contextValue?: string; hoverMessage?: string; whitespaceBefore?: boolean; whitespaceAfter?: boolean; - constructor(text: string, range: Range, triggerPosition: Position, prefix?: string, postfix?: string, contextValue?: string, hoverMessage?: string, whitespaceBefore?: boolean, whitespaceAfter?: boolean) { + constructor(text: string, range: Range, hoverMessage?: string, whitespaceBefore?: boolean, whitespaceAfter?: boolean) { this.text = text; this.range = range; - this.triggerPosition = triggerPosition; - this.prefix = prefix; - this.postfix = postfix; - this.contextValue = contextValue; this.hoverMessage = hoverMessage; this.whitespaceBefore = whitespaceBefore; this.whitespaceAfter = whitespaceAfter; From ae67879ed5d3ffb43810fd0e485d720271fd0707 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 16:14:21 +0100 Subject: [PATCH 28/36] don't propose new API on ThemableDecorationAttachmentRenderOptions --- src/vs/vscode.proposed.d.ts | 15 --------------- .../workbench/api/common/extHostTypeConverters.ts | 3 --- 2 files changed, 18 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 89820bd682124..c599b1487adb0 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1962,21 +1962,6 @@ declare module 'vscode' { export function registerInlineHintsProvider(selector: DocumentSelector, provider: InlineHintsProvider): Disposable; } - export interface ThemableDecorationAttachmentRenderOptions { - /** - * CSS styling property that will be applied to the decoration attachment. - */ - fontSize?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - fontFamily?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - padding?: string; - } - /** * Inline hint information. */ diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 1200911d09c00..345f37fbba2ed 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -394,13 +394,10 @@ export namespace ThemableDecorationAttachmentRenderOptions { borderColor: options.borderColor, fontStyle: options.fontStyle, fontWeight: options.fontWeight, - fontSize: options.fontSize, - fontFamily: options.fontFamily, textDecoration: options.textDecoration, color: options.color, backgroundColor: options.backgroundColor, margin: options.margin, - padding: options.padding, width: options.width, height: options.height, }; From 0d4bf785b6c3ed78fa37895e679c0c35abd9c6b8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 16:16:03 +0100 Subject: [PATCH 29/36] remove hover (should come via decoration) and action/menu (should be self contained if at all) --- src/vs/editor/common/modes.ts | 1 + .../inlineHints/inlineHintsContribution.ts | 109 ------------------ src/vs/editor/editor.all.ts | 2 +- src/vs/platform/actions/common/actions.ts | 1 - .../api/common/menusExtensionPoint.ts | 5 - 5 files changed, 2 insertions(+), 116 deletions(-) delete mode 100644 src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 1bad51ca94c19..28b30aadea39e 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1776,6 +1776,7 @@ export const TypeDefinitionProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts b/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts deleted file mode 100644 index e7943d317d6bd..0000000000000 --- a/src/vs/editor/contrib/inlineHints/inlineHintsContribution.ts +++ /dev/null @@ -1,109 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import 'vs/editor/contrib/inlineHints/inlineHintsController'; - -import { Disposable, dispose } from 'vs/base/common/lifecycle'; -import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { MenuId, IMenuService, IMenu } from 'vs/platform/actions/common/actions'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IAction } from 'vs/base/common/actions'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { Range } from 'vs/editor/common/core/range'; -import { ModesHoverController } from 'vs/editor/contrib/hover/hover'; -import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; - -interface InlineHintsMenuCommandArgs { - range: Range; -} - -export class InlineHintsContribution extends Disposable implements IEditorContribution { - - public static readonly ID: string = 'editor.contrib.inlineHintsContribution'; - - private menu: IMenu; - - constructor(private readonly _editor: ICodeEditor, - @IMenuService private readonly _menuService: IMenuService, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - ) { - super(); - this.menu = this._menuService.createMenu(MenuId.InlineHintContext, this._contextKeyService); - this._register(this.menu); - this._register(this._editor.onContextMenu(e => this.onContextMenu(e))); - this._register(this._editor.onMouseMove(e => this.onMouseMove(e))); - } - - dispose(): void { - super.dispose(); - } - - private getTriggerDecorationFromEvent(mouseEvent: IEditorMouseEvent) { - const targetType = mouseEvent.target.type; - - if (targetType !== MouseTargetType.CONTENT_TEXT) { - return; - } - - const isInlineHintsClassName = (className: string | null | undefined) => !!className?.startsWith('ced-inlineHints'); - const hoverOnInlineHints = [...mouseEvent.target.element?.classList.values() || []].find(isInlineHintsClassName); - if (!hoverOnInlineHints) { - return; - } - - const mouseRange = mouseEvent.target.range; - if (!mouseRange) { - return; - } - - const model = this._editor.getModel(); - if (!model) { - return; - } - - const decorations = model.getDecorationsInRange(mouseRange); - if (!decorations?.length) { - return; - } - - return decorations.find(decoration => isInlineHintsClassName(decoration.options.beforeContentClassName)); - } - - private onMouseMove(mouseEvent: IEditorMouseEvent) { - const decorationAndMetadata = this.getTriggerDecorationFromEvent(mouseEvent); - if (!decorationAndMetadata) { - return; - } - - const currentDecoration = decorationAndMetadata; - const hoverController = this._editor.getContribution(ModesHoverController.ID); - hoverController.showContentHover(currentDecoration.range, HoverStartMode.Immediate, false); - } - - private onContextMenu(mouseEvent: IEditorMouseEvent) { - const decorationAndMetadata = this.getTriggerDecorationFromEvent(mouseEvent); - if (!decorationAndMetadata) { - return; - } - - const actions: IAction[] = []; - const arg: InlineHintsMenuCommandArgs = { - range: decorationAndMetadata.range - }; - - const actionsDisposable = createAndFillInContextMenuActions(this.menu, { arg, shouldForwardArgs: false }, actions); - const anchor = { x: mouseEvent.event.posx, width: 0, y: mouseEvent.event.posy, height: 0 }; - this._contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => actions, - onHide: () => dispose(actionsDisposable) - }); - } -} - -registerEditorContribution(InlineHintsContribution.ID, InlineHintsContribution); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 2d8d9af65ee76..9b4870b45b91f 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -29,6 +29,7 @@ import 'vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition'; import 'vs/editor/contrib/gotoError/gotoError'; import 'vs/editor/contrib/hover/hover'; import 'vs/editor/contrib/indentation/indentation'; +import 'vs/editor/contrib/inlineHints/inlineHintsController'; import 'vs/editor/contrib/inPlaceReplace/inPlaceReplace'; import 'vs/editor/contrib/linesOperations/linesOperations'; import 'vs/editor/contrib/linkedEditing/linkedEditing'; @@ -46,7 +47,6 @@ import 'vs/editor/contrib/viewportSemanticTokens/viewportSemanticTokens'; import 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import 'vs/editor/contrib/wordOperations/wordOperations'; import 'vs/editor/contrib/wordPartOperations/wordPartOperations'; -import 'vs/editor/contrib/inlineHints/inlineHintsContribution'; // Load up these strings even in VSCode, even if they are not used // in order to get them translated diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 10d9371243c12..514fc0bd6009e 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -148,7 +148,6 @@ export class MenuId { static readonly TimelineTitleContext = new MenuId('TimelineTitleContext'); static readonly AccountsContext = new MenuId('AccountsContext'); static readonly PanelTitle = new MenuId('PanelTitle'); - static readonly InlineHintContext = new MenuId('InlineHintContext'); readonly id: number; readonly _debugName: string; diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 4327328b40223..046cac1905094 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -175,11 +175,6 @@ const apiMenus: IAPIMenu[] = [ key: 'timeline/item/context', id: MenuId.TimelineItemContext, description: localize('view.timelineContext', "The Timeline view item context menu") - }, - { - key: 'inlineHints/context', - id: MenuId.InlineHintContext, - description: localize('menus.inlineHintsContext', "The inline hints context menu") } ]; From 1981776d0f9da360983a17a58f48b740ac15be34 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 16:53:15 +0100 Subject: [PATCH 30/36] less state inside InlineHintsController-type, only have one type of decorations, and much :lipstick: --- .../inlineHints/inlineHintsController.ts | 168 ++++++------------ 1 file changed, 59 insertions(+), 109 deletions(-) diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 5a25e3bab967b..d2177712e8554 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -3,21 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { hash } from 'vs/base/common/hash'; -import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { InlineHintsProvider, InlineHintsProviderRegistry, InlineHint } from 'vs/editor/common/modes'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { flatten } from 'vs/base/common/arrays'; import { editorInlineHintForeground, editorInlineHintBackground } from 'vs/platform/theme/common/colorRegistry'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Range } from 'vs/editor/common/core/range'; import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; @@ -30,155 +29,107 @@ export interface InlineHintsData { provider: InlineHintsProvider; } -export function getInlineHints(model: ITextModel, ranges: Range[], token: CancellationToken): Promise { +export async function getInlineHints(model: ITextModel, ranges: Range[], token: CancellationToken): Promise { const datas: InlineHintsData[] = []; const providers = InlineHintsProviderRegistry.ordered(model).reverse(); const promises = flatten(providers.map(provider => ranges.map(range => Promise.resolve(provider.provideInlineHints(model, range, token)).then(result => { if (result) { datas.push({ list: result, provider }); } + }, err => { + onUnexpectedExternalError(err); })))); - return Promise.all(promises).then(() => datas); + await Promise.all(promises); + + return datas; } -export class InlineHintsController extends Disposable implements IEditorContribution { +export class InlineHintsController implements IEditorContribution { static readonly ID: string = 'editor.contrib.InlineHints'; - private readonly _localToDispose = this._register(new DisposableStore()); - private _decorationsIds: string[] = []; - private _hintsDatas = new Map(); - private _hintsDecoratorIds: string[] = []; - private readonly _decorationsTypes = new Set(); - private _isEnabled: boolean; - private _getInlineHintsPromise: CancelablePromise | undefined; + + // static get(editor: ICodeEditor): InlineHintsController { + // return editor.getContribution(this.ID); + // } + + private readonly _disposables = new DisposableStore(); + private readonly _sessionDisposables = new DisposableStore(); + private readonly _getInlineHintsDelays = new LanguageFeatureRequestDelays(InlineHintsProviderRegistry, 250, 2500); + private readonly _decorationsTypes = new Set(); - private _inlineHintsProvidersChangeListeners: IDisposable[] = []; + private _decorationIds: string[] = []; constructor(private readonly _editor: ICodeEditor, @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IThemeService private readonly _themeService: IThemeService, ) { - super(); - this._register(_editor.onDidChangeModel(() => { - this._isEnabled = this.isEnabled(); - this._onModelChanged(); - })); - this._register(_editor.onDidChangeModelLanguage(() => this._onModelChanged())); - this._register(InlineHintsProviderRegistry.onDidChange(() => this._onModelChanged())); - this._register(_editor.onDidChangeConfiguration((e) => { + this._disposables.add(InlineHintsProviderRegistry.onDidChange(() => this._update())); + this._disposables.add(_editor.onDidChangeModel(() => this._update())); + this._disposables.add(_editor.onDidChangeModelLanguage(() => this._update())); + this._disposables.add(_editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.inlineHints)) { - if (this._isEnabled) { - this._onModelChanged(); - } else { - this._removeAllDecorations(); - } + this._update(); } })); - this._register(_editor.onDidScrollChange(() => { - this._onModelChanged(); - })); - - this._isEnabled = this.isEnabled(); - this._onModelChanged(); - } - - isEnabled(): boolean { - const model = this._editor.getModel(); - if (!model) { - return false; - } - - return this._editor.getOption(EditorOption.inlineHints).enabled; - } - static get(editor: ICodeEditor): InlineHintsController { - return editor.getContribution(this.ID); + this._update(); } dispose(): void { - this._stop(); + this._sessionDisposables.dispose(); this._removeAllDecorations(); - super.dispose(); + this._disposables.dispose(); } - private _onModelChanged(): void { - this._stop(); + private _update(): void { + this._sessionDisposables.clear(); - if (!this._isEnabled) { + if (!this._editor.getOption(EditorOption.inlineHints).enabled) { + this._removeAllDecorations(); return; } - const model = this._editor.getModel(); + const model = this._editor.getModel(); if (!model || !InlineHintsProviderRegistry.has(model)) { + this._removeAllDecorations(); return; } - const scheduler = new RunOnceScheduler(() => { + const scheduler = new RunOnceScheduler(async () => { const t1 = Date.now(); - this._getInlineHintsPromise?.cancel(); - this._getInlineHintsPromise = createCancelablePromise(token => { - const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow(); - return getInlineHints(model, visibleRanges, token); - }); + const cts = new CancellationTokenSource(); + this._sessionDisposables.add(toDisposable(() => cts.dispose(true))); + + const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow(); + const result = await getInlineHints(model, visibleRanges, cts.token); - this._getInlineHintsPromise.then(result => { - // update moving average - const newDelay = this._getInlineHintsDelays.update(model, Date.now() - t1); - scheduler.delay = newDelay; + // update moving average + const newDelay = this._getInlineHintsDelays.update(model, Date.now() - t1); + scheduler.delay = newDelay; - // render hints - this._updateDecorations(result); - this._updateHintsDecorators(result); - }, onUnexpectedError); + // render hints + this._updateHintsDecorators(result); }, this._getInlineHintsDelays.get(model)); - this._localToDispose.add(scheduler); + this._sessionDisposables.add(scheduler); - const bindInlineHintsChangeListeners = () => { - dispose(this._inlineHintsProvidersChangeListeners); - this._inlineHintsProvidersChangeListeners = []; - for (const provider of InlineHintsProviderRegistry.all(model)) { - if (typeof provider.onDidChangeInlineHints === 'function') { - this._inlineHintsProvidersChangeListeners.push(provider.onDidChangeInlineHints(() => scheduler.schedule())); - } - } - }; - bindInlineHintsChangeListeners(); - this._localToDispose.add(this._editor.onDidChangeModelContent(() => { - scheduler.schedule(); - })); + // update inline hints when content or scroll position changes + this._sessionDisposables.add(this._editor.onDidChangeModelContent(() => scheduler.schedule())); + this._disposables.add(this._editor.onDidScrollChange(() => scheduler.schedule())); scheduler.schedule(); - } - private _stop(): void { - if (this._getInlineHintsPromise) { - this._getInlineHintsPromise.cancel(); - this._getInlineHintsPromise = undefined; + // update inline hints when any any provider fires an event + const providerListener = new DisposableStore(); + this._sessionDisposables.add(providerListener); + for (const provider of InlineHintsProviderRegistry.all(model)) { + if (typeof provider.onDidChangeInlineHints === 'function') { + providerListener.add(provider.onDidChangeInlineHints(() => scheduler.schedule())); + } } - this._localToDispose.clear(); - } - - private _updateDecorations(hintsData: InlineHintsData[]): void { - const decorations = flatten(hintsData.map(hints => hints.list.map(hint => { - return { - range: { - startLineNumber: hint.range.startLineNumber, - startColumn: hint.range.startColumn, - endLineNumber: hint.range.endLineNumber, - endColumn: hint.range.endColumn - }, - options: ModelDecorationOptions.EMPTY - }; - }))); - - this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations); - - this._hintsDatas = new Map(); - this._decorationsIds.forEach((id, i) => this._hintsDatas.set(id, hintsData[i])); } private _updateHintsDecorators(hintsData: InlineHintsData[]): void { @@ -236,7 +187,7 @@ export class InlineHintsController extends Disposable implements IEditorContribu } }); - this._hintsDecoratorIds = this._editor.deltaDecorations(this._hintsDecoratorIds, decorations); + this._decorationIds = this._editor.deltaDecorations(this._decorationIds, decorations); } private _getLayoutInfo() { @@ -251,8 +202,7 @@ export class InlineHintsController extends Disposable implements IEditorContribu } private _removeAllDecorations(): void { - this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []); - this._hintsDecoratorIds = this._editor.deltaDecorations(this._hintsDecoratorIds, []); + this._decorationIds = this._editor.deltaDecorations(this._decorationIds, []); this._decorationsTypes.forEach(subType => { this._codeEditorService.removeDecorationType(subType); From 5087b08c6d178404da6d1bddef8c052a394b21ac Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 17:03:49 +0100 Subject: [PATCH 31/36] :lipstick: --- src/vs/vscode.proposed.d.ts | 1 - src/vs/workbench/api/common/menusExtensionPoint.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c599b1487adb0..6b1d597fcfc5d 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2546,5 +2546,4 @@ declare module 'vscode' { } //#endregion - } diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 046cac1905094..32027cbe2528b 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -175,7 +175,7 @@ const apiMenus: IAPIMenu[] = [ key: 'timeline/item/context', id: MenuId.TimelineItemContext, description: localize('view.timelineContext', "The Timeline view item context menu") - } + }, ]; namespace schema { From 4651f66cca106b55c64ffce1ab79efe1f68db2b7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 17:28:06 +0100 Subject: [PATCH 32/36] simpler decoration type management (rely on internal ref counting) --- .../inlineHints/inlineHintsController.ts | 74 ++++++++----------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index d2177712e8554..a7bbaf5bba7fe 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -55,10 +55,9 @@ export class InlineHintsController implements IEditorContribution { private readonly _disposables = new DisposableStore(); private readonly _sessionDisposables = new DisposableStore(); - private readonly _getInlineHintsDelays = new LanguageFeatureRequestDelays(InlineHintsProviderRegistry, 250, 2500); - private readonly _decorationsTypes = new Set(); + private _decorationsTypeIds: string[] = []; private _decorationIds: string[] = []; constructor(private readonly _editor: ICodeEditor, @@ -133,61 +132,54 @@ export class InlineHintsController implements IEditorContribution { } private _updateHintsDecorators(hintsData: InlineHintsData[]): void { - let decorations: IModelDeltaDecoration[] = []; - const newDecorationsTypes: { [key: string]: boolean } = {}; const { fontSize, fontFamily } = this._getLayoutInfo(); const backgroundColor = this._themeService.getColorTheme().getColor(editorInlineHintBackground); const fontColor = this._themeService.getColorTheme().getColor(editorInlineHintForeground); - for (let i = 0; i < hintsData.length; i++) { - const hint = hintsData[i].list; - for (let j = 0; j < hint.length && decorations.length < MAX_DECORATORS; j++) { - const { text, range, hoverMessage, whitespaceBefore, whitespaceAfter } = hint[j]; + const newDecorationsTypeIds: string[] = []; + const newDecorationsData: IModelDeltaDecoration[] = []; + + for (const { list: hints } of hintsData) { + + for (let j = 0; j < hints.length && newDecorationsData.length < MAX_DECORATORS; j++) { + const { text, range, hoverMessage, whitespaceBefore, whitespaceAfter } = hints[j]; const marginBefore = whitespaceBefore ? fontSize / 3 : 0; const marginAfter = whitespaceAfter ? fontSize / 3 : 0; - const subKey = hash(text).toString(16); - let key = 'inlineHints-' + subKey; - - if (!this._decorationsTypes.has(key) && !newDecorationsTypes[key]) { - this._codeEditorService.registerDecorationType(key, { - before: { - contentText: text, - backgroundColor: `${backgroundColor}`, - color: `${fontColor}`, - margin: `0px ${marginAfter}px 0px ${marginBefore}px`, - fontSize: `${fontSize}px`, - fontFamily: fontFamily, - padding: '0px 2px' - } - }, undefined, this._editor); - } + const subKey = hash([text, marginBefore, marginAfter]).toString(16); + const key = 'inlineHints-' + subKey; + + // decoration types are ref-counted which means we only need to + // call register und remove equally often + newDecorationsTypeIds.push(key); + this._codeEditorService.registerDecorationType(key, { + before: { + contentText: text, + backgroundColor: `${backgroundColor}`, + color: `${fontColor}`, + margin: `0px ${marginAfter}px 0px ${marginBefore}px`, + fontSize: `${fontSize}px`, + fontFamily: fontFamily, + padding: '0px 2px' + } + }, undefined, this._editor); - newDecorationsTypes[key] = true; const options = this._codeEditorService.resolveDecorationOptions(key, true); if (hoverMessage) { options.hoverMessage = new MarkdownString().appendText(hoverMessage); } - decorations.push({ - range: { - startLineNumber: range.startLineNumber, - startColumn: range.startColumn, - endLineNumber: range.endLineNumber, - endColumn: range.endColumn - }, + newDecorationsData.push({ + range, options }); } } - this._decorationsTypes.forEach(subType => { - if (!newDecorationsTypes[subType]) { - this._codeEditorService.removeDecorationType(subType); - } - }); + this._decorationsTypeIds.forEach(this._codeEditorService.removeDecorationType, this._codeEditorService); + this._decorationsTypeIds = newDecorationsTypeIds; - this._decorationIds = this._editor.deltaDecorations(this._decorationIds, decorations); + this._decorationIds = this._editor.deltaDecorations(this._decorationIds, newDecorationsData); } private _getLayoutInfo() { @@ -203,10 +195,8 @@ export class InlineHintsController implements IEditorContribution { private _removeAllDecorations(): void { this._decorationIds = this._editor.deltaDecorations(this._decorationIds, []); - - this._decorationsTypes.forEach(subType => { - this._codeEditorService.removeDecorationType(subType); - }); + this._decorationsTypeIds.forEach(this._codeEditorService.removeDecorationType, this._codeEditorService); + this._decorationsTypeIds = []; } } From 1d3b03bd5517a6508cdb6a16e3860c71ed1464a2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 17:38:00 +0100 Subject: [PATCH 33/36] padding should depend on font size too --- src/vs/editor/contrib/inlineHints/inlineHintsController.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index a7bbaf5bba7fe..7605e45d96601 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -143,8 +143,8 @@ export class InlineHintsController implements IEditorContribution { for (let j = 0; j < hints.length && newDecorationsData.length < MAX_DECORATORS; j++) { const { text, range, hoverMessage, whitespaceBefore, whitespaceAfter } = hints[j]; - const marginBefore = whitespaceBefore ? fontSize / 3 : 0; - const marginAfter = whitespaceAfter ? fontSize / 3 : 0; + const marginBefore = whitespaceBefore ? (fontSize / 3) | 0 : 0; + const marginAfter = whitespaceAfter ? (fontSize / 3) | 0 : 0; const subKey = hash([text, marginBefore, marginAfter]).toString(16); const key = 'inlineHints-' + subKey; @@ -160,7 +160,7 @@ export class InlineHintsController implements IEditorContribution { margin: `0px ${marginAfter}px 0px ${marginBefore}px`, fontSize: `${fontSize}px`, fontFamily: fontFamily, - padding: '0px 2px' + padding: `0px ${(fontSize / 4) | 0}px` } }, undefined, this._editor); From d65ab8dcd9a00040b7efe9833ff3f9017ffd6c83 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 17:48:54 +0100 Subject: [PATCH 34/36] use all of context decoration as decoration type key --- .../inlineHints/inlineHintsController.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 7605e45d96601..64394c4889db5 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -10,7 +10,7 @@ import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IContentDecorationRenderOptions, IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { InlineHintsProvider, InlineHintsProviderRegistry, InlineHint } from 'vs/editor/common/modes'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -146,23 +146,21 @@ export class InlineHintsController implements IEditorContribution { const marginBefore = whitespaceBefore ? (fontSize / 3) | 0 : 0; const marginAfter = whitespaceAfter ? (fontSize / 3) | 0 : 0; - const subKey = hash([text, marginBefore, marginAfter]).toString(16); - const key = 'inlineHints-' + subKey; + const before: IContentDecorationRenderOptions = { + contentText: text, + backgroundColor: `${backgroundColor}`, + color: `${fontColor}`, + margin: `0px ${marginAfter}px 0px ${marginBefore}px`, + fontSize: `${fontSize}px`, + fontFamily: fontFamily, + padding: `0px ${(fontSize / 4) | 0}px` + }; + const key = 'inlineHints-' + hash(before).toString(16); + this._codeEditorService.registerDecorationType(key, { before }, undefined, this._editor); // decoration types are ref-counted which means we only need to // call register und remove equally often newDecorationsTypeIds.push(key); - this._codeEditorService.registerDecorationType(key, { - before: { - contentText: text, - backgroundColor: `${backgroundColor}`, - color: `${fontColor}`, - margin: `0px ${marginAfter}px 0px ${marginBefore}px`, - fontSize: `${fontSize}px`, - fontFamily: fontFamily, - padding: `0px ${(fontSize / 4) | 0}px` - } - }, undefined, this._editor); const options = this._codeEditorService.resolveDecorationOptions(key, true); if (hoverMessage) { From 7a938679f827954fa8b29a4d73f88f8b978ba53b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 17:51:49 +0100 Subject: [PATCH 35/36] clamp font size at editor font size, don't go bigger --- src/vs/editor/contrib/inlineHints/inlineHintsController.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 64394c4889db5..9cc48a27c8cef 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -182,11 +182,11 @@ export class InlineHintsController implements IEditorContribution { private _getLayoutInfo() { const options = this._editor.getOption(EditorOption.inlineHints); + const editorFontSize = this._editor.getOption(EditorOption.fontSize); let fontSize = options.fontSize; - if (!fontSize || fontSize < 5) { - fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; + if (!fontSize || fontSize < 5 || fontSize > editorFontSize) { + fontSize = (editorFontSize * .9) | 0; } - const fontFamily = options.fontFamily; return { fontSize, fontFamily }; } From a40b4e72d080fe6d1df01344680d85cf0623983e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jan 2021 18:41:49 +0100 Subject: [PATCH 36/36] add API command `vscode.executeInlineHintProvider` and some end-to-end tests --- .../inlineHints/inlineHintsController.ts | 21 +++++ .../api/common/extHostApiCommands.ts | 8 ++ .../browser/api/extHostApiCommands.test.ts | 82 ++++++++++++++++++- 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts index 9cc48a27c8cef..0a5d3dd6622aa 100644 --- a/src/vs/editor/contrib/inlineHints/inlineHintsController.ts +++ b/src/vs/editor/contrib/inlineHints/inlineHintsController.ts @@ -21,6 +21,11 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Range } from 'vs/editor/common/core/range'; import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; import { MarkdownString } from 'vs/base/common/htmlContent'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { URI } from 'vs/base/common/uri'; +import { IRange } from 'vs/base/common/range'; +import { assertType } from 'vs/base/common/types'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; const MAX_DECORATORS = 500; @@ -199,3 +204,19 @@ export class InlineHintsController implements IEditorContribution { } registerEditorContribution(InlineHintsController.ID, InlineHintsController); + +CommandsRegistry.registerCommand('_executeInlineHintProvider', async (accessor, ...args: [URI, IRange]): Promise => { + + const [uri, range] = args; + assertType(URI.isUri(uri)); + assertType(Range.isIRange(range)); + + const ref = await accessor.get(ITextModelService).createModelReference(uri); + try { + const data = await getInlineHints(ref.object.textEditorModel, [Range.lift(range)], CancellationToken.None); + return flatten(data.map(item => item.list)).sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + + } finally { + ref.dispose(); + } +}); diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 972664cdebaf2..94c23b401b245 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -271,6 +271,14 @@ const newCommands: ApiCommand[] = [ return []; }) ), + // --- inline hints + new ApiCommand( + 'vscode.executeInlineHintProvider', '_executeInlineHintProvider', 'Execute inline hints provider', + [ApiCommandArgument.Uri, ApiCommandArgument.Range], + new ApiCommandResult('A promise that resolves to an array of InlineHint objects', result => { + return result.map(typeConverters.InlineHint.to); + }) + ), // --- notebooks new ApiCommand( 'vscode.resolveNotebookContentProviders', '_resolveNotebookContentProvider', 'Resolve Notebook Content Providers', diff --git a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts index 47db11d1a7b70..0cc30aa021ed9 100644 --- a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts @@ -35,6 +35,7 @@ import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDep import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import 'vs/editor/contrib/codeAction/codeAction'; import 'vs/editor/contrib/codelens/codelens'; @@ -48,7 +49,7 @@ import 'vs/editor/contrib/parameterHints/provideSignatureHelp'; import 'vs/editor/contrib/smartSelect/smartSelect'; import 'vs/editor/contrib/suggest/suggest'; import 'vs/editor/contrib/rename/rename'; -import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; +import 'vs/editor/contrib/inlineHints/inlineHintsController'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = createTextModel( @@ -1141,6 +1142,85 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); + // --- inline hints + + test('Inline Hints, back and forth', async function () { + disposables.push(extHost.registerInlineHintsProvider(nullExtensionDescription, defaultSelector, { + provideInlineHints() { + return [new types.InlineHint('Foo', new types.Range(0, 1, 2, 3), undefined, true, false)]; + } + })); + + await rpcProtocol.sync(); + + const value = await commands.executeCommand('vscode.executeInlineHintProvider', model.uri, new types.Range(0, 0, 20, 20)); + assert.strictEqual(value.length, 1); + + const [first] = value; + assert.strictEqual(first.text, 'Foo'); + assert.strictEqual(first.range.start.line, 0); + assert.strictEqual(first.range.start.character, 1); + assert.strictEqual(first.range.end.line, 2); + assert.strictEqual(first.range.end.character, 3); + }); + + test('Inline Hints, merge', async function () { + disposables.push(extHost.registerInlineHintsProvider(nullExtensionDescription, defaultSelector, { + provideInlineHints() { + return [new types.InlineHint('Bar', new types.Range(10, 11, 12, 13), undefined, true, false)]; + } + })); + + disposables.push(extHost.registerInlineHintsProvider(nullExtensionDescription, defaultSelector, { + provideInlineHints() { + return [new types.InlineHint('Foo', new types.Range(0, 1, 2, 3), undefined, true, false)]; + } + })); + + await rpcProtocol.sync(); + + const value = await commands.executeCommand('vscode.executeInlineHintProvider', model.uri, new types.Range(0, 0, 20, 20)); + assert.strictEqual(value.length, 2); + + const [first, second] = value; + assert.strictEqual(first.text, 'Foo'); + assert.strictEqual(first.range.start.line, 0); + assert.strictEqual(first.range.start.character, 1); + assert.strictEqual(first.range.end.line, 2); + assert.strictEqual(first.range.end.character, 3); + + assert.strictEqual(second.text, 'Bar'); + assert.strictEqual(second.range.start.line, 10); + assert.strictEqual(second.range.start.character, 11); + assert.strictEqual(second.range.end.line, 12); + assert.strictEqual(second.range.end.character, 13); + }); + + test('Inline Hints, bad provider', async function () { + disposables.push(extHost.registerInlineHintsProvider(nullExtensionDescription, defaultSelector, { + provideInlineHints() { + return [new types.InlineHint('Foo', new types.Range(0, 1, 2, 3), undefined, true, false)]; + } + })); + disposables.push(extHost.registerInlineHintsProvider(nullExtensionDescription, defaultSelector, { + provideInlineHints() { + throw new Error(); + } + })); + + await rpcProtocol.sync(); + + const value = await commands.executeCommand('vscode.executeInlineHintProvider', model.uri, new types.Range(0, 0, 20, 20)); + assert.strictEqual(value.length, 1); + + const [first] = value; + assert.strictEqual(first.text, 'Foo'); + assert.strictEqual(first.range.start.line, 0); + assert.strictEqual(first.range.start.character, 1); + assert.strictEqual(first.range.end.line, 2); + assert.strictEqual(first.range.end.character, 3); + }); + // --- selection ranges test('Selection Range, back and forth', async function () {