From d7b978113766052fbcddfc62052dae37fe943226 Mon Sep 17 00:00:00 2001 From: yogeshwaran-c Date: Thu, 26 Mar 2026 06:43:52 +0530 Subject: [PATCH 1/2] fix: send user preferences to TS server even without visible editor When ensureConfigurationForDocument is called and no visible text editor is found for the document, getFormattingOptions returns undefined and the method returns early without sending any configuration including user preferences like preferTypeOnlyAutoImports to the TS server. This causes source.addMissingImports to ignore the user's preferTypeOnlyAutoImports setting. Fix by falling back to undefined formatting options when no visible editor is found, ensuring user preferences are always sent. Closes #272479 --- .../src/languageFeatures/fileConfigurationManager.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index 915de13e13cf4..adac2436edfaa 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -53,10 +53,9 @@ export default class FileConfigurationManager extends Disposable { document: vscode.TextDocument, token: vscode.CancellationToken ): Promise { - const formattingOptions = this.getFormattingOptions(document); - if (formattingOptions) { - return this.ensureConfigurationOptions(document, formattingOptions, token); - } + const formattingOptions = this.getFormattingOptions(document) + ?? { tabSize: undefined, insertSpaces: undefined }; + return this.ensureConfigurationOptions(document, formattingOptions, token); } private getFormattingOptions(document: vscode.TextDocument): FormattingOptions | undefined { From 9b03f3b79555ef68630a8524748f1127542067e7 Mon Sep 17 00:00:00 2001 From: yogeshwaran-c Date: Thu, 26 Mar 2026 06:49:03 +0530 Subject: [PATCH 2/2] fix: use setupDelayedHover for settings indicator hovers to support Ctrl+K I Switch all indicator hovers from showInstantHover+addHoverDisposables to setupDelayedHover with setupKeyboardEvents. This registers hovers in _delayedHovers, allowing _showAndFocusHoverForActiveElement to find and trigger the correct hover when Ctrl+K I is pressed while a setting indicator (Modified Elsewhere, Workspace Trust, etc.) is focused. Previously, Ctrl+K I would walk up the DOM tree and find a parent element hover (showing the setting ID) instead of the indicator hover. Fixes #257903 --- .../settingsEditorSettingIndicators.ts | 148 ++++++------------ 1 file changed, 46 insertions(+), 102 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index 47874f09ade60..35a683edd7f58 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -5,10 +5,9 @@ import * as DOM from '../../../../base/browser/dom.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { HoverStyle, type IHoverOptions, type IHoverWidget } from '../../../../base/browser/ui/hover/hover.js'; +import { HoverStyle, type IHoverOptions } from '../../../../base/browser/ui/hover/hover.js'; import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'; import { SimpleIconLabel } from '../../../../base/browser/ui/iconLabel/simpleIconLabel.js'; -import { RunOnceScheduler } from '../../../../base/common/async.js'; import { Emitter } from '../../../../base/common/event.js'; import { IMarkdownString, MarkdownString, createMarkdownLink } from '../../../../base/common/htmlContent.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; @@ -110,33 +109,6 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { }, }; - private addHoverDisposables(disposables: DisposableStore, element: HTMLElement, showHover: (focus: boolean) => IHoverWidget | undefined) { - disposables.clear(); - const scheduler: RunOnceScheduler = disposables.add(new RunOnceScheduler(() => { - const hover = showHover(false); - if (hover) { - disposables.add(hover); - } - }, this.configurationService.getValue('workbench.hover.delay'))); - disposables.add(DOM.addDisposableListener(element, DOM.EventType.MOUSE_OVER, () => { - if (!scheduler.isScheduled()) { - scheduler.schedule(); - } - })); - disposables.add(DOM.addDisposableListener(element, DOM.EventType.MOUSE_LEAVE, () => { - scheduler.cancel(); - })); - disposables.add(DOM.addDisposableListener(element, DOM.EventType.KEY_DOWN, (e) => { - const evt = new StandardKeyboardEvent(e); - if (evt.equals(KeyCode.Space) || evt.equals(KeyCode.Enter)) { - const hover = showHover(true); - if (hover) { - disposables.add(hover); - } - e.preventDefault(); - } - })); - } private createWorkspaceTrustIndicator(): SettingIndicator { const disposables = new DisposableStore(); @@ -145,21 +117,17 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { workspaceTrustLabel.text = '$(shield) ' + localize('workspaceUntrustedLabel', "Requires workspace trust"); const content = localize('trustLabel', "The setting value can only be applied in a trusted workspace."); - const showHover = (focus: boolean) => { - return this.hoverService.showInstantHover({ - ...this.defaultHoverOptions, - content, - target: workspaceTrustElement, - actions: [{ - label: localize('manageWorkspaceTrust', "Manage Workspace Trust"), - commandId: 'workbench.trust.manage', - run: (target: HTMLElement) => { - this.commandService.executeCommand('workbench.trust.manage'); - } - }], - }, focus); - }; - this.addHoverDisposables(disposables, workspaceTrustElement, showHover); + disposables.add(this.hoverService.setupDelayedHover(workspaceTrustElement, () => ({ + ...this.defaultHoverOptions, + content, + actions: [{ + label: localize('manageWorkspaceTrust', "Manage Workspace Trust"), + commandId: 'workbench.trust.manage', + run: (target: HTMLElement) => { + this.commandService.executeCommand('workbench.trust.manage'); + } + }], + }), { setupKeyboardEvents: true })); return { element: workspaceTrustElement, label: workspaceTrustLabel, @@ -186,14 +154,10 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { syncIgnoredLabel.text = localize('extensionSyncIgnoredLabel', 'Not synced'); const syncIgnoredHoverContent = localize('syncIgnoredTitle', "This setting is ignored during sync"); - const showHover = (focus: boolean) => { - return this.hoverService.showInstantHover({ - ...this.defaultHoverOptions, - content: syncIgnoredHoverContent, - target: syncIgnoredElement - }, focus); - }; - this.addHoverDisposables(disposables, syncIgnoredElement, showHover); + disposables.add(this.hoverService.setupDelayedHover(syncIgnoredElement, { + ...this.defaultHoverOptions, + content: syncIgnoredHoverContent, + }, { setupKeyboardEvents: true })); return { element: syncIgnoredElement, @@ -233,14 +197,10 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { const advancedLabel = disposables.add(new SimpleIconLabel(advancedIndicator)); advancedLabel.text = localize('advancedLabel', "Advanced"); - const showHover = (focus: boolean) => { - return this.hoverService.showInstantHover({ - ...this.defaultHoverOptions, - content: ADVANCED_INDICATOR_DESCRIPTION, - target: advancedIndicator - }, focus); - }; - this.addHoverDisposables(disposables, advancedIndicator, showHover); + disposables.add(this.hoverService.setupDelayedHover(advancedIndicator, { + ...this.defaultHoverOptions, + content: ADVANCED_INDICATOR_DESCRIPTION, + }, { setupKeyboardEvents: true })); return { element: advancedIndicator, @@ -351,14 +311,10 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { localize('experimentalLabel', "Experimental"); const content = isPreviewSetting ? PREVIEW_INDICATOR_DESCRIPTION : EXPERIMENTAL_INDICATOR_DESCRIPTION; - const showHover = (focus: boolean) => { - return this.hoverService.showInstantHover({ - ...this.defaultHoverOptions, - content, - target: this.previewIndicator.element - }, focus); - }; - this.addHoverDisposables(this.previewIndicator.disposables, this.previewIndicator.element, showHover); + this.previewIndicator.disposables.add(this.hoverService.setupDelayedHover(this.previewIndicator.element, { + ...this.defaultHoverOptions, + content, + }, { setupKeyboardEvents: true })); this.render(); } @@ -402,21 +358,17 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { this.scopeOverridesIndicator.label.text = '$(briefcase) ' + localize('policyLabelText', "Managed by organization"); const content = localize('policyDescription', "This setting is managed by your organization and its actual value cannot be changed."); - const showHover = (focus: boolean) => { - return this.hoverService.showInstantHover({ - ...this.defaultHoverOptions, - content, - actions: [{ - label: localize('policyFilterLink', "View policy settings"), - commandId: '_settings.action.viewPolicySettings', - run: (_) => { - onApplyFilter.fire(`@${POLICY_SETTING_TAG}`); - } - }], - target: this.scopeOverridesIndicator.element - }, focus); - }; - this.addHoverDisposables(this.scopeOverridesIndicator.disposables, this.scopeOverridesIndicator.element, showHover); + this.scopeOverridesIndicator.disposables.add(this.hoverService.setupDelayedHover(this.scopeOverridesIndicator.element, () => ({ + ...this.defaultHoverOptions, + content, + actions: [{ + label: localize('policyFilterLink', "View policy settings"), + commandId: '_settings.action.viewPolicySettings', + run: (_) => { + onApplyFilter.fire(`@${POLICY_SETTING_TAG}`); + } + }], + }), { setupKeyboardEvents: true })); } else if (element.settingsTarget === ConfigurationTarget.USER_LOCAL && this.configurationService.isSettingAppliedForAllProfiles(element.setting.key)) { this.scopeOverridesIndicator.element.style.display = 'inline'; this.scopeOverridesIndicator.element.classList.add('setting-indicator'); @@ -424,14 +376,10 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { this.scopeOverridesIndicator.label.text = localize('applicationSetting', "Applies to all profiles"); const content = localize('applicationSettingDescription', "The setting is not specific to the current profile, and will retain its value when switching profiles."); - const showHover = (focus: boolean) => { - return this.hoverService.showInstantHover({ - ...this.defaultHoverOptions, - content, - target: this.scopeOverridesIndicator.element - }, focus); - }; - this.addHoverDisposables(this.scopeOverridesIndicator.disposables, this.scopeOverridesIndicator.element, showHover); + this.scopeOverridesIndicator.disposables.add(this.hoverService.setupDelayedHover(this.scopeOverridesIndicator.element, { + ...this.defaultHoverOptions, + content, + }, { setupKeyboardEvents: true })); } else if (element.overriddenScopeList.length || element.overriddenDefaultsLanguageList.length) { if (element.overriddenScopeList.length === 1 && !element.overriddenDefaultsLanguageList.length) { // We can inline the override and show all the text in the label @@ -540,17 +488,13 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { defaultOverrideHoverContent = localize('multipledefaultOverriddenDetails', "A default values has been set by {0}", sourceToDisplay.slice(0, -1).join(', ') + ' & ' + sourceToDisplay.slice(-1)); } - const showHover = (focus: boolean) => { - return this.hoverService.showInstantHover({ - content: new MarkdownString().appendMarkdown(defaultOverrideHoverContent), - target: this.defaultOverrideIndicator.element, - style: HoverStyle.Pointer, - position: { - hoverPosition: HoverPosition.BELOW, - }, - }, focus); - }; - this.addHoverDisposables(this.defaultOverrideIndicator.disposables, this.defaultOverrideIndicator.element, showHover); + this.defaultOverrideIndicator.disposables.add(this.hoverService.setupDelayedHover(this.defaultOverrideIndicator.element, () => ({ + content: new MarkdownString().appendMarkdown(defaultOverrideHoverContent), + style: HoverStyle.Pointer, + position: { + hoverPosition: HoverPosition.BELOW, + }, + }), { setupKeyboardEvents: true })); } this.render(); }