diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2021-06-15 09:31:36 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-15 09:31:36 -0400 |
commit | b4026dac9c4fb2b6cfc4a7015b4bacd102e06d08 (patch) | |
tree | 6c092c667d0bb25c07bbaff7e0bc521288bbc9ab /cli/tools/repl.rs | |
parent | 4cbc4a7eb321ae2808cf3dce9428f45aaed0e338 (diff) |
fix(repl): Complete declarations (#10963)
Diffstat (limited to 'cli/tools/repl.rs')
-rw-r--r-- | cli/tools/repl.rs | 139 |
1 files changed, 88 insertions, 51 deletions
diff --git a/cli/tools/repl.rs b/cli/tools/repl.rs index e48aa3685..67e68c0cd 100644 --- a/cli/tools/repl.rs +++ b/cli/tools/repl.rs @@ -50,47 +50,34 @@ impl EditorHelper { self.message_tx.send((method.to_string(), params))?; self.response_rx.recv()? } -} - -fn is_word_boundary(c: char) -> bool { - if c == '.' { - false - } else { - char::is_ascii_whitespace(&c) || char::is_ascii_punctuation(&c) - } -} - -impl Completer for EditorHelper { - type Candidate = String; - fn complete( - &self, - line: &str, - pos: usize, - _ctx: &Context<'_>, - ) -> Result<(usize, Vec<String>), ReadlineError> { - let start = line[..pos].rfind(is_word_boundary).map_or_else(|| 0, |i| i); - let end = line[pos..] - .rfind(is_word_boundary) - .map_or_else(|| pos, |i| pos + i); - - let word = &line[start..end]; - let word = word.strip_prefix(is_word_boundary).unwrap_or(word); - let word = word.strip_suffix(is_word_boundary).unwrap_or(word); - - let fallback = format!(".{}", word); + fn get_global_lexical_scope_names(&self) -> Vec<String> { + let evaluate_response = self + .post_message( + "Runtime.globalLexicalScopeNames", + Some(json!({ + "executionContextId": self.context_id, + })), + ) + .unwrap(); - let (prefix, suffix) = match word.rfind('.') { - Some(index) => word.split_at(index), - None => ("globalThis", fallback.as_str()), - }; + evaluate_response + .get("names") + .unwrap() + .as_array() + .unwrap() + .iter() + .map(|n| n.as_str().unwrap().to_string()) + .collect() + } + fn get_expression_property_names(&self, expr: &str) -> Vec<String> { let evaluate_response = self .post_message( "Runtime.evaluate", Some(json!({ "contextId": self.context_id, - "expression": prefix, + "expression": expr, "throwOnSideEffect": true, "timeout": 200, })), @@ -98,8 +85,7 @@ impl Completer for EditorHelper { .unwrap(); if evaluate_response.get("exceptionDetails").is_some() { - let candidates = Vec::new(); - return Ok((pos, candidates)); + return Vec::new(); } if let Some(result) = evaluate_response.get("result") { @@ -113,32 +99,83 @@ impl Completer for EditorHelper { if let Ok(get_properties_response) = get_properties_response { if let Some(result) = get_properties_response.get("result") { - let candidates = result + let property_names = result .as_array() .unwrap() .iter() - .filter_map(|r| { - let name = r.get("name").unwrap().as_str().unwrap().to_string(); - - if name.starts_with("Symbol(") { - return None; - } - - if name.starts_with(&suffix[1..]) { - return Some(name); - } - - None - }) + .map(|r| r.get("name").unwrap().as_str().unwrap().to_string()) .collect(); - return Ok((pos - (suffix.len() - 1), candidates)); + return property_names; } } } } - Ok((pos, Vec::new())) + Vec::new() + } +} + +fn is_word_boundary(c: char) -> bool { + if c == '.' { + false + } else { + char::is_ascii_whitespace(&c) || char::is_ascii_punctuation(&c) + } +} + +fn get_expr_from_line_at_pos(line: &str, cursor_pos: usize) -> &str { + let start = line[..cursor_pos] + .rfind(is_word_boundary) + .map_or_else(|| 0, |i| i); + let end = line[cursor_pos..] + .rfind(is_word_boundary) + .map_or_else(|| cursor_pos, |i| cursor_pos + i); + + let word = &line[start..end]; + let word = word.strip_prefix(is_word_boundary).unwrap_or(word); + let word = word.strip_suffix(is_word_boundary).unwrap_or(word); + + word +} + +impl Completer for EditorHelper { + type Candidate = String; + + fn complete( + &self, + line: &str, + pos: usize, + _ctx: &Context<'_>, + ) -> Result<(usize, Vec<String>), ReadlineError> { + let expr = get_expr_from_line_at_pos(line, pos); + + // check if the expression is in the form `obj.prop` + if let Some(index) = expr.rfind('.') { + let sub_expr = &expr[..index]; + let prop_name = &expr[index + 1..]; + let candidates = self + .get_expression_property_names(sub_expr) + .into_iter() + .filter(|n| !n.starts_with("Symbol(") && n.starts_with(prop_name)) + .collect(); + + Ok((pos - prop_name.len(), candidates)) + } else { + // combine results of declarations and globalThis properties + let mut candidates = self + .get_expression_property_names("globalThis") + .into_iter() + .chain(self.get_global_lexical_scope_names()) + .filter(|n| n.starts_with(expr)) + .collect::<Vec<_>>(); + + // sort and remove duplicates + candidates.sort(); + candidates.dedup(); // make sure to sort first + + Ok((pos - expr.len(), candidates)) + } } } |