Skip to content

Commit

Permalink
Add eval support (#86)
Browse files Browse the repository at this point in the history
Adds support for evaluating commands in source files on reload.

This looks for line commands starting with `-- $>` or multiline commands
delimited by `{- $>` and `<$ -}`.

This also removes the `failed_modules` mechanism from `Ghci`, which was
populated by extracting filenames from error messages, and replaces it
with a `targets` `ModuleSet` populated by `:show targets`. This should
be more consistent.

Finally, paths will be printed as relative paths when possible.
  • Loading branch information
9999years authored Sep 22, 2023
1 parent 01a3a80 commit 93ab3a0
Show file tree
Hide file tree
Showing 27 changed files with 1,508 additions and 127 deletions.
29 changes: 29 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ miette = { version = "5.9.0", features = ["fancy"] }
nix = { version = "0.26.2", default_features = false, features = ["process"] }
once_cell = "1.18.0"
owo-colors = { version = "3.5.0", features = ["supports-colors"] }
path-absolutize = "3.1.1"
pathdiff = { version = "0.2.1", features = ["camino"] }
regex = { version = "1.9.3", default-features = false, features = ["perf", "std"] }
shell-words = "1.1.0"
strip-ansi-escapes = "0.2.0"
Expand Down
32 changes: 32 additions & 0 deletions src/clap/ghci_command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use clap::builder::StringValueParser;
use clap::builder::TypedValueParser;
use clap::builder::ValueParserFactory;

use crate::ghci::GhciCommand;

/// [`clap`] parser for [`GhciCommand`] values.
#[derive(Default, Clone)]
pub struct GhciCommandParser {
inner: StringValueParser,
}

impl TypedValueParser for GhciCommandParser {
type Value = GhciCommand;

fn parse_ref(
&self,
cmd: &clap::Command,
arg: Option<&clap::Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> {
self.inner.parse_ref(cmd, arg, value).map(GhciCommand)
}
}

impl ValueParserFactory for GhciCommand {
type Parser = GhciCommandParser;

fn value_parser() -> Self::Parser {
Self::Parser::default()
}
}
2 changes: 2 additions & 0 deletions src/clap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ mod camino;
mod clonable_command;
mod error_message;
mod fmt_span;
mod ghci_command;
mod humantime;
mod rust_backtrace;

pub use self::humantime::DurationValueParser;
pub use clonable_command::ClonableCommandParser;
pub use error_message::value_validation_error;
pub use fmt_span::FmtSpanParserFactory;
pub use ghci_command::GhciCommandParser;
pub use rust_backtrace::RustBacktrace;
12 changes: 10 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use tracing_subscriber::fmt::format::FmtSpan;
use crate::clap::FmtSpanParserFactory;
use crate::clap::RustBacktrace;
use crate::command::ClonableCommand;
use crate::ghci::GhciCommand;

/// A `ghci`-based file watcher and Haskell recompiler.
#[derive(Debug, Clone, Parser)]
Expand All @@ -27,7 +28,7 @@ pub struct Opts {
/// A `ghci` command which runs tests, like `TestMain.testMain`. If given, this command will be
/// run after reloads.
#[arg(long, value_name = "GHCI_COMMAND")]
pub test_ghci: Option<String>,
pub test_ghci: Option<GhciCommand>,

/// Shell commands to run before starting or restarting `ghci`.
///
Expand All @@ -38,12 +39,19 @@ pub struct Opts {
/// `ghci` commands to run on startup. Use `:set args ...` in combination with `--test` to set
/// the command-line arguments for tests.
#[arg(long, value_name = "GHCI_COMMAND")]
pub after_startup_ghci: Vec<String>,
pub after_startup_ghci: Vec<GhciCommand>,

/// A file to write compilation errors to. This is analogous to `ghcid.txt`.
#[arg(long)]
pub errors: Option<Utf8PathBuf>,

/// Enable evaluating commands.
///
/// This parses line commands starting with `-- $>` or multiline commands delimited by `{- $>`
/// and `<$ -}` and evaluates them after reloads.
#[arg(long)]
pub enable_eval: bool,

/// Options to modify file watching.
#[command(flatten)]
pub watch: WatchOpts,
Expand Down
46 changes: 46 additions & 0 deletions src/ghci/ghci_command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::fmt::Debug;
use std::fmt::Display;

/// A `ghci` command.
///
/// This is a string that can be written to a `ghci` session, typically a Haskell expression or
/// `ghci` command starting with `:`.
#[derive(Clone, PartialEq, Eq)]
pub struct GhciCommand(pub String);

impl GhciCommand {
/// Consume this command, producing the wrapped string.
pub fn into_string(self) -> String {
self.0
}
}

impl Debug for GhciCommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.0, f)
}
}

impl Display for GhciCommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, f)
}
}

impl From<String> for GhciCommand {
fn from(value: String) -> Self {
Self(value)
}
}

impl From<GhciCommand> for String {
fn from(value: GhciCommand) -> Self {
value.into_string()
}
}

impl AsRef<str> for GhciCommand {
fn as_ref(&self) -> &str {
&self.0
}
}
Loading

0 comments on commit 93ab3a0

Please sign in to comment.