Skip to content

Commit d833244

Browse files
committed
stash: complete relational XML IDs
1 parent dbc5abb commit d833244

File tree

4 files changed

+86
-55
lines changed

4 files changed

+86
-55
lines changed

src/main.rs

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use globwalk::FileType;
99
use lasso::{Key, Spur};
1010
use log::{debug, error, info};
1111
use miette::{diagnostic, IntoDiagnostic};
12+
use odoo_lsp::record::Record;
1213
use ropey::Rope;
1314
use serde_json::Value;
1415
use tower::ServiceBuilder;
@@ -566,7 +567,7 @@ enum Language {
566567
}
567568

568569
impl Backend {
569-
const LIMIT: usize = 20;
570+
const LIMIT: usize = 80;
570571
async fn on_change(&self, params: TextDocumentItem) -> miette::Result<()> {
571572
let split_uri = params.uri.path().rsplit_once('.');
572573
let mut diagnostics = vec![];
@@ -649,7 +650,7 @@ impl Backend {
649650
self.ast_map.insert(uri.path().to_string(), ast);
650651
Ok(())
651652
}
652-
fn complete_inherit_id(
653+
async fn complete_xml_id(
653654
&self,
654655
needle: &str,
655656
range: std::ops::Range<CharOffset>,
@@ -659,41 +660,45 @@ impl Backend {
659660
items: &mut Vec<CompletionItem>,
660661
) -> miette::Result<()> {
661662
let range = char_range_to_lsp_range(range, rope).ok_or_else(|| diagnostic!("(complete_inherit_id) range"))?;
662-
let matches = self
663-
.module_index
664-
.records
665-
.iter()
666-
.filter(|entry| {
667-
let id_filter = if let Some((module, xml_id)) = needle.split_once('.') {
668-
entry.module == module && entry.id.contains(xml_id)
669-
} else {
670-
entry.id.contains(needle)
671-
};
672-
if let (Some(filter), Some(model)) = (model_filter, &entry.model) {
673-
id_filter && filter == model
674-
} else {
675-
id_filter && model_filter.is_none()
676-
}
677-
})
678-
.take(Self::LIMIT)
679-
.map(|entry| {
680-
let label = if entry.module == current_module {
681-
entry.id.to_string()
682-
} else {
683-
entry.qualified_id()
684-
};
685-
CompletionItem {
686-
text_edit: Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
687-
new_text: label.clone(),
688-
insert: range,
689-
replace: range,
690-
})),
691-
label,
692-
kind: Some(CompletionItemKind::REFERENCE),
693-
..Default::default()
694-
}
663+
let by_prefix = self.module_index.records.by_prefix.read().await;
664+
fn to_completion_items(entry: &Record, current_module: &str, range: Range) -> CompletionItem {
665+
let label = if entry.module == current_module {
666+
entry.id.to_string()
667+
} else {
668+
entry.qualified_id()
669+
};
670+
CompletionItem {
671+
text_edit: Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
672+
new_text: label.clone(),
673+
insert: range,
674+
replace: range,
675+
})),
676+
label,
677+
kind: Some(CompletionItemKind::REFERENCE),
678+
..Default::default()
679+
}
680+
}
681+
if let Some((module, needle)) = needle.split_once('.') {
682+
let completions = by_prefix.iter_prefix(needle.as_bytes()).flat_map(|(_, keys)| {
683+
keys.iter().flat_map(|key| {
684+
self.module_index.records.get(key.as_str()).and_then(|entry| {
685+
(entry.module == module && entry.model.as_deref() == model_filter)
686+
.then(|| to_completion_items(&entry, current_module, range))
687+
})
688+
})
695689
});
696-
items.extend(matches);
690+
items.extend(completions.take(Self::LIMIT));
691+
} else {
692+
let completions = by_prefix.iter_prefix(needle.as_bytes()).flat_map(|(_, keys)| {
693+
keys.iter().flat_map(|key| {
694+
self.module_index.records.get(key.as_str()).and_then(|entry| {
695+
(entry.model.as_deref() == model_filter)
696+
.then(|| to_completion_items(&entry, current_module, range))
697+
})
698+
})
699+
});
700+
items.extend(completions.take(Self::LIMIT));
701+
}
697702
Ok(())
698703
}
699704
async fn complete_field_name(

src/python.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::sync::OnceLock;
77

88
use intmap::IntMap;
99
use lasso::Key;
10-
use log::{debug, error, info};
10+
use log::{debug, error};
1111
use miette::{diagnostic, Context, IntoDiagnostic};
1212
use ropey::Rope;
1313
use tower_lsp::lsp_types::*;
@@ -102,7 +102,8 @@ impl Backend {
102102
let needle = Cow::from(slice.byte_slice(1..offset - relative_offset));
103103
// remove the quotes
104104
let range = range.contract(1).map_unit(|unit| CharOffset(rope.byte_to_char(unit)));
105-
self.complete_inherit_id(&needle, range, rope.clone(), None, &current_module, &mut items)?;
105+
self.complete_xml_id(&needle, range, rope.clone(), None, &current_module, &mut items)
106+
.await?;
106107
return Ok(Some(CompletionResponse::List(CompletionList {
107108
is_incomplete: items.len() >= Self::LIMIT,
108109
items,

src/queries/model_fields.scm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515
(#eq? @_fields "fields")
1616
(#match? @_Relational "^(Many2one|One2many|Many2many)$")
1717
(#match? @_Type "^[A-Z]")
18+
(#not-match? @_Type "^(Many2one|One2many|Many2many)$")
1819
(#eq? @_comodel_name "comodel_name"))

src/xml.rs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ use std::borrow::Cow;
44
use std::cmp::Ordering;
55
use std::path::Path;
66

7+
use lasso::{Key, Spur, ThreadedRodeo};
78
use log::debug;
89
use miette::{diagnostic, IntoDiagnostic};
10+
use odoo_lsp::model::Field;
911
use ropey::Rope;
1012
use tower_lsp::lsp_types::*;
1113
use xmlparser::{StrSpan, Token, Tokenizer};
@@ -14,7 +16,7 @@ use odoo_lsp::record::Record;
1416
use odoo_lsp::{utils::*, ImStr};
1517

1618
enum RefKind {
17-
InheritId,
19+
Ref(Spur),
1820
Model,
1921
Id,
2022
FieldName,
@@ -155,21 +157,40 @@ impl Backend {
155157
.expect("must be in a module");
156158

157159
let mut items = vec![];
158-
let (_, cursor_value, ref_kind, model_filter) = gather_refs(cursor_by_char, &mut reader)?;
160+
let (_, cursor_value, ref_kind, model_filter) =
161+
gather_refs(cursor_by_char, &mut reader, &self.module_index.interner)?;
159162
let (Some(value), Some(record_field)) = (cursor_value, ref_kind) else {
160163
return Ok(None);
161164
};
162165
let needle = &value.as_str()[..cursor_by_char - value.range().start];
163166
let replace_range = value.range().map_unit(|unit| CharOffset(unit + relative_offset));
164167
match record_field {
165-
RefKind::InheritId => self.complete_inherit_id(
166-
needle,
167-
replace_range,
168-
rope.clone(),
169-
model_filter.as_deref(),
170-
&current_module,
171-
&mut items,
172-
)?,
168+
RefKind::Ref(relation) => {
169+
let Some(model) = model_filter else { return Ok(None) };
170+
let Some(mut entry) = self.module_index.models.get_mut(model.as_str()) else {
171+
return Ok(None);
172+
};
173+
// TODO: Extract into method
174+
let fields = if let Some(fields) = &entry.fields {
175+
fields
176+
} else {
177+
let range = char_range_to_lsp_range(replace_range.clone(), rope.clone()).unwrap();
178+
let fields = self.populate_field_names(&entry, None, range).await?;
179+
entry.fields.insert(fields)
180+
};
181+
let Some(Field::Relational(relation)) = dbg!(fields.get(dbg!(relation).into_usize() as u64)) else {
182+
return Ok(None);
183+
};
184+
self.complete_xml_id(
185+
needle,
186+
replace_range,
187+
rope.clone(),
188+
Some(self.module_index.interner.resolve(relation)),
189+
&current_module,
190+
&mut items,
191+
)
192+
.await?
193+
}
173194
RefKind::Model => {
174195
self.complete_model(needle, replace_range, rope.clone(), &mut items)
175196
.await?
@@ -194,13 +215,13 @@ impl Backend {
194215
let uri = &params.text_document_position_params.text_document.uri;
195216
let (slice, cursor_by_char, _) = self.record_slice(&rope, uri, position)?;
196217
let mut reader = Tokenizer::from(&slice[..]);
197-
let (_, cursor_value, ref_, _) = gather_refs(cursor_by_char, &mut reader)?;
218+
let (_, cursor_value, ref_, _) = gather_refs(cursor_by_char, &mut reader, &self.module_index.interner)?;
198219

199220
let Some(cursor_value) = cursor_value else {
200221
return Ok(None);
201222
};
202223
match ref_ {
203-
Some(RefKind::InheritId) => self.jump_def_inherit_id(&cursor_value, uri),
224+
Some(RefKind::Ref(_)) => self.jump_def_inherit_id(&cursor_value, uri),
204225
Some(RefKind::Model) => self.jump_def_model(&cursor_value),
205226
Some(RefKind::Id) | Some(RefKind::FieldName) | None => Ok(None),
206227
}
@@ -210,7 +231,7 @@ impl Backend {
210231
let uri = &params.text_document_position.text_document.uri;
211232
let (slice, cursor_by_char, _) = self.record_slice(&rope, uri, position)?;
212233
let mut reader = Tokenizer::from(&slice[..]);
213-
let (_, cursor_value, ref_, _) = gather_refs(cursor_by_char, &mut reader)?;
234+
let (_, cursor_value, ref_, _) = gather_refs(cursor_by_char, &mut reader, &self.module_index.interner)?;
214235

215236
let Some(cursor_value) = cursor_value else {
216237
return Ok(None);
@@ -221,7 +242,7 @@ impl Backend {
221242
.map(|ref_| ref_.to_string());
222243
match ref_ {
223244
Some(RefKind::Model) => self.model_references(&cursor_value),
224-
Some(RefKind::InheritId) | Some(RefKind::Id) => {
245+
Some(RefKind::Ref(_)) | Some(RefKind::Id) => {
225246
self.record_references(&cursor_value, current_module.as_deref())
226247
}
227248
Some(RefKind::FieldName) | None => Ok(None),
@@ -232,6 +253,7 @@ impl Backend {
232253
fn gather_refs<'read>(
233254
cursor_by_char: usize,
234255
reader: &mut Tokenizer<'read>,
256+
interner: &ThreadedRodeo,
235257
) -> miette::Result<(Option<Tag>, Option<StrSpan<'read>>, Option<RefKind>, Option<String>)> {
236258
let mut tag = None;
237259
let mut cursor_value = None;
@@ -255,17 +277,19 @@ fn gather_refs<'read>(
255277
} else if local.as_str() == "name" && value_in_range {
256278
cursor_value = Some(value);
257279
ref_kind = Some(RefKind::FieldName);
258-
} else if local.as_str() == "name" && value.as_str() == "inherit_id" {
259-
ref_kind = Some(RefKind::InheritId);
280+
} else if local.as_str() == "name" {
281+
let relation = interner.get_or_intern(value.as_str());
282+
ref_kind = Some(RefKind::Ref(relation));
260283
}
261284
}
262285
Ok(Token::Attribute { local, value, .. })
263286
if matches!(tag, Some(Tag::Template))
264287
&& local.as_str() == "inherit_id"
265288
&& value.range().contains(&cursor_by_char) =>
266289
{
290+
let inherit_id = interner.get_or_intern("ir.ui.view");
267291
cursor_value = Some(value);
268-
ref_kind = Some(RefKind::InheritId);
292+
ref_kind = Some(RefKind::Ref(inherit_id));
269293
}
270294
Ok(Token::Attribute { local, value, .. })
271295
if matches!(tag, Some(Tag::Record)) && local.as_str() == "model" =>

0 commit comments

Comments
 (0)