|
|
@@ -11,7 +11,6 @@ import { promises as fs, readFileSync } from 'fs';
|
|
|
import { spawn } from 'child_process';
|
|
|
import { pathToFileURL } from 'url';
|
|
|
import { lex, Token } from './m2/lexer';
|
|
|
-import { parseListing } from './pascalListing';
|
|
|
import { M2Symbol, M2Unit, parseUnitText } from './m2/parser';
|
|
|
import { analyseUnit } from './m2/analyse';
|
|
|
import { computeSemanticTokens, semanticTokensLegend } from './m2/semantic';
|
|
|
@@ -25,20 +24,15 @@ const connection = createConnection(ProposedFeatures.all);
|
|
|
const documents = new TextDocuments(TextDocument);
|
|
|
let validatorCommand = '';
|
|
|
let validatorArguments: string[] = ['{file}'];
|
|
|
-let pascalDialect = 'freepascal';
|
|
|
-let pascalValidators: Record<string, string> = {};
|
|
|
let formatOptions: FormatOptions = { ...DEFAULT_FORMAT_OPTIONS };
|
|
|
|
|
|
connection.onInitialize((params: InitializeParams): InitializeResult => {
|
|
|
const options = (params.initializationOptions || {}) as {
|
|
|
validatorCommand?: string; validatorArguments?: string[];
|
|
|
- pascalDialect?: string; pascalValidators?: Record<string, string>;
|
|
|
format?: Partial<FormatOptions>;
|
|
|
};
|
|
|
validatorCommand = options.validatorCommand || '';
|
|
|
validatorArguments = options.validatorArguments || ['{file}'];
|
|
|
- pascalDialect = options.pascalDialect || 'freepascal';
|
|
|
- pascalValidators = options.pascalValidators || {};
|
|
|
formatOptions = normalizeFormatOptions(options.format ?? {});
|
|
|
return {
|
|
|
capabilities: {
|
|
|
@@ -63,14 +57,11 @@ connection.onDidChangeConfiguration((params: DidChangeConfigurationParams) => {
|
|
|
const settings = (params.settings || {}) as {
|
|
|
modula2?: {
|
|
|
cocoR?: { validatorCommand?: string; validatorArguments?: string[] };
|
|
|
- pascal?: { dialect?: string; validators?: Record<string, string> };
|
|
|
format?: Partial<FormatOptions>;
|
|
|
};
|
|
|
};
|
|
|
validatorCommand = settings.modula2?.cocoR?.validatorCommand || validatorCommand;
|
|
|
validatorArguments = settings.modula2?.cocoR?.validatorArguments || validatorArguments;
|
|
|
- pascalDialect = settings.modula2?.pascal?.dialect || pascalDialect;
|
|
|
- pascalValidators = settings.modula2?.pascal?.validators || pascalValidators;
|
|
|
if (settings.modula2?.format) {
|
|
|
formatOptions = normalizeFormatOptions(settings.modula2.format);
|
|
|
}
|
|
|
@@ -81,10 +72,6 @@ documents.onDidChangeContent(e => validate(e.document));
|
|
|
documents.onDidClose(e => connection.sendDiagnostics({ uri: e.document.uri, diagnostics: [] }));
|
|
|
|
|
|
async function validate(document: TextDocument): Promise<void> {
|
|
|
- if (document.languageId === 'pascal') {
|
|
|
- await validatePascal(document);
|
|
|
- return;
|
|
|
- }
|
|
|
const local = analyseDocument(document);
|
|
|
if (!validatorCommand) {
|
|
|
connection.sendDiagnostics({ uri: document.uri, diagnostics: local });
|
|
|
@@ -113,47 +100,6 @@ async function validate(document: TextDocument): Promise<void> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/** Validate a Pascal document with the configured dialect validator.
|
|
|
- *
|
|
|
- * The local Modula-2 analysis never runs on Pascal text. Each pilot
|
|
|
- * validator takes the source file as its single argument and writes
|
|
|
- * `<base>.LST` next to it; diagnostics come from that listing, whose
|
|
|
- * `***** ^ message` lines point at the preceding source line.
|
|
|
- * No validator configured (or dialect unknown) means no diagnostics.
|
|
|
- */
|
|
|
-async function validatePascal(document: TextDocument): Promise<void> {
|
|
|
- const command = pascalValidators[pascalDialect];
|
|
|
- if (!command) {
|
|
|
- connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
|
|
|
- return;
|
|
|
- }
|
|
|
- const filePath = uriToFilePath(document.uri);
|
|
|
- const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'pascal-lsp-'));
|
|
|
- const base = path.basename(filePath, path.extname(filePath));
|
|
|
- const tempFile = path.join(dir, base + '.pas');
|
|
|
- try {
|
|
|
- await fs.writeFile(tempFile, document.getText(), 'utf8');
|
|
|
- await execute(command, [tempFile], path.dirname(filePath));
|
|
|
- let listing = '';
|
|
|
- try {
|
|
|
- listing = await fs.readFile(path.join(dir, base + '.LST'), 'utf8');
|
|
|
- } catch {
|
|
|
- listing = '';
|
|
|
- }
|
|
|
- connection.sendDiagnostics({ uri: document.uri, diagnostics: parseListing(listing) });
|
|
|
- } catch (error) {
|
|
|
- connection.sendDiagnostics({ uri: document.uri, diagnostics: [{
|
|
|
- severity: DiagnosticSeverity.Warning,
|
|
|
- range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } },
|
|
|
- message: `Pascal validator: ${error instanceof Error ? error.message : String(error)}`,
|
|
|
- source: 'Pascal'
|
|
|
- }] });
|
|
|
- } finally {
|
|
|
- await fs.rm(dir, { recursive: true, force: true });
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
function execute(command: string, args: string[], cwd: string): Promise<string> {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
const child = spawn(command, args, { cwd, shell: false });
|