Verified Commit 2dcbc719 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska 2️⃣ Committed by GitLab
Browse files

feat: Disable Duo button when LS fails to start

parent bab6fcf6
Loading
Loading
Loading
Loading
+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,
@@ -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}`,
      );
    });
  });
});
+32 −1
Original line number Diff line number Diff line
@@ -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,
@@ -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;

@@ -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();
  }
}

+1 −1
Original line number Diff line number Diff line
@@ -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,