htmx-lsp-server 0.1.0

lsp server for htmx-lsp
Documentation
use log::debug;
use lsp_types::TextDocumentPositionParams;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};

use crate::tree_sitter::Position;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HxCompletion {
    pub name: String,
    pub desc: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HxHover {
    pub name: String,
    pub desc: String,
}

impl From<&(&str, &str)> for HxCompletion {
    fn from((name, desc): &(&str, &str)) -> Self {
        Self {
            name: name.to_string(),
            desc: desc.to_string(),
        }
    }
}

impl TryFrom<&(PathBuf, String)> for HxCompletion {
    type Error = anyhow::Error;

    fn try_from((path, desc): &(PathBuf, String)) -> Result<Self, Self::Error> {
        match path.to_str() {
            None | Some("") => anyhow::bail!("Invalid path"),
            Some(name) => Ok(Self {
                name: name.to_string(),
                desc: desc.to_string(),
            }),
        }
    }
}

pub fn hx_completion(text_params: TextDocumentPositionParams) -> Option<Vec<HxCompletion>> {
    let result = crate::tree_sitter::get_position_from_lsp_completion(text_params.clone())?;

    debug!("result: {:?} params: {:?}", result, text_params);

    match result {
        Position::AttributeName(name) => {
            if name.starts_with("hx-") {
                return HX_TAGS.get().cloned();
            }
        }

        Position::AttributeValue { name, .. } => {
            let values = HX_ATTRIBUTE_VALUES.get()?.get(&name)?;
            return Some(values.clone());
        }
    };

    None
}

pub fn hx_hover(text_params: TextDocumentPositionParams) -> Option<HxCompletion> {
    let result = crate::tree_sitter::get_position_from_lsp_completion(text_params.clone())?;
    debug!("handle_hover result: {:?}", result);

    match result {
        Position::AttributeName(name) => HX_TAGS
            .get()
            .expect("Why it can't get HX_TAGS?")
            .iter()
            .find(|x| x.name == name)
            .cloned(),

        Position::AttributeValue { name, .. } => HX_TAGS
            .get()
            .expect("Why it can't get HX_TAGS?")
            .iter()
            .find(|x| x.name == name)
            .cloned(),
    }
}

pub static HX_TAGS: OnceLock<Vec<HxCompletion>> = OnceLock::new();
pub static HX_ATTRIBUTE_VALUES: OnceLock<HashMap<String, Vec<HxCompletion>>> = OnceLock::new();

fn to_hx_completion(values: Vec<(&str, &str)>) -> Vec<HxCompletion> {
    return values.iter().filter_map(|x| x.try_into().ok()).collect();
}

pub fn init_hx_tags() {
    _ = HX_ATTRIBUTE_VALUES.set(maplit::hashmap! {
        String::from("hx-swap") => to_hx_completion(vec![
            ("innerHTML", include_str!("./hx-swap/innerHTML.md")),
            ("outerHTML", include_str!("./hx-swap/outerHTML.md")),
            ("afterbegin", include_str!("./hx-swap/afterbegin.md")),
            ("afterend", include_str!("./hx-swap/afterend.md")),
            ("beforebegin", include_str!("./hx-swap/beforebegin.md")),
            ("beforeend", include_str!("./hx-swap/beforeend.md")),
            ("delete", include_str!("./hx-swap/delete.md")),
            ("none", include_str!("./hx-swap/none.md")),
        ]),

        String::from("hx-target") => to_hx_completion(vec![
            ("closest", include_str!("./hx-target/closest.md")),
            ("find", include_str!("./hx-target/find.md")),
            ("next", include_str!("./hx-target/next.md")),
            ("prev", include_str!("./hx-target/prev.md")),
            ("this", include_str!("./hx-target/this.md")),
        ]),

        String::from("hx-boost") => to_hx_completion(vec![
            ("true", include_str!("./hx-boost/true.md")),
            ("false", include_str!("./hx-boost/false.md")),
        ]),

        String::from("hx-trigger") => to_hx_completion(vec![
            ("click", include_str!("./hx-trigger/click.md")),
            ("once", include_str!("./hx-trigger/once.md")),
            ("changed", include_str!("./hx-trigger/changed.md")),
            ("delay:", include_str!("./hx-trigger/delay.md")),
            ("throttle:", include_str!("./hx-trigger/throttle.md")),
            ("from:", include_str!("./hx-trigger/from.md")),
            ("target:", include_str!("./hx-trigger/target.md")),
            ("consume", include_str!("./hx-trigger/consume.md")),
            ("queue:", include_str!("./hx-trigger/queue.md")),
            ("keyup", include_str!("./hx-trigger/keyup.md")),
            ("load", include_str!("./hx-trigger/load.md")),
            ("revealed", include_str!("./hx-trigger/revealed.md")),
            ("intersect", include_str!("./hx-trigger/intersect.md")),
            ("every", include_str!("./hx-trigger/every.md")),
        ]),

        String::from("hx-ext") => to_hx_completion(vec![
            ("ajax-header", include_str!("./hx-ext/ajax-header.md")),
            ("alpine-morph", include_str!("./hx-ext/alpine-morph.md")),
            ("class-tools", include_str!("./hx-ext/class-tools.md")),
            ("client-side-templates", include_str!("./hx-ext/client-side-templates.md")),
            ("debug", include_str!("./hx-ext/debug.md")),
            ("disable-element", include_str!("./hx-ext/disable-element.md")),
            ("event-header", include_str!("./hx-ext/event-header.md")),
            ("head-support", include_str!("./hx-ext/head-support.md")),
            ("include-vals", include_str!("./hx-ext/include-vals.md")),
            ("json-enc", include_str!("./hx-ext/json-enc.md")),
            ("morph", include_str!("./hx-ext/morph.md")),
            ("loading-states", include_str!("./hx-ext/loading-states.md")),
            ("method-override", include_str!("./hx-ext/method-override.md")),
            ("morphdom-swap", include_str!("./hx-ext/morphdom-swap.md")),
            ("multi-swap", include_str!("./hx-ext/multi-swap.md")),
            ("path-deps", include_str!("./hx-ext/path-deps.md")),
            ("preload", include_str!("./hx-ext/preload.md")),
            ("remove-me", include_str!("./hx-ext/remove-me.md")),
            ("response-targets", include_str!("./hx-ext/response-targets.md")),
            ("restored", include_str!("./hx-ext/restored.md")),
            ("sse", include_str!("./hx-ext/sse.md")),
            ("ws", include_str!("./hx-ext/ws.md")),
        ]),

        String::from("hx-push-url") => to_hx_completion(vec![
            ("true", include_str!("./hx-push-url/true.md")),
            ("false", include_str!("./hx-push-url/false.md")),
        ]),

        String::from("hx-swap-oob") => to_hx_completion(vec![
            ("true", include_str!("./hx-swap-oob/true.md")),
            ("innerHTML", include_str!("./hx-swap/innerHTML.md")),
            ("outerHTML", include_str!("./hx-swap/outerHTML.md")),
            ("afterbegin", include_str!("./hx-swap/afterbegin.md")),
            ("afterend", include_str!("./hx-swap/afterend.md")),
            ("beforebegin", include_str!("./hx-swap/beforebegin.md")),
            ("beforeend", include_str!("./hx-swap/beforeend.md")),
            ("delete", include_str!("./hx-swap/delete.md")),
            ("none", include_str!("./hx-swap/none.md")),
        ]),

        String::from("hx-history") => to_hx_completion(vec![
            ("false", include_str!("./hx-history/false.md")),
        ]),

        String::from("hx-params") => to_hx_completion(vec![
            ("*", include_str!("./hx-params/star.md")),
            ("none", include_str!("./hx-params/none.md")),
            ("not", include_str!("./hx-params/not.md")),
        ]),

        String::from("hx-replace-url") => to_hx_completion(vec![
            ("true", include_str!("./hx-replace-url/true.md")),
            ("false", include_str!("./hx-replace-url/false.md")),
        ]),

        String::from("hx-sync") => to_hx_completion(vec![
            ("drop", include_str!("./hx-sync/drop.md")),
            ("abort", include_str!("./hx-sync/abort.md")),
            ("replace", include_str!("./hx-sync/replace.md")),
            ("queue", include_str!("./hx-sync/queue.md")),
        ])
    });

    _ = HX_TAGS.set(to_hx_completion(vec![
        ("hx-boost", include_str!("./attributes/hx-boost.md")),
        ("hx-delete", include_str!("./attributes/hx-delete.md")),
        ("hx-get", include_str!("./attributes/hx-get.md")),
        ("hx-include", include_str!("./attributes/hx-include.md")),
        ("hx-patch", include_str!("./attributes/hx-patch.md")),
        ("hx-post", include_str!("./attributes/hx-post.md")),
        ("hx-put", include_str!("./attributes/hx-put.md")),
        ("hx-swap", include_str!("./attributes/hx-swap.md")),
        ("hx-target", include_str!("./attributes/hx-target.md")),
        ("hx-trigger", include_str!("./attributes/hx-trigger.md")),
        ("hx-vals", include_str!("./attributes/hx-vals.md")),
        ("hx-push-url", include_str!("./attributes/hx-push-url.md")),
        ("hx-select", include_str!("./attributes/hx-select.md")),
        ("hx-ext", include_str!("./attributes/hx-ext.md")),
        ("hx-on", include_str!("./attributes/hx-on.md")),
        (
            "hx-select-oob",
            include_str!("./attributes/hx-select-oob.md"),
        ),
        ("hx-swap-oob", include_str!("./attributes/hx-swap-oob.md")),
        ("hx-confirm", include_str!("./attributes/hx-confirm.md")),
        ("hx-disable", include_str!("./attributes/hx-disable.md")),
        ("hx-encoding", include_str!("./attributes/hx-encoding.md")),
        ("hx-headers", include_str!("./attributes/hx-headers.md")),
        ("hx-history", include_str!("./attributes/hx-history.md")),
        (
            "hx-history-elt",
            include_str!("./attributes/hx-history-elt.md"),
        ),
        ("hx-indicator", include_str!("./attributes/hx-indicator.md")),
        ("hx-params", include_str!("./attributes/hx-params.md")),
        ("hx-preserve", include_str!("./attributes/hx-preserve.md")),
        ("hx-prompt", include_str!("./attributes/hx-prompt.md")),
        (
            "hx-replace-url",
            include_str!("./attributes/hx-replace-url.md"),
        ),
        ("hx-request", include_str!("./attributes/hx-request.md")),
        ("hx-sync", include_str!("./attributes/hx-sync.md")),
        ("hx-validate", include_str!("./attributes/hx-validate.md")),
    ]));
}