diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2021-07-08 12:58:18 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-08 12:58:18 -0400 |
commit | d9c43f7f43905376c908d9bb534a0b04a8727195 (patch) | |
tree | d26acdab08074015bd5685cf80a38b1cd9ccd0f7 | |
parent | 27e1b4cb5ac81c5ac2ca5adf78c91fdbef9409e8 (diff) |
feat(repl): support autocomplete on declarations containing a primitive (#11325)
-rw-r--r-- | cli/tests/integration/repl_tests.rs | 108 | ||||
-rw-r--r-- | cli/tools/repl.rs | 116 |
2 files changed, 121 insertions, 103 deletions
diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs index 43ab32232..abf3321a8 100644 --- a/cli/tests/integration/repl_tests.rs +++ b/cli/tests/integration/repl_tests.rs @@ -6,10 +6,7 @@ use test_util as util; #[test] fn pty_multiline() { use std::io::{Read, Write}; - use util::pty::fork::*; - let deno_exe = util::deno_exe_path(); - let fork = Fork::from_ptmx().unwrap(); - if let Ok(mut master) = fork.is_parent() { + run_pty_test(|master| { master.write_all(b"(\n1 + 2\n)\n").unwrap(); master.write_all(b"{\nfoo: \"foo\"\n}\n").unwrap(); master.write_all(b"`\nfoo\n`\n").unwrap(); @@ -37,24 +34,14 @@ fn pty_multiline() { assert!(output.contains("/(/")); assert!(output.contains("/{/")); assert!(output.contains("[ \"{test1}\", \"test1\" ]")); - - fork.wait().unwrap(); - } else { - std::env::set_var("NO_COLOR", "1"); - let err = exec::Command::new(deno_exe).arg("repl").exec(); - println!("err {}", err); - unreachable!() - } + }); } #[cfg(unix)] #[test] fn pty_unpaired_braces() { use std::io::{Read, Write}; - use util::pty::fork::*; - let deno_exe = util::deno_exe_path(); - let fork = Fork::from_ptmx().unwrap(); - if let Ok(mut master) = fork.is_parent() { + run_pty_test(|master| { master.write_all(b")\n").unwrap(); master.write_all(b"]\n").unwrap(); master.write_all(b"}\n").unwrap(); @@ -66,24 +53,14 @@ fn pty_unpaired_braces() { assert!(output.contains("Unexpected token `)`")); assert!(output.contains("Unexpected token `]`")); assert!(output.contains("Unexpected token `}`")); - - fork.wait().unwrap(); - } else { - std::env::set_var("NO_COLOR", "1"); - let err = exec::Command::new(deno_exe).arg("repl").exec(); - println!("err {}", err); - unreachable!() - } + }); } #[cfg(unix)] #[test] fn pty_bad_input() { use std::io::{Read, Write}; - use util::pty::fork::*; - let deno_exe = util::deno_exe_path(); - let fork = Fork::from_ptmx().unwrap(); - if let Ok(mut master) = fork.is_parent() { + run_pty_test(|master| { master.write_all(b"'\\u{1f3b5}'[0]\n").unwrap(); master.write_all(b"close();\n").unwrap(); @@ -91,24 +68,14 @@ fn pty_bad_input() { master.read_to_string(&mut output).unwrap(); assert!(output.contains("Unterminated string literal")); - - fork.wait().unwrap(); - } else { - std::env::set_var("NO_COLOR", "1"); - let err = exec::Command::new(deno_exe).arg("repl").exec(); - println!("err {}", err); - unreachable!() - } + }); } #[cfg(unix)] #[test] fn pty_complete_symbol() { use std::io::{Read, Write}; - use util::pty::fork::*; - let deno_exe = util::deno_exe_path(); - let fork = Fork::from_ptmx().unwrap(); - if let Ok(mut master) = fork.is_parent() { + run_pty_test(|master| { master.write_all(b"Symbol.it\t\n").unwrap(); master.write_all(b"close();\n").unwrap(); @@ -116,24 +83,14 @@ fn pty_complete_symbol() { master.read_to_string(&mut output).unwrap(); assert!(output.contains("Symbol(Symbol.iterator)")); - - fork.wait().unwrap(); - } else { - std::env::set_var("NO_COLOR", "1"); - let err = exec::Command::new(deno_exe).arg("repl").exec(); - println!("err {}", err); - unreachable!() - } + }); } #[cfg(unix)] #[test] fn pty_complete_declarations() { use std::io::{Read, Write}; - use util::pty::fork::*; - let deno_exe = util::deno_exe_path(); - let fork = Fork::from_ptmx().unwrap(); - if let Ok(mut master) = fork.is_parent() { + run_pty_test(|master| { master.write_all(b"class MyClass {}\n").unwrap(); master.write_all(b"My\t\n").unwrap(); master.write_all(b"let myVar;\n").unwrap(); @@ -145,24 +102,40 @@ fn pty_complete_declarations() { assert!(output.contains("> MyClass")); assert!(output.contains("> myVar")); + }); +} - fork.wait().unwrap(); - } else { - std::env::set_var("NO_COLOR", "1"); - let err = exec::Command::new(deno_exe).arg("repl").exec(); - println!("err {}", err); - unreachable!() - } +#[cfg(unix)] +#[test] +fn pty_complete_primitives() { + use std::io::{Read, Write}; + run_pty_test(|master| { + master.write_all(b"let func = function test(){}\n").unwrap(); + master.write_all(b"func.appl\t\n").unwrap(); + master.write_all(b"let str = ''\n").unwrap(); + master.write_all(b"str.leng\t\n").unwrap(); + master.write_all(b"false.valueO\t\n").unwrap(); + master.write_all(b"5n.valueO\t\n").unwrap(); + master.write_all(b"let num = 5\n").unwrap(); + master.write_all(b"num.toStrin\t\n").unwrap(); + master.write_all(b"close();\n").unwrap(); + + let mut output = String::new(); + master.read_to_string(&mut output).unwrap(); + + assert!(output.contains("> func.apply")); + assert!(output.contains("> str.length")); + assert!(output.contains("> 5n.valueOf")); + assert!(output.contains("> false.valueOf")); + assert!(output.contains("> num.toString")); + }); } #[cfg(unix)] #[test] fn pty_ignore_symbols() { use std::io::{Read, Write}; - use util::pty::fork::*; - let deno_exe = util::deno_exe_path(); - let fork = Fork::from_ptmx().unwrap(); - if let Ok(mut master) = fork.is_parent() { + run_pty_test(|master| { master.write_all(b"Array.Symbol\t\n").unwrap(); master.write_all(b"close();\n").unwrap(); @@ -173,7 +146,16 @@ fn pty_ignore_symbols() { assert!( !output.contains("Uncaught TypeError: Array.Symbol is not a function") ); + }); +} +#[cfg(unix)] +fn run_pty_test(mut run: impl FnMut(&mut util::pty::fork::Master)) { + use util::pty::fork::*; + let deno_exe = util::deno_exe_path(); + let fork = Fork::from_ptmx().unwrap(); + if let Ok(mut master) = fork.is_parent() { + run(&mut master); fork.wait().unwrap(); } else { std::env::set_var("NO_COLOR", "1"); diff --git a/cli/tools/repl.rs b/cli/tools/repl.rs index e3dbc5be8..8cde1e39c 100644 --- a/cli/tools/repl.rs +++ b/cli/tools/repl.rs @@ -47,18 +47,7 @@ struct EditorHelper { } impl EditorHelper { - fn post_message( - &self, - method: &str, - params: Option<Value>, - ) -> Result<Value, AnyError> { - self - .message_tx - .blocking_send((method.to_string(), params))?; - self.response_rx.borrow_mut().blocking_recv().unwrap() - } - - fn get_global_lexical_scope_names(&self) -> Vec<String> { + pub fn get_global_lexical_scope_names(&self) -> Vec<String> { let evaluate_response = self .post_message( "Runtime.globalLexicalScopeNames", @@ -78,7 +67,67 @@ impl EditorHelper { .collect() } - fn get_expression_property_names(&self, expr: &str) -> Vec<String> { + pub fn get_expression_property_names(&self, expr: &str) -> Vec<String> { + // try to get the properties from the expression + if let Some(properties) = self.get_object_expr_properties(expr) { + return properties; + } + + // otherwise fall back to the prototype + let expr_type = self.get_expression_type(expr); + let object_expr = match expr_type.as_deref() { + // possibilities: https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#type-RemoteObject + Some("object") => "Object.prototype", + Some("function") => "Function.prototype", + Some("string") => "String.prototype", + Some("boolean") => "Boolean.prototype", + Some("bigint") => "BigInt.prototype", + Some("number") => "Number.prototype", + _ => return Vec::new(), // undefined, symbol, and unhandled + }; + + self + .get_object_expr_properties(object_expr) + .unwrap_or_else(Vec::new) + } + + fn get_expression_type(&self, expr: &str) -> Option<String> { + self + .evaluate_expression(expr)? + .get("result")? + .get("type")? + .as_str() + .map(|s| s.to_string()) + } + + fn get_object_expr_properties( + &self, + object_expr: &str, + ) -> Option<Vec<String>> { + let evaluate_result = self.evaluate_expression(object_expr)?; + let object_id = evaluate_result.get("result")?.get("objectId")?; + + let get_properties_response = self + .post_message( + "Runtime.getProperties", + Some(json!({ + "objectId": object_id, + })), + ) + .ok()?; + + Some( + get_properties_response + .get("result")? + .as_array() + .unwrap() + .iter() + .map(|r| r.get("name").unwrap().as_str().unwrap().to_string()) + .collect(), + ) + } + + fn evaluate_expression(&self, expr: &str) -> Option<Value> { let evaluate_response = self .post_message( "Runtime.evaluate", @@ -89,37 +138,24 @@ impl EditorHelper { "timeout": 200, })), ) - .unwrap(); + .ok()?; if evaluate_response.get("exceptionDetails").is_some() { - return Vec::new(); - } - - if let Some(result) = evaluate_response.get("result") { - if let Some(object_id) = result.get("objectId") { - let get_properties_response = self.post_message( - "Runtime.getProperties", - Some(json!({ - "objectId": object_id, - })), - ); - - if let Ok(get_properties_response) = get_properties_response { - if let Some(result) = get_properties_response.get("result") { - let property_names = result - .as_array() - .unwrap() - .iter() - .map(|r| r.get("name").unwrap().as_str().unwrap().to_string()) - .collect(); - - return property_names; - } - } - } + None + } else { + Some(evaluate_response) } + } - Vec::new() + fn post_message( + &self, + method: &str, + params: Option<Value>, + ) -> Result<Value, AnyError> { + self + .message_tx + .blocking_send((method.to_string(), params))?; + self.response_rx.borrow_mut().blocking_recv().unwrap() } } |