get rid of custom database handle

This commit is contained in:
Josh Thomas 2025-05-14 19:23:32 -05:00
parent ec1ecd62af
commit a8173618f1
3 changed files with 55 additions and 61 deletions

View file

@ -1,66 +1,21 @@
use salsa::Database;
/// A thread-safe handle to a Salsa database that can be shared between threads.
///
/// This implements the insight from [this Salsa Zulip discussion](https://salsa.zulipchat.com/#narrow/channel/145099-Using-Salsa/topic/.E2.9C.94.20Advice.20on.20using.20salsa.20from.20Sync.20.2B.20Send.20context/with/495497515)
/// where we're using the `StorageHandle` to create a thread-safe handle that can be
/// shared between threads. When we need to use it, we clone the handle to get a new reference.
///
/// Usage:
/// ```rust,ignore
/// // Create a new handle
/// let db_handle = ServerDatabaseHandle::new();
///
/// // Clone it to pass to different threads
/// let db_handle_clone = db_handle.clone();
///
/// // Use it in an async context
/// async_fn(move || {
/// // Get a database from the handle
/// let db = db_handle_clone.db();
///
/// // Use the database
/// db.some_query(args)
/// });
/// ```
#[derive(Clone, Default)]
pub struct ServerDatabaseHandle(salsa::StorageHandle<ServerDatabase>);
impl ServerDatabaseHandle {
/// Create a new thread-safe database handle
///
/// This creates a handle that can be safely shared between threads and cloned
/// to create new references to the same underlying database storage.
pub fn new() -> Self {
Self(salsa::StorageHandle::new(None))
}
/// Get a database instance from this handle
///
/// This creates a usable database from the handle, which can be used
/// to query and update data in the database. The database can be cloned
/// cheaply as it's just a reference to the underlying storage.
pub fn db(&self) -> ServerDatabase {
let storage = self.0.clone().into_storage();
ServerDatabase { storage }
}
}
impl std::fmt::Debug for ServerDatabaseHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ServerDatabaseHandle").finish()
}
}
#[salsa::db]
#[derive(Clone, Default)]
pub struct ServerDatabase {
storage: salsa::Storage<Self>,
}
impl ServerDatabase {
/// Create a new database from storage
pub fn new(storage: salsa::Storage<Self>) -> Self {
Self { storage }
}
}
impl std::fmt::Debug for ServerDatabase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ServerDatabase").finish()
f.debug_struct("ServerDatabase").finish_non_exhaustive()
}
}

View file

@ -262,7 +262,7 @@ impl LanguageServer for DjangoLanguageServer {
.await;
self.with_session_mut(|session| {
let db = session.db_handle().db();
let db = session.db();
session.documents_mut().handle_did_open(&db, &params);
})
.await;
@ -277,7 +277,7 @@ impl LanguageServer for DjangoLanguageServer {
.await;
self.with_session_mut(|session| {
let db = session.db_handle().db();
let db = session.db();
let _ = session.documents_mut().handle_did_change(&db, &params);
})
.await;
@ -302,8 +302,8 @@ impl LanguageServer for DjangoLanguageServer {
.with_session(|session| {
if let Some(project) = session.project() {
if let Some(tags) = project.template_tags() {
// Get a database instance from the handle
let db = session.db_handle().db();
// Get a database instance directly
let db = session.db();
return session.documents().get_completions(
&db,
params.text_document_position.text_document.uri.as_str(),

View file

@ -1,8 +1,9 @@
use djls_conf::Settings;
use djls_project::DjangoProject;
use salsa::StorageHandle;
use tower_lsp_server::lsp_types::ClientCapabilities;
use crate::db::ServerDatabaseHandle;
use crate::db::ServerDatabase;
use crate::documents::Store;
#[derive(Default)]
@ -11,7 +12,32 @@ pub struct Session {
project: Option<DjangoProject>,
documents: Store,
settings: Settings,
db_handle: ServerDatabaseHandle,
/// A thread-safe Salsa database handle that can be shared between threads.
///
/// This implements the insight from [this Salsa Zulip discussion](https://salsa.zulipchat.com/#narrow/channel/145099-Using-Salsa/topic/.E2.9C.94.20Advice.20on.20using.20salsa.20from.20Sync.20.2B.20Send.20context/with/495497515)
/// where we're using the `StorageHandle` to create a thread-safe handle that can be
/// shared between threads. When we need to use it, we clone the handle to get a new reference.
///
/// Usage:
/// ```rust,ignore
/// // Use the StorageHandle in Session
/// let db_handle = StorageHandle::new(None);
///
/// // Clone it to pass to different threads
/// let db_handle_clone = db_handle.clone();
///
/// // Use it in an async context
/// async_fn(move || {
/// // Get a database from the handle
/// let storage = db_handle_clone.into_storage();
/// let db = ServerDatabase::new(storage);
///
/// // Use the database
/// db.some_query(args)
/// });
/// ```
db_handle: StorageHandle<ServerDatabase>,
}
impl Session {
@ -21,7 +47,7 @@ impl Session {
project: None,
documents: Store::new(),
settings: Settings::default(),
db_handle: ServerDatabaseHandle::new(),
db_handle: StorageHandle::new(None),
}
}
@ -57,7 +83,20 @@ impl Session {
&mut self.settings
}
pub fn db_handle(&self) -> &ServerDatabaseHandle {
/// Get the raw database handle from the session
///
/// Note: In most cases, you'll want to use `db()` instead to get a usable
/// database instance directly.
pub fn db_handle(&self) -> &StorageHandle<ServerDatabase> {
&self.db_handle
}
/// Get a database instance directly from the session
///
/// This creates a usable database from the handle, which can be used
/// to query and update data in the database.
pub fn db(&self) -> ServerDatabase {
let storage = self.db_handle.clone().into_storage();
ServerDatabase::new(storage)
}
}