|
@@ -7,13 +7,27 @@ import { RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core
|
|
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
|
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
|
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
|
|
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
|
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
|
|
|
|
+import { ProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
|
|
|
|
|
+import { Diagnostic, DiagnosticSeverity } from '@theia/core/shared/vscode-languageserver-protocol';
|
|
|
|
|
+import URI from '@theia/core/lib/common/uri';
|
|
|
import {
|
|
import {
|
|
|
- BuildResult, Modula2Compiler, Modula2CompilerServicePath,
|
|
|
|
|
|
|
+ BuildDiagnostic, BuildResult, Modula2Compiler, Modula2CompilerServicePath,
|
|
|
Modula2Project, parseProjectFile,
|
|
Modula2Project, parseProjectFile,
|
|
|
} from '../common/compiler';
|
|
} from '../common/compiler';
|
|
|
|
|
|
|
|
const CATEGORY = 'Modula-2';
|
|
const CATEGORY = 'Modula-2';
|
|
|
|
|
|
|
|
|
|
+/** Marker owner for gm2 build diagnostics shown in the Problems view. */
|
|
|
|
|
+const MARKER_OWNER = 'modula2-gm2';
|
|
|
|
|
+
|
|
|
|
|
+function severityOf(d: BuildDiagnostic): DiagnosticSeverity {
|
|
|
|
|
+ switch (d.severity) {
|
|
|
|
|
+ case 'error': return DiagnosticSeverity.Error;
|
|
|
|
|
+ case 'warning': return DiagnosticSeverity.Warning;
|
|
|
|
|
+ default: return DiagnosticSeverity.Information;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const Commands = {
|
|
const Commands = {
|
|
|
build: Command.toLocalizedCommand({ id: 'modula2.build', label: 'Build', category: CATEGORY }),
|
|
build: Command.toLocalizedCommand({ id: 'modula2.build', label: 'Build', category: CATEGORY }),
|
|
|
buildFile: Command.toLocalizedCommand({ id: 'modula2.buildFile', label: 'Build File', category: CATEGORY }),
|
|
buildFile: Command.toLocalizedCommand({ id: 'modula2.buildFile', label: 'Build File', category: CATEGORY }),
|
|
@@ -30,6 +44,9 @@ class CompilerCommands implements CommandContribution {
|
|
|
@inject(WorkspaceService) protected readonly workspace: WorkspaceService;
|
|
@inject(WorkspaceService) protected readonly workspace: WorkspaceService;
|
|
|
@inject(EditorManager) protected readonly editors: EditorManager;
|
|
@inject(EditorManager) protected readonly editors: EditorManager;
|
|
|
@inject(MessageService) protected readonly messages: MessageService;
|
|
@inject(MessageService) protected readonly messages: MessageService;
|
|
|
|
|
+ @inject(ProblemManager) protected readonly problems: ProblemManager;
|
|
|
|
|
+ /** URIs currently carrying our markers, so stale ones can be cleared. */
|
|
|
|
|
+ protected readonly markedUris = new Set<string>();
|
|
|
|
|
|
|
|
registerCommands(commands: CommandRegistry): void {
|
|
registerCommands(commands: CommandRegistry): void {
|
|
|
commands.registerCommand(Commands.detect, { execute: () => this.detect() });
|
|
commands.registerCommand(Commands.detect, { execute: () => this.detect() });
|
|
@@ -70,6 +87,38 @@ class CompilerCommands implements CommandContribution {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** Publish build diagnostics as Problems markers (click-to-navigate). */
|
|
|
|
|
+ protected setProblemMarkers(project: Modula2Project, diagnostics: BuildDiagnostic[]): void {
|
|
|
|
|
+ this.clearProblemMarkers();
|
|
|
|
|
+ const rootUri = new URI(`file://${project.root}`);
|
|
|
|
|
+ const byFile = new Map<string, Diagnostic[]>();
|
|
|
|
|
+ for (const d of diagnostics) {
|
|
|
|
|
+ const uri = d.file.startsWith('/') ? new URI(`file://${d.file}`) : rootUri.resolve(d.file);
|
|
|
|
|
+ const list = byFile.get(uri.toString()) ?? [];
|
|
|
|
|
+ list.push({
|
|
|
|
|
+ range: {
|
|
|
|
|
+ start: { line: d.line, character: d.character },
|
|
|
|
|
+ end: { line: d.line, character: d.character + 1 },
|
|
|
|
|
+ },
|
|
|
|
|
+ severity: severityOf(d),
|
|
|
|
|
+ message: d.message,
|
|
|
|
|
+ source: 'gm2',
|
|
|
|
|
+ });
|
|
|
|
|
+ byFile.set(uri.toString(), list);
|
|
|
|
|
+ }
|
|
|
|
|
+ for (const [key, list] of byFile) {
|
|
|
|
|
+ this.problems.setMarkers(new URI(key), MARKER_OWNER, list);
|
|
|
|
|
+ this.markedUris.add(key);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected clearProblemMarkers(): void {
|
|
|
|
|
+ for (const key of this.markedUris) {
|
|
|
|
|
+ this.problems.cleanAllMarkers(new URI(key));
|
|
|
|
|
+ }
|
|
|
|
|
+ this.markedUris.clear();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
protected summarize(result: BuildResult): string {
|
|
protected summarize(result: BuildResult): string {
|
|
|
const errors = result.diagnostics.filter(d => d.severity === 'error').length;
|
|
const errors = result.diagnostics.filter(d => d.severity === 'error').length;
|
|
|
const warnings = result.diagnostics.filter(d => d.severity === 'warning').length;
|
|
const warnings = result.diagnostics.filter(d => d.severity === 'warning').length;
|
|
@@ -78,6 +127,7 @@ class CompilerCommands implements CommandContribution {
|
|
|
|
|
|
|
|
protected async build(project: Modula2Project): Promise<void> {
|
|
protected async build(project: Modula2Project): Promise<void> {
|
|
|
const result = await this.compiler.build(project);
|
|
const result = await this.compiler.build(project);
|
|
|
|
|
+ this.setProblemMarkers(project, result.diagnostics);
|
|
|
if (result.success) {
|
|
if (result.success) {
|
|
|
this.messages.info(`Modula-2: build succeeded. ${this.summarize(result)}`);
|
|
this.messages.info(`Modula-2: build succeeded. ${this.summarize(result)}`);
|
|
|
} else {
|
|
} else {
|
|
@@ -103,13 +153,17 @@ class CompilerCommands implements CommandContribution {
|
|
|
|
|
|
|
|
protected async clean(project: Modula2Project): Promise<void> {
|
|
protected async clean(project: Modula2Project): Promise<void> {
|
|
|
await this.compiler.clean(project);
|
|
await this.compiler.clean(project);
|
|
|
|
|
+ this.clearProblemMarkers();
|
|
|
this.messages.info('Modula-2: cleaned build outputs.');
|
|
this.messages.info('Modula-2: cleaned build outputs.');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
protected async run(project: Modula2Project): Promise<void> {
|
|
protected async run(project: Modula2Project): Promise<void> {
|
|
|
const result = await this.compiler.run(project);
|
|
const result = await this.compiler.run(project);
|
|
|
|
|
+ this.setProblemMarkers(project, result.diagnostics);
|
|
|
if (!result.success && result.diagnostics.length > 0) {
|
|
if (!result.success && result.diagnostics.length > 0) {
|
|
|
- await this.build(project);
|
|
|
|
|
|
|
+ const first = result.diagnostics.slice(0, 3)
|
|
|
|
|
+ .map(d => `${d.file}:${d.line + 1}:${d.character + 1}: ${d.message}`).join('\n');
|
|
|
|
|
+ this.messages.error(`Modula-2: build failed.\n${first}`);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
const output = result.output.length > 2000 ? result.output.slice(0, 2000) + '\n…' : result.output;
|
|
const output = result.output.length > 2000 ? result.output.slice(0, 2000) + '\n…' : result.output;
|