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 {