Loading src/common/code_suggestions/code_suggestions_status_bar_item.test.ts +55 −0 Original line number Diff line number Diff line import * as vscode from 'vscode'; import { createFakePartial } from '../test_utils/create_fake_partial'; import { SHOW_QUICK_PICK_MENU } from '../duo_quick_pick/commands/show_quick_pick_menu'; import { LanguageServerStartupMonitor, StartupPhase, } from '../language_server/language_server_startup_monitor'; import { CODE_SUGGESTION_LABEL, CODE_SUGGESTIONS_STATUSES, Loading Loading @@ -105,4 +109,55 @@ describe('code suggestions status bar item', () => { it('uses correct command for toggling showing the quick pick menu', () => { expect(fakeStatusBarItem.command).toBe(SHOW_QUICK_PICK_MENU); }); describe('when LanguageServerStartupMonitor is provided', () => { let onDidChangePhaseHandler: (phase: StartupPhase) => void; let startupMonitor: LanguageServerStartupMonitor; beforeEach(() => { startupMonitor = createFakePartial<LanguageServerStartupMonitor>({ onDidChangePhase: jest.fn(handler => { onDidChangePhaseHandler = handler; return { dispose: jest.fn() }; }), }); codeSuggestionsStatusBarItem = new CodeSuggestionsStatusBarItem( codeSuggestionsStateManager, startupMonitor, ); }); it('shows error state when LS phase is failed', () => { onDidChangePhaseHandler('failed'); expect(fakeStatusBarItem.text).toBe( `$(gitlab-code-suggestions-error) ${CODE_SUGGESTION_LABEL}`, ); expect(fakeStatusBarItem.tooltip).toBe( 'GitLab Duo is disabled: Language Server failed to start.', ); expect(fakeStatusBarItem.backgroundColor).toStrictEqual( new vscode.ThemeColor('statusBarItem.errorBackground'), ); expect(fakeStatusBarItem.command).toBeUndefined(); }); it('ignores state manager updates after LS failure', () => { onDidChangePhaseHandler('failed'); visibleState = VisibleCodeSuggestionsState.READY; notifyStateChange(); expect(fakeStatusBarItem.text).toBe( `$(gitlab-code-suggestions-error) ${CODE_SUGGESTION_LABEL}`, ); }); it('does not change state for non-failed phases', () => { onDidChangePhaseHandler('running'); expect(fakeStatusBarItem.text).toBe( `$(${CODE_SUGGESTIONS_STATUSES[DefaultVisibleState].iconName}) ${CODE_SUGGESTION_LABEL}`, ); }); }); }); src/common/code_suggestions/code_suggestions_status_bar_item.ts +32 −1 Original line number Diff line number Diff line Loading @@ -2,6 +2,10 @@ import * as vscode from 'vscode'; import { StatusBarItemUI, createStatusBarItem } from '../utils/status_bar_item'; import { SHOW_QUICK_PICK_MENU } from '../duo_quick_pick/commands/show_quick_pick_menu'; import { log } from '../log'; import { LanguageServerStartupMonitor, StartupPhase, } from '../language_server/language_server_startup_monitor'; import { CodeSuggestionsStateManager, VisibleCodeSuggestionsState, Loading Loading @@ -78,12 +82,23 @@ export const CODE_SUGGESTIONS_STATUSES: Record<VisibleCodeSuggestionsState, Stat }, }; const LS_FAILED_UI: StatusBarItemUI = { iconName: 'gitlab-code-suggestions-error', tooltip: 'GitLab Duo is disabled: Language Server failed to start.', backgroundColor: errorBackgroundColor, }; export class CodeSuggestionsStatusBarItem { codeSuggestionsStatusBarItem?: vscode.StatusBarItem; #codeSuggestionsStateSubscription?: vscode.Disposable; #startupMonitorSubscription?: vscode.Disposable; #lsFailed = false; updateCodeSuggestionsItem(state: VisibleCodeSuggestionsState) { if (this.#lsFailed) return; if (!this.codeSuggestionsStatusBarItem) return; if (state === VisibleCodeSuggestionsState.LOADING) return; Loading Loading @@ -113,16 +128,32 @@ export class CodeSuggestionsStatusBarItem { } } constructor(state: CodeSuggestionsStateManager) { constructor(state: CodeSuggestionsStateManager, startupMonitor?: LanguageServerStartupMonitor) { this.codeSuggestionsStatusBarItem = createCodeSuggestionStatusBarItem(); this.updateCodeSuggestionsItem(state.getVisibleState()); this.#codeSuggestionsStateSubscription = state.onDidChangeVisibleState(e => this.updateCodeSuggestionsItem(e), ); if (startupMonitor) { this.#startupMonitorSubscription = startupMonitor.onDidChangePhase( this.#onStartupPhaseChange, ); } } #onStartupPhaseChange = (phase: StartupPhase): void => { if (phase !== 'failed') return; if (!this.codeSuggestionsStatusBarItem) return; this.#lsFailed = true; this.codeSuggestionsStatusBarItem.text = `$(${LS_FAILED_UI.iconName}) ${CODE_SUGGESTION_LABEL}`; this.codeSuggestionsStatusBarItem.tooltip = LS_FAILED_UI.tooltip; this.codeSuggestionsStatusBarItem.backgroundColor = LS_FAILED_UI.backgroundColor; this.codeSuggestionsStatusBarItem.command = undefined; }; dispose(): void { this.#codeSuggestionsStateSubscription?.dispose(); this.#startupMonitorSubscription?.dispose(); } } Loading src/common/language_server/language_server_manager.ts +1 −1 Original line number Diff line number Diff line Loading @@ -110,13 +110,13 @@ export class LanguageServerManager implements WebviewManager, VersionProvider { this.#context, this.#languageServerFeatureStateProvider, ); const statusBarItem = new CodeSuggestionsStatusBarItem(stateManager); const gutterIcon = new CodeSuggestionsGutterIcon(this.#context, stateManager); const middleware = new LanguageClientMiddleware(stateManager); const startupMonitor = new LanguageServerStartupMonitor( gitLabTelemetryEnvironment, observabilityService, ); const statusBarItem = new CodeSuggestionsStatusBarItem(stateManager, startupMonitor); const startupStatusBarItem = new LanguageServerStartupStatusBarItem(startupMonitor); const baseAssetsUrl = vscode.Uri.joinPath( this.#context.extensionUri, Loading Loading
src/common/code_suggestions/code_suggestions_status_bar_item.test.ts +55 −0 Original line number Diff line number Diff line import * as vscode from 'vscode'; import { createFakePartial } from '../test_utils/create_fake_partial'; import { SHOW_QUICK_PICK_MENU } from '../duo_quick_pick/commands/show_quick_pick_menu'; import { LanguageServerStartupMonitor, StartupPhase, } from '../language_server/language_server_startup_monitor'; import { CODE_SUGGESTION_LABEL, CODE_SUGGESTIONS_STATUSES, Loading Loading @@ -105,4 +109,55 @@ describe('code suggestions status bar item', () => { it('uses correct command for toggling showing the quick pick menu', () => { expect(fakeStatusBarItem.command).toBe(SHOW_QUICK_PICK_MENU); }); describe('when LanguageServerStartupMonitor is provided', () => { let onDidChangePhaseHandler: (phase: StartupPhase) => void; let startupMonitor: LanguageServerStartupMonitor; beforeEach(() => { startupMonitor = createFakePartial<LanguageServerStartupMonitor>({ onDidChangePhase: jest.fn(handler => { onDidChangePhaseHandler = handler; return { dispose: jest.fn() }; }), }); codeSuggestionsStatusBarItem = new CodeSuggestionsStatusBarItem( codeSuggestionsStateManager, startupMonitor, ); }); it('shows error state when LS phase is failed', () => { onDidChangePhaseHandler('failed'); expect(fakeStatusBarItem.text).toBe( `$(gitlab-code-suggestions-error) ${CODE_SUGGESTION_LABEL}`, ); expect(fakeStatusBarItem.tooltip).toBe( 'GitLab Duo is disabled: Language Server failed to start.', ); expect(fakeStatusBarItem.backgroundColor).toStrictEqual( new vscode.ThemeColor('statusBarItem.errorBackground'), ); expect(fakeStatusBarItem.command).toBeUndefined(); }); it('ignores state manager updates after LS failure', () => { onDidChangePhaseHandler('failed'); visibleState = VisibleCodeSuggestionsState.READY; notifyStateChange(); expect(fakeStatusBarItem.text).toBe( `$(gitlab-code-suggestions-error) ${CODE_SUGGESTION_LABEL}`, ); }); it('does not change state for non-failed phases', () => { onDidChangePhaseHandler('running'); expect(fakeStatusBarItem.text).toBe( `$(${CODE_SUGGESTIONS_STATUSES[DefaultVisibleState].iconName}) ${CODE_SUGGESTION_LABEL}`, ); }); }); });
src/common/code_suggestions/code_suggestions_status_bar_item.ts +32 −1 Original line number Diff line number Diff line Loading @@ -2,6 +2,10 @@ import * as vscode from 'vscode'; import { StatusBarItemUI, createStatusBarItem } from '../utils/status_bar_item'; import { SHOW_QUICK_PICK_MENU } from '../duo_quick_pick/commands/show_quick_pick_menu'; import { log } from '../log'; import { LanguageServerStartupMonitor, StartupPhase, } from '../language_server/language_server_startup_monitor'; import { CodeSuggestionsStateManager, VisibleCodeSuggestionsState, Loading Loading @@ -78,12 +82,23 @@ export const CODE_SUGGESTIONS_STATUSES: Record<VisibleCodeSuggestionsState, Stat }, }; const LS_FAILED_UI: StatusBarItemUI = { iconName: 'gitlab-code-suggestions-error', tooltip: 'GitLab Duo is disabled: Language Server failed to start.', backgroundColor: errorBackgroundColor, }; export class CodeSuggestionsStatusBarItem { codeSuggestionsStatusBarItem?: vscode.StatusBarItem; #codeSuggestionsStateSubscription?: vscode.Disposable; #startupMonitorSubscription?: vscode.Disposable; #lsFailed = false; updateCodeSuggestionsItem(state: VisibleCodeSuggestionsState) { if (this.#lsFailed) return; if (!this.codeSuggestionsStatusBarItem) return; if (state === VisibleCodeSuggestionsState.LOADING) return; Loading Loading @@ -113,16 +128,32 @@ export class CodeSuggestionsStatusBarItem { } } constructor(state: CodeSuggestionsStateManager) { constructor(state: CodeSuggestionsStateManager, startupMonitor?: LanguageServerStartupMonitor) { this.codeSuggestionsStatusBarItem = createCodeSuggestionStatusBarItem(); this.updateCodeSuggestionsItem(state.getVisibleState()); this.#codeSuggestionsStateSubscription = state.onDidChangeVisibleState(e => this.updateCodeSuggestionsItem(e), ); if (startupMonitor) { this.#startupMonitorSubscription = startupMonitor.onDidChangePhase( this.#onStartupPhaseChange, ); } } #onStartupPhaseChange = (phase: StartupPhase): void => { if (phase !== 'failed') return; if (!this.codeSuggestionsStatusBarItem) return; this.#lsFailed = true; this.codeSuggestionsStatusBarItem.text = `$(${LS_FAILED_UI.iconName}) ${CODE_SUGGESTION_LABEL}`; this.codeSuggestionsStatusBarItem.tooltip = LS_FAILED_UI.tooltip; this.codeSuggestionsStatusBarItem.backgroundColor = LS_FAILED_UI.backgroundColor; this.codeSuggestionsStatusBarItem.command = undefined; }; dispose(): void { this.#codeSuggestionsStateSubscription?.dispose(); this.#startupMonitorSubscription?.dispose(); } } Loading
src/common/language_server/language_server_manager.ts +1 −1 Original line number Diff line number Diff line Loading @@ -110,13 +110,13 @@ export class LanguageServerManager implements WebviewManager, VersionProvider { this.#context, this.#languageServerFeatureStateProvider, ); const statusBarItem = new CodeSuggestionsStatusBarItem(stateManager); const gutterIcon = new CodeSuggestionsGutterIcon(this.#context, stateManager); const middleware = new LanguageClientMiddleware(stateManager); const startupMonitor = new LanguageServerStartupMonitor( gitLabTelemetryEnvironment, observabilityService, ); const statusBarItem = new CodeSuggestionsStatusBarItem(stateManager, startupMonitor); const startupStatusBarItem = new LanguageServerStartupStatusBarItem(startupMonitor); const baseAssetsUrl = vscode.Uri.joinPath( this.#context.extensionUri, Loading