mod eval;
mod parse;

use crate::graph::PageStore;
use crate::parser::PageId;
use lazy_static::lazy_static;
use regex::Regex;

pub use parse::QueryExpr;

lazy_static! {
    /// Matches <div class="query-fallback"><code>...</code><div class="query-note">This query uses advanced features. View in Logseq for live results.</div></div> inline queries
    static ref QUERY_INLINE_RE: Regex = Regex::new(
        r"(?s)\{\{query\s+(.*?)\}\}"
    ).unwrap();

    /// Matches #+BEGIN_QUERY ... #+END_QUERY blocks
    static ref QUERY_BLOCK_RE: Regex = Regex::new(
        r"(?sm)^\s*#\+BEGIN_QUERY\s*\n(.*?)\s*#\+END_QUERY"
    ).unwrap();
}

/// Resolve all query blocks in markdown content, returning HTML with results.
pub fn resolve_queries(markdown: &str, store: &PageStore) -> String {
    // Fast path: skip if no query patterns
    if !markdown.contains("{{query") && !markdown.contains("#+BEGIN_QUERY") {
        return markdown.to_string();
    }

    let mut result = markdown.to_string();

    // Resolve inline <div class="query-fallback"><code>...</code><div class="query-note">This query uses advanced features. View in Logseq for live results.</div></div> blocks
    result = QUERY_INLINE_RE
        .replace_all(&result, |caps: &regex::Captures| {
            let query_text = caps[1].trim();
            match parse::parse_query(query_text) {
                Some(expr) => {
                    let page_ids = eval::evaluate(&expr, store);
                    render_query_results(query_text, &page_ids, store)
                }
                None => render_query_fallback(query_text),
            }
        })
        .to_string();

    // Resolve #+BEGIN_QUERY ... #+END_QUERY blocks
    result = QUERY_BLOCK_RE
        .replace_all(&result, |caps: &regex::Captures| {
            let query_body = caps[1].trim();
            render_query_fallback(query_body)
        })
        .to_string();

    result
}

/// Render query results as an HTML list of linked page titles.
fn render_query_results(query_text: &str, page_ids: &[PageId], store: &PageStore) -> String {
    if page_ids.is_empty() {
        return format!(
            "<div class=\"query-results\">\
             <div class=\"query-results-header\">Query: <code>{}</code></div>\
             <p><em>No results</em></p></div>",
            html_escape(query_text)
        );
    }

    let mut items = String::new();
    for id in page_ids {
        if let Some(page) = store.pages.get(id) {
            items.push_str(&format!(
                "<li><a href=\"/{}\" class=\"internal-link\">{}</a></li>\n",
                id, html_escape(&page.meta.title)
            ));
        }
    }

    format!(
        "<div class=\"query-results\">\
         <div class=\"query-results-header\">Query: <code>{}</code> ({} results)</div>\
         <ul>\n{}</ul></div>",
        html_escape(query_text),
        page_ids.len(),
        items
    )
}

/// Render an unrecognized query as a styled fallback.
fn render_query_fallback(query_text: &str) -> String {
    format!(
        "<div class=\"query-fallback\">\
         <code>{}</code>\
         <div class=\"query-note\">This query uses advanced features. View in Logseq for live results.</div>\
         </div>",
        html_escape(query_text)
    )
}

fn html_escape(s: &str) -> String {
    s.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
}

Dimensions

trident/src/diagnostic/mod.rs
trident/src/ir/mod.rs
trident/src/deploy/mod.rs
trident/src/syntax/mod.rs
trident/src/api/mod.rs
nebu/rs/extension/mod.rs
optica/src/render/mod.rs
trident/src/config/mod.rs
trident/src/field/mod.rs
trident/src/cli/mod.rs
optica/src/parser/mod.rs
trident/src/neural/mod.rs
trident/src/cost/mod.rs
trident/src/typecheck/mod.rs
optica/src/server/mod.rs
trident/src/package/mod.rs
optica/src/scanner/mod.rs
optica/src/output/mod.rs
trident/src/verify/mod.rs
optica/src/graph/mod.rs
trident/src/ast/mod.rs
trident/src/lsp/mod.rs
trident/src/runtime/mod.rs
trident/src/gpu/mod.rs
trident/src/lsp/semantic/mod.rs
trident/src/verify/equiv/mod.rs
trident/src/package/hash/mod.rs
trident/src/neural/training/mod.rs
trident/src/verify/synthesize/mod.rs
trident/src/ir/tir/mod.rs
rs/macros/src/addressed/mod.rs
trident/src/package/registry/mod.rs
rs/rsc/src/lints/mod.rs
trident/src/verify/report/mod.rs
trident/src/config/resolve/mod.rs
trident/src/verify/solve/mod.rs
rs/macros/src/registers/mod.rs
trident/src/verify/smt/mod.rs
rs/macros/src/cell/mod.rs
rs/core/src/fixed_point/mod.rs
trident/src/neural/data/mod.rs
rs/core/src/bounded/mod.rs
trident/src/lsp/util/mod.rs
trident/src/typecheck/tests/mod.rs
trident/src/neural/model/mod.rs
trident/src/cost/stack_verifier/mod.rs
trident/src/syntax/grammar/mod.rs
trident/src/package/manifest/mod.rs
trident/src/syntax/parser/mod.rs
trident/src/ir/kir/mod.rs
trident/src/neural/inference/mod.rs
trident/src/syntax/lexer/mod.rs
trident/src/cost/model/mod.rs
trident/src/ir/lir/mod.rs
trident/src/syntax/format/mod.rs
trident/src/config/scaffold/mod.rs
trident/src/verify/sym/mod.rs
trident/src/api/tests/mod.rs
trident/src/package/store/mod.rs
trident/src/ir/tree/mod.rs
trident/src/ir/kir/lower/mod.rs
trident/src/ir/lir/lower/mod.rs
trident/src/ir/tir/lower/mod.rs
trident/src/ir/tir/builder/mod.rs
trident/src/ir/tir/neural/mod.rs
trident/src/neural/data/tir_graph/mod.rs
trident/src/syntax/parser/tests/mod.rs
cw-cyber/packages/cyber-std/src/tokenfactory/mod.rs
trident/src/ir/tree/lower/mod.rs
trident/src/ir/tir/stack/mod.rs
cw-cyber/contracts/cybernet/src/tests/mod.rs
trident/src/ir/tir/optimize/mod.rs

Local Graph