mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-13 04:09:27 +00:00
Moving repo to bmci-labs
This commit is contained in:
13
arduino-ide-extension/src/browser/arduino-commands.ts
Normal file
13
arduino-ide-extension/src/browser/arduino-commands.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Command } from '@theia/core/lib/common/command';
|
||||
|
||||
export namespace ArduinoCommands {
|
||||
|
||||
export const VERIFY: Command = {
|
||||
id: 'arduino-verify'
|
||||
}
|
||||
|
||||
export const UPLOAD: Command = {
|
||||
id: 'arduino-upload'
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import * as React from 'react';
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { DefaultFrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { BoardsService } from '../common/protocol/boards-service';
|
||||
import { ArduinoCommands } from './arduino-commands';
|
||||
import { ConnectedBoards } from './components/connected-boards';
|
||||
import { CoreService } from '../common/protocol/core-service';
|
||||
import { WorkspaceServiceExt } from './workspace-service-ext';
|
||||
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFrontendContribution extends DefaultFrontendApplicationContribution implements TabBarToolbarContribution, CommandContribution {
|
||||
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService;
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardService: BoardsService;
|
||||
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
|
||||
@inject(WorkspaceServiceExt)
|
||||
protected readonly workspaceServiceExt: WorkspaceServiceExt;
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
// This is a hack. Otherwise, the backend services won't bind.
|
||||
await this.workspaceServiceExt.roots();
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.VERIFY.id,
|
||||
command: ArduinoCommands.VERIFY.id,
|
||||
tooltip: 'Verify',
|
||||
group: 'arduino',
|
||||
text: '$(check)'
|
||||
});
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.UPLOAD.id,
|
||||
command: ArduinoCommands.UPLOAD.id,
|
||||
tooltip: 'Upload',
|
||||
group: 'arduino',
|
||||
text: '$(arrow-right)'
|
||||
});
|
||||
registry.registerItem({
|
||||
id: ConnectedBoards.TOOLBAR_ID,
|
||||
render: () => <ConnectedBoards boardsService={this.boardService}/>,
|
||||
isVisible: widget => this.isArduinoEditor(widget)
|
||||
})
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(ArduinoCommands.VERIFY, {
|
||||
isVisible: widget => this.isArduinoEditor(widget),
|
||||
isEnabled: widget => this.isArduinoEditor(widget),
|
||||
execute: async widget => {
|
||||
const uri = this.toUri(widget);
|
||||
if (uri) {
|
||||
const result = await this.coreService.compile({ uri: uri.toString() });
|
||||
console.log('compile result', result);
|
||||
}
|
||||
}
|
||||
});
|
||||
registry.registerCommand(ArduinoCommands.UPLOAD, {
|
||||
isVisible: widget => this.isArduinoEditor(widget),
|
||||
isEnabled: widget => this.isArduinoEditor(widget),
|
||||
execute: widget => {
|
||||
const uri = this.toUri(widget);
|
||||
if (uri) {
|
||||
this.messageService.info(`Uploading ${uri.toString()}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private isArduinoEditor(maybeEditorWidget: any): boolean {
|
||||
if (maybeEditorWidget instanceof EditorWidget) {
|
||||
return maybeEditorWidget.editor.uri.toString().endsWith('.ino');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private toUri(arg: any): URI | undefined {
|
||||
if (arg instanceof URI) {
|
||||
return arg;
|
||||
}
|
||||
if (typeof arg === 'string') {
|
||||
return new URI(arg);
|
||||
}
|
||||
if (arg instanceof EditorWidget) {
|
||||
return arg.editor.uri;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
70
arduino-ide-extension/src/browser/arduino-frontend-module.ts
Normal file
70
arduino-ide-extension/src/browser/arduino-frontend-module.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { ContainerModule, interfaces } from 'inversify';
|
||||
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
|
||||
import { CommandContribution } from '@theia/core/lib/common/command';
|
||||
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'
|
||||
import { LanguageGrammarDefinitionContribution } from '@theia/monaco/lib/browser/textmate';
|
||||
import { LibraryListWidget } from './library/library-list-widget';
|
||||
import { ArduinoFrontendContribution } from './arduino-frontend-contribution';
|
||||
import { ArduinoLanguageGrammarContribution } from './language/arduino-language-grammar-contribution';
|
||||
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
|
||||
import { BoardsService, BoardsServicePath } from '../common/protocol/boards-service';
|
||||
import { LibraryListWidgetFrontendContribution } from './library/list-widget-frontend-contribution';
|
||||
import { CoreService, CoreServicePath } from '../common/protocol/core-service';
|
||||
import { BoardsListWidget } from './boards/boards-list-widget';
|
||||
import { BoardsListWidgetFrontendContribution } from './boards/boards-widget-frontend-contribution';
|
||||
import { WorkspaceServiceExt, WorkspaceServiceExtPath } from './workspace-service-ext';
|
||||
import { WorkspaceServiceExtImpl } from './workspace-service-ext-impl';
|
||||
|
||||
import '../../src/browser/style/index.css';
|
||||
|
||||
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
|
||||
// Commands and toolbar items
|
||||
bind(ArduinoFrontendContribution).toSelf().inSingletonScope();
|
||||
bind(CommandContribution).toService(ArduinoFrontendContribution);
|
||||
bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution);
|
||||
|
||||
// `ino` TextMate grammar
|
||||
bind(LanguageGrammarDefinitionContribution).to(ArduinoLanguageGrammarContribution).inSingletonScope();
|
||||
|
||||
// Library service
|
||||
bind(LibraryService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, LibraryServicePath)).inSingletonScope();
|
||||
|
||||
// Library list widget
|
||||
bind(LibraryListWidget).toSelf();
|
||||
bindViewContribution(bind, LibraryListWidgetFrontendContribution);
|
||||
bind(WidgetFactory).toDynamicValue(context => ({
|
||||
id: LibraryListWidget.WIDGET_ID,
|
||||
createWidget: () => context.container.get(LibraryListWidget)
|
||||
}));
|
||||
bind(FrontendApplicationContribution).toService(LibraryListWidgetFrontendContribution);
|
||||
|
||||
// Boards service
|
||||
bind(BoardsService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath)).inSingletonScope();
|
||||
|
||||
// Boards list widget
|
||||
bind(BoardsListWidget).toSelf();
|
||||
bindViewContribution(bind, BoardsListWidgetFrontendContribution);
|
||||
bind(WidgetFactory).toDynamicValue(context => ({
|
||||
id: BoardsListWidget.WIDGET_ID,
|
||||
createWidget: () => context.container.get(BoardsListWidget)
|
||||
}));
|
||||
bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution);
|
||||
|
||||
// Core service
|
||||
bind(CoreService)
|
||||
.toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, CoreServicePath))
|
||||
.inSingletonScope();
|
||||
|
||||
// The workspace service extension
|
||||
bind(WorkspaceServiceExt).to(WorkspaceServiceExtImpl).inSingletonScope().onActivation(({ container }, workspaceServiceExt) => {
|
||||
WebSocketConnectionProvider.createProxy(container, WorkspaceServiceExtPath, workspaceServiceExt);
|
||||
// Eagerly active the core, library, and boards services.
|
||||
container.get(CoreService);
|
||||
container.get(LibraryService);
|
||||
container.get(BoardsService);
|
||||
return workspaceServiceExt;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ListWidget } from './list-widget';
|
||||
|
||||
export class BoardsListWidget extends ListWidget {
|
||||
|
||||
static WIDGET_ID = 'boards-list-widget';
|
||||
static WIDGET_LABEL = 'Boards Manager';
|
||||
|
||||
protected widgetProps(): ListWidget.Props {
|
||||
return {
|
||||
id: BoardsListWidget.WIDGET_ID,
|
||||
title: BoardsListWidget.WIDGET_LABEL,
|
||||
iconClass: 'fa fa-microchip'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
import { ListWidget } from './list-widget';
|
||||
import { BoardsListWidget } from './boards-list-widget';
|
||||
|
||||
@injectable()
|
||||
export abstract class ListWidgetFrontendContribution extends AbstractViewContribution<ListWidget> implements FrontendApplicationContribution {
|
||||
|
||||
async initializeLayout(): Promise<void> {
|
||||
await this.openView();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendContribution {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: BoardsListWidget.WIDGET_ID,
|
||||
widgetName: BoardsListWidget.WIDGET_LABEL,
|
||||
defaultWidgetOptions: {
|
||||
area: 'left',
|
||||
rank: 600
|
||||
},
|
||||
toggleCommandId: `${BoardsListWidget.WIDGET_ID}:toggle`,
|
||||
toggleKeybinding: 'ctrlcmd+shift+b'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
72
arduino-ide-extension/src/browser/boards/list-widget.tsx
Normal file
72
arduino-ide-extension/src/browser/boards/list-widget.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { FilterableListContainer } from '../components/component-list/filterable-list-container';
|
||||
import { BoardsService } from '../../common/protocol/boards-service';
|
||||
|
||||
@injectable()
|
||||
export abstract class ListWidget extends ReactWidget {
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
|
||||
@inject(WindowService)
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const { id, title, iconClass } = this.widgetProps();
|
||||
this.id = id;
|
||||
this.title.label = title;
|
||||
this.title.caption = title;
|
||||
this.title.iconClass = iconClass;
|
||||
this.title.closable = true;
|
||||
this.addClass(ListWidget.Styles.LIST_WIDGET_CLASS);
|
||||
this.node.tabIndex = 0; // To be able to set the focus on the widget.
|
||||
}
|
||||
|
||||
protected abstract widgetProps(): ListWidget.Props;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.node.focus();
|
||||
this.render();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.render();
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <FilterableListContainer
|
||||
service={this.boardsService}
|
||||
windowService={this.windowService}
|
||||
/>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ListWidget {
|
||||
|
||||
/**
|
||||
* Props for customizing the abstract list widget.
|
||||
*/
|
||||
export interface Props {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly iconClass: string;
|
||||
}
|
||||
|
||||
export namespace Styles {
|
||||
export const LIST_WIDGET_CLASS = 'arduino-list-widget'
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import * as React from 'react';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
|
||||
export class ComponentListItem extends React.Component<ComponentListItem.Props> {
|
||||
|
||||
private onClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
|
||||
const { target } = event.nativeEvent;
|
||||
if (target instanceof HTMLAnchorElement) {
|
||||
this.props.windowService.openNewWindow(target.href);
|
||||
event.nativeEvent.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { item } = this.props;
|
||||
|
||||
const style = ComponentListItem.Styles;
|
||||
const name = <span className={style.NAME_CLASS}>{item.name}</span>;
|
||||
const author = <span className={style.AUTHOR_CLASS}>{item.author}</span>;
|
||||
const installedVersion = !!item.installedVersion && <React.Fragment>
|
||||
<span className={style.VERSION_CLASS}>Version {item.installedVersion}</span>
|
||||
<span className={style.INSTALLED_CLASS}>INSTALLED</span>
|
||||
</React.Fragment>;
|
||||
|
||||
const summary = <div className={style.SUMMARY_CLASS}>{item.summary}</div>;
|
||||
const description = !!item.description && <div className={style.DESCRIPTION_CLASS}>{item.description}</div>;
|
||||
|
||||
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>;
|
||||
const install = item.installable && !item.installedVersion && <button className={style.INSTALL_BTN_CLASS}>INSTALL</button>;
|
||||
|
||||
return <div className={[style.LIST_ITEM_CLASS, style.NO_SELECT_CLASS].join(' ')}>
|
||||
<div className={style.HEADER_CLASS}>
|
||||
<span>{name} by {author}</span>
|
||||
{installedVersion}
|
||||
</div>
|
||||
<div className={style.CONTENT_CLASS}>
|
||||
{summary}
|
||||
{description}
|
||||
</div>
|
||||
<div className={style.FOOTER_CLASS}>
|
||||
{moreInfo}
|
||||
{install}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ComponentListItem {
|
||||
|
||||
export interface Props {
|
||||
readonly item: ArduinoComponent;
|
||||
readonly windowService: WindowService;
|
||||
}
|
||||
|
||||
export namespace Styles {
|
||||
export const LIST_ITEM_CLASS = 'component-list-item';
|
||||
export const HEADER_CLASS = 'header';
|
||||
export const CONTENT_CLASS = 'content';
|
||||
export const FOOTER_CLASS = 'footer';
|
||||
export const INSTALLED_CLASS = 'installed';
|
||||
export const NO_SELECT_CLASS = 'noselect';
|
||||
|
||||
export const NAME_CLASS = 'name';
|
||||
export const AUTHOR_CLASS = 'author';
|
||||
export const VERSION_CLASS = 'version';
|
||||
export const SUMMARY_CLASS = 'summary';
|
||||
export const DESCRIPTION_CLASS = 'description';
|
||||
export const INSTALL_BTN_CLASS = 'install';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { ComponentListItem } from './component-list-item';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
|
||||
export class ComponentList extends React.Component<ComponentList.Props> {
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <div>
|
||||
{this.props.items.map(item => <ComponentListItem key={item.name} item={item} windowService={this.props.windowService}/>)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ComponentList {
|
||||
|
||||
export interface Props {
|
||||
readonly items: ArduinoComponent[];
|
||||
readonly windowService: WindowService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import * as React from 'react';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { ComponentList } from './component-list';
|
||||
import { SearchBar } from './search-bar';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
|
||||
export class FilterableListContainer extends React.Component<FilterableListContainer.Props, FilterableListContainer.State> {
|
||||
|
||||
constructor(props: Readonly<FilterableListContainer.Props>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
filterText: '',
|
||||
items: []
|
||||
};
|
||||
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount(): void {
|
||||
this.handleFilterTextChange('');
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <div className={FilterableListContainer.Styles.FILTERABLE_LIST_CONTAINER_CLASS}>
|
||||
<SearchBar
|
||||
filterText={this.state.filterText}
|
||||
onFilterTextChanged={this.handleFilterTextChange}
|
||||
/>
|
||||
<ComponentList
|
||||
items={this.state.items}
|
||||
windowService={this.props.windowService}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
private handleFilterTextChange(filterText: string): void {
|
||||
this.props.service.search({ query: filterText }).then(result => {
|
||||
const { items } = result;
|
||||
this.setState({
|
||||
filterText,
|
||||
items
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace FilterableListContainer {
|
||||
|
||||
export interface Props {
|
||||
readonly service: ComponentSource;
|
||||
readonly windowService: WindowService;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
filterText: string;
|
||||
items: ArduinoComponent[];
|
||||
}
|
||||
|
||||
export namespace Styles {
|
||||
export const FILTERABLE_LIST_CONTAINER_CLASS = 'filterable-list-container';
|
||||
}
|
||||
|
||||
export interface ComponentSource {
|
||||
search(req: { query: string }): Promise<{ items: ArduinoComponent[] }>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export class SearchBar extends React.Component<SearchBar.Props> {
|
||||
|
||||
constructor(props: Readonly<SearchBar.Props>) {
|
||||
super(props);
|
||||
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <form className={SearchBar.Styles.SEARCH_BAR_CLASS}>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='Search'
|
||||
size={1}
|
||||
value={this.props.filterText}
|
||||
onChange={this.handleFilterTextChange}
|
||||
/>
|
||||
</form>;
|
||||
}
|
||||
|
||||
private handleFilterTextChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
this.props.onFilterTextChanged(event.target.value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace SearchBar {
|
||||
|
||||
export interface Props {
|
||||
filterText: string;
|
||||
onFilterTextChanged(filterText: string): void;
|
||||
}
|
||||
|
||||
export namespace Styles {
|
||||
export const SEARCH_BAR_CLASS = 'search-bar';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import * as React from 'react';
|
||||
// TODO: make this `async`.
|
||||
// import { Async } from 'react-select/lib/Async';
|
||||
import { BoardsService, Board } from '../../common/protocol/boards-service';
|
||||
|
||||
export class ConnectedBoards extends React.Component<ConnectedBoards.Props, ConnectedBoards.State> {
|
||||
|
||||
static TOOLBAR_ID: 'connected-boards-toolbar';
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <div className={ConnectedBoards.Styles.CONNECTED_BOARDS_CLASS}>
|
||||
{this.select(this.state ? this.state.boards : undefined, this.state ? this.state.current : undefined)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.props.boardsService.connectedBoards().then(result => {
|
||||
const { boards, current } = result;
|
||||
this.setState({
|
||||
boards,
|
||||
current
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private select(boards: Board[] | undefined, current: Board | undefined): React.ReactNode {
|
||||
// Initial pessimistic.
|
||||
const options = [<option>Loading...</option>];
|
||||
if (boards) {
|
||||
options.length = 0;
|
||||
options.push(...boards.map(b => b.name).map(name => <option value={name} key={name}>{name}</option>));
|
||||
}
|
||||
const onChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const current = (boards || []).find(board => board.name === event.target.value);
|
||||
this.setState({ current });
|
||||
};
|
||||
return <select
|
||||
onChange={onChange}
|
||||
value={current ? current.name : 'Loading...'}
|
||||
name={current ? current.name : 'Loading...'}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ConnectedBoards {
|
||||
|
||||
export interface Props {
|
||||
readonly boardsService: BoardsService;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
boards?: Board[];
|
||||
current?: Board;
|
||||
}
|
||||
|
||||
export namespace Styles {
|
||||
export const CONNECTED_BOARDS_CLASS = 'connected-boards';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { LanguageGrammarDefinitionContribution, TextmateRegistry } from '@theia/monaco/lib/browser/textmate';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoLanguageGrammarContribution implements LanguageGrammarDefinitionContribution {
|
||||
|
||||
static INO_LANGUAGE_ID = 'ino';
|
||||
|
||||
registerTextmateLanguage(registry: TextmateRegistry) {
|
||||
monaco.languages.register({
|
||||
id: ArduinoLanguageGrammarContribution.INO_LANGUAGE_ID,
|
||||
extensions: ['.ino'],
|
||||
aliases: ['INO', 'Ino', 'ino'],
|
||||
});
|
||||
|
||||
monaco.languages.setLanguageConfiguration(ArduinoLanguageGrammarContribution.INO_LANGUAGE_ID, this.configuration);
|
||||
|
||||
const inoGrammar = require('../../../data/ino.tmLanguage.json');
|
||||
registry.registerTextmateGrammarScope('source.ino', {
|
||||
async getGrammarDefinition() {
|
||||
return {
|
||||
format: 'json',
|
||||
content: inoGrammar
|
||||
};
|
||||
}
|
||||
});
|
||||
registry.mapLanguageIdToTextmateGrammar(ArduinoLanguageGrammarContribution.INO_LANGUAGE_ID, 'source.ino');
|
||||
}
|
||||
|
||||
private readonly configuration: monaco.languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
lineComment: '//',
|
||||
blockComment: ['/*', '*/'],
|
||||
},
|
||||
brackets: [
|
||||
['{', '}'],
|
||||
['[', ']'],
|
||||
['(', ')']
|
||||
],
|
||||
autoClosingPairs: [
|
||||
{ open: '[', close: ']' },
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
|
||||
{ open: '"', close: '"', notIn: ['string'] },
|
||||
{ open: '/*', close: ' */', notIn: ['string'] }
|
||||
],
|
||||
surroundingPairs: [
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '[', close: ']' },
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '"', close: '"' },
|
||||
{ open: '\'', close: '\'' },
|
||||
],
|
||||
folding: {
|
||||
markers: {
|
||||
start: new RegExp('^\\s*#pragma\\s+region\\b'),
|
||||
end: new RegExp('^\\s*#pragma\\s+endregion\\b')
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ListWidget } from './list-widget';
|
||||
|
||||
export class LibraryListWidget extends ListWidget {
|
||||
|
||||
static WIDGET_ID = 'library-list-widget';
|
||||
static WIDGET_LABEL = 'Library Manager';
|
||||
|
||||
protected widgetProps(): ListWidget.Props {
|
||||
return {
|
||||
id: LibraryListWidget.WIDGET_ID,
|
||||
title: LibraryListWidget.WIDGET_LABEL,
|
||||
iconClass: 'fa fa-book' // TODO: find a better icon
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
import { ListWidget } from './list-widget';
|
||||
import { LibraryListWidget } from './library-list-widget';
|
||||
|
||||
@injectable()
|
||||
export abstract class ListWidgetFrontendContribution extends AbstractViewContribution<ListWidget> implements FrontendApplicationContribution {
|
||||
|
||||
async initializeLayout(): Promise<void> {
|
||||
await this.openView();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class LibraryListWidgetFrontendContribution extends ListWidgetFrontendContribution {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: LibraryListWidget.WIDGET_ID,
|
||||
widgetName: LibraryListWidget.WIDGET_LABEL,
|
||||
defaultWidgetOptions: {
|
||||
area: 'left',
|
||||
rank: 600
|
||||
},
|
||||
toggleCommandId: `${LibraryListWidget.WIDGET_ID}:toggle`,
|
||||
toggleKeybinding: 'ctrlcmd+shift+l'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
72
arduino-ide-extension/src/browser/library/list-widget.tsx
Normal file
72
arduino-ide-extension/src/browser/library/list-widget.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { FilterableListContainer } from '../components/component-list/filterable-list-container';
|
||||
import { LibraryService } from '../../common/protocol/library-service';
|
||||
|
||||
@injectable()
|
||||
export abstract class ListWidget extends ReactWidget {
|
||||
|
||||
@inject(LibraryService)
|
||||
protected readonly libraryService: LibraryService;
|
||||
|
||||
@inject(WindowService)
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const { id, title, iconClass } = this.widgetProps();
|
||||
this.id = id;
|
||||
this.title.label = title;
|
||||
this.title.caption = title;
|
||||
this.title.iconClass = iconClass;
|
||||
this.title.closable = true;
|
||||
this.addClass(ListWidget.Styles.LIST_WIDGET_CLASS);
|
||||
this.node.tabIndex = 0; // To be able to set the focus on the widget.
|
||||
}
|
||||
|
||||
protected abstract widgetProps(): ListWidget.Props;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.node.focus();
|
||||
this.render();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.render();
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <FilterableListContainer
|
||||
service={this.libraryService}
|
||||
windowService={this.windowService}
|
||||
/>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ListWidget {
|
||||
|
||||
/**
|
||||
* Props for customizing the abstract list widget.
|
||||
*/
|
||||
export interface Props {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly iconClass: string;
|
||||
}
|
||||
|
||||
export namespace Styles {
|
||||
export const LIST_WIDGET_CLASS = 'arduino-list-widget'
|
||||
}
|
||||
|
||||
}
|
||||
1
arduino-ide-extension/src/browser/style/index.css
Normal file
1
arduino-ide-extension/src/browser/style/index.css
Normal file
@@ -0,0 +1 @@
|
||||
@import './list-widget.css';
|
||||
76
arduino-ide-extension/src/browser/style/list-widget.css
Normal file
76
arduino-ide-extension/src/browser/style/list-widget.css
Normal file
@@ -0,0 +1,76 @@
|
||||
.arduino-list-widget {
|
||||
color: var(--theia-ui-font-color1);
|
||||
}
|
||||
|
||||
.arduino-list-widget .search-bar > input {
|
||||
margin: 0px 5px 0px 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.component-list-item {
|
||||
padding: 10px;
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
}
|
||||
|
||||
.component-list-item:hover {
|
||||
background: var(--theia-accent-color4);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.component-list-item:hover .meta-info {
|
||||
color: var(--theia-ui-font-color1);
|
||||
}
|
||||
|
||||
.component-list-item .meta-info {
|
||||
color: var(--theia-ui-font-color3);
|
||||
}
|
||||
|
||||
.component-list-item .header {
|
||||
padding-bottom: 2px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.component-list-item .header .name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.component-list-item .header .author {
|
||||
font-weight: bold;
|
||||
color: var(--theia-ui-font-color2);
|
||||
}
|
||||
|
||||
.component-list-item .header .version {
|
||||
margin-left: auto;
|
||||
justify-self: end;
|
||||
color: var(--theia-ui-font-color2);
|
||||
}
|
||||
|
||||
.component-list-item .header .installed {
|
||||
margin-left: 4px;
|
||||
justify-self: end;
|
||||
background-color: var(--theia-accent-color2);
|
||||
padding: 4px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.component-list-item .footer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.component-list-item .footer a {
|
||||
color: var(--theia-brand-color1);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.component-list-item .footer .install {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.component-list-item a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.component-list-item strong.installed {
|
||||
color: rgb(0, 151, 157)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { WorkspaceServiceExt } from './workspace-service-ext';
|
||||
|
||||
/**
|
||||
* This is a workaround to be able to inject the workspace service to the backend with its service path.
|
||||
*/
|
||||
@injectable()
|
||||
export class WorkspaceServiceExtImpl implements WorkspaceServiceExt {
|
||||
|
||||
@inject(WorkspaceService)
|
||||
protected readonly delegate: WorkspaceService;
|
||||
|
||||
async roots(): Promise<string[]> {
|
||||
const stats = await this.delegate.roots;
|
||||
return stats.map(stat => stat.uri);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const WorkspaceServiceExtPath = '/services/workspace-service-ext';
|
||||
export const WorkspaceServiceExt = Symbol('WorkspaceServiceExt');
|
||||
export interface WorkspaceServiceExt {
|
||||
roots(): Promise<string[]>;
|
||||
}
|
||||
Reference in New Issue
Block a user