From 53f82ca6e82ea5c08b9eb62de4104e321bb5dee6 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Fri, 13 Aug 2021 12:24:27 -0700 Subject: [PATCH] Move to "Auto Detect" in language picker and use smarts in Auto Detect for non-Untitled files --- .../browser/parts/editor/editorStatus.ts | 76 +++++++------------ .../browser/languageDetectionSimpleWorker.ts | 17 ++--- .../languageDetectionWorkerServiceImpl.ts | 14 ---- .../common/languageDetectionWorkerService.ts | 8 -- 4 files changed, 31 insertions(+), 84 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 0b58ae151bb..9155121d2ca 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -1121,10 +1121,6 @@ export class ShowLanguageExtensionsAction extends Action { } } -interface IDetectedLanguageQuickPickItem extends IQuickPickItem { - guessRank: number; -} - export class ChangeModeAction extends Action { static readonly ID = 'workbench.action.editor.changeLanguageMode'; @@ -1165,21 +1161,14 @@ export class ChangeModeAction extends Action { } let hasLanguageSupport = !!resource; - let detectedLanguages: string[] = []; if (resource?.scheme === Schemas.untitled && !this.textFileService.untitled.get(resource)?.hasAssociatedFilePath) { hasLanguageSupport = false; // no configuration for untitled resources (e.g. "Untitled-1") - - // Detect languages since we are in an untitled file - detectedLanguages = await this.languageDetectionService.detectLanguages(resource); } // All languages are valid picks const languages = this.modeService.getRegisteredLanguageNames(); const picks: QuickPickInput[] = languages.sort() - .filter(lang => { - const modeId = this.modeService.getModeIdForLanguageName(lang.toLowerCase()) || 'unknown'; - return (detectedLanguages.indexOf(modeId) === -1); - }).map(lang => { + .map(lang => { const modeId = this.modeService.getModeIdForLanguageName(lang.toLowerCase()) || 'unknown'; const extensions = this.modeService.getExtensions(lang).join(' '); let description: string; @@ -1221,32 +1210,7 @@ export class ChangeModeAction extends Action { const autoDetectMode: IQuickPickItem = { label: localize('autoDetect', "Auto Detect") }; - - if (hasLanguageSupport) { - picks.unshift(autoDetectMode); - } else if (detectedLanguages) { - // Add untitled detected languages - let index = detectedLanguages.length - 1; - for (const modeId of detectedLanguages.reverse()) { - const lang = this.modeService.getLanguageName(modeId) || 'unknown'; - let description: string; - if (currentLanguageId === lang) { - description = localize('languageDescriptionCurrent', "({0}) - Current Language", modeId); - } else { - description = localize('languageDescriptionConfigured', "({0})", modeId); - } - - const pick: IDetectedLanguageQuickPickItem = { - label: lang, - iconClasses: getIconClassesForModeId(modeId), - description, - guessRank: index--, - }; - picks.unshift(pick); - } - - picks.unshift({ type: 'separator', label: localize('detectedLanguagesPicks', "detected languages (identifier)") }); - } + picks.unshift(autoDetectMode); const pick = await this.quickInputService.pick(picks, { placeHolder: localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }); if (!pick) { @@ -1280,27 +1244,39 @@ export class ChangeModeAction extends Action { // Find mode let languageSelection: ILanguageSelection | undefined; + let detectedLanguage: string | undefined; if (pick === autoDetectMode) { if (textModel) { const resource = EditorResourceAccessor.getOriginalUri(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (resource) { - languageSelection = this.modeService.createByFilepathOrFirstLine(resource, textModel.getLineContent(1)); + // Detect languages since we are in an untitled file + let modeId: string | undefined = withNullAsUndefined(this.modeService.getModeIdByFilepathOrFirstLine(resource, textModel.getLineContent(1))); + if (!modeId) { + detectedLanguage = await this.languageDetectionService.detectLanguage(resource); + modeId = detectedLanguage; + } + if (modeId) { + languageSelection = this.modeService.create(modeId); + } } } } else { languageSelection = this.modeService.createByLanguageName(pick.label); - } - const guessRankOfPicked: number = (pick as IDetectedLanguageQuickPickItem).guessRank ?? -1; - // If we detected languages and they didn't choose the top detected language (which should also be the active language if automatic detection is enabled) - // then the automatic language detection was likely wrong and the user is correcting it. In this case, we want telemetry. - if (detectedLanguages.length && guessRankOfPicked !== 0) { - this.telemetryService.publicLog2(AutomaticLanguageDetectionLikelyWrongId, { - // For languages that weren't guessed, the guessRankOfPicked will be -1. This detail tells us if the user chose the language that was guessed or not. - choseOtherGuessedLanguage: guessRankOfPicked !== -1, - currentLanguageId: currentLanguageId ?? 'unknown', - nextLanguageId: languageSelection?.languageIdentifier.language ?? 'unknown' - }); + if (resource) { + // fire and forget to not slow things down + this.languageDetectionService.detectLanguage(resource).then(detectedModeId => { + const chosenModeId = this.modeService.getModeIdForLanguageName(pick.label.toLowerCase()) || 'unknown'; + if (detectedModeId === currentModeId && currentModeId !== chosenModeId) { + // If they didn't choose the detected language (which should also be the active language if automatic detection is enabled) + // then the automatic language detection was likely wrong and the user is correcting it. In this case, we want telemetry. + this.telemetryService.publicLog2(AutomaticLanguageDetectionLikelyWrongId, { + currentLanguageId: currentLanguageId ?? 'unknown', + nextLanguageId: pick.label + }); + } + }); + } } // Change mode diff --git a/src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts b/src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts index bae02c821ee..f68f798fca5 100644 --- a/src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts +++ b/src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts @@ -27,16 +27,6 @@ export class LanguageDetectionSimpleWorker extends EditorSimpleWorker { private _loadFailed: boolean = false; public async detectLanguage(uri: string): Promise { - const stopWatch = new StopWatch(true); - for await (const language of this.detectLanguagesImpl(uri)) { - stopWatch.stop(); - this._host.fhr('sendTelemetryEvent', [[language.languageId], [language.confidence], stopWatch.elapsed()]); - return language.languageId; - } - return undefined; - } - - public async detectLanguages(uri: string): Promise { const languages: string[] = []; const confidences: number[] = []; const stopWatch = new StopWatch(true); @@ -46,8 +36,11 @@ export class LanguageDetectionSimpleWorker extends EditorSimpleWorker { } stopWatch.stop(); - this._host.fhr('sendTelemetryEvent', [languages, confidences, stopWatch.elapsed()]); - return languages; + if (languages.length) { + this._host.fhr('sendTelemetryEvent', [languages, confidences, stopWatch.elapsed()]); + return languages[0]; + } + return undefined; } private async getModelOperations(): Promise { diff --git a/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts b/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts index 9c61e9e7a29..31960a01541 100644 --- a/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts +++ b/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts @@ -69,16 +69,6 @@ export class LanguageDetectionService extends Disposable implements ILanguageDet } return undefined; } - - async detectLanguages(resource: URI): Promise { - const languages: Array = await this._languageDetectionWorkerClient.detectLanguages(resource); - for (let i = 0; i < languages.length; i++) { - const modeId = this.getModeId(languages[i]); - languages[i] = modeId ? modeId : undefined; - } - - return languages.filter((l?: T): l is T => Boolean(l)); - } } export interface IWorkerClient { @@ -196,10 +186,6 @@ export class LanguageDetectionWorkerClient extends EditorWorkerClient { await this._withSyncedResources([resource]); return (await this._getProxy()).detectLanguage(resource.toString()); } - public async detectLanguages(resource: URI): Promise { - await this._withSyncedResources([resource]); - return (await this._getProxy()).detectLanguages(resource.toString()); - } } registerSingleton(ILanguageDetectionService, LanguageDetectionService); diff --git a/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts b/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts index ed38c41b3d0..d1c9f5ece44 100644 --- a/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts +++ b/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts @@ -22,12 +22,6 @@ export interface ILanguageDetectionService { * @returns the language mode for the given resource or undefined if the model is not confident enough. */ detectLanguage(resource: URI): Promise; - - /** - * @param resource The resource to detect the language for. - * @returns all possible language modes detected in this resource. - */ - detectLanguages(resource: URI): Promise; } //#region Telemetry events @@ -35,13 +29,11 @@ export interface ILanguageDetectionService { export const AutomaticLanguageDetectionLikelyWrongId = 'automaticlanguagedetection.likelywrong'; export interface IAutomaticLanguageDetectionLikelyWrongData { - choseOtherGuessedLanguage: boolean; currentLanguageId: string; nextLanguageId: string; } export type AutomaticLanguageDetectionLikelyWrongClassification = { - choseOtherGuessedLanguage: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }, currentLanguageId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }, nextLanguageId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' } }; -- GitLab