implemented 'type' and 'wait' builtins
fixed some tcsetpgrp() misbehavior fixed not being able to redirect stderr from builtins
This commit is contained in:
@@ -8,10 +8,11 @@ use crate::{
|
||||
builtin::complete::{CompFlags, CompOptFlags, CompOpts},
|
||||
libsh::{
|
||||
error::ShResult,
|
||||
guards::var_ctx_guard,
|
||||
utils::TkVecUtils,
|
||||
},
|
||||
parse::{
|
||||
execute::{VarCtxGuard, exec_input},
|
||||
execute::exec_input,
|
||||
lex::{self, LexFlags, Tk, TkRule, ends_with_unescaped},
|
||||
},
|
||||
readline::{
|
||||
@@ -341,7 +342,7 @@ impl BashCompSpec {
|
||||
] {
|
||||
vars_to_unset.insert(var.to_string());
|
||||
}
|
||||
let _guard = VarCtxGuard::new(vars_to_unset);
|
||||
let _guard = var_ctx_guard(vars_to_unset);
|
||||
|
||||
let CompContext {
|
||||
words,
|
||||
@@ -391,7 +392,7 @@ impl BashCompSpec {
|
||||
"{} {cmd_name} {cword_str} {pword_str}",
|
||||
self.function.as_ref().unwrap()
|
||||
);
|
||||
exec_input(input, None, false)?;
|
||||
exec_input(input, None, false, Some("comp_function".into()))?;
|
||||
|
||||
Ok(read_vars(|v| v.get_arr_elems("COMPREPLY")).unwrap_or_default())
|
||||
}
|
||||
@@ -532,7 +533,7 @@ impl Completer {
|
||||
(before_cursor, after_cursor)
|
||||
}
|
||||
|
||||
pub fn get_completion_context(&self, line: &str, cursor_pos: usize) -> (Vec<Marker>, usize) {
|
||||
pub fn get_subtoken_completion(&self, line: &str, cursor_pos: usize) -> (Vec<Marker>, usize) {
|
||||
let annotated = annotate_input_recursive(line);
|
||||
let mut ctx = vec![markers::NULL];
|
||||
let mut last_priority = 0;
|
||||
@@ -776,20 +777,19 @@ impl Completer {
|
||||
// Use marker-based context detection for sub-token awareness (e.g. VAR_SUB
|
||||
// inside a token). Run this before comp specs so variable completions take
|
||||
// priority over programmable completion.
|
||||
let (mut marker_ctx, token_start) = self.get_completion_context(&line, cursor_pos);
|
||||
let (mut marker_ctx, token_start) = self.get_subtoken_completion(&line, cursor_pos);
|
||||
|
||||
if marker_ctx.last() == Some(&markers::VAR_SUB) {
|
||||
if let Some(cur) = ctx.words.get(ctx.cword) {
|
||||
self.token_span.0 = token_start;
|
||||
let mut span = cur.span.clone();
|
||||
span.set_range(token_start..self.token_span.1);
|
||||
let raw_tk = span.as_str();
|
||||
let candidates = complete_vars(raw_tk);
|
||||
if !candidates.is_empty() {
|
||||
return Ok(CompResult::from_candidates(candidates));
|
||||
}
|
||||
}
|
||||
}
|
||||
if marker_ctx.last() == Some(&markers::VAR_SUB)
|
||||
&& let Some(cur) = ctx.words.get(ctx.cword) {
|
||||
self.token_span.0 = token_start;
|
||||
let mut span = cur.span.clone();
|
||||
span.set_range(token_start..self.token_span.1);
|
||||
let raw_tk = span.as_str();
|
||||
let candidates = complete_vars(raw_tk);
|
||||
if !candidates.is_empty() {
|
||||
return Ok(CompResult::from_candidates(candidates));
|
||||
}
|
||||
}
|
||||
|
||||
// Try programmable completion
|
||||
match self.try_comp_spec(&ctx)? {
|
||||
|
||||
@@ -785,6 +785,10 @@ impl LineBuf {
|
||||
}
|
||||
(start, end)
|
||||
}
|
||||
pub fn this_line_content(&mut self) -> Option<&str> {
|
||||
let (start,end) = self.this_line_exclusive();
|
||||
self.slice(start..end)
|
||||
}
|
||||
pub fn this_line(&mut self) -> (usize, usize) {
|
||||
let line_no = self.cursor_line_number();
|
||||
self.line_bounds(line_no)
|
||||
@@ -2801,6 +2805,25 @@ impl LineBuf {
|
||||
Verb::InsertChar(ch) => {
|
||||
self.insert_at_cursor(ch);
|
||||
self.cursor.add(1);
|
||||
let before = self.auto_indent_level;
|
||||
if read_shopts(|o| o.prompt.auto_indent)
|
||||
&& let Some(line_content) = self.this_line_content() {
|
||||
match line_content.trim() {
|
||||
"esac" | "done" | "fi" | "}" => {
|
||||
self.calc_indent_level();
|
||||
if self.auto_indent_level < before {
|
||||
let delta = before - self.auto_indent_level;
|
||||
let line_start = self.start_of_line();
|
||||
for _ in 0..delta {
|
||||
if self.grapheme_at(line_start).is_some_and(|gr| gr == "\t") {
|
||||
self.remove(line_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { /* nothing to see here */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
Verb::Insert(string) => {
|
||||
self.push_str(&string);
|
||||
|
||||
@@ -10,52 +10,24 @@ use nix::{
|
||||
errno::Errno,
|
||||
libc::{self},
|
||||
poll::{self, PollFlags, PollTimeout},
|
||||
sys::termios::{self, tcgetattr, tcsetattr},
|
||||
unistd::isatty,
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
use vte::{Parser, Perform};
|
||||
|
||||
pub use crate::libsh::guards::{RawModeGuard, raw_mode};
|
||||
use crate::{
|
||||
libsh::{
|
||||
error::{ShErr, ShErrKind, ShResult},
|
||||
sys::TTY_FILENO,
|
||||
},
|
||||
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||
readline::keys::{KeyCode, ModKeys},
|
||||
state::read_shopts,
|
||||
};
|
||||
use crate::{
|
||||
procio::borrow_fd,
|
||||
state::{read_meta, write_meta},
|
||||
};
|
||||
|
||||
use super::keys::KeyEvent;
|
||||
|
||||
pub fn raw_mode() -> RawModeGuard {
|
||||
let orig = termios::tcgetattr(unsafe { BorrowedFd::borrow_raw(*TTY_FILENO) })
|
||||
.expect("Failed to get terminal attributes");
|
||||
let mut raw = orig.clone();
|
||||
termios::cfmakeraw(&mut raw);
|
||||
// Keep ISIG enabled so Ctrl+C/Ctrl+Z still generate signals
|
||||
raw.local_flags |= termios::LocalFlags::ISIG;
|
||||
// Keep OPOST enabled so \n is translated to \r\n on output
|
||||
raw.output_flags |= termios::OutputFlags::OPOST;
|
||||
termios::tcsetattr(
|
||||
unsafe { BorrowedFd::borrow_raw(*TTY_FILENO) },
|
||||
termios::SetArg::TCSANOW,
|
||||
&raw,
|
||||
)
|
||||
.expect("Failed to set terminal to raw mode");
|
||||
|
||||
let (_cols, _rows) = get_win_size(*TTY_FILENO);
|
||||
|
||||
RawModeGuard {
|
||||
orig,
|
||||
fd: *TTY_FILENO,
|
||||
}
|
||||
}
|
||||
|
||||
pub type Row = u16;
|
||||
pub type Col = u16;
|
||||
|
||||
@@ -325,65 +297,6 @@ impl Read for TermBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RawModeGuard {
|
||||
orig: termios::Termios,
|
||||
fd: RawFd,
|
||||
}
|
||||
|
||||
impl RawModeGuard {
|
||||
/// Disable raw mode temporarily for a specific operation
|
||||
pub fn disable_for<F: FnOnce() -> R, R>(&self, func: F) -> R {
|
||||
unsafe {
|
||||
let fd = BorrowedFd::borrow_raw(self.fd);
|
||||
// Temporarily restore the original termios
|
||||
termios::tcsetattr(fd, termios::SetArg::TCSANOW, &self.orig)
|
||||
.expect("Failed to temporarily disable raw mode");
|
||||
|
||||
// Run the function
|
||||
let result = func();
|
||||
|
||||
// Re-enable raw mode
|
||||
let mut raw = self.orig.clone();
|
||||
termios::cfmakeraw(&mut raw);
|
||||
// Keep ISIG enabled so Ctrl+C/Ctrl+Z still generate signals
|
||||
raw.local_flags |= termios::LocalFlags::ISIG;
|
||||
// Keep OPOST enabled so \n is translated to \r\n on output
|
||||
raw.output_flags |= termios::OutputFlags::OPOST;
|
||||
termios::tcsetattr(fd, termios::SetArg::TCSANOW, &raw).expect("Failed to re-enable raw mode");
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_cooked_mode<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
let raw = tcgetattr(borrow_fd(*TTY_FILENO)).expect("Failed to get terminal attributes");
|
||||
let mut cooked = raw.clone();
|
||||
cooked.local_flags |= termios::LocalFlags::ICANON | termios::LocalFlags::ECHO;
|
||||
cooked.input_flags |= termios::InputFlags::ICRNL;
|
||||
tcsetattr(borrow_fd(*TTY_FILENO), termios::SetArg::TCSANOW, &cooked)
|
||||
.expect("Failed to set cooked mode");
|
||||
let res = f();
|
||||
tcsetattr(borrow_fd(*TTY_FILENO), termios::SetArg::TCSANOW, &raw)
|
||||
.expect("Failed to restore raw mode");
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RawModeGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = termios::tcsetattr(
|
||||
BorrowedFd::borrow_raw(self.fd),
|
||||
termios::SetArg::TCSANOW,
|
||||
&self.orig,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PollReader - non-blocking key reader using vte parser
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user