zen mode for the output

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta
2020-07-31 13:56:21 +02:00
parent 06c3015158
commit a2b3d9b314
12 changed files with 358 additions and 356 deletions

View File

@@ -311,24 +311,6 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
hc: 'activityBar.inactiveForeground'
},
description: 'Background color of the toolbar items when hovering over them. Such as Upload, Verify, etc.'
},
{
id: 'arduino.output.background',
defaults: {
dark: 'editorWidget.background',
light: 'editorWidget.background',
hc: 'editorWidget.background'
},
description: 'Background color of the Output view.'
},
{
id: 'arduino.output.foreground',
defaults: {
dark: 'editorWidget.foreground',
light: 'editorWidget.foreground',
hc: 'editorWidget.foreground'
},
description: 'Color of the text in the Output view.'
}
);
}

View File

@@ -108,9 +108,7 @@
"secondaryButton.hoverBackground": "#dae3e3",
"arduino.branding.primary": "#00979d",
"arduino.branding.secondary": "#b5c8c9",
"arduino.foreground": "#edf1f1",
"arduino.output.background": "#000000",
"arduino.output.foreground": "#ffffff"
"arduino.foreground": "#edf1f1"
},
"type": "light",
"name": "Arduino"

View File

@@ -155,12 +155,3 @@
border: 1px solid var(--theia-arduino-toolbar-background);
padding: 2px 0px 2px 9px;
}
#outputView .monaco-editor .lines-content.monaco-editor-background {
background-color: var(--theia-arduino-output-background);
}
.monaco-editor {
/* #outputView .monaco-editor .inputarea.ime-input { */
color: var(--theia-arduino-output-foreground);
}

View File

