Merge pull request #294264 from mjbvz/dev/mjbvz/long-meerkat

Start adopting unified js/ts config for code lenses
This commit is contained in:
Matt Bierner
2026-02-10 16:46:47 -08:00
committed by GitHub
6 changed files with 240 additions and 51 deletions

View File

@@ -173,36 +173,6 @@
"description": "%typescript.enablePromptUseWorkspaceTsdk%",
"scope": "window"
},
"javascript.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%javascript.referencesCodeLens.enabled%",
"scope": "window"
},
"javascript.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "%javascript.referencesCodeLens.showOnAllFunctions%",
"scope": "window"
},
"typescript.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%typescript.referencesCodeLens.enabled%",
"scope": "window"
},
"typescript.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "%typescript.referencesCodeLens.showOnAllFunctions%",
"scope": "window"
},
"typescript.implementationsCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%typescript.implementationsCodeLens.enabled%",
"scope": "window"
},
"typescript.experimental.useTsgo": {
"type": "boolean",
"default": false,
@@ -212,16 +182,103 @@
"experimental"
]
},
"js/ts.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%configuration.referencesCodeLens.enabled%",
"scope": "language-overridable",
"tags": [
"JavaScript",
"TypeScript"
]
},
"javascript.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%configuration.referencesCodeLens.enabled%",
"markdownDeprecationMessage": "%configuration.referencesCodeLens.enabled.unifiedDeprecationMessage%",
"scope": "window"
},
"typescript.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%configuration.referencesCodeLens.enabled%",
"markdownDeprecationMessage": "%configuration.referencesCodeLens.enabled.unifiedDeprecationMessage%",
"scope": "window"
},
"js/ts.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "%configuration.referencesCodeLens.showOnAllFunctions%",
"scope": "language-overridable",
"tags": [
"JavaScript",
"TypeScript"
]
},
"javascript.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "%configuration.referencesCodeLens.showOnAllFunctions%",
"markdownDeprecationMessage": "%configuration.referencesCodeLens.showOnAllFunctions.unifiedDeprecationMessage%",
"scope": "window"
},
"typescript.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "%configuration.referencesCodeLens.showOnAllFunctions%",
"markdownDeprecationMessage": "%configuration.referencesCodeLens.showOnAllFunctions.unifiedDeprecationMessage%",
"scope": "window"
},
"js/ts.implementationsCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%configuration.implementationsCodeLens.enabled%",
"scope": "language-overridable",
"tags": [
"JavaScript",
"TypeScript"
]
},
"typescript.implementationsCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%configuration.implementationsCodeLens.enabled%",
"markdownDeprecationMessage": "%configuration.implementationsCodeLens.enabled.unifiedDeprecationMessage%",
"scope": "window"
},
"js/ts.implementationsCodeLens.showOnInterfaceMethods": {
"type": "boolean",
"default": false,
"description": "%configuration.implementationsCodeLens.showOnInterfaceMethods%",
"scope": "language-overridable",
"tags": [
"JavaScript",
"TypeScript"
]
},
"typescript.implementationsCodeLens.showOnInterfaceMethods": {
"type": "boolean",
"default": false,
"description": "%typescript.implementationsCodeLens.showOnInterfaceMethods%",
"description": "%configuration.implementationsCodeLens.showOnInterfaceMethods%",
"markdownDeprecationMessage": "%configuration.implementationsCodeLens.showOnInterfaceMethods.unifiedDeprecationMessage%",
"scope": "window"
},
"js/ts.implementationsCodeLens.showOnAllClassMethods": {
"type": "boolean",
"default": false,
"description": "%configuration.implementationsCodeLens.showOnAllClassMethods%",
"scope": "language-overridable",
"tags": [
"JavaScript",
"TypeScript"
]
},
"typescript.implementationsCodeLens.showOnAllClassMethods": {
"type": "boolean",
"default": false,
"description": "%typescript.implementationsCodeLens.showOnAllClassMethods%",
"description": "%configuration.implementationsCodeLens.showOnAllClassMethods%",
"markdownDeprecationMessage": "%configuration.implementationsCodeLens.showOnAllClassMethods.unifiedDeprecationMessage%",
"scope": "window"
},
"typescript.reportStyleChecksAsWarnings": {

View File

@@ -49,13 +49,16 @@
"javascript.validate.enable": "Enable/disable JavaScript validation.",
"javascript.goToProjectConfig.title": "Go to Project Configuration (jsconfig / tsconfig)",
"typescript.goToProjectConfig.title": "Go to Project Configuration (tsconfig)",
"javascript.referencesCodeLens.enabled": "Enable/disable references CodeLens in JavaScript files.",
"javascript.referencesCodeLens.showOnAllFunctions": "Enable/disable references CodeLens on all functions in JavaScript files.",
"typescript.referencesCodeLens.enabled": "Enable/disable references CodeLens in TypeScript files.",
"typescript.referencesCodeLens.showOnAllFunctions": "Enable/disable references CodeLens on all functions in TypeScript files.",
"typescript.implementationsCodeLens.enabled": "Enable/disable implementations CodeLens. This CodeLens shows the implementers of an interface.",
"typescript.implementationsCodeLens.showOnInterfaceMethods": "Enable/disable implementations CodeLens on interface methods.",
"typescript.implementationsCodeLens.showOnAllClassMethods": "Enable/disable showing implementations CodeLens above all class methods instead of only on abstract methods.",
"configuration.referencesCodeLens.enabled": "Enable/disable references CodeLens in JavaScript and TypeScript files. This CodeLens shows the number of references for classes and exported functions and allows you to peek or navigate to them.",
"configuration.referencesCodeLens.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.referencesCodeLens.enabled#` instead.",
"configuration.referencesCodeLens.showOnAllFunctions": "Enable/disable the references CodeLens on all functions in JavaScript and TypeScript files.",
"configuration.referencesCodeLens.showOnAllFunctions.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.referencesCodeLens.showOnAllFunctions#` instead.",
"configuration.implementationsCodeLens.enabled": "Enable/disable implementations CodeLens in TypeScript files. This CodeLens shows the implementers of a TypeScript interface.",
"configuration.implementationsCodeLens.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.implementationsCodeLens.enabled#` instead.",
"configuration.implementationsCodeLens.showOnInterfaceMethods": "Enable/disable implementations CodeLens on TypeScript interface methods.",
"configuration.implementationsCodeLens.showOnInterfaceMethods.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.implementationsCodeLens.showOnInterfaceMethods#` instead.",
"configuration.implementationsCodeLens.showOnAllClassMethods": "Enable/disable showing implementations CodeLens above all TypeScript class methods instead of only on abstract methods.",
"configuration.implementationsCodeLens.showOnAllClassMethods.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.implementationsCodeLens.showOnAllClassMethods#` instead.",
"typescript.openTsServerLog.title": "Open TS Server log",
"typescript.restartTsServer": "Restart TS Server",
"typescript.selectTypeScriptVersion.title": "Select TypeScript Version...",

View File

@@ -11,10 +11,16 @@ import type * as Proto from '../../tsServer/protocol/protocol';
import * as PConst from '../../tsServer/protocol/protocol.const';
import * as typeConverters from '../../typeConverters';
import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService';
import { conditionalRegistration, requireGlobalConfiguration, requireSomeCapability } from '../util/dependentRegistration';
import { readUnifiedConfig, unifiedConfigSection } from '../../utils/configuration';
import { conditionalRegistration, requireHasModifiedUnifiedConfig, requireSomeCapability } from '../util/dependentRegistration';
import { ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider';
import { ExecutionTarget } from '../../tsServer/server';
const Config = Object.freeze({
enabled: 'implementationsCodeLens.enabled',
showOnInterfaceMethods: 'implementationsCodeLens.showOnInterfaceMethods',
showOnAllClassMethods: 'implementationsCodeLens.showOnAllClassMethods',
});
export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider {
public constructor(
@@ -25,14 +31,30 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
super(client, _cachedResponse);
this._register(
vscode.workspace.onDidChangeConfiguration(evt => {
if (evt.affectsConfiguration(`${language.id}.implementationsCodeLens.showOnInterfaceMethods`) ||
evt.affectsConfiguration(`${language.id}.implementationsCodeLens.showOnAllClassMethods`)) {
if (
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.enabled}`) ||
evt.affectsConfiguration(`${language.id}.${Config.enabled}`) ||
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.showOnInterfaceMethods}`) ||
evt.affectsConfiguration(`${language.id}.${Config.showOnInterfaceMethods}`) ||
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.showOnAllClassMethods}`) ||
evt.affectsConfiguration(`${language.id}.${Config.showOnAllClassMethods}`)
) {
this.changeEmitter.fire();
}
})
);
}
override async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<ReferencesCodeLens[]> {
const enabled = readUnifiedConfig<boolean>(Config.enabled, false, { scope: document, fallbackSection: this.language.id });
if (!enabled) {
return [];
}
return super.provideCodeLenses(document, token);
}
public async resolveCodeLens(
codeLens: ReferencesCodeLens,
token: vscode.CancellationToken,
@@ -88,8 +110,6 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
item: Proto.NavigationTree,
parent: Proto.NavigationTree | undefined
): vscode.Range | undefined {
const cfg = vscode.workspace.getConfiguration(this.language.id);
// Always show on interfaces
if (item.kind === PConst.Kind.interface) {
return getSymbolRange(document, item);
@@ -111,7 +131,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
if (
item.kind === PConst.Kind.method &&
parent?.kind === PConst.Kind.interface &&
cfg.get<boolean>('implementationsCodeLens.showOnInterfaceMethods', false)
readUnifiedConfig<boolean>('implementationsCodeLens.showOnInterfaceMethods', false, { scope: document, fallbackSection: this.language.id })
) {
return getSymbolRange(document, item);
}
@@ -121,7 +141,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
if (
item.kind === PConst.Kind.method &&
parent?.kind === PConst.Kind.class &&
cfg.get<boolean>('implementationsCodeLens.showOnAllClassMethods', false)
readUnifiedConfig<boolean>('implementationsCodeLens.showOnAllClassMethods', false, { scope: document, fallbackSection: this.language.id })
) {
// But not private ones as these can never be overridden
if (/\bprivate\b/.test(item.kindModifiers ?? '')) {
@@ -141,7 +161,7 @@ export function register(
cachedResponse: CachedResponse<Proto.NavTreeResponse>,
) {
return conditionalRegistration([
requireGlobalConfiguration(language.id, 'implementationsCodeLens.enabled'),
requireHasModifiedUnifiedConfig(Config.enabled, language.id),
requireSomeCapability(client, ClientCapability.Semantic),
], () => {
return vscode.languages.registerCodeLensProvider(selector.semantic,

View File

@@ -12,9 +12,14 @@ import * as PConst from '../../tsServer/protocol/protocol.const';
import { ExecutionTarget } from '../../tsServer/server';
import * as typeConverters from '../../typeConverters';
import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService';
import { conditionalRegistration, requireGlobalConfiguration, requireSomeCapability } from '../util/dependentRegistration';
import { readUnifiedConfig, unifiedConfigSection } from '../../utils/configuration';
import { conditionalRegistration, requireHasModifiedUnifiedConfig, requireSomeCapability } from '../util/dependentRegistration';
import { ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider';
const Config = Object.freeze({
enabled: 'referencesCodeLens.enabled',
showOnAllFunctions: 'referencesCodeLens.showOnAllFunctions',
});
export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvider {
public constructor(
@@ -25,13 +30,27 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens
super(client, _cachedResponse);
this._register(
vscode.workspace.onDidChangeConfiguration(evt => {
if (evt.affectsConfiguration(`${language.id}.referencesCodeLens.showOnAllFunctions`)) {
if (
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.enabled}`) ||
evt.affectsConfiguration(`${language.id}.${Config.enabled}`) ||
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.showOnAllFunctions}`) ||
evt.affectsConfiguration(`${language.id}.${Config.showOnAllFunctions}`)
) {
this.changeEmitter.fire();
}
})
);
}
override async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<ReferencesCodeLens[]> {
const enabled = readUnifiedConfig<boolean>(Config.enabled, false, { scope: document, fallbackSection: this.language.id });
if (!enabled) {
return [];
}
return super.provideCodeLenses(document, token);
}
public async resolveCodeLens(codeLens: ReferencesCodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start);
const response = await this.client.execute('references', args, token, {
@@ -76,7 +95,7 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens
switch (item.kind) {
case PConst.Kind.function: {
const showOnAllFunctions = vscode.workspace.getConfiguration(this.language.id).get<boolean>('referencesCodeLens.showOnAllFunctions');
const showOnAllFunctions = readUnifiedConfig<boolean>(Config.showOnAllFunctions, false, { scope: document, fallbackSection: this.language.id });
if (showOnAllFunctions && item.nameSpan) {
return getSymbolRange(document, item);
}
@@ -137,7 +156,7 @@ export function register(
cachedResponse: CachedResponse<Proto.NavTreeResponse>,
) {
return conditionalRegistration([
requireGlobalConfiguration(language.id, 'referencesCodeLens.enabled'),
requireHasModifiedUnifiedConfig(Config.enabled, language.id),
requireSomeCapability(client, ClientCapability.Semantic),
], () => {
return vscode.languages.registerCodeLensProvider(selector.semantic,

View File

@@ -6,6 +6,7 @@
import * as vscode from 'vscode';
import { API } from '../../tsServer/api';
import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService';
import { hasModifiedUnifiedConfig } from '../../utils/configuration';
import { Disposable } from '../../utils/dispose';
export class Condition extends Disposable {
@@ -102,6 +103,21 @@ export function requireGlobalConfiguration(
);
}
/**
* Requires that a configuration value has been modified from its default value in either the global or workspace scope
*
* Does not check the value, only that it has been modified from the default.
*/
export function requireHasModifiedUnifiedConfig(
configValue: string,
fallbackSection: string,
) {
return new Condition(
() => hasModifiedUnifiedConfig(configValue, { fallbackSection }),
vscode.workspace.onDidChangeConfiguration
);
}
export function requireSomeCapability(
client: ITypeScriptServiceClient,
...capabilities: readonly ClientCapability[]

View File

@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* 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';
type ConfigurationScope = vscode.ConfigurationScope | null | undefined;
export const unifiedConfigSection = 'js/ts';
/**
* Gets a configuration value, checking the unified `js/ts` setting first,
* then falling back to the language-specific setting.
*/
export function readUnifiedConfig<T>(
subSectionName: string,
defaultValue: T,
options: {
readonly scope?: ConfigurationScope;
readonly fallbackSection: string;
}
): T {
// Check unified setting first
const unifiedConfig = vscode.workspace.getConfiguration(unifiedConfigSection, options.scope);
const unifiedInspect = unifiedConfig.inspect<T>(subSectionName);
if (hasModifiedValue(unifiedInspect)) {
return unifiedConfig.get<T>(subSectionName, defaultValue);
}
// Fall back to language-specific setting
const languageConfig = vscode.workspace.getConfiguration(options.fallbackSection, options.scope);
return languageConfig.get<T>(subSectionName, defaultValue);
}
/**
* Checks if an inspected configuration value has any user-defined values set.
*/
function hasModifiedValue(inspect: ReturnType<vscode.WorkspaceConfiguration['inspect']>): boolean {
if (!inspect) {
return false;
}
return (
typeof inspect.globalValue !== 'undefined'
|| typeof inspect.workspaceValue !== 'undefined'
|| typeof inspect.workspaceFolderValue !== 'undefined'
|| typeof inspect.globalLanguageValue !== 'undefined'
|| typeof inspect.workspaceLanguageValue !== 'undefined'
|| typeof inspect.workspaceFolderLanguageValue !== 'undefined'
|| ((inspect.languageIds?.length ?? 0) > 0)
);
}
/**
* Checks if a unified configuration value has been modified from its default value.
*/
export function hasModifiedUnifiedConfig(
subSectionName: string,
options: {
readonly scope?: ConfigurationScope;
readonly fallbackSection: string;
}
): boolean {
// Check unified setting
const unifiedConfig = vscode.workspace.getConfiguration(unifiedConfigSection, options.scope);
if (hasModifiedValue(unifiedConfig.inspect(subSectionName))) {
return true;
}
// Check language-specific setting
const languageConfig = vscode.workspace.getConfiguration(options.fallbackSection, options.scope);
return hasModifiedValue(languageConfig.inspect(subSectionName));
}