Update project issues: resolve #02-#04, add #08-#10
Mark issues #02 (backtick lexing/parsing), #03 (command execution runtime), and #04 (argv parsing) as resolved. Add new issues for configuration (#08), programmable prompt (#09), and interactive command execution (#10).
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 02 — Complete backtick command lexing and parsing
|
||||
|
||||
**Status:** open
|
||||
**Status:** resolved
|
||||
**Blocked by:** #01
|
||||
**Blocks:** #03
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 03 — Implement direct command execution runtime (no shell)
|
||||
|
||||
**Status:** open
|
||||
**Status:** resolved
|
||||
**Blocked by:** #02
|
||||
**Blocks:** #06, #07
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 04 — Implement argv parsing (tokenize command strings)
|
||||
|
||||
**Status:** open
|
||||
**Status:** resolved
|
||||
**Blocked by:** #01
|
||||
**Blocks:** #06
|
||||
|
||||
|
||||
29
issues/08-configuration.md
Normal file
29
issues/08-configuration.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# 08 — Configuration support
|
||||
|
||||
**Status:** open
|
||||
|
||||
Users should be able to programmatically configure the shell at startup.
|
||||
|
||||
## Config location
|
||||
|
||||
- `~/.config/lush/config` — main config file (Lua script)
|
||||
- `~/.config/lush/config.d/` — drop-in directory, files sourced in lexicographic order
|
||||
|
||||
## What can be configured
|
||||
|
||||
- **Prompt** — see #09
|
||||
- **Aliases** — command aliases (e.g. `alias("ll", "ls -l")`)
|
||||
- **Environment setup** — set/modify env vars at startup
|
||||
- **Autocompletion rules** — TBD, mechanism for registering custom completions
|
||||
|
||||
## Startup behaviour
|
||||
|
||||
1. If `~/.config/lush/config` exists, execute it as a Lua script
|
||||
2. If `~/.config/lush/config.d/` exists, execute each `.lua` file in lexicographic order
|
||||
3. Config files run in the same Lua state as the interactive session — locals, globals, and env changes persist
|
||||
|
||||
## Open questions
|
||||
|
||||
- Should there be a system-wide config (`/etc/lush/config`) sourced before user config?
|
||||
- What API surface to expose for autocompletion registration?
|
||||
- Should config errors be fatal or just print a warning and continue?
|
||||
58
issues/09-prompt.md
Normal file
58
issues/09-prompt.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# 09 — Programmable prompt
|
||||
|
||||
**Status:** open
|
||||
**Blocked by:** #08
|
||||
|
||||
Users should be able to customize the shell prompt by defining a Lua function, similar to how other shells use `PS1`/`PROMPT_COMMAND`/`precmd`.
|
||||
|
||||
## Design
|
||||
|
||||
- The prompt is generated by calling a global Lua function (e.g. `__prompt()`)
|
||||
- The function returns a string which is used as the prompt
|
||||
- If the function is not defined or errors, fall back to a sensible default
|
||||
|
||||
## Default prompt
|
||||
|
||||
A built-in default prompt is provided (implemented on the C side) until the user overrides `__prompt()` in their config. The default could be something like:
|
||||
|
||||
```
|
||||
lush>
|
||||
```
|
||||
|
||||
or include the current directory:
|
||||
|
||||
```
|
||||
~/Code/project>
|
||||
```
|
||||
|
||||
## User customization
|
||||
|
||||
Users override the prompt in their config (`~/.config/lush/config`):
|
||||
|
||||
```lua
|
||||
function __prompt()
|
||||
return $PWD .. "> "
|
||||
end
|
||||
```
|
||||
|
||||
Or with colours/git info/etc:
|
||||
|
||||
```lua
|
||||
function __prompt()
|
||||
local cwd = $PWD or "?"
|
||||
local branch = `git branch --show-current 2>/dev/null`
|
||||
local git = (branch.code == 0) and " (" .. branch.stdout:gsub("\n","") .. ")" or ""
|
||||
return cwd .. git .. "> "
|
||||
end
|
||||
```
|
||||
|
||||
## Implementation considerations
|
||||
|
||||
- The REPL loop (in `lua.c`) calls `__prompt()` before each input line
|
||||
- If `__prompt` is not a function or the call fails, use the default C-side prompt
|
||||
- The function runs in the normal Lua state so it has access to all globals, env vars, commands, etc.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Should there be a separate `__continuation_prompt()` for multiline input?
|
||||
- Should the prompt function receive any arguments (e.g. exit code of last command)?
|
||||
114
issues/10-interactive-command-execution.md
Normal file
114
issues/10-interactive-command-execution.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Issue #10 — Interactive command execution
|
||||
|
||||
## Problem
|
||||
|
||||
All backtick commands redirect stdout/stderr to pipes and capture output into a table `{code, stdout, stderr}`. Interactive programs lose their tty connection:
|
||||
|
||||
```lua
|
||||
`bash` -- launches bash but it can't read/write the terminal
|
||||
`vim foo` -- screen is blank, no input works
|
||||
```
|
||||
|
||||
A real shell needs to hand the terminal to a child process and wait for it to finish.
|
||||
|
||||
## What interactive execution means
|
||||
|
||||
The child process inherits all three file descriptors (stdin, stdout, stderr) directly from the parent. The parent `waitpid()`s until the child exits. The terminal behaves as if lush isn't there — the child owns it.
|
||||
|
||||
## Syntax
|
||||
|
||||
### Option A: `!` prefix (recommended starting point)
|
||||
|
||||
A `!` token marks a command as interactive. The command string extends to end-of-line (no closing delimiter needed):
|
||||
|
||||
```lua
|
||||
!bash
|
||||
!vim foo.lua
|
||||
!ssh user@host
|
||||
```
|
||||
|
||||
`!` doesn't conflict with Lua syntax (`not` is the logical-not keyword). Supports `${}` interpolation like backticks:
|
||||
|
||||
```lua
|
||||
local f = "foo.lua"
|
||||
!vim ${f}
|
||||
```
|
||||
|
||||
### Option B: Bare-word REPL fallback (separate issue?)
|
||||
|
||||
If a line fails to parse as Lua, interpret it as an interactive command:
|
||||
|
||||
```
|
||||
> vim foo.lua -- not valid Lua, runs interactively
|
||||
> ls -la -- same
|
||||
> local x = 1 -- valid Lua, runs as Lua
|
||||
```
|
||||
|
||||
Only applies in the REPL, not scripts. Could be combined with Option A. Touches `lua.c` (REPL loop) rather than the lexer/parser, so may warrant its own issue.
|
||||
|
||||
### Option C: Built-in function
|
||||
|
||||
```lua
|
||||
exec("bash")
|
||||
exec("vim", "foo.lua")
|
||||
```
|
||||
|
||||
No new syntax. Could serve as the low-level primitive that `!` compiles down to.
|
||||
|
||||
## Return value
|
||||
|
||||
Interactive commands return the exit code as a plain integer:
|
||||
|
||||
```lua
|
||||
local code = !bash
|
||||
assert(type(code) == "number")
|
||||
```
|
||||
|
||||
No stdout/stderr to capture — they go to the terminal. Returning an integer (not a table) clearly distinguishes interactive from captured commands.
|
||||
|
||||
When used as a statement (no assignment), the return value is discarded.
|
||||
|
||||
## Runtime implementation
|
||||
|
||||
Add `luaB_interactive` to `lcmd.c`, registered as `__interactive` global:
|
||||
|
||||
- `fork()` without creating any pipes
|
||||
- Child: `execvp()` directly (inherits parent's stdin/stdout/stderr)
|
||||
- Parent: ignore `SIGINT`/`SIGQUIT` (so Ctrl-C goes to child, not lush), then `waitpid()` and return exit code as integer
|
||||
- Child: restore `SIG_DFL` for `SIGINT`/`SIGQUIT` before `execvp()`
|
||||
- Reuses existing `parse_argv()` for command string tokenization
|
||||
|
||||
## Lexer changes (if using `!` prefix)
|
||||
|
||||
- Add `TK_ICMD` and `TK_ICMD_INTERP` tokens to enum in `llex.h`
|
||||
- Add `in_icmd` flag to `LexState` (mirrors `in_command` for backticks)
|
||||
- `read_icmd_body()`: like `read_command_body()` but terminates on newline/EOF instead of closing backtick
|
||||
- `read_icmd()`: skips leading whitespace after `!`, then calls `read_icmd_body()`
|
||||
- `luaX_readicmdcont()`: continuation reader after `${}` interpolation (mirrors `luaX_readcommandcont()`)
|
||||
- Handle `!` case in `llex()` switch: when `!` is followed by a non-`=` character at statement position, read as interactive command
|
||||
|
||||
## Parser changes (if using `!` prefix)
|
||||
|
||||
- Add `interactiveexp()` in `lparser.c` — mirrors `commandexp()` but compiles to `__interactive(cmdstring)` instead of `__command(cmdstring)`
|
||||
- Add `TK_ICMD` / `TK_ICMD_INTERP` cases in `simpleexp()`
|
||||
- Both simple and interpolated forms follow the same pattern as backtick commands
|
||||
|
||||
## Open questions
|
||||
|
||||
- Which syntax option(s) to pursue? Could start with `!` prefix and add others later.
|
||||
- Job control: should `Ctrl-Z` suspend the child and return to lush? Requires `tcsetpgrp()` / process group work. Probably defer to a later issue.
|
||||
- Should bare-word REPL fallback (Option B) be a separate issue?
|
||||
- `PATH` lookup: `execvp` already searches `PATH`, so this should just work.
|
||||
- Should `!` support multi-line commands (e.g. with `\` continuation)? Probably not — keep it simple.
|
||||
|
||||
## Files touched
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `lcmd.c` | Add `luaB_interactive` — fork/exec without pipes, return exit code |
|
||||
| `lcmd.h` | Declare `luaB_interactive` |
|
||||
| `linit.c` | Register `__interactive` global in `opencommand()` |
|
||||
| `llex.h` | Add `TK_ICMD`, `TK_ICMD_INTERP` tokens; `in_icmd` flag on `LexState`; declare `luaX_readicmdcont()` |
|
||||
| `llex.c` | Add `read_icmd_body()`, `read_icmd()`, `luaX_readicmdcont()`; handle `!` in `llex()` |
|
||||
| `lparser.c` | Add `interactiveexp()`; handle `TK_ICMD`/`TK_ICMD_INTERP` in `simpleexp()` |
|
||||
| `lua.c` | *(Only if doing Option B)* REPL fallback logic in `loadline()` |
|
||||
Reference in New Issue
Block a user