summaryrefslogtreecommitdiff
path: root/cli/tools/repl.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2021-06-15 09:31:36 -0400
committerGitHub <noreply@github.com>2021-06-15 09:31:36 -0400
commitb4026dac9c4fb2b6cfc4a7015b4bacd102e06d08 (patch)
tree6c092c667d0bb25c07bbaff7e0bc521288bbc9ab /cli/tools/repl.rs
parent4cbc4a7eb321ae2808cf3dce9428f45aaed0e338 (diff)
fix(repl): Complete declarations (#10963)
Diffstat (limited to 'cli/tools/repl.rs')
-rw-r--r--cli/tools/repl.rs139
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))
+ }
}
}