shuck_server/session/
index.rs1#![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}