Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
084f0ac
Small simplification of gc functions
jpco Mar 25, 2025
cec72d5
working (?!) implementation of palloc and friends
jpco Mar 26, 2025
ea81fd3
only scan the necessary pspace pointers while copying into gcspace
jpco Mar 26, 2025
ba9b099
move LOCAL_GETENV logic out of input.c into var.c
jpco Mar 26, 2025
b8b31bd
Pull in %write-history from #65
jpco Mar 26, 2025
2eb69a1
Start shoving readline logic into readline.c
jpco Mar 26, 2025
dd2fef1
Shove yet more readline logic into readline.c
jpco Mar 26, 2025
1a4923b
initial sketch of a $&readline primitive
jpco Mar 26, 2025
288fdd8
Demo user-defineable completion function
jpco Mar 26, 2025
2c05f72
use hook functions for shell input
jpco Mar 26, 2025
ec430cb
Make pseal(NULL) work okay
jpco Mar 27, 2025
b8110e9
Li'l bug fixes
jpco Mar 28, 2025
8c3ae10
Add (and fix) tests for new parser behavior
jpco Mar 31, 2025
a679134
Try to isolate test execution better
jpco Mar 31, 2025
eea5e0c
Small test improvements
jpco Mar 31, 2025
0fae344
fix obvious little segfault bug
jpco Mar 31, 2025
1c00488
Restore previous scanspace logic.
jpco Apr 14, 2025
d3f888f
Merge remote-tracking branch 'upstream/master' into palloc
jpco Apr 22, 2025
e74ac6a
Merge remote-tracking branch 'upstream/master' into palloc
jpco May 3, 2025
cb1f629
Configure our yacc to use "pure" mode.
jpco May 6, 2025
5064cb2
Add "fastread" patch.
jpco May 10, 2025
0feb012
Make most parsing and lexing state per-Input.
jpco May 10, 2025
963a6db
Rough draft - make per-Input pspaces
jpco May 19, 2025
aa4b3d2
Fix configure.ac bug
jpco May 19, 2025
2c74768
Fix per-input pspace.
jpco May 19, 2025
f8a735f
Make fillcmd work with multiple Inputs.
jpco May 20, 2025
147af90
Un-break GCPROTECT with per-Input pspaces.
jpco May 20, 2025
561f58f
Merge remote-tracking branch 'upstream/master' into palloc
jpco May 20, 2025
adf1d08
Move readline completion to demo completion.es file.
jpco May 30, 2025
e226270
Improve completion.es based completion.
jpco Jun 1, 2025
3f1c2a5
Even more thorough tab-completion logic.
jpco Jun 2, 2025
6e21a84
More-robust scanning for beginning of command.
jpco Jun 4, 2025
45cf982
Add hook to control filename completion.
jpco Jun 10, 2025
19a2607
Slightly better behavior for `man a.b` completions
jpco Jun 11, 2025
1dc5cc0
Merge remote-tracking branch 'upstream/master' into palloc
jpco Dec 12, 2025
f50d0ee
Make %parse write to history even on syntax errors
jpco Jan 28, 2026
eeba776
Autoloadable completion functions, to reduce function namespace pollu…
jpco Feb 4, 2026
573933a
demo of "heredoc mode" for reader command?
jpco Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ LIBS = @READLINE_LIBS@ @LIBS@ $(ADDLIBS)
HFILES = config.h es.h gc.h input.h prim.h print.h sigmsgs.h \
stdenv.h syntax.h term.h token.h var.h
CFILES = access.c closure.c conv.c dict.c eval.c except.c fd.c gc.c glob.c \
glom.c input.c heredoc.c history.c list.c main.c match.c open.c opt.c \
glom.c input.c heredoc.c list.c main.c match.c open.c opt.c \
prim-ctl.c prim-etc.c prim-io.c prim-sys.c prim.c print.c proc.c \
sigmsgs.c signal.c split.c status.c str.c syntax.c term.c token.c \
tree.c util.c var.c vec.c version.c y.tab.c dump.c
readline.c sigmsgs.c signal.c split.c status.c str.c syntax.c term.c \
token.c tree.c util.c var.c vec.c version.c y.tab.c dump.c
OFILES = access.o closure.o conv.o dict.o eval.o except.o fd.o gc.o glob.o \
glom.o input.o heredoc.o history.o list.o main.o match.o open.o opt.o \
glom.o input.o heredoc.o list.o main.o match.o open.o opt.o \
prim-ctl.o prim-etc.o prim-io.o prim-sys.o prim.o print.o proc.o \
sigmsgs.o signal.o split.o status.o str.o syntax.o term.o token.o \
tree.o util.o var.o vec.o version.o y.tab.o
readline.o sigmsgs.o signal.o split.o status.o str.o syntax.o term.o \
token.o tree.o util.o var.o vec.o version.o y.tab.o
OTHER = Makefile parse.y mksignal
GEN = esdump y.tab.h y.output sigmsgs.c initial.c

Expand Down Expand Up @@ -131,7 +131,6 @@ glob.o : glob.c es.h config.h stdenv.h gc.h
glom.o : glom.c es.h config.h stdenv.h gc.h
input.o : input.c es.h config.h stdenv.h input.h token.h
heredoc.o : heredoc.c es.h config.h stdenv.h gc.h input.h syntax.h token.h
history.o : history.c es.h config.h stdenv.h gc.h input.h token.h
list.o : list.c es.h config.h stdenv.h gc.h
main.o : main.c es.h config.h stdenv.h
match.o : match.c es.h config.h stdenv.h
Expand All @@ -141,6 +140,7 @@ prim.o : prim.c es.h config.h stdenv.h prim.h
prim-ctl.o : prim-ctl.c es.h config.h stdenv.h prim.h
prim-etc.o : prim-etc.c es.h config.h stdenv.h prim.h
prim-io.o : prim-io.c es.h config.h stdenv.h gc.h prim.h
readline.o : readline.c es.h config.h stdenv.h prim.h
prim-sys.o : prim-sys.c es.h config.h stdenv.h prim.h
print.o : print.c es.h config.h stdenv.h print.h
proc.o : proc.c es.h config.h stdenv.h prim.h
Expand Down
2 changes: 1 addition & 1 deletion access.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ PRIM(access) {
if (suffix)
err = str("%s: %s", suffix, err);
gcenable();
fail("$&access", err);
fail("$&access", "%s", err);
RefEnd(err);
}

Expand Down
189 changes: 189 additions & 0 deletions completion.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# completion.es -- demo of programmable completion in es

# This file exists to explore some options for programmable completion in
# es; it's not an endorsement of any particular design for completion.
# However, this setup already performs much better than the current
# "built-in" readline completion that es has, with surprisingly little
# direct support from the readline library. This corresponds well with
# how much es is built on top of es, and (hopefully) indicates that es
# could switch between different line-editing libraries while using a
# single common user-visible completion "API".
#
# Syntax isn't handled really robustly with this setup, and probably
# requires some kind of internal parsing machinery to do right; for
# example, we don't have great behavior with `$(var1 var2[TAB]`,
# `let (f[TAB]`, `let (a = b) command[TAB]`, or `cmd > fi[TAB]`.
#
# Some hook functions back syntax and ideally modifying the hook functions'
# completion functions (e.g., `%complete-%create`) would also modify how
# the syntax is completed (e.g., `command arg > fi[TAB]`). In theory, this
# could even extend to things like %complete-%seq and %complete-%pipe,
# though there's some trickiness in designing how that would actually be
# executed.
#
# We also want good automatic de-quoting and re-quoting of terms, and
# splitting arguments in a syntax-aware way.
#
# Closer integration with readline is another open question. For example,
# it's common to have specific key bindings refer to specific types of
# completion. How do we implement that? Moreover, how do we do so in a
# way that works with .inputrc? How might we design this in a way that's
# library-agnostic?
#
# This setup produces a fairly large amount of overhead in the es function
# namespace. We would likely want to reduce that overhead, especially since
# all of this has absolutely no value in the non-interactive case. Perhaps
# all of the per-command completions should come from autoloadable files and
# be marked noexport. I suspect that while it's quite nice to have good
# coverage for autocompletion, in practical use, only a few commands are
# actually auto-completed in any interactive session.


#
# Base/dispatcher completion function
#

# %complete is called by $&readline whenever the user hits "tab". It is
# called with two arguments: 'prefix' contains the entire line (in string
# form) before the current word being completed, and 'word' is the current
# word.
#
# It uses some fairly simple heuristics to try to decide what kind of
# completion it is performing, and then dispatches to other completion
# functions. While the heuristics leave something to be desired, calling
# out to other functions (and allowing those other functions to recursively
# call %complete again) enables quite a bit of power, especially given
# how much of es' "internal" behavior is based on hook functions.

