diff --git a/client/src/extension.ts b/client/src/extension.ts index 160b122..503346e 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -55,7 +55,7 @@ async function downloadLspBinary(context: ExtensionContext) { const preferNightly = !!workspace.getConfiguration("odoo-lsp.binary").get("preferNightly"); const overrideVersion = workspace.getConfiguration("odoo-lsp.binary").get("overrideVersion"); - let release = overrideVersion || 'nightly'; + let release = overrideVersion || "nightly"; if (!preferNightly && !overrideVersion) { release = context.extension.packageJSON._release || release; } diff --git a/src/backend.rs b/src/backend.rs index c87ee77..1849224 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -4,7 +4,6 @@ use std::sync::atomic::Ordering::Relaxed; use std::sync::atomic::{AtomicBool, AtomicUsize}; use dashmap::{DashMap, DashSet}; -use futures::executor::block_on; use globwalk::FileType; use lasso::{Key, Spur}; use log::debug; @@ -19,7 +18,7 @@ use odoo_lsp::config::{Config, ModuleConfig, ReferencesConfig, SymbolsConfig}; use odoo_lsp::index::{interner, Index, Interner, ModuleName, RecordId, SymbolSet}; use odoo_lsp::model::{Field, FieldKind, ModelEntry, ModelLocation, ModelName}; use odoo_lsp::record::Record; -use odoo_lsp::utils::isolate::Isolate; +// use odoo_lsp::utils::isolate::Isolate; use odoo_lsp::{some, utils::*}; pub struct Backend { @@ -33,7 +32,7 @@ pub struct Backend { pub root_setup: AtomicBool, pub symbols_limit: AtomicUsize, pub references_limit: AtomicUsize, - pub isolate: Isolate, + // pub isolate: Isolate, } #[derive(Debug, Default)] @@ -325,19 +324,24 @@ impl Backend { }), })) } - pub fn jump_def_field_name(&self, field: &str, model: &str) -> miette::Result> { + pub async fn jump_def_field_name(&self, field: &str, model: &str) -> miette::Result> { let model = interner().get_or_intern(model); let mut entry = some!(self.index.models.get_mut(&model.into())); let field = some!(interner().get(field)); - let fields = block_on(self.populate_field_names(&mut entry, &[]))?; + let fields = self.populate_field_names(&mut entry, &[]).await?; let field = some!(fields.get(&field.into())); Ok(Some(field.location.clone().into())) } - pub fn hover_field_name(&self, name: &str, model: &str, range: Option) -> miette::Result> { + pub async fn hover_field_name( + &self, + name: &str, + model: &str, + range: Option, + ) -> miette::Result> { let model = interner().get_or_intern(model); let mut entry = some!(self.index.models.get_mut(&model.into())); let field = some!(interner().get(name)); - let fields = block_on(self.populate_field_names(&mut entry, &[]))?; + let fields = self.populate_field_names(&mut entry, &[]).await?; let field = some!(fields.get(&field.into())); Ok(Some(Hover { range, diff --git a/src/main.rs b/src/main.rs index a366aa7..319380b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use tower_lsp::{LanguageServer, LspService, Server}; use odoo_lsp::config::Config; use odoo_lsp::index::{interner, Interner}; use odoo_lsp::model::FieldKind; -use odoo_lsp::utils::isolate::Isolate; +// use odoo_lsp::utils::isolate::Isolate; use odoo_lsp::{format_loc, some, utils::*}; mod analyze; @@ -126,7 +126,7 @@ impl LanguageServer for Backend { root_setup: _, symbols_limit: _, references_limit: _, - isolate: _, + // isolate: _, } = self; document_map.remove(path); record_ranges.remove(path); @@ -401,10 +401,8 @@ impl LanguageServer for Backend { return Ok(None); }; if ext == "xml" { - let completions = self.isolate.send_task(|send| { - Box::pin(async { _ = send.send(self.xml_completions(params, document.value().clone()).await) }) - }); - match completions.await.expect("isolate error") { + let completions = self.xml_completions(params, document.value().clone()).await; + match completions { Ok(ret) => Ok(ret), Err(report) => { self.client @@ -418,15 +416,10 @@ impl LanguageServer for Backend { debug!("Bug: did not build AST for {}", uri.path()); return Ok(None); }; - let completions = self.isolate.send_task(|send| { - Box::pin(async { - _ = send.send( - self.python_completions(params, ast.value().clone(), document.value().clone()) - .await, - ); - }) - }); - match completions.await.expect("isolate error") { + let completions = self + .python_completions(params, ast.value().clone(), document.value().clone()) + .await; + match completions { Ok(ret) => Ok(ret), Err(err) => { self.client @@ -622,7 +615,7 @@ async fn main() { ast_map: DashMap::new(), symbols_limit: AtomicUsize::new(100), references_limit: AtomicUsize::new(100), - isolate: Isolate::new(), + // isolate: Isolate::new(), }) .finish(); diff --git a/src/python.rs b/src/python.rs index a5c0bcb..c5aa535 100644 --- a/src/python.rs +++ b/src/python.rs @@ -178,7 +178,6 @@ impl Backend { error!(format_loc!("python_completions: invalid offset")); return Ok(None); }; - let root = some!(top_level_stmt(ast.root_node(), offset)); let Some(current_module) = self .index .module_of_path(Path::new(params.text_document_position.text_document.uri.path())) @@ -190,67 +189,108 @@ impl Backend { let bytes = rope.bytes().collect::>(); let query = PyCompletions::query(); let mut items = vec![]; - 'match_: for match_ in cursor.matches(query, root, &bytes[..]) { - let mut model_filter = None; - for capture in match_.captures { - if capture.index == PyCompletions::REQUEST { - model_filter = Some("ir.ui.view"); - } else if capture.index == PyCompletions::XML_ID { - let range = capture.node.byte_range(); - if range.contains(&offset) { - let Some(slice) = rope.get_byte_slice(range.clone()) else { - dbg!(&range); + let mut early_return = None; + enum EarlyReturn { + XmlId, + Model, + Access(String), + } + { + let root = some!(top_level_stmt(ast.root_node(), offset)); + 'match_: for match_ in cursor.matches(query, root, &bytes[..]) { + let mut model_filter = None; + for capture in match_.captures { + if capture.index == PyCompletions::REQUEST { + model_filter = Some("ir.ui.view"); + } else if capture.index == PyCompletions::XML_ID { + let range = capture.node.byte_range(); + if range.contains(&offset) { + let Some(slice) = rope.get_byte_slice(range.clone()) else { + dbg!(&range); + break 'match_; + }; + let relative_offset = range.start; + let needle = Cow::from(slice.byte_slice(1..offset - relative_offset)); + // remove the quotes + let range = range.contract(1).map_unit(|unit| CharOffset(rope.byte_to_char(unit))); + early_return = Some(( + EarlyReturn::XmlId, + needle, + range, + rope.clone(), + model_filter, + *current_module, + )); break 'match_; - }; - let relative_offset = range.start; - let needle = Cow::from(slice.byte_slice(1..offset - relative_offset)); - // remove the quotes - let range = range.contract(1).map_unit(|unit| CharOffset(rope.byte_to_char(unit))); - self.complete_xml_id(&needle, range, rope.clone(), model_filter, *current_module, &mut items) - .await?; - return Ok(Some(CompletionResponse::List(CompletionList { - is_incomplete: items.len() >= Self::LIMIT, - items, - }))); - } - } else if capture.index == PyCompletions::MODEL { - let range = capture.node.byte_range(); - if range.contains(&offset) || range.end == offset { - let Some(slice) = rope.get_byte_slice(range.clone()) else { - dbg!(&range); + } + } else if capture.index == PyCompletions::MODEL { + let range = capture.node.byte_range(); + if range.contains(&offset) || range.end == offset { + let Some(slice) = rope.get_byte_slice(range.clone()) else { + dbg!(&range); + break 'match_; + }; + let relative_offset = range.start; + let needle = Cow::from(slice.byte_slice(1..offset - relative_offset)); + let range = range.contract(1).map_unit(|unit| CharOffset(rope.byte_to_char(unit))); + early_return = + Some((EarlyReturn::Model, needle, range, rope.clone(), None, *current_module)); break 'match_; - }; - let relative_offset = range.start; - let needle = Cow::from(slice.byte_slice(1..offset - relative_offset)); - let range = range.contract(1).map_unit(|unit| CharOffset(rope.byte_to_char(unit))); - self.complete_model(&needle, range, rope.clone(), &mut items).await?; - return Ok(Some(CompletionResponse::List(CompletionList { - is_incomplete: items.len() >= Self::LIMIT, - items, - }))); - } - } else if capture.index == PyCompletions::ACCESS { - let range = capture.node.byte_range(); - if range.contains(&offset) || range.end == offset { - let Some(slice) = rope.get_byte_slice(range.clone()) else { - dbg!(&range); + } + } else if capture.index == PyCompletions::ACCESS { + let range = capture.node.byte_range(); + if range.contains(&offset) || range.end == offset { + let Some(slice) = rope.get_byte_slice(range.clone()) else { + dbg!(&range); + break 'match_; + }; + let lhs = some!(capture.node.prev_named_sibling()); + let model = + some!(self.model_of_range(root, lhs.byte_range().map_unit(ByteOffset), None, &bytes)); + let model = interner().resolve(&model); + let needle = Cow::from(slice.byte_slice(..offset - range.start)); + let range = range.map_unit(|unit| CharOffset(rope.byte_to_char(unit))); + early_return = Some(( + EarlyReturn::Access(model.to_string()), + needle, + range, + rope.clone(), + None, + *current_module, + )); break 'match_; - }; - let lhs = some!(capture.node.prev_named_sibling()); - let model = - some!(self.model_of_range(root, lhs.byte_range().map_unit(ByteOffset), None, &bytes)); - let model = interner().resolve(&model); - let needle = Cow::from(slice.byte_slice(..offset - range.start)); - let range = range.map_unit(|unit| CharOffset(rope.byte_to_char(unit))); - self.complete_field_name(&needle, range, model.to_string(), rope.clone(), &mut items) - .await?; - return Ok(Some(CompletionResponse::List(CompletionList { - is_incomplete: items.len() >= Self::LIMIT, - items, - }))); + } } } + // if let Some((needle, range, rope, model_filter, current_module)) = early_return { + // } + } + } + match early_return { + Some((EarlyReturn::XmlId, needle, range, rope, model_filter, current_module)) => { + self.complete_xml_id(&needle, range, rope, model_filter, current_module, &mut items) + .await?; + return Ok(Some(CompletionResponse::List(CompletionList { + is_incomplete: items.len() >= Self::LIMIT, + items, + }))); } + Some((EarlyReturn::Model, needle, range, rope, _, _)) => { + self.complete_model(&needle, range, rope.clone(), &mut items).await?; + return Ok(Some(CompletionResponse::List(CompletionList { + is_incomplete: items.len() >= Self::LIMIT, + items, + }))); + } + Some((EarlyReturn::Access(model), needle, range, rope, _, _)) => { + self.complete_field_name(&needle, range, model, rope.clone(), &mut items) + .await?; + return Ok(Some(CompletionResponse::List(CompletionList { + is_incomplete: items.len() >= Self::LIMIT, + items, + }))); + } + None => {} } Ok(None) } @@ -264,48 +304,55 @@ impl Backend { else { Err(diagnostic!("could not find offset for {}", uri.path()))? }; - let root = some!(top_level_stmt(ast.root_node(), offset)); - let query = PyCompletions::query(); let bytes = rope.bytes().collect::>(); - let mut cursor = tree_sitter::QueryCursor::new(); - 'match_: for match_ in cursor.matches(query, root, &bytes[..]) { - for capture in match_.captures { - if capture.index == PyCompletions::XML_ID { - let range = capture.node.byte_range(); - if range.contains(&offset) { - let range = range.contract(1); - let Some(slice) = rope.get_byte_slice(range.clone()) else { - dbg!(&range); - break 'match_; - }; - let slice = Cow::from(slice); - return self - .jump_def_inherit_id(&slice, ¶ms.text_document_position_params.text_document.uri); - } - } else if capture.index == PyCompletions::MODEL { - let range = capture.node.byte_range(); - if range.contains(&offset) { - let range = range.contract(1); - let Some(slice) = rope.get_byte_slice(range.clone()) else { - dbg!(&range); + let mut early_return = None; + { + let root = some!(top_level_stmt(ast.root_node(), offset)); + let query = PyCompletions::query(); + let mut cursor = tree_sitter::QueryCursor::new(); + 'match_: for match_ in cursor.matches(query, root, &bytes[..]) { + for capture in match_.captures { + if capture.index == PyCompletions::XML_ID { + let range = capture.node.byte_range(); + if range.contains(&offset) { + let range = range.contract(1); + let Some(slice) = rope.get_byte_slice(range.clone()) else { + dbg!(&range); + break 'match_; + }; + let slice = Cow::from(slice); + return self + .jump_def_inherit_id(&slice, ¶ms.text_document_position_params.text_document.uri); + } + } else if capture.index == PyCompletions::MODEL { + let range = capture.node.byte_range(); + if range.contains(&offset) { + let range = range.contract(1); + let Some(slice) = rope.get_byte_slice(range.clone()) else { + dbg!(&range); + break 'match_; + }; + let slice = Cow::from(slice); + return self.jump_def_model(&slice); + } + } else if capture.index == PyCompletions::ACCESS { + let range = capture.node.byte_range(); + if range.contains(&offset) || range.end == offset { + let lhs = some!(capture.node.prev_named_sibling()); + let lhs = lhs.byte_range().map_unit(ByteOffset); + let model = some!(self.model_of_range(ast.root_node(), lhs, None, &bytes)); + let field = String::from_utf8_lossy(&bytes[range]); + let model = interner().resolve(&model); + early_return = Some((field, model)); break 'match_; - }; - let slice = Cow::from(slice); - return self.jump_def_model(&slice); - } - } else if capture.index == PyCompletions::ACCESS { - let range = capture.node.byte_range(); - if range.contains(&offset) || range.end == offset { - let lhs = some!(capture.node.prev_named_sibling()); - let lhs = lhs.byte_range().map_unit(ByteOffset); - let model = some!(self.model_of_range(ast.root_node(), lhs, None, &bytes)); - let field = String::from_utf8_lossy(&bytes[range]); - let model = interner().resolve(&model); - return self.jump_def_field_name(&field, model); + } } } } } + if let Some((field, model)) = early_return { + return self.jump_def_field_name(&field, model).await; + } Ok(None) } pub fn python_references( @@ -493,39 +540,49 @@ impl Backend { else { Err(diagnostic!("could not find offset for {}", uri.path()))? }; - let root = some!(top_level_stmt(ast.root_node(), offset)); - let query = PyCompletions::query(); + let bytes = rope.bytes().collect::>(); - let mut cursor = tree_sitter::QueryCursor::new(); - 'match_: for match_ in cursor.matches(query, root, &bytes[..]) { - for capture in match_.captures { - if capture.index == PyCompletions::MODEL { - let range = capture.node.byte_range(); - if range.contains(&offset) { - let range = range.contract(1); - let lsp_range = ts_range_to_lsp_range(capture.node.range()); - let Some(slice) = rope.get_byte_slice(range.clone()) else { - dbg!(&range); + let mut early_return = None; + { + let root = some!(top_level_stmt(ast.root_node(), offset)); + let query = PyCompletions::query(); + let mut cursor = tree_sitter::QueryCursor::new(); + 'match_: for match_ in cursor.matches(query, root, &bytes[..]) { + for capture in match_.captures { + if capture.index == PyCompletions::MODEL { + let range = capture.node.byte_range(); + if range.contains(&offset) { + let range = range.contract(1); + let lsp_range = ts_range_to_lsp_range(capture.node.range()); + let Some(slice) = rope.get_byte_slice(range.clone()) else { + dbg!(&range); + break 'match_; + }; + let slice = Cow::from(slice); + return self.hover_model(&slice, Some(lsp_range), false); + } + } else if capture.index == PyCompletions::ACCESS { + let range = capture.node.byte_range(); + if range.contains(&offset) { + let lsp_range = ts_range_to_lsp_range(capture.node.range()); + let lhs = some!(capture.node.prev_named_sibling()); + let lhs = lhs.byte_range().map_unit(ByteOffset); + let model = some!(self.model_of_range(root, lhs, None, &bytes)); + let field = String::from_utf8_lossy(&bytes[range]); + let model = interner().resolve(&model); + early_return = Some((field, model, lsp_range)); break 'match_; - }; - let slice = Cow::from(slice); - return self.hover_model(&slice, Some(lsp_range), false); - } - } else if capture.index == PyCompletions::ACCESS { - let range = capture.node.byte_range(); - if range.contains(&offset) { - let lsp_range = ts_range_to_lsp_range(capture.node.range()); - let lhs = some!(capture.node.prev_named_sibling()); - let lhs = lhs.byte_range().map_unit(ByteOffset); - let model = some!(self.model_of_range(root, lhs, None, &bytes)); - let field = String::from_utf8_lossy(&bytes[range]); - let model = interner().resolve(&model); - return self.hover_field_name(&field, model, Some(lsp_range)); + } } } } } + if let Some((field, model, lsp_range)) = early_return { + return self.hover_field_name(&field, model, Some(lsp_range)).await; + } + // No matches, assume arbitrary expression. + let root = some!(top_level_stmt(ast.root_node(), offset)); let needle = some!(root.named_descendant_for_byte_range(offset, offset)); let lsp_range = ts_range_to_lsp_range(needle.range()); let model = some!(self.model_of_range(root, needle.byte_range().map_unit(ByteOffset), None, &bytes)); diff --git a/src/utils.rs b/src/utils.rs index c0f9b15..ab331b7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -9,7 +9,7 @@ use xmlparser::{StrSpan, Token}; use crate::index::interner; -pub mod isolate; +// pub mod isolate; /// A more economical version of [Location]. #[derive(Clone, Debug)] diff --git a/src/xml.rs b/src/xml.rs index a2c746f..44ccf47 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -231,7 +231,7 @@ impl Backend { Some(RefKind::Model) => self.jump_def_model(&cursor_value), Some(RefKind::FieldName) => { let model = some!(model_filter); - self.jump_def_field_name(&cursor_value, &model) + self.jump_def_field_name(&cursor_value, &model).await } Some(RefKind::Id) | None => Ok(None), }