@@ -1,19 +1,41 @@
import { ToolOutputServiceClient } from '../../common/protocol/tool-output-service';
import { injectable, inject } from 'inversify';
import { CommandService } from '@theia/core/lib/common/command';
import { OutputCommands } from '@theia/output/lib/browser/output-commands';
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
import { OutputChannelManager, OutputChannelSeverity } from '@theia/output/lib/common/output-channel';
import { ToolOutputServiceClient, ToolOutputMessage } from '../../common/protocol/tool-output-service';
@injectable()
export class ToolOutputServiceClientImpl implements ToolOutputServiceClient {
@inject(CommandService)
protected commandService: CommandService;
@inject(OutputContribution)
protected outputContribution: OutputContribution;
onNewOutput(tool: string, text: string): void {
@inject(OutputChannelManager)
protected outputChannelManager: OutputChannelManager;
onMessageReceived(message: ToolOutputMessage): void {
const { tool, chunk } = message;
const name = `Arduino: ${tool}`;
const channel = this.outputChannelManager.getChannel(name);
// Zen-mode: we do not reveal the output for daemon messages.
const show = tool === 'daemon22' ? Promise.resolve() : this.commandService.executeCommand(OutputCommands.SHOW.id, { name, options: { preserveFocus: false } });
show.then(() => this.commandService.executeCommand(OutputCommands.APPEND.id, { name, text }));
const show: Promise<any> = tool === 'daemon'
// This will open and reveal the view but won't show it. You will see the toggle bottom panel on the status bar
? this.outputContribution.openView({ activate: false, reveal: false })
// This will open, reveal but do not activate the Output view.
: Promise.resolve(channel.show({ preserveFocus: true }));
show.then(() => channel.append(chunk, this.toOutputSeverity(message)));
}
private toOutputSeverity(message: ToolOutputMessage): OutputChannelSeverity {
if (message.severity) {
switch (message.severity) {
case 'error': return OutputChannelSeverity.Error
case 'warning': return OutputChannelSeverity.Warning
case 'info': return OutputChannelSeverity.Info
default: return OutputChannelSeverity.Info
}
}
return OutputChannelSeverity.Info
}
}

View File

@@ -1,16 +1,22 @@
import { JsonRpcServer } from "@theia/core";
import { JsonRpcServer } from '@theia/core';
export const ToolOutputServiceServer = Symbol("ToolOutputServiceServer");
export interface ToolOutputMessage {
readonly tool: string;
readonly chunk: string;
readonly severity?: 'error' | 'warning' | 'info';
}
export const ToolOutputServiceServer = Symbol('ToolOutputServiceServer');
export interface ToolOutputServiceServer extends JsonRpcServer<ToolOutputServiceClient> {
publishNewOutput(tool: string, chunk: string): void;
append(message: ToolOutputMessage): void;
disposeClient(client: ToolOutputServiceClient): void;
}
export const ToolOutputServiceClient = Symbol("ToolOutputServiceClient");
export const ToolOutputServiceClient = Symbol('ToolOutputServiceClient');
export interface ToolOutputServiceClient {
onNewOutput(tool: string, chunk: string): void;
onMessageReceived(message: ToolOutputMessage): void;
}
export namespace ToolOutputService {
export const SERVICE_PATH = "/tool-output-service";
export const SERVICE_PATH = '/tool-output-service';
}

View File

@@ -218,7 +218,7 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
protected onData(message: string, options: { useOutput: boolean } = { useOutput: true }): void {
if (options.useOutput) {
this.toolOutputService.publishNewOutput('daemon', DaemonLog.toPrettyString(message));
this.toolOutputService.append({ tool: 'daemon', chunk: DaemonLog.toPrettyString(message) });
}
DaemonLog.log(this.logger, message);
}

View File

@@ -167,13 +167,13 @@ export class BoardsServiceImpl implements BoardsService {
// The `BoardListResp` looks like this for a known attached board:
// [
// {
// "address": "COM10",
// "protocol": "serial",
// "protocol_label": "Serial Port (USB)",
// "boards": [
// 'address': 'COM10',
// 'protocol': 'serial',
// 'protocol_label': 'Serial Port (USB)',
// 'boards': [
// {
// "name": "Arduino MKR1000",
// "FQBN": "arduino:samd:mkr1000"
// 'name': 'Arduino MKR1000',
// 'FQBN': 'arduino:samd:mkr1000'
// }
// ]
// }
@@ -181,9 +181,9 @@ export class BoardsServiceImpl implements BoardsService {
// And the `BoardListResp` looks like this for an unknown board:
// [
// {
// "address": "COM9",
// "protocol": "serial",
// "protocol_label": "Serial Port (USB)",
// 'address': 'COM9',
// 'protocol': 'serial',
// 'protocol_label': 'Serial Port (USB)',
// }
// ]
ports.push({ protocol, address });
@@ -301,7 +301,7 @@ export class BoardsServiceImpl implements BoardsService {
const installedPlatforms = installedPlatformsResp.getInstalledPlatformList();
const req = new PlatformSearchReq();
req.setSearchArgs(options.query || "");
req.setSearchArgs(options.query || '');
req.setAllVersions(true);
req.setInstance(instance);
const resp = await new Promise<PlatformSearchResp>((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp)));
@@ -317,9 +317,9 @@ export class BoardsServiceImpl implements BoardsService {
name: platform.getName(),
author: platform.getMaintainer(),
availableVersions: [platform.getLatest()],
description: platform.getBoardsList().map(b => b.getName()).join(", "),
description: platform.getBoardsList().map(b => b.getName()).join(', '),
installable: true,
summary: "Boards included in this package:",
summary: 'Boards included in this package:',
installedVersion,
boards: platform.getBoardsList().map(b => <Board>{ name: b.getName(), fqbn: b.getFqbn() }),
moreInfoLink: platform.getWebsite()
@@ -378,7 +378,7 @@ export class BoardsServiceImpl implements BoardsService {
}
const { client, instance } = coreClient;
const [platform, architecture] = pkg.id.split(":");
const [platform, architecture] = pkg.id.split(':');
const req = new PlatformInstallReq();
req.setInstance(instance);
@@ -386,12 +386,12 @@ export class BoardsServiceImpl implements BoardsService {
req.setPlatformPackage(platform);
req.setVersion(version);
console.info("Starting board installation", pkg);
console.info('Starting board installation', pkg);
const resp = client.platformInstall(req);
resp.on('data', (r: PlatformInstallResp) => {
const prog = r.getProgress();
if (prog && prog.getFile()) {
this.toolOutputService.publishNewOutput("board download", `downloading ${prog.getFile()}\n`)
this.toolOutputService.append({ tool: 'board download', chunk: `downloading ${prog.getFile()}\n` });
}
});
await new Promise<void>((resolve, reject) => {
@@ -403,7 +403,7 @@ export class BoardsServiceImpl implements BoardsService {
const updatedPackage = packages.find(({ id }) => id === pkg.id) || pkg;
this.client.notifyBoardInstalled({ pkg: updatedPackage });
}
console.info("Board installation done", pkg);
console.info('Board installation done', pkg);
}
async uninstall(options: { item: BoardsPackage }): Promise<void> {
@@ -414,19 +414,19 @@ export class BoardsServiceImpl implements BoardsService {
}
const { client, instance } = coreClient;
const [platform, architecture] = pkg.id.split(":");
const [platform, architecture] = pkg.id.split(':');
const req = new PlatformUninstallReq();
req.setInstance(instance);
req.setArchitecture(architecture);
req.setPlatformPackage(platform);
console.info("Starting board uninstallation", pkg);
console.info('Starting board uninstallation', pkg);
let logged = false;
const resp = client.platformUninstall(req);
resp.on('data', (_: PlatformUninstallResp) => {
if (!logged) {
this.toolOutputService.publishNewOutput("board uninstall", `uninstalling ${pkg.id}\n`)
this.toolOutputService.append({ tool: 'board uninstall', chunk: `uninstalling ${pkg.id}\n` });
logged = true;
}
})
@@ -438,7 +438,7 @@ export class BoardsServiceImpl implements BoardsService {
// Here, unlike at `install` we send out the argument `pkg`. Otherwise, we would not know about the board FQBN.
this.client.notifyBoardUninstalled({ pkg });
}
console.info("Board uninstallation done", pkg);
console.info('Board uninstallation done', pkg);
}
}

View File

@@ -1,12 +1,12 @@
import * as grpc from '@grpc/grpc-js';
import { inject, injectable } from 'inversify';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { ToolOutputServiceServer } from '../common/protocol';
import { GrpcClientProvider } from './grpc-client-provider';
import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb';
import * as commandsGrpcPb from './cli-protocol/commands/commands_grpc_pb';
import { Instance } from './cli-protocol/commands/common_pb';
import { InitReq, InitResp, UpdateIndexReq, UpdateIndexResp, UpdateLibrariesIndexResp, UpdateLibrariesIndexReq } from './cli-protocol/commands/commands_pb';
import { Event, Emitter } from '@theia/core/lib/common/event';
@injectable()
export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Client> {
@@ -69,11 +69,11 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
indexUpdateSucceeded = true;
break;
} catch (e) {
this.toolOutputService.publishNewOutput("daemon", `Error while updating index in attempt ${i}: ${e}`);
this.toolOutputService.append({ tool: 'daemon', chunk: `Error while updating index in attempt ${i}: ${e}`, severity: 'error' });
}
}
if (!indexUpdateSucceeded) {
this.toolOutputService.publishNewOutput("daemon", `Was unable to update the index. Please restart to try again.`);
this.toolOutputService.append({ tool: 'daemon', chunk: 'Was unable to update the index. Please restart to try again.', severity: 'error' });
}
let libIndexUpdateSucceeded = true;
@@ -83,11 +83,11 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
libIndexUpdateSucceeded = true;
break;
} catch (e) {
this.toolOutputService.publishNewOutput("daemon", `Error while updating library index in attempt ${i}: ${e}`);
this.toolOutputService.append({ tool: 'daemon', chunk: `Error while updating library index in attempt ${i}: ${e}`, severity: 'error' });
}
}
if (!libIndexUpdateSucceeded) {
this.toolOutputService.publishNewOutput("daemon", `Was unable to update the library index. Please restart to try again.`);
this.toolOutputService.append({ tool: 'daemon', chunk: `Was unable to update the library index. Please restart to try again.`, severity: 'error' });
}
if (indexUpdateSucceeded && libIndexUpdateSucceeded) {
@@ -109,12 +109,12 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
if (progress.getCompleted()) {
if (file) {
if (/\s/.test(file)) {
this.toolOutputService.publishNewOutput("daemon", `${file} completed.\n`);
this.toolOutputService.append({ tool: 'daemon', chunk: `${file} completed.\n` });
} else {
this.toolOutputService.publishNewOutput("daemon", `Download of '${file}' completed.\n'`);
this.toolOutputService.append({ tool: 'daemon', chunk: `Download of '${file}' completed.\n'` });
}
} else {
this.toolOutputService.publishNewOutput("daemon", `The library index has been successfully updated.\n'`);
this.toolOutputService.append({ tool: 'daemon', chunk: `The library index has been successfully updated.\n'` });
}
file = undefined;
}
@@ -140,12 +140,12 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
if (progress.getCompleted()) {
if (file) {
if (/\s/.test(file)) {
this.toolOutputService.publishNewOutput("daemon", `${file} completed.\n`);
this.toolOutputService.append({ tool: 'daemon', chunk: `${file} completed.\n` });
} else {
this.toolOutputService.publishNewOutput("daemon", `Download of '${file}' completed.\n'`);
this.toolOutputService.append({ tool: 'daemon', chunk: `Download of '${file}' completed.\n'` });
}
} else {
this.toolOutputService.publishNewOutput("daemon", `The index has been successfully updated.\n'`);
this.toolOutputService.append({ tool: 'daemon', chunk: `The index has been successfully updated.\n'` });
}
file = undefined;
}

View File

@@ -35,7 +35,7 @@ export class CoreServiceImpl implements CoreService {
}
async compile(options: CoreService.Compile.Options): Promise<void> {
this.toolOutputService.publishNewOutput('compile', 'Compiling...\n' + JSON.stringify(options, null, 2) + '\n');
this.toolOutputService.append({ tool: 'compile', chunk: 'Compiling...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
const { sketchUri, fqbn } = options;
const sketchFilePath = await this.fileSystem.getFsPath(sketchUri);
if (!sketchFilePath) {
@@ -66,22 +66,22 @@ export class CoreServiceImpl implements CoreService {
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (cr: CompileResp) => {
this.toolOutputService.publishNewOutput("compile", Buffer.from(cr.getOutStream_asU8()).toString());
this.toolOutputService.publishNewOutput("compile", Buffer.from(cr.getErrStream_asU8()).toString());
this.toolOutputService.append({ tool: 'compile', chunk: Buffer.from(cr.getOutStream_asU8()).toString() });
this.toolOutputService.append({ tool: 'compile', chunk: Buffer.from(cr.getErrStream_asU8()).toString() });
});
result.on('error', error => reject(error));
result.on('end', () => resolve());
});
this.toolOutputService.publishNewOutput("compile", "Compilation complete.\n");
this.toolOutputService.append({ tool: 'compile', chunk: '\n--------------------------\nCompilation complete.\n' });
} catch (e) {
this.toolOutputService.publishNewOutput("compile", `Compilation error: ${e}\n`);
this.toolOutputService.append({ tool: 'compile', chunk: `Compilation error: ${e}\n`, severity: 'error' });
throw e;
}
}
async upload(options: CoreService.Upload.Options): Promise<void> {
await this.compile(options);
this.toolOutputService.publishNewOutput('upload', 'Uploading...\n' + JSON.stringify(options, null, 2) + '\n');
this.toolOutputService.append({ tool: 'upload', chunk: 'Uploading...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
const { sketchUri, fqbn } = options;
const sketchFilePath = await this.fileSystem.getFsPath(sketchUri);
if (!sketchFilePath) {
@@ -113,15 +113,15 @@ export class CoreServiceImpl implements CoreService {
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (cr: UploadResp) => {
this.toolOutputService.publishNewOutput("upload", Buffer.from(cr.getOutStream_asU8()).toString());
this.toolOutputService.publishNewOutput("upload", Buffer.from(cr.getErrStream_asU8()).toString());
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(cr.getOutStream_asU8()).toString() });
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(cr.getErrStream_asU8()).toString() });
});
result.on('error', error => reject(error));
result.on('end', () => resolve());
});
this.toolOutputService.publishNewOutput("upload", "Upload complete.\n");
this.toolOutputService.append({ tool: 'upload', chunk: '\n--------------------------\nUpload complete.\n' });
} catch (e) {
this.toolOutputService.publishNewOutput("upload", `Upload error: ${e}\n`);
this.toolOutputService.append({ tool: 'upload', chunk: `Upload error: ${e}\n`, severity: 'error' });
throw e;
}
}

