Skip to content

Commit

Permalink
WIP: Add newline support
Browse files Browse the repository at this point in the history
  • Loading branch information
shelvacu committed Oct 21, 2021
1 parent 3d8d581 commit 8127e99
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 31 deletions.
9 changes: 9 additions & 0 deletions dev.bash
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ if ! this_dir=$(cd "$(dirname "$0")" && pwd); then
exit $?
fi

MCFLY_BIN_PATH=`mktemp -d`
ln -s $(readlink -f $(find target/debug/deps/mcfly-* -maxdepth 1 -type f | grep -v '\.d')) $MCFLY_BIN_PATH/mcfly

rm -f target/debug/mcfly
cargo build
# For some reason, to get line numbers in backtraces, we have to run the binary directly.
HISTFILE=$HOME/.bash_history \
<<<<<<< HEAD
MCFLY_PATH=target/debug/mcfly \
||||||| parent of 1b981b2 (WIP: Add newline support)
MCFLY_PATH=$(find target/debug/deps/mcfly-* -maxdepth 1 -type f | grep -v '\.d') \
=======
PATH="$MCFLY_BIN_PATH:$PATH" \
>>>>>>> 1b981b2 (WIP: Add newline support)
RUST_BACKTRACE=full \
MCFLY_DEBUG=1 \
PATH=target/debug/:$PATH \
Expand Down
17 changes: 6 additions & 11 deletions mcfly.bash
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ MCFLY_SESSION_ID="$(command dd if=/dev/urandom bs=256 count=1 2> /dev/null | LC_
export MCFLY_SESSION_ID

# Find the binary
MCFLY_PATH=${MCFLY_PATH:-$(which mcfly)}
if [ -z "$MCFLY_PATH" ]; then
if [ -z "$(which mcfly)" ]; then
echo "Cannot find the mcfly binary, please make sure that mcfly is in your path before sourcing mcfly.bash."
return 1
fi
Expand All @@ -44,15 +43,11 @@ function mcfly_prompt_command {
command tail -n100 "${HISTFILE}" >| "${MCFLY_HISTORY}"
fi

history -a "${MCFLY_HISTORY}" # Append history to $MCFLY_HISTORY.
# Run mcfly with the saved code. It will:
# * append commands to $HISTFILE, (~/.bash_history by default)
# for backwards compatibility and to load in new terminal sessions;
# * find the text of the last command in $MCFLY_HISTORY and save it to the database.
$MCFLY_PATH add --exit ${exit_code} --append-to-histfile
# Clear the in-memory history and reload it from $MCFLY_HISTORY
# (to remove instances of '#mcfly: ' from the local session history).
history -cr "${MCFLY_HISTORY}"
# Run mcfly with the last history entry on stdin. It will:
# * Append the command to $HISTFILE, (~/.bash_history by default)
# * Parse out the command and and save it to the database.
HISTTIMEFORMAT="%s:" history 1 | mcfly add --exit $exit_code --command-from-stdin

return ${exit_code} # Restore the original exit code by returning it.
}

Expand Down
48 changes: 28 additions & 20 deletions src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ impl<'a> Interface<'a> {
self.selection = self.matches.len() - 1;
}

