|
@@ -0,0 +1,101 @@
|
|
|
|
|
+import { codicon } from '@theia/core/lib/browser';
|
|
|
|
|
+import { AbstractDialog, DialogProps } from '@theia/core/lib/browser';
|
|
|
|
|
+import { BuildResult, BuildStats, formatBytes } from '../common/compiler';
|
|
|
|
|
+
|
|
|
|
|
+/** Centered build report with a status icon, statistics and an OK button. */
|
|
|
|
|
+export class BuildReportDialog extends AbstractDialog<void> {
|
|
|
|
|
+ protected readonly success: boolean;
|
|
|
|
|
+ protected readonly rows: Array<[string, string]>;
|
|
|
|
|
+ protected readonly details?: string;
|
|
|
|
|
+
|
|
|
|
|
+ constructor(result: BuildResult, projectName: string) {
|
|
|
|
|
+ super({
|
|
|
|
|
+ title: result.success ? 'Build succeeded' : 'Build failed',
|
|
|
|
|
+ maxWidth: 480,
|
|
|
|
|
+ } as DialogProps);
|
|
|
|
|
+ this.success = result.success;
|
|
|
|
|
+ this.rows = reportRows(result, projectName);
|
|
|
|
|
+ this.details = result.success ? undefined : failureDetails(result);
|
|
|
|
|
+ this.contentNode.appendChild(this.renderBody());
|
|
|
|
|
+ const ok = this.appendAcceptButton('OK');
|
|
|
|
|
+ ok.classList.add('main');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ get value(): void {
|
|
|
|
|
+ return undefined;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected renderBody(): HTMLElement {
|
|
|
|
|
+ const wrap = document.createElement('div');
|
|
|
|
|
+ wrap.style.display = 'flex';
|
|
|
|
|
+ wrap.style.gap = '16px';
|
|
|
|
|
+ wrap.style.alignItems = 'flex-start';
|
|
|
|
|
+ wrap.style.padding = '4px 2px';
|
|
|
|
|
+ const icon = document.createElement('div');
|
|
|
|
|
+ icon.className = codicon(this.success ? 'check' : 'error');
|
|
|
|
|
+ icon.style.fontSize = '40px';
|
|
|
|
|
+ icon.style.color = this.success ? '#89D185' : '#F14C4C';
|
|
|
|
|
+ icon.style.lineHeight = '1';
|
|
|
|
|
+ wrap.appendChild(icon);
|
|
|
|
|
+ const right = document.createElement('div');
|
|
|
|
|
+ right.style.flex = '1';
|
|
|
|
|
+ right.style.minWidth = '0';
|
|
|
|
|
+ for (const [label, value] of this.rows) {
|
|
|
|
|
+ const row = document.createElement('div');
|
|
|
|
|
+ row.style.display = 'flex';
|
|
|
|
|
+ row.style.justifyContent = 'space-between';
|
|
|
|
|
+ row.style.gap = '24px';
|
|
|
|
|
+ row.style.padding = '2px 0';
|
|
|
|
|
+ const left = document.createElement('span');
|
|
|
|
|
+ left.textContent = label;
|
|
|
|
|
+ left.style.opacity = '0.7';
|
|
|
|
|
+ const val = document.createElement('span');
|
|
|
|
|
+ val.textContent = value;
|
|
|
|
|
+ val.style.fontWeight = '600';
|
|
|
|
|
+ val.style.textAlign = 'right';
|
|
|
|
|
+ row.appendChild(left);
|
|
|
|
|
+ row.appendChild(val);
|
|
|
|
|
+ right.appendChild(row);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.details) {
|
|
|
|
|
+ const pre = document.createElement('pre');
|
|
|
|
|
+ pre.textContent = this.details;
|
|
|
|
|
+ pre.style.marginTop = '12px';
|
|
|
|
|
+ pre.style.maxHeight = '160px';
|
|
|
|
|
+ pre.style.overflow = 'auto';
|
|
|
|
|
+ pre.style.fontSize = '12px';
|
|
|
|
|
+ pre.style.whiteSpace = 'pre-wrap';
|
|
|
|
|
+ right.appendChild(pre);
|
|
|
|
|
+ }
|
|
|
|
|
+ wrap.appendChild(right);
|
|
|
|
|
+ return wrap;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function reportRows(result: BuildResult, projectName: string): Array<[string, string]> {
|
|
|
|
|
+ const errors = result.diagnostics.filter(d => d.severity === 'error').length;
|
|
|
|
|
+ const warnings = result.diagnostics.filter(d => d.severity === 'warning').length;
|
|
|
|
|
+ if (!result.success) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ ['Project', projectName],
|
|
|
|
|
+ ['Errors', String(errors)],
|
|
|
|
|
+ ['Warnings', String(warnings)],
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+ const stats: BuildStats | undefined = result.stats;
|
|
|
|
|
+ return [
|
|
|
|
|
+ ['Project', projectName],
|
|
|
|
|
+ ['Sources', stats ? `${stats.files} Modula-2 file${stats.files === 1 ? '' : 's'}` : '—'],
|
|
|
|
|
+ ['Lines', stats ? stats.lines.toLocaleString('en-US') : '—'],
|
|
|
|
|
+ ['Executable', stats ? formatBytes(stats.outputSize) : '—'],
|
|
|
|
|
+ ];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function failureDetails(result: BuildResult): string {
|
|
|
|
|
+ const lines = result.diagnostics.slice(0, 5)
|
|
|
|
|
+ .map(d => `${d.file}:${d.line + 1}:${d.character + 1}: ${d.message}`);
|
|
|
|
|
+ if (lines.length === 0 && result.output.trim().length > 0) {
|
|
|
|
|
+ return result.output.trim().split('\n').slice(0, 5).join('\n');
|
|
|
|
|
+ }
|
|
|
|
|
+ return lines.join('\n');
|
|
|
|
|
+}
|