Skip to content

Commit fdb6aad

Browse files
Tokenized autocompletion (#10)
* tokenizer autocompletion * fix * readme * fix Co-authored-by: chenyan-dfinity <[email protected]>
1 parent 69405a1 commit fdb6aad

File tree

3 files changed

+59
-49
lines changed

3 files changed

+59
-49
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,7 @@ If you are writing your own `.did` file, you can also supply the did file via th
141141

142142
* Acess to service init type
143143
* `IDLValue::Blob` for efficient blob serialization
144-
* Tokenization for partial parser (variable needs a preceding space for autocompletion)
145-
* Autocompletion within Candid value; autocompletion for decode method
144+
* Autocompletion within Candid value
146145
* Robust support for `~=`, requires inferring principal types
147146
* Loop detection for `load`
148147
* Assert upgrade correctness

src/exp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ impl std::str::FromStr for Exp {
269269
super::grammar::ExpParser::new().parse(lexer)
270270
}
271271
}
272-
fn str_to_principal(id: &str, helper: &MyHelper) -> Result<Principal> {
272+
pub fn str_to_principal(id: &str, helper: &MyHelper) -> Result<Principal> {
273273
let try_id = Principal::from_text(id);
274274
Ok(match try_id {
275275
Ok(id) => id,

src/helper.rs

Lines changed: 57 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::exp::Exp;
1+
use crate::exp::{str_to_principal, Exp};
2+
use crate::token::{Token, Tokenizer};
23
use ansi_term::Color;
34
use candid::{
45
check_prog,
@@ -145,52 +146,62 @@ enum Partial {
145146

146147
fn partial_parse(line: &str, pos: usize, helper: &MyHelper) -> Option<(usize, Partial)> {
147148
let (start, _) = extract_word(line, pos, None, b" ");
148-
let prev = &line[..start].trim_end();
149-
let (_, prev) = extract_word(prev, prev.len(), None, b" ");
150-
let is_call = matches!(prev, "call" | "encode");
151-
if is_call {
152-
let pos_tail = line[start..pos]
153-
.rfind('.')
154-
.map(|v| start + v)
155-
.unwrap_or(pos);
156-
let tail = if pos_tail < pos {
157-
line[pos_tail + 1..pos].to_string()
149+
let iter = Tokenizer::new(&line[start..pos]);
150+
let mut tokens = Vec::new();
151+
let mut pos_start = 0;
152+
for v in iter {
153+
let v = v.ok()?;
154+
if pos_start == 0
155+
&& matches!(
156+
v.1,
157+
Token::Equals | Token::TestEqual | Token::SubEqual | Token::NotEqual
158+
)
159+
{
160+
pos_start = v.2;
161+
}
162+
let tok = if let Token::Text(id) = v.1 {
163+
Token::Id(id)
158164
} else {
159-
String::new()
165+
v.1
160166
};
161-
let id = &line[start..pos_tail];
162-
if id.starts_with('"') {
163-
if id.len() >= 7 {
164-
let id = Principal::from_text(&id[1..id.len() - 1]).ok()?;
165-
Some((pos_tail, Partial::Call(id, tail)))
166-
} else {
167-
None
168-
}
169-
} else {
170-
match helper.env.0.get(id)? {
171-
IDLValue::Principal(id) => Some((pos_tail, Partial::Call(id.clone(), tail))),
172-
_ => None,
167+
tokens.push((v.0, tok));
168+
}
169+
match tokens.as_slice() {
170+
[(_, Token::Id(id))] => match str_to_principal(id, helper) {
171+
Ok(id) => Some((pos, Partial::Call(id, "".to_string()))),
172+
Err(_) => parse_value(&line[..pos], start, pos, helper),
173+
},
174+
[(_, Token::Id(id)), (pos_tail, Token::Dot)]
175+
| [(_, Token::Id(id)), (pos_tail, Token::Dot), (_, _)] => {
176+
match str_to_principal(id, helper) {
177+
Ok(id) => Some((
178+
start + pos_tail,
179+
Partial::Call(id, line[start + pos_tail + 1..pos].to_string()),
180+
)),
181+
Err(_) => parse_value(&line[..pos], start + pos_start, start + pos_tail, helper),
173182
}
174183
}
175-
} else {
176-
let pos_tail = if line[..pos].ends_with(']') {
177-
pos
178-
} else {
179-
line[start..pos]
180-
.rfind(|c| c == '.' || c == '[' || c == ']')
181-
.map(|v| start + v)
182-
.unwrap_or(pos)
183-
};
184-
let v = line[start..pos_tail].parse::<Exp>().ok()?;
185-
let v = v.eval(helper).ok()?;
186-
let tail = if pos_tail < pos {
187-
line[pos_tail..pos].to_string()
188-
} else {
189-
String::new()
190-
};
191-
Some((pos_tail, Partial::Val(v, tail)))
184+
[.., (_, Token::RSquare)] | [.., (_, Token::Question)] => {
185+
parse_value(&line[..pos], start + pos_start, pos, helper)
186+
}
187+
[.., (pos_tail, Token::Dot)]
188+
| [.., (pos_tail, Token::Dot), (_, _)]
189+
| [.., (pos_tail, Token::LSquare)]
190+
| [.., (pos_tail, Token::LSquare), (_, Token::Decimal(_))] => {
191+
parse_value(&line[..pos], start + pos_start, start + pos_tail, helper)
192+
}
193+
_ => None,
192194
}
193195
}
196+
fn parse_value(
197+
line: &str,
198+
start: usize,
199+
end: usize,
200+
helper: &MyHelper,
201+
) -> Option<(usize, Partial)> {
202+
let v = line[start..end].parse::<Exp>().ok()?.eval(helper).ok()?;
203+
Some((end, Partial::Val(v, line[end..].to_string())))
204+
}
194205
fn match_selector(v: &IDLValue, prefix: &str) -> Vec<Pair> {
195206
match v {
196207
IDLValue::Opt(_) => vec![Pair {
@@ -399,7 +410,7 @@ fn test_partial_parse() -> anyhow::Result<()> {
399410
.env
400411
.0
401412
.insert("ic0".to_string(), IDLValue::Principal(ic0.clone()));
402-
assert_eq!(partial_parse("call a", 6, &helper), None);
413+
assert_eq!(partial_parse("call x", 6, &helper), None);
403414
assert_eq!(
404415
partial_parse("let id = call \"aaaaa-aa\"", 24, &helper).unwrap(),
405416
(24, Partial::Call(ic0.clone(), "".to_string()))
@@ -437,19 +448,19 @@ fn test_partial_parse() -> anyhow::Result<()> {
437448
);
438449
assert_eq!(partial_parse("let id = a.f1.", 14, &helper), None);
439450
assert_eq!(
440-
partial_parse("let id = a?", 11, &helper).unwrap(),
451+
partial_parse("let id =a?", 10, &helper).unwrap(),
441452
(
442-
11,
453+
10,
443454
Partial::Val(
444455
"record { variant {b=vec{1;2;3}}; 42; f1=42;42=35;a1=30}".parse::<IDLValue>()?,
445456
"".to_string()
446457
)
447458
)
448459
);
449460
assert_eq!(
450-
partial_parse("let id = a?.", 12, &helper).unwrap(),
461+
partial_parse("let id=a?.", 10, &helper).unwrap(),
451462
(
452-
11,
463+
9,
453464
Partial::Val(
454465
"record { variant {b=vec{1;2;3}}; 42; f1=42;42=35;a1=30}".parse::<IDLValue>()?,
455466
".".to_string()

0 commit comments

Comments
 (0)