mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
fix(repl): Complete declarations (#10963)
This commit is contained in:
parent
4cbc4a7eb3
commit
b4026dac9c
2 changed files with 117 additions and 51 deletions
|
@ -2085,6 +2085,35 @@ mod integration {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn pty_complete_declarations() {
|
||||
use std::io::Read;
|
||||
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() {
|
||||
master.write_all(b"class MyClass {}\n").unwrap();
|
||||
master.write_all(b"My\t\n").unwrap();
|
||||
master.write_all(b"let myVar;\n").unwrap();
|
||||
master.write_all(b"myV\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("> 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_ignore_symbols() {
|
||||
|
|
|
@ -50,47 +50,34 @@ impl EditorHelper {
|
|||
self.message_tx.send((method.to_string(), params))?;
|
||||
self.response_rx.recv()?
|
||||
}
|
||||
|
||||
fn get_global_lexical_scope_names(&self) -> Vec<String> {
|
||||
let evaluate_response = self
|
||||
.post_message(
|
||||
"Runtime.globalLexicalScopeNames",
|
||||
Some(json!({
|
||||
"executionContextId": self.context_id,
|
||||
})),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
evaluate_response
|
||||
.get("names")
|
||||
.unwrap()
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|n| n.as_str().unwrap().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
let (prefix, suffix) = match word.rfind('.') {
|
||||
Some(index) => word.split_at(index),
|
||||
None => ("globalThis", fallback.as_str()),
|
||||
};
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue