diff options
Diffstat (limited to 'cli/tools')
-rw-r--r-- | cli/tools/repl/channel.rs | 53 | ||||
-rw-r--r-- | cli/tools/repl/mod.rs | 66 |
2 files changed, 104 insertions, 15 deletions
diff --git a/cli/tools/repl/channel.rs b/cli/tools/repl/channel.rs index 54ec6869d..a02f88523 100644 --- a/cli/tools/repl/channel.rs +++ b/cli/tools/repl/channel.rs @@ -11,6 +11,8 @@ use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; +use crate::lsp::ReplCompletionItem; + /// Rustyline uses synchronous methods in its interfaces, but we need to call /// async methods. To get around this, we communicate with async code by using /// a channel and blocking on the result. @@ -31,8 +33,21 @@ pub fn rustyline_channel( ) } -pub type RustylineSyncMessage = (String, Option<Value>); -pub type RustylineSyncResponse = Result<Value, AnyError>; +pub enum RustylineSyncMessage { + PostMessage { + method: String, + params: Option<Value>, + }, + LspCompletions { + line_text: String, + position: usize, + }, +} + +pub enum RustylineSyncResponse { + PostMessage(Result<Value, AnyError>), + LspCompletions(Vec<ReplCompletionItem>), +} pub struct RustylineSyncMessageSender { message_tx: Sender<RustylineSyncMessage>, @@ -46,11 +61,41 @@ impl RustylineSyncMessageSender { params: Option<Value>, ) -> Result<Value, AnyError> { if let Err(err) = - self.message_tx.blocking_send((method.to_string(), params)) + self + .message_tx + .blocking_send(RustylineSyncMessage::PostMessage { + method: method.to_string(), + params, + }) { Err(anyhow!("{}", err)) } else { - self.response_rx.borrow_mut().blocking_recv().unwrap() + match self.response_rx.borrow_mut().blocking_recv().unwrap() { + RustylineSyncResponse::PostMessage(result) => result, + RustylineSyncResponse::LspCompletions(_) => unreachable!(), + } + } + } + + pub fn lsp_completions( + &self, + line_text: &str, + position: usize, + ) -> Vec<ReplCompletionItem> { + if self + .message_tx + .blocking_send(RustylineSyncMessage::LspCompletions { + line_text: line_text.to_string(), + position, + }) + .is_err() + { + Vec::new() + } else { + match self.response_rx.borrow_mut().blocking_recv().unwrap() { + RustylineSyncResponse::LspCompletions(result) => result, + RustylineSyncResponse::PostMessage(_) => unreachable!(), + } } } } diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index 687e8a300..e971d0a7a 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -4,7 +4,10 @@ use crate::ast::transpile; use crate::ast::Diagnostics; use crate::ast::ImportsNotUsedAsValues; use crate::colors; +use crate::lsp::ReplLanguageServer; use crate::proc_state::ProcState; +use crate::tools::repl::channel::RustylineSyncMessage; +use crate::tools::repl::channel::RustylineSyncResponse; use deno_ast::swc::parser::error::SyntaxError; use deno_ast::swc::parser::token::Token; use deno_ast::swc::parser::token::Word; @@ -181,6 +184,15 @@ impl Completer for EditorHelper { pos: usize, _ctx: &Context<'_>, ) -> Result<(usize, Vec<String>), ReadlineError> { + let lsp_completions = self.sync_sender.lsp_completions(line, pos); + if !lsp_completions.is_empty() { + // assumes all lsp completions have the same start position + return Ok(( + lsp_completions[0].span.lo.0 as usize, + lsp_completions.into_iter().map(|c| c.new_text).collect(), + )); + } + let expr = get_expr_from_line_at_pos(line, pos); // check if the expression is in the form `obj.prop` @@ -428,14 +440,21 @@ impl std::fmt::Display for EvaluationOutput { } } +struct TsEvaluateResponse { + ts_code: String, + value: Value, +} + struct ReplSession { worker: MainWorker, session: LocalInspectorSession, pub context_id: u64, + language_server: ReplLanguageServer, } impl ReplSession { pub async fn initialize(mut worker: MainWorker) -> Result<Self, AnyError> { + let language_server = ReplLanguageServer::new_initialized().await?; let mut session = worker.create_inspector_session().await; worker @@ -467,6 +486,7 @@ impl ReplSession { worker, session, context_id, + language_server, }; // inject prelude @@ -520,13 +540,18 @@ impl ReplSession { match self.evaluate_line_with_object_wrapping(line).await { Ok(evaluate_response) => { - let evaluate_result = evaluate_response.get("result").unwrap(); + let evaluate_result = evaluate_response.value.get("result").unwrap(); let evaluate_exception_details = - evaluate_response.get("exceptionDetails"); + evaluate_response.value.get("exceptionDetails"); if evaluate_exception_details.is_some() { self.set_last_thrown_error(evaluate_result).await?; } else { + self + .language_server + .commit_text(&evaluate_response.ts_code) + .await; + self.set_last_eval_result(evaluate_result).await?; } @@ -561,7 +586,7 @@ impl ReplSession { async fn evaluate_line_with_object_wrapping( &mut self, line: &str, - ) -> Result<Value, AnyError> { + ) -> Result<TsEvaluateResponse, AnyError> { // Expressions like { "foo": "bar" } are interpreted as block expressions at the // statement level rather than an object literal so we interpret it as an expression statement // to match the behavior found in a typical prompt including browser developer tools. @@ -582,6 +607,7 @@ impl ReplSession { || evaluate_response .as_ref() .unwrap() + .value .get("exceptionDetails") .is_some()) { @@ -658,7 +684,7 @@ impl ReplSession { async fn evaluate_ts_expression( &mut self, expression: &str, - ) -> Result<Value, AnyError> { + ) -> Result<TsEvaluateResponse, AnyError> { let parsed_module = deno_ast::parse_module(deno_ast::ParseParams { specifier: "repl.ts".to_string(), source: deno_ast::SourceTextInfo::from_string(expression.to_string()), @@ -688,12 +714,17 @@ impl ReplSession { )? .0; - self + let value = self .evaluate_expression(&format!( "'use strict'; void 0;\n{}", transpiled_src )) - .await + .await?; + + Ok(TsEvaluateResponse { + ts_code: expression.to_string(), + value, + }) } async fn evaluate_expression( @@ -727,11 +758,24 @@ async fn read_line_and_poll( return result.unwrap(); } result = message_handler.recv() => { - if let Some((method, params)) = result { - let result = repl_session - .post_message_with_event_loop(&method, params) - .await; - message_handler.send(result).unwrap(); + match result { + Some(RustylineSyncMessage::PostMessage { + method, + params + }) => { + let result = repl_session + .post_message_with_event_loop(&method, params) + .await; + message_handler.send(RustylineSyncResponse::PostMessage(result)).unwrap(); + }, + Some(RustylineSyncMessage::LspCompletions { + line_text, + position, + }) => { + let result = repl_session.language_server.completions(&line_text, position).await; + message_handler.send(RustylineSyncResponse::LspCompletions(result)).unwrap(); + } + None => {}, // channel closed } poll_worker = true; |