"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.NexusDocumentSymbolProvider = void 0;
const vscode = __importStar(require("vscode"));
const lexer_1 = require("./lexer");
const parser_1 = require("./parser");
/**
 * Outline / breadcrumbs for `let` bindings and `fn` definitions.
 */
class NexusDocumentSymbolProvider {
    provideDocumentSymbols(document, _token) {
        const lexer = new lexer_1.Lexer(document.getText());
        const parser = new parser_1.Parser(lexer);
        const program = parser.parseProgram();
        return collectSymbols(document, program);
    }
}
exports.NexusDocumentSymbolProvider = NexusDocumentSymbolProvider;
function collectSymbols(document, program) {
    const symbols = [];
    for (const stmt of program.statements) {
        const symbol = statementToSymbol(document, stmt);
        if (symbol) {
            symbols.push(symbol);
        }
    }
    return symbols;
}
function statementToSymbol(document, stmt) {
    if (stmt.type !== "LetStatement") {
        return undefined;
    }
    const letStmt = stmt;
    const name = letStmt.name.value;
    const nameRange = tokenRange(document, letStmt.name.token);
    const fullRange = expandRange(document, tokenRange(document, letStmt.token), expressionEndRange(document, letStmt.value));
    const fn = asFunctionLiteral(letStmt.value);
    if (fn) {
        const params = fn.parameters.map((p) => p.value).join(", ");
        const symbol = new vscode.DocumentSymbol(name, `fn(${params})`, vscode.SymbolKind.Function, fullRange, nameRange);
        for (const nested of fn.body.statements) {
            const child = statementToSymbol(document, nested);
            if (child) {
                symbol.children.push(child);
            }
        }
        return symbol;
    }
    return new vscode.DocumentSymbol(name, "let", vscode.SymbolKind.Variable, fullRange, nameRange);
}
function asFunctionLiteral(expr) {
    if (expr && expr.type === "FunctionLiteral") {
        return expr;
    }
    return undefined;
}
function expressionEndRange(document, expr) {
    if (!expr) {
        return new vscode.Range(0, 0, 0, 0);
    }
    if (expr.type === "FunctionLiteral") {
        return tokenRange(document, expr.body.token);
    }
    const token = getExpressionToken(expr);
    if (token) {
        return tokenRange(document, token);
    }
    return new vscode.Range(0, 0, 0, 0);
}
function getExpressionToken(expr) {
    if ("token" in expr) {
        return expr.token;
    }
    return undefined;
}
function tokenRange(document, token) {
    const line = Math.max(0, token.line - 1);
    const character = Math.max(0, token.column - 1);
    const safeLine = Math.min(line, Math.max(0, document.lineCount - 1));
    const lineText = document.lineAt(safeLine).text;
    const start = Math.min(character, lineText.length);
    const end = Math.min(start + Math.max(token.literal.length, 1), lineText.length);
    return new vscode.Range(safeLine, start, safeLine, Math.max(end, start));
}
function expandRange(document, start, end) {
    const a = start.start.isBefore(end.start) ? start.start : end.start;
    let b = start.end.isAfter(end.end) ? start.end : end.end;
    if (b.isEqual(a)) {
        b = document.lineAt(a.line).range.end;
    }
    return new vscode.Range(a, b);
}
//# sourceMappingURL=symbols.js.map