let mut line_offset = 0;
for (index, command) in self.matches.iter().enumerate() {
let mut fg = if self.settings.lightmode {
color::Fg(color::Black).to_string()
Expand Down Expand Up @@ -231,34 +232,39 @@ impl<'a> Interface<'a> {
}

write!(screen, "{}{}", fg, bg).unwrap();

let command_display = Interface::truncate_for_display(
command,
&self.input.command,
width,
highlight,
fg,
self.debug
);

let mut lines_count = 0;

let command_line_index = self.command_line_index(index as i16);

write!(
screen,
"{}{}",
cursor::Goto(
1,
(command_line_index as i16 + result_top_index as i16) as u16
),
Interface::truncate_for_display(
command,
&self.input.command,
width,
highlight,
fg,
self.debug
for line in command_display.lines() {
write!(
screen,
"{}{}",
cursor::Goto(
1,
(line_offset as i16 + lines_count as i16 + result_top_index as i16) as u16
),
line
)
)
.unwrap();
.unwrap();
lines_count += 1;
}

if command.last_run.is_some() {
write!(
screen,
"{}",
cursor::Goto(
width - 9,
(command_line_index as i16 + result_top_index as i16) as u16
(line_offset as i16 + result_top_index as i16) as u16
)
)
.unwrap();
Expand Down Expand Up @@ -303,6 +309,8 @@ impl<'a> Interface<'a> {

write!(screen, "{}", color::Bg(color::Reset)).unwrap();
write!(screen, "{}", color::Fg(color::Reset)).unwrap();

line_offset += lines_count;
}
screen.flush().unwrap();
}
Expand Down Expand Up @@ -629,7 +637,7 @@ impl<'a> Interface<'a> {
width: u16,
highlight_color: String,
base_color: String,
debug: bool,
debug: bool
) -> String {
let mut prev: usize = 0;
let debug_space = if debug { 90 } else { 0 };
Expand Down
11 changes: 11 additions & 0 deletions src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ impl Settings {
.value_name("PATH")
.help("The previous directory the user was in before running the command (default $OLDPWD)")
.takes_value(true))
.arg(Arg::with_name("command_from_stdin")
.long("command-from-stdin")
.help("Read the command from `history` (must have HISTTIMEFORMAT=\"%s:\") command piped in.")
.conflicts_with("command"))
.arg(Arg::with_name("command")
.help("The command that was run (default last line of $MCFLY_HISTORY file)")
.value_name("COMMAND")
Expand Down Expand Up @@ -349,6 +353,7 @@ impl Settings {
if let Some(dir) = add_matches.value_of("directory") {
settings.dir = dir.to_string();
} else {
// XXX: Why not just use std::env::current_dir here?
settings.dir = env::var("PWD").unwrap_or_else(|err| {
panic!(
"McFly error: Unable to determine current directory ({})",
Expand All @@ -365,6 +370,11 @@ impl Settings {

if let Some(commands) = add_matches.values_of("command") {
settings.command = commands.collect::<Vec<_>>().join(" ");
} else if add_matches.is_present("command_from_stdin") {
let bash_history = shell_history::read_from_bash_stdin()
.expect("Could not read from stdin");
//ignore the other values for now
settings.command = bash_history.command;
} else {
settings.command = shell_history::last_history_line(
&settings.mcfly_history,
Expand All @@ -384,6 +394,7 @@ impl Settings {

("search", Some(search_matches)) => {
settings.mode = Mode::Search;

if let Some(dir) = search_matches.value_of("directory") {
settings.dir = dir.to_string();
} else {
Expand Down
32 changes: 32 additions & 0 deletions src/shell_history.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::settings::HistoryFormat;
use regex::Regex;
use regex::RegexBuilder;
use std::env;
use std::fmt;
use std::fs;
use std::fs::File;
use std::fs::OpenOptions;
use std::io;
use std::io::Read;
use std::io::Write;
use std::path::Path;
Expand Down Expand Up @@ -222,6 +224,36 @@ pub fn append_history_entry(command: &HistoryCommand, path: &Path, debug: bool)
}
}

pub struct BashHistoryLine {
pub idx: u32,
pub edited: bool,
pub timestamp: u64,
pub command: String,
}

pub fn read_from_bash_stdin() -> io::Result<BashHistoryLine> {
// See https://github.com/bminor/bash/blob/ce23728687ce9e584333367075c9deef413553fa/builtins/history.def#L386
// First is the history index left-padded with space
// then either ' ' or '*' depending if the entry has been edited
// then a space ''
// then the timestamp in HISTTIMEFORMAT, which should be "%s:"
// then the command
// then a newline
let bash_history_regex = RegexBuilder::new(r"^\s*(?P<idx>\d+)(?P<edit>[\* ]) (?P<timestamp>\d+):(?P<command>.*)\n$")
.case_insensitive(false)
.dot_matches_new_line(true)
.build().unwrap();
let mut full = String::new();
io::stdin().read_to_string(&mut full)?;
let matches = bash_history_regex.captures(&full).expect("History line did not match expected format");
Ok(BashHistoryLine{
idx: matches.name("idx").unwrap().as_str().parse().unwrap(),
edited: matches.name("edit").unwrap().as_str() == "*",
timestamp: matches.name("timestamp").unwrap().as_str().parse().unwrap(),
command: matches.name("command").unwrap().as_str().to_string(),
})
}

#[cfg(test)]
mod tests {
use super::has_leading_timestamp;
Expand Down

0 comments on commit 8127e99

Please sign in to comment.