renamed fern.rs back to main.rs

This commit is contained in:
2026-01-28 19:57:14 -05:00
parent ad0e4277cb
commit 7f3e1cfcee
15 changed files with 52 additions and 119 deletions

View File

@@ -24,4 +24,3 @@ pretty_assertions = "1.4.1"
[[bin]] [[bin]]
name = "fern" name = "fern"
path = "src/fern.rs"

View File

@@ -1,6 +1,5 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::iter::Peekable; use std::iter::Peekable;
use std::mem::take;
use std::str::{Chars, FromStr}; use std::str::{Chars, FromStr};
use glob::Pattern; use glob::Pattern;
@@ -68,11 +67,10 @@ impl Expander {
pub fn expand(&mut self) -> ShResult<Vec<String>> { pub fn expand(&mut self) -> ShResult<Vec<String>> {
let mut chars = self.raw.chars().peekable(); let mut chars = self.raw.chars().peekable();
self.raw = expand_raw(&mut chars)?; self.raw = expand_raw(&mut chars)?;
if let Ok(glob_exp) = expand_glob(&self.raw) { if let Ok(glob_exp) = expand_glob(&self.raw)
if !glob_exp.is_empty() { && !glob_exp.is_empty() {
self.raw = glob_exp; self.raw = glob_exp;
} }
}
Ok(self.split_words()) Ok(self.split_words())
} }
pub fn split_words(&mut self) -> Vec<String> { pub fn split_words(&mut self) -> Vec<String> {
@@ -776,11 +774,10 @@ pub fn expand_proc_sub(raw: &str, is_input: bool) -> ShResult<String> {
pub fn expand_cmd_sub(raw: &str) -> ShResult<String> { pub fn expand_cmd_sub(raw: &str) -> ShResult<String> {
flog!(DEBUG, "in expand_cmd_sub"); flog!(DEBUG, "in expand_cmd_sub");
flog!(DEBUG, raw); flog!(DEBUG, raw);
if raw.starts_with('(') && raw.ends_with(')') { if raw.starts_with('(') && raw.ends_with(')')
if let Ok(output) = expand_arithmetic(raw) { && let Ok(output) = expand_arithmetic(raw) {
return Ok(output); // It's actually an arithmetic sub return Ok(output); // It's actually an arithmetic sub
} }
}
let (rpipe, wpipe) = IoMode::get_pipes(); let (rpipe, wpipe) = IoMode::get_pipes();
let cmd_sub_redir = Redir::new(wpipe, RedirType::Output); let cmd_sub_redir = Redir::new(wpipe, RedirType::Output);
let cmd_sub_io_frame = IoFrame::from_redir(cmd_sub_redir); let cmd_sub_io_frame = IoFrame::from_redir(cmd_sub_redir);

View File

@@ -507,6 +507,8 @@ impl Job {
flog!(TRACE, "waiting on children"); flog!(TRACE, "waiting on children");
flog!(TRACE, self.children); flog!(TRACE, self.children);
for child in self.children.iter_mut() { for child in self.children.iter_mut() {
flog!(TRACE, "shell pid {}", Pid::this());
flog!(TRACE, "child pid {}", child.pid);
if child.pid == Pid::this() { if child.pid == Pid::this() {
// TODO: figure out some way to get the exit code of builtins // TODO: figure out some way to get the exit code of builtins
let code = state::get_status(); let code = state::get_status();

View File

@@ -55,12 +55,12 @@ pub fn save_termios() {
///This function is unsafe because it accesses a public mutable static value. ///This function is unsafe because it accesses a public mutable static value.
/// This function should only ever be called after save_termios() has already /// This function should only ever be called after save_termios() has already
/// been called. /// been called.
pub unsafe fn get_saved_termios() -> Option<Termios> { pub unsafe fn get_saved_termios() -> Option<Termios> { unsafe {
// SAVED_TERMIOS should *only ever* be set once and accessed once // SAVED_TERMIOS should *only ever* be set once and accessed once
// Set at the start of the program, and accessed during the exit of the program // Set at the start of the program, and accessed during the exit of the program
// to reset the termios. Do not use this variable anywhere else // to reset the termios. Do not use this variable anywhere else
SAVED_TERMIOS.clone().flatten() SAVED_TERMIOS.clone().flatten()
} }}
/// Set termios to not echo control characters, like ^Z for instance /// Set termios to not echo control characters, like ^Z for instance
pub fn set_termios() { pub fn set_termios() {

View File

@@ -118,12 +118,7 @@ impl ExecArgs {
pub fn exec_input(input: String, io_stack: Option<IoStack>) -> ShResult<()> { pub fn exec_input(input: String, io_stack: Option<IoStack>) -> ShResult<()> {
write_meta(|m| m.start_timer()); write_meta(|m| m.start_timer());
let log_tab = { let log_tab = read_logic(|l| l.clone());
let fern = FERN.read().unwrap();
// TODO: Is there a better way to do this?
// The goal is mainly to not be holding a lock while executing input
fern.read_logic().clone()
};
let input = expand_aliases(input, HashSet::new(), &log_tab); let input = expand_aliases(input, HashSet::new(), &log_tab);
let mut parser = ParsedSrc::new(Arc::new(input)); let mut parser = ParsedSrc::new(Arc::new(input));
if let Err(errors) = parser.parse_src() { if let Err(errors) = parser.parse_src() {

View File

@@ -317,17 +317,16 @@ impl LexStream {
let slice = self.slice_from_cursor().unwrap().to_string(); let slice = self.slice_from_cursor().unwrap().to_string();
let mut pos = self.cursor; let mut pos = self.cursor;
let mut chars = slice.chars().peekable(); let mut chars = slice.chars().peekable();
let mut can_be_subshell = chars.peek() == Some(&'('); let can_be_subshell = chars.peek() == Some(&'(');
if self.flags.contains(LexFlags::IN_CASE) { if self.flags.contains(LexFlags::IN_CASE)
if let Some(count) = case_pat_lookahead(chars.clone()) { && let Some(count) = case_pat_lookahead(chars.clone()) {
pos += count; pos += count;
let casepat_tk = self.get_token(self.cursor..pos, TkRule::CasePattern); let casepat_tk = self.get_token(self.cursor..pos, TkRule::CasePattern);
self.cursor = pos; self.cursor = pos;
self.set_next_is_cmd(true); self.set_next_is_cmd(true);
return Ok(casepat_tk); return Ok(casepat_tk);
} }
}
while let Some(ch) = chars.next() { while let Some(ch) = chars.next() {
match ch { match ch {

View File

@@ -1,13 +1,11 @@
pub mod highlight; pub mod highlight;
pub mod readline; pub mod readline;
use std::path::Path;
use readline::{FernVi, Readline}; use readline::{FernVi, Readline};
use crate::{ use crate::{
expand::expand_prompt, libsh::error::ShResult, prelude::*, shopt::FernEditMode, expand::expand_prompt, libsh::error::ShResult, prelude::*, shopt::FernEditMode,
state::read_shopts,
}; };
/// Initialize the line editor /// Initialize the line editor
@@ -32,7 +30,7 @@ pub fn readline(edit_mode: FernEditMode, initial: Option<&str>) -> ShResult<Stri
FernEditMode::Vi => { FernEditMode::Vi => {
let mut fern_vi = FernVi::new(Some(prompt))?; let mut fern_vi = FernVi::new(Some(prompt))?;
if let Some(input) = initial { if let Some(input) = initial {
fern_vi = fern_vi.with_initial(&input) fern_vi = fern_vi.with_initial(input)
} }
Box::new(fern_vi) as Box<dyn Readline> Box::new(fern_vi) as Box<dyn Readline>
} }

View File

@@ -953,11 +953,10 @@ impl LineBuf {
} }
let start = start.unwrap_or(0); let start = start.unwrap_or(0);
if count > 1 { if count > 1
if let Some((_, new_end)) = self.text_obj_sentence(end, count - 1, bound) { && let Some((_, new_end)) = self.text_obj_sentence(end, count - 1, bound) {
end = new_end; end = new_end;
} }
}
Some((start, end)) Some((start, end))
} }
@@ -2590,8 +2589,8 @@ impl LineBuf {
self.cursor.add(content.len().saturating_sub(1)); self.cursor.add(content.len().saturating_sub(1));
} }
Verb::SwapVisualAnchor => { Verb::SwapVisualAnchor => {
if let Some((start, end)) = self.select_range() { if let Some((start, end)) = self.select_range()
if let Some(mut mode) = self.select_mode { && let Some(mut mode) = self.select_mode {
mode.invert_anchor(); mode.invert_anchor();
let new_cursor_pos = match mode.anchor() { let new_cursor_pos = match mode.anchor() {
SelectAnchor::Start => start, SelectAnchor::Start => start,
@@ -2600,7 +2599,6 @@ impl LineBuf {
self.cursor.set(new_cursor_pos); self.cursor.set(new_cursor_pos);
self.select_mode = Some(mode) self.select_mode = Some(mode)
} }
}
} }
Verb::JoinLines => { Verb::JoinLines => {
let start = self.start_of_line(); let start = self.start_of_line();
@@ -2748,11 +2746,10 @@ impl LineBuf {
let edit_is_merging = self.undo_stack.last().is_some_and(|edit| edit.merging); let edit_is_merging = self.undo_stack.last().is_some_and(|edit| edit.merging);
// Merge character inserts into one edit // Merge character inserts into one edit
if edit_is_merging && cmd.verb.as_ref().is_none_or(|v| !v.1.is_char_insert()) { if edit_is_merging && cmd.verb.as_ref().is_none_or(|v| !v.1.is_char_insert())
if let Some(edit) = self.undo_stack.last_mut() { && let Some(edit) = self.undo_stack.last_mut() {
edit.stop_merge(); edit.stop_merge();
} }
}
let ViCmd { let ViCmd {
register, register,
@@ -2839,11 +2836,10 @@ impl LineBuf {
self.saved_col = None; self.saved_col = None;
} }
if is_char_insert { if is_char_insert
if let Some(edit) = self.undo_stack.last_mut() { && let Some(edit) = self.undo_stack.last_mut() {
edit.start_merge(); edit.start_merge();
} }
}
Ok(()) Ok(())
} }

View File

@@ -1,4 +1,4 @@
use history::{History, SearchConstraint, SearchKind}; use history::History;
use keys::{KeyCode, KeyEvent, ModKeys}; use keys::{KeyCode, KeyEvent, ModKeys};
use linebuf::{LineBuf, SelectAnchor, SelectMode}; use linebuf::{LineBuf, SelectAnchor, SelectMode};
use nix::libc::STDOUT_FILENO; use nix::libc::STDOUT_FILENO;

View File

@@ -2,9 +2,7 @@ use std::{
env, env,
fmt::{Debug, Write}, fmt::{Debug, Write},
io::{BufRead, BufReader, Read}, io::{BufRead, BufReader, Read},
iter::Peekable,
os::fd::{AsFd, BorrowedFd, RawFd}, os::fd::{AsFd, BorrowedFd, RawFd},
str::Chars,
}; };
use nix::{ use nix::{

View File

@@ -161,15 +161,14 @@ impl ViCmd {
} }
/// If a ViCmd has a linewise motion, but no verb, we change it to charwise /// If a ViCmd has a linewise motion, but no verb, we change it to charwise
pub fn alter_line_motion_if_no_verb(&mut self) { pub fn alter_line_motion_if_no_verb(&mut self) {
if self.is_line_motion() && self.verb.is_none() { if self.is_line_motion() && self.verb.is_none()
if let Some(motion) = self.motion.as_mut() { && let Some(motion) = self.motion.as_mut() {
match motion.1 { match motion.1 {
Motion::LineUp => motion.1 = Motion::LineUpCharwise, Motion::LineUp => motion.1 = Motion::LineUpCharwise,
Motion::LineDown => motion.1 = Motion::LineDownCharwise, Motion::LineDown => motion.1 = Motion::LineDownCharwise,
_ => unreachable!(), _ => unreachable!(),
} }
} }
}
} }
pub fn is_mode_transition(&self) -> bool { pub fn is_mode_transition(&self) -> bool {
self.verb.as_ref().is_some_and(|v| { self.verb.as_ref().is_some_and(|v| {

View File

@@ -5,7 +5,6 @@ use nix::NixPath;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use super::keys::{KeyCode as K, KeyEvent as E, ModKeys as M}; use super::keys::{KeyCode as K, KeyEvent as E, ModKeys as M};
use super::linebuf::CharClass;
use super::vicmd::{ use super::vicmd::{
Anchor, Bound, CmdFlags, Dest, Direction, Motion, MotionCmd, RegisterName, TextObj, To, Verb, Anchor, Bound, CmdFlags, Dest, Direction, Motion, MotionCmd, RegisterName, TextObj, To, Verb,
VerbCmd, ViCmd, Word, VerbCmd, ViCmd, Word,

View File

@@ -4,7 +4,7 @@ use nix::sys::signal::{SaFlags, SigAction, sigaction};
use crate::{ use crate::{
jobs::{JobCmdFlags, JobID, take_term}, jobs::{JobCmdFlags, JobID, take_term},
libsh::{error::{ShErr, ShErrKind, ShResult}, sys::sh_quit}, libsh::error::{ShErr, ShErrKind, ShResult},
prelude::*, prelude::*,
state::{read_jobs, write_jobs}, state::{read_jobs, write_jobs},
}; };
@@ -249,8 +249,8 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
} else { } else {
None None
} }
}) { })
if is_finished { && is_finished {
if is_fg { if is_fg {
take_term()?; take_term()?;
} else { } else {
@@ -262,6 +262,5 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
} }
} }
} }
}
Ok(()) Ok(())
} }

View File

@@ -1,5 +1,5 @@
use std::{ use std::{
collections::{HashMap, VecDeque}, fmt::Display, ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref}, str::FromStr, sync::{LazyLock, RwLock, RwLockReadGuard, RwLockWriteGuard}, time::Duration cell::RefCell, collections::{HashMap, VecDeque}, fmt::Display, ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref}, str::FromStr, time::Duration
}; };
use nix::unistd::{gethostname, getppid, User}; use nix::unistd::{gethostname, getppid, User};
@@ -17,53 +17,23 @@ use crate::{
}; };
pub struct Fern { pub struct Fern {
pub jobs: JobTab, pub jobs: RefCell<JobTab>,
pub var_scopes: ScopeStack, pub var_scopes: RefCell<ScopeStack>,
pub meta: MetaTab, pub meta: RefCell<MetaTab>,
pub logic: LogTab, pub logic: RefCell<LogTab>,
pub shopts: ShOpts, pub shopts: RefCell<ShOpts>,
} }
impl Fern { impl Fern {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
jobs: JobTab::new(), jobs: RefCell::new(JobTab::new()),
var_scopes: ScopeStack::new(), var_scopes: RefCell::new(ScopeStack::new()),
meta: MetaTab::new(), meta: RefCell::new(MetaTab::new()),
logic: LogTab::new(), logic: RefCell::new(LogTab::new()),
shopts: ShOpts::default(), shopts: RefCell::new(ShOpts::default()),
} }
} }
pub fn write_jobs(&mut self) -> &mut JobTab {
&mut self.jobs
}
pub fn write_vars(&mut self) -> &mut ScopeStack {
&mut self.var_scopes
}
pub fn write_meta(&mut self) -> &mut MetaTab {
&mut self.meta
}
pub fn write_logic(&mut self) -> &mut LogTab {
&mut self.logic
}
pub fn write_shopts(&mut self) -> &mut ShOpts {
&mut self.shopts
}
pub fn read_jobs(&self) -> &JobTab {
&self.jobs
}
pub fn read_vars(&self) -> &ScopeStack {
&self.var_scopes
}
pub fn read_meta(&self) -> &MetaTab {
&self.meta
}
pub fn read_logic(&self) -> &LogTab {
&self.logic
}
pub fn read_shopts(&self) -> &ShOpts {
&self.shopts
}
} }
impl Default for Fern { impl Default for Fern {
@@ -257,7 +227,9 @@ impl ScopeStack {
} }
} }
pub static FERN: LazyLock<RwLock<Fern>> = LazyLock::new(|| RwLock::new(Fern::new())); thread_local! {
pub static FERN: Fern = Fern::new();
}
/// A shell function /// A shell function
/// ///
@@ -721,69 +693,49 @@ impl MetaTab {
/// Read from the job table /// Read from the job table
pub fn read_jobs<T, F: FnOnce(&JobTab) -> T>(f: F) -> T { pub fn read_jobs<T, F: FnOnce(&JobTab) -> T>(f: F) -> T {
let fern = FERN.read().unwrap(); FERN.with(|fern| f(&fern.jobs.borrow()))
let jobs = fern.read_jobs();
f(jobs)
} }
/// Write to the job table /// Write to the job table
pub fn write_jobs<T, F: FnOnce(&mut JobTab) -> T>(f: F) -> T { pub fn write_jobs<T, F: FnOnce(&mut JobTab) -> T>(f: F) -> T {
let mut fern = FERN.write().unwrap(); FERN.with(|fern| f(&mut fern.jobs.borrow_mut()))
let jobs = &mut fern.jobs;
f(jobs)
} }
/// Read from the var scope stack /// Read from the var scope stack
pub fn read_vars<T, F: FnOnce(&ScopeStack) -> T>(f: F) -> T { pub fn read_vars<T, F: FnOnce(&ScopeStack) -> T>(f: F) -> T {
let fern = FERN.read().unwrap(); FERN.with(|fern| f(&fern.var_scopes.borrow()))
let vars = fern.read_vars();
f(vars)
} }
/// Write to the variable table /// Write to the variable table
pub fn write_vars<T, F: FnOnce(&mut ScopeStack) -> T>(f: F) -> T { pub fn write_vars<T, F: FnOnce(&mut ScopeStack) -> T>(f: F) -> T {
let mut fern = FERN.write().unwrap(); FERN.with(|fern| f(&mut fern.var_scopes.borrow_mut()))
let vars = fern.write_vars();
f(vars)
} }
pub fn read_meta<T, F: FnOnce(&MetaTab) -> T>(f: F) -> T { pub fn read_meta<T, F: FnOnce(&MetaTab) -> T>(f: F) -> T {
let fern = FERN.read().unwrap(); FERN.with(|fern| f(&fern.meta.borrow()))
let meta = fern.read_meta();
f(meta)
} }
/// Write to the variable table /// Write to the meta table
pub fn write_meta<T, F: FnOnce(&mut MetaTab) -> T>(f: F) -> T { pub fn write_meta<T, F: FnOnce(&mut MetaTab) -> T>(f: F) -> T {
let mut fern = FERN.write().unwrap(); FERN.with(|fern| f(&mut fern.meta.borrow_mut()))
let meta = fern.write_meta();
f(meta)
} }
/// Read from the logic table /// Read from the logic table
pub fn read_logic<T, F: FnOnce(&LogTab) -> T>(f: F) -> T { pub fn read_logic<T, F: FnOnce(&LogTab) -> T>(f: F) -> T {
let fern = FERN.read().unwrap(); FERN.with(|fern| f(&fern.logic.borrow()))
let logic = fern.read_logic();
f(logic)
} }
/// Write to the logic table /// Write to the logic table
pub fn write_logic<T, F: FnOnce(&mut LogTab) -> T>(f: F) -> T { pub fn write_logic<T, F: FnOnce(&mut LogTab) -> T>(f: F) -> T {
let mut fern = FERN.write().unwrap(); FERN.with(|fern| f(&mut fern.logic.borrow_mut()))
let logic = &mut fern.logic;
f(logic)
} }
pub fn read_shopts<T, F: FnOnce(&ShOpts) -> T>(f: F) -> T { pub fn read_shopts<T, F: FnOnce(&ShOpts) -> T>(f: F) -> T {
let fern = FERN.read().unwrap(); FERN.with(|fern| f(&fern.shopts.borrow()))
let shopts = fern.read_shopts();
f(shopts)
} }
pub fn write_shopts<T, F: FnOnce(&mut ShOpts) -> T>(f: F) -> T { pub fn write_shopts<T, F: FnOnce(&mut ShOpts) -> T>(f: F) -> T {
let mut fern = FERN.write().unwrap(); FERN.with(|fern| f(&mut fern.shopts.borrow_mut()))
let shopts = &mut fern.shopts;
f(shopts)
} }
pub fn descend_scope(argv: Option<Vec<String>>) { pub fn descend_scope(argv: Option<Vec<String>>) {