View File

@@ -89,7 +89,7 @@ export class LibraryServiceImpl implements LibraryService {
resp.on('data', (r: LibraryInstallResp) => {
const prog = r.getProgress();
if (prog) {
this.toolOutputService.publishNewOutput("library download", `downloading ${prog.getFile()}: ${prog.getCompleted()}%\n`)
this.toolOutputService.append({ tool: 'library', chunk: `downloading ${prog.getFile()}: ${prog.getCompleted()}%\n` });
}
});
await new Promise<void>((resolve, reject) => {
@@ -115,7 +115,7 @@ export class LibraryServiceImpl implements LibraryService {
const resp = client.libraryUninstall(req);
resp.on('data', (_: LibraryUninstallResp) => {
if (!logged) {
this.toolOutputService.publishNewOutput("library uninstall", `uninstalling ${library.name}:${library.installedVersion}%\n`)
this.toolOutputService.append({ tool: 'library', chunk: `uninstalling ${library.name}:${library.installedVersion}%\n` });
logged = true;
}
});
@@ -129,7 +129,7 @@ export class LibraryServiceImpl implements LibraryService {
function toLibrary(tpl: Partial<Library>, release: LibraryRelease, availableVersions: string[]): Library {
return {
name: "",
name: '',
installable: false,
...tpl,

View File

@@ -1,23 +1,23 @@
import { injectable } from "inversify";
import { ToolOutputServiceServer, ToolOutputServiceClient } from "../common/protocol/tool-output-service";
import { injectable } from 'inversify';
import { ToolOutputServiceServer, ToolOutputServiceClient, ToolOutputMessage } from '../common/protocol/tool-output-service';
@injectable()
export class ToolOutputServiceServerImpl implements ToolOutputServiceServer {
protected clients: ToolOutputServiceClient[] = [];
publishNewOutput(tool: string, chunk: string): void {
if (!chunk) {
append(message: ToolOutputMessage): void {
if (!message.chunk) {
return;
}
this.clients.forEach(c => c.onNewOutput(tool, chunk));
for (const client of this.clients) {
client.onMessageReceived(message);
}
}
setClient(client: ToolOutputServiceClient | undefined): void {
if (!client) {
return;
}
this.clients.push(client);
}
@@ -31,7 +31,10 @@ export class ToolOutputServiceServerImpl implements ToolOutputServiceServer {
}
dispose(): void {
this.clients = [];
for (const client of this.clients) {
this.disposeClient(client);
}
this.clients.length = 0;
}
}