Skip to content

Commit 8082a53

Browse files
committed
fix: parse more incomplete xml
1 parent 458f570 commit 8082a53

File tree

4 files changed

+77
-75
lines changed

4 files changed

+77
-75
lines changed

examples/two/views/templates.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
<odoo>
2-
<template id="one" inherit_id='generic_tax_report'>
3-
</template>
2+
<template id="asd" inherit_id='one.one'
43
</odoo>

src/record.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use ropey::Rope;
44
use tower_lsp::lsp_types::*;
55
use xmlparser::{ElementEnd, Token, Tokenizer};
66

7-
use crate::utils::{char_to_position, CharOffset};
7+
use crate::utils::{char_to_position, position_to_char, CharOffset};
88

99
macro_rules! unwrap_or_none {
1010
($opt:expr) => {
@@ -114,7 +114,16 @@ impl Record {
114114
break;
115115
}
116116
}
117-
None | Some(Err(_)) => break,
117+
// None | Some(Err(_)) => break,
118+
None => break,
119+
Some(Err(err)) => {
120+
let pos = Position {
121+
line: err.pos().row,
122+
character: err.pos().col,
123+
};
124+
end = position_to_char(pos, rope.clone()).map(|offset| offset.0);
125+
break;
126+
}
118127
_ => {}
119128
}
120129
}
@@ -191,7 +200,11 @@ impl Record {
191200
}
192201
None => break,
193202
Some(Err(err)) => {
194-
eprintln!("error parsing template {}:\n{err}", uri.path());
203+
let pos = Position {
204+
line: err.pos().row,
205+
character: err.pos().col,
206+
};
207+
end = position_to_char(pos, rope.clone()).map(|offset| offset.0);
195208
break;
196209
}
197210
_ => {}

src/utils.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::ops::{Add, Sub};
33

44
use ropey::Rope;
55
use tower_lsp::lsp_types::*;
6+
use xmlparser::{StrSpan, Token};
67

78
pub fn offset_to_position(offset: usize, rope: Rope) -> Option<Position> {
89
let line = rope.try_char_to_line(offset).ok()?;
@@ -54,6 +55,23 @@ pub fn offset_range_to_lsp_range(range: std::ops::Range<ByteOffset>, rope: Rope)
5455
Some(Range { start, end })
5556
}
5657

58+
pub fn token_span<'r, 't>(token: &'r Token<'t>) -> &'r StrSpan<'t> {
59+
match token {
60+
Token::Declaration { span, .. }
61+
| Token::ProcessingInstruction { span, .. }
62+
| Token::Comment { span, .. }
63+
| Token::DtdStart { span, .. }
64+
| Token::EmptyDtd { span, .. }
65+
| Token::EntityDeclaration { span, .. }
66+
| Token::DtdEnd { span, .. }
67+
| Token::ElementStart { span, .. }
68+
| Token::Attribute { span, .. }
69+
| Token::ElementEnd { span, .. }
70+
| Token::Text { text: span, .. }
71+
| Token::Cdata { span, .. } => span,
72+
}
73+
}
74+
5775
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
5876
#[repr(transparent)]
5977
pub struct ByteOffset(pub usize);

src/xml.rs

Lines changed: 42 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use faststr::FastStr;
88
use miette::{diagnostic, IntoDiagnostic};
99
use ropey::Rope;
1010
use tower_lsp::lsp_types::*;
11-
use xmlparser::{ElementEnd, StrSpan, Token, Tokenizer};
11+
use xmlparser::{StrSpan, Token, Tokenizer};
1212