fn %complete prefix word {
if {~ $word '$&'*} {
# Primitive completion. So far, no need to make a function of this.
result $word^<={~~ '$&'^<=$&primitives $word^*}
} {~ $word '$#'*} {
result '$#'^<={%var-complete <={~~ $word '$#'*}}
} {~ $word '$^'*} {
result '$^'^<={%var-complete <={~~ $word '$^'*}}
} {~ $word '$'*} {
result '$'^<={%var-complete <={~~ $word '$'*}}
} {
let (line = <={%split ' '\t $prefix}) {
# Basic "start-of-command" detection.
if {~ $line () ||
~ $^line *'<=' ||
~ $^line *'{' ||
~ $^line *'|' ||
~ $^line *'`' ||
~ $^line *'|['*']' ||
~ $^line *'&'
} {
# Command-position completion.
%whatis-complete $word
} {
# Strip the first term from the line.
%complete-fn $line(1) <={%flatten ' ' $line(2 ...)} $word
}
}
}
}


# %complete-fn finds if necessary, and evaluates if possible, a particular
# completion function. Calling this is strongly recommended instead of
# directly calling `%complete-$fnname` for any completion function other
# than those found in this file.

completion-path = /home/jpco/git/es-fork/completions

fn %complete-fn func prefix word {
if {~ $#(fn-%complete-^$func) 0} {
let (f = ()) {
f = <={access -n complete-$func^.es -1 -f $completion-path}
if {!~ $f ()} {
. $f
}
}
}
if {!~ $#(fn-%complete-^$func) 0} {
%complete-^$func $prefix $word
} {
%file-complete {} $word
}
}


#
# Completion logic for built-in concepts.
#

# These functions (named according to the pattern %foo-complete) provide
# completion for specific internal behaviors in the shell. They're pulled
# out of %complete largely so that they can be called by per-command
# completions.

# Completion of variable names.

fn %var-complete word {
result $word^<={~~ (<=$&vars <=$&internals) $word^*}
}

# Generic command-position completion.
# This calls out to %complete-%pathsearch, which is what should be
# overridden when %pathsearch is overridden.

