Skip to main content

shuck_server/session/
index.rs

1#![allow(dead_code)]
2
3use std::path::{Path, PathBuf};
4use std::sync::Arc;
5
6use anyhow::anyhow;
7use lsp_types::{FileEvent, Url};
8use rustc_hash::FxHashMap;
9
10use crate::edit::{DocumentKey, DocumentVersion};
11use crate::session::Client;
12use crate::session::settings::{ClientSettings, GlobalClientSettings, ShuckSettings};
13use crate::workspace::Workspaces;
14use crate::{PositionEncoding, TextDocument};
15
16#[derive(Default)]
17pub(crate) struct Index {
18    documents: FxHashMap<Url, Arc<TextDocument>>,
19    workspace_roots: Vec<PathBuf>,
20    client_settings: Arc<ClientSettings>,
21}
22
23#[derive(Clone)]
24pub enum DocumentQuery {
25    Text {
26        file_url: Url,
27        document: Arc<TextDocument>,
28        settings: Arc<ShuckSettings>,
29    },
30}
31
32impl Index {
33    pub(super) fn new(
34        workspaces: &Workspaces,
35        global: &GlobalClientSettings,
36        _client: &Client,
37    ) -> crate::Result<Self> {
38        let workspace_roots = workspaces
39            .iter()
40            .filter_map(|workspace| workspace.url().to_file_path().ok())
41            .collect();
42        Ok(Self {
43            documents: FxHashMap::default(),
44            workspace_roots,
45            client_settings: global.to_settings_arc(),
46        })
47    }
48
49    pub(super) fn key_from_url(&self, url: Url) -> DocumentKey {
50        DocumentKey::Text(url)
51    }
52
53    pub(super) fn make_document_ref(&self, key: DocumentKey) -> Option<DocumentQuery> {
54        let DocumentKey::Text(url) = key;
55        let document = self.documents.get(&url)?.clone();
56        Some(DocumentQuery::Text {
57            file_url: url,
58            document,
59            settings: Arc::new(ShuckSettings),
60        })
61    }
62
63    pub(super) fn client_settings(&self, key: &DocumentKey) -> Option<Arc<ClientSettings>> {
64        let DocumentKey::Text(url) = key;
65        self.documents
66            .contains_key(url)
67            .then(|| self.client_settings.clone())
68    }
69
70    pub(super) fn update_text_document(
71        &mut self,
72        key: &DocumentKey,
73        content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
74        new_version: DocumentVersion,
75        encoding: PositionEncoding,
76    ) -> crate::Result<()> {
77        let DocumentKey::Text(url) = key;
78        let Some(document) = self.documents.get_mut(url) else {
79            return Err(anyhow!(
80                "text document URI does not point to an open document"
81            ));
82        };
83
84        std::sync::Arc::make_mut(document).apply_changes(content_changes, new_version, encoding);
85        Ok(())
86    }
87
88    pub(super) fn open_text_document(&mut self, url: Url, document: TextDocument) {
89        self.documents.insert(url, Arc::new(document));
90    }
91
92    pub(super) fn close_document(&mut self, key: &DocumentKey) -> crate::Result<()> {
93        let DocumentKey::Text(url) = key;
94        self.documents
95            .remove(url)
96            .map(|_| ())
97            .ok_or_else(|| anyhow!("document is not open: {url}"))
98    }
99
100    pub(super) fn reload_settings(&mut self, _changes: &[FileEvent], _client: &Client) {}
101
102    pub(super) fn open_workspace_folder(
103        &mut self,
104        url: Url,
105        _global: &GlobalClientSettings,
106        _client: &Client,
107    ) -> crate::Result<()> {
108        let path = url
109            .to_file_path()
110            .map_err(|()| anyhow!("failed to convert workspace URL to file path: {url}"))?;
111        if !self.workspace_roots.contains(&path) {
112            self.workspace_roots.push(path);
113        }
114        Ok(())
115    }
116
117    pub(super) fn close_workspace_folder(&mut self, workspace_url: &Url) -> crate::Result<()> {
118        let path = workspace_url.to_file_path().map_err(|()| {
119            anyhow!("failed to convert workspace URL to file path: {workspace_url}")
120        })?;
121        self.workspace_roots.retain(|root| root != &path);
122        self.documents.retain(|url, _| {
123            url.to_file_path()
124                .map(|file_path| !file_path.starts_with(&path))
125                .unwrap_or(true)
126        });
127        Ok(())
128    }
129
130    pub(super) fn config_file_paths(&self) -> impl Iterator<Item = &Path> {
131        std::iter::empty()
132    }
133}
134
135impl DocumentQuery {
136    pub(crate) fn file_url(&self) -> &Url {
137        match self {
138            Self::Text { file_url, .. } => file_url,
139        }
140    }
141
142    pub(crate) fn document(&self) -> &Arc<TextDocument> {
143        match self {
144            Self::Text { document, .. } => document,
145        }
146    }
147
148    pub(crate) fn settings(&self) -> &ShuckSettings {
149        match self {
150            Self::Text { settings, .. } => settings,
151        }
152    }
153}