remove left over Database code from workspace crate (#197)

This commit is contained in:
Josh Thomas 2025-09-06 10:02:48 -05:00 committed by GitHub
parent fd0a1c160f
commit 318a395d6f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 47 additions and 165 deletions

View file

@ -7,6 +7,8 @@
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
#[cfg(test)]
use std::sync::Mutex;
use dashmap::DashMap; use dashmap::DashMap;
use djls_templates::db::Db as TemplateDb; use djls_templates::db::Db as TemplateDb;
@ -31,15 +33,49 @@ pub struct DjangoDatabase {
files: Arc<DashMap<PathBuf, SourceFile>>, files: Arc<DashMap<PathBuf, SourceFile>>,
storage: salsa::Storage<Self>, storage: salsa::Storage<Self>,
// The logs are only used for testing and demonstrating reuse:
#[cfg(test)]
#[allow(dead_code)]
logs: Arc<Mutex<Option<Vec<String>>>>,
}
#[cfg(test)]
impl Default for DjangoDatabase {
fn default() -> Self {
use djls_workspace::InMemoryFileSystem;
let logs = <Arc<Mutex<Option<Vec<String>>>>>::default();
Self {
fs: Arc::new(InMemoryFileSystem::new()),
files: Arc::new(DashMap::new()),
storage: salsa::Storage::new(Some(Box::new({
let logs = logs.clone();
move |event| {
eprintln!("Event: {event:?}");
// Log interesting events, if logging is enabled
if let Some(logs) = &mut *logs.lock().unwrap() {
// only log interesting events
if let salsa::EventKind::WillExecute { .. } = event.kind {
logs.push(format!("Event: {event:?}"));
}
}
}
}))),
logs,
}
}
} }
impl DjangoDatabase { impl DjangoDatabase {
/// Create a new [`DjangoDatabase`] with the given file system and file map. /// Create a new [`DjangoDatabase`] with the given file system and file map.
pub fn new(file_system: Arc<dyn FileSystem>, files: Arc<DashMap<PathBuf, SourceFile>>) -> Self { pub fn new(file_system: Arc<dyn FileSystem>, files: Arc<DashMap<PathBuf, SourceFile>>) -> Self {
Self { Self {
storage: salsa::Storage::new(None),
fs: file_system, fs: file_system,
files, files,
storage: salsa::Storage::new(None),
#[cfg(test)]
logs: Arc::new(Mutex::new(None)),
} }
} }

View file

@ -21,13 +21,7 @@
//! ``` //! ```
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
#[cfg(test)]
use std::sync::Mutex;
use dashmap::DashMap;
use salsa::Setter;
use crate::FileKind; use crate::FileKind;
use crate::FileSystem; use crate::FileSystem;
@ -45,156 +39,6 @@ pub trait Db: salsa::Database {
fn read_file_content(&self, path: &Path) -> std::io::Result<String>; fn read_file_content(&self, path: &Path) -> std::io::Result<String>;
} }
/// Temporary concrete database for workspace.
///
/// This will be moved to the server crate in the refactoring.
/// For now, it's kept here to avoid breaking existing code.
#[salsa::db]
#[derive(Clone)]
pub struct Database {
storage: salsa::Storage<Self>,
/// File system for reading file content (checks buffers first, then disk).
fs: Arc<dyn FileSystem>,
/// Maps paths to [`SourceFile`] entities for O(1) lookup.
files: Arc<DashMap<PathBuf, SourceFile>>,
// The logs are only used for testing and demonstrating reuse:
#[cfg(test)]
#[allow(dead_code)]
logs: Arc<Mutex<Option<Vec<String>>>>,
}
#[cfg(test)]
impl Default for Database {
fn default() -> Self {
use crate::fs::InMemoryFileSystem;
let logs = <Arc<Mutex<Option<Vec<String>>>>>::default();
Self {
storage: salsa::Storage::new(Some(Box::new({
let logs = logs.clone();
move |event| {
eprintln!("Event: {event:?}");
// Log interesting events, if logging is enabled
if let Some(logs) = &mut *logs.lock().unwrap() {
// only log interesting events
if let salsa::EventKind::WillExecute { .. } = event.kind {
logs.push(format!("Event: {event:?}"));
}
}
}
}))),
fs: Arc::new(InMemoryFileSystem::new()),
files: Arc::new(DashMap::new()),
logs,
}
}
}
impl Database {
pub fn new(file_system: Arc<dyn FileSystem>, files: Arc<DashMap<PathBuf, SourceFile>>) -> Self {
Self {
storage: salsa::Storage::new(None),
fs: file_system,
files,
#[cfg(test)]
logs: Arc::new(Mutex::new(None)),
}
}
/// Read file content through the file system.
pub fn read_file_content(&self, path: &Path) -> std::io::Result<String> {
self.fs.read_to_string(path)
}
/// Get an existing [`SourceFile`] for the given path without creating it.
///
/// Returns `Some(SourceFile)` if the file is already tracked, `None` otherwise.
/// This method uses an immutable reference and doesn't modify the database.
pub fn get_file(&self, path: &Path) -> Option<SourceFile> {
self.files.get(path).map(|file_ref| *file_ref)
}
/// Get or create a [`SourceFile`] for the given path.
///
/// Files are created with an initial revision of 0 and tracked in the [`Database`]'s
/// `DashMap`. The `Arc` ensures cheap cloning while maintaining thread safety.
///
/// ## Thread Safety
///
/// This method is inherently thread-safe despite the check-then-create pattern because
/// it requires `&mut self`, ensuring exclusive access to the Database. Only one thread
/// can call this method at a time due to Rust's ownership rules.
pub fn get_or_create_file(&mut self, path: &PathBuf) -> SourceFile {
if let Some(file_ref) = self.files.get(path) {
// Copy the value (SourceFile is Copy)
// The guard drops automatically, no need for explicit drop
return *file_ref;
}
// File doesn't exist, so we need to create it
let kind = FileKind::from_path(path);
let file = SourceFile::new(self, kind, Arc::from(path.to_string_lossy().as_ref()), 0);
self.files.insert(path.clone(), file);
file
}
/// Check if a file is being tracked without creating it.
///
/// This is primarily used for testing to verify that files have been
/// created without affecting the database state.
pub fn has_file(&self, path: &Path) -> bool {
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
);
}
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {
fn fs(&self) -> Arc<dyn FileSystem> {
self.fs.clone()
}
fn read_file_content(&self, path: &Path) -> std::io::Result<String> {
self.fs.read_to_string(path)
}
}
/// Represents a single file without storing its content. /// Represents a single file without storing its content.
/// ///
/// [`SourceFile`] is a Salsa input entity that tracks a file's path, revision, and /// [`SourceFile`] is a Salsa input entity that tracks a file's path, revision, and

View file

@ -3,11 +3,9 @@
//! This module provides the [`FileSystem`] trait that abstracts file I/O operations. //! This module provides the [`FileSystem`] trait that abstracts file I/O operations.
//! This allows the LSP to work with both real files and in-memory overlays. //! This allows the LSP to work with both real files and in-memory overlays.
#[cfg(test)]
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
#[cfg(test)]
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -19,13 +17,12 @@ pub trait FileSystem: Send + Sync {
fn exists(&self, path: &Path) -> bool; fn exists(&self, path: &Path) -> bool;
} }
#[cfg(test)]
pub struct InMemoryFileSystem { pub struct InMemoryFileSystem {
files: HashMap<PathBuf, String>, files: HashMap<PathBuf, String>,
} }
#[cfg(test)]
impl InMemoryFileSystem { impl InMemoryFileSystem {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
files: HashMap::new(), files: HashMap::new(),
@ -37,7 +34,12 @@ impl InMemoryFileSystem {
} }
} }
#[cfg(test)] impl Default for InMemoryFileSystem {
fn default() -> Self {
Self::new()
}
}
impl FileSystem for InMemoryFileSystem { impl FileSystem for InMemoryFileSystem {
fn read_to_string(&self, path: &Path) -> io::Result<String> { fn read_to_string(&self, path: &Path) -> io::Result<String> {
self.files self.files
@ -81,7 +83,7 @@ impl FileSystem for OsFileSystem {
/// This ensures consistent behavior across all filesystem operations for /// This ensures consistent behavior across all filesystem operations for
/// buffered files that may not yet be saved to disk. /// buffered files that may not yet be saved to disk.
/// ///
/// This type is used by the [`Database`](crate::db::Database) to ensure all file reads go /// This type is used by the database implementations to ensure all file reads go
/// through the buffer system first. /// through the buffer system first.
pub struct WorkspaceFileSystem { pub struct WorkspaceFileSystem {
/// In-memory buffers that take precedence over disk files /// In-memory buffers that take precedence over disk files

View file

@ -7,7 +7,7 @@
//! # Key Components //! # Key Components
//! //!
//! - [`Buffers`] - Thread-safe storage for open documents //! - [`Buffers`] - Thread-safe storage for open documents
//! - [`Database`] - Salsa database for incremental computation //! - [`Db`] - Database trait for file system access (concrete impl in server crate)
//! - [`TextDocument`] - LSP document representation with efficient indexing //! - [`TextDocument`] - LSP document representation with efficient indexing
//! - [`FileSystem`] - Abstraction layer for file operations with overlay support //! - [`FileSystem`] - Abstraction layer for file operations with overlay support
//! - [`paths`] - Consistent URL/path conversion utilities //! - [`paths`] - Consistent URL/path conversion utilities
@ -24,12 +24,12 @@ mod workspace;
use std::path::Path; use std::path::Path;
pub use buffers::Buffers; pub use buffers::Buffers;
pub use db::Database;
pub use db::Db; pub use db::Db;
pub use db::SourceFile; pub use db::SourceFile;
pub use document::TextDocument; pub use document::TextDocument;
pub use encoding::PositionEncoding; pub use encoding::PositionEncoding;
pub use fs::FileSystem; pub use fs::FileSystem;
pub use fs::InMemoryFileSystem;
pub use fs::OsFileSystem; pub use fs::OsFileSystem;
pub use fs::WorkspaceFileSystem; pub use fs::WorkspaceFileSystem;
pub use language::LanguageId; pub use language::LanguageId;