renamed customization folder to theia.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta
2020-07-18 21:09:08 +02:00
parent 5788ca3761
commit a74b159366
26 changed files with 21 additions and 21 deletions

View File

@@ -0,0 +1,25 @@
import { injectable, inject, postConstruct } from 'inversify';
import { AboutDialog as TheiaAboutDialog, ABOUT_CONTENT_CLASS } from '@theia/core/lib/browser/about-dialog';
import { ConfigService } from '../../../common/protocol/config-service';
@injectable()
export class AboutDialog extends TheiaAboutDialog {
@inject(ConfigService)
protected readonly configService: ConfigService;
@postConstruct()
protected async init(): Promise<void> {
const [, version] = await Promise.all([super.init(), this.configService.getVersion()]);
if (version) {
const { firstChild } = this.contentNode;
if (firstChild instanceof HTMLElement && firstChild.classList.contains(ABOUT_CONTENT_CLASS)) {
const cliVersion = document.createElement('div');
cliVersion.textContent = version;
firstChild.appendChild(cliVersion);
// TODO: anchor to the commit in the `arduino-cli` repository.
}
}
}
}

View File

@@ -0,0 +1,44 @@
import { injectable, inject } from 'inversify';
import { EditorWidget } from '@theia/editor/lib/browser';
import { CommandService } from '@theia/core/lib/common/command';
import { PreferencesWidget } from '@theia/preferences/lib/browser/views/preference-widget';
import { ApplicationShell as TheiaApplicationShell, Widget } from '@theia/core/lib/browser';
import { EditorMode } from '../../editor-mode';
import { SaveAsSketch } from '../../contributions/save-as-sketch';
@injectable()
export class ApplicationShell extends TheiaApplicationShell {
@inject(EditorMode)
protected readonly editorMode: EditorMode;
@inject(CommandService)
protected readonly commandService: CommandService;
protected track(widget: Widget): void {
super.track(widget);
if (!this.editorMode.proMode) {
if (widget instanceof EditorWidget) {
// Always allow closing the whitelisted files.
// TODO: It would be better to blacklist the sketch files only.
if (['tasks.json',
'launch.json',
'settings.json',
'arduino-cli.yaml'].some(fileName => widget.editor.uri.toString().endsWith(fileName))) {
return;
}
}
if (widget instanceof PreferencesWidget) {
return;
}
widget.title.closable = false;
}
}
async save(): Promise<void> {
await super.save();
await this.commandService.executeCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH.id, { execOnlyIfTemp: true, openAfterMove: true });
}
}

View File

@@ -0,0 +1,22 @@
import { injectable } from 'inversify';
import { BrowserMainMenuFactory as TheiaBrowserMainMenuFactory, MenuBarWidget } from '@theia/core/lib/browser/menu/browser-menu-plugin';
import { MainMenuManager } from '../../../common/main-menu-manager';
@injectable()
export class BrowserMainMenuFactory extends TheiaBrowserMainMenuFactory implements MainMenuManager {
protected menuBar: MenuBarWidget | undefined;
createMenuBar(): MenuBarWidget {
this.menuBar = super.createMenuBar();
return this.menuBar;
}
update() {
if (this.menuBar) {
this.menuBar.clearMenus();
this.fillMenuBar(this.menuBar);
}
}
}

View File

@@ -0,0 +1,13 @@
import '../../../../src/browser/style/browser-menu.css';
import { ContainerModule } from 'inversify';
import { BrowserMenuBarContribution, BrowserMainMenuFactory as TheiaBrowserMainMenuFactory } from '@theia/core/lib/browser/menu/browser-menu-plugin';
import { MainMenuManager } from '../../../common/main-menu-manager';
import { ArduinoMenuContribution } from './browser-menu-plugin';
import { BrowserMainMenuFactory } from './browser-main-menu-factory';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(BrowserMainMenuFactory).toSelf().inSingletonScope();
bind(MainMenuManager).toService(BrowserMainMenuFactory);
rebind(TheiaBrowserMainMenuFactory).toService(BrowserMainMenuFactory);
rebind(BrowserMenuBarContribution).to(ArduinoMenuContribution).inSingletonScope();
});

View File

@@ -0,0 +1,13 @@
import { injectable } from 'inversify';
import { FrontendApplication } from '@theia/core/lib/browser';
import { BrowserMenuBarContribution } from '@theia/core/lib/browser/menu/browser-menu-plugin';
@injectable()
export class ArduinoMenuContribution extends BrowserMenuBarContribution {
onStart(app: FrontendApplication): void {
const menu = this.factory.createMenuBar();
app.shell.addWidget(menu, { area: 'top' });
}
}

View File

@@ -0,0 +1,24 @@
import { injectable } from 'inversify';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import { CommonFrontendContribution as TheiaCommonFrontendContribution, CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
@injectable()
export class CommonFrontendContribution extends TheiaCommonFrontendContribution {
registerMenus(registry: MenuModelRegistry): void {
super.registerMenus(registry);
for (const command of [
CommonCommands.SAVE,
CommonCommands.SAVE_ALL,
CommonCommands.CUT,
CommonCommands.COPY,
CommonCommands.PASTE,
CommonCommands.COPY_PATH,
CommonCommands.FIND,
CommonCommands.REPLACE
]) {
registry.unregisterMenuAction(command);
}
}
}

View File

@@ -0,0 +1,55 @@
import { inject, injectable, postConstruct } from 'inversify';
import { Disposable } from '@theia/core/lib/common/disposable';
import { StatusBarAlignment } from '@theia/core/lib/browser/status-bar/status-bar';
import {
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
ApplicationConnectionStatusContribution as TheiaApplicationConnectionStatusContribution,
ConnectionStatus
} from '@theia/core/lib/browser/connection-status-service';
import { ArduinoDaemonClientImpl } from '../../arduino-daemon-client-impl';
@injectable()
export class FrontendConnectionStatusService extends TheiaFrontendConnectionStatusService {
@inject(ArduinoDaemonClientImpl)
protected readonly daemonClient: ArduinoDaemonClientImpl;
@postConstruct()
protected init(): void {
this.schedulePing();
this.wsConnectionProvider.onIncomingMessageActivity(() => {
// natural activity
this.updateStatus(this.daemonClient.isRunning);
this.schedulePing();
});
}
}
@injectable()
export class ApplicationConnectionStatusContribution extends TheiaApplicationConnectionStatusContribution {
@inject(ArduinoDaemonClientImpl)
protected readonly daemonClient: ArduinoDaemonClientImpl;
protected onStateChange(state: ConnectionStatus): void {
if (!this.daemonClient.isRunning && state === ConnectionStatus.ONLINE) {
return;
}
super.onStateChange(state);
}
protected handleOffline(): void {
const { isRunning } = this.daemonClient;
this.statusBar.setElement('connection-status', {
alignment: StatusBarAlignment.LEFT,
text: isRunning ? 'Offline' : '$(bolt) CLI Daemon Offline',
tooltip: isRunning ? 'Cannot connect to the backend.' : 'Cannot connect to the CLI daemon.',
priority: 5000
});
this.toDisposeOnOnline.push(Disposable.create(() => this.statusBar.removeElement('connection-status')));
document.body.classList.add('theia-mod-offline');
this.toDisposeOnOnline.push(Disposable.create(() => document.body.classList.remove('theia-mod-offline')));
}
}

View File

@@ -0,0 +1,31 @@
import { injectable, inject } from 'inversify';
import { FileSystem } from '@theia/filesystem/lib/common/filesystem';
import { CommandService } from '@theia/core/lib/common/command';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { FrontendApplication as TheiaFrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { ArduinoCommands } from '../../arduino-commands';
@injectable()
export class FrontendApplication extends TheiaFrontendApplication {
@inject(FileSystem)
protected readonly fileSystem: FileSystem;
@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;
@inject(CommandService)
protected readonly commandService: CommandService;
protected async initializeLayout(): Promise<void> {
await super.initializeLayout();
const roots = await this.workspaceService.roots;
for (const root of roots) {
const exists = await this.fileSystem.exists(root.uri);
if (exists) {
await this.commandService.executeCommand(ArduinoCommands.OPEN_SKETCH_FILES.id, root.uri);
}
}
}
}

View File

@@ -0,0 +1,24 @@
import { injectable } from 'inversify';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { ShellLayoutRestorer as TheiaShellLayoutRestorer } from '@theia/core/lib/browser/shell/shell-layout-restorer';
@injectable()
export class ShellLayoutRestorer extends TheiaShellLayoutRestorer {
// Workaround for https://github.com/eclipse-theia/theia/issues/6579.
async storeLayoutAsync(app: FrontendApplication): Promise<void> {
if (this.shouldStoreLayout) {
try {
this.logger.info('>>> Storing the layout...');
const layoutData = app.shell.getLayoutData();
const serializedLayoutData = this.deflate(layoutData);
await this.storageService.setData(this.storageKey, serializedLayoutData);
this.logger.info('<<< The layout has been successfully stored.');
} catch (error) {
await this.storageService.setData(this.storageKey, undefined);
this.logger.error('Error during serialization of layout data', error);
}
}
}
}

View File

@@ -0,0 +1,40 @@
import { inject, injectable, postConstruct } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { Title, Widget } from '@phosphor/widgets';
import { ILogger } from '@theia/core';
import { WidgetDecoration } from '@theia/core/lib/browser/widget-decoration';
import { TabBarDecoratorService as TheiaTabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator';
import { ConfigService } from '../../../common/protocol/config-service';
import { EditorWidget } from '@theia/editor/lib/browser';
@injectable()
export class TabBarDecoratorService extends TheiaTabBarDecoratorService {
@inject(ConfigService)
protected readonly configService: ConfigService;
@inject(ILogger)
protected readonly logger: ILogger;
protected dataDirUri: URI | undefined;
@postConstruct()
protected init(): void {
super.init();
this.configService.getConfiguration()
.then(({ dataDirUri }) => this.dataDirUri = new URI(dataDirUri))
.catch(err => this.logger.error(`Failed to determine the data directory: ${err}`));
}
getDecorations(title: Title<Widget>): WidgetDecoration.Data[] {
if (title.owner instanceof EditorWidget) {
const editor = title.owner.editor;
if (this.dataDirUri && this.dataDirUri.isEqualOrParent(editor.uri)) {
return [];
}
}
return super.getDecorations(title);
}
}

View File

@@ -0,0 +1,25 @@
import { injectable } from 'inversify';
import { EditorContribution as TheiaEditorContribution } from '@theia/editor/lib/browser/editor-contribution';
import { TextEditor } from '@theia/editor/lib/browser';
import { StatusBarAlignment } from '@theia/core/lib/browser';
@injectable()
export class EditorContribution extends TheiaEditorContribution {
protected updateLanguageStatus(editor: TextEditor | undefined): void {
}
protected setCursorPositionStatus(editor: TextEditor | undefined): void {
if (!editor) {
this.statusBar.removeElement('editor-status-cursor-position');
return;
}
const { cursor } = editor;
this.statusBar.setElement('editor-status-cursor-position', {
text: `${cursor.line + 1}`,
alignment: StatusBarAlignment.LEFT,
priority: 100
});
}
}

View File

@@ -0,0 +1,37 @@
import { inject, injectable } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { EditorManager as TheiaEditorManager, EditorOpenerOptions } from '@theia/editor/lib/browser/editor-manager';
import { ConfigService } from '../../../common/protocol/config-service';
import { EditorWidget } from '@theia/editor/lib/browser';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
@injectable()
export class EditorManager extends TheiaEditorManager {
@inject(ConfigService)
protected readonly configService: ConfigService;
async open(uri: URI, options?: EditorOpenerOptions): Promise<EditorWidget> {
const [widget, readOnly] = await Promise.all([super.open(uri, options), this.isReadOnly(uri)]);
if (readOnly) {
const { editor } = widget;
if (editor instanceof MonacoEditor) {
const codeEditor = editor.getControl();
codeEditor.updateOptions({ readOnly });
}
}
return widget;
}
protected async isReadOnly(uri: URI): Promise<boolean> {
const [config, configFileUri] = await Promise.all([
this.configService.getConfiguration(),
this.configService.getCliConfigFileUri()
]);
if (new URI(configFileUri).toString(true) === uri.toString(true)) {
return false;
}
return new URI(config.dataDirUri).isEqualOrParent(uri)
}
}

View File

@@ -0,0 +1,35 @@
import { inject, injectable } from 'inversify';
import { KeybindingRegistry } from '@theia/core/lib/browser';
import { ProblemStat } from '@theia/markers/lib/browser/problem/problem-manager';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { ProblemContribution as TheiaProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
import { EditorMode } from '../../editor-mode';
@injectable()
export class ProblemContribution extends TheiaProblemContribution {
@inject(EditorMode)
protected readonly editorMode: EditorMode;
async initializeLayout(app: FrontendApplication): Promise<void> {
if (this.editorMode.proMode) {
return super.initializeLayout(app);
}
}
protected setStatusBarElement(problemStat: ProblemStat): void {
if (this.editorMode.proMode) {
super.setStatusBarElement(problemStat);
}
}
registerKeybindings(keybindings: KeybindingRegistry): void {
if (this.toggleCommand) {
keybindings.registerKeybinding({
command: this.toggleCommand.id,
keybinding: 'ctrlcmd+alt+shift+m'
});
}
}
}

View File

@@ -0,0 +1,35 @@
import { inject, injectable, postConstruct } from 'inversify';
import { Diagnostic } from 'vscode-languageserver-types';
import URI from '@theia/core/lib/common/uri';
import { ILogger } from '@theia/core';
import { Marker } from '@theia/markers/lib/common/marker';
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
import { ConfigService } from '../../../common/protocol/config-service';
@injectable()
export class ProblemManager extends TheiaProblemManager {
@inject(ConfigService)
protected readonly configService: ConfigService;
@inject(ILogger)
protected readonly logger: ILogger;
protected dataDirUri: URI | undefined;
@postConstruct()
protected init(): void {
super.init();
this.configService.getConfiguration()
.then(({ dataDirUri }) => this.dataDirUri = new URI(dataDirUri))
.catch(err => this.logger.error(`Failed to determine the data directory: ${err}`));
}
setMarkers(uri: URI, owner: string, data: Diagnostic[]): Marker<Diagnostic>[] {
if (this.dataDirUri && this.dataDirUri.isEqualOrParent(uri)) {
return [];
}
return super.setMarkers(uri, owner, data);
}
}

View File

@@ -0,0 +1,13 @@
import { injectable } from 'inversify';
import { MonacoStatusBarContribution as TheiaMonacoStatusBarContribution } from '@theia/monaco/lib/browser/monaco-status-bar-contribution';
@injectable()
export class MonacoStatusBarContribution extends TheiaMonacoStatusBarContribution {
protected setConfigTabSizeWidget() {
}
protected setLineEndingWidget() {
}
}

View File

@@ -0,0 +1,18 @@
import { injectable, inject } from 'inversify';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { FileNavigatorContribution as TheiaFileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
import { EditorMode } from '../../editor-mode';
@injectable()
export class FileNavigatorContribution extends TheiaFileNavigatorContribution {
@inject(EditorMode)
protected readonly editorMode: EditorMode;
async initializeLayout(app: FrontendApplication): Promise<void> {
if (this.editorMode.proMode) {
return super.initializeLayout(app);
}
}
}

View File

@@ -0,0 +1,19 @@
import { injectable, inject } from 'inversify';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { OutlineViewContribution as TheiaOutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
import { EditorMode } from '../../editor-mode';
@injectable()
export class OutlineViewContribution extends TheiaOutlineViewContribution {
@inject(EditorMode)
protected readonly editorMode: EditorMode;
async initializeLayout(app: FrontendApplication): Promise<void> {
if (this.editorMode.proMode) {
return super.initializeLayout(app);
}
}
}

View File

@@ -0,0 +1,24 @@
import { inject, injectable } from 'inversify';
import { ScmContribution as TheiaScmContribution } from '@theia/scm/lib/browser/scm-contribution';
import { StatusBarEntry } from '@theia/core/lib/browser/status-bar/status-bar';
import { EditorMode } from '../../editor-mode';
@injectable()
export class ScmContribution extends TheiaScmContribution {
@inject(EditorMode)
protected readonly editorMode: EditorMode;
async initializeLayout(): Promise<void> {
if (this.editorMode.proMode) {
return super.initializeLayout();
}
}
protected setStatusBarEntry(id: string, entry: StatusBarEntry): void {
if (this.editorMode.proMode) {
super.setStatusBarEntry(id, entry);
}
}
}

View File

@@ -0,0 +1,24 @@
import { inject, injectable } from 'inversify';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { SearchInWorkspaceFrontendContribution as TheiaSearchInWorkspaceFrontendContribution, SearchInWorkspaceCommands } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
import { EditorMode } from '../../editor-mode';
import { MenuModelRegistry } from '@theia/core';
@injectable()
export class SearchInWorkspaceFrontendContribution extends TheiaSearchInWorkspaceFrontendContribution {
@inject(EditorMode)
protected readonly editorMode: EditorMode;
async initializeLayout(app: FrontendApplication): Promise<void> {
if (this.editorMode.proMode) {
return super.initializeLayout(app);
}
}
registerMenus(registry: MenuModelRegistry): void {
super.registerMenus(registry);
registry.unregisterMenuAction(SearchInWorkspaceCommands.OPEN_SIW_WIDGET);
}
}

View File

@@ -0,0 +1,38 @@
import { injectable } from 'inversify';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import { WorkspaceCommands, FileMenuContribution } from '@theia/workspace/lib/browser/workspace-commands';
import { WorkspaceFrontendContribution as TheiaWorkspaceFrontendContribution } from '@theia/workspace/lib/browser/workspace-frontend-contribution';
@injectable()
export class WorkspaceFrontendContribution extends TheiaWorkspaceFrontendContribution {
registerCommands(registry: CommandRegistry): void {
super.registerCommands(registry);
// TODO: instead of blacklisting commands to remove, it would be more robust to whitelist the ones we want to keep
const commands = new Set(registry.commands);
[
WorkspaceCommands.OPEN,
WorkspaceCommands.OPEN_FILE,
WorkspaceCommands.OPEN_FOLDER,
WorkspaceCommands.OPEN_WORKSPACE,
WorkspaceCommands.OPEN_RECENT_WORKSPACE,
WorkspaceCommands.SAVE_WORKSPACE_AS,
WorkspaceCommands.SAVE_AS,
WorkspaceCommands.CLOSE
].filter(commands.has.bind(commands)).forEach(registry.unregisterCommand.bind(registry));
}
registerMenus(_: MenuModelRegistry): void {
}
}
@injectable()
export class ArduinoFileMenuContribution extends FileMenuContribution {
registerMenus(_: MenuModelRegistry): void {
// NOOP
}
}

View File

@@ -0,0 +1,77 @@
import { injectable, inject } from 'inversify';
import { MessageService } from '@theia/core';
import { LabelProvider } from '@theia/core/lib/browser';
import { WorkspaceService as TheiaWorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { ConfigService } from '../../../common/protocol/config-service';
import { SketchesService } from '../../../common/protocol/sketches-service';
import { ArduinoWorkspaceRootResolver } from '../../arduino-workspace-resolver';
import { EditorMode } from '../../editor-mode';
@injectable()
export class WorkspaceService extends TheiaWorkspaceService {
@inject(SketchesService)
protected readonly sketchService: SketchesService;
@inject(ConfigService)
protected readonly configService: ConfigService;
@inject(LabelProvider)
protected readonly labelProvider: LabelProvider;
@inject(EditorMode)
protected readonly editorMode: EditorMode;
@inject(MessageService)
protected readonly messageService: MessageService;
private workspaceUri?: Promise<string | undefined>;
protected getDefaultWorkspaceUri(): Promise<string | undefined> {
if (this.workspaceUri) {
// Avoid creating a new sketch twice
return this.workspaceUri;
}
this.workspaceUri = (async () => {
try {
const hash = window.location.hash;
const [recentWorkspaces, recentSketches] = await Promise.all([
this.server.getRecentWorkspaces(),
this.sketchService.getSketches().then(sketches => sketches.map(s => s.uri))
]);
const toOpen = await new ArduinoWorkspaceRootResolver({
isValid: this.isValid.bind(this)
}).resolve({ hash, recentWorkspaces, recentSketches });
if (toOpen) {
const { uri } = toOpen;
await this.server.setMostRecentlyUsedWorkspace(uri);
return toOpen.uri;
}
return (await this.sketchService.createNewSketch()).uri;
} catch (err) {
this.logger.fatal(`Failed to determine the sketch directory: ${err}`)
this.messageService.error(
'There was an error creating the sketch directory. ' +
'See the log for more details. ' +
'The application will probably not work as expected.')
return super.getDefaultWorkspaceUri();
}
})();
return this.workspaceUri;
}
private async isValid(uri: string): Promise<boolean> {
const exists = await this.fileSystem.exists(uri);
if (!exists) {
return false;
}
// The workspace root location must exist. However, when opening a workspace root in pro-mode,
// the workspace root must not be a sketch folder. It can be the default sketch directory, or any other directories, for instance.
if (this.editorMode.proMode) {
return true;
}
const sketchFolder = await this.sketchService.isSketchFolder(uri);
return sketchFolder;
}
}