Skip to content

Commit

Permalink
fix(chisel): Windows prompt (#4505)
Browse files Browse the repository at this point in the history
* fix: Chisel Windows prompt

* fix(chisel): windows prompts

* fix: move prompt highlighting to SolidityHelper

* docs

---------

Co-authored-by: DaniPopes <danipopes1@proton.me>
  • Loading branch information
DaniPopes and DaniPopes committed Mar 14, 2023
1 parent df8ab09 commit 1e0b628
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 24 deletions.
16 changes: 11 additions & 5 deletions chisel/src/bin/chisel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,17 @@ pub enum ChiselParserSub {

#[tokio::main]
async fn main() -> eyre::Result<()> {
#[cfg(windows)]
if !Paint::enable_windows_ascii() {
Paint::disable()
}

// Parse command args
let args = ChiselParser::parse();

// Keeps track of whether or not an interrupt was the last input
let mut interrupt = false;

// Create a new rustyline Editor
let mut rl = Editor::<SolidityHelper>::new()?;
rl.set_helper(Some(SolidityHelper));

// Load configuration
let (config, evm_opts) = args.load_config_and_evm_opts()?;

Expand Down Expand Up @@ -127,6 +128,10 @@ async fn main() -> eyre::Result<()> {
None => { /* No chisel subcommand present; Continue */ }
}

// Create a new rustyline Editor
let mut rl = Editor::<SolidityHelper>::new()?;
rl.set_helper(Some(SolidityHelper::default()));

// Print welcome header
println!("Welcome to Chisel! Type `{}` to show available commands.", Paint::green("!help"));

Expand All @@ -135,9 +140,10 @@ async fn main() -> eyre::Result<()> {
// Get the prompt from the dispatcher
// Variable based on status of the last entry
let prompt = dispatcher.get_prompt();
rl.helper_mut().unwrap().set_errored(dispatcher.errored);

// Read the next line
let next_string = rl.readline(prompt.as_str());
let next_string = rl.readline(prompt.as_ref());

// Try to read the string
match next_string {
Expand Down
32 changes: 19 additions & 13 deletions chisel/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ use regex::Regex;
use reqwest::Url;
use serde::{Deserialize, Serialize};
use solang_parser::diagnostics::Diagnostic;
use std::{error::Error, io::Write, path::PathBuf, process::Command, str::FromStr};
use std::{borrow::Cow, error::Error, io::Write, path::PathBuf, process::Command, str::FromStr};
use strum::IntoEnumIterator;
use yansi::Paint;

/// Prompt arrow slice
/// Prompt arrow character
pub static PROMPT_ARROW: char = '➜';
static DEFAULT_PROMPT: &str = "➜ ";

/// Command leader character
pub static COMMAND_LEADER: char = '!';
/// Chisel character
Expand Down Expand Up @@ -121,17 +123,21 @@ impl ChiselDispatcher {
ChiselSession::new(config).map(|session| Self { errored: false, session })
}

/// Returns the prompt given the last input's error status
pub fn get_prompt(&self) -> String {
format!(
"{}{} ",
self.session
.id
.as_ref()
.map(|id| format!("({}: {}) ", Paint::cyan("ID"), Paint::yellow(id)))
.unwrap_or_default(),
if self.errored { Paint::red(PROMPT_ARROW) } else { Paint::green(PROMPT_ARROW) }
)
/// Returns the prompt based on the current status of the Dispatcher
pub fn get_prompt(&self) -> Cow<'static, str> {
match self.session.id.as_deref() {
// `(ID: {id}) ➜ `
Some(id) => {
let mut prompt = String::with_capacity(DEFAULT_PROMPT.len() + id.len() + 7);
prompt.push_str("(ID: ");
prompt.push_str(id);
prompt.push_str(") ");
prompt.push_str(DEFAULT_PROMPT);
Cow::Owned(prompt)
}
// `➜ `
None => Cow::Borrowed(DEFAULT_PROMPT),
}
}

/// Dispatches a [ChiselCommand]
Expand Down
82 changes: 76 additions & 6 deletions chisel/src/solidity_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
//! This module contains the `SolidityHelper`, a [rustyline::Helper] implementation for
//! usage in Chisel. It is ported from [soli](https://github.com/jpopesculian/soli/blob/master/src/main.rs).

use crate::prelude::{ChiselCommand, COMMAND_LEADER};
use crate::{
dispatcher::PROMPT_ARROW,
prelude::{ChiselCommand, COMMAND_LEADER},
};
use rustyline::{
completion::Completer,
highlight::Highlighter,
Expand Down Expand Up @@ -34,9 +37,24 @@ const MAX_ANSI_LEN: usize = 9;
pub type SpannedStyle = (usize, Style, usize);

/// A rustyline helper for Solidity code
pub struct SolidityHelper;
#[derive(Clone, Debug, Default)]
pub struct SolidityHelper {
/// Whether the dispatcher has errored.
pub errored: bool,
}

impl SolidityHelper {
/// Create a new SolidityHelper.
pub fn new() -> Self {
Self::default()
}

/// Set the errored field.
pub fn set_errored(&mut self, errored: bool) -> &mut Self {
self.errored = errored;
self
}

/// Get styles for a solidity source string
pub fn get_styles(input: &str) -> Vec<SpannedStyle> {
let mut comments = Vec::with_capacity(DEFAULT_COMMENTS);
Expand Down Expand Up @@ -185,9 +203,20 @@ impl SolidityHelper {
/// `Paint::is_enabled`
#[inline]
fn paint_unchecked(string: &str, style: Style, out: &mut String) {
let _ = style.fmt_prefix(out);
out.push_str(string);
let _ = style.fmt_suffix(out);
if style == Style::default() {
out.push_str(string);
} else {
let _ = style.fmt_prefix(out);
out.push_str(string);
let _ = style.fmt_suffix(out);
}
}

#[inline]
fn paint_unchecked_owned(string: &str, style: Style) -> String {
let mut out = String::with_capacity(MAX_ANSI_LEN + string.len());
Self::paint_unchecked(string, style, &mut out);
out
}

/// Whether to skip parsing this input due to known errors or panics
Expand All @@ -207,6 +236,41 @@ impl Highlighter for SolidityHelper {
fn highlight_char(&self, line: &str, pos: usize) -> bool {
pos == line.len()
}

fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
&'s self,
prompt: &'p str,
_default: bool,
) -> Cow<'b, str> {
if !Paint::is_enabled() {
return Cow::Borrowed(prompt)
}

let mut out = prompt.to_string();

// `^(\(ID: .*?\) )? ➜ `
if prompt.starts_with("(ID: ") {
let id_end = prompt.find(')').unwrap();
let id_span = 5..id_end;
let id = &prompt[id_span.clone()];
out.replace_range(id_span, &Self::paint_unchecked_owned(id, Color::Yellow.style()));
out.replace_range(1..=2, &Self::paint_unchecked_owned("ID", Color::Cyan.style()));
}

if let Some(i) = out.find(PROMPT_ARROW) {
let style = if self.errored { Color::Red.style() } else { Color::Green.style() };

let mut arrow = String::with_capacity(MAX_ANSI_LEN + 4);

let _ = style.fmt_prefix(&mut arrow);
arrow.push(PROMPT_ARROW);
let _ = style.fmt_suffix(&mut arrow);

out.replace_range(i..=i + 2, &arrow);
}

Cow::Owned(out)
}
}

impl Validator for SolidityHelper {
Expand Down Expand Up @@ -236,7 +300,8 @@ impl<'a> TokenStyle for Token<'a> {
fn style(&self) -> Style {
use Token::*;
match self {
StringLiteral(_, _) => Style::new(Color::Green),
StringLiteral(_, _) => Color::Green.style(),

AddressLiteral(_) |
HexLiteral(_) |
Number(_, _) |
Expand All @@ -245,18 +310,23 @@ impl<'a> TokenStyle for Token<'a> {
True |
False |
This => Color::Yellow.style(),

Memory | Storage | Calldata | Public | Private | Internal | External | Constant |
Pure | View | Payable | Anonymous | Indexed | Abstract | Virtual | Override |
Modifier | Immutable | Unchecked => Color::Cyan.style(),

Contract | Library | Interface | Function | Pragma | Import | Struct | Event |
Enum | Type | Constructor | As | Is | Using | New | Delete | Do | Continue |
Break | Throw | Emit | Return | Returns | Revert | For | While | If | Else | Try |
Catch | Assembly | Let | Leave | Switch | Case | Default | YulArrow | Arrow => {
Color::Magenta.style()
}

Uint(_) | Int(_) | Bytes(_) | Byte | DynamicBytes | Bool | Address | String |
Mapping => Color::Blue.style(),

Identifier(_) => Style::default(),

_ => Style::default(),
}
}
Expand Down

0 comments on commit 1e0b628

Please sign in to comment.