dts-lsp 0.1.3

Language Server for Device Tree Source files
use crate::utils::convert_range;
use crate::utils::Symbol;
use crate::FileDepot;
use crate::{info, log_message};
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Mutex;
use tower_lsp::lsp_types::{MessageType, Range, Url};

#[derive(Clone, Eq, Hash, PartialEq)]
struct Label {
    uri: Url,
    name: String,
}

#[derive(Clone)]
struct Data {
    label_to_symbol: HashMap<Label, Range>,
    fd: FileDepot,
}

impl Data {
    fn new(fd: &FileDepot) -> Data {
        Data {
            label_to_symbol: HashMap::new(),
            fd: fd.clone(),
        }
    }
    fn add_label(&mut self, label: &str, uri: &Url, range: tree_sitter::Range) {
        self.label_to_symbol.insert(
            Label {
                uri: uri.clone(),
                name: label.to_string(),
            },
            convert_range(&range),
        );
    }

    #[cfg(test)]
    fn size(&self) -> usize {
        self.label_to_symbol.keys().count()
    }

    async fn find_label(&self, uri: &Url, label: &str) -> Vec<Symbol> {
        let mut visited = HashSet::new();
        let mut to_visit = Vec::new();
        let mut res = Vec::new();

        to_visit.push(uri.clone());

        while let Some(uri) = to_visit.pop() {
            info!("processing {uri}");

            if let Some(range) = self.label_to_symbol.get(&Label {
                name: label.to_string(),
                uri: uri.clone(),
            }) {
                res.push(Symbol::new(uri.clone(), *range));
            }

            for f in self.fd.get_neighbours(&uri).await {
                if !visited.contains(&f) {
                    to_visit.push(f);
                }
            }

            visited.insert(uri);
        }

        res
    }

    fn invalidate(&mut self, uri: &Url) {
        let mut v = Vec::new();

        for k in self.label_to_symbol.keys() {
            if k.uri == *uri {
                v.push(k.clone());
            }
        }

        for label in v {
            self.label_to_symbol.remove(&label);
        }
    }

    async fn dump(&self) {
        info!("====== (labels) ======");
        for k in self.label_to_symbol.keys() {
            info!("url: {}: {}", k.uri, k.name);
        }
        info!("======================");
    }
}

pub struct LabelsDepot {
    data: Mutex<Data>,
}

impl LabelsDepot {
    pub fn new(fd: &FileDepot) -> LabelsDepot {
        LabelsDepot {
            data: Mutex::new(Data::new(fd)),
        }
    }

    pub async fn add_label(&self, label: &str, uri: &Url, range: tree_sitter::Range) {
        info!("LabelsDepot::add_label()");
        let mut data = self.data.lock().unwrap();
        data.add_label(label, uri, range);
    }

    pub async fn find_label(&self, uri: &Url, label: &str) -> Vec<Symbol> {
        info!("LabelsDepot::find_label()");
        {
            let data = self.data.lock().unwrap();
            data.clone()
        }
        .find_label(uri, label)
        .await
    }

    pub async fn dump(&self) {
        info!("LabelsDepot::dump()");
        {
            let lock = self.data.lock().unwrap();
            lock.clone()
        }
        .dump()
        .await;
    }

    pub async fn invalidate(&self, uri: &Url) {
        info!("LabelsDepot::invalidate()");
        let mut data = self.data.lock().unwrap();
        data.invalidate(uri);
    }

    #[cfg(test)]
    pub async fn size(&self) -> usize {
        let data = self.data.lock().unwrap();
        data.size()
    }
}