diff --git a/crates/pgt_lsp/tests/server.rs b/crates/pgt_lsp/tests/server.rs index d911b50d..4a5655e6 100644 --- a/crates/pgt_lsp/tests/server.rs +++ b/crates/pgt_lsp/tests/server.rs @@ -818,9 +818,8 @@ async fn test_execute_statement() -> Result<()> { result.unwrap().exists.unwrap() }; - assert_eq!( - users_tbl_exists().await, - false, + assert!( + !(users_tbl_exists().await), "The user table shouldn't exist at this point." ); @@ -888,9 +887,8 @@ async fn test_execute_statement() -> Result<()> { ) .await?; - assert_eq!( + assert!( users_tbl_exists().await, - true, "Users table did not exists even though it should've been created by the workspace/executeStatement command." ); @@ -899,3 +897,87 @@ async fn test_execute_statement() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn test_issue_281() -> Result<()> { + let factory = ServerFactory::default(); + let mut fs = MemoryFileSystem::default(); + let test_db = get_new_test_db().await; + + let setup = r#" + create table public.users ( + id serial primary key, + name varchar(255) not null + ); + "#; + + test_db + .execute(setup) + .await + .expect("Failed to setup test database"); + + let mut conf = PartialConfiguration::init(); + conf.merge_with(PartialConfiguration { + db: Some(PartialDatabaseConfiguration { + database: Some( + test_db + .connect_options() + .get_database() + .unwrap() + .to_string(), + ), + ..Default::default() + }), + ..Default::default() + }); + fs.insert( + url!("postgrestools.jsonc").to_file_path().unwrap(), + serde_json::to_string_pretty(&conf).unwrap(), + ); + + let (service, client) = factory + .create_with_fs(None, DynRef::Owned(Box::new(fs))) + .into_inner(); + + let (stream, sink) = client.split(); + let mut server = Server::new(service); + + let (sender, _) = channel(CHANNEL_BUFFER_SIZE); + let reader = tokio::spawn(client_handler(stream, sink, sender)); + + server.initialize().await?; + server.initialized().await?; + + server.load_configuration().await?; + + server.open_document("\n------------- Meta -------------\n\n-- name: GetValueFromMetaKVStore :one\nSELECT value FROM meta_kv\nWHERE key = $1;\n\n-- name: SetValueToMetaKVStore :exec\nINSERT INTO meta_kv (key, value)\nVALUES ($1, $2)\nON CONFLICT (key) DO UPDATE\nSET value = excluded.value;\n\n\nasdsadsad\n\nыывфыв khgk\nasdыdsf\ndsdsjdfnfmdsвтьвыаыdsfsmndf,m\nы\n").await?; + + let chars = ["s", "n", ",", "d", "f", "j", "s", "d", "f", "в"]; + + for (i, c) in chars.iter().enumerate() { + server + .change_document( + i as i32 + 4, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 20, + character: i as u32, + }, + end: Position { + line: 20, + character: i as u32, + }, + }), + range_length: Some(0), + text: c.to_string(), + }], + ) + .await?; + } + + server.shutdown().await?; + reader.abort(); + + Ok(()) +} diff --git a/crates/pgt_workspace/src/workspace/server/change.rs b/crates/pgt_workspace/src/workspace/server/change.rs index 92422a8a..226a0ffd 100644 --- a/crates/pgt_workspace/src/workspace/server/change.rs +++ b/crates/pgt_workspace/src/workspace/server/change.rs @@ -249,10 +249,7 @@ impl Document { // if within a statement, we can modify it if the change results in also a single statement if affected_indices.len() == 1 { - let changed_content = new_content - .as_str() - .get(usize::from(affected_range.start())..usize::from(affected_range.end())) - .unwrap(); + let changed_content = get_affected(&new_content, affected_range); let (new_ranges, diags) = document::split_with_diagnostics(changed_content, Some(affected_range.start())); @@ -305,10 +302,7 @@ impl Document { } // in any other case, parse the full affected range - let changed_content = new_content - .as_str() - .get(usize::from(full_affected_range.start())..usize::from(full_affected_range.end())) - .unwrap(); + let changed_content = get_affected(&new_content, full_affected_range); let (new_ranges, diags) = document::split_with_diagnostics(changed_content, Some(full_affected_range.start())); @@ -412,6 +406,22 @@ impl ChangeParams { } } +fn get_affected(content: &str, range: TextRange) -> &str { + let start_byte = content + .char_indices() + .nth(usize::from(range.start())) + .map(|(i, _)| i) + .unwrap_or(content.len()); + + let end_byte = content + .char_indices() + .nth(usize::from(range.end())) + .map(|(i, _)| i) + .unwrap_or(content.len()); + + &content[start_byte..end_byte] +} + #[cfg(test)] mod tests { use super::*;