mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-09-14 14:25:36 +00:00
weeeee
This commit is contained in:
parent
361d7e2598
commit
00fef522ad
5 changed files with 44 additions and 47 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -518,6 +518,7 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-lsp-server",
|
"tower-lsp-server",
|
||||||
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -216,7 +216,7 @@ impl LanguageServer for DjangoLanguageServer {
|
||||||
language_id,
|
language_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
session.open_document(url, document);
|
session.open_document(&url, document);
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ use djls_workspace::{
|
||||||
db::{Database, SourceFile},
|
db::{Database, SourceFile},
|
||||||
paths, Buffers, FileSystem, OsFileSystem, TextDocument, WorkspaceFileSystem,
|
paths, Buffers, FileSystem, OsFileSystem, TextDocument, WorkspaceFileSystem,
|
||||||
};
|
};
|
||||||
use salsa::{Setter, StorageHandle};
|
use salsa::StorageHandle;
|
||||||
use tower_lsp_server::lsp_types;
|
use tower_lsp_server::lsp_types;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -286,13 +286,13 @@ impl Session {
|
||||||
/// This method coordinates both layers:
|
/// This method coordinates both layers:
|
||||||
/// - Layer 1: Stores the document content in buffers
|
/// - Layer 1: Stores the document content in buffers
|
||||||
/// - Layer 2: Creates the SourceFile in Salsa (if path is resolvable)
|
/// - Layer 2: Creates the SourceFile in Salsa (if path is resolvable)
|
||||||
pub fn open_document(&mut self, url: Url, document: TextDocument) {
|
pub fn open_document(&mut self, url: &Url, document: TextDocument) {
|
||||||
tracing::debug!("Opening document: {}", url);
|
tracing::debug!("Opening document: {}", url);
|
||||||
|
|
||||||
// Layer 1: Set buffer
|
// Layer 1: Set buffer
|
||||||
self.buffers.open(url.clone(), document);
|
self.buffers.open(url.clone(), document);
|
||||||
|
|
||||||
// Layer 2: Create file and bump revision if it already exists
|
// Layer 2: Create file and touch if it already exists
|
||||||
// This is crucial: if the file was already read from disk, we need to
|
// This is crucial: if the file was already read from disk, we need to
|
||||||
// invalidate Salsa's cache so it re-reads through the buffer system
|
// invalidate Salsa's cache so it re-reads through the buffer system
|
||||||
if let Some(path) = paths::url_to_path(&url) {
|
if let Some(path) = paths::url_to_path(&url) {
|
||||||
|
@ -302,16 +302,8 @@ impl Session {
|
||||||
let file = db.get_or_create_file(path.clone());
|
let file = db.get_or_create_file(path.clone());
|
||||||
|
|
||||||
if already_exists {
|
if already_exists {
|
||||||
// File was already read - bump revision to invalidate cache
|
// File was already read - touch to invalidate cache
|
||||||
let current_rev = file.revision(db);
|
db.touch_file(&path);
|
||||||
let new_rev = current_rev + 1;
|
|
||||||
file.set_revision(db).to(new_rev);
|
|
||||||
tracing::debug!(
|
|
||||||
"Bumped revision for {} on open: {} -> {}",
|
|
||||||
path.display(),
|
|
||||||
current_rev,
|
|
||||||
new_rev
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// New file - starts at revision 0
|
// New file - starts at revision 0
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
|
@ -336,9 +328,9 @@ impl Session {
|
||||||
// Layer 1: Update buffer
|
// Layer 1: Update buffer
|
||||||
self.buffers.update(url.clone(), document);
|
self.buffers.update(url.clone(), document);
|
||||||
|
|
||||||
// Layer 2: Bump revision to trigger invalidation
|
// Layer 2: Touch file to trigger invalidation
|
||||||
if let Some(path) = paths::url_to_path(url) {
|
if let Some(path) = paths::url_to_path(url) {
|
||||||
self.notify_file_changed(&path);
|
self.with_db_mut(|db| db.touch_file(&path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,43 +375,15 @@ impl Session {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layer 2: Bump revision to trigger re-read from disk
|
// Layer 2: Touch file to trigger re-read from disk
|
||||||
// We keep the file alive for potential re-opening
|
// We keep the file alive for potential re-opening
|
||||||
if let Some(path) = paths::url_to_path(url) {
|
if let Some(path) = paths::url_to_path(url) {
|
||||||
self.notify_file_changed(&path);
|
self.with_db_mut(|db| db.touch_file(&path));
|
||||||
}
|
}
|
||||||
|
|
||||||
removed
|
removed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal: Notify that a file's content has changed.
|
|
||||||
///
|
|
||||||
/// This bumps the file's revision number in Salsa, which triggers
|
|
||||||
/// invalidation of any queries that depend on the file's content.
|
|
||||||
fn notify_file_changed(&mut self, path: &Path) {
|
|
||||||
self.with_db_mut(|db| {
|
|
||||||
// Only bump revision if file is already being tracked
|
|
||||||
// We don't create files just for notifications
|
|
||||||
if db.has_file(path) {
|
|
||||||
let file = db.get_or_create_file(path.to_path_buf());
|
|
||||||
let current_rev = file.revision(db);
|
|
||||||
let new_rev = current_rev + 1;
|
|
||||||
file.set_revision(db).to(new_rev);
|
|
||||||
tracing::debug!(
|
|
||||||
"Bumped revision for {}: {} -> {}",
|
|
||||||
path.display(),
|
|
||||||
current_rev,
|
|
||||||
new_rev
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
tracing::debug!(
|
|
||||||
"File {} not tracked, skipping revision bump",
|
|
||||||
path.display()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== Safe Query API =====
|
// ===== Safe Query API =====
|
||||||
// These methods encapsulate all Salsa interactions, preventing the
|
// These methods encapsulate all Salsa interactions, preventing the
|
||||||
// "mixed database instance" bug by never exposing SourceFile or Database.
|
// "mixed database instance" bug by never exposing SourceFile or Database.
|
||||||
|
@ -503,7 +467,7 @@ mod tests {
|
||||||
1,
|
1,
|
||||||
LanguageId::Other,
|
LanguageId::Other,
|
||||||
);
|
);
|
||||||
session.open_document(url.clone(), document);
|
session.open_document(&url, document);
|
||||||
|
|
||||||
// Try to read content - this might be where it hangs
|
// Try to read content - this might be where it hangs
|
||||||
println!("**[test]** try to read content - this might be where it hangs");
|
println!("**[test]** try to read content - this might be where it hangs");
|
||||||
|
|
|
@ -15,6 +15,7 @@ percent-encoding = { workspace = true }
|
||||||
salsa = { workspace = true }
|
salsa = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tower-lsp-server = { workspace = true }
|
tower-lsp-server = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -32,6 +32,7 @@ use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
|
use salsa::Setter;
|
||||||
|
|
||||||
use crate::{FileKind, FileSystem};
|
use crate::{FileKind, FileSystem};
|
||||||
|
|
||||||
|
@ -160,6 +161,36 @@ impl Database {
|
||||||
self.files.contains_key(path)
|
self.files.contains_key(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Touch a file to mark it as modified, triggering re-evaluation of dependent queries.
|
||||||
|
///
|
||||||
|
/// Similar to Unix `touch`, this updates the file's revision number to signal
|
||||||
|
/// that cached query results depending on this file should be invalidated.
|
||||||
|
///
|
||||||
|
/// This is typically called when:
|
||||||
|
/// - A file is opened in the editor (if it was previously cached from disk)
|
||||||
|
/// - A file's content is modified
|
||||||
|
/// - A file's buffer is closed (reverting to disk content)
|
||||||
|
pub fn touch_file(&mut self, path: &Path) {
|
||||||
|
// Get the file if it exists
|
||||||
|
let Some(file_ref) = self.files.get(path) else {
|
||||||
|
tracing::debug!("File {} not tracked, skipping touch", path.display());
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let file = *file_ref;
|
||||||
|
drop(file_ref); // Explicitly drop to release the lock
|
||||||
|
|
||||||
|
let current_rev = file.revision(self);
|
||||||
|
let new_rev = current_rev + 1;
|
||||||
|
file.set_revision(self).to(new_rev);
|
||||||
|
|
||||||
|
tracing::debug!(
|
||||||
|
"Touched {}: revision {} -> {}",
|
||||||
|
path.display(),
|
||||||
|
current_rev,
|
||||||
|
new_rev
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a reference to the storage for handle extraction.
|
/// Get a reference to the storage for handle extraction.
|
||||||
///
|
///
|
||||||
/// This is used by `Session` to extract the [`StorageHandle`](salsa::StorageHandle) after mutations.
|
/// This is used by `Session` to extract the [`StorageHandle`](salsa::StorageHandle) after mutations.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue