From 79a0c4cb4f53c174960d429a06e69e3329e9fecd Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 22 Aug 2025 14:40:32 -0500 Subject: [PATCH] wip --- crates/djls-server/src/workspace/document.rs | 132 ++----------------- crates/djls-server/src/workspace/store.rs | 73 ++++------ 2 files changed, 37 insertions(+), 168 deletions(-) diff --git a/crates/djls-server/src/workspace/document.rs b/crates/djls-server/src/workspace/document.rs index 8892159..c671c7e 100644 --- a/crates/djls-server/src/workspace/document.rs +++ b/crates/djls-server/src/workspace/document.rs @@ -1,136 +1,29 @@ -use salsa::Database; use tower_lsp_server::lsp_types::DidOpenTextDocumentParams; use tower_lsp_server::lsp_types::Position; -use tower_lsp_server::lsp_types::Range; -use tower_lsp_server::lsp_types::TextDocumentContentChangeEvent; -#[salsa::input(debug)] +#[derive(Debug, Clone)] pub struct TextDocument { - #[returns(ref)] + #[allow(dead_code)] pub uri: String, - #[returns(ref)] - pub contents: String, - #[returns(ref)] - pub index: LineIndex, pub version: i32, pub language_id: LanguageId, } impl TextDocument { - pub fn from_did_open_params(db: &dyn Database, params: &DidOpenTextDocumentParams) -> Self { + pub fn new(uri: String, version: i32, language_id: LanguageId) -> Self { + Self { + uri, + version, + language_id, + } + } + + pub fn from_did_open_params(params: &DidOpenTextDocumentParams) -> Self { let uri = params.text_document.uri.to_string(); - let contents = params.text_document.text.clone(); let version = params.text_document.version; let language_id = LanguageId::from(params.text_document.language_id.as_str()); - let index = LineIndex::new(&contents); - TextDocument::new(db, uri, contents, index, version, language_id) - } - - pub fn with_changes( - self, - db: &dyn Database, - changes: &[TextDocumentContentChangeEvent], - new_version: i32, - ) -> Self { - let mut new_contents = self.contents(db).to_string(); - - for change in changes { - if let Some(range) = change.range { - let index = LineIndex::new(&new_contents); - - if let (Some(start_offset), Some(end_offset)) = ( - index.offset(range.start).map(|o| o as usize), - index.offset(range.end).map(|o| o as usize), - ) { - let mut updated_content = String::with_capacity( - new_contents.len() - (end_offset - start_offset) + change.text.len(), - ); - - updated_content.push_str(&new_contents[..start_offset]); - updated_content.push_str(&change.text); - updated_content.push_str(&new_contents[end_offset..]); - - new_contents = updated_content; - } - } else { - // Full document update - new_contents.clone_from(&change.text); - } - } - - let index = LineIndex::new(&new_contents); - TextDocument::new( - db, - self.uri(db).to_string(), - new_contents, - index, - new_version, - self.language_id(db), - ) - } - - #[allow(dead_code)] - pub fn get_text(self, db: &dyn Database) -> String { - self.contents(db).to_string() - } - - #[allow(dead_code)] - pub fn get_text_range(self, db: &dyn Database, range: Range) -> Option { - let index = self.index(db); - let start = index.offset(range.start)? as usize; - let end = index.offset(range.end)? as usize; - let contents = self.contents(db); - Some(contents[start..end].to_string()) - } - - pub fn get_line(self, db: &dyn Database, line: u32) -> Option { - let index = self.index(db); - let start = index.line_starts.get(line as usize)?; - let end = index - .line_starts - .get(line as usize + 1) - .copied() - .unwrap_or(index.length); - - let contents = self.contents(db); - Some(contents[*start as usize..end as usize].to_string()) - } - - #[allow(dead_code)] - pub fn line_count(self, db: &dyn Database) -> usize { - self.index(db).line_starts.len() - } - - pub fn get_template_tag_context( - self, - db: &dyn Database, - position: Position, - ) -> Option { - let line = self.get_line(db, position.line)?; - let char_pos: usize = position.character.try_into().ok()?; - let prefix = &line[..char_pos]; - let rest_of_line = &line[char_pos..]; - let rest_trimmed = rest_of_line.trim_start(); - - prefix.rfind("{%").map(|tag_start| { - // Check if we're immediately after {% with no space - let needs_leading_space = prefix.ends_with("{%"); - - let closing_brace = if rest_trimmed.starts_with("%}") { - ClosingBrace::FullClose - } else if rest_trimmed.starts_with('}') { - ClosingBrace::PartialClose - } else { - ClosingBrace::None - }; - - TemplateTagContext { - partial_tag: prefix[tag_start + 2..].trim().to_string(), - closing_brace, - needs_leading_space, - } - }) + TextDocument::new(uri, version, language_id) } } @@ -224,3 +117,4 @@ pub struct TemplateTagContext { pub closing_brace: ClosingBrace, pub needs_leading_space: bool, } + diff --git a/crates/djls-server/src/workspace/store.rs b/crates/djls-server/src/workspace/store.rs index bec6515..22b23de 100644 --- a/crates/djls-server/src/workspace/store.rs +++ b/crates/djls-server/src/workspace/store.rs @@ -59,7 +59,7 @@ impl Store { false } } - pub fn handle_did_open(&mut self, db: &dyn Database, params: &DidOpenTextDocumentParams) { + pub fn handle_did_open(&mut self, _db: &dyn Database, params: &DidOpenTextDocumentParams) { // Only process files within the workspace if !self.is_workspace_file(¶ms.text_document.uri) { // Silently ignore files outside workspace @@ -85,7 +85,7 @@ impl Store { } } - let document = TextDocument::from_did_open_params(db, params); + let document = TextDocument::from_did_open_params(params); self.add_document(document, uri.clone()); self.versions.insert(uri, version); @@ -93,7 +93,7 @@ impl Store { pub fn handle_did_change( &mut self, - db: &dyn Database, + _db: &dyn Database, params: &DidChangeTextDocumentParams, ) -> Result<()> { // Only process files within the workspace @@ -110,21 +110,9 @@ impl Store { if let Ok(relative_path) = absolute_path.strip_prefix(&self.root_path) { let relative_path_str = relative_path.to_string_lossy(); - // Read current content from VFS - let current_content = match self.vfs.read_to_string(&relative_path_str) { - Ok(content) => content, - Err(e) => { - eprintln!("Warning: Failed to read from VFS, falling back to TextDocument: {}", e); - // Fallback to existing TextDocument approach - let document = self - .get_document(&uri) - .ok_or_else(|| anyhow!("Document not found: {}", uri))?; - let new_document = document.with_changes(db, ¶ms.content_changes, version); - self.documents.insert(uri.clone(), new_document); - self.versions.insert(uri, version); - return Ok(()); - } - }; + // Read current content from VFS (single source of truth) + let current_content = self.vfs.read_to_string(&relative_path_str) + .map_err(|e| anyhow!("Failed to read from VFS: {}", e))?; // Apply text changes to VFS content let updated_content = self.apply_changes_to_content(current_content, ¶ms.content_changes)?; @@ -134,31 +122,18 @@ impl Store { eprintln!("Warning: Failed to write to VFS: {}", e); } - // Create new TextDocument with updated content for backward compatibility - let index = LineIndex::new(&updated_content); - let language_id = self.get_document(&uri) - .map(|doc| doc.language_id(db)) - .unwrap_or(LanguageId::HtmlDjango); - - let new_document = TextDocument::new(db, uri.clone(), updated_content.clone(), index, version, language_id); - self.documents.insert(uri.clone(), new_document); + // Update document metadata (just version) + if let Some(document) = self.documents.get_mut(&uri) { + document.version = version; + } self.versions.insert(uri, version); return Ok(()); } } - // Fallback to original implementation if path conversion fails - let document = self - .get_document(&uri) - .ok_or_else(|| anyhow!("Document not found: {}", uri))?; - - let new_document = document.with_changes(db, ¶ms.content_changes, version); - - self.documents.insert(uri.clone(), new_document); - self.versions.insert(uri, version); - - Ok(()) + // If path conversion fails, this is an error since we need VFS + Err(anyhow!("Document not in workspace or path conversion failed: {}", uri)) } pub fn handle_did_close(&mut self, params: &DidCloseTextDocumentParams) { @@ -266,12 +241,12 @@ impl Store { #[allow(dead_code)] pub fn get_documents_by_language<'db>( &'db self, - db: &'db dyn Database, + _db: &'db dyn Database, language_id: LanguageId, ) -> impl Iterator + 'db { self.documents .values() - .filter(move |doc| doc.language_id(db) == language_id) + .filter(move |doc| doc.language_id == language_id) } #[allow(dead_code)] @@ -286,14 +261,14 @@ impl Store { pub fn get_completions( &self, - db: &dyn Database, + _db: &dyn Database, uri: &str, position: Position, tags: &TemplateTags, ) -> Option { let document = self.get_document(uri)?; - if document.language_id(db) != LanguageId::HtmlDjango { + if document.language_id != LanguageId::HtmlDjango { return None; } @@ -307,21 +282,21 @@ impl Store { match self.vfs.read_to_string(&relative_path_str) { Ok(vfs_content) => vfs_content, Err(_) => { - // Fallback to document content if VFS read fails - document.contents(db).to_string() + // Return None if we can't read from VFS + return None; } } } else { - // Path not within workspace, use document content - document.contents(db).to_string() + // Path not within workspace + return None; } } else { - // URI parsing failed, use document content - document.contents(db).to_string() + // URI parsing failed + return None; } } else { - // URI parsing failed, use document content - document.contents(db).to_string() + // URI parsing failed + return None; }; // Use standalone analyzer instead of salsa-tracked method