Signed-off-by: Sebastian <sebastian@sebsite.pw>
---
I'm a bit meh on storing the kill ring in history. At first I tried
renaming history to persistent_state (I would've just done "state" but
that's already taken), but I feel like it's a bit weird to have
functions which load a generic "state" from a file, when really it's
loading history. But honestly as I'm typing this out maybe that is the
way to go, mainly just sending this for feedback.
One other option would be to have made::line *only* take in a config
(maybe config could be renamed to context), and then the kill ring can
just be stored in the context. I can't think of very many use cases for
just passing in a prompt without a config, and when that is desired it's
pretty easy to just, make an empty config.
made/actions.ha | 7 +++++++
made/hist.ha | 7 ++++++-
made/line.ha | 14 +++++++++++++-
3 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/made/actions.ha b/made/actions.ha
index 0444d74..97181f3 100644
--- a/made/actions.ha
+++ b/made/actions.ha
@@ -113,3 +113,10 @@ fn cmp_completion(a: const *void, b: const *void) int = {
const a = a: *const (str, str), b = b: *const (str, str);
return strings::compare(a.0, b.0);
};
+
+fn kill(s: *state, buf: []u8) void = match (s.cfg.history) {
+case null => void;
+case let h: *history =>
+ free(h.kill);
+ h.kill = alloc(buf...);
+};
diff --git a/made/hist.ha b/made/hist.ha
index 7a43899..8e8fbf9 100644
--- a/made/hist.ha
+++ b/made/hist.ha
@@ -6,10 +6,12 @@ use slices;
use strings;
// An in-memory representation of line-by-line history, backed by an
-// [[io::handle]] for long-term storage. In-memory history is de-duplicated.
+// [[io::handle]] for long-term storage. In-memory history is de-duplicated. The
+// kill ring is also stored here, for persistence across calls to [[line]].
export type history = struct {
storage: io::handle,
hist: []str,
+ kill: []u8,
};
// Loads a [[history]] from a file at the given path, creating a new file if
@@ -35,6 +37,7 @@ export fn histhandle(file: io::handle) (history | error) = {
let store = history {
storage = file,
hist = [],
+ ...
};
for (true) match (bufio::scanline(file)?) {
@@ -61,6 +64,7 @@ fn histhandle_mmap(file: io::file) (history | error) = {
let store = history {
storage = file,
hist = [],
+ ...
};
const off = io::seek(file, 0, io::whence::END)?;
@@ -93,6 +97,7 @@ fn histhandle_mmap(file: io::file) (history | error) = {
// [[io::handle]] to the long-term storage.
export fn hist_finish(h: history) (void | io::error) = {
strings::freeall(h.hist);
+ free(h.kill);
io::close(h.storage)?;
};
diff --git a/made/line.ha b/made/line.ha
index 8ed74bf..16a65e4 100644
--- a/made/line.ha
+++ b/made/line.ha
@@ -106,7 +106,10 @@ export fn line(cfg: (*config | str)) (str | void | io::EOF | error) = {
};
s.ret = buf;
case '\x0b' => // ^k
- delete(s.buf[s.pos..]);
+ if (len(s.buf) > 0 && s.pos < len(s.buf)) {
+ kill(&s, s.buf[s.pos..]);
+ delete(s.buf[s.pos..]);
+ };
case '\x0c' => // ^l
fmt::fprint(s.out, "\x1b[2J\x1b[H")?;
case '\x0e' => // ^n
@@ -124,6 +127,7 @@ export fn line(cfg: (*config | str)) (str | void | io::EOF | error) = {
};
case '\x15' => // ^u
if (s.pos > 0) {
+ kill(&s, s.buf[..s.pos]);
delete(s.buf[..s.pos]);
s.pos = 0;
};
@@ -137,9 +141,17 @@ export fn line(cfg: (*config | str)) (str | void | io::EOF | error) = {
case '\x17' => // ^w
if (s.pos > 0) {
let new = prevword(s.buf, s.pos);
+ kill(&s, s.buf[new..s.pos]);
delete(s.buf[new..s.pos]);
s.pos = new;
};
+ case '\x19' => // ^y
+ match (s.cfg.history) {
+ case null => void;
+ case let h: *history =>
+ insert(s.buf[s.pos], h.kill...);
+ s.pos += len(h.kill);
+ };
case '\x1b' => // esc
switch (getbyte(&s)?) {
case '[' =>
--
2.38.4
we should store the kill ring as a [][]u8, so that ^w^w^y^y is a no-op
On Tue Feb 28, 2023 at 10:49 PM UTC, Sebastian wrote:
> One other option would be to have made::line *only* take in a config
> (maybe config could be renamed to context), and then the kill ring can
> just be stored in the context. I can't think of very many use cases for
> just passing in a prompt without a config, and when that is desired it's
> pretty easy to just, make an empty config.
+1 to that (and +1 to renaming to context). the goal was to make
made::line("prompt") mostly work with minimal effort, but i think
requiring a full context struct makes more sense in light of the kill
ring
Feb 28, 2023 20:03:35 Ember Sawady <ecs@d2evs.net>:
> we should store the kill ring as a [][]u8, so that ^w^w^y^y is a no-op
I'm not aware of any other program that works like this. readline has
only one kill ring, and fish allows pressing ^w multiple times to prepend
to the kill ring, but there's still only one; pressing ^y multiple times
always pastes the same thing. I can see how the fish behavior would be
desirable.