import { ApplicationError } from '@theia/core/lib/common/application-error'; import { nls } from '@theia/core/lib/common/nls'; import type { Location, Position, Range, } from '@theia/core/shared/vscode-languageserver-protocol'; import type { CompileSummary as ApiCompileSummary } from 'vscode-arduino-api'; import type { BoardUserField, Installable } from '../../common/protocol/'; import { isPortIdentifier, PortIdentifier, Programmer } from './boards-service'; import type { IndexUpdateSummary } from './notification-service'; import type { Sketch } from './sketches-service'; export const CompilerWarningLiterals = [ 'None', 'Default', 'More', 'All', ] as const; export type CompilerWarnings = (typeof CompilerWarningLiterals)[number]; export namespace CompilerWarnings { export function labelOf(warning: CompilerWarnings): string { return CompilerWarningLabels[warning]; } const CompilerWarningLabels: Record = { None: nls.localize('arduino/core/compilerWarnings/none', 'None'), Default: nls.localize('arduino/core/compilerWarnings/default', 'Default'), More: nls.localize('arduino/core/compilerWarnings/more', 'More'), All: nls.localize('arduino/core/compilerWarnings/all', 'All'), }; } export namespace CoreError { export interface ErrorLocationRef { readonly message: string; readonly location: Location; readonly details?: string; } export namespace ErrorLocationRef { export function equals( left: ErrorLocationRef, right: ErrorLocationRef ): boolean { return ( left.message === right.message && left.details === right.details && equalsLocation(left.location, right.location) ); } function equalsLocation(left: Location, right: Location): boolean { return left.uri === right.uri && equalsRange(left.range, right.range); } function equalsRange(left: Range, right: Range): boolean { return ( equalsPosition(left.start, right.start) && equalsPosition(left.end, right.end) ); } function equalsPosition(left: Position, right: Position): boolean { return left.character === right.character && left.line === right.line; } } export interface ErrorLocation extends ErrorLocationRef { /** * The range of the error location source from the CLI output. */ readonly rangesInOutput: Range[]; // The same error might show up multiple times in the CLI output: https://github.com/arduino/arduino-cli/issues/1761 } export const Codes = { Verify: 4001, Upload: 4002, UploadUsingProgrammer: 4003, BurnBootloader: 4004, }; export const VerifyFailed = declareCoreError(Codes.Verify); export const UploadFailed = declareCoreError(Codes.Upload); export const UploadUsingProgrammerFailed = declareCoreError( Codes.UploadUsingProgrammer ); export const BurnBootloaderFailed = declareCoreError(Codes.BurnBootloader); export function is( error: unknown ): error is ApplicationError { return ( error instanceof Error && ApplicationError.is(error) && Object.values(Codes).includes(error.code) ); } function declareCoreError( code: number ): ApplicationError.Constructor { return ApplicationError.declare( code, (message: string, data: ErrorLocation[]) => { return { data, message, }; } ); } } export interface InstalledPlatformReference { readonly id: string; readonly version: Installable.Version; /** * Absolute filesystem path. */ readonly installDir: string; readonly packageUrl: string; } export interface ExecutableSectionSize { readonly name: string; readonly size: number; readonly maxSize: number; } export interface CompileSummary { readonly buildPath: string; /** * To be compatible with the `vscode-arduino-tools` API. * @deprecated Use `buildPath` instead. Use Theia or VS Code URI to convert to an URI string on the client side. */ readonly buildOutputUri: string; readonly usedLibraries: ApiCompileSummary['usedLibraries']; readonly executableSectionsSize: ExecutableSectionSize[]; readonly boardPlatform?: InstalledPlatformReference | undefined; readonly buildPlatform?: InstalledPlatformReference | undefined; readonly buildProperties: string[]; } export function isCompileSummary(arg: unknown): arg is CompileSummary { return ( Boolean(arg) && typeof arg === 'object' && (arg).buildPath !== undefined && typeof (arg).buildPath === 'string' && (arg).buildOutputUri !== undefined && typeof (arg).buildOutputUri === 'string' && (arg).executableSectionsSize !== undefined && Array.isArray((arg).executableSectionsSize) && (arg).usedLibraries !== undefined && Array.isArray((arg).usedLibraries) && (arg).buildProperties !== undefined && Array.isArray((arg).buildProperties) ); } export interface UploadResponse { readonly portAfterUpload: PortIdentifier; } export function isUploadResponse(arg: unknown): arg is UploadResponse { return ( Boolean(arg) && typeof arg === 'object' && isPortIdentifier((arg).portAfterUpload) ); } export const CoreServicePath = '/services/core-service'; export const CoreService = Symbol('CoreService'); export interface CoreService { compile(options: CoreService.Options.Compile): Promise; upload(options: CoreService.Options.Upload): Promise; burnBootloader(options: CoreService.Options.Bootloader): Promise; /** * Refreshes the underling core gRPC client for the Arduino CLI. */ refresh(): Promise; /** * Updates the index of the given index types and refreshes (`init`) the underlying core gRPC client. * If `types` is empty, only the refresh part will be executed. */ updateIndex({ types }: { types: IndexType[] }): Promise; /** * If the IDE2 detects invalid or missing indexes on core client init, * IDE2 tries to update the indexes before the first frontend connects. * Use this method to determine whether the backend has already updated * the indexes before updating them. * * If yes, the connected frontend can update the local storage with the most * recent index update date-time for a particular index type, * and IDE2 can avoid the double indexes update. */ indexUpdateSummaryBeforeInit(): Promise>; } export const IndexTypeLiterals = ['platform', 'library'] as const; export type IndexType = (typeof IndexTypeLiterals)[number]; export namespace IndexType { export function is(arg: unknown): arg is IndexType { return ( typeof arg === 'string' && IndexTypeLiterals.includes(arg as IndexType) ); } export const All: IndexType[] = IndexTypeLiterals.filter(is); } export namespace CoreService { export namespace Options { export interface Base { readonly fqbn?: string | undefined; readonly verbose: boolean; // TODO: (API) why not optional with a default false? readonly progressId?: string; } export interface SketchBased { readonly sketch: Sketch; } export interface BoardBased { readonly port?: PortIdentifier; readonly programmer?: Programmer | undefined; /** * For the _Verify after upload_ setting. */ readonly verify: boolean; // TODO: (API) why not optional with false as the default value? } export interface Compile extends Base, SketchBased { readonly optimizeForDebug: boolean; // TODO: (API) make this optional readonly sourceOverride: Record; // TODO: (API) make this optional readonly exportBinaries?: boolean; readonly compilerWarnings?: CompilerWarnings; } export interface Upload extends Base, SketchBased, BoardBased { readonly userFields: BoardUserField[]; readonly usingProgrammer?: boolean; } export interface Bootloader extends Base, BoardBased {} } }