"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 (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSemanticTokenReference = exports.LSPServer = void 0;
const antlr4_c3_1 = require("antlr4-c3");
const antlr4ts_1 = require("antlr4ts");
const tree_1 = require("antlr4ts/tree");
const lsp = __importStar(require("vscode-languageserver"));
const flinkdb_schema_json_1 = __importDefault(require("./assets/flinkdb.schema.json"));
const completion_parser_1 = require("./completion-parser");
const cursor_1 = require("./cursor");
const FlinkSQLLexer_1 = require("./lib/FlinkSQLLexer");
const FlinkSQLParser_1 = require("./lib/FlinkSQLParser");
const preview_listener_1 = require("./listeners/preview.listener");
const schema_listener_1 = require("./listeners/schema.listener");
const statement_listener_1 = require("./listeners/statement.listener");
const structure_listener_1 = require("./listeners/structure.listener");
const tokens_collector_listener_1 = require("./listeners/tokens-collector.listener");
const parsing_warehouse_1 = require("./parsing-warehouse");
const protocol_translation_1 = require("./protocol-translation");
const schema_registry_1 = require("./schema-registry");
const utils_1 = require("./utils");
const lineage_visitor_1 = require("./visitors/lineage.visitor");
const space_replacer_format_visitor_1 = require("./visitors/space-replacer-format.visitor");
const preferredRules = [
    FlinkSQLParser_1.FlinkSQLParser.RULE_functionName,
    FlinkSQLParser_1.FlinkSQLParser.RULE_tableName,
    FlinkSQLParser_1.FlinkSQLParser.RULE_columnName,
    FlinkSQLParser_1.FlinkSQLParser.RULE_viewName,
    FlinkSQLParser_1.FlinkSQLParser.RULE_databaseName,
    FlinkSQLParser_1.FlinkSQLParser.RULE_catalogName
];
const ignoredTokens = [
    FlinkSQLLexer_1.FlinkSQLLexer.EOF,
    FlinkSQLLexer_1.FlinkSQLLexer.SEMICOLON,
    FlinkSQLLexer_1.FlinkSQLLexer.COMMA,
    FlinkSQLLexer_1.FlinkSQLLexer.DOT,
    FlinkSQLLexer_1.FlinkSQLLexer.ASTERISK,
    FlinkSQLLexer_1.FlinkSQLLexer.COLON,
    FlinkSQLLexer_1.FlinkSQLLexer.L_PARENTHESIS,
    FlinkSQLLexer_1.FlinkSQLLexer.R_PARENTHESIS,
    FlinkSQLLexer_1.FlinkSQLLexer.L_BRACE,
    FlinkSQLLexer_1.FlinkSQLLexer.R_BRACE,
    FlinkSQLLexer_1.FlinkSQLLexer.L_BRACKET,
    FlinkSQLLexer_1.FlinkSQLLexer.R_BRACKET,
    FlinkSQLLexer_1.FlinkSQLLexer.STRING_LITERAL,
    FlinkSQLLexer_1.FlinkSQLLexer.BIND_PARAMETER,
    FlinkSQLLexer_1.FlinkSQLLexer.REAL_LITERAL,
    FlinkSQLLexer_1.FlinkSQLLexer.IDENTIFIER
];
class LSPServer {
    constructor() {
        this._warehouse = new parsing_warehouse_1.ParsingWarehouse();
        this._schemaRegistry = new schema_registry_1.SchemaRegistry();
    }
    get warehouse() {
        return this._warehouse;
    }
    get schemaRegistry() {
        return this._schemaRegistry;
    }
    getTokenByPosition(document, position) {
        const parsingValue = this._warehouse.getParsingValue(document);
        const offset = document.offsetAt(position);
        return parsingValue.tokenStream.getTokens().find(t => t.startIndex <= offset && offset <= t.stopIndex + 1);
    }
    doValidation(document) {
        const parsingValue = this._warehouse.getParsingValue(document);
        return parsingValue.errors;
    }
    doFormatting(document, options) {
        const parsingValue = this._warehouse.getParsingValue(document);
        try {
            const visitor = new space_replacer_format_visitor_1.SpaceReplacerFormatVisitor(parsingValue.tokenStream, options);
            visitor.visit(parsingValue.tree);
            return visitor.textEdits;
        }
        catch (e) {
            return [];
        }
    }
    doFoldingRanges(document) {
        const parsingValue = this._warehouse.getParsingValue(document);
        const listener = new statement_listener_1.StatementListener(document);
        tree_1.ParseTreeWalker.DEFAULT.walk(listener, parsingValue.tree);
        const ranges = listener.getRanges();
        return ranges.map(range => (0, protocol_translation_1.toFoldingRange)(range));
    }
    doReferences(document, position, skipSelf = true) {
        const parsingValue = this._warehouse.getParsingValue(document);
        const tokens = parsingValue.tokenStream.getTokens();
        const currentOffset = document.offsetAt(position);
        const currentToken = tokens.find(token => {
            return token.startIndex <= currentOffset && currentOffset <= token.stopIndex;
        });
        if (currentToken?.text === undefined) {
            return [];
        }
        const listener = new tokens_collector_listener_1.TokensCollectorListener(document);
        tree_1.ParseTreeWalker.DEFAULT.walk(listener, parsingValue.tree);
        const semanticTokens = listener.getSemanticTokens();
        const references = [];
        const compareText = (str1, str2) => (0, utils_1.sanitizeText)(str1).localeCompare((0, utils_1.sanitizeText)(str2)) === 0;
        const findReferences = (token) => {
            if (currentToken.text && compareText(currentToken.text, token.name)) {
                references.push(token.range);
            }
        };
        semanticTokens.forEach(findReferences);
        if (skipSelf) {
            return references.filter(range => !(0, protocol_translation_1.positionInRange)(document, position, range));
        }
        return references;
    }
    doCompletion(document, position) {
        const sql = cursor_1.defaultCursor.insertAt(document.getText(), position);
        const charStream = antlr4ts_1.CharStreams.fromString(sql);
        const lexer = new FlinkSQLLexer_1.FlinkSQLLexer(charStream);
        const tokenStream = new antlr4ts_1.CommonTokenStream(lexer);
        const parser = new FlinkSQLParser_1.FlinkSQLParser(tokenStream);
        parser.buildParseTree = true;
        parser.removeErrorListeners();
        const tree = parser.program();
        const completionItems = [];
        const sqlCompletionParser = new completion_parser_1.FlinkSQLCompletionParser(tree, cursor_1.defaultCursor);
        const suggestions = sqlCompletionParser.getSuggestions({
            getCatalogs: () => {
                return this.schemaRegistry.getSchemaCatalogs();
            },
            getDatabases: args => {
                return this.schemaRegistry.getSchemaDatabases(args);
            },
            getTables: args => {
                return this.schemaRegistry.getSchemaTables(args);
            },
            getColumns: args => {
                return this.schemaRegistry.getSchemaColumns(args);
            }
        });
        completionItems.push(...suggestions);
        const parsingValue = this._warehouse.getParsingValue(document);
        const offset = document.offsetAt(position);
        const token = parsingValue.tokenStream.getTokens().find(t => t.startIndex <= offset && offset <= t.stopIndex + 1);
        if (!token) {
            return { completionItems: [], identifiers: [] };
        }
        const core = new antlr4_c3_1.CodeCompletionCore(parsingValue.parser);
        core.ignoredTokens = new Set(ignoredTokens);
        core.preferredRules = new Set(preferredRules);
        let index = token.tokenIndex;
        if (token.type === FlinkSQLLexer_1.FlinkSQLLexer.DOT) {
            index += 1;
        }
        const candidates = core.collectCandidates(index);
        const identifiers = [];
        if (candidates.rules.size > 0) {
            if (candidates.rules.has(FlinkSQLParser_1.FlinkSQLParser.RULE_functionName)) {
                completionItems.push(...flinkdb_schema_json_1.default.functions.map(f => (0, protocol_translation_1.toCompletionItem)('built-in', f.name, f.name, 'built-in function', f.description)));
            }
        }
        for (const candidate of Array.from(candidates.tokens)) {
            const keyword = parsingValue.parser.vocabulary.getDisplayName(candidate[0]).replace(/'/g, '');
            completionItems.push({
                kind: lsp.CompletionItemKind.Keyword,
                label: keyword,
                insertText: keyword,
                insertTextFormat: lsp.InsertTextFormat.Snippet
            });
        }
        return { completionItems, identifiers };
    }
    parseStructure(document, options = { ignoreInsertSource: false, addCreateContext: false }) {
        const parsingValue = this._warehouse.getParsingValue(document);
        const listener = new structure_listener_1.FlinkStructureListener(document);
        tree_1.ParseTreeWalker.DEFAULT.walk(listener, parsingValue.tree);
        let nodes = listener.getNodes();
        let edges = listener.getEdges();
        if (options.ignoreInsertSource) {
            const tmp = (0, structure_listener_1.ignoreInsertSource)(nodes, edges);
            nodes = tmp.nodes;
            edges = tmp.edges;
        }
        if (options.addCreateContext) {
            const tmp = (0, structure_listener_1.addFromCreateContext)(nodes, edges);
            nodes = tmp.nodes;
            edges = tmp.edges;
        }
        return { nodes, edges };
    }
    generatePreviewScript(document, context, range) {
        const parsingValue = range
            ? this._warehouse.parseDocument(document, range)
            : this._warehouse.getParsingValue(document);
        if (parsingValue.errors.length) {
            console.error(`Document '${document.uri}' conatins syntax errors, preview script failed to generate.`);
            return document.getText();
        }
        const listener = new preview_listener_1.PreviewListener(parsingValue.origin, context);
        let previewScript = parsingValue.origin;
        try {
            tree_1.ParseTreeWalker.DEFAULT.walk(listener, parsingValue.tree);
            previewScript = listener.previewScript;
        }
        catch (e) {
            console.error(e);
        }
        return (0, preview_listener_1.appendCreateNonTemporaryTables)(previewScript, context);
    }
    extractSchemaContexts(document, range) {
        const parsingValue = range
            ? this._warehouse.parseDocument(document, range)
            : this._warehouse.getParsingValue(document);
        const listener = new schema_listener_1.SchemaListener();
        try {
            tree_1.ParseTreeWalker.DEFAULT.walk(listener, parsingValue.tree);
            return listener.getSchemaContexts();
        }
        catch (e) {
            console.error(e);
            return [];
        }
    }
    collectTokens(document, range) {
        const parsingValue = range
            ? this._warehouse.parseDocument(document, range)
            : this._warehouse.getParsingValue(document);
        if (parsingValue.tokenCollection) {
            return parsingValue.tokenCollection;
        }
        const listener = new tokens_collector_listener_1.TokensCollectorListener(document);
        tree_1.ParseTreeWalker.DEFAULT.walk(listener, parsingValue.tree);
        if (!range) {
            this._warehouse.setTokenCollection(document, {
                semanticTokens: listener.getSemanticTokens(),
                stringTokens: listener.getStringTokens()
            });
        }
        return { semanticTokens: listener.getSemanticTokens(), stringTokens: listener.getStringTokens() };
    }
    getLineage(document, options = {}) {
        const parsingValue = this._warehouse.getParsingValue(document);
        const visitor = new lineage_visitor_1.LineageVisitor(options);
        try {
            parsingValue.tree.accept(visitor);
            return visitor.getLineage();
        }
        catch (e) {
            console.error(`Language Server: Lineage visitor transversal failed.\n\n${e}`);
            return { nodes: [], edges: [] };
        }
    }
}
exports.LSPServer = LSPServer;
function isSemanticTokenReference(token1, token2) {
    return (token1.name.replace(/['"`]/g, '').localeCompare(token2.name.replace(/['"`]/g, '')) === 0 &&
        token1.type === token2.type);
}
exports.isSemanticTokenReference = isSemanticTokenReference;
//# sourceMappingURL=lsp-server.js.map