diff --git a/extension/src/editor/AspireCodeLensProvider.ts b/extension/src/editor/AspireCodeLensProvider.ts index fea20d62065..1efd5be2142 100644 --- a/extension/src/editor/AspireCodeLensProvider.ts +++ b/extension/src/editor/AspireCodeLensProvider.ts @@ -27,6 +27,7 @@ import { codeLensViewLogs, codeLensCommand, } from '../loc/strings'; +import { isNotNullOrUndefined } from '../utils/typeGuards'; export class AspireCodeLensProvider implements vscode.CodeLensProvider { private readonly _onDidChangeCodeLenses = new vscode.EventEmitter(); @@ -200,6 +201,8 @@ export class AspireCodeLensProvider implements vscode.CodeLensProvider { } export function getCodeLensStateLabel(state: string, stateStyle: string, exitCode?: number | null): string { + const nonZeroExitCode = isNotNullOrUndefined(exitCode) && exitCode !== 0 ? exitCode : undefined; + switch (state) { case ResourceState.Running: case ResourceState.Active: @@ -226,9 +229,9 @@ export function getCodeLensStateLabel(state: string, stateStyle: string, exitCod case ResourceState.Exited: case ResourceState.Stopped: if (stateStyle === StateStyle.Error) { - return exitCode != null && exitCode !== 0 ? codeLensResourceStoppedErrorWithExitCode(exitCode) : codeLensResourceStoppedError; + return nonZeroExitCode !== undefined ? codeLensResourceStoppedErrorWithExitCode(nonZeroExitCode) : codeLensResourceStoppedError; } - return exitCode != null && exitCode !== 0 ? codeLensResourceStoppedWithExitCode(exitCode) : codeLensResourceStopped; + return nonZeroExitCode !== undefined ? codeLensResourceStoppedWithExitCode(nonZeroExitCode) : codeLensResourceStopped; default: return state || codeLensResourceStopped; } diff --git a/extension/src/editor/AspireGutterDecorationProvider.ts b/extension/src/editor/AspireGutterDecorationProvider.ts index 15461949d04..2bc88a7a34d 100644 --- a/extension/src/editor/AspireGutterDecorationProvider.ts +++ b/extension/src/editor/AspireGutterDecorationProvider.ts @@ -6,6 +6,7 @@ import './parsers/jsTsAppHostParser'; import { AspireAppHostTreeProvider } from '../views/AspireAppHostTreeProvider'; import { findResourceState, findWorkspaceResourceState } from './resourceStateUtils'; import { ResourceState, StateStyle, HealthStatus } from './resourceConstants'; +import { isNotNullOrUndefined } from '../utils/typeGuards'; type GutterCategory = 'running' | 'warning' | 'error' | 'starting' | 'stopped' | 'completed'; @@ -90,7 +91,7 @@ function classifyState(state: string, stateStyle: string, healthStatus: string, case ResourceState.Finished: case ResourceState.Exited: case ResourceState.Stopped: - if (stateStyle === StateStyle.Error || (exitCode != null && exitCode !== 0)) { + if (stateStyle === StateStyle.Error || (isNotNullOrUndefined(exitCode) && exitCode !== 0)) { return 'error'; } return 'completed'; diff --git a/extension/src/utils/typeGuards.ts b/extension/src/utils/typeGuards.ts new file mode 100644 index 00000000000..cf6451367da --- /dev/null +++ b/extension/src/utils/typeGuards.ts @@ -0,0 +1,3 @@ +export function isNotNullOrUndefined(value: T | null | undefined): value is T { + return value !== null && value !== undefined; +} diff --git a/extension/src/views/AspireAppHostTreeProvider.ts b/extension/src/views/AspireAppHostTreeProvider.ts index f9c314675db..1ba6b4b3ef6 100644 --- a/extension/src/views/AspireAppHostTreeProvider.ts +++ b/extension/src/views/AspireAppHostTreeProvider.ts @@ -32,6 +32,7 @@ import { ViewMode, shortenPath, } from './AppHostDataRepository'; +import { isNotNullOrUndefined } from '../utils/typeGuards'; type TreeElement = AppHostItem | PidItem | EndpointUrlItem | ResourcesGroupItem | ResourceItem | WorkspaceResourcesItem | HealthChecksGroupItem | HealthCheckItem; @@ -194,6 +195,7 @@ export function getResourceContextValue(resource: ResourceJson): string { export function getResourceIcon(resource: ResourceJson): vscode.ThemeIcon { const state = resource.state; const health = resource.healthStatus; + const hasNonZeroExitCode = isNotNullOrUndefined(resource.exitCode) && resource.exitCode !== 0; switch (state) { case ResourceState.Running: case ResourceState.Active: @@ -210,7 +212,7 @@ export function getResourceIcon(resource: ResourceJson): vscode.ThemeIcon { case ResourceState.Finished: case ResourceState.Exited: case ResourceState.Stopped: - if (resource.stateStyle === StateStyle.Error || (resource.exitCode != null && resource.exitCode !== 0)) { + if (resource.stateStyle === StateStyle.Error || hasNonZeroExitCode) { return new vscode.ThemeIcon('error', new vscode.ThemeColor('list.errorForeground')); } return new vscode.ThemeIcon('pass', new vscode.ThemeColor('charts.green')); @@ -225,7 +227,7 @@ export function getResourceIcon(resource: ResourceJson): vscode.ThemeIcon { case ResourceState.NotStarted: return new vscode.ThemeIcon('record', new vscode.ThemeColor('descriptionForeground')); default: - if (state === null || state === undefined) { + if (!isNotNullOrUndefined(state)) { return new vscode.ThemeIcon('record', new vscode.ThemeColor('descriptionForeground')); } return new vscode.ThemeIcon('circle-filled', new vscode.ThemeColor('aspire.brandPurple')); @@ -273,7 +275,7 @@ export function buildResourceDescription(resource: ResourceJson): string { const passed = Object.values(reports).filter(r => r.status === 'Healthy').length; parts.push(resourceDescriptionHealth(passed, total)); } - if (exitCode != null && exitCode !== 0) { + if (isNotNullOrUndefined(exitCode) && exitCode !== 0) { parts.push(resourceDescriptionExitCode(exitCode)); } return parts.join(' ยท ');