fn %whatis-complete word {
if {~ $word (/* ./* ../* '~'*)} {
%file-complete @ {access -x -- $*} $word
} {
result $word^<={~~ (
local let for fn %closure match
<={~~ (<=$&vars <=$&internals) 'fn-'^*}
) $word^*} <={%complete-fn %pathsearch '' $word}
}
}

# %file-complete calls out to %complete-%home to perform tilde completion,
# and to %home to perform tilde expansion for subdirectories.
# The `filter` argument allows callers to only get specific files, like
# directories or executable files.

fn %file-complete filter word {
# Defining the %completion-to-file function during %complete signals to
# the line editing library that the results of this function are meant
# to be treated as files, and defines a function for the line editing
# library to use to map from each entry to a valid file. This enables
# nice behavior for things like path-searching commands; see
# completions/complete-%pathsearch.es for an example of this.
fn-%completion-to-file = result

let (files = (); homepat = ()) {
if {!~ <={homepat = <={~~ $word '~'*'/'*}} ()} {
let (homedir = (); path = $homepat(2)) {
if {~ $homepat(1) ''} {
homedir = <=%home
} {
homedir = <={%home $homepat(1)}
}
result '~'^$homepat(1)^'/'^<={~~ <={%file-complete $filter $homedir/$path} $homedir/*}
}
} {!~ <={homepat = <={~~ $word '~'*}} ()} {
result '~'^<={%complete-%home '' $homepat}
} {
for (f = $word^*) {
if {$filter $f} {
if {access -d -- $f} {
files = $files $f
} {access -- $f} {
files = $files $f
}
}
}
result $files
}
}
}

fn %pid-complete word {
result $word^<={~~ <=%apids $word^*}
}

# TODO: %fd-complete? %ifs-complete?
1 change: 1 addition & 0 deletions completions/complete-%and.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-%and _ word {%whatis-complete $word}
1 change: 1 addition & 0 deletions completions/complete-%append.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-%append p w {%complete-fn %openfile 'a '^$p $w}
1 change: 1 addition & 0 deletions completions/complete-%background.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn-%complete-%background = %complete
1 change: 1 addition & 0 deletions completions/complete-%create.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-%create p w {%complete-fn %openfile 'w '^$p $w}
4 changes: 4 additions & 0 deletions completions/complete-%home.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn %complete-%home _ word {
result $word^<={~~ `` \n {awk -F: '{print $1}' /etc/passwd} $word^*}
}

1 change: 1 addition & 0 deletions completions/complete-%is-interactive.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn-%complete-%is-interactive = {result}
1 change: 1 addition & 0 deletions completions/complete-%not.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn-%complete-%not = %complete
1 change: 1 addition & 0 deletions completions/complete-%open-append.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-%open-append p w {%complete-fn %openfile 'a+ '^$p $w}
1 change: 1 addition & 0 deletions completions/complete-%open-create.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-%open-create p w {%complete-fn %openfile 'w+ '^$p $w}
1 change: 1 addition & 0 deletions completions/complete-%open-write.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-%open-write p w {%complete-fn %openfile 'r+ '^$p $w}
1 change: 1 addition & 0 deletions completions/complete-%open.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-%open p w {%complete-fn %openfile 'r '^$p $w}
21 changes: 21 additions & 0 deletions completions/complete-%openfile.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
fn %complete-%openfile prefix word {
let (cmd = <={%split ' '\t $prefix}) {
if {~ $#cmd 0} {
# mode
result $word^<={~~ (r w a r+ w+ a+) $word^*}
} {~ $#cmd 1} {
# fd
if {~ $cmd(1) r*} {
result 0
} {
result 1
}
} {~ $#cmd 2} {
# file
%file-complete {} $word
} {
# cmd: pass-through completion
%complete <={%flatten ' ' $cmd(4 ...)} $word
}
}
}
1 change: 1 addition & 0 deletions completions/complete-%or.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-%or _ word {%whatis-complete $word}
16 changes: 16 additions & 0 deletions completions/complete-%pathsearch.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
fn %complete-%pathsearch _ word {
fn %completion-to-file f {
catch @ e {result $f} {
# Like %pathsearch, but don't filter file types.
access -n $f -1e $path
}
}
let (files = ()) {
for (p = $path)
for (w = $p/$word^*)
if {access -x -- $w} {
files = $files <={~~ $w $p/*}
}
result $files
}
}
1 change: 1 addition & 0 deletions completions/complete-%read.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn-%complete-%read = {result}
23 changes: 23 additions & 0 deletions completions/complete-%run.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
fn %complete-%run prefix word {
let (cmd = <={%split ' '\t $prefix})
if {~ $#cmd 0} {
let (result = ()) {
# enforce an absolute path
for (r = <={%file-complete @ {access -x $*} $word}) {
if {~ $r /* ./* ../*} {
result = $result $r
} {
result = $result ./$r
}
}
result $result
}
} {~ $#cmd 1} {
# assume basename of the first term
let (ps = <={%split '/' $cmd(1)}) result $ps($#ps)
} {
# try to pass through to completion on second term
%complete <={%flatten ' ' $cmd(2 ...)} $word
}
}

3 changes: 3 additions & 0 deletions completions/complete-%var.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn %complete-%var _ word {
%var-complete $word
}
3 changes: 3 additions & 0 deletions completions/complete-%whatis.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn %complete-%whatis _ word {
%whatis-complete $word
}
4 changes: 4 additions & 0 deletions completions/complete-cd.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn %complete-cd _ word {
%file-complete @ {access -d -- $*} $word
}

1 change: 1 addition & 0 deletions completions/complete-eval.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn-%complete-eval = %complete
1 change: 1 addition & 0 deletions completions/complete-exec.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn-%complete-exec = %complete
1 change: 1 addition & 0 deletions completions/complete-false.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn-%complete-false = {result}
1 change: 1 addition & 0 deletions completions/complete-if.es
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn %complete-if _ word {%whatis-complete $word}
17 changes: 17 additions & 0 deletions completions/complete-ls.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Very incomplete ls completion to see how --option= completion works.
# Not great so far!
# TODO: enable --opt[TAB] to complete to '--option=', not '--option= '.
# TODO: some kind of fanciness to enable good short-option support?

fn %complete-ls _ word {
if {~ $word -*} {
result $word^<={~~ (
--all
--author
--block-size=
--color=
) $word^*}
} {
%file-complete {} $word
}
}
Loading