diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 87fa7c5b815..a0adbe40cb2 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -113,17 +113,21 @@ const registry = Registry.as(ConfigurationExtensions.Con description: localize('workbench.editor.preferBasedLanguageDetection', "When enabled, a language detection model that takes into account editor history will be given higher precedence."), }, 'workbench.editor.languageDetectionHints': { - type: 'string', - default: 'always', + type: 'object', + default: { 'untitledEditors': true, 'notebookEditors': true }, tags: ['experimental'], - enum: ['always', 'notebookEditors', 'textEditors', 'never'], description: localize('workbench.editor.showLanguageDetectionHints', "When enabled, shows a status bar quick fix when the editor language doesn't match detected content language."), - enumDescriptions: [ - localize('workbench.editor.showLanguageDetectionHints.always', "Show show language detection quick fixes in both notebooks and untitled editors"), - localize('workbench.editor.showLanguageDetectionHints.notebook', "Only show language detection quick fixes in notebooks"), - localize('workbench.editor.showLanguageDetectionHints.editors', "Only show language detection quick fixes in untitled editors"), - localize('workbench.editor.showLanguageDetectionHints.never', "Never show language quick fixes"), - ] + additionalProperties: false, + properties: { + untitledEditors: { + type: 'boolean', + description: localize('workbench.editor.showLanguageDetectionHints.editors', "Show in untitled text editors"), + }, + notebookEditors: { + type: 'boolean', + description: localize('workbench.editor.showLanguageDetectionHints.notebook', "Show in notebook editors"), + } + } }, 'workbench.editor.tabCloseButton': { 'type': 'string', diff --git a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts index 94f1e9af9bd..a931e2bb2f2 100644 --- a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts +++ b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts @@ -11,7 +11,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWo import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; -import { ILanguageDetectionService } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; +import { ILanguageDetectionService, LanguageDetectionHintConfig } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; import { ThrottledDelayer } from 'vs/base/common/async'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -75,8 +75,8 @@ class LanguageDetectionStatusContribution implements IWorkbenchContribution { const editorModel = editor?.getModel(); const editorUri = editorModel?.uri; const existingId = editorModel?.getLanguageId(); - const enablementConfig = this._configurationService.getValue('workbench.editor.languageDetectionHints'); - const enabled = enablementConfig === 'always' || enablementConfig === 'textEditors'; + const enablementConfig = this._configurationService.getValue('workbench.editor.languageDetectionHints'); + const enabled = typeof enablementConfig === 'object' && enablementConfig?.untitledEditors; const disableLightbulb = !enabled || editorUri?.scheme !== Schemas.untitled || !existingId; if (disableLightbulb || !editorUri) { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts index 750af12fdf4..ee2b1fdfa22 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts @@ -19,7 +19,7 @@ import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/com import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarItem, INotebookCellStatusBarItemList, INotebookCellStatusBarItemProvider } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { ILanguageDetectionService } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; +import { ILanguageDetectionService, LanguageDetectionHintConfig } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; class CellStatusBarLanguagePickerProvider implements INotebookCellStatusBarItemProvider { @@ -60,9 +60,11 @@ class CellStatusBarLanguageDetectionProvider implements INotebookCellStatusBarIt readonly viewType = '*'; private cache = new ResourceMap<{ - lastUpdate: number; - lastCellLang: string; - lastGuess?: string; + contentVersion: number; + updateTimestamp: number; + cellLanguage: string; + + guess?: string; }>(); constructor( @@ -79,37 +81,45 @@ class CellStatusBarLanguageDetectionProvider implements INotebookCellStatusBarIt const cell = doc?.cells[index]; if (!cell) { return; } - const enablementConfig = this._configurationService.getValue('workbench.editor.languageDetectionHints'); - const enabled = enablementConfig === 'always' || enablementConfig === 'notebookEditors'; + const enablementConfig = this._configurationService.getValue('workbench.editor.languageDetectionHints'); + const enabled = typeof enablementConfig === 'object' && enablementConfig?.notebookEditors; if (!enabled) { return; } + const cellUri = cell.uri; + const contentVersion = cell.textModel?.getVersionId(); + if (!contentVersion) { + return; + } const currentLanguageId = cell.cellKind === CellKind.Markup ? 'markdown' : (this._languageService.getLanguageIdByLanguageName(cell.language) || cell.language); - if (!this.cache.has(uri)) { - this.cache.set(uri, { lastCellLang: currentLanguageId, lastUpdate: 0 }); + if (!this.cache.has(cellUri)) { + this.cache.set(cellUri, { + cellLanguage: currentLanguageId, // force a re-compute upon a change in configured language + updateTimestamp: 0, // facilitates a disposable-free debounce operation + contentVersion: 1, // dont run for the initial contents, only on update + }); } - const cached = this.cache.get(uri)!; - if (cached.lastUpdate < Date.now() - 1000 || cached.lastCellLang !== currentLanguageId) { - cached.lastUpdate = Date.now(); - cached.lastCellLang = currentLanguageId; + const cached = this.cache.get(cellUri)!; + if (cached.cellLanguage !== currentLanguageId || (cached.updateTimestamp < Date.now() - 1000 && cached.contentVersion !== contentVersion)) { + cached.updateTimestamp = Date.now(); + cached.cellLanguage = currentLanguageId; + cached.contentVersion = contentVersion; const kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(doc); - if (kernel) { - const availableLangs = []; - availableLangs.push(...kernel.supportedLanguages, 'markdown'); - cached.lastGuess = await this._languageDetectionService.detectLanguage(cell.uri, availableLangs); + const supportedLangs = [...kernel.supportedLanguages, 'markdown']; + cached.guess = await this._languageDetectionService.detectLanguage(cell.uri, supportedLangs); } } const items: INotebookCellStatusBarItem[] = []; - if (cached.lastGuess && currentLanguageId !== cached.lastGuess) { - const detectedName = this._languageService.getLanguageName(cached.lastGuess) || cached.lastGuess; + if (cached.guess && currentLanguageId !== cached.guess) { + const detectedName = this._languageService.getLanguageName(cached.guess) || cached.guess; let tooltip = localize('notebook.cell.status.autoDetectLanguage', "Accept Detected Language: {0}", detectedName); const keybinding = this._keybindingService.lookupKeybinding(DETECT_CELL_LANGUAGE); const label = keybinding?.getLabel(); diff --git a/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts b/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts index 0fe24c9c6fd..199b0d8217a 100644 --- a/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts +++ b/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts @@ -25,6 +25,11 @@ export interface ILanguageDetectionService { detectLanguage(resource: URI, supportedLangs?: string[]): Promise; } +export type LanguageDetectionHintConfig = { + untitledEditors: boolean; + notebookEditors: boolean; +}; + //#region Telemetry events export const AutomaticLanguageDetectionLikelyWrongId = 'automaticlanguagedetection.likelywrong';