1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-22 15:06:54 -05:00

refactor(repl): move rustyline sync channel communication into struct (#12900)

This commit is contained in:
David Sherret 2021-11-25 14:05:12 -05:00 committed by GitHub
parent f3c0f0565b
commit 6a780543a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 92 additions and 31 deletions

74
cli/tools/repl/channel.rs Normal file
View file

@ -0,0 +1,74 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::serde_json::Value;
use std::cell::RefCell;
use tokio::sync::mpsc::channel;
use tokio::sync::mpsc::unbounded_channel;
use tokio::sync::mpsc::Receiver;
use tokio::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::mpsc::UnboundedSender;
/// 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.
pub fn rustyline_channel(
) -> (RustylineSyncMessageSender, RustylineSyncMessageHandler) {
let (message_tx, message_rx) = channel(1);
let (response_tx, response_rx) = unbounded_channel();
(
RustylineSyncMessageSender {
message_tx,
response_rx: RefCell::new(response_rx),
},
RustylineSyncMessageHandler {
response_tx,
message_rx,
},
)
}
pub type RustylineSyncMessage = (String, Option<Value>);
pub type RustylineSyncResponse = Result<Value, AnyError>;
pub struct RustylineSyncMessageSender {
message_tx: Sender<RustylineSyncMessage>,
response_rx: RefCell<UnboundedReceiver<RustylineSyncResponse>>,
}
impl RustylineSyncMessageSender {
pub fn post_message(
&self,
method: &str,
params: Option<Value>,
) -> Result<Value, AnyError> {
if let Err(err) =
self.message_tx.blocking_send((method.to_string(), params))
{
Err(anyhow!("{}", err))
} else {
self.response_rx.borrow_mut().blocking_recv().unwrap()
}
}
}
pub struct RustylineSyncMessageHandler {
message_rx: Receiver<RustylineSyncMessage>,
response_tx: UnboundedSender<RustylineSyncResponse>,
}
impl RustylineSyncMessageHandler {
pub async fn recv(&mut self) -> Option<RustylineSyncMessage> {
self.message_rx.recv().await
}
pub fn send(&self, response: RustylineSyncResponse) -> Result<(), AnyError> {
self
.response_tx
.send(response)
.map_err(|err| anyhow!("{}", err))
}
}

View file

@ -5,7 +5,8 @@ use crate::ast::ImportsNotUsedAsValues;
use crate::colors;
use crate::proc_state::ProcState;
use deno_ast::swc::parser::error::SyntaxError;
use deno_ast::swc::parser::token::{Token, Word};
use deno_ast::swc::parser::token::Token;
use deno_ast::swc::parser::token::Word;
use deno_core::error::AnyError;
use deno_core::futures::FutureExt;
use deno_core::parking_lot::Mutex;
@ -25,28 +26,27 @@ use rustyline::Context;
use rustyline::Editor;
use rustyline_derive::{Helper, Hinter};
use std::borrow::Cow;
use std::cell::RefCell;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::mpsc::channel;
use tokio::sync::mpsc::unbounded_channel;
use tokio::sync::mpsc::Receiver;
use tokio::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::mpsc::UnboundedSender;
mod channel;
use channel::rustyline_channel;
use channel::RustylineSyncMessageHandler;
use channel::RustylineSyncMessageSender;
// Provides helpers to the editor like validation for multi-line edits, completion candidates for
// tab completion.
#[derive(Helper, Hinter)]
struct EditorHelper {
context_id: u64,
message_tx: Sender<(String, Option<Value>)>,
response_rx: RefCell<UnboundedReceiver<Result<Value, AnyError>>>,
sync_sender: RustylineSyncMessageSender,
}
impl EditorHelper {
pub fn get_global_lexical_scope_names(&self) -> Vec<String> {
let evaluate_response = self
.sync_sender
.post_message(
"Runtime.globalLexicalScopeNames",
Some(json!({
@ -106,6 +106,7 @@ impl EditorHelper {
let object_id = evaluate_result.get("result")?.get("objectId")?;
let get_properties_response = self
.sync_sender
.post_message(
"Runtime.getProperties",
Some(json!({
@ -127,6 +128,7 @@ impl EditorHelper {
fn evaluate_expression(&self, expr: &str) -> Option<Value> {
let evaluate_response = self
.sync_sender
.post_message(
"Runtime.evaluate",
Some(json!({
@ -144,17 +146,6 @@ impl EditorHelper {
Some(evaluate_response)
}
}
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 is_word_boundary(c: char) -> bool {
@ -705,8 +696,7 @@ impl ReplSession {
async fn read_line_and_poll(
repl_session: &mut ReplSession,
message_rx: &mut Receiver<(String, Option<Value>)>,
response_tx: &UnboundedSender<Result<Value, AnyError>>,
message_handler: &mut RustylineSyncMessageHandler,
editor: ReplEditor,
) -> Result<String, ReadlineError> {
let mut line_fut = tokio::task::spawn_blocking(move || editor.readline());
@ -717,12 +707,12 @@ async fn read_line_and_poll(
result = &mut line_fut => {
return result.unwrap();
}
result = message_rx.recv() => {
result = message_handler.recv() => {
if let Some((method, params)) = result {
let result = repl_session
.post_message_with_event_loop(&method, params)
.await;
response_tx.send(result).unwrap();
message_handler.send(result).unwrap();
}
poll_worker = true;
@ -740,13 +730,11 @@ pub async fn run(
maybe_eval: Option<String>,
) -> Result<(), AnyError> {
let mut repl_session = ReplSession::initialize(worker).await?;
let (message_tx, mut message_rx) = channel(1);
let (response_tx, response_rx) = unbounded_channel();
let mut rustyline_channel = rustyline_channel();
let helper = EditorHelper {
context_id: repl_session.context_id,
message_tx,
response_rx: RefCell::new(response_rx),
sync_sender: rustyline_channel.0,
};
let history_file_path = ps.dir.root.join("deno_history.txt");
@ -766,8 +754,7 @@ pub async fn run(
loop {
let line = read_line_and_poll(
&mut repl_session,
&mut message_rx,
&response_tx,
&mut rustyline_channel.1,
editor.clone(),
)
.await;