Skip to content

Commit

Permalink
Merge pull request #111 from mobusoperandi/no-ptmx
Browse files Browse the repository at this point in the history
feat: does not rely on a pty
  • Loading branch information
warren2k authored Jun 15, 2024
2 parents 3eca8ce + e8adac0 commit 70607b5
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 105 deletions.
46 changes: 21 additions & 25 deletions Cargo.lock

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

9 changes: 2 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,9 @@ futures = "0.3.28"
glob = "0.3.1"
indoc = "2.0.3"
itertools = "0.11.0"
nix = "0.28.0"
strip-ansi-escapes = "0.1.1"
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread", "io-util"] }

[dependencies.pty-process]
version = "0.4.0"
features = ["async"]
git = "https://github.com/mobusoperandi/pty-process.git"
rev = "7889630"
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread", "io-util", "process", "fs"] }

[dev-dependencies]
assert_cmd = "2.0.12"
Expand Down
92 changes: 59 additions & 33 deletions src/app/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl State {

fn repl_event_spawn(
&mut self,
spawn: Result<ExampleId, pty_process::Error>,
spawn: Result<ExampleId, std::io::Error>,
) -> anyhow::Result<Vec<OutputEvent>> {
let id = spawn?;

Expand Down Expand Up @@ -128,48 +128,46 @@ impl State {
let session_live = session_live.state.live_mut()?;

let output = match &mut session_live.expecting {
ReplSessionExpecting::Nothing => anyhow::bail!("not expecting, got {:?}", ch),
ReplSessionExpecting::Prompt(acc) => {
acc.push(ch);
let string = String::from_utf8(strip_ansi_escapes::strip(acc)?)?;

if string.ends_with("nix-repl> ") {
session_live.expecting = ReplSessionExpecting::Nothing;
self.next_query(&id)?
} else {
vec![]
ReplSessionExpecting::ClearlineBeforeInitialPrompt { cl_progress } => {
use ClearLineProgressStatus::*;
match cl_progress.clone().character(ch)? {
InProgress(progress) => {
*cl_progress = progress;
vec![]
}
ReachedEnd => self.next_query(&id)?,
}
}
ReplSessionExpecting::Echo {
acc,
last_query: expected,
ReplSessionExpecting::ClearLineBeforeResult {
cl_progress,
expected_result,
} => {
acc.push(ch);
if !acc.ends_with('\n') {
vec![]
} else if Self::sanitize(acc)? == expected.as_str() {
session_live.expecting = ReplSessionExpecting::ResultAndNextPrompt {
acc: String::new(),
expected_result: expected_result.clone(),
};
vec![]
} else {
anyhow::bail!("actual: {acc:?}, expected: {expected:?}");
}
use ClearLineProgressStatus::*;
match cl_progress.clone().character(ch)? {
InProgress(progress) => {
*cl_progress = progress;
}
ReachedEnd => {
session_live.expecting =
ReplSessionExpecting::ResultAndClearlineBeforeNextPrompt {
acc: String::new(),
expected_result: expected_result.clone(),
};
}
};
vec![]
}
ReplSessionExpecting::ResultAndNextPrompt {
ReplSessionExpecting::ResultAndClearlineBeforeNextPrompt {
acc,
expected_result,
} => 'arm: {
acc.push(ch);

let sanitized = Self::sanitize(acc)?;

let Some(result) = sanitized.strip_suffix("\nnix-repl> ") else {
let Some(result) = acc.strip_suffix(CLEAR_LINE) else {
break 'arm vec![];
};

let result = Self::sanitize(result)?;
let result = result.trim_end_matches('\n');

if result != expected_result.as_str() {
Expand Down Expand Up @@ -208,9 +206,8 @@ impl State {
return self.session_end(id);
};

session_live.expecting = ReplSessionExpecting::Echo {
acc: String::new(),
last_query: entry.query.clone(),
session_live.expecting = ReplSessionExpecting::ClearLineBeforeResult {
cl_progress: ClearLineProgress::new(),
expected_result: entry.expected_result,
};

Expand Down Expand Up @@ -341,3 +338,32 @@ pub(crate) enum ExampleState {
Repl(ReplExampleState),
Expression(ExpressionExampleState),
}

const CLEAR_LINE: &str = "\r\u{1b}[K";

#[derive(Debug, Clone)]
pub struct ClearLineProgress(std::iter::Peekable<std::str::Chars<'static>>);

impl ClearLineProgress {
fn character(mut self, ch: char) -> anyhow::Result<ClearLineProgressStatus> {
let expected = self.0.next().unwrap();
if ch != expected {
bail!("expected {expected:?}, got {ch:?}")
}
Ok(if self.0.peek().is_none() {
ClearLineProgressStatus::ReachedEnd
} else {
ClearLineProgressStatus::InProgress(self)
})
}

fn new() -> Self {
Self(CLEAR_LINE.chars().peekable())
}
}

#[derive(Debug, Clone)]
enum ClearLineProgressStatus {
InProgress(ClearLineProgress),
ReachedEnd,
}
20 changes: 12 additions & 8 deletions src/app/state/repl_state.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::repl::{
driver::{LFLine, ReplQuery},
driver::LFLine,
example::{ReplEntry, ReplExample, ReplExampleEntries},
};

use super::ClearLineProgress;

#[derive(Debug)]
pub(crate) struct ReplExampleState {
pub(crate) example: ReplExample,
Expand Down Expand Up @@ -44,14 +46,14 @@ pub(crate) struct ReplSessionLive {

#[derive(Debug)]
pub(crate) enum ReplSessionExpecting {
Nothing,
Prompt(String),
Echo {
acc: String,
last_query: ReplQuery,
ClearlineBeforeInitialPrompt {
cl_progress: ClearLineProgress,
},
ClearLineBeforeResult {
cl_progress: ClearLineProgress,
expected_result: ExpectedResult,
},
ResultAndNextPrompt {
ResultAndClearlineBeforeNextPrompt {
acc: String,
expected_result: ExpectedResult,
},
Expand All @@ -61,7 +63,9 @@ impl ReplSessionLive {
pub(crate) fn new(entries: ReplExampleEntries) -> Self {
Self {
iterator: entries.into_iter(),
expecting: ReplSessionExpecting::Prompt(String::new()),
expecting: ReplSessionExpecting::ClearlineBeforeInitialPrompt {
cl_progress: ClearLineProgress::new(),
},
}
}
}
Expand Down
Loading

0 comments on commit 70607b5

Please sign in to comment.