"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.NexusDiagnostics = void 0;
exports.collectParseDiagnostics = collectParseDiagnostics;
const vscode = __importStar(require("vscode"));
const lexer_1 = require("./lexer");
const parser_1 = require("./parser");
const macro_1 = require("./macro");
const checker_1 = require("./checker");
const effects_1 = require("./effects");
const reflection_1 = require("./reflection");
const syntax_1 = require("./syntax");
const DIAGNOSTIC_SOURCE = "nexus";
/**
 * Parses Nexus source and publishes diagnostics to the Problems panel.
 */
class NexusDiagnostics {
    collection;
    timers = new Map();
    debounceMs = 250;
    constructor() {
        this.collection = vscode.languages.createDiagnosticCollection("nexus");
    }
    get disposable() {
        return this.collection;
    }
    dispose() {
        for (const timer of this.timers.values()) {
            clearTimeout(timer);
        }
        this.timers.clear();
        this.collection.dispose();
    }
    register(context) {
        context.subscriptions.push(this.collection);
        for (const doc of vscode.workspace.textDocuments) {
            this.schedule(doc);
        }
        context.subscriptions.push(vscode.workspace.onDidOpenTextDocument((doc) => this.schedule(doc)), vscode.workspace.onDidChangeTextDocument((e) => this.schedule(e.document)), vscode.workspace.onDidSaveTextDocument((doc) => this.schedule(doc, true)), vscode.workspace.onDidCloseTextDocument((doc) => {
            this.clearTimer(doc.uri);
            this.collection.delete(doc.uri);
        }), vscode.workspace.onDidChangeConfiguration((e) => {
            if (e.affectsConfiguration("nexus.diagnostics")) {
                for (const doc of vscode.workspace.textDocuments) {
                    this.schedule(doc, true);
                }
            }
        }));
    }
    schedule(document, immediate = false) {
        if (document.languageId !== "nexus") {
            return;
        }
        const key = document.uri.toString();
        this.clearTimer(document.uri);
        if (immediate) {
            this.refresh(document);
            return;
        }
        const timer = setTimeout(() => {
            this.timers.delete(key);
            this.refresh(document);
        }, this.debounceMs);
        this.timers.set(key, timer);
    }
    clearTimer(uri) {
        const key = uri.toString();
        const existing = this.timers.get(key);
        if (existing) {
            clearTimeout(existing);
            this.timers.delete(key);
        }
    }
    refresh(document) {
        const enabled = vscode.workspace
            .getConfiguration("nexus")
            .get("diagnostics.enable", true);
        if (!enabled) {
            this.collection.delete(document.uri);
            return;
        }
        const diagnostics = collectParseDiagnostics(document);
        this.collection.set(document.uri, diagnostics);
    }
}
exports.NexusDiagnostics = NexusDiagnostics;
function collectParseDiagnostics(document) {
    const lowered = (0, syntax_1.lowerSyntax)(document.getText());
    const lexer = new lexer_1.Lexer(lowered);
    const parser = new parser_1.Parser(lexer);
    let program = parser.parseProgram();
    const diagnostics = [];
    for (const message of parser.getErrors()) {
        diagnostics.push(errorToDiagnostic(document, message));
    }
    const macro = new macro_1.MacroExpander().expand(program);
    program = macro.program;
    for (const message of macro.errors) {
        diagnostics.push(errorToDiagnostic(document, message));
    }
    program = (0, reflection_1.expandReflection)(program).program;
    const ownership = new checker_1.OwnershipChecker().check(program);
    for (const d of ownership) {
        diagnostics.push(errorToDiagnostic(document, d.message, vscode.DiagnosticSeverity.Error));
    }
    for (const d of (0, effects_1.checkEffects)(program)) {
        diagnostics.push(errorToDiagnostic(document, d.message, vscode.DiagnosticSeverity.Error));
    }
    return diagnostics;
}
function errorToDiagnostic(document, message, severity = vscode.DiagnosticSeverity.Error) {
    const match = /line (\d+), column (\d+)/i.exec(message);
    let range;
    if (match) {
        const line = Math.max(0, Number.parseInt(match[1], 10) - 1);
        const column = Math.max(0, Number.parseInt(match[2], 10) - 1);
        const safeLine = Math.min(line, Math.max(0, document.lineCount - 1));
        const lineText = document.lineAt(safeLine).text;
        const start = Math.min(column, lineText.length);
        const end = Math.min(start + 1, Math.max(lineText.length, start + 1));
        range = new vscode.Range(safeLine, start, safeLine, end);
    }
    else {
        range =
            document.lineCount > 0
                ? document.lineAt(0).range
                : new vscode.Range(0, 0, 0, 0);
    }
    const diagnostic = new vscode.Diagnostic(range, message, severity);
    diagnostic.source = DIAGNOSTIC_SOURCE;
    return diagnostic;
}
//# sourceMappingURL=diagnostics.js.map