Skip to content

Commit e4d0311

Browse files
todo: adjust result types to use tower_lsp::jsonrpc
1 parent 0af11c4 commit e4d0311

File tree

4 files changed

+141
-29
lines changed

4 files changed

+141
-29
lines changed

crates/pg_lsp/src/b_server.rs

Lines changed: 119 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::sync::Arc;
33
use notification::ShowMessage;
44
use pg_commands::CommandType;
55
use pg_workspace::Workspace;
6-
use tokio::sync::{Mutex, RwLock};
7-
use tower_lsp::jsonrpc::Result;
6+
use tokio::sync::RwLock;
7+
use tower_lsp::jsonrpc::Error;
88
use tower_lsp::lsp_types::*;
99
use tower_lsp::{Client, LanguageServer};
1010

@@ -14,7 +14,7 @@ use crate::server::options::ClientConfigurationOptions;
1414

1515
struct Server {
1616
client: Client,
17-
db: Mutex<Option<DbConnection>>,
17+
db: RwLock<Option<DbConnection>>,
1818
ide: Arc<RwLock<Workspace>>,
1919
client_capabilities: RwLock<Option<ClientFlags>>,
2020
}
@@ -24,7 +24,7 @@ impl Server {
2424
let ide = Arc::new(RwLock::new(Workspace::new()));
2525
Self {
2626
client,
27-
db: Mutex::new(None),
27+
db: RwLock::new(None),
2828
ide,
2929
client_capabilities: RwLock::new(None),
3030
}
@@ -34,13 +34,13 @@ impl Server {
3434
fn parse_options_from_client(
3535
&self,
3636
mut value: serde_json::Value,
37-
) -> Result<ClientConfigurationOptions> {
37+
) -> Option<ClientConfigurationOptions> {
3838
let options = match value.get_mut("pglsp") {
3939
Some(section) => section.take(),
4040
None => value,
4141
};
4242

43-
let options = match serde_json::from_value::<ClientConfigurationOptions>(options) {
43+
match serde_json::from_value::<ClientConfigurationOptions>(options) {
4444
Ok(new_options) => Some(new_options),
4545
Err(why) => {
4646
let message = format!(
@@ -51,19 +51,21 @@ impl Server {
5151
.send_notification::<ShowMessage>(ShowMessageParams { message, typ });
5252
None
5353
}
54-
};
55-
56-
Ok(options.unwrap_or_default())
54+
}
5755
}
5856

57+
/// `update_db_connection` will update `Self`'s database connection.
58+
/// If the passed-in connection string is the same that we're already connected to, it's a noop.
59+
/// Otherwise, it'll first open a new connection, replace `Self`'s connection, and then close
60+
/// the old one.
5961
async fn update_db_connection(
6062
&self,
6163
options: ClientConfigurationOptions,
6264
) -> anyhow::Result<()> {
6365
if options.db_connection_string.is_none()
6466
|| self
6567
.db
66-
.lock()
68+
.read()
6769
.await
6870
.as_ref()
6971
// if the connection is already connected to the same database, do nothing
@@ -81,7 +83,7 @@ impl Server {
8183
let _guard = ide.blocking_write().set_schema_cache(schema);
8284
});
8385

84-
let mut current_db = self.db.lock().await;
86+
let mut current_db = self.db.blocking_write();
8587
let old_db = current_db.replace(db);
8688

8789
if old_db.is_some() {
@@ -91,11 +93,48 @@ impl Server {
9193

9294
Ok(())
9395
}
96+
97+
async fn request_opts_from_client(&self) -> Option<ClientConfigurationOptions> {
98+
let params = ConfigurationParams {
99+
items: vec![ConfigurationItem {
100+
section: Some("pglsp".to_string()),
101+
scope_uri: None,
102+
}],
103+
};
104+
105+
match self
106+
.client
107+
.send_request::<request::WorkspaceConfiguration>(params)
108+
.await
109+
{
110+
Ok(json) => {
111+
// The client reponse fits the requested `ConfigurationParams.items`,
112+
// so the first value is what we're looking for.
113+
let relevant = json
114+
.into_iter()
115+
.next()
116+
.expect("workspace/configuration request did not yield expected response.");
117+
118+
let opts = self.parse_options_from_client(relevant);
119+
120+
opts
121+
}
122+
Err(why) => {
123+
let message = format!(
124+
"Unable to pull client options via workspace/configuration request: {}",
125+
why
126+
);
127+
println!("{}", message);
128+
self.client.log_message(MessageType::ERROR, message);
129+
None
130+
}
131+
}
132+
}
94133
}
95134

96135
#[tower_lsp::async_trait]
97136
impl LanguageServer for Server {
98-
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
137+
async fn initialize(&self, params: InitializeParams) -> tower_lsp::jsonrpc::Result<InitializeResult> {
99138
let flags = ClientFlags::from_initialize_request_params(&params);
100139
self.client_capabilities.blocking_write().replace(flags);
101140

@@ -135,25 +174,86 @@ impl LanguageServer for Server {
135174
.await;
136175
}
137176

138-
async fn shutdown(&self) -> Result<()> {
177+
async fn shutdown(&self) -> anyhow::Result<()> {
139178
self.client
140179
.log_message(MessageType::INFO, "Postgres LSP terminated.")
141180
.await;
142181
Ok(())
143182
}
144183

145-
146184
async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
147-
match self.parse_options_from_client(params.settings) {
148-
Ok(opts) => {
149-
self.update_db_connection(opts).await;
185+
let capabilities = self.client_capabilities.read().await;
186+
187+
if capabilities.as_ref().unwrap().supports_pull_opts {
188+
let opts = self.request_opts_from_client().await;
189+
if opts.is_some() {
190+
self.update_db_connection(opts.unwrap()).await;
191+
return;
192+
}
193+
}
194+
195+
let opts = self.parse_options_from_client(params.settings);
196+
197+
if opts.is_some() {
198+
self.update_db_connection(opts.unwrap()).await;
199+
}
200+
}
201+
202+
async fn execute_command(
203+
&self,
204+
params: ExecuteCommandParams,
205+
) -> tower_lsp::jsonrpc::Result<Option<serde_json::Value>> {
206+
match CommandType::from_id(params.command.replace("pglsp.", "").as_str()) {
207+
Some(CommandType::ExecuteStatement) => {
208+
if params.arguments.is_empty() {
209+
return tower_lsp::jsonrpc::Result::Err(Error::new("No arguments provided!"));
210+
}
211+
212+
let stmt = params
213+
.arguments
214+
.into_iter()
215+
.next()
216+
.map(|v| serde_json::from_value(v))
217+
.unwrap()?;
218+
219+
let conn = self.db.read().await;
220+
match conn
221+
.as_ref()
222+
.expect("No connection to the database.")
223+
.run_stmt(stmt)
224+
.await
225+
{
226+
Ok(pg_result) => {
227+
self.client
228+
.send_notification::<ShowMessage>(ShowMessageParams {
229+
typ: MessageType::INFO,
230+
message: format!(
231+
"Success! Affected rows: {}",
232+
pg_result.rows_affected()
233+
),
234+
})
235+
.await;
236+
}
237+
Err(why) => {
238+
self.client
239+
.send_notification::<ShowMessage>(ShowMessageParams {
240+
typ: MessageType::ERROR,
241+
message: format!("Error! Statement exectuion failed: {}", why),
242+
})
243+
.await;
244+
}
245+
};
150246
}
151-
Err(e) => {
247+
None => {
152248
self.client
153-
.log_message(MessageType::ERROR, format!("Error parsing configuration: {}", e))
249+
.show_message(
250+
MessageType::ERROR,
251+
format!("Unknown command: {}", params.command),
252+
)
154253
.await;
155254
}
156255
};
157256

257+
Ok(None)
158258
}
159259
}

crates/pg_lsp/src/client/client_flags.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@ use tower_lsp::lsp_types::InitializeParams;
55
#[derive(Debug, Clone)]
66
pub struct ClientFlags {
77
/// If `true`, the server can pull configuration from the client.
8-
pub has_configuration: bool,
8+
pub supports_pull_opts: bool,
99

1010
/// If `true`, the client notifies the server when its configuration changes.
11-
pub will_push_configuration: bool,
11+
pub supports_dynamic_registration: bool,
1212
}
1313

1414
impl ClientFlags {
1515
pub(crate) fn from_initialize_request_params(params: &InitializeParams) -> Self {
16-
let has_configuration = params
16+
let supports_pull_opts = params
1717
.capabilities
1818
.workspace
1919
.as_ref()
2020
.and_then(|w| w.configuration)
2121
.unwrap_or(false);
2222

23-
let will_push_configuration = params
23+
let supports_dynamic_registration = params
2424
.capabilities
2525
.workspace
2626
.as_ref()
@@ -29,8 +29,8 @@ impl ClientFlags {
2929
.unwrap_or(false);
3030

3131
Self {
32-
has_configuration,
33-
will_push_configuration,
32+
supports_pull_opts,
33+
supports_dynamic_registration,
3434
}
3535
}
3636
}

crates/pg_lsp/src/db_connection.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
use pg_commands::ExecuteStatementCommand;
12
use pg_schema_cache::SchemaCache;
2-
use sqlx::{postgres::PgListener, PgPool};
3+
use sqlx::{
4+
postgres::{PgListener, PgQueryResult},
5+
PgPool,
6+
};
37
use tokio::task::JoinHandle;
48

59
#[derive(Debug)]
@@ -19,6 +23,14 @@ impl DbConnection {
1923
})
2024
}
2125

26+
/// TODO: this should simply take a `Command` type, and the individual
27+
/// enums should have their deps included (i.e. `ExecuteStatement(String)`)
28+
pub async fn run_stmt(&self, stmt: String) -> anyhow::Result<PgQueryResult> {
29+
let command = ExecuteStatementCommand::new(stmt);
30+
let pool = self.pool.clone();
31+
command.run(Some(pool)).await
32+
}
33+
2234
pub(crate) fn connected_to(&self, connection_string: &str) -> bool {
2335
connection_string == self.connection_string
2436
}

crates/pg_lsp/src/server.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ impl Server {
725725
&mut self,
726726
params: DidChangeConfigurationParams,
727727
) -> anyhow::Result<()> {
728-
if self.client_flags.has_configuration {
728+
if self.client_flags.supports_pull_opts {
729729
self.pull_options();
730730
} else {
731731
let options = self.client.parse_options(params.settings)?;
@@ -913,11 +913,11 @@ impl Server {
913913
}
914914

915915
pub async fn run(mut self) -> anyhow::Result<()> {
916-
if self.client_flags.will_push_configuration {
916+
if self.client_flags.supports_dynamic_registration {
917917
self.register_configuration();
918918
}
919919

920-
if self.client_flags.has_configuration {
920+
if self.client_flags.supports_pull_opts {
921921
self.pull_options();
922922
}
923923

0 commit comments

Comments
 (0)