Skip to content

Commit dbc5abb

Browse files
committed
feat: field completions
1 parent d39afbb commit dbc5abb

File tree

11 files changed

+217
-63
lines changed

11 files changed

+217
-63
lines changed

examples/two/test.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ class Foo(Model):
66
class Bar(Model):
77
_name = 'bar'
88
_description = 'asd'
9+
barr = Boolean()
10+
bark = Char()
911

1012
class Baz(models.Model):
1113
_inherit = 'bar'
12-
13-
foo = Char()
14+
foo = fields.Char()
1415

1516
class Quux(models.Model):
1617
_name = 'quux'

examples/two/views/templates.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<odoo>
2-
<record id="another" model='quux'>
3-
<field name='what'></field>
2+
<record id="another" model='bar'>
3+
<field name='barr'></field>
44
</record>
55
</odoo>

src/index.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -262,17 +262,16 @@ async fn add_root_py(path: PathBuf, _: Arc<ThreadedRodeo>) -> miette::Result<Out
262262
continue 'match_;
263263
}
264264
maybe_base = Some(String::from_utf8_lossy(&contents[name.clone()]));
265-
range = Some(name);
266265
} else if capture.index == 5 {
267266
// @inherit
268267
let inherit = capture.node.byte_range().contract(1);
269268
if !inherit.is_empty() {
270269
inherits.push(ImStr::from(String::from_utf8_lossy(&contents[inherit]).as_ref()));
271270
}
272-
// } else if capture.index == 6 {
271+
// } else if capture.index == 7 {
273272
// // @field
274273
// current_field = Some(capture.node.byte_range());
275-
// } else if capture.index == 9 {
274+
// } else if capture.index == 10 {
276275
// // @relation
277276
// if let Some(current_field) = current_field.take() {
278277
// let field = String::from_utf8_lossy(&contents[current_field]);
@@ -282,7 +281,7 @@ async fn add_root_py(path: PathBuf, _: Arc<ThreadedRodeo>) -> miette::Result<Out
282281
// let field_relation = interner.get_or_intern(field_relation.as_ref());
283282
// fields.push((field, Field::Relational(field_relation)));
284283
// }
285-
// } else if capture.index == 11 {
284+
// } else if capture.index == 12 {
286285
// // @_Type
287286
// if let Some(current_field) = current_field.take() {
288287
// let field = String::from_utf8_lossy(&contents[current_field]);
@@ -297,38 +296,40 @@ async fn add_root_py(path: PathBuf, _: Arc<ThreadedRodeo>) -> miette::Result<Out
297296
}
298297
}
299298
let Some(range) = range else { continue };
300-
let range = offset_range_to_lsp_range(range.map_unit(ByteOffset), rope.clone())
301-
.ok_or_else(|| diagnostic!("model out of bounds"))?;
302-
let mut has_primary = false;
299+
let range = range.map_unit(ByteOffset);
300+
let lsp_range =
301+
offset_range_to_lsp_range(range.clone(), rope.clone()).ok_or_else(|| diagnostic!("model out of bounds"))?;
302+
let mut has_primary = inherits.len() == 1;
303303
if !inherits.is_empty() {
304304
// Rearranges the primary inherit to the first index
305305
if let Some(maybe_base) = &maybe_base {
306306
if let Some(position) = inherits.iter().position(|inherit| inherit == maybe_base) {
307307
inherits.swap(position, 0);
308308
has_primary = true;
309+
} else {
310+
has_primary = false;
309311
}
310312
}
311313
}
312-
let fields = vec![];
313314
match (inherits.as_slice(), maybe_base) {
314315
([_, ..], None) => out.push(Model {
315-
model: ModelId::Inherit { inherits, has_primary },
316-
range,
317-
fields,
316+
model: ModelId::Inherit(inherits),
317+
range: lsp_range,
318+
byte_range: range,
318319
}),
319320
([], None) => {}
320321
(_, Some(base)) => {
321322
if has_primary {
322323
out.push(Model {
323-
model: ModelId::Inherit { inherits, has_primary },
324-
range,
325-
fields,
324+
model: ModelId::Inherit(inherits),
325+
range: lsp_range,
326+
byte_range: range,
326327
});
327328
} else {
328329
out.push(Model {
329330
model: ModelId::Base(base.as_ref().into()),
330-
range,
331-
fields,
331+
range: lsp_range,
332+
byte_range: range,
332333
})
333334
}
334335
}

src/main.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use odoo_lsp::utils::isolate::Isolate;
2626
use odoo_lsp::{format_loc, utils::*};
2727

2828
mod catch_panic;
29+
mod partial;
2930
mod python;
3031
mod xml;
3132

@@ -695,19 +696,26 @@ impl Backend {
695696
items.extend(matches);
696697
Ok(())
697698
}
698-
fn complete_field_name(
699+
async fn complete_field_name(
699700
&self,
700701
needle: &str,
701702
range: std::ops::Range<CharOffset>,
702703
model: String,
703704
rope: Rope,
705+
partial_token: Option<ProgressToken>,
704706
items: &mut Vec<CompletionItem>,
705707
) -> miette::Result<()> {
706-
let Some(entry) = self.module_index.models.get(model.as_str()) else {
708+
let Some(mut entry) = self.module_index.models.get_mut(model.as_str()) else {
707709
return Ok(());
708710
};
709711
let range = char_range_to_lsp_range(range, rope).ok_or_else(|| diagnostic!("range"))?;
710-
let completions = entry.fields.iter().flat_map(|(key, _)| {
712+
let fields = if let Some(fields) = &entry.fields {
713+
fields
714+
} else {
715+
let fields = self.populate_field_names(&entry, partial_token, range).await?;
716+
entry.fields.insert(fields)
717+
};
718+
let completions = fields.iter().flat_map(|(key, _)| {
711719
let field_name = self
712720
.module_index
713721
.interner
@@ -779,7 +787,7 @@ impl Backend {
779787
.get(model)
780788
.and_then(|entry| entry.base.as_ref().cloned())
781789
{
782-
Some(ModelLocation(base)) => Ok(Some(base.into())),
790+
Some(ModelLocation(base, _)) => Ok(Some(base.into())),
783791
None => Ok(None),
784792
}
785793
}

src/model.rs

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,27 @@ use std::{fmt::Display, ops::Deref, sync::Arc};
22

33
use dashmap::DashMap;
44
use intmap::IntMap;
5-
use lasso::{Key, Spur};
5+
use lasso::Spur;
66
use qp_trie::{wrapper::BString, Trie};
77
use tokio::sync::RwLock;
88
use tower_lsp::lsp_types::Range;
99

10-
use crate::{utils::MinLoc, ImStr};
10+
use crate::{
11+
utils::{ByteOffset, MinLoc},
12+
ImStr,
13+
};
1114

1215
#[derive(Clone, Debug)]
1316
pub struct Model {
1417
pub model: ModelId,
1518
pub range: Range,
16-
pub fields: Vec<(Spur, Field)>,
19+
pub byte_range: std::ops::Range<ByteOffset>,
1720
}
1821

1922
#[derive(Clone, Debug)]
2023
pub enum ModelId {
2124
Base(ImStr),
22-
Inherit { inherits: Vec<ImStr>, has_primary: bool },
25+
Inherit(Vec<ImStr>),
2326
}
2427

2528
#[derive(Default, Clone)]
@@ -32,7 +35,7 @@ pub struct ModelIndex {
3235
pub struct ModelEntry {
3336
pub base: Option<ModelLocation>,
3437
pub descendants: Vec<ModelLocation>,
35-
pub fields: IntMap<Field>,
38+
pub fields: Option<IntMap<Field>>,
3639
}
3740

3841
#[derive(Clone, Debug)]
@@ -42,7 +45,7 @@ pub enum Field {
4245
}
4346

4447
#[derive(Clone)]
45-
pub struct ModelLocation(pub MinLoc);
48+
pub struct ModelLocation(pub MinLoc, pub std::ops::Range<ByteOffset>);
4649

4750
impl Display for ModelLocation {
4851
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -85,27 +88,23 @@ impl ModelIndex {
8588
ModelId::Base(base) => {
8689
by_prefix.insert_str(&base, base.clone());
8790
let mut entry = self.entry(base.clone()).or_default();
88-
entry.base = Some(ModelLocation(MinLoc {
89-
path: path.clone(),
90-
range: item.range,
91-
}));
92-
for (field, type_) in item.fields {
93-
entry.fields.insert(field.into_usize() as u64, type_);
94-
}
95-
}
96-
ModelId::Inherit { inherits, has_primary } => {
97-
if has_primary {
98-
let primary = &inherits[0];
99-
let mut entry = self.entry(primary.clone()).or_default();
100-
for (field, type_) in item.fields {
101-
entry.fields.insert(field.into_usize() as u64, type_);
102-
}
103-
}
104-
for inherit in inherits {
105-
self.entry(inherit).or_default().descendants.push(ModelLocation(MinLoc {
91+
entry.base = Some(ModelLocation(
92+
MinLoc {
10693
path: path.clone(),
10794
range: item.range,
108-
}));
95+
},
96+
item.byte_range,
97+
));
98+
}
99+
ModelId::Inherit(inherits) => {
100+
for inherit in inherits {
101+
self.entry(inherit).or_default().descendants.push(ModelLocation(
102+
MinLoc {
103+
path: path.clone(),
104+
range: item.range,
105+
},
106+
item.byte_range.clone(),
107+
));
109108
}
110109
}
111110
}

src/partial.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use std::marker::PhantomData;
2+
3+
use serde::{Deserialize, Serialize};
4+
use tower_lsp::{
5+
lsp_types::{notification::Notification, CompletionResponse, ProgressToken},
6+
Client,
7+
};
8+
9+
#[derive(Debug)]
10+
pub struct Partial<T>(PhantomData<T>);
11+
12+
impl<T> Notification for Partial<T>
13+
where
14+
T: Serialize + for<'de> Deserialize<'de>,
15+
{
16+
type Params = PartialParams<T>;
17+
const METHOD: &'static str = "$/progress";
18+
}
19+
20+
#[derive(Serialize, Deserialize)]
21+
pub struct PartialParams<T> {
22+
pub token: ProgressToken,
23+
pub value: T,
24+
}
25+
26+
pub async fn emit_partial<T, U>(client: &Client, token: ProgressToken, value: U)
27+
where
28+
T: Notification<Params = PartialParams<U>>,
29+
{
30+
_ = client.send_notification::<T>(PartialParams { token, value }).await;
31+
}
32+
33+
/// If the first response is [CompletionResponse::List],
34+
/// subsequent [CompletionResponse::Array] responses add to the original list.
35+
pub type PartialCompletions = Partial<CompletionResponse>;

0 commit comments

Comments
 (0)