···11(* A shell... one day *)
22module C = Merry.Eval.Make (Merry_posix.State) (Merry_posix.Exec)
33-module I = Merry.Interactive.Make (Merry_posix.State) (Merry_posix.Exec)
33+44+module I =
55+ Merry.Interactive.Make (Merry_posix.State) (Merry_posix.Exec)
66+ (Merry.History.Prefix_search)
4758let sh ~command ~dump ~file ~rest env =
69 let executor = Merry_posix.Exec.{ mgr = env#process_mgr } in
···73737474 let state ctx = ctx.state
7575 let sigint_set ctx = ctx.signal_handler.sigint_set
7676+ let fs ctx = ctx.fs
7677 let clear_local_state ctx = { ctx with local_state = [] }
77787879 let rec tilde_expansion ctx = function
+3
src/lib/eval.mli
···3232 E.t ->
3333 ctx
34343535+ val fs : ctx -> Eio.Fs.dir_ty Eio.Path.t
3636+ (** The file system capability *)
3737+3538 val state : ctx -> S.t
3639 (** Return the current state of the context. *)
3740
+24
src/lib/history.ml
···11+(* Some history implementation that can be used out of the box. *)
22+module Prefix_search : Types.History = struct
33+ type t = string list
44+55+ let empty = []
66+77+ type entry = string
88+99+ let make_entry s = s
1010+ let add e t = e :: t
1111+1212+ let history ~command h =
1313+ if command <> "" then
1414+ List.filter (fun s -> String.starts_with ~prefix:command s) h
1515+ else h
1616+1717+ let commands t = t
1818+1919+ let save t path =
2020+ Eio.Path.save ~create:(`If_missing 0o644) path (String.concat "\n" t)
2121+2222+ let load path = Eio.Path.load path |> String.split_on_char '\n'
2323+ let pp ppf = Fmt.(list ~sep:(Fmt.any "\n") string) ppf
2424+end
+15-15
src/lib/interactive.ml
···11let () = Fmt.set_style_renderer Format.str_formatter `Ansi_tty
2233-module Make (S : Types.State) (E : Types.Exec) = struct
33+module Make (S : Types.State) (E : Types.Exec) (H : Types.History) = struct
44 module Eval = Eval.Make (S) (E)
5566 let pp_colored c pp fmt v = Fmt.pf fmt "%a" (Fmt.styled (`Fg c) pp) v
···6565 | S_DIR -> completions path None
6666 | _ -> [])
67676868- (* For now a very simple, prefixed based history *)
6969-7070- let h = ref []
7171-7272- let add_history c =
7373- let c = String.trim c in
7474- let new_h = List.filter (fun s -> not (String.equal c s)) !h in
7575- h := c :: new_h
7676-7777- let history prefix =
7878- if prefix <> "" then List.filter (fun s -> String.starts_with ~prefix s) !h
7979- else !h
8080-8168 let run ?(prompt = default_prompt) initial_ctx =
8269 Sys.set_signal Sys.sigttou Sys.Signal_ignore;
8370 Sys.set_signal Sys.sigttin Sys.Signal_ignore;
8471 Sys.set_signal Sys.sigtstp Sys.Signal_ignore;
8572 Sys.set_signal Sys.sigint Sys.Signal_ignore;
7373+ let xdg = Xdge.create (Eval.fs (Exit.value initial_ctx)) "merry" in
7474+ let history = Eio.Path.(Xdge.data_dir xdg / ".merry_history") in
7575+ let initial_history = try H.load history with _ -> H.empty in
7676+ let h = ref initial_history in
7777+ let add_history c =
7878+ let c = String.trim c in
7979+ h := H.add (H.make_entry c) !h
8080+ in
8681 let rec loop (ctx : Eval.ctx Exit.t) =
8782 Option.iter (Fmt.epr "%s%!")
8883 (S.lookup (Exit.value ctx |> Eval.state) ~param:"PS1"
8984 |> Option.map Ast.word_components_to_string);
9085 let p = prompt ctx in
9186 Fmt.pr "%s\r%!" p;
9292- match Bruit.bruit ~history ~complete "" with
8787+ match
8888+ Bruit.bruit
8989+ ~history:(fun command -> H.history ~command !h |> H.commands)
9090+ ~complete ""
9191+ with
9392 | String None ->
9493 Fmt.pr "exit\n%!";
9594 exit 0
···9897 Fmt.pr "\n%!";
9998 let ctx', _ast = Eval.run ctx ast in
10099 add_history c;
100100+ H.save !h history;
101101 loop ctx'
102102 | Ctrl_c ->
103103 let c = Exit.value ctx in
+1
src/lib/merry.ml
···77module Eval = Eval
88module Interactive = Interactive
99module Built_ins = Built_ins
1010+module History = History
10111112module Variable = struct
1213 type t
+1
src/lib/merry.mli
···66module Eval = Eval
77module Interactive = Interactive
88module Built_ins = Built_ins
99+module History = History
9101011module Variable : sig
1112 type t
+33
src/lib/types.ml
···128128 (** Given a job, [await_exit] will wait for the job to finish and return the
129129 exit based on the various options passed in. *)
130130end
131131+132132+module type History = sig
133133+ type t
134134+ (** A history of commands *)
135135+136136+ val empty : t
137137+ (** The empty history *)
138138+139139+ type entry
140140+ (** A history entry, usually a command. *)
141141+142142+ val make_entry : string -> entry
143143+ (** Construct an entry given a command. *)
144144+145145+ val add : entry -> t -> t
146146+ (** Add an entry to the history. *)
147147+148148+ val save : t -> _ Eio.Path.t -> unit
149149+ (** [save t path] should save history [t] to [path]. *)
150150+151151+ val load : _ Eio.Path.t -> t
152152+ (** [load path] should try to read a history from [path]. *)
153153+154154+ val history : command:string -> t -> t
155155+ (** [history ~command t] should return some subset of [t] (perhaps all of [t])
156156+ based on the current [command] to be used for history searching. *)
157157+158158+ val commands : t -> string list
159159+ (** Converts your (perhaps richer) history to just a series of commands. *)
160160+161161+ val pp : t Fmt.t
162162+ (** A pretty printer for the commands *)
163163+end