From 001318db2ac1361a6737742a7dbeb80ddaa9c5ff Mon Sep 17 00:00:00 2001 From: psteinroe Date: Mon, 6 Jan 2025 18:05:44 -0100 Subject: [PATCH 1/8] lets get into the mess... --- .../src/workspace/server/change.rs | 190 ++++++++++++++---- 1 file changed, 151 insertions(+), 39 deletions(-) diff --git a/crates/pg_workspace_new/src/workspace/server/change.rs b/crates/pg_workspace_new/src/workspace/server/change.rs index 4e8c0405..cf718d89 100644 --- a/crates/pg_workspace_new/src/workspace/server/change.rs +++ b/crates/pg_workspace_new/src/workspace/server/change.rs @@ -122,13 +122,22 @@ impl Document { // special case: if no statement is affected, the affected range is between the prev and // the next statement if affected.is_empty() { - let start = self + // since we do not now whether the change should be part of the previous statement, we + // will take the range form the start of the previous statement to the start of the + // next statement. if the resulting split has length one, we will modify it instead. + + let (from_stmt_index, from_stmt_range) = self .statements .iter() + .enumerate() .rev() - .find(|(_, r)| r.end() <= change.range.unwrap().start()) - .map(|(_, r)| r.end()) - .unwrap_or(TextSize::new(0)); + .find(|(_, (_, r))| r.start() <= change.range.unwrap().start()) + .map(|(i, (_, r))| (i, *r)) + .unwrap_or((0, TextRange::empty(TextSize::new(0)))); + + + let start = from_stmt_range.start(); + let end = self .statements .iter() @@ -141,24 +150,49 @@ impl Document { .get(usize::from(start)..usize::from(end)) .unwrap(); - // add new statements - for range in pg_statement_splitter::split(affected).ranges { - let doc_range = range + start; - match self - .statements - .binary_search_by(|(_, r)| r.start().cmp(&doc_range.start())) - { - Ok(_) => {} - Err(pos) => { - let new_id = self.id_generator.next(); - self.statements.insert(pos, (new_id, doc_range)); - changed.push(StatementChange::Added(Statement { - ref_: StatementRef { - path: self.path.clone(), - id: new_id, - }, - text: new_content[doc_range].to_string(), - })); + let new_ranges = pg_statement_splitter::split(affected).ranges; + + if new_ranges.len() == 1 && !from_stmt_range.is_empty() { + if !change.is_whitespace() { + // modify previous statement + let new_stmt = &new_ranges[0]; + + let new_id = self.id_generator.next(); + let old_stmt = self.statement(&self.statements[from_stmt_index]); + self.statements[from_stmt_index] = (new_id, new_stmt.add(start)); + + println!("change prev"); + let changed_stmt = ChangedStatement { + old: old_stmt, + new_ref: self.statement_ref(&self.statements[from_stmt_index]), + // change must be relative to statement + // TODO: range and text must be filled up with whitespaces + range: change.range.unwrap().sub(from_stmt_range.start()), + text: change.text.clone(), + }; + + changed.push(StatementChange::Modified(changed_stmt)); + } + } else { + // add new statements + for range in new_ranges { + let doc_range = range + start; + match self + .statements + .binary_search_by(|(_, r)| r.start().cmp(&doc_range.start())) + { + Ok(_) => {} + Err(pos) => { + let new_id = self.id_generator.next(); + self.statements.insert(pos, (new_id, doc_range)); + changed.push(StatementChange::Added(Statement { + ref_: StatementRef { + path: self.path.clone(), + id: new_id, + }, + text: new_content[doc_range].to_string(), + })); + } } } } @@ -204,22 +238,25 @@ impl Document { let ranges = pg_statement_splitter::split(changed_content).ranges; if affected.len() == 1 && ranges.len() == 1 { - // from one to one, so we do a modification - let stmt = &affected[0]; - let new_stmt = &ranges[0]; - - let new_id = self.id_generator.next(); - self.statements[stmt.0] = (new_id, new_stmt.add(start)); - - let changed_stmt = ChangedStatement { - old: self.statement(&stmt.1), - new_ref: self.statement_ref(&self.statements[stmt.0]), - // change must be relative to statement - range: change.range.unwrap().sub(stmt.1 .1.start()), - text: change.text.clone(), - }; - - changed.push(StatementChange::Modified(changed_stmt)); + if !change.is_whitespace() { + // from one to one, so we do a modification + let stmt = &affected[0]; + let new_stmt = &ranges[0]; + + let new_id = self.id_generator.next(); + self.statements[stmt.0] = (new_id, new_stmt.add(start)); + + println!("change one to one"); + let changed_stmt = ChangedStatement { + old: self.statement(&stmt.1), + new_ref: self.statement_ref(&self.statements[stmt.0]), + // change must be relative to statement + range: change.range.unwrap().sub(stmt.1 .1.start()), + text: change.text.clone(), + }; + + changed.push(StatementChange::Modified(changed_stmt)); + } } else { // delete and add new ones for (_, (id, r)) in &affected { @@ -282,7 +319,7 @@ fn apply_text_change(text: &str, range: Option, change_text: &str) -> impl ChangeParams { pub fn is_whitespace(&self) -> bool { - self.text.chars().all(char::is_whitespace) + self.text.chars().count() > 0 && self.text.chars().all(char::is_whitespace) } pub fn diff_size(&self) -> TextSize { @@ -362,6 +399,81 @@ mod tests { assert_document_integrity(&d); } + #[test] + fn julians_sample() { + let path = PgLspPath::new("test.sql"); + let input = "select\n *\nfrom\n test;\n\nselect\n\nalter table test\ndrop column id;"; + let mut d = Document::new(path.clone(), input.to_string(), 0); + + assert_eq!(d.statements.len(), 3); + println!("{:#?}", d.statements); + + let change1 = ChangeFileParams { + path: path.clone(), + version: 1, + changes: vec![ChangeParams { + text: " ".to_string(), + range: Some(TextRange::new(31.into(), 31.into())), + }], + }; + + let changed1 = d.apply_file_change(&change1); + println!("after change 1"); + println!("{:#?}", d.content); + println!("{:#?}", d.statements); + println!("{:#?}", changed1); + + // problem: this creates a new statement + let change2 = ChangeFileParams { + path: path.clone(), + version: 2, + changes: vec![ChangeParams { + text: ";".to_string(), + range: Some(TextRange::new(32.into(), 32.into())), + }], + }; + + let changed2 = d.apply_file_change(&change2); + println!("after change 2"); + println!("{:#?}", d.content); + println!("{:#?}", d.statements); + println!("{:#?}", changed2); + + let change3 = ChangeFileParams { + path: path.clone(), + version: 3, + changes: vec![ChangeParams { + text: "".to_string(), + range: Some(TextRange::new(32.into(), 33.into())), + }], + }; + + let changed3 = d.apply_file_change(&change3); + println!("after change 3"); + println!("{:#?}", d.content); + println!("{:#?}", d.statements); + println!("{:#?}", changed3); + + // + // assert_eq!(changed.len(), 4); + // assert!(matches!( + // changed[0], + // StatementChange::Deleted(StatementRef { id: 0, .. }) + // )); + // assert!(matches!( + // changed[1], + // StatementChange::Deleted(StatementRef { id: 1, .. }) + // )); + // assert!( + // matches!(&changed[2], StatementChange::Added(Statement { ref_: _, text }) if text == "select id,test from users;") + // ); + // assert!( + // matches!(&changed[3], StatementChange::Added(Statement { ref_: _, text }) if text == "select 1;") + // ); + // + assert_document_integrity(&d); + } + #[test] fn across_statements() { let path = PgLspPath::new("test.sql"); From 41f3eb8a6b1bb4461a8c3942e6c839c449c46f24 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Thu, 9 Jan 2025 10:40:10 -0100 Subject: [PATCH 2/8] woooorks now and its much simpler --- crates/pg_completions/Cargo.toml | 2 +- crates/pg_lsp_new/Cargo.toml | 2 +- crates/pg_workspace_new/Cargo.toml | 2 +- .../pg_workspace_new/src/workspace/server.rs | 50 +- .../src/workspace/server/change.rs | 762 +++++++++--------- .../src/workspace/server/document.rs | 140 +--- .../src/workspace/server/pg_query.rs | 36 +- .../src/workspace/server/store.rs | 23 - .../src/workspace/server/tree_sitter.rs | 41 +- 9 files changed, 473 insertions(+), 585 deletions(-) delete mode 100644 crates/pg_workspace_new/src/workspace/server/store.rs diff --git a/crates/pg_completions/Cargo.toml b/crates/pg_completions/Cargo.toml index c1cf8afe..d30451eb 100644 --- a/crates/pg_completions/Cargo.toml +++ b/crates/pg_completions/Cargo.toml @@ -16,9 +16,9 @@ async-std = "1.12.0" text-size.workspace = true +pg_schema_cache.workspace = true serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -pg_schema_cache.workspace = true tree-sitter.workspace = true tree_sitter_sql.workspace = true diff --git a/crates/pg_lsp_new/Cargo.toml b/crates/pg_lsp_new/Cargo.toml index 0454893e..8e20b521 100644 --- a/crates/pg_lsp_new/Cargo.toml +++ b/crates/pg_lsp_new/Cargo.toml @@ -16,10 +16,10 @@ anyhow = { workspace = true } biome_deserialize = { workspace = true } futures = "0.3.31" pg_analyse = { workspace = true } +pg_completions = { workspace = true } pg_configuration = { workspace = true } pg_console = { workspace = true } pg_diagnostics = { workspace = true } -pg_completions = { workspace = true } pg_fs = { workspace = true } pg_lsp_converters = { workspace = true } pg_text_edit = { workspace = true } diff --git a/crates/pg_workspace_new/Cargo.toml b/crates/pg_workspace_new/Cargo.toml index 9da718cf..c48bb6e2 100644 --- a/crates/pg_workspace_new/Cargo.toml +++ b/crates/pg_workspace_new/Cargo.toml @@ -18,10 +18,10 @@ futures = "0.3.31" ignore = { workspace = true } pg_analyse = { workspace = true, features = ["serde"] } pg_analyser = { workspace = true } +pg_completions = { workspace = true } pg_configuration = { workspace = true } pg_console = { workspace = true } pg_diagnostics = { workspace = true } -pg_completions = { workspace = true } pg_fs = { workspace = true, features = ["serde"] } pg_query_ext = { workspace = true } pg_schema_cache = { workspace = true } diff --git a/crates/pg_workspace_new/src/workspace/server.rs b/crates/pg_workspace_new/src/workspace/server.rs index 37ec4897..3bd93280 100644 --- a/crates/pg_workspace_new/src/workspace/server.rs +++ b/crates/pg_workspace_new/src/workspace/server.rs @@ -3,7 +3,7 @@ use std::{fs, future::Future, panic::RefUnwindSafe, path::Path, sync::RwLock}; use analyser::AnalyserVisitorBuilder; use change::StatementChange; use dashmap::{DashMap, DashSet}; -use document::{Document, StatementRef}; +use document::{Document, Statement}; use pg_analyse::{AnalyserOptions, AnalysisFilter}; use pg_analyser::{Analyser, AnalyserConfig, AnalyserContext}; use pg_diagnostics::{serde::Diagnostic as SDiagnostic, Diagnostic, DiagnosticExt, Severity}; @@ -12,7 +12,6 @@ use pg_query::PgQueryStore; use pg_schema_cache::SchemaCache; use sqlx::PgPool; use std::sync::LazyLock; -use store::Store; use tokio::runtime::Runtime; use tracing::info; use tree_sitter::TreeSitterStore; @@ -33,7 +32,6 @@ mod analyser; mod change; mod document; mod pg_query; -mod store; mod tree_sitter; /// Simple helper to manage the db connection and the associated connection string @@ -78,7 +76,7 @@ pub(super) struct WorkspaceServer { pg_query: PgQueryStore, /// Stores the statements that have changed since the last analysis - changed_stmts: DashSet, + changed_stmts: DashSet, connection: RwLock, } @@ -220,10 +218,9 @@ impl Workspace for WorkspaceServer { let doc = Document::new(params.path.clone(), params.content, params.version); - doc.statements.iter().for_each(|s| { - let stmt = doc.statement(s); - self.tree_sitter.add_statement(&stmt); - self.pg_query.add_statement(&stmt); + doc.iter_statements_with_text().for_each(|(stmt, content)| { + self.tree_sitter.add_statement(&stmt, content); + self.pg_query.add_statement(&stmt, content); }); self.documents.insert(params.path, doc); @@ -238,8 +235,9 @@ impl Workspace for WorkspaceServer { .remove(¶ms.path) .ok_or_else(WorkspaceError::not_found)?; - for stmt in doc.statement_refs() { + for stmt in doc.iter_statements() { self.tree_sitter.remove_statement(&stmt); + self.pg_query.remove_statement(&stmt); } Ok(()) @@ -260,12 +258,12 @@ impl Workspace for WorkspaceServer { for c in &doc.apply_file_change(¶ms) { match c { - StatementChange::Added(s) => { - tracing::info!("Adding statement: {:?}", s); - self.tree_sitter.add_statement(s); - self.pg_query.add_statement(s); + StatementChange::Added(added) => { + tracing::info!("Adding statement: {:?}", added); + self.tree_sitter.add_statement(&added.stmt, &added.text); + self.pg_query.add_statement(&added.stmt, &added.text); - self.changed_stmts.insert(s.ref_.to_owned()); + self.changed_stmts.insert(added.stmt.clone()); } StatementChange::Deleted(s) => { tracing::info!("Deleting statement: {:?}", s); @@ -279,8 +277,8 @@ impl Workspace for WorkspaceServer { self.tree_sitter.modify_statement(s); self.pg_query.modify_statement(s); - self.changed_stmts.remove(&s.old.ref_); - self.changed_stmts.insert(s.new_ref.to_owned()); + self.changed_stmts.remove(&s.old_stmt); + self.changed_stmts.insert(s.new_stmt.clone()); } } } @@ -339,13 +337,12 @@ impl Workspace for WorkspaceServer { }); let diagnostics: Vec = doc - .statement_refs_with_ranges() - .iter() + .iter_statements_with_range() .flat_map(|(stmt, r)| { let mut stmt_diagnostics = vec![]; - stmt_diagnostics.extend(self.pg_query.diagnostics(stmt)); - let ast = self.pg_query.load(stmt); + stmt_diagnostics.extend(self.pg_query.get_diagnostics(&stmt)); + let ast = self.pg_query.get_ast(&stmt); if let Some(ast) = ast { stmt_diagnostics.extend( analyser @@ -413,20 +410,19 @@ impl Workspace for WorkspaceServer { ¶ms.position ); - let statement = match doc.statement_at_offset(¶ms.position) { + let (statement, stmt_range, text) = match doc + .iter_statements_with_text_and_range() + .find(|(_, r, _)| r.contains(params.position)) + { Some(s) => s, None => return Ok(pg_completions::CompletionResult::default()), }; // `offset` is the position in the document, // but we need the position within the *statement*. - let stmt_range = doc - .statement_range(&statement.ref_) - .expect("Range of statement should be defined."); let position = params.position - stmt_range.start(); - let tree = self.tree_sitter.load(&statement.ref_); - let text = statement.text; + let tree = self.tree_sitter.get_parse_tree(&statement); tracing::debug!("Found the statement. We're looking for position {:?}. Statement Range {:?} to {:?}. Statement: {}", position, stmt_range.start(), stmt_range.end(), text); @@ -439,7 +435,7 @@ impl Workspace for WorkspaceServer { position, schema: &schema_cache, tree: tree.as_deref(), - text, + text: text.to_string(), }); Ok(result) diff --git a/crates/pg_workspace_new/src/workspace/server/change.rs b/crates/pg_workspace_new/src/workspace/server/change.rs index cf718d89..6267ef96 100644 --- a/crates/pg_workspace_new/src/workspace/server/change.rs +++ b/crates/pg_workspace_new/src/workspace/server/change.rs @@ -3,45 +3,54 @@ use text_size::{TextLen, TextRange, TextSize}; use crate::workspace::{ChangeFileParams, ChangeParams}; -use super::{document::Statement, Document, StatementRef}; +use super::{Document, Statement}; #[derive(Debug, PartialEq, Eq)] pub enum StatementChange { - Added(Statement), - Deleted(StatementRef), - Modified(ChangedStatement), + Added(AddedStatement), + Deleted(Statement), + Modified(ModifiedStatement), } #[derive(Debug, PartialEq, Eq)] -pub struct ChangedStatement { - pub old: Statement, - pub new_ref: StatementRef, - - pub range: TextRange, +pub struct AddedStatement { + pub stmt: Statement, pub text: String, } -impl ChangedStatement { - pub fn new_statement(&self) -> Statement { - Statement { - ref_: self.new_ref.clone(), - text: apply_text_change(&self.old.text, Some(self.range), &self.text), - } - } +#[derive(Debug, PartialEq, Eq)] +pub struct ModifiedStatement { + pub old_stmt: Statement, + pub old_stmt_text: String, + + pub new_stmt: Statement, + pub new_stmt_text: String, + + pub change_range: TextRange, + pub change_text: String, } impl StatementChange { #[allow(dead_code)] - pub fn statement_ref(&self) -> &StatementRef { + pub fn statement(&self) -> &Statement { match self { - StatementChange::Added(stmt) => &stmt.ref_, - StatementChange::Deleted(ref_) => ref_, - StatementChange::Modified(changed) => &changed.new_ref, + StatementChange::Added(stmt) => &stmt.stmt, + StatementChange::Deleted(stmt) => stmt, + StatementChange::Modified(changed) => &changed.new_stmt, } } } +struct Affected { + affected_range: TextRange, + affected_indices: Vec, + prev_index: Option, + next_index: Option, + full_affected_range: TextRange, +} + impl Document { + /// Applies a file change to the document and returns the affected statements pub fn apply_file_change(&mut self, change: &ChangeFileParams) -> Vec { let changes = change .changes @@ -54,267 +63,273 @@ impl Document { changes } - fn apply_change(&mut self, change: &ChangeParams) -> Vec { - self.debug_statements(); + /// Applies a full change to the document and returns the affected statements + fn apply_full_change(&mut self, text: &str) -> Vec { + let mut changes = Vec::new(); - let mut changed: Vec = Vec::with_capacity(self.statements.len()); + changes.extend(self.positions.drain(..).map(|(id, _)| { + StatementChange::Deleted(Statement { + id, + path: self.path.clone(), + }) + })); - tracing::info!("applying change: {:?}", change); + self.content = text.to_string(); - if change.range.is_none() { - // apply full text change and return early - changed.extend( - self.statements - .drain(..) - .map(|(id, _)| { - StatementChange::Deleted(StatementRef { - id, + changes.extend( + pg_statement_splitter::split(&self.content) + .ranges + .iter() + .map(|range| { + let id = self.id_generator.next(); + let text = self.content[*range].to_string(); + self.positions.push((id, *range)); + + StatementChange::Added(AddedStatement { + stmt: Statement { path: self.path.clone(), - }) + id, + }, + text, }) - .collect::>(), - ); + }), + ); - self.content = change.text.clone(); + changes + } - for (id, range) in pg_statement_splitter::split(&self.content) - .ranges - .iter() - .map(|r| (self.id_generator.next(), *r)) - { - self.statements.push((id, range)); - changed.push(StatementChange::Added(Statement { - ref_: StatementRef { - path: self.path.clone(), - id, - }, - text: self.content[range].to_string(), - })) - } + fn insert_statement(&mut self, range: TextRange) -> usize { + let pos = self + .positions + .binary_search_by(|(_, r)| r.start().cmp(&range.start())) + .unwrap_err(); + + let new_id = self.id_generator.next(); + self.positions.insert(pos, (new_id, range)); - return changed; + new_id + } + + /// Returns all relevant details about the change and its effects on the current state of the document. + /// - The affected range is the full range of the change, including the range of all statements that intersect with the change + /// - All indices of affected statement positions + /// - The index of the first statement position before the change, if any + /// - The index of the first statement position after the change, if any + /// - the full affected range includng the prev and next statement + fn get_affected( + &self, + change_range: TextRange, + content_size: TextSize, + diff_size: TextSize, + is_addition: bool, + ) -> Affected { + let mut start = change_range.start(); + let mut end = change_range.end().min(content_size); + + let mut affected_indices = Vec::new(); + let mut prev_index = None; + let mut next_index = None; + + for (index, (_, pos_range)) in self.positions.iter().enumerate() { + if pos_range.intersect(change_range).is_some() { + affected_indices.push(index); + start = start.min(pos_range.start()); + end = end.max(pos_range.end()); + } else if pos_range.end() <= change_range.start() { + prev_index = Some(index); + } else if pos_range.start() >= change_range.end() && next_index.is_none() { + next_index = Some(index); + break; + } } - // no matter where the change is, we can never be sure if its a modification or a deletion/addition - // e.g. if a statement is "select 1", and the change is "select 2; select 2", its an addition even though its in the middle of the statement. - // hence we only have three "real" cases: - // 1. the change touches no statement at all (addition) - // 2. the change touches exactly one statement AND splitting the statement results in just - // one statement (modification) - // 3. the change touches more than one statement (addition/deletion) + let start_incl = prev_index + .map(|i| self.positions[i].1.start()) + .unwrap_or(start); + let end_incl = next_index + .map(|i| self.positions[i].1.end()) + .unwrap_or_else(|| end); - let new_content = change.apply_to_text(&self.content); + let end_incl = if is_addition { + end_incl.add(diff_size) + } else { + end_incl.sub(diff_size) + }; - let mut affected = vec![]; + let end = if is_addition { + end.add(diff_size) + } else { + end.sub(diff_size) + }; - for (idx, (id, r)) in self.statements.iter_mut().enumerate() { - if r.intersect(change.range.unwrap()).is_some() { - affected.push((idx, (*id, *r))); - } else if r.start() > change.range.unwrap().end() { - if change.is_addition() { - *r += change.diff_size(); - } else if change.is_deletion() { - *r -= change.diff_size(); - } - } + Affected { + affected_range: TextRange::new(start, end.min(content_size)), + affected_indices, + prev_index, + next_index, + full_affected_range: TextRange::new(start_incl, end_incl.min(content_size)), } + } - // special case: if no statement is affected, the affected range is between the prev and - // the next statement - if affected.is_empty() { - // since we do not now whether the change should be part of the previous statement, we - // will take the range form the start of the previous statement to the start of the - // next statement. if the resulting split has length one, we will modify it instead. - - let (from_stmt_index, from_stmt_range) = self - .statements - .iter() - .enumerate() - .rev() - .find(|(_, (_, r))| r.start() <= change.range.unwrap().start()) - .map(|(i, (_, r))| (i, *r)) - .unwrap_or((0, TextRange::empty(TextSize::new(0)))); + fn move_ranges(&mut self, offset: TextSize, diff_size: TextSize, is_addition: bool) { + self.positions + .iter_mut() + .skip_while(|(_, r)| offset > r.start()) + .for_each(|(_, range)| { + let new_range = if is_addition { + range.add(diff_size) + } else { + range.sub(diff_size) + }; + *range = new_range; + }); + } - let start = from_stmt_range.start(); + /// Applies a single change to the document and returns the affected statements + fn apply_change(&mut self, change: &ChangeParams) -> Vec { + tracing::info!("applying change: {:?}", change); - let end = self - .statements - .iter() - .find(|(_, r)| r.start() >= change.range.unwrap().end()) - .map(|(_, r)| r.start()) - .unwrap_or_else(|| self.content.text_len()); + // if range is none, we have a full change + if change.range.is_none() { + return self.apply_full_change(&change.text); + } - let affected = new_content - .as_str() - .get(usize::from(start)..usize::from(end)) - .unwrap(); + // i spent a relatively large amount of time thinking about how to handle range changes + // properly. there are quite a few edge cases to consider. I eventually skipped most of + // them, because the complexity is not worth the return for now. we might want to revisit + // this later though. - let new_ranges = pg_statement_splitter::split(affected).ranges; + let mut changed: Vec = Vec::with_capacity(self.positions.len()); - if new_ranges.len() == 1 && !from_stmt_range.is_empty() { - if !change.is_whitespace() { - // modify previous statement - let new_stmt = &new_ranges[0]; + let change_range = change.range.unwrap(); + let new_content = change.apply_to_text(&self.content); - let new_id = self.id_generator.next(); - let old_stmt = self.statement(&self.statements[from_stmt_index]); - self.statements[from_stmt_index] = (new_id, new_stmt.add(start)); + // we first need to determine the affected range and all affected statements, as well as + // the index of the prev and the next statement, if any. The full affected range is the + // affected range expanded to the start of the previous statement and the end of the next + let Affected { + affected_range, + affected_indices, + prev_index, + next_index, + full_affected_range, + } = self.get_affected( + change_range, + new_content.text_len(), + change.diff_size(), + change.is_addition(), + ); - println!("change prev"); - let changed_stmt = ChangedStatement { - old: old_stmt, - new_ref: self.statement_ref(&self.statements[from_stmt_index]), - // change must be relative to statement - // TODO: range and text must be filled up with whitespaces - range: change.range.unwrap().sub(from_stmt_range.start()), - text: change.text.clone(), - }; + // 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(); - changed.push(StatementChange::Modified(changed_stmt)); - } - } else { - // add new statements - for range in new_ranges { - let doc_range = range + start; - match self - .statements - .binary_search_by(|(_, r)| r.start().cmp(&doc_range.start())) - { - Ok(_) => {} - Err(pos) => { - let new_id = self.id_generator.next(); - self.statements.insert(pos, (new_id, doc_range)); - changed.push(StatementChange::Added(Statement { - ref_: StatementRef { - path: self.path.clone(), - id: new_id, - }, - text: new_content[doc_range].to_string(), - })); - } - } - } - } - } else { - // get full affected range - let mut start = change.range.unwrap().start(); - let mut end = change.range.unwrap().end(); + let new_ranges = pg_statement_splitter::split(changed_content).ranges; - if end > new_content.text_len() { - end = new_content.text_len(); - } + if new_ranges.len() == 1 { + if change.is_whitespace() { + self.move_ranges( + affected_range.end(), + change.diff_size(), + change.is_addition(), + ); - for (_, (_, r)) in &affected { - // adjust the range to the new content - let adjusted_start = if r.start() >= change.range.unwrap().end() { - r.start() + change.diff_size() - } else { - r.start() - }; - let adjusted_end = if r.end() >= change.range.unwrap().end() { - if change.is_addition() { - r.end() + change.diff_size() - } else { - r.end() - change.diff_size() - } - } else { - r.end() - }; + self.content = new_content; - if adjusted_start < start { - start = adjusted_start; - } - if adjusted_end > end && adjusted_end <= new_content.text_len() { - end = adjusted_end; + return changed; } - } - let changed_content = new_content - .as_str() - .get(usize::from(start)..usize::from(end)) - .unwrap(); + let affected_idx = affected_indices[0]; + let new_range = new_ranges[0].add(affected_range.start()); + let (old_id, old_range) = self.positions[affected_idx]; - let ranges = pg_statement_splitter::split(changed_content).ranges; + // move all statements after the afffected range + self.move_ranges(old_range.end(), change.diff_size(), change.is_addition()); - if affected.len() == 1 && ranges.len() == 1 { - if !change.is_whitespace() { - // from one to one, so we do a modification - let stmt = &affected[0]; - let new_stmt = &ranges[0]; + let new_id = self.id_generator.next(); + self.positions[affected_idx] = (new_id, new_range); - let new_id = self.id_generator.next(); - self.statements[stmt.0] = (new_id, new_stmt.add(start)); + changed.push(StatementChange::Modified(ModifiedStatement { + old_stmt: Statement { + id: old_id, + path: self.path.clone(), + }, + old_stmt_text: self.content[old_range].to_string(), - println!("change one to one"); - let changed_stmt = ChangedStatement { - old: self.statement(&stmt.1), - new_ref: self.statement_ref(&self.statements[stmt.0]), - // change must be relative to statement - range: change.range.unwrap().sub(stmt.1 .1.start()), - text: change.text.clone(), - }; + new_stmt: Statement { + id: new_id, + path: self.path.clone(), + }, + new_stmt_text: changed_content[new_ranges[0]].to_string(), + // change must be relative to the statement + change_text: change.text.clone(), + change_range, + })); - changed.push(StatementChange::Modified(changed_stmt)); - } - } else { - // delete and add new ones - for (_, (id, r)) in &affected { - changed.push(StatementChange::Deleted(self.statement_ref(&(*id, *r)))); - } + self.content = new_content; - // remove affected statements - self.statements - .retain(|(id, _)| !affected.iter().any(|(affected_id, _)| id == affected_id)); - - // add new statements - for range in ranges { - match self - .statements - .binary_search_by(|(_, r)| r.start().cmp(&range.start())) - { - Ok(_) => {} - Err(pos) => { - let new_id = self.id_generator.next(); - self.statements.insert(pos, (new_id, range)); - changed.push(StatementChange::Added(Statement { - ref_: StatementRef { - path: self.path.clone(), - id: new_id, - }, - text: new_content[range].to_string(), - })); - } - } - } + return changed; } } - self.content = new_content; - - self.debug_statements(); - - changed - } -} + // 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 new_ranges = pg_statement_splitter::split(changed_content).ranges; + + // delete and add new ones + if let Some(next_index) = next_index { + changed.push(StatementChange::Deleted(Statement { + id: self.positions[next_index].0, + path: self.path.clone(), + })); + self.positions.remove(next_index); + } + for idx in affected_indices.iter().rev() { + changed.push(StatementChange::Deleted(Statement { + id: self.positions[*idx].0, + path: self.path.clone(), + })); + self.positions.remove(*idx); + } + if let Some(prev_index) = prev_index { + changed.push(StatementChange::Deleted(Statement { + id: self.positions[prev_index].0, + path: self.path.clone(), + })); + self.positions.remove(prev_index); + } -fn apply_text_change(text: &str, range: Option, change_text: &str) -> String { - if range.is_none() { - return change_text.to_string(); - } + new_ranges.iter().for_each(|range| { + let actual_range = range.add(full_affected_range.start()); + let new_id = self.insert_statement(actual_range); + changed.push(StatementChange::Added(AddedStatement { + stmt: Statement { + id: new_id, + path: self.path.clone(), + }, + text: new_content[actual_range].to_string(), + })); + }); + + // move all statements after the afffected range + self.move_ranges( + full_affected_range.end(), + change.diff_size(), + change.is_addition(), + ); - let range = range.unwrap(); - let start = usize::from(range.start()); - let end = usize::from(range.end()); + self.content = new_content; - let mut new_text = String::new(); - new_text.push_str(&text[..start]); - new_text.push_str(change_text); - if end < text.len() { - new_text.push_str(&text[end..]); + changed } - - new_text } impl ChangeParams { @@ -364,13 +379,30 @@ impl ChangeParams { #[cfg(test)] mod tests { - use text_size::{TextRange, TextSize}; + use super::*; + use text_size::TextRange; - use crate::workspace::{server::document::Statement, ChangeFileParams, ChangeParams}; + use crate::workspace::{ChangeFileParams, ChangeParams}; - use super::{super::StatementRef, Document, StatementChange}; use pg_fs::PgLspPath; + impl Document { + pub fn get_text(&self, idx: usize) -> String { + self.content[self.positions[idx].1.start().into()..self.positions[idx].1.end().into()] + .to_string() + } + } + + fn assert_document_integrity(d: &Document) { + let ranges = pg_statement_splitter::split(&d.content).ranges; + + assert!(ranges.len() == d.positions.len()); + + assert!(ranges + .iter() + .all(|r| { d.positions.iter().any(|(_, stmt_range)| stmt_range == r) })); + } + #[test] fn within_statements() { let path = PgLspPath::new("test.sql"); @@ -378,7 +410,7 @@ mod tests { let mut d = Document::new(PgLspPath::new("test.sql"), input.to_string(), 0); - assert_eq!(d.statements.len(), 2); + assert_eq!(d.positions.len(), 2); let change = ChangeFileParams { path: path.clone(), @@ -391,9 +423,20 @@ mod tests { let changed = d.apply_file_change(&change); - assert_eq!(changed.len(), 1); - assert!( - matches!(&changed[0], StatementChange::Added(Statement { ref_: _, text }) if text == "select 1;") + assert_eq!(changed.len(), 5); + assert_eq!( + changed + .iter() + .filter(|c| matches!(c, StatementChange::Deleted(_))) + .count(), + 2 + ); + assert_eq!( + changed + .iter() + .filter(|c| matches!(c, StatementChange::Added(_))) + .count(), + 3 ); assert_document_integrity(&d); @@ -402,11 +445,10 @@ mod tests { #[test] fn julians_sample() { let path = PgLspPath::new("test.sql"); - let input = "select\n *\nfrom\n test;\n\nselect\n\nalter table test\ndrop column id;"; + let input = "select\n *\nfrom\n test;\n\nselect\n\nalter table test\n\ndrop column id;"; let mut d = Document::new(path.clone(), input.to_string(), 0); - assert_eq!(d.statements.len(), 3); - println!("{:#?}", d.statements); + assert_eq!(d.positions.len(), 4); let change1 = ChangeFileParams { path: path.clone(), @@ -418,10 +460,16 @@ mod tests { }; let changed1 = d.apply_file_change(&change1); - println!("after change 1"); - println!("{:#?}", d.content); - println!("{:#?}", d.statements); - println!("{:#?}", changed1); + assert_eq!( + changed1.len(), + 0, + "should not emit change if its only whitespace" + ); + assert_eq!( + d.content, + "select\n *\nfrom\n test;\n\nselect \n\nalter table test\n\ndrop column id;" + ); + assert_document_integrity(&d); // problem: this creates a new statement let change2 = ChangeFileParams { @@ -434,10 +482,22 @@ mod tests { }; let changed2 = d.apply_file_change(&change2); - println!("after change 2"); - println!("{:#?}", d.content); - println!("{:#?}", d.statements); - println!("{:#?}", changed2); + assert_eq!(changed2.len(), 4); + assert_eq!( + changed2 + .iter() + .filter(|c| matches!(c, StatementChange::Deleted(_))) + .count(), + 2 + ); + assert_eq!( + changed2 + .iter() + .filter(|c| matches!(c, StatementChange::Added(_))) + .count(), + 2 + ); + assert_document_integrity(&d); let change3 = ChangeFileParams { path: path.clone(), @@ -449,28 +509,21 @@ mod tests { }; let changed3 = d.apply_file_change(&change3); - println!("after change 3"); - println!("{:#?}", d.content); - println!("{:#?}", d.statements); - println!("{:#?}", changed3); - - // - // assert_eq!(changed.len(), 4); - // assert!(matches!( - // changed[0], - // StatementChange::Deleted(StatementRef { id: 0, .. }) - // )); - // assert!(matches!( - // changed[1], - // StatementChange::Deleted(StatementRef { id: 1, .. }) - // )); - // assert!( - // matches!(&changed[2], StatementChange::Added(Statement { ref_: _, text }) if text == "select id,test from users;") - // ); - // assert!( - // matches!(&changed[3], StatementChange::Added(Statement { ref_: _, text }) if text == "select 1;") - // ); - // + assert_eq!(changed3.len(), 1); + assert!(matches!(&changed3[0], StatementChange::Modified(_))); + assert_eq!( + d.content, + "select\n *\nfrom\n test;\n\nselect \n\nalter table test\n\ndrop column id;" + ); + match &changed3[0] { + StatementChange::Modified(changed) => { + assert_eq!(changed.old_stmt_text, "select ;"); + assert_eq!(changed.new_stmt_text, "select"); + assert_eq!(changed.change_text, ""); + assert_eq!(changed.change_range, TextRange::new(32.into(), 33.into())); + } + _ => panic!("expected modified statement"), + } assert_document_integrity(&d); } @@ -481,7 +534,7 @@ mod tests { let mut d = Document::new(PgLspPath::new("test.sql"), input.to_string(), 0); - assert_eq!(d.statements.len(), 2); + assert_eq!(d.positions.len(), 2); let change = ChangeFileParams { path: path.clone(), @@ -497,40 +550,30 @@ mod tests { assert_eq!(changed.len(), 4); assert!(matches!( changed[0], - StatementChange::Deleted(StatementRef { id: 0, .. }) + StatementChange::Deleted(Statement { id: 1, .. }) )); assert!(matches!( changed[1], - StatementChange::Deleted(StatementRef { id: 1, .. }) + StatementChange::Deleted(Statement { id: 0, .. }) )); assert!( - matches!(&changed[2], StatementChange::Added(Statement { ref_: _, text }) if text == "select id,test from users;") + matches!(&changed[2], StatementChange::Added(AddedStatement { stmt: _, text }) if text == "select id,test from users;") ); assert!( - matches!(&changed[3], StatementChange::Added(Statement { ref_: _, text }) if text == "select 1;") + matches!(&changed[3], StatementChange::Added(AddedStatement { stmt: _, text }) if text == "select 1;") ); assert_document_integrity(&d); } - fn assert_document_integrity(d: &Document) { - let ranges = pg_statement_splitter::split(&d.content).ranges; - - assert!(ranges.len() == d.statements.len()); - - assert!(ranges - .iter() - .all(|r| { d.statements.iter().any(|(_, stmt_range)| stmt_range == r) })); - } - #[test] - fn append_to_statement() { + fn append_whitespace_to_statement() { let path = PgLspPath::new("test.sql"); let input = "select id"; let mut d = Document::new(PgLspPath::new("test.sql"), input.to_string(), 0); - assert_eq!(d.statements.len(), 1); + assert_eq!(d.positions.len(), 1); let change = ChangeFileParams { path: path.clone(), @@ -543,8 +586,7 @@ mod tests { let changed = d.apply_file_change(&change); - assert_eq!(changed.len(), 1); - matches!(changed[0], StatementChange::Modified(_)); + assert_eq!(changed.len(), 0); assert_document_integrity(&d); } @@ -556,7 +598,7 @@ mod tests { let mut d = Document::new(PgLspPath::new("test.sql"), input.to_string(), 0); - assert_eq!(d.statements.len(), 2); + assert_eq!(d.positions.len(), 2); let change = ChangeFileParams { path: path.clone(), @@ -573,22 +615,22 @@ mod tests { assert_eq!( changed[0], - StatementChange::Deleted(StatementRef { + StatementChange::Deleted(Statement { path: path.clone(), - id: 0 + id: 1 }) ); assert_eq!( changed[1], - StatementChange::Deleted(StatementRef { + StatementChange::Deleted(Statement { path: path.clone(), - id: 1 + id: 0 }) ); assert_eq!( changed[2], - StatementChange::Added(Statement { - ref_: StatementRef { + StatementChange::Added(AddedStatement { + stmt: Statement { path: path.clone(), id: 2 }, @@ -597,8 +639,8 @@ mod tests { ); assert_eq!( changed[3], - StatementChange::Added(Statement { - ref_: StatementRef { + StatementChange::Added(AddedStatement { + stmt: Statement { path: path.clone(), id: 3 }, @@ -607,18 +649,6 @@ mod tests { ); assert_eq!("select id,test from users\nselect 1;", d.content); - assert_eq!(d.statements.len(), 2); - - for r in &pg_statement_splitter::split(&d.content).ranges { - assert!( - d.statements.iter().any(|x| r == &x.1), - "should have stmt with range {:#?}", - r - ); - } - - assert_eq!(d.statements[0].1, TextRange::new(0.into(), 25.into())); - assert_eq!(d.statements[1].1, TextRange::new(26.into(), 35.into())); assert_document_integrity(&d); } @@ -630,24 +660,14 @@ mod tests { let mut d = Document::new(path.clone(), input.to_string(), 1); - assert_eq!(d.statements.len(), 2); - - let stmt_1_range = d.statements[0]; - let stmt_2_range = d.statements[1]; - - let update_text = " contacts;"; - - let update_range = TextRange::new(14.into(), 14.into()); - - let update_text_len = u32::try_from(update_text.chars().count()).unwrap(); - let update_addition = update_text_len - u32::from(update_range.len()); + assert_eq!(d.positions.len(), 2); let change = ChangeFileParams { path: path.clone(), version: 2, changes: vec![ChangeParams { - text: update_text.to_string(), - range: Some(update_range), + text: " contacts;".to_string(), + range: Some(TextRange::new(14.into(), 14.into())), }], }; @@ -661,20 +681,6 @@ mod tests { "select id from contacts;\nselect * from contacts;", d.content ); - assert_eq!(d.statements.len(), 2); - assert_eq!(d.statements[0].1.start(), stmt_1_range.1.start()); - assert_eq!( - u32::from(d.statements[0].1.end()), - u32::from(stmt_1_range.1.end()) + update_addition - ); - assert_eq!( - u32::from(d.statements[1].1.start()), - u32::from(stmt_2_range.1.start()) + update_addition - ); - assert_eq!( - u32::from(d.statements[1].1.end()), - u32::from(stmt_2_range.1.end()) + update_addition - ); assert_document_integrity(&d); } @@ -696,20 +702,14 @@ mod tests { doc.apply_file_change(&change); + assert_eq!(doc.get_text(0), "select 1;".to_string()); + assert_eq!(doc.get_text(1), "select 2;".to_string()); assert_eq!( - doc.statement(&doc.statements[0]).text, - "select 1;".to_string() - ); - assert_eq!( - doc.statement(&doc.statements[1]).text, - "select 2;".to_string() - ); - assert_eq!( - doc.statements[0].1, + doc.positions[0].1, TextRange::new(TextSize::new(0), TextSize::new(9)) ); assert_eq!( - doc.statements[1].1, + doc.positions[1].1, TextRange::new(TextSize::new(10), TextSize::new(19)) ); @@ -725,21 +725,15 @@ mod tests { doc.apply_file_change(&change_2); assert_eq!(doc.content, "select ;\nselect 2;"); - assert_eq!(doc.statements.len(), 2); - assert_eq!( - doc.statement(&doc.statements[0]).text, - "select ;".to_string() - ); - assert_eq!( - doc.statement(&doc.statements[1]).text, - "select 2;".to_string() - ); + assert_eq!(doc.positions.len(), 2); + assert_eq!(doc.get_text(0), "select ;".to_string()); + assert_eq!(doc.get_text(1), "select 2;".to_string()); assert_eq!( - doc.statements[0].1, + doc.positions[0].1, TextRange::new(TextSize::new(0), TextSize::new(8)) ); assert_eq!( - doc.statements[1].1, + doc.positions[1].1, TextRange::new(TextSize::new(9), TextSize::new(18)) ); @@ -755,13 +749,13 @@ mod tests { doc.apply_file_change(&change_3); assert_eq!(doc.content, "select !;\nselect 2;"); - assert_eq!(doc.statements.len(), 2); + assert_eq!(doc.positions.len(), 2); assert_eq!( - doc.statements[0].1, + doc.positions[0].1, TextRange::new(TextSize::new(0), TextSize::new(9)) ); assert_eq!( - doc.statements[1].1, + doc.positions[1].1, TextRange::new(TextSize::new(10), TextSize::new(19)) ); @@ -777,13 +771,13 @@ mod tests { doc.apply_file_change(&change_4); assert_eq!(doc.content, "select ;\nselect 2;"); - assert_eq!(doc.statements.len(), 2); + assert_eq!(doc.positions.len(), 2); assert_eq!( - doc.statements[0].1, + doc.positions[0].1, TextRange::new(TextSize::new(0), TextSize::new(8)) ); assert_eq!( - doc.statements[1].1, + doc.positions[1].1, TextRange::new(TextSize::new(9), TextSize::new(18)) ); @@ -799,13 +793,13 @@ mod tests { doc.apply_file_change(&change_5); assert_eq!(doc.content, "select 1;\nselect 2;"); - assert_eq!(doc.statements.len(), 2); + assert_eq!(doc.positions.len(), 2); assert_eq!( - doc.statements[0].1, + doc.positions[0].1, TextRange::new(TextSize::new(0), TextSize::new(9)) ); assert_eq!( - doc.statements[1].1, + doc.positions[1].1, TextRange::new(TextSize::new(10), TextSize::new(19)) ); @@ -819,10 +813,10 @@ mod tests { let mut doc = Document::new(path.clone(), input.to_string(), 0); - assert_eq!(doc.statements.len(), 2); + assert_eq!(doc.positions.len(), 2); - let stmt_1_range = doc.statements[0]; - let stmt_2_range = doc.statements[1]; + let stmt_1_range = doc.positions[0]; + let stmt_2_range = doc.positions[1]; let update_text = ",test"; @@ -846,18 +840,18 @@ mod tests { "select id,test from users;\nselect * from contacts;", doc.content ); - assert_eq!(doc.statements.len(), 2); - assert_eq!(doc.statements[0].1.start(), stmt_1_range.1.start()); + assert_eq!(doc.positions.len(), 2); + assert_eq!(doc.positions[0].1.start(), stmt_1_range.1.start()); assert_eq!( - u32::from(doc.statements[0].1.end()), + u32::from(doc.positions[0].1.end()), u32::from(stmt_1_range.1.end()) + update_addition ); assert_eq!( - u32::from(doc.statements[1].1.start()), + u32::from(doc.positions[1].1.start()), u32::from(stmt_2_range.1.start()) + update_addition ); assert_eq!( - u32::from(doc.statements[1].1.end()), + u32::from(doc.positions[1].1.end()), u32::from(stmt_2_range.1.end()) + update_addition ); diff --git a/crates/pg_workspace_new/src/workspace/server/document.rs b/crates/pg_workspace_new/src/workspace/server/document.rs index 4422daad..7b080332 100644 --- a/crates/pg_workspace_new/src/workspace/server/document.rs +++ b/crates/pg_workspace_new/src/workspace/server/document.rs @@ -3,30 +3,23 @@ use text_size::{TextRange, TextSize}; /// Global unique identifier for a statement #[derive(Debug, Hash, Eq, PartialEq, Clone)] -pub(crate) struct StatementRef { +pub(crate) struct Statement { /// Path of the document pub(crate) path: PgLspPath, /// Unique id within the document pub(crate) id: StatementId, } -/// Represenation of a statement -#[derive(Debug, PartialEq, Eq)] -pub(crate) struct Statement { - pub(crate) ref_: StatementRef, - pub(crate) text: String, -} - pub type StatementId = usize; -type StatementPosition = (StatementId, TextRange); +type StatementPos = (StatementId, TextRange); pub(crate) struct Document { pub(crate) path: PgLspPath, pub(crate) content: String, pub(crate) version: i32, /// List of statements sorted by range.start() - pub(super) statements: Vec, + pub(super) positions: Vec, pub(super) id_generator: IdGenerator, } @@ -35,7 +28,7 @@ impl Document { pub(crate) fn new(path: PgLspPath, content: String, version: i32) -> Self { let mut id_generator = IdGenerator::new(); - let statements: Vec = pg_statement_splitter::split(&content) + let ranges: Vec = pg_statement_splitter::split(&content) .ranges .iter() .map(|r| (id_generator.next(), *r)) @@ -43,7 +36,7 @@ impl Document { Self { path, - statements, + positions: ranges, content, version, @@ -51,103 +44,48 @@ impl Document { } } - pub fn debug_statements(&self) { - for (id, range) in self.statements.iter() { - tracing::info!( - "Document::debug_statements: statement: id: {}, range: {:?}, text: {:?}", - id, - range, - &self.content[*range] - ); - } - } - - #[allow(dead_code)] - pub fn get_statements(&self) -> &[StatementPosition] { - &self.statements - } - - pub fn statement_refs(&self) -> Vec { - self.statements - .iter() - .map(|inner_ref| self.statement_ref(inner_ref)) - .collect() - } - - pub fn statement_refs_with_ranges(&self) -> Vec<(StatementRef, TextRange)> { - self.statements - .iter() - .map(|inner_ref| (self.statement_ref(inner_ref), inner_ref.1)) - .collect() - } - - #[allow(dead_code)] - /// Returns the statement ref at the given offset - pub fn statement_ref_at_offset(&self, offset: &TextSize) -> Option { - self.statements.iter().find_map(|r| { - if r.1.contains(*offset) { - Some(self.statement_ref(r)) - } else { - None - } + pub fn iter_statements(&self) -> impl Iterator + '_ { + self.positions.iter().map(move |(id, _)| Statement { + id: *id, + path: self.path.clone(), }) } - #[allow(dead_code)] - /// Returns the statement refs at the given range - pub fn statement_refs_at_range(&self, range: &TextRange) -> Vec { - self.statements - .iter() - .filter(|(_, r)| { - range.contains_range(r.to_owned().to_owned()) || r.contains_range(range.to_owned()) - }) - .map(|x| self.statement_ref(x)) - .collect() - } - - #[allow(dead_code)] - /// Returns the statement at the given offset - pub fn statement_at_offset(&self, offset: &TextSize) -> Option { - self.statements.iter().find_map(|r| { - if r.1.contains(*offset) { - Some(self.statement(r)) - } else { - None - } + pub fn iter_statements_with_text(&self) -> impl Iterator + '_ { + self.positions.iter().map(move |(id, range)| { + let statement = Statement { + id: *id, + path: self.path.clone(), + }; + let text = &self.content[range.start().into()..range.end().into()]; + (statement, text) }) } - #[allow(dead_code)] - /// Returns the statements at the given range - pub fn statements_at_range(&self, range: &TextRange) -> Vec { - self.statements - .iter() - .filter(|(_, r)| { - range.contains_range(r.to_owned().to_owned()) || r.contains_range(range.to_owned()) - }) - .map(|x| self.statement(x)) - .collect() - } - - pub(super) fn statement_ref(&self, inner_ref: &StatementPosition) -> StatementRef { - StatementRef { - id: inner_ref.0, - path: self.path.clone(), - } - } - - pub(super) fn statement_range(&self, sref: &StatementRef) -> Option { - self.statements - .iter() - .find(|s| s.0 == sref.id) - .map(|it| it.1) + pub fn iter_statements_with_range(&self) -> impl Iterator + '_ { + self.positions.iter().map(move |(id, range)| { + let statement = Statement { + id: *id, + path: self.path.clone(), + }; + (statement, range) + }) } - pub(super) fn statement(&self, inner_ref: &StatementPosition) -> Statement { - Statement { - ref_: self.statement_ref(inner_ref), - text: self.content[inner_ref.1].to_string(), - } + pub fn iter_statements_with_text_and_range( + &self, + ) -> impl Iterator + '_ { + self.positions.iter().map(move |(id, range)| { + let statement = Statement { + id: *id, + path: self.path.clone(), + }; + ( + statement, + range, + &self.content[range.start().into()..range.end().into()], + ) + }) } } diff --git a/crates/pg_workspace_new/src/workspace/server/pg_query.rs b/crates/pg_workspace_new/src/workspace/server/pg_query.rs index cbdf8bf0..8986a543 100644 --- a/crates/pg_workspace_new/src/workspace/server/pg_query.rs +++ b/crates/pg_workspace_new/src/workspace/server/pg_query.rs @@ -4,15 +4,11 @@ use dashmap::DashMap; use pg_diagnostics::serde::Diagnostic as SDiagnostic; use pg_query_ext::diagnostics::*; -use super::{ - change::ChangedStatement, - document::{Statement, StatementRef}, - store::Store, -}; +use super::{change::ModifiedStatement, document::Statement}; pub struct PgQueryStore { - ast_db: DashMap>, - diagnostics: DashMap, + ast_db: DashMap>, + diagnostics: DashMap, } impl PgQueryStore { @@ -22,37 +18,33 @@ impl PgQueryStore { diagnostics: DashMap::new(), } } -} -impl Store for PgQueryStore { - fn load(&self, statement: &StatementRef) -> Option> { + pub fn get_ast(&self, statement: &Statement) -> Option> { self.ast_db.get(statement).map(|x| x.clone()) } - fn add_statement(&self, statement: &Statement) { - let r = pg_query_ext::parse(statement.text.as_str()); + pub fn add_statement(&self, statement: &Statement, content: &str) { + let r = pg_query_ext::parse(content); if let Ok(ast) = r { - self.ast_db.insert(statement.ref_.clone(), Arc::new(ast)); + self.ast_db.insert(statement.clone(), Arc::new(ast)); } else { tracing::info!("adding diagnostics"); - self.diagnostics.insert( - statement.ref_.clone(), - SyntaxDiagnostic::from(r.unwrap_err()), - ); + self.diagnostics + .insert(statement.clone(), SyntaxDiagnostic::from(r.unwrap_err())); } } - fn remove_statement(&self, statement: &StatementRef) { + pub fn remove_statement(&self, statement: &Statement) { self.ast_db.remove(statement); self.diagnostics.remove(statement); } - fn modify_statement(&self, change: &ChangedStatement) { - self.remove_statement(&change.old.ref_); - self.add_statement(&change.new_statement()); + pub fn modify_statement(&self, change: &ModifiedStatement) { + self.remove_statement(&change.old_stmt); + self.add_statement(&change.new_stmt, &change.change_text); } - fn diagnostics(&self, stmt: &StatementRef) -> Vec { + pub fn get_diagnostics(&self, stmt: &Statement) -> Vec { self.diagnostics .get(stmt) .map_or_else(Vec::new, |err| vec![SDiagnostic::new(err.value().clone())]) diff --git a/crates/pg_workspace_new/src/workspace/server/store.rs b/crates/pg_workspace_new/src/workspace/server/store.rs deleted file mode 100644 index 0891a974..00000000 --- a/crates/pg_workspace_new/src/workspace/server/store.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::sync::Arc; - -use super::{ - change::ChangedStatement, - document::{Statement, StatementRef}, -}; - -pub(crate) trait Store { - fn diagnostics(&self, _stmt: &StatementRef) -> Vec { - Vec::new() - } - - #[allow(dead_code)] - fn load(&self, _stmt: &StatementRef) -> Option> { - None - } - - fn add_statement(&self, _stmt: &Statement) {} - - fn remove_statement(&self, _stmt: &StatementRef) {} - - fn modify_statement(&self, _change: &ChangedStatement) {} -} diff --git a/crates/pg_workspace_new/src/workspace/server/tree_sitter.rs b/crates/pg_workspace_new/src/workspace/server/tree_sitter.rs index e0e8f120..ad4eb0d6 100644 --- a/crates/pg_workspace_new/src/workspace/server/tree_sitter.rs +++ b/crates/pg_workspace_new/src/workspace/server/tree_sitter.rs @@ -3,14 +3,10 @@ use std::sync::{Arc, RwLock}; use dashmap::DashMap; use tree_sitter::InputEdit; -use super::{ - change::ChangedStatement, - document::{Statement, StatementRef}, - store::Store, -}; +use super::{change::ModifiedStatement, document::Statement}; pub struct TreeSitterStore { - db: DashMap>, + db: DashMap>, parser: RwLock, } @@ -27,30 +23,28 @@ impl TreeSitterStore { parser: RwLock::new(parser), } } -} -impl Store for TreeSitterStore { - fn load(&self, statement: &StatementRef) -> Option> { + pub fn get_parse_tree(&self, statement: &Statement) -> Option> { self.db.get(statement).map(|x| x.clone()) } - fn add_statement(&self, statement: &Statement) { + pub fn add_statement(&self, statement: &Statement, content: &str) { let mut guard = self.parser.write().expect("Error reading parser"); // todo handle error - let tree = guard.parse(&statement.text, None).unwrap(); + let tree = guard.parse(content, None).unwrap(); drop(guard); - self.db.insert(statement.ref_.clone(), Arc::new(tree)); + self.db.insert(statement.clone(), Arc::new(tree)); } - fn remove_statement(&self, statement: &StatementRef) { + pub fn remove_statement(&self, statement: &Statement) { self.db.remove(statement); } - fn modify_statement(&self, change: &ChangedStatement) { - let old = self.db.remove(&change.old.ref_); + pub fn modify_statement(&self, change: &ModifiedStatement) { + let old = self.db.remove(&change.old_stmt); if old.is_none() { - self.add_statement(&change.new_statement()); + self.add_statement(&change.new_stmt, &change.change_text); return; } @@ -59,22 +53,19 @@ impl Store for TreeSitterStore { let mut tree = old.unwrap().1.as_ref().clone(); let edit = edit_from_change( - change.old.text.as_str(), - usize::from(change.range.start()), - usize::from(change.range.end()), - change.text.as_str(), + change.old_stmt_text.as_str(), + usize::from(change.change_range.start()), + usize::from(change.change_range.end()), + change.change_text.as_str(), ); tree.edit(&edit); - let new_stmt = change.new_statement(); - let new_text = new_stmt.text.clone(); - let mut guard = self.parser.write().expect("Error reading parser"); // todo handle error self.db.insert( - new_stmt.ref_, - Arc::new(guard.parse(new_text, Some(&tree)).unwrap()), + change.new_stmt.clone(), + Arc::new(guard.parse(&change.new_stmt_text, Some(&tree)).unwrap()), ); drop(guard); } From b7d3cd531f2a6b1857df23b585d5a3aedfdd8e51 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Fri, 10 Jan 2025 12:34:05 -0100 Subject: [PATCH 3/8] fix: minor fixes --- .env | 2 +- crates/pg_query_ext/src/lib.rs | 7 +++---- crates/pg_workspace_new/src/workspace/server/pg_query.rs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.env b/.env index dda71743..88b3b55c 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/postgres +DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres diff --git a/crates/pg_query_ext/src/lib.rs b/crates/pg_query_ext/src/lib.rs index 8cbbd2d7..433f881f 100644 --- a/crates/pg_query_ext/src/lib.rs +++ b/crates/pg_query_ext/src/lib.rs @@ -26,8 +26,7 @@ pub fn parse(sql: &str) -> Result { .nodes() .iter() .find(|n| n.1 == 1) - .unwrap() - .0 - .to_enum() - }) + .map(|n| n.0.to_enum()) + .ok_or_else(|| Error::Parse("Unable to find root node".to_string())) + })? } diff --git a/crates/pg_workspace_new/src/workspace/server/pg_query.rs b/crates/pg_workspace_new/src/workspace/server/pg_query.rs index 8986a543..e9ca77eb 100644 --- a/crates/pg_workspace_new/src/workspace/server/pg_query.rs +++ b/crates/pg_workspace_new/src/workspace/server/pg_query.rs @@ -41,7 +41,7 @@ impl PgQueryStore { pub fn modify_statement(&self, change: &ModifiedStatement) { self.remove_statement(&change.old_stmt); - self.add_statement(&change.new_stmt, &change.change_text); + self.add_statement(&change.new_stmt, &change.new_stmt_text); } pub fn get_diagnostics(&self, stmt: &Statement) -> Vec { From 591a81bb5a004682884d411ad2470a810e63d8d4 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Fri, 10 Jan 2025 12:37:54 -0100 Subject: [PATCH 4/8] fix: lint --fix --- crates/pg_analyse/src/filter.rs | 10 +++++----- crates/pg_cli/src/commands/daemon.rs | 2 +- crates/pg_cli/src/commands/mod.rs | 3 +-- crates/pg_cli/src/execute/traverse.rs | 4 ++-- crates/pg_cli/src/reporter/github.rs | 2 +- crates/pg_cli/src/reporter/gitlab.rs | 6 +++--- crates/pg_cli/src/reporter/junit.rs | 4 ++-- crates/pg_cli/src/reporter/terminal.rs | 8 ++++---- crates/pg_completions/src/relevance.rs | 4 ++-- crates/pg_configuration/src/analyser/mod.rs | 2 +- crates/pg_lsp_new/src/handlers/text_document.rs | 2 +- crates/pg_statement_splitter/src/parser.rs | 4 ++-- crates/pg_syntax/src/statement_parser.rs | 2 +- crates/pg_type_resolver/src/types.rs | 4 ++-- crates/pg_workspace_new/src/dome.rs | 10 +++++----- crates/pg_workspace_new/src/matcher/pattern.rs | 9 ++++----- crates/pg_workspace_new/src/settings.rs | 4 ++-- crates/pg_workspace_new/src/workspace.rs | 2 +- .../pg_workspace_new/src/workspace/server/analyser.rs | 2 +- .../pg_workspace_new/src/workspace/server/document.rs | 2 +- 20 files changed, 42 insertions(+), 44 deletions(-) diff --git a/crates/pg_analyse/src/filter.rs b/crates/pg_analyse/src/filter.rs index 391831f3..045f5f51 100644 --- a/crates/pg_analyse/src/filter.rs +++ b/crates/pg_analyse/src/filter.rs @@ -42,7 +42,7 @@ impl<'analysis> AnalysisFilter<'analysis> { /// Return `true` if the group `G` matches this filter pub fn match_group(&self) -> bool { self.match_category::() - && self.enabled_rules.map_or(true, |enabled_rules| { + && self.enabled_rules.is_none_or(|enabled_rules| { enabled_rules.iter().any(|filter| filter.match_group::()) }) && !self @@ -54,7 +54,7 @@ impl<'analysis> AnalysisFilter<'analysis> { /// Return `true` if the rule `R` matches this filter pub fn match_rule(&self) -> bool { self.match_category::<::Category>() - && self.enabled_rules.map_or(true, |enabled_rules| { + && self.enabled_rules.is_none_or(|enabled_rules| { enabled_rules.iter().any(|filter| filter.match_rule::()) }) && !self @@ -94,13 +94,13 @@ impl<'a> RuleFilter<'a> { } } -impl<'a> Debug for RuleFilter<'a> { +impl Debug for RuleFilter<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Display::fmt(self, f) } } -impl<'a> Display for RuleFilter<'a> { +impl Display for RuleFilter<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { RuleFilter::Group(group) => { @@ -113,7 +113,7 @@ impl<'a> Display for RuleFilter<'a> { } } -impl<'a> pg_console::fmt::Display for RuleFilter<'a> { +impl pg_console::fmt::Display for RuleFilter<'_> { fn fmt(&self, fmt: &mut pg_console::fmt::Formatter) -> std::io::Result<()> { match self { RuleFilter::Group(group) => { diff --git a/crates/pg_cli/src/commands/daemon.rs b/crates/pg_cli/src/commands/daemon.rs index 714327f1..f35b6111 100644 --- a/crates/pg_cli/src/commands/daemon.rs +++ b/crates/pg_cli/src/commands/daemon.rs @@ -180,7 +180,7 @@ pub(crate) fn read_most_recent_log_file( let most_recent = fs::read_dir(biome_log_path)? .flatten() - .filter(|file| file.file_type().map_or(false, |ty| ty.is_file())) + .filter(|file| file.file_type().is_ok_and(|ty| ty.is_file())) .filter_map(|file| { match file .file_name() diff --git a/crates/pg_cli/src/commands/mod.rs b/crates/pg_cli/src/commands/mod.rs index a0934a90..9345c29b 100644 --- a/crates/pg_cli/src/commands/mod.rs +++ b/crates/pg_cli/src/commands/mod.rs @@ -229,8 +229,7 @@ impl PgLspCommand { } pub fn is_verbose(&self) -> bool { - self.cli_options() - .map_or(false, |cli_options| cli_options.verbose) + self.cli_options().is_some_and(|cli_options| cli_options.verbose) } pub fn log_level(&self) -> LoggingLevel { diff --git a/crates/pg_cli/src/execute/traverse.rs b/crates/pg_cli/src/execute/traverse.rs index 934edc40..b67e9f3d 100644 --- a/crates/pg_cli/src/execute/traverse.rs +++ b/crates/pg_cli/src/execute/traverse.rs @@ -413,7 +413,7 @@ pub(crate) struct TraversalOptions<'ctx, 'app> { pub(crate) evaluated_paths: RwLock>, } -impl<'ctx, 'app> TraversalOptions<'ctx, 'app> { +impl TraversalOptions<'_, '_> { pub(crate) fn increment_changed(&self, path: &PgLspPath) { self.changed.fetch_add(1, Ordering::Relaxed); self.evaluated_paths @@ -441,7 +441,7 @@ impl<'ctx, 'app> TraversalOptions<'ctx, 'app> { } } -impl<'ctx, 'app> TraversalContext for TraversalOptions<'ctx, 'app> { +impl TraversalContext for TraversalOptions<'_, '_> { fn interner(&self) -> &PathInterner { &self.interner } diff --git a/crates/pg_cli/src/reporter/github.rs b/crates/pg_cli/src/reporter/github.rs index 783ed758..6b1588b1 100644 --- a/crates/pg_cli/src/reporter/github.rs +++ b/crates/pg_cli/src/reporter/github.rs @@ -16,7 +16,7 @@ impl Reporter for GithubReporter { } pub(crate) struct GithubReporterVisitor<'a>(pub(crate) &'a mut dyn Console); -impl<'a> ReporterVisitor for GithubReporterVisitor<'a> { +impl ReporterVisitor for GithubReporterVisitor<'_> { fn report_summary( &mut self, _execution: &Execution, diff --git a/crates/pg_cli/src/reporter/gitlab.rs b/crates/pg_cli/src/reporter/gitlab.rs index 2948ddb3..9be7974e 100644 --- a/crates/pg_cli/src/reporter/gitlab.rs +++ b/crates/pg_cli/src/reporter/gitlab.rs @@ -57,7 +57,7 @@ impl<'a> GitLabReporterVisitor<'a> { } } -impl<'a> ReporterVisitor for GitLabReporterVisitor<'a> { +impl ReporterVisitor for GitLabReporterVisitor<'_> { fn report_summary(&mut self, _: &Execution, _: TraversalSummary) -> std::io::Result<()> { Ok(()) } @@ -80,7 +80,7 @@ struct GitLabDiagnostics<'a>( Option<&'a Path>, ); -impl<'a> GitLabDiagnostics<'a> { +impl GitLabDiagnostics<'_> { fn attempt_to_relativize(&self, subject: &str) -> Option { let Ok(resolved) = Path::new(subject).absolutize() else { return None; @@ -116,7 +116,7 @@ impl<'a> GitLabDiagnostics<'a> { } } -impl<'a> Display for GitLabDiagnostics<'a> { +impl Display for GitLabDiagnostics<'_> { fn fmt(&self, fmt: &mut Formatter) -> std::io::Result<()> { let mut hasher = self.1.write().unwrap(); let gitlab_diagnostics: Vec<_> = self diff --git a/crates/pg_cli/src/reporter/junit.rs b/crates/pg_cli/src/reporter/junit.rs index 358b5953..c10059fc 100644 --- a/crates/pg_cli/src/reporter/junit.rs +++ b/crates/pg_cli/src/reporter/junit.rs @@ -24,7 +24,7 @@ struct JunitDiagnostic<'a> { diagnostic: &'a Error, } -impl<'a> Display for JunitDiagnostic<'a> { +impl Display for JunitDiagnostic<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.diagnostic.description(f) } @@ -39,7 +39,7 @@ impl<'a> JunitReporterVisitor<'a> { } } -impl<'a> ReporterVisitor for JunitReporterVisitor<'a> { +impl ReporterVisitor for JunitReporterVisitor<'_> { fn report_summary( &mut self, _execution: &Execution, diff --git a/crates/pg_cli/src/reporter/terminal.rs b/crates/pg_cli/src/reporter/terminal.rs index c2802a7c..5fd493a2 100644 --- a/crates/pg_cli/src/reporter/terminal.rs +++ b/crates/pg_cli/src/reporter/terminal.rs @@ -53,7 +53,7 @@ struct FixedPathsDiagnostic { pub(crate) struct ConsoleReporterVisitor<'a>(pub(crate) &'a mut dyn Console); -impl<'a> ReporterVisitor for ConsoleReporterVisitor<'a> { +impl ReporterVisitor for ConsoleReporterVisitor<'_> { fn report_summary( &mut self, execution: &Execution, @@ -132,7 +132,7 @@ impl fmt::Display for Files { struct SummaryDetail<'a>(pub(crate) &'a TraversalMode, usize); -impl<'a> fmt::Display for SummaryDetail<'a> { +impl fmt::Display for SummaryDetail<'_> { fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> { if self.1 > 0 { fmt.write_markup(markup! { @@ -147,7 +147,7 @@ impl<'a> fmt::Display for SummaryDetail<'a> { } struct SummaryTotal<'a>(&'a TraversalMode, usize, &'a Duration); -impl<'a> fmt::Display for SummaryTotal<'a> { +impl fmt::Display for SummaryTotal<'_> { fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> { let files = Files(self.1); match self.0 { @@ -165,7 +165,7 @@ pub(crate) struct ConsoleTraversalSummary<'a>( pub(crate) &'a TraversalMode, pub(crate) &'a TraversalSummary, ); -impl<'a> fmt::Display for ConsoleTraversalSummary<'a> { +impl fmt::Display for ConsoleTraversalSummary<'_> { fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> { let summary = SummaryTotal(self.0, self.1.changed + self.1.unchanged, &self.1.duration); let detail = SummaryDetail(self.0, self.1.changed); diff --git a/crates/pg_completions/src/relevance.rs b/crates/pg_completions/src/relevance.rs index 5408a8e4..07838acb 100644 --- a/crates/pg_completions/src/relevance.rs +++ b/crates/pg_completions/src/relevance.rs @@ -6,7 +6,7 @@ pub(crate) enum CompletionRelevanceData<'a> { Function(&'a pg_schema_cache::Function), } -impl<'a> CompletionRelevanceData<'a> { +impl CompletionRelevanceData<'_> { pub fn get_score(self, ctx: &CompletionContext) -> i32 { CompletionRelevance::from(self).into_score(ctx) } @@ -27,7 +27,7 @@ pub(crate) struct CompletionRelevance<'a> { data: CompletionRelevanceData<'a>, } -impl<'a> CompletionRelevance<'a> { +impl CompletionRelevance<'_> { pub fn into_score(mut self, ctx: &CompletionContext) -> i32 { self.check_matches_schema(ctx); self.check_matches_query_input(ctx); diff --git a/crates/pg_configuration/src/analyser/mod.rs b/crates/pg_configuration/src/analyser/mod.rs index 2273eff0..5dae9d19 100644 --- a/crates/pg_configuration/src/analyser/mod.rs +++ b/crates/pg_configuration/src/analyser/mod.rs @@ -362,7 +362,7 @@ impl serde::Serialize for RuleSelector { impl<'de> serde::Deserialize<'de> for RuleSelector { fn deserialize>(deserializer: D) -> Result { struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { + impl serde::de::Visitor<'_> for Visitor { type Value = RuleSelector; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("/") diff --git a/crates/pg_lsp_new/src/handlers/text_document.rs b/crates/pg_lsp_new/src/handlers/text_document.rs index 98ecfc8a..173d7e99 100644 --- a/crates/pg_lsp_new/src/handlers/text_document.rs +++ b/crates/pg_lsp_new/src/handlers/text_document.rs @@ -5,7 +5,7 @@ use pg_workspace_new::workspace::{ ChangeFileParams, ChangeParams, CloseFileParams, GetFileContentParams, OpenFileParams, }; use tower_lsp::lsp_types; -use tracing::{error, field, info}; +use tracing::{error, field}; /// Handler for `textDocument/didOpen` LSP notification #[tracing::instrument( diff --git a/crates/pg_statement_splitter/src/parser.rs b/crates/pg_statement_splitter/src/parser.rs index 33fcfaf7..25fe685e 100644 --- a/crates/pg_statement_splitter/src/parser.rs +++ b/crates/pg_statement_splitter/src/parser.rs @@ -186,6 +186,6 @@ impl Parser { } fn is_irrelevant_token(t: &Token) -> bool { - return WHITESPACE_TOKENS.contains(&t.kind) - && (t.kind != SyntaxKind::Newline || t.text.chars().count() == 1); + WHITESPACE_TOKENS.contains(&t.kind) + && (t.kind != SyntaxKind::Newline || t.text.chars().count() == 1) } diff --git a/crates/pg_syntax/src/statement_parser.rs b/crates/pg_syntax/src/statement_parser.rs index 40fcd641..56bba2c2 100644 --- a/crates/pg_syntax/src/statement_parser.rs +++ b/crates/pg_syntax/src/statement_parser.rs @@ -435,7 +435,7 @@ struct Ancestors<'a> { current_node: NodeIndex, } -impl<'a> Iterator for Ancestors<'a> { +impl Iterator for Ancestors<'_> { type Item = NodeIndex; fn next(&mut self) -> Option { diff --git a/crates/pg_type_resolver/src/types.rs b/crates/pg_type_resolver/src/types.rs index 76a0d1e8..ee01a28d 100644 --- a/crates/pg_type_resolver/src/types.rs +++ b/crates/pg_type_resolver/src/types.rs @@ -27,7 +27,7 @@ pub fn resolve_type(node: &pg_query_ext::NodeEnum, schema_cache: &SchemaCache) - .types .iter() .filter(|t| { - types.iter().any(|i| &i == &&t.name) && t.schema == "pg_catalog" + types.iter().any(|i| i == &t.name) && t.schema == "pg_catalog" }) .map(|t| t.id) .collect(), @@ -63,7 +63,7 @@ pub fn resolve_type(node: &pg_query_ext::NodeEnum, schema_cache: &SchemaCache) - .types .iter() .filter(|t| { - (types.iter().any(|i| &i == &&t.name) + (types.iter().any(|i| i == &t.name) && t.schema == "pg_catalog") || t.enums.values.contains(&v.sval) }) diff --git a/crates/pg_workspace_new/src/dome.rs b/crates/pg_workspace_new/src/dome.rs index 8cc8ed5e..4d59837c 100644 --- a/crates/pg_workspace_new/src/dome.rs +++ b/crates/pg_workspace_new/src/dome.rs @@ -37,7 +37,7 @@ pub struct DomeIterator<'a> { impl<'a> DomeIterator<'a> { pub fn next_config(&mut self) -> Option<&'a PgLspPath> { - return if let Some(path) = self.iter.peek() { + if let Some(path) = self.iter.peek() { if path.is_config() { self.iter.next() } else { @@ -45,11 +45,11 @@ impl<'a> DomeIterator<'a> { } } else { None - }; + } } pub fn next_ignore(&mut self) -> Option<&'a PgLspPath> { - return if let Some(path) = self.iter.peek() { + if let Some(path) = self.iter.peek() { if path.is_ignore() { self.iter.next() } else { @@ -57,7 +57,7 @@ impl<'a> DomeIterator<'a> { } } else { None - }; + } } } @@ -69,4 +69,4 @@ impl<'a> Iterator for DomeIterator<'a> { } } -impl<'a> FusedIterator for DomeIterator<'a> {} +impl FusedIterator for DomeIterator<'_> {} diff --git a/crates/pg_workspace_new/src/matcher/pattern.rs b/crates/pg_workspace_new/src/matcher/pattern.rs index afbaae34..35c6c8cb 100644 --- a/crates/pg_workspace_new/src/matcher/pattern.rs +++ b/crates/pg_workspace_new/src/matcher/pattern.rs @@ -149,7 +149,7 @@ impl Pattern { tokens.push(AnyRecursiveSequence); } else { // A pattern is absolute if it starts with a path separator, eg. "/home" or "\\?\C:\Users" - let mut is_absolute = chars.first().map_or(false, |c| path::is_separator(*c)); + let mut is_absolute = chars.first().is_some_and(|c| path::is_separator(*c)); // On windows a pattern may also be absolute if it starts with a // drive letter, a colon and a separator, eg. "c:/Users" or "G:\Users" @@ -368,7 +368,7 @@ impl Pattern { /// `Pattern` using the default match options (i.e. `MatchOptions::new()`). pub fn matches_path(&self, path: &Path) -> bool { // FIXME (#9639): This needs to handle non-utf8 paths - path.to_str().map_or(false, |s| self.matches(s)) + path.to_str().is_some_and(|s| self.matches(s)) } /// Return if the given `str` matches this `Pattern` using the specified @@ -381,8 +381,7 @@ impl Pattern { /// `Pattern` using the specified match options. pub fn matches_path_with(&self, path: &Path, options: MatchOptions) -> bool { // FIXME (#9639): This needs to handle non-utf8 paths - path.to_str() - .map_or(false, |s| self.matches_with(s, options)) + path.to_str().is_some_and(|s| self.matches_with(s, options)) } /// Access the original glob pattern. @@ -551,7 +550,7 @@ fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool { true } else if !case_sensitive && a.is_ascii() && b.is_ascii() { // FIXME: work with non-ascii chars properly (issue #9084) - a.to_ascii_lowercase() == b.to_ascii_lowercase() + a.eq_ignore_ascii_case(&b) } else { a == b } diff --git a/crates/pg_workspace_new/src/settings.rs b/crates/pg_workspace_new/src/settings.rs index 1950ef8d..376a4aa6 100644 --- a/crates/pg_workspace_new/src/settings.rs +++ b/crates/pg_workspace_new/src/settings.rs @@ -48,7 +48,7 @@ impl<'a> SettingsHandle<'a> { } } -impl<'a> AsRef for SettingsHandle<'a> { +impl AsRef for SettingsHandle<'_> { fn as_ref(&self) -> &Settings { &self.inner } @@ -62,7 +62,7 @@ impl<'a> SettingsHandleMut<'a> { } } -impl<'a> AsMut for SettingsHandleMut<'a> { +impl AsMut for SettingsHandleMut<'_> { fn as_mut(&mut self) -> &mut Settings { &mut self.inner } diff --git a/crates/pg_workspace_new/src/workspace.rs b/crates/pg_workspace_new/src/workspace.rs index 8a5e2a5c..cbfd3756 100644 --- a/crates/pg_workspace_new/src/workspace.rs +++ b/crates/pg_workspace_new/src/workspace.rs @@ -286,7 +286,7 @@ impl<'app, W: Workspace + ?Sized> FileGuard<'app, W> { // } } -impl<'app, W: Workspace + ?Sized> Drop for FileGuard<'app, W> { +impl Drop for FileGuard<'_, W> { fn drop(&mut self) { self.workspace .close_file(CloseFileParams { diff --git a/crates/pg_workspace_new/src/workspace/server/analyser.rs b/crates/pg_workspace_new/src/workspace/server/analyser.rs index 7f6aa443..e0ad4c2f 100644 --- a/crates/pg_workspace_new/src/workspace/server/analyser.rs +++ b/crates/pg_workspace_new/src/workspace/server/analyser.rs @@ -99,7 +99,7 @@ impl<'a, 'b> LintVisitor<'a, 'b> { } } -impl<'a, 'b> RegistryVisitor for LintVisitor<'a, 'b> { +impl RegistryVisitor for LintVisitor<'_, '_> { fn record_category(&mut self) { if C::CATEGORY == RuleCategory::Lint { C::record_groups(self) diff --git a/crates/pg_workspace_new/src/workspace/server/document.rs b/crates/pg_workspace_new/src/workspace/server/document.rs index 7b080332..7c8dba06 100644 --- a/crates/pg_workspace_new/src/workspace/server/document.rs +++ b/crates/pg_workspace_new/src/workspace/server/document.rs @@ -1,5 +1,5 @@ use pg_fs::PgLspPath; -use text_size::{TextRange, TextSize}; +use text_size::TextRange; /// Global unique identifier for a statement #[derive(Debug, Hash, Eq, PartialEq, Clone)] From bcba5b3726fbe219fa3cee71bdbfdeebfc912018 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Fri, 10 Jan 2025 12:44:41 -0100 Subject: [PATCH 5/8] format --- crates/pg_cli/src/commands/mod.rs | 3 ++- crates/pg_type_resolver/src/types.rs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pg_cli/src/commands/mod.rs b/crates/pg_cli/src/commands/mod.rs index 9345c29b..22708491 100644 --- a/crates/pg_cli/src/commands/mod.rs +++ b/crates/pg_cli/src/commands/mod.rs @@ -229,7 +229,8 @@ impl PgLspCommand { } pub fn is_verbose(&self) -> bool { - self.cli_options().is_some_and(|cli_options| cli_options.verbose) + self.cli_options() + .is_some_and(|cli_options| cli_options.verbose) } pub fn log_level(&self) -> LoggingLevel { diff --git a/crates/pg_type_resolver/src/types.rs b/crates/pg_type_resolver/src/types.rs index ee01a28d..8e8a0694 100644 --- a/crates/pg_type_resolver/src/types.rs +++ b/crates/pg_type_resolver/src/types.rs @@ -63,8 +63,7 @@ pub fn resolve_type(node: &pg_query_ext::NodeEnum, schema_cache: &SchemaCache) - .types .iter() .filter(|t| { - (types.iter().any(|i| i == &t.name) - && t.schema == "pg_catalog") + (types.iter().any(|i| i == &t.name) && t.schema == "pg_catalog") || t.enums.values.contains(&v.sval) }) .map(|t| t.id) From 43fd7ca64fe3725d69747317e50e7e3b18ee6a9e Mon Sep 17 00:00:00 2001 From: psteinroe Date: Fri, 10 Jan 2025 12:50:49 -0100 Subject: [PATCH 6/8] format --- .env | 2 +- crates/pg_workspace_new/src/workspace/server.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 88b3b55c..dda71743 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres +DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/postgres diff --git a/crates/pg_workspace_new/src/workspace/server.rs b/crates/pg_workspace_new/src/workspace/server.rs index 3bd93280..fd7f4416 100644 --- a/crates/pg_workspace_new/src/workspace/server.rs +++ b/crates/pg_workspace_new/src/workspace/server.rs @@ -399,6 +399,12 @@ impl Workspace for WorkspaceServer { &self, params: super::CompletionParams, ) -> Result { + tracing::debug!( + "Getting completions for file {:?} at position {:?}", + ¶ms.path, + ¶ms.position + ); + let doc = self .documents .get(¶ms.path) From f9f50cc5d2ba99d20e106258e7c7f3e04eca8aa3 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Fri, 10 Jan 2025 13:42:50 -0100 Subject: [PATCH 7/8] pssssssht --- crates/pg_flags/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pg_flags/src/lib.rs b/crates/pg_flags/src/lib.rs index 21b2a15c..864ea6a6 100644 --- a/crates/pg_flags/src/lib.rs +++ b/crates/pg_flags/src/lib.rs @@ -26,15 +26,15 @@ impl PgLspEnv { fn new() -> Self { Self { pglsp_log_path: PgLspEnvVariable::new( - "BIOME_LOG_PATH", + "PGLSP_LOG_PATH", "The directory where the Daemon logs will be saved.", ), pglsp_log_prefix: PgLspEnvVariable::new( - "BIOME_LOG_PREFIX_NAME", + "PGLSP_LOG_PREFIX_NAME", "A prefix that's added to the name of the log. Default: `server.log.`", ), pglsp_config_path: PgLspEnvVariable::new( - "BIOME_CONFIG_PATH", + "PGLSP_CONFIG_PATH", "A path to the configuration file", ), } From 24493cf3f486f4569d82f6abfa2fd80041e7013e Mon Sep 17 00:00:00 2001 From: psteinroe Date: Fri, 10 Jan 2025 15:30:29 -0100 Subject: [PATCH 8/8] fixes --- crates/pg_statement_splitter/src/lib.rs | 5 +++++ .../pg_workspace_new/src/workspace/server/change.rs | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/pg_statement_splitter/src/lib.rs b/crates/pg_statement_splitter/src/lib.rs index ab4bafa8..242c2a26 100644 --- a/crates/pg_statement_splitter/src/lib.rs +++ b/crates/pg_statement_splitter/src/lib.rs @@ -56,6 +56,11 @@ mod tests { assert_eq!(*expected, self.input[*range].to_string()); } + assert!( + self.parse.ranges.is_sorted_by_key(|r| r.start()), + "Ranges are not sorted" + ); + self } diff --git a/crates/pg_workspace_new/src/workspace/server/change.rs b/crates/pg_workspace_new/src/workspace/server/change.rs index 6267ef96..ccf9b35b 100644 --- a/crates/pg_workspace_new/src/workspace/server/change.rs +++ b/crates/pg_workspace_new/src/workspace/server/change.rs @@ -41,11 +41,17 @@ impl StatementChange { } } +/// Returns all relevant details about the change and its effects on the current state of the document. struct Affected { + /// Full range of the change, including the range of all statements that intersect with the change affected_range: TextRange, + /// All indices of affected statement positions affected_indices: Vec, + /// The index of the first statement position before the change, if any prev_index: Option, + /// The index of the first statement position after the change, if any next_index: Option, + /// the full affected range includng the prev and next statement full_affected_range: TextRange, } @@ -79,11 +85,11 @@ impl Document { changes.extend( pg_statement_splitter::split(&self.content) .ranges - .iter() + .into_iter() .map(|range| { let id = self.id_generator.next(); - let text = self.content[*range].to_string(); - self.positions.push((id, *range)); + let text = self.content[range].to_string(); + self.positions.push((id, range)); StatementChange::Added(AddedStatement { stmt: Statement {