Skip to content

Commit

Permalink
language server: clarify async shared state design
Browse files Browse the repository at this point in the history
  • Loading branch information
micahscopes committed Jan 26, 2024
1 parent 63f3e65 commit b4c6c01
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 95 deletions.
27 changes: 12 additions & 15 deletions crates/language-server/src/backend.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::sync::Arc;
use tokio::sync::Mutex;
use std::sync::{Mutex, MutexGuard};
// use tokio::sync::{Mutex, MutexGuard};

use crate::db::LanguageServerDatabase;

Expand All @@ -11,28 +12,24 @@ use lsp_types::{
use tower_lsp::Client;

pub struct Backend {
client: Arc<Mutex<Client>>,
db: Arc<Mutex<LanguageServerDatabase>>,
workspace: Arc<Mutex<Workspace>>,
pub(crate) client: Arc<tokio::sync::Mutex<Client>>,
pub(crate) db: Arc<Mutex<LanguageServerDatabase>>,
pub(crate) workspace: Arc<Mutex<Workspace>>,
}

impl Backend {
pub(crate) fn db(&self) -> Arc<Mutex<LanguageServerDatabase>> {
self.db.clone()
}

pub(crate) fn workspace(&self) -> Arc<Mutex<Workspace>> {
self.workspace.clone()
}
// pub(crate) fn db(&self) -> MutexGuard<LanguageServerDatabase> {
// self.db.lock().unwrap()
// }

pub(crate) fn client(&self) -> Arc<Mutex<Client>> {
self.client.clone()
}
// pub(crate) fn workspace(&self) -> MutexGuard<Workspace> {
// self.workspace.lock().unwrap()
// }

pub fn new(client: Client) -> Self {
let db = Arc::new(Mutex::new(LanguageServerDatabase::default()));
let workspace = Arc::new(Mutex::new(Workspace::default()));
let client = Arc::new(Mutex::new(client));
let client = Arc::new(tokio::sync::Mutex::new(client));
Self {
client,
db,
Expand Down
2 changes: 1 addition & 1 deletion crates/language-server/src/handlers/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub fn handle_watched_file_changes(
lsp_types::FileChangeType::CREATED => {
// TODO: handle this more carefully!
// this is inefficient, a hack for now
// let db = state.db();
// let db = state.db.lock().unwrap();
// let db = &mut state.db.lock().unwrap();
let _ = workspace.sync(db);
let input = workspace
Expand Down
147 changes: 71 additions & 76 deletions crates/language-server/src/language_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use lsp_types::{
TextDocumentItem,
};

use tower_lsp::{jsonrpc::Result, LanguageServer};
use tokio::sync::MutexGuard;
use tower_lsp::{jsonrpc::Result, Client, LanguageServer};

use crate::{
backend::Backend,
Expand All @@ -27,23 +28,24 @@ impl LanguageServer for Backend {
}),
};
// setup logging
let _ = self.init_logger(log::Level::Info);
{
let _ = self.init_logger(log::Level::Info);
}

// setup workspace
let workspace = self.workspace();
let workspace = &mut workspace.lock().await;
let db = self.db();
let db = &mut db.lock().await;

let _ = workspace.set_workspace_root(
db,
initialize_params
.root_uri
.unwrap()
.to_file_path()
.ok()
.unwrap(),
);
{
let workspace = &mut self.workspace.lock().unwrap();
let db = &mut self.db.lock().unwrap();
let _ = workspace.set_workspace_root(
db,
initialize_params
.root_uri
.unwrap()
.to_file_path()
.ok()
.unwrap(),
);
}

// register watchers
let _ = self.register_watchers().await;
Expand All @@ -56,29 +58,35 @@ impl LanguageServer for Backend {

async fn did_open(&self, params: lsp_types::DidOpenTextDocumentParams) {
info!("did open: {:?}", params);
let workspace = self.workspace();
let workspace = &mut workspace.lock().await;
let db = self.db();
let db = &mut db.lock().await;
let _ = workspace.sync(db);
{
let workspace = &mut self.workspace.lock().unwrap();
let db = &mut self.db.lock().unwrap();
let _ = workspace.sync(db);
}

self.on_change(TextDocumentItem {
uri: params.text_document.uri,
language_id: LANGUAGE_ID.to_string(),
version: params.text_document.version,
text: params.text_document.text,
})
on_change(
self,
TextDocumentItem {
uri: params.text_document.uri,
language_id: LANGUAGE_ID.to_string(),
version: params.text_document.version,
text: params.text_document.text,
},
)
.await;
}

async fn did_change(&self, params: lsp_types::DidChangeTextDocumentParams) {
info!("did change: {:?}", params);
self.on_change(TextDocumentItem {
uri: params.text_document.uri,
language_id: LANGUAGE_ID.to_string(),
version: params.text_document.version,
text: params.content_changes[0].text.clone(),
})
on_change(
self,
TextDocumentItem {
uri: params.text_document.uri,
language_id: LANGUAGE_ID.to_string(),
version: params.text_document.version,
text: params.content_changes[0].text.clone(),
},
)
.await;
}

Expand All @@ -88,10 +96,10 @@ impl LanguageServer for Backend {
// The fix: handle document renaming more explicitly in the "will rename" flow, along with the document
// rename refactor.
async fn did_close(&self, params: DidCloseTextDocumentParams) {
let workspace = self.workspace();
let workspace = &mut workspace.lock().await;
let db = self.db();
let db = &mut db.lock().await;
let workspace = &mut self.workspace.lock().unwrap();
// let workspace = &mut workspace.lock().await;
let db = &mut self.db.lock().unwrap();
// let db = &mut db.lock().await;

let input = workspace
.input_from_file_path(
Expand All @@ -108,11 +116,6 @@ impl LanguageServer for Backend {
let _ = input.sync(db, None);
}
async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) {
let workspace = self.workspace();
let workspace = &mut workspace.lock().await;
let db = self.db();
let db = &mut db.lock().await;

let changes = params.changes;
for change in changes {
let uri = change.uri;
Expand All @@ -122,13 +125,17 @@ impl LanguageServer for Backend {
lsp_types::FileChangeType::CREATED => {
// TODO: handle this more carefully!
// this is inefficient, a hack for now
let workspace = &mut self.workspace.lock().unwrap();
let db = &mut self.db.lock().unwrap();
let _ = workspace.sync(db);
let input = workspace
.input_from_file_path(db, path.to_str().unwrap())
.unwrap();
let _ = input.sync(db, None);
}
lsp_types::FileChangeType::CHANGED => {
let workspace = &mut self.workspace.lock().unwrap();
let db = &mut self.db.lock().unwrap();
let input = workspace
.input_from_file_path(db, path.to_str().unwrap())
.unwrap();
Expand All @@ -137,50 +144,34 @@ impl LanguageServer for Backend {
lsp_types::FileChangeType::DELETED => {
// TODO: handle this more carefully!
// this is inefficient, a hack for now
let workspace = &mut self.workspace.lock().unwrap();
let db = &mut self.db.lock().unwrap();
let _ = workspace.sync(db);
}
_ => {}
}
// collect diagnostics for the file
if change.typ != lsp_types::FileChangeType::DELETED {
// let diags = get_diagnostics(db, workspace, uri.clone());
// for (uri, more_diags) in diags.ok().unwrap() {
// let diags = diagnostics.entry(uri).or_insert_with(Vec::new);
// diags.extend(more_diags);
// }
let text = std::fs::read_to_string(path).unwrap();
self.on_change(TextDocumentItem {
uri: uri.clone(),
language_id: LANGUAGE_ID.to_string(),
version: 0,
text,
})
on_change(
self,
TextDocumentItem {
uri: uri.clone(),
language_id: LANGUAGE_ID.to_string(),
version: 0,
text,
},
)
.await;
}
}
}

// async fn will_rename_files(&self, params: RenameFilesParams) -> Result<Option<WorkspaceEdit>> {
// let workspace = &mut *self.workspace.lock().await;
// let db = &mut *self.db.lock().await;

// for file in params.files {
// let _ = workspace.rename_file(db, &*file.old_uri, &*file.new_uri);
// }

// // TODO: implement file rename auto-refactoring
// Ok(None)
// }
}

impl Backend {
async fn on_change(&self, params: TextDocumentItem) {
let workspace = self.workspace();
let workspace = &mut workspace.lock().await;
let db = self.db();
let db = &mut db.lock().await;
let client = self.client();
let client = &mut *client.lock().await;
async fn on_change(backend: &Backend, params: TextDocumentItem) {
let diagnostics = {
let workspace = &mut backend.workspace.lock().unwrap();
let db = &mut backend.db.lock().unwrap();
let input = workspace
.input_from_file_path(
db,
Expand All @@ -193,12 +184,16 @@ impl Backend {
)
.unwrap();
let _ = input.sync(db, Some(params.text));
let diagnostics = get_diagnostics(db, workspace, params.uri.clone())
get_diagnostics(db, workspace, params.uri.clone())
};

let client = backend.client.lock().await;
let diagnostics =
diagnostics
.unwrap()
.into_iter()
.map(|(uri, diags)| client.publish_diagnostics(uri, diags, None))
.collect::<Vec<_>>();

futures::future::join_all(diagnostics).await;
}
futures::future::join_all(diagnostics).await;
}
22 changes: 19 additions & 3 deletions crates/language-server/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ impl log::Log for Logger {
let message = format!("{} - {}", record.level(), record.args());
let level = record.level();
let client = self.client.clone();
tokio::task::spawn(async move {
let client = client.lock().await;
tokio::spawn(async move {
let mut client = client.lock().await;
client
.log_message(
match level {
Expand All @@ -37,6 +37,22 @@ impl log::Log for Logger {
)
.await;
});
// let client = self.client.clone();
// tokio::task::spawn_blocking(async move {
// let client = client.lock().await;
// client
// .log_message(
// match level {
// log::Level::Error => lsp_types::MessageType::ERROR,
// log::Level::Warn => lsp_types::MessageType::WARNING,
// log::Level::Info => lsp_types::MessageType::INFO,
// log::Level::Debug => lsp_types::MessageType::LOG,
// log::Level::Trace => lsp_types::MessageType::LOG,
// },
// message,
// )
// .await;
// });
}
}

Expand All @@ -47,7 +63,7 @@ impl Backend {
pub fn init_logger(&self, level: Level) -> Result<(), SetLoggerError> {
let logger = Logger {
level,
client: self.client(),
client: self.client.clone(),
};
let static_logger = Box::leak(Box::new(logger));
log::set_logger(static_logger)?;
Expand Down

0 comments on commit b4c6c01

Please sign in to comment.