1313
use odoo_lsp::record::Record;
1414
use odoo_lsp::utils::*;
@@ -85,7 +85,7 @@ impl Backend {
8585
rope: &'rope Rope,
8686
uri: &Url,
8787
position: Position,
88-
) -> miette::Result<Option<(Cow<'rope, str>, usize, usize)>> {
88+
) -> miette::Result<(Cow<'rope, str>, usize, usize)> {
8989
let ranges = self
9090
.record_ranges
9191
.get(uri.path())
@@ -101,11 +101,9 @@ impl Backend {
101101
Ordering::Equal
102102
}
103103
}) else {
104-
eprintln!(
105-
"Could not find record for cursor={cursor:?} ranges={:?}",
106-
ranges.value()
107-
);
108-
return Ok(None);
104+
eprintln!("(record_slice) fall back to full slice");
105+
let slice = Cow::from(rope.slice(..));
106+
return Ok((slice, cursor_by_char, 0));
109107
};
110108
let record_range = &ranges.value()[record];
111109
let relative_offset = rope.try_byte_to_char(record_range.start.0).into_diagnostic()?;
@@ -114,7 +112,7 @@ impl Backend {
114112
let slice = rope.byte_slice(record_range.clone().map_unit(|unit| unit.0));
115113
let slice = Cow::from(slice);
116114

117-
Ok(Some((slice, cursor_by_char, relative_offset)))
115+
Ok((slice, cursor_by_char, relative_offset))
118116
}
119117
pub async fn xml_completions(
120118
&self,
@@ -123,9 +121,7 @@ impl Backend {
123121
) -> miette::Result<Option<CompletionResponse>> {
124122
let position = params.text_document_position.position;
125123
let uri = &params.text_document_position.text_document.uri;
126-
let Some((slice, cursor_by_char, relative_offset)) = self.record_slice(&rope, uri, position)? else {
127-
return Ok(None);
128-
};
124+
let (slice, cursor_by_char, relative_offset) = self.record_slice(&rope, uri, position)?;
129125
let reader = Tokenizer::from(&slice[..]);
130126

131127
let current_module = self
@@ -177,54 +173,38 @@ impl Backend {
177173
{
178174
cursor_value = Some(value);
179175
}
180-
Ok(Token::ElementEnd {
181-
end: ElementEnd::Empty, ..
182-
}) if matches!(tag, Some(Tag::Field)) => {
183-
let (Some(value), Some(record_field)) = (cursor_value, record_field.take()) else {
184-
continue;
185-
};
186-
let needle = &value.as_str()[..cursor_by_char - value.range().start];
187-
let replace_range = value.range().map_unit(|unit| CharOffset(unit + relative_offset));
188-
match record_field {
189-
RecordField::InheritId => self.complete_inherit_id(
190-
needle,
191-
replace_range,
192-
rope.clone(),
193-
model_filter.as_deref(),
194-
&current_module,
195-
&mut items,
196-
)?,
197-
}
198-
break;
199-
}
200176
Ok(Token::Attribute { local, value, .. })
201177
if matches!(tag, Some(Tag::Template))
202178
&& local.as_str() == "inherit_id"
203179
&& (value.range().contains(&cursor_by_char) || value.range().end == cursor_by_char) =>
204180
{
205181
cursor_value = Some(value);
182+
record_field = Some(RecordField::InheritId);
206183
}
207-
Ok(Token::ElementEnd { .. }) if matches!(tag, Some(Tag::Template)) => {
208-
let Some(value) = cursor_value else { continue };
209-
let needle = &value.as_str()[..cursor_by_char - value.range().start];
210-
let replace_range = value.range().map_unit(|unit| CharOffset(unit + relative_offset));
211-
self.complete_inherit_id(
212-
needle,
213-
replace_range,
214-
rope.clone(),
215-
model_filter.as_deref(),
216-
&current_module,
217-
&mut items,
218-
)?;
219-
break;
220-
}
221-
Err(err) => {
222-
eprintln!("bug:\n{err}\nin file:\n{}", slice);
223-
break;
184+
Ok(Token::ElementEnd { .. }) if cursor_value.is_some() => break,
185+
Err(_) => break,
186+
Ok(token) => {
187+
if token_span(&token).start() > cursor_by_char {
188+
break;
189+
}
224190
}
225-
_ => {}
226191
}
227192
}
193+
let (Some(value), Some(record_field)) = (cursor_value, record_field.take()) else {
194+
return Ok(None);
195+
};
196+
let needle = &value.as_str()[..cursor_by_char - value.range().start];
197+
let replace_range = value.range().map_unit(|unit| CharOffset(unit + relative_offset));
198+
match record_field {
199+
RecordField::InheritId => self.complete_inherit_id(
200+
needle,
201+
replace_range,
202+
rope.clone(),
203+
model_filter.as_deref(),
204+
&current_module,
205+
&mut items,
206+
)?,
207+
}
228208

229209
Ok(Some(CompletionResponse::List(CompletionList {
230210
is_incomplete: items.len() >= Self::LIMIT,
@@ -234,9 +214,7 @@ impl Backend {
234214
pub async fn xml_jump_def(&self, params: GotoDefinitionParams, rope: Rope) -> miette::Result<Option<Location>> {
235215
let position = params.text_document_position_params.position;
236216
let uri = &params.text_document_position_params.text_document.uri;
237-
let Some((slice, cursor_by_char, _)) = self.record_slice(&rope, uri, position)? else {
238-
return Ok(None);
239-
};
217+
let (slice, cursor_by_char, _) = self.record_slice(&rope, uri, position)?;
240218
let reader = Tokenizer::from(&slice[..]);
241219

242220
enum RecordField {
@@ -266,34 +244,28 @@ impl Backend {
266244
record_field = Some(RecordField::InheritId);
267245
}
268246
}
269-
Ok(Token::ElementEnd { .. }) if matches!(tag, Some(Tag::Field)) => {
270-
let Some(cursor_value) = cursor_value else { continue };
271-
match record_field {
272-
Some(RecordField::InheritId) => {
273-
return self.jump_def_inherit_id(&cursor_value, uri);
274-
}
275-
None => {}
276-
}
277-
}
278247
Ok(Token::Attribute { local, value, .. })
279248
if matches!(tag, Some(Tag::Template))
280249
&& local.as_str() == "inherit_id"
281250
&& value.range().contains(&cursor_by_char) =>
282251
{
283252
cursor_value = Some(value);
253+
record_field = Some(RecordField::InheritId);
284254
}
285-
Ok(Token::ElementEnd { .. }) if matches!(tag, Some(Tag::Template)) => {
286-
let Some(cursor_value) = cursor_value else { continue };
287-
return self.jump_def_inherit_id(&cursor_value, uri);
288-
}
289-
Err(err) => {
290-
eprintln!("bug:\n{err}\nin file:\n{}", slice);
291-
break;
255+
Ok(Token::ElementEnd { .. }) if cursor_value.is_some() => break,
256+
Err(_) => break,
257+
Ok(token) => {
258+
if token_span(&token).start() > cursor_by_char {
259+
break;
260+
}
292261
}
293-
_ => {}
294262
}
295263
}
296264

297-
Ok(None)
265+
let Some(cursor_value) = cursor_value else { return Ok(None) };
266+
match record_field {
267+
Some(RecordField::InheritId) => self.jump_def_inherit_id(&cursor_value, uri),
268+
None => Ok(None),
269+
}
298270
}
299271
}

0 commit comments

Comments
 (0)