mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-09 10:28:32 +00:00
Make tab width 2 spaces (#445)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
export const MainMenuManager = Symbol('MainMenuManager');
|
||||
export interface MainMenuManager {
|
||||
/**
|
||||
* Call this method if you have changed the content of the main menu (updated a toggle flag, removed/added new groups or menu items)
|
||||
* and you want to re-render it from scratch. Works for electron too.
|
||||
*/
|
||||
update(): void;
|
||||
/**
|
||||
* Call this method if you have changed the content of the main menu (updated a toggle flag, removed/added new groups or menu items)
|
||||
* and you want to re-render it from scratch. Works for electron too.
|
||||
*/
|
||||
update(): void;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { Installable } from './installable';
|
||||
|
||||
export interface ArduinoComponent {
|
||||
readonly name: string;
|
||||
readonly deprecated: boolean;
|
||||
readonly author: string;
|
||||
readonly summary: string;
|
||||
readonly description: string;
|
||||
readonly moreInfoLink?: string;
|
||||
readonly name: string;
|
||||
readonly deprecated: boolean;
|
||||
readonly author: string;
|
||||
readonly summary: string;
|
||||
readonly description: string;
|
||||
readonly moreInfoLink?: string;
|
||||
|
||||
readonly availableVersions: Installable.Version[];
|
||||
readonly installable: boolean;
|
||||
readonly availableVersions: Installable.Version[];
|
||||
readonly installable: boolean;
|
||||
|
||||
readonly installedVersion?: Installable.Version;
|
||||
readonly installedVersion?: Installable.Version;
|
||||
}
|
||||
export namespace ArduinoComponent {
|
||||
export function is(arg: any): arg is ArduinoComponent {
|
||||
return (
|
||||
!!arg &&
|
||||
'name' in arg &&
|
||||
typeof arg['name'] === 'string' &&
|
||||
'author' in arg &&
|
||||
typeof arg['author'] === 'string' &&
|
||||
'summary' in arg &&
|
||||
typeof arg['summary'] === 'string' &&
|
||||
'description' in arg &&
|
||||
typeof arg['description'] === 'string' &&
|
||||
'installable' in arg &&
|
||||
typeof arg['installable'] === 'boolean'
|
||||
);
|
||||
}
|
||||
export function is(arg: any): arg is ArduinoComponent {
|
||||
return (
|
||||
!!arg &&
|
||||
'name' in arg &&
|
||||
typeof arg['name'] === 'string' &&
|
||||
'author' in arg &&
|
||||
typeof arg['author'] === 'string' &&
|
||||
'summary' in arg &&
|
||||
typeof arg['summary'] === 'string' &&
|
||||
'description' in arg &&
|
||||
typeof arg['description'] === 'string' &&
|
||||
'installable' in arg &&
|
||||
typeof arg['installable'] === 'boolean'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const ArduinoDaemonPath = '/services/arduino-daemon';
|
||||
export const ArduinoDaemon = Symbol('ArduinoDaemon');
|
||||
export interface ArduinoDaemon {
|
||||
isRunning(): Promise<boolean>;
|
||||
isRunning(): Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -2,29 +2,29 @@ import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { AuthOptions } from '../../node/auth/types';
|
||||
|
||||
export interface AuthenticationSession {
|
||||
readonly id: string;
|
||||
readonly accessToken: string;
|
||||
readonly account: AuthenticationSessionAccountInformation;
|
||||
readonly scopes: ReadonlyArray<string>;
|
||||
readonly id: string;
|
||||
readonly accessToken: string;
|
||||
readonly account: AuthenticationSessionAccountInformation;
|
||||
readonly scopes: ReadonlyArray<string>;
|
||||
}
|
||||
export interface AuthenticationSessionAccountInformation {
|
||||
readonly id: string;
|
||||
readonly email: string;
|
||||
readonly label: string;
|
||||
readonly picture: string;
|
||||
readonly id: string;
|
||||
readonly email: string;
|
||||
readonly label: string;
|
||||
readonly picture: string;
|
||||
}
|
||||
|
||||
export const AuthenticationServicePath = '/services/authentication-service';
|
||||
export const AuthenticationService = Symbol('AuthenticationService');
|
||||
export interface AuthenticationService
|
||||
extends JsonRpcServer<AuthenticationServiceClient> {
|
||||
login(): Promise<AuthenticationSession>;
|
||||
logout(): Promise<void>;
|
||||
session(): Promise<AuthenticationSession | undefined>;
|
||||
disposeClient(client: AuthenticationServiceClient): void;
|
||||
setOptions(authOptions: AuthOptions): void;
|
||||
extends JsonRpcServer<AuthenticationServiceClient> {
|
||||
login(): Promise<AuthenticationSession>;
|
||||
logout(): Promise<void>;
|
||||
session(): Promise<AuthenticationSession | undefined>;
|
||||
disposeClient(client: AuthenticationServiceClient): void;
|
||||
setOptions(authOptions: AuthOptions): void;
|
||||
}
|
||||
|
||||
export interface AuthenticationServiceClient {
|
||||
notifySessionDidChange(session?: AuthenticationSession | undefined): void;
|
||||
notifySessionDidChange(session?: AuthenticationSession | undefined): void;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,117 +1,115 @@
|
||||
export const ConfigServicePath = '/services/config-service';
|
||||
export const ConfigService = Symbol('ConfigService');
|
||||
export interface ConfigService {
|
||||
getVersion(): Promise<
|
||||
Readonly<{ version: string; commit: string; status?: string }>
|
||||
>;
|
||||
getCliConfigFileUri(): Promise<string>;
|
||||
getConfiguration(): Promise<Config>;
|
||||
setConfiguration(config: Config): Promise<void>;
|
||||
isInDataDir(uri: string): Promise<boolean>;
|
||||
isInSketchDir(uri: string): Promise<boolean>;
|
||||
getVersion(): Promise<
|
||||
Readonly<{ version: string; commit: string; status?: string }>
|
||||
>;
|
||||
getCliConfigFileUri(): Promise<string>;
|
||||
getConfiguration(): Promise<Config>;
|
||||
setConfiguration(config: Config): Promise<void>;
|
||||
isInDataDir(uri: string): Promise<boolean>;
|
||||
isInSketchDir(uri: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface ProxySettings {
|
||||
protocol: string;
|
||||
hostname: string;
|
||||
port: string;
|
||||
username: string;
|
||||
password: string;
|
||||
protocol: string;
|
||||
hostname: string;
|
||||
port: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
export type Network = 'none' | ProxySettings;
|
||||
export namespace Network {
|
||||
export function Default(): Network {
|
||||
return {
|
||||
protocol: 'http',
|
||||
hostname: '',
|
||||
port: '',
|
||||
username: '',
|
||||
password: '',
|
||||
};
|
||||
}
|
||||
export function Default(): Network {
|
||||
return {
|
||||
protocol: 'http',
|
||||
hostname: '',
|
||||
port: '',
|
||||
username: '',
|
||||
password: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function parse(raw: string | undefined): Network {
|
||||
if (!raw) {
|
||||
return 'none';
|
||||
}
|
||||
try {
|
||||
// Patter: PROTOCOL://USER:PASS@HOSTNAME:PORT/
|
||||
const { protocol, hostname, password, username, port } = new URL(
|
||||
raw
|
||||
);
|
||||
return {
|
||||
protocol,
|
||||
hostname,
|
||||
password,
|
||||
username,
|
||||
port,
|
||||
};
|
||||
} catch {
|
||||
return 'none';
|
||||
}
|
||||
export function parse(raw: string | undefined): Network {
|
||||
if (!raw) {
|
||||
return 'none';
|
||||
}
|
||||
try {
|
||||
// Patter: PROTOCOL://USER:PASS@HOSTNAME:PORT/
|
||||
const { protocol, hostname, password, username, port } = new URL(raw);
|
||||
return {
|
||||
protocol,
|
||||
hostname,
|
||||
password,
|
||||
username,
|
||||
port,
|
||||
};
|
||||
} catch {
|
||||
return 'none';
|
||||
}
|
||||
}
|
||||
|
||||
export function stringify(network: Network): string | undefined {
|
||||
if (network === 'none') {
|
||||
return undefined;
|
||||
}
|
||||
const { protocol, hostname, password, username, port } = network;
|
||||
try {
|
||||
const defaultUrl = new URL(
|
||||
`${protocol ? protocol : 'http'}://${hostname ? hostname : '_'}`
|
||||
);
|
||||
return Object.assign(defaultUrl, {
|
||||
protocol,
|
||||
hostname,
|
||||
password,
|
||||
username,
|
||||
port,
|
||||
}).toString();
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
export function stringify(network: Network): string | undefined {
|
||||
if (network === 'none') {
|
||||
return undefined;
|
||||
}
|
||||
const { protocol, hostname, password, username, port } = network;
|
||||
try {
|
||||
const defaultUrl = new URL(
|
||||
`${protocol ? protocol : 'http'}://${hostname ? hostname : '_'}`
|
||||
);
|
||||
return Object.assign(defaultUrl, {
|
||||
protocol,
|
||||
hostname,
|
||||
password,
|
||||
username,
|
||||
port,
|
||||
}).toString();
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function sameAs(left: Network, right: Network): boolean {
|
||||
if (left === 'none') {
|
||||
return right === 'none';
|
||||
}
|
||||
if (right === 'none') {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
left.hostname === right.hostname &&
|
||||
left.password === right.password &&
|
||||
left.protocol === right.protocol &&
|
||||
left.username === right.username
|
||||
);
|
||||
export function sameAs(left: Network, right: Network): boolean {
|
||||
if (left === 'none') {
|
||||
return right === 'none';
|
||||
}
|
||||
if (right === 'none') {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
left.hostname === right.hostname &&
|
||||
left.password === right.password &&
|
||||
left.protocol === right.protocol &&
|
||||
left.username === right.username
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
readonly sketchDirUri: string;
|
||||
readonly dataDirUri: string;
|
||||
readonly downloadsDirUri: string;
|
||||
readonly additionalUrls: string[];
|
||||
readonly network: Network;
|
||||
readonly sketchDirUri: string;
|
||||
readonly dataDirUri: string;
|
||||
readonly downloadsDirUri: string;
|
||||
readonly additionalUrls: string[];
|
||||
readonly network: Network;
|
||||
}
|
||||
export namespace Config {
|
||||
export function sameAs(left: Config, right: Config): boolean {
|
||||
const leftUrls = left.additionalUrls.sort();
|
||||
const rightUrls = right.additionalUrls.sort();
|
||||
if (leftUrls.length !== rightUrls.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < leftUrls.length; i++) {
|
||||
if (leftUrls[i] !== rightUrls[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (
|
||||
left.dataDirUri === right.dataDirUri &&
|
||||
left.downloadsDirUri === right.downloadsDirUri &&
|
||||
left.sketchDirUri === right.sketchDirUri &&
|
||||
Network.sameAs(left.network, right.network)
|
||||
);
|
||||
export function sameAs(left: Config, right: Config): boolean {
|
||||
const leftUrls = left.additionalUrls.sort();
|
||||
const rightUrls = right.additionalUrls.sort();
|
||||
if (leftUrls.length !== rightUrls.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < leftUrls.length; i++) {
|
||||
if (leftUrls[i] !== rightUrls[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (
|
||||
left.dataDirUri === right.dataDirUri &&
|
||||
left.downloadsDirUri === right.downloadsDirUri &&
|
||||
left.sketchDirUri === right.sketchDirUri &&
|
||||
Network.sameAs(left.network, right.network)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
import { Programmer } from './boards-service';
|
||||
|
||||
export const CompilerWarningLiterals = [
|
||||
'None',
|
||||
'Default',
|
||||
'More',
|
||||
'All',
|
||||
'None',
|
||||
'Default',
|
||||
'More',
|
||||
'All',
|
||||
] as const;
|
||||
export type CompilerWarnings = typeof CompilerWarningLiterals[number];
|
||||
|
||||
export const CoreServicePath = '/services/core-service';
|
||||
export const CoreService = Symbol('CoreService');
|
||||
export interface CoreService {
|
||||
compile(
|
||||
options: CoreService.Compile.Options &
|
||||
Readonly<{
|
||||
exportBinaries?: boolean;
|
||||
compilerWarnings?: CompilerWarnings;
|
||||
}>
|
||||
): Promise<void>;
|
||||
upload(options: CoreService.Upload.Options): Promise<void>;
|
||||
uploadUsingProgrammer(options: CoreService.Upload.Options): Promise<void>;
|
||||
burnBootloader(options: CoreService.Bootloader.Options): Promise<void>;
|
||||
compile(
|
||||
options: CoreService.Compile.Options &
|
||||
Readonly<{
|
||||
exportBinaries?: boolean;
|
||||
compilerWarnings?: CompilerWarnings;
|
||||
}>
|
||||
): Promise<void>;
|
||||
upload(options: CoreService.Upload.Options): Promise<void>;
|
||||
uploadUsingProgrammer(options: CoreService.Upload.Options): Promise<void>;
|
||||
burnBootloader(options: CoreService.Bootloader.Options): Promise<void>;
|
||||
}
|
||||
|
||||
export namespace CoreService {
|
||||
export namespace Compile {
|
||||
export interface Options {
|
||||
/**
|
||||
* `file` URI to the sketch folder.
|
||||
*/
|
||||
readonly sketchUri: string;
|
||||
readonly fqbn?: string | undefined;
|
||||
readonly optimizeForDebug: boolean;
|
||||
readonly verbose: boolean;
|
||||
readonly sourceOverride: Record<string, string>;
|
||||
}
|
||||
export namespace Compile {
|
||||
export interface Options {
|
||||
/**
|
||||
* `file` URI to the sketch folder.
|
||||
*/
|
||||
readonly sketchUri: string;
|
||||
readonly fqbn?: string | undefined;
|
||||
readonly optimizeForDebug: boolean;
|
||||
readonly verbose: boolean;
|
||||
readonly sourceOverride: Record<string, string>;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Upload {
|
||||
export interface Options extends Compile.Options {
|
||||
readonly port?: string | undefined;
|
||||
readonly programmer?: Programmer | undefined;
|
||||
readonly verify: boolean;
|
||||
}
|
||||
export namespace Upload {
|
||||
export interface Options extends Compile.Options {
|
||||
readonly port?: string | undefined;
|
||||
readonly programmer?: Programmer | undefined;
|
||||
readonly verify: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Bootloader {
|
||||
export interface Options {
|
||||
readonly fqbn?: string | undefined;
|
||||
readonly port?: string | undefined;
|
||||
readonly programmer?: Programmer | undefined;
|
||||
readonly verbose: boolean;
|
||||
readonly verify: boolean;
|
||||
}
|
||||
export namespace Bootloader {
|
||||
export interface Options {
|
||||
readonly fqbn?: string | undefined;
|
||||
readonly port?: string | undefined;
|
||||
readonly programmer?: Programmer | undefined;
|
||||
readonly verbose: boolean;
|
||||
readonly verify: boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ import { SketchContainer } from './sketches-service';
|
||||
export const ExamplesServicePath = '/services/example-service';
|
||||
export const ExamplesService = Symbol('ExamplesService');
|
||||
export interface ExamplesService {
|
||||
builtIns(): Promise<SketchContainer[]>;
|
||||
installed(options: { fqbn?: string }): Promise<{
|
||||
user: SketchContainer[];
|
||||
current: SketchContainer[];
|
||||
any: SketchContainer[];
|
||||
}>;
|
||||
builtIns(): Promise<SketchContainer[]>;
|
||||
installed(options: { fqbn?: string }): Promise<{
|
||||
user: SketchContainer[];
|
||||
current: SketchContainer[];
|
||||
any: SketchContainer[];
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const ExecutableServicePath = '/services/executable-service';
|
||||
export const ExecutableService = Symbol('ExecutableService');
|
||||
export interface ExecutableService {
|
||||
list(): Promise<{ clangdUri: string; cliUri: string; lsUri: string }>;
|
||||
list(): Promise<{ clangdUri: string; cliUri: string; lsUri: string }>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const FileSystemExtPath = '/services/file-system-ext';
|
||||
export const FileSystemExt = Symbol('FileSystemExt');
|
||||
export interface FileSystemExt {
|
||||
getUri(fsPath: string): Promise<string>;
|
||||
getUri(fsPath: string): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as semver from 'semver';
|
||||
import { Progress } from '@theia/core/lib/common/message-service-protocol';
|
||||
import {
|
||||
CancellationToken,
|
||||
CancellationTokenSource,
|
||||
CancellationToken,
|
||||
CancellationTokenSource,
|
||||
} from '@theia/core/lib/common/cancellation';
|
||||
import { naturalCompare } from './../utils';
|
||||
import { ArduinoComponent } from './arduino-component';
|
||||
@@ -10,123 +10,123 @@ import { MessageService } from '@theia/core';
|
||||
import { ResponseServiceImpl } from '../../browser/response-service-impl';
|
||||
|
||||
export interface Installable<T extends ArduinoComponent> {
|
||||
/**
|
||||
* If `options.version` is specified, that will be installed. Otherwise, `item.availableVersions[0]`.
|
||||
*/
|
||||
install(options: {
|
||||
item: T;
|
||||
progressId?: string;
|
||||
version?: Installable.Version;
|
||||
}): Promise<void>;
|
||||
/**
|
||||
* If `options.version` is specified, that will be installed. Otherwise, `item.availableVersions[0]`.
|
||||
*/
|
||||
install(options: {
|
||||
item: T;
|
||||
progressId?: string;
|
||||
version?: Installable.Version;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Uninstalls the given component. It is a NOOP if not installed.
|
||||
*/
|
||||
uninstall(options: { item: T; progressId?: string }): Promise<void>;
|
||||
/**
|
||||
* Uninstalls the given component. It is a NOOP if not installed.
|
||||
*/
|
||||
uninstall(options: { item: T; progressId?: string }): Promise<void>;
|
||||
}
|
||||
export namespace Installable {
|
||||
export type Version = string;
|
||||
export type Version = string;
|
||||
|
||||
export namespace Version {
|
||||
/**
|
||||
* Most recent version comes first, then the previous versions. (`1.8.1`, `1.6.3`, `1.6.2`, `1.6.1` and so on.)
|
||||
*/
|
||||
export const COMPARATOR = (left: Version, right: Version) => {
|
||||
if (semver.valid(left) && semver.valid(right)) {
|
||||
return semver.compare(left, right);
|
||||
export namespace Version {
|
||||
/**
|
||||
* Most recent version comes first, then the previous versions. (`1.8.1`, `1.6.3`, `1.6.2`, `1.6.1` and so on.)
|
||||
*/
|
||||
export const COMPARATOR = (left: Version, right: Version) => {
|
||||
if (semver.valid(left) && semver.valid(right)) {
|
||||
return semver.compare(left, right);
|
||||
}
|
||||
return naturalCompare(left, right);
|
||||
};
|
||||
}
|
||||
|
||||
export async function installWithProgress<
|
||||
T extends ArduinoComponent
|
||||
>(options: {
|
||||
installable: Installable<T>;
|
||||
messageService: MessageService;
|
||||
responseService: ResponseServiceImpl;
|
||||
item: T;
|
||||
version: Installable.Version;
|
||||
}): Promise<void> {
|
||||
const { item, version } = options;
|
||||
return doWithProgress({
|
||||
...options,
|
||||
progressText: `Processing ${item.name}:${version}`,
|
||||
run: ({ progressId }) =>
|
||||
options.installable.install({
|
||||
item: options.item,
|
||||
version: options.version,
|
||||
progressId,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function uninstallWithProgress<
|
||||
T extends ArduinoComponent
|
||||
>(options: {
|
||||
installable: Installable<T>;
|
||||
messageService: MessageService;
|
||||
responseService: ResponseServiceImpl;
|
||||
item: T;
|
||||
}): Promise<void> {
|
||||
const { item } = options;
|
||||
return doWithProgress({
|
||||
...options,
|
||||
progressText: `Processing ${item.name}${
|
||||
item.installedVersion ? `:${item.installedVersion}` : ''
|
||||
}`,
|
||||
run: ({ progressId }) =>
|
||||
options.installable.uninstall({
|
||||
item: options.item,
|
||||
progressId,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function doWithProgress(options: {
|
||||
run: ({ progressId }: { progressId: string }) => Promise<void>;
|
||||
messageService: MessageService;
|
||||
responseService: ResponseServiceImpl;
|
||||
progressText: string;
|
||||
}): Promise<void> {
|
||||
return withProgress(
|
||||
options.progressText,
|
||||
options.messageService,
|
||||
async (progress, _) => {
|
||||
const progressId = progress.id;
|
||||
const toDispose = options.responseService.onProgressDidChange(
|
||||
(progressMessage) => {
|
||||
if (progressId === progressMessage.progressId) {
|
||||
const { message, work } = progressMessage;
|
||||
progress.report({ message, work });
|
||||
}
|
||||
return naturalCompare(left, right);
|
||||
};
|
||||
}
|
||||
|
||||
export async function installWithProgress<
|
||||
T extends ArduinoComponent
|
||||
>(options: {
|
||||
installable: Installable<T>;
|
||||
messageService: MessageService;
|
||||
responseService: ResponseServiceImpl;
|
||||
item: T;
|
||||
version: Installable.Version;
|
||||
}): Promise<void> {
|
||||
const { item, version } = options;
|
||||
return doWithProgress({
|
||||
...options,
|
||||
progressText: `Processing ${item.name}:${version}`,
|
||||
run: ({ progressId }) =>
|
||||
options.installable.install({
|
||||
item: options.item,
|
||||
version: options.version,
|
||||
progressId,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function uninstallWithProgress<
|
||||
T extends ArduinoComponent
|
||||
>(options: {
|
||||
installable: Installable<T>;
|
||||
messageService: MessageService;
|
||||
responseService: ResponseServiceImpl;
|
||||
item: T;
|
||||
}): Promise<void> {
|
||||
const { item } = options;
|
||||
return doWithProgress({
|
||||
...options,
|
||||
progressText: `Processing ${item.name}${
|
||||
item.installedVersion ? `:${item.installedVersion}` : ''
|
||||
}`,
|
||||
run: ({ progressId }) =>
|
||||
options.installable.uninstall({
|
||||
item: options.item,
|
||||
progressId,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function doWithProgress(options: {
|
||||
run: ({ progressId }: { progressId: string }) => Promise<void>;
|
||||
messageService: MessageService;
|
||||
responseService: ResponseServiceImpl;
|
||||
progressText: string;
|
||||
}): Promise<void> {
|
||||
return withProgress(
|
||||
options.progressText,
|
||||
options.messageService,
|
||||
async (progress, _) => {
|
||||
const progressId = progress.id;
|
||||
const toDispose = options.responseService.onProgressDidChange(
|
||||
(progressMessage) => {
|
||||
if (progressId === progressMessage.progressId) {
|
||||
const { message, work } = progressMessage;
|
||||
progress.report({ message, work });
|
||||
}
|
||||
}
|
||||
);
|
||||
try {
|
||||
options.responseService.clearArduinoChannel();
|
||||
await options.run({ progressId });
|
||||
} finally {
|
||||
toDispose.dispose();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function withProgress(
|
||||
text: string,
|
||||
messageService: MessageService,
|
||||
cb: (progress: Progress, token: CancellationToken) => Promise<void>
|
||||
): Promise<void> {
|
||||
const cancellationSource = new CancellationTokenSource();
|
||||
const { token } = cancellationSource;
|
||||
const progress = await messageService.showProgress(
|
||||
{ text, options: { cancelable: false } },
|
||||
() => cancellationSource.cancel()
|
||||
}
|
||||
);
|
||||
try {
|
||||
await cb(progress, token);
|
||||
options.responseService.clearArduinoChannel();
|
||||
await options.run({ progressId });
|
||||
} finally {
|
||||
progress.cancel();
|
||||
toDispose.dispose();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function withProgress(
|
||||
text: string,
|
||||
messageService: MessageService,
|
||||
cb: (progress: Progress, token: CancellationToken) => Promise<void>
|
||||
): Promise<void> {
|
||||
const cancellationSource = new CancellationTokenSource();
|
||||
const { token } = cancellationSource;
|
||||
const progress = await messageService.showProgress(
|
||||
{ text, options: { cancelable: false } },
|
||||
() => cancellationSource.cancel()
|
||||
);
|
||||
try {
|
||||
await cb(progress, token);
|
||||
} finally {
|
||||
progress.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,121 +5,118 @@ import { ArduinoComponent } from './arduino-component';
|
||||
export const LibraryServicePath = '/services/library-service';
|
||||
export const LibraryService = Symbol('LibraryService');
|
||||
export interface LibraryService
|
||||
extends Installable<LibraryPackage>,
|
||||
Searchable<LibraryPackage> {
|
||||
list(options: LibraryService.List.Options): Promise<LibraryPackage[]>;
|
||||
/**
|
||||
* When `installDependencies` is not set, it is `true` by default. If you want to skip the installation of required dependencies, set it to `false`.
|
||||
*/
|
||||
install(options: {
|
||||
item: LibraryPackage;
|
||||
progressId?: string;
|
||||
version?: Installable.Version;
|
||||
installDependencies?: boolean;
|
||||
}): Promise<void>;
|
||||
installZip(options: {
|
||||
zipUri: string;
|
||||
progressId?: string;
|
||||
overwrite?: boolean;
|
||||
}): Promise<void>;
|
||||
/**
|
||||
* Set `filterSelf` to `true` if you want to avoid having `item` in the result set.
|
||||
* Note: as of today (22.02.2021), the CLI works like this: `./arduino-cli lib deps Adaino@0.1.0 ✕ Adaino 0.1.0 must be installed.`.
|
||||
*/
|
||||
listDependencies({
|
||||
item,
|
||||
version,
|
||||
filterSelf,
|
||||
}: {
|
||||
item: LibraryPackage;
|
||||
version: Installable.Version;
|
||||
filterSelf?: boolean;
|
||||
}): Promise<LibraryDependency[]>;
|
||||
extends Installable<LibraryPackage>,
|
||||
Searchable<LibraryPackage> {
|
||||
list(options: LibraryService.List.Options): Promise<LibraryPackage[]>;
|
||||
/**
|
||||
* When `installDependencies` is not set, it is `true` by default. If you want to skip the installation of required dependencies, set it to `false`.
|
||||
*/
|
||||
install(options: {
|
||||
item: LibraryPackage;
|
||||
progressId?: string;
|
||||
version?: Installable.Version;
|
||||
installDependencies?: boolean;
|
||||
}): Promise<void>;
|
||||
installZip(options: {
|
||||
zipUri: string;
|
||||
progressId?: string;
|
||||
overwrite?: boolean;
|
||||
}): Promise<void>;
|
||||
/**
|
||||
* Set `filterSelf` to `true` if you want to avoid having `item` in the result set.
|
||||
* Note: as of today (22.02.2021), the CLI works like this: `./arduino-cli lib deps Adaino@0.1.0 ✕ Adaino 0.1.0 must be installed.`.
|
||||
*/
|
||||
listDependencies({
|
||||
item,
|
||||
version,
|
||||
filterSelf,
|
||||
}: {
|
||||
item: LibraryPackage;
|
||||
version: Installable.Version;
|
||||
filterSelf?: boolean;
|
||||
}): Promise<LibraryDependency[]>;
|
||||
}
|
||||
|
||||
export namespace LibraryService {
|
||||
export namespace List {
|
||||
export interface Options {
|
||||
readonly fqbn?: string | undefined;
|
||||
}
|
||||
export namespace List {
|
||||
export interface Options {
|
||||
readonly fqbn?: string | undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum LibraryLocation {
|
||||
/**
|
||||
* In the `libraries` subdirectory of the Arduino IDE installation.
|
||||
*/
|
||||
IDE_BUILTIN = 0,
|
||||
/**
|
||||
* In the `libraries` subdirectory of the Arduino IDE installation.
|
||||
*/
|
||||
IDE_BUILTIN = 0,
|
||||
|
||||
/**
|
||||
* In the `libraries` subdirectory of the user directory (sketchbook).
|
||||
*/
|
||||
USER = 1,
|
||||
/**
|
||||
* In the `libraries` subdirectory of the user directory (sketchbook).
|
||||
*/
|
||||
USER = 1,
|
||||
|
||||
/**
|
||||
* In the `libraries` subdirectory of a platform.
|
||||
*/
|
||||
PLATFORM_BUILTIN = 2,
|
||||
/**
|
||||
* In the `libraries` subdirectory of a platform.
|
||||
*/
|
||||
PLATFORM_BUILTIN = 2,
|
||||
|
||||
/**
|
||||
* When `LibraryLocation` is used in a context where a board is specified, this indicates the library is in the `libraries`
|
||||
* subdirectory of a platform referenced by the board's platform.
|
||||
*/
|
||||
REFERENCED_PLATFORM_BUILTIN = 3,
|
||||
/**
|
||||
* When `LibraryLocation` is used in a context where a board is specified, this indicates the library is in the `libraries`
|
||||
* subdirectory of a platform referenced by the board's platform.
|
||||
*/
|
||||
REFERENCED_PLATFORM_BUILTIN = 3,
|
||||
}
|
||||
|
||||
export interface LibraryPackage extends ArduinoComponent {
|
||||
/**
|
||||
* Same as [`Library#real_name`](https://arduino.github.io/arduino-cli/latest/rpc/commands/#library).
|
||||
* Should be used for the UI, and `name` is used to uniquely identify a library. It does not have an ID.
|
||||
*/
|
||||
readonly label: string;
|
||||
/**
|
||||
* Same as [`Library#real_name`](https://arduino.github.io/arduino-cli/latest/rpc/commands/#library).
|
||||
* Should be used for the UI, and `name` is used to uniquely identify a library. It does not have an ID.
|
||||
*/
|
||||
readonly label: string;
|
||||
|
||||
/**
|
||||
* An array of string that should be included into the `ino` file if this library is used.
|
||||
* For example, including `SD` will prepend `#include <SD.h>` to the `ino` file. While including `Bridge`
|
||||
* requires multiple `#include` declarations: `YunClient`, `YunServer`, `Bridge`, etc.
|
||||
*/
|
||||
readonly includes: string[];
|
||||
readonly exampleUris: string[];
|
||||
readonly location: LibraryLocation;
|
||||
readonly installDirUri?: string;
|
||||
/**
|
||||
* An array of string that should be included into the `ino` file if this library is used.
|
||||
* For example, including `SD` will prepend `#include <SD.h>` to the `ino` file. While including `Bridge`
|
||||
* requires multiple `#include` declarations: `YunClient`, `YunServer`, `Bridge`, etc.
|
||||
*/
|
||||
readonly includes: string[];
|
||||
readonly exampleUris: string[];
|
||||
readonly location: LibraryLocation;
|
||||
readonly installDirUri?: string;
|
||||
}
|
||||
export namespace LibraryPackage {
|
||||
export function is(arg: any): arg is LibraryPackage {
|
||||
return (
|
||||
ArduinoComponent.is(arg) &&
|
||||
'includes' in arg &&
|
||||
Array.isArray(arg['includes'])
|
||||
);
|
||||
}
|
||||
export function is(arg: any): arg is LibraryPackage {
|
||||
return (
|
||||
ArduinoComponent.is(arg) &&
|
||||
'includes' in arg &&
|
||||
Array.isArray(arg['includes'])
|
||||
);
|
||||
}
|
||||
|
||||
export function equals(
|
||||
left: LibraryPackage,
|
||||
right: LibraryPackage
|
||||
): boolean {
|
||||
return left.name === right.name && left.author === right.author;
|
||||
}
|
||||
export function equals(left: LibraryPackage, right: LibraryPackage): boolean {
|
||||
return left.name === right.name && left.author === right.author;
|
||||
}
|
||||
|
||||
export function groupByLocation(packages: LibraryPackage[]): {
|
||||
user: LibraryPackage[];
|
||||
rest: LibraryPackage[];
|
||||
} {
|
||||
const user: LibraryPackage[] = [];
|
||||
const rest: LibraryPackage[] = [];
|
||||
for (const pkg of packages) {
|
||||
if (pkg.location === LibraryLocation.USER) {
|
||||
user.push(pkg);
|
||||
} else {
|
||||
rest.push(pkg);
|
||||
}
|
||||
}
|
||||
return { user, rest };
|
||||
export function groupByLocation(packages: LibraryPackage[]): {
|
||||
user: LibraryPackage[];
|
||||
rest: LibraryPackage[];
|
||||
} {
|
||||
const user: LibraryPackage[] = [];
|
||||
const rest: LibraryPackage[] = [];
|
||||
for (const pkg of packages) {
|
||||
if (pkg.location === LibraryLocation.USER) {
|
||||
user.push(pkg);
|
||||
} else {
|
||||
rest.push(pkg);
|
||||
}
|
||||
}
|
||||
return { user, rest };
|
||||
}
|
||||
}
|
||||
|
||||
export interface LibraryDependency {
|
||||
readonly name: string;
|
||||
readonly requiredVersion: Installable.Version;
|
||||
readonly installedVersion: Installable.Version;
|
||||
readonly name: string;
|
||||
readonly requiredVersion: Installable.Version;
|
||||
readonly installedVersion: Installable.Version;
|
||||
}
|
||||
|
||||
@@ -4,86 +4,86 @@ import { Board, Port } from './boards-service';
|
||||
export interface Status {}
|
||||
export type OK = Status;
|
||||
export interface ErrorStatus extends Status {
|
||||
readonly message: string;
|
||||
readonly message: string;
|
||||
}
|
||||
export namespace Status {
|
||||
export function isOK(status: Status & { message?: string }): status is OK {
|
||||
return typeof status.message !== 'string';
|
||||
}
|
||||
export const OK: OK = {};
|
||||
export const NOT_CONNECTED: ErrorStatus = { message: 'Not connected.' };
|
||||
export const ALREADY_CONNECTED: ErrorStatus = {
|
||||
message: 'Already connected.',
|
||||
};
|
||||
export function isOK(status: Status & { message?: string }): status is OK {
|
||||
return typeof status.message !== 'string';
|
||||
}
|
||||
export const OK: OK = {};
|
||||
export const NOT_CONNECTED: ErrorStatus = { message: 'Not connected.' };
|
||||
export const ALREADY_CONNECTED: ErrorStatus = {
|
||||
message: 'Already connected.',
|
||||
};
|
||||
}
|
||||
|
||||
export const MonitorServicePath = '/services/serial-monitor';
|
||||
export const MonitorService = Symbol('MonitorService');
|
||||
export interface MonitorService extends JsonRpcServer<MonitorServiceClient> {
|
||||
connect(config: MonitorConfig): Promise<Status>;
|
||||
disconnect(): Promise<Status>;
|
||||
send(message: string): Promise<Status>;
|
||||
request(): Promise<{ message: string }>;
|
||||
connect(config: MonitorConfig): Promise<Status>;
|
||||
disconnect(): Promise<Status>;
|
||||
send(message: string): Promise<Status>;
|
||||
request(): Promise<{ message: string }>;
|
||||
}
|
||||
|
||||
export interface MonitorConfig {
|
||||
readonly board: Board;
|
||||
readonly port: Port;
|
||||
/**
|
||||
* Defaults to [`SERIAL`](MonitorConfig#ConnectionType#SERIAL).
|
||||
*/
|
||||
readonly type?: MonitorConfig.ConnectionType;
|
||||
/**
|
||||
* Defaults to `9600`.
|
||||
*/
|
||||
readonly baudRate?: MonitorConfig.BaudRate;
|
||||
readonly board: Board;
|
||||
readonly port: Port;
|
||||
/**
|
||||
* Defaults to [`SERIAL`](MonitorConfig#ConnectionType#SERIAL).
|
||||
*/
|
||||
readonly type?: MonitorConfig.ConnectionType;
|
||||
/**
|
||||
* Defaults to `9600`.
|
||||
*/
|
||||
readonly baudRate?: MonitorConfig.BaudRate;
|
||||
}
|
||||
export namespace MonitorConfig {
|
||||
export type BaudRate =
|
||||
| 300
|
||||
| 1200
|
||||
| 2400
|
||||
| 4800
|
||||
| 9600
|
||||
| 19200
|
||||
| 38400
|
||||
| 57600
|
||||
| 115200;
|
||||
export namespace BaudRate {
|
||||
export const DEFAULT: BaudRate = 9600;
|
||||
}
|
||||
export type BaudRate =
|
||||
| 300
|
||||
| 1200
|
||||
| 2400
|
||||
| 4800
|
||||
| 9600
|
||||
| 19200
|
||||
| 38400
|
||||
| 57600
|
||||
| 115200;
|
||||
export namespace BaudRate {
|
||||
export const DEFAULT: BaudRate = 9600;
|
||||
}
|
||||
|
||||
export enum ConnectionType {
|
||||
SERIAL = 0,
|
||||
}
|
||||
export enum ConnectionType {
|
||||
SERIAL = 0,
|
||||
}
|
||||
}
|
||||
|
||||
export const MonitorServiceClient = Symbol('MonitorServiceClient');
|
||||
export interface MonitorServiceClient {
|
||||
notifyError(event: MonitorError): void;
|
||||
notifyError(event: MonitorError): void;
|
||||
}
|
||||
|
||||
export interface MonitorError {
|
||||
readonly message: string;
|
||||
/**
|
||||
* If no `code` is available, clients must reestablish the serial-monitor connection.
|
||||
*/
|
||||
readonly code: number | undefined;
|
||||
readonly config: MonitorConfig;
|
||||
readonly message: string;
|
||||
/**
|
||||
* If no `code` is available, clients must reestablish the serial-monitor connection.
|
||||
*/
|
||||
readonly code: number | undefined;
|
||||
readonly config: MonitorConfig;
|
||||
}
|
||||
export namespace MonitorError {
|
||||
export namespace ErrorCodes {
|
||||
/**
|
||||
* The frontend has refreshed the browser, for instance.
|
||||
*/
|
||||
export const CLIENT_CANCEL = 1;
|
||||
/**
|
||||
* When detaching a physical device when the duplex channel is still opened.
|
||||
*/
|
||||
export const DEVICE_NOT_CONFIGURED = 2;
|
||||
/**
|
||||
* Another serial monitor was opened on this port. For another electron-instance, Java IDE.
|
||||
*/
|
||||
export const DEVICE_BUSY = 3;
|
||||
}
|
||||
export namespace ErrorCodes {
|
||||
/**
|
||||
* The frontend has refreshed the browser, for instance.
|
||||
*/
|
||||
export const CLIENT_CANCEL = 1;
|
||||
/**
|
||||
* When detaching a physical device when the duplex channel is still opened.
|
||||
*/
|
||||
export const DEVICE_NOT_CONFIGURED = 2;
|
||||
/**
|
||||
* Another serial monitor was opened on this port. For another electron-instance, Java IDE.
|
||||
*/
|
||||
export const DEVICE_BUSY = 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import { LibraryPackage } from './library-service';
|
||||
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import {
|
||||
Sketch,
|
||||
Config,
|
||||
BoardsPackage,
|
||||
AttachedBoardsChangeEvent,
|
||||
Sketch,
|
||||
Config,
|
||||
BoardsPackage,
|
||||
AttachedBoardsChangeEvent,
|
||||
} from '../protocol';
|
||||
|
||||
export interface NotificationServiceClient {
|
||||
notifyIndexUpdated(): void;
|
||||
notifyDaemonStarted(): void;
|
||||
notifyDaemonStopped(): void;
|
||||
notifyConfigChanged(event: { config: Config | undefined }): void;
|
||||
notifyPlatformInstalled(event: { item: BoardsPackage }): void;
|
||||
notifyPlatformUninstalled(event: { item: BoardsPackage }): void;
|
||||
notifyLibraryInstalled(event: { item: LibraryPackage }): void;
|
||||
notifyLibraryUninstalled(event: { item: LibraryPackage }): void;
|
||||
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void;
|
||||
notifyRecentSketchesChanged(event: { sketches: Sketch[] }): void;
|
||||
notifyIndexUpdated(): void;
|
||||
notifyDaemonStarted(): void;
|
||||
notifyDaemonStopped(): void;
|
||||
notifyConfigChanged(event: { config: Config | undefined }): void;
|
||||
notifyPlatformInstalled(event: { item: BoardsPackage }): void;
|
||||
notifyPlatformUninstalled(event: { item: BoardsPackage }): void;
|
||||
notifyLibraryInstalled(event: { item: LibraryPackage }): void;
|
||||
notifyLibraryUninstalled(event: { item: LibraryPackage }): void;
|
||||
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void;
|
||||
notifyRecentSketchesChanged(event: { sketches: Sketch[] }): void;
|
||||
}
|
||||
|
||||
export const NotificationServicePath = '/services/notification-service';
|
||||
export const NotificationServiceServer = Symbol('NotificationServiceServer');
|
||||
export interface NotificationServiceServer
|
||||
extends Required<NotificationServiceClient>,
|
||||
JsonRpcServer<NotificationServiceClient> {
|
||||
disposeClient(client: NotificationServiceClient): void;
|
||||
extends Required<NotificationServiceClient>,
|
||||
JsonRpcServer<NotificationServiceClient> {
|
||||
disposeClient(client: NotificationServiceClient): void;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export interface OutputMessage {
|
||||
readonly chunk: string;
|
||||
readonly severity?: 'error' | 'warning' | 'info'; // Currently not used!
|
||||
readonly chunk: string;
|
||||
readonly severity?: 'error' | 'warning' | 'info'; // Currently not used!
|
||||
}
|
||||
|
||||
export interface ProgressMessage {
|
||||
readonly progressId: string;
|
||||
readonly message: string;
|
||||
readonly work?: ProgressMessage.Work;
|
||||
readonly progressId: string;
|
||||
readonly message: string;
|
||||
readonly work?: ProgressMessage.Work;
|
||||
}
|
||||
export namespace ProgressMessage {
|
||||
export interface Work {
|
||||
readonly done: number;
|
||||
readonly total: number;
|
||||
}
|
||||
export interface Work {
|
||||
readonly done: number;
|
||||
readonly total: number;
|
||||
}
|
||||
}
|
||||
|
||||
export const ResponseServicePath = '/services/response-service';
|
||||
export const ResponseService = Symbol('ResponseService');
|
||||
export interface ResponseService {
|
||||
appendToOutput(message: OutputMessage): void;
|
||||
reportProgress(message: ProgressMessage): void;
|
||||
appendToOutput(message: OutputMessage): void;
|
||||
reportProgress(message: ProgressMessage): void;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
export interface Searchable<T> {
|
||||
search(options: Searchable.Options): Promise<T[]>;
|
||||
search(options: Searchable.Options): Promise<T[]>;
|
||||
}
|
||||
export namespace Searchable {
|
||||
export interface Options {
|
||||
/**
|
||||
* Defaults to empty an empty string.
|
||||
*/
|
||||
readonly query?: string;
|
||||
}
|
||||
export interface Options {
|
||||
/**
|
||||
* Defaults to empty an empty string.
|
||||
*/
|
||||
readonly query?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,185 +14,167 @@ import { SketchContainer } from './sketches-service';
|
||||
|
||||
@injectable()
|
||||
export class SketchesServiceClientImpl
|
||||
implements FrontendApplicationContribution
|
||||
implements FrontendApplicationContribution
|
||||
{
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService;
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService;
|
||||
|
||||
@inject(SketchesService)
|
||||
protected readonly sketchService: SketchesService;
|
||||
@inject(SketchesService)
|
||||
protected readonly sketchService: SketchesService;
|
||||
|
||||
@inject(WorkspaceService)
|
||||
protected readonly workspaceService: WorkspaceService;
|
||||
@inject(WorkspaceService)
|
||||
protected readonly workspaceService: WorkspaceService;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
protected toDispose = new DisposableCollection();
|
||||
protected sketches = new Map<string, Sketch>();
|
||||
protected sketchbookDidChangeEmitter = new Emitter<{
|
||||
created: Sketch[];
|
||||
removed: Sketch[];
|
||||
}>();
|
||||
readonly onSketchbookDidChange = this.sketchbookDidChangeEmitter.event;
|
||||
protected toDispose = new DisposableCollection();
|
||||
protected sketches = new Map<string, Sketch>();
|
||||
protected sketchbookDidChangeEmitter = new Emitter<{
|
||||
created: Sketch[];
|
||||
removed: Sketch[];
|
||||
}>();
|
||||
readonly onSketchbookDidChange = this.sketchbookDidChangeEmitter.event;
|
||||
|
||||
onStart(): void {
|
||||
this.configService.getConfiguration().then(({ sketchDirUri }) => {
|
||||
this.sketchService
|
||||
.getSketches({ uri: sketchDirUri })
|
||||
.then((container) => {
|
||||
const sketchbookUri = new URI(sketchDirUri);
|
||||
for (const sketch of SketchContainer.toArray(container)) {
|
||||
this.sketches.set(sketch.uri, sketch);
|
||||
onStart(): void {
|
||||
this.configService.getConfiguration().then(({ sketchDirUri }) => {
|
||||
this.sketchService
|
||||
.getSketches({ uri: sketchDirUri })
|
||||
.then((container) => {
|
||||
const sketchbookUri = new URI(sketchDirUri);
|
||||
for (const sketch of SketchContainer.toArray(container)) {
|
||||
this.sketches.set(sketch.uri, sketch);
|
||||
}
|
||||
this.toDispose.push(
|
||||
this.fileService.watch(new URI(sketchDirUri), {
|
||||
recursive: true,
|
||||
excludes: [],
|
||||
})
|
||||
);
|
||||
this.toDispose.push(
|
||||
this.fileService.onDidFilesChange(async (event) => {
|
||||
for (const { type, resource } of event.changes) {
|
||||
// We track main sketch files changes only. // TODO: check sketch folder changes. One can rename the folder without renaming the `.ino` file.
|
||||
if (sketchbookUri.isEqualOrParent(resource)) {
|
||||
if (Sketch.isSketchFile(resource)) {
|
||||
if (type === FileChangeType.ADDED) {
|
||||
try {
|
||||
const toAdd = await this.sketchService.loadSketch(
|
||||
resource.parent.toString()
|
||||
);
|
||||
if (!this.sketches.has(toAdd.uri)) {
|
||||
console.log(
|
||||
`New sketch '${toAdd.name}' was crated in sketchbook '${sketchDirUri}'.`
|
||||
);
|
||||
this.sketches.set(toAdd.uri, toAdd);
|
||||
this.fireSoon(toAdd, 'created');
|
||||
}
|
||||
} catch {}
|
||||
} else if (type === FileChangeType.DELETED) {
|
||||
const uri = resource.parent.toString();
|
||||
const toDelete = this.sketches.get(uri);
|
||||
if (toDelete) {
|
||||
console.log(
|
||||
`Sketch '${toDelete.name}' was removed from sketchbook '${sketchbookUri}'.`
|
||||
);
|
||||
this.sketches.delete(uri);
|
||||
this.fireSoon(toDelete, 'removed');
|
||||
}
|
||||
}
|
||||
this.toDispose.push(
|
||||
this.fileService.watch(new URI(sketchDirUri), {
|
||||
recursive: true,
|
||||
excludes: [],
|
||||
})
|
||||
);
|
||||
this.toDispose.push(
|
||||
this.fileService.onDidFilesChange(async (event) => {
|
||||
for (const { type, resource } of event.changes) {
|
||||
// We track main sketch files changes only. // TODO: check sketch folder changes. One can rename the folder without renaming the `.ino` file.
|
||||
if (sketchbookUri.isEqualOrParent(resource)) {
|
||||
if (Sketch.isSketchFile(resource)) {
|
||||
if (type === FileChangeType.ADDED) {
|
||||
try {
|
||||
const toAdd =
|
||||
await this.sketchService.loadSketch(
|
||||
resource.parent.toString()
|
||||
);
|
||||
if (
|
||||
!this.sketches.has(
|
||||
toAdd.uri
|
||||
)
|
||||
) {
|
||||
console.log(
|
||||
`New sketch '${toAdd.name}' was crated in sketchbook '${sketchDirUri}'.`
|
||||
);
|
||||
this.sketches.set(
|
||||
toAdd.uri,
|
||||
toAdd
|
||||
);
|
||||
this.fireSoon(
|
||||
toAdd,
|
||||
'created'
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
} else if (
|
||||
type === FileChangeType.DELETED
|
||||
) {
|
||||
const uri =
|
||||
resource.parent.toString();
|
||||
const toDelete =
|
||||
this.sketches.get(uri);
|
||||
if (toDelete) {
|
||||
console.log(
|
||||
`Sketch '${toDelete.name}' was removed from sketchbook '${sketchbookUri}'.`
|
||||
);
|
||||
this.sketches.delete(uri);
|
||||
this.fireSoon(
|
||||
toDelete,
|
||||
'removed'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
async currentSketch(): Promise<Sketch | undefined> {
|
||||
const sketches = (
|
||||
await Promise.all(
|
||||
this.workspaceService
|
||||
.tryGetRoots()
|
||||
.map(({ resource }) =>
|
||||
this.sketchService.getSketchFolder(resource.toString())
|
||||
)
|
||||
)
|
||||
).filter(notEmpty);
|
||||
if (!sketches.length) {
|
||||
return undefined;
|
||||
}
|
||||
if (sketches.length > 1) {
|
||||
console.log(
|
||||
`Multiple sketch folders were found in the workspace. Falling back to the first one. Sketch folders: ${JSON.stringify(
|
||||
sketches
|
||||
)}`
|
||||
);
|
||||
}
|
||||
return sketches[0];
|
||||
}
|
||||
|
||||
async currentSketchFile(): Promise<string | undefined> {
|
||||
const sketch = await this.currentSketch();
|
||||
if (sketch) {
|
||||
const uri = sketch.mainFileUri;
|
||||
const exists = await this.fileService.exists(new URI(uri));
|
||||
if (!exists) {
|
||||
this.messageService.warn(`Could not find sketch file: ${uri}`);
|
||||
return undefined;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private fireSoonHandle?: number;
|
||||
private bufferedSketchbookEvents: {
|
||||
type: 'created' | 'removed';
|
||||
sketch: Sketch;
|
||||
}[] = [];
|
||||
|
||||
private fireSoon(sketch: Sketch, type: 'created' | 'removed'): void {
|
||||
this.bufferedSketchbookEvents.push({ type, sketch });
|
||||
|
||||
if (typeof this.fireSoonHandle === 'number') {
|
||||
window.clearTimeout(this.fireSoonHandle);
|
||||
}
|
||||
|
||||
this.fireSoonHandle = window.setTimeout(() => {
|
||||
const event: { created: Sketch[]; removed: Sketch[] } = {
|
||||
created: [],
|
||||
removed: [],
|
||||
};
|
||||
for (const { type, sketch } of this.bufferedSketchbookEvents) {
|
||||
if (type === 'created') {
|
||||
event.created.push(sketch);
|
||||
} else {
|
||||
event.removed.push(sketch);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.sketchbookDidChangeEmitter.fire(event);
|
||||
this.bufferedSketchbookEvents.length = 0;
|
||||
}, 100);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
async currentSketch(): Promise<Sketch | undefined> {
|
||||
const sketches = (
|
||||
await Promise.all(
|
||||
this.workspaceService
|
||||
.tryGetRoots()
|
||||
.map(({ resource }) =>
|
||||
this.sketchService.getSketchFolder(resource.toString())
|
||||
)
|
||||
)
|
||||
).filter(notEmpty);
|
||||
if (!sketches.length) {
|
||||
return undefined;
|
||||
}
|
||||
if (sketches.length > 1) {
|
||||
console.log(
|
||||
`Multiple sketch folders were found in the workspace. Falling back to the first one. Sketch folders: ${JSON.stringify(
|
||||
sketches
|
||||
)}`
|
||||
);
|
||||
}
|
||||
return sketches[0];
|
||||
}
|
||||
|
||||
async currentSketchFile(): Promise<string | undefined> {
|
||||
const sketch = await this.currentSketch();
|
||||
if (sketch) {
|
||||
const uri = sketch.mainFileUri;
|
||||
const exists = await this.fileService.exists(new URI(uri));
|
||||
if (!exists) {
|
||||
this.messageService.warn(`Could not find sketch file: ${uri}`);
|
||||
return undefined;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private fireSoonHandle?: number;
|
||||
private bufferedSketchbookEvents: {
|
||||
type: 'created' | 'removed';
|
||||
sketch: Sketch;
|
||||
}[] = [];
|
||||
|
||||
private fireSoon(sketch: Sketch, type: 'created' | 'removed'): void {
|
||||
this.bufferedSketchbookEvents.push({ type, sketch });
|
||||
|
||||
if (typeof this.fireSoonHandle === 'number') {
|
||||
window.clearTimeout(this.fireSoonHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if the `uri` is not contained in any of the opened workspaces. Otherwise, `false`.
|
||||
*/
|
||||
isReadOnly(uri: URI | monaco.Uri | string): boolean {
|
||||
const toCheck = uri instanceof URI ? uri : new URI(uri);
|
||||
if (toCheck.scheme === 'user-storage') {
|
||||
return false;
|
||||
this.fireSoonHandle = window.setTimeout(() => {
|
||||
const event: { created: Sketch[]; removed: Sketch[] } = {
|
||||
created: [],
|
||||
removed: [],
|
||||
};
|
||||
for (const { type, sketch } of this.bufferedSketchbookEvents) {
|
||||
if (type === 'created') {
|
||||
event.created.push(sketch);
|
||||
} else {
|
||||
event.removed.push(sketch);
|
||||
}
|
||||
const readOnly = !this.workspaceService
|
||||
.tryGetRoots()
|
||||
.some(({ resource }) => resource.isEqualOrParent(toCheck));
|
||||
return readOnly;
|
||||
}
|
||||
this.sketchbookDidChangeEmitter.fire(event);
|
||||
this.bufferedSketchbookEvents.length = 0;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if the `uri` is not contained in any of the opened workspaces. Otherwise, `false`.
|
||||
*/
|
||||
isReadOnly(uri: URI | monaco.Uri | string): boolean {
|
||||
const toCheck = uri instanceof URI ? uri : new URI(uri);
|
||||
if (toCheck.scheme === 'user-storage') {
|
||||
return false;
|
||||
}
|
||||
const readOnly = !this.workspaceService
|
||||
.tryGetRoots()
|
||||
.some(({ resource }) => resource.isEqualOrParent(toCheck));
|
||||
return readOnly;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,188 +3,184 @@ import URI from '@theia/core/lib/common/uri';
|
||||
export const SketchesServicePath = '/services/sketches-service';
|
||||
export const SketchesService = Symbol('SketchesService');
|
||||
export interface SketchesService {
|
||||
/**
|
||||
* Resolves to a sketch container representing the hierarchical structure of the sketches.
|
||||
* If `uri` is not given, `directories.user` will be user instead. Specify `exclude` global patterns to filter folders from the sketch container.
|
||||
* If `exclude` is not set `['**\/libraries\/**', '**\/hardware\/**']` will be used instead.
|
||||
*/
|
||||
getSketches({
|
||||
uri,
|
||||
exclude,
|
||||
}: {
|
||||
uri?: string;
|
||||
exclude?: string[];
|
||||
}): Promise<SketchContainer>;
|
||||
/**
|
||||
* Resolves to a sketch container representing the hierarchical structure of the sketches.
|
||||
* If `uri` is not given, `directories.user` will be user instead. Specify `exclude` global patterns to filter folders from the sketch container.
|
||||
* If `exclude` is not set `['**\/libraries\/**', '**\/hardware\/**']` will be used instead.
|
||||
*/
|
||||
getSketches({
|
||||
uri,
|
||||
exclude,
|
||||
}: {
|
||||
uri?: string;
|
||||
exclude?: string[];
|
||||
}): Promise<SketchContainer>;
|
||||
|
||||
/**
|
||||
* This is the TS implementation of `SketchLoad` from the CLI and should be replaced with a gRPC call eventually.
|
||||
* See: https://github.com/arduino/arduino-cli/issues/837
|
||||
* Based on: https://github.com/arduino/arduino-cli/blob/eef3705c4afcba4317ec38b803d9ffce5dd59a28/arduino/builder/sketch.go#L100-L215
|
||||
*/
|
||||
loadSketch(uri: string): Promise<Sketch>;
|
||||
/**
|
||||
* This is the TS implementation of `SketchLoad` from the CLI and should be replaced with a gRPC call eventually.
|
||||
* See: https://github.com/arduino/arduino-cli/issues/837
|
||||
* Based on: https://github.com/arduino/arduino-cli/blob/eef3705c4afcba4317ec38b803d9ffce5dd59a28/arduino/builder/sketch.go#L100-L215
|
||||
*/
|
||||
loadSketch(uri: string): Promise<Sketch>;
|
||||
|
||||
/**
|
||||
* Unlike `loadSketch`, this method gracefully resolves to `undefined` instead or rejecting if the `uri` is not a sketch folder.
|
||||
*/
|
||||
maybeLoadSketch(uri: string): Promise<Sketch | undefined>;
|
||||
/**
|
||||
* Unlike `loadSketch`, this method gracefully resolves to `undefined` instead or rejecting if the `uri` is not a sketch folder.
|
||||
*/
|
||||
maybeLoadSketch(uri: string): Promise<Sketch | undefined>;
|
||||
|
||||
/**
|
||||
* Creates a new sketch folder in the temp location.
|
||||
*/
|
||||
createNewSketch(): Promise<Sketch>;
|
||||
/**
|
||||
* Creates a new sketch folder in the temp location.
|
||||
*/
|
||||
createNewSketch(): Promise<Sketch>;
|
||||
|
||||
/**
|
||||
* Creates a new sketch with existing content. Rejects if `uri` is not pointing to a valid sketch folder.
|
||||
*/
|
||||
cloneExample(uri: string): Promise<Sketch>;
|
||||
/**
|
||||
* Creates a new sketch with existing content. Rejects if `uri` is not pointing to a valid sketch folder.
|
||||
*/
|
||||
cloneExample(uri: string): Promise<Sketch>;
|
||||
|
||||
isSketchFolder(uri: string): Promise<boolean>;
|
||||
isSketchFolder(uri: string): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Sketches are created to the temp location by default and will be moved under `directories.user` on save.
|
||||
* This method resolves to `true` if the `sketch` is still in the temp location. Otherwise, `false`.
|
||||
*/
|
||||
isTemp(sketch: Sketch): Promise<boolean>;
|
||||
/**
|
||||
* Sketches are created to the temp location by default and will be moved under `directories.user` on save.
|
||||
* This method resolves to `true` if the `sketch` is still in the temp location. Otherwise, `false`.
|
||||
*/
|
||||
isTemp(sketch: Sketch): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* If `isTemp` is `true` for the `sketch`, you can call this method to move the sketch from the temp
|
||||
* location to `directories.user`. Resolves with the URI of the sketch after the move. Rejects, when the sketch
|
||||
* was not in the temp folder. This method always overrides. It's the callers responsibility to ask the user whether
|
||||
* the files at the destination can be overwritten or not.
|
||||
*/
|
||||
copy(sketch: Sketch, options: { destinationUri: string }): Promise<string>;
|
||||
/**
|
||||
* If `isTemp` is `true` for the `sketch`, you can call this method to move the sketch from the temp
|
||||
* location to `directories.user`. Resolves with the URI of the sketch after the move. Rejects, when the sketch
|
||||
* was not in the temp folder. This method always overrides. It's the callers responsibility to ask the user whether
|
||||
* the files at the destination can be overwritten or not.
|
||||
*/
|
||||
copy(sketch: Sketch, options: { destinationUri: string }): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns with the container sketch for the input `uri`. If the `uri` is not in a sketch folder, resolved `undefined`.
|
||||
*/
|
||||
getSketchFolder(uri: string): Promise<Sketch | undefined>;
|
||||
/**
|
||||
* Returns with the container sketch for the input `uri`. If the `uri` is not in a sketch folder, resolved `undefined`.
|
||||
*/
|
||||
getSketchFolder(uri: string): Promise<Sketch | undefined>;
|
||||
|
||||
/**
|
||||
* Marks the sketch with the given URI as recently opened. It does nothing if the sketch is temp or not valid.
|
||||
*/
|
||||
markAsRecentlyOpened(uri: string): Promise<void>;
|
||||
/**
|
||||
* Marks the sketch with the given URI as recently opened. It does nothing if the sketch is temp or not valid.
|
||||
*/
|
||||
markAsRecentlyOpened(uri: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Resolves to an array of sketches in inverse chronological order. The newest is the first.
|
||||
*/
|
||||
recentlyOpenedSketches(): Promise<Sketch[]>;
|
||||
/**
|
||||
* Resolves to an array of sketches in inverse chronological order. The newest is the first.
|
||||
*/
|
||||
recentlyOpenedSketches(): Promise<Sketch[]>;
|
||||
|
||||
/**
|
||||
* Archives the sketch, resolves to the archive URI.
|
||||
*/
|
||||
archive(sketch: Sketch, destinationUri: string): Promise<string>;
|
||||
/**
|
||||
* Archives the sketch, resolves to the archive URI.
|
||||
*/
|
||||
archive(sketch: Sketch, destinationUri: string): Promise<string>;
|
||||
|
||||
/**
|
||||
* Counterpart of the CLI's `genBuildPath` functionality.
|
||||
* Based on https://github.com/arduino/arduino-cli/blob/550179eefd2d2bca299d50a4af9e9bfcfebec649/arduino/builder/builder.go#L30-L38
|
||||
*/
|
||||
getIdeTempFolderUri(sketch: Sketch): Promise<string>;
|
||||
/**
|
||||
* Counterpart of the CLI's `genBuildPath` functionality.
|
||||
* Based on https://github.com/arduino/arduino-cli/blob/550179eefd2d2bca299d50a4af9e9bfcfebec649/arduino/builder/builder.go#L30-L38
|
||||
*/
|
||||
getIdeTempFolderUri(sketch: Sketch): Promise<string>;
|
||||
}
|
||||
|
||||
export interface Sketch {
|
||||
readonly name: string;
|
||||
readonly uri: string; // `LocationPath`
|
||||
readonly mainFileUri: string; // `MainFile`
|
||||
readonly otherSketchFileUris: string[]; // `OtherSketchFiles`
|
||||
readonly additionalFileUris: string[]; // `AdditionalFiles`
|
||||
readonly rootFolderFileUris: string[]; // `RootFolderFiles` (does not include the main sketch file)
|
||||
readonly name: string;
|
||||
readonly uri: string; // `LocationPath`
|
||||
readonly mainFileUri: string; // `MainFile`
|
||||
readonly otherSketchFileUris: string[]; // `OtherSketchFiles`
|
||||
readonly additionalFileUris: string[]; // `AdditionalFiles`
|
||||
readonly rootFolderFileUris: string[]; // `RootFolderFiles` (does not include the main sketch file)
|
||||
}
|
||||
export namespace Sketch {
|
||||
export function is(arg: any): arg is Sketch {
|
||||
return (
|
||||
!!arg &&
|
||||
'name' in arg &&
|
||||
'uri' in arg &&
|
||||
typeof arg.name === 'string' &&
|
||||
typeof arg.uri === 'string'
|
||||
);
|
||||
}
|
||||
export namespace Extensions {
|
||||
export const MAIN = ['.ino', '.pde'];
|
||||
export const SOURCE = ['.c', '.cpp', '.s'];
|
||||
export const ADDITIONAL = [
|
||||
'.h',
|
||||
'.c',
|
||||
'.hpp',
|
||||
'.hh',
|
||||
'.cpp',
|
||||
'.S',
|
||||
'.json',
|
||||
'.md',
|
||||
'.adoc',
|
||||
];
|
||||
export const ALL = Array.from(
|
||||
new Set([...MAIN, ...SOURCE, ...ADDITIONAL])
|
||||
);
|
||||
}
|
||||
export function isInSketch(uri: string | URI, sketch: Sketch): boolean {
|
||||
const { mainFileUri, otherSketchFileUris, additionalFileUris } = sketch;
|
||||
return (
|
||||
[
|
||||
mainFileUri,
|
||||
...otherSketchFileUris,
|
||||
...additionalFileUris,
|
||||
].indexOf(uri.toString()) !== -1
|
||||
);
|
||||
}
|
||||
export function isSketchFile(arg: string | URI): boolean {
|
||||
if (arg instanceof URI) {
|
||||
return isSketchFile(arg.toString());
|
||||
}
|
||||
return Extensions.MAIN.some((ext) => arg.endsWith(ext));
|
||||
export function is(arg: any): arg is Sketch {
|
||||
return (
|
||||
!!arg &&
|
||||
'name' in arg &&
|
||||
'uri' in arg &&
|
||||
typeof arg.name === 'string' &&
|
||||
typeof arg.uri === 'string'
|
||||
);
|
||||
}
|
||||
export namespace Extensions {
|
||||
export const MAIN = ['.ino', '.pde'];
|
||||
export const SOURCE = ['.c', '.cpp', '.s'];
|
||||
export const ADDITIONAL = [
|
||||
'.h',
|
||||
'.c',
|
||||
'.hpp',
|
||||
'.hh',
|
||||
'.cpp',
|
||||
'.S',
|
||||
'.json',
|
||||
'.md',
|
||||
'.adoc',
|
||||
];
|
||||
export const ALL = Array.from(new Set([...MAIN, ...SOURCE, ...ADDITIONAL]));
|
||||
}
|
||||
export function isInSketch(uri: string | URI, sketch: Sketch): boolean {
|
||||
const { mainFileUri, otherSketchFileUris, additionalFileUris } = sketch;
|
||||
return (
|
||||
[mainFileUri, ...otherSketchFileUris, ...additionalFileUris].indexOf(
|
||||
uri.toString()
|
||||
) !== -1
|
||||
);
|
||||
}
|
||||
export function isSketchFile(arg: string | URI): boolean {
|
||||
if (arg instanceof URI) {
|
||||
return isSketchFile(arg.toString());
|
||||
}
|
||||
return Extensions.MAIN.some((ext) => arg.endsWith(ext));
|
||||
}
|
||||
}
|
||||
|
||||
export interface SketchContainer {
|
||||
readonly label: string;
|
||||
readonly children: SketchContainer[];
|
||||
readonly sketches: Sketch[];
|
||||
readonly label: string;
|
||||
readonly children: SketchContainer[];
|
||||
readonly sketches: Sketch[];
|
||||
}
|
||||
export namespace SketchContainer {
|
||||
export function is(arg: any): arg is SketchContainer {
|
||||
return (
|
||||
!!arg &&
|
||||
'label' in arg &&
|
||||
typeof arg.label === 'string' &&
|
||||
'children' in arg &&
|
||||
Array.isArray(arg.children) &&
|
||||
'sketches' in arg &&
|
||||
Array.isArray(arg.sketches)
|
||||
);
|
||||
}
|
||||
export function is(arg: any): arg is SketchContainer {
|
||||
return (
|
||||
!!arg &&
|
||||
'label' in arg &&
|
||||
typeof arg.label === 'string' &&
|
||||
'children' in arg &&
|
||||
Array.isArray(arg.children) &&
|
||||
'sketches' in arg &&
|
||||
Array.isArray(arg.sketches)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* `false` if the `container` recursively contains at least one sketch. Otherwise, `true`.
|
||||
*/
|
||||
export function isEmpty(container: SketchContainer): boolean {
|
||||
const hasSketch = (parent: SketchContainer) => {
|
||||
if (
|
||||
parent.sketches.length ||
|
||||
parent.children.some((child) => hasSketch(child))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return !hasSketch(container);
|
||||
}
|
||||
/**
|
||||
* `false` if the `container` recursively contains at least one sketch. Otherwise, `true`.
|
||||
*/
|
||||
export function isEmpty(container: SketchContainer): boolean {
|
||||
const hasSketch = (parent: SketchContainer) => {
|
||||
if (
|
||||
parent.sketches.length ||
|
||||
parent.children.some((child) => hasSketch(child))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return !hasSketch(container);
|
||||
}
|
||||
|
||||
export function prune<T extends SketchContainer>(container: T): T {
|
||||
for (let i = container.children.length - 1; i >= 0; i--) {
|
||||
if (isEmpty(container.children[i])) {
|
||||
container.children.splice(i, 1);
|
||||
}
|
||||
}
|
||||
return container;
|
||||
export function prune<T extends SketchContainer>(container: T): T {
|
||||
for (let i = container.children.length - 1; i >= 0; i--) {
|
||||
if (isEmpty(container.children[i])) {
|
||||
container.children.splice(i, 1);
|
||||
}
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
export function toArray(container: SketchContainer): Sketch[] {
|
||||
const visit = (parent: SketchContainer, toPushSketch: Sketch[]) => {
|
||||
toPushSketch.push(...parent.sketches);
|
||||
parent.children.map((child) => visit(child, toPushSketch));
|
||||
};
|
||||
const sketches: Sketch[] = [];
|
||||
visit(container, sketches);
|
||||
return sketches;
|
||||
}
|
||||
export function toArray(container: SketchContainer): Sketch[] {
|
||||
const visit = (parent: SketchContainer, toPushSketch: Sketch[]) => {
|
||||
toPushSketch.push(...parent.sketches);
|
||||
parent.children.map((child) => visit(child, toPushSketch));
|
||||
};
|
||||
const sketches: Sketch[] = [];
|
||||
visit(container, sketches);
|
||||
return sketches;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export type RecursiveRequired<T> = {
|
||||
[P in keyof T]-?: RecursiveRequired<T[P]>;
|
||||
[P in keyof T]-?: RecursiveRequired<T[P]>;
|
||||
};
|
||||
|
||||
export interface Index {
|
||||
[key: string]: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export const naturalCompare: (left: string, right: string) => number =
|
||||
require('string-natural-compare').caseInsensitive;
|
||||
require('string-natural-compare').caseInsensitive;
|
||||
|
||||
export function notEmpty(arg: string | undefined | null): arg is string {
|
||||
return !!arg;
|
||||
return !!arg;
|
||||
}
|
||||
|
||||
export function firstToLowerCase(what: string): string {
|
||||
return what.charAt(0).toLowerCase() + what.slice(1);
|
||||
return what.charAt(0).toLowerCase() + what.slice(1);
|
||||
}
|
||||
|
||||
export function firstToUpperCase(what: string): string {
|
||||
return what.charAt(0).toUpperCase() + what.slice(1);
|
||||
return what.charAt(0).toUpperCase() + what.slice(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user