summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2021-07-08 12:58:18 -0400
committerGitHub <noreply@github.com>2021-07-08 12:58:18 -0400
commitd9c43f7f43905376c908d9bb534a0b04a8727195 (patch)
treed26acdab08074015bd5685cf80a38b1cd9ccd0f7
parent27e1b4cb5ac81c5ac2ca5adf78c91fdbef9409e8 (diff)
feat(repl): support autocomplete on declarations containing a primitive (#11325)
-rw-r--r--cli/tests/integration/repl_tests.rs108
-rw-r--r--cli/tools/repl.rs116
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()
}
}