diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index e2fdcb5..b72bc3b 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. - +import * as path from "path"; import * as vscode from "vscode"; import * as anchor from "./anchor"; @@ -10,6 +10,8 @@ import * as utility from "./utility"; export class JavaDebugConfigurationProvider implements vscode.DebugConfigurationProvider { private isUserSettingsDirty: boolean = true; + private debugHistory: MostRecentlyUsedHistory = new MostRecentlyUsedHistory(); + constructor() { vscode.workspace.onDidChangeConfiguration((event) => { if (vscode.debug.activeDebugSession) { @@ -213,30 +215,86 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration }); return undefined; } - const pickItems = res.map((item) => { - let name = item.mainClass; - let details = `main class: ${item.mainClass}`; - if (item.projectName !== undefined) { - name += `<${item.projectName}>`; - details += ` | project name: ${item.projectName}`; - } - return { - description: details, - label: name, - item, - }; - }).sort((a, b): number => { - return a.label > b.label ? 1 : -1; - }); + + const pickItems: IMainClassQuickPickItem[] = this.formatRecentlyUsedMainClassOptions(res); + const selection = pickItems.length > 1 ? await vscode.window.showQuickPick(pickItems, { placeHolder: "Select main class" }) : pickItems[0]; + if (selection && selection.item) { + this.debugHistory.updateMRUTimestamp(selection.item); return selection.item; } else { return undefined; } } + + private isOpenedInActiveEditor(file: string): boolean { + const activeEditor: vscode.TextEditor = vscode.window.activeTextEditor; + const currentActiveFile: string = activeEditor ? activeEditor.document.uri.fsPath : undefined; + + return file && currentActiveFile && path.relative(file, currentActiveFile) === ""; + } + + private formatRecentlyUsedMainClassOptions(options: IMainClassOption[]): IMainClassQuickPickItem[] { + // Sort the Main Class options with the recently used timestamp. + options.sort((a: IMainClassOption, b: IMainClassOption) => { + return this.debugHistory.getMRUTimestamp(b) - this.debugHistory.getMRUTimestamp(a); + }); + + // Move the Main Class from Active Editor to the top. + // If it's not the most recently used one, then put it as the second. + let positionForActiveEditor = options.findIndex((value: IMainClassOption) => { + return this.isOpenedInActiveEditor(value.filePath); + }); + if (positionForActiveEditor >= 1) { + let newPosition = 0; + if (this.debugHistory.contains(options[0])) { + newPosition = 1; + } + + if (newPosition !== positionForActiveEditor) { + const update: IMainClassOption[] = options.splice(positionForActiveEditor, 1); + options.splice(newPosition, 0, ...update); + positionForActiveEditor = newPosition; + } + } + + const pickItems: IMainClassQuickPickItem[] = this.formatMainClassOptions(options); + + if (this.debugHistory.contains(options[0])) { + pickItems[0].detail = "$(clock) recently used"; + } + + if (positionForActiveEditor >= 0) { + if (pickItems[positionForActiveEditor].detail) { + pickItems[positionForActiveEditor].detail += `, active editor (${path.basename(options[positionForActiveEditor].filePath)})`; + } else { + pickItems[positionForActiveEditor].detail = `$(clock) active editor (${path.basename(options[positionForActiveEditor].filePath)})`; + } + } + + return pickItems; + } + + private formatMainClassOptions(options: IMainClassOption[]): IMainClassQuickPickItem[] { + return options.map((item) => { + let label = item.mainClass; + let description = `main class: ${item.mainClass}`; + if (item.projectName) { + label += `<${item.projectName}>`; + description += ` | project name: ${item.projectName}`; + } + + return { + label, + description, + detail: null, + item, + }; + }); + } } function startDebugSession() { @@ -288,6 +346,31 @@ function convertLogLevel(commonLogLevel: string) { } interface IMainClassOption { - readonly projectName?: string; readonly mainClass: string; + readonly projectName?: string; + readonly filePath?: string; +} + +interface IMainClassQuickPickItem extends vscode.QuickPickItem { + item: IMainClassOption; +} + +class MostRecentlyUsedHistory { + private cache: { [key: string]: number } = {}; + + public getMRUTimestamp(mainClassOption: IMainClassOption): number { + return this.cache[this.getKey(mainClassOption)] || 0; + } + + public updateMRUTimestamp(mainClassOption: IMainClassOption): void { + this.cache[this.getKey(mainClassOption)] = Date.now(); + } + + public contains(mainClassOption: IMainClassOption): boolean { + return Boolean(this.cache[this.getKey(mainClassOption)]); + } + + private getKey(mainClassOption: IMainClassOption): string { + return mainClassOption.mainClass + "|" + mainClassOption.projectName; + } }