View source with formatted comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2017-2025, VU University Amsterdam
    7                              CWI Amsterdam
    8                              SWI-Prolog Solutions b.v.
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(editline,
   38          [ el_wrap/0,                          % wrap user_input, etc.
   39            el_wrap/1,                          % +Options
   40            el_wrap/4,                          % +Prog, +Input, +Output, +Error
   41            el_wrap/5,                          % +Prog, +Input, +Output, +Error, +Options
   42            el_wrapped/1,                       % +Input
   43            el_unwrap/1,                        % +Input
   44
   45            el_source/2,                        % +Input, +File
   46            el_bind/2,                          % +Input, +Args
   47            el_set/2,                           % +Input, +Action
   48            el_get/2,                           % +Input, ?Property
   49            el_addfn/4,                         % +Input, +Name, +Help, :Goal
   50            el_cursor/2,                        % +Input, +Move
   51            el_line/2,                          % +Input, -Line
   52            el_insertstr/2,                     % +Input, +Text
   53            el_deletestr/2,                     % +Input, +Count
   54
   55            el_history/2,                       % +Input, ?Action
   56            el_history_events/2,                % +Input, -Events
   57            el_add_history/2,                   % +Input, +Line
   58            el_write_history/2,                 % +Input, +FileName
   59            el_read_history/2,                  % +Input, +FileName
   60
   61	    el_version/1			% -Version:integer
   62          ]).   63:- autoload(library(apply),[maplist/2,maplist/3]).   64:- autoload(library(lists),[reverse/2,max_list/2,append/3,member/2]).   65:- autoload(library(solution_sequences),[call_nth/2]).   66:- autoload(library(option), [merge_options/3]).   67
   68:- use_foreign_library(foreign(libedit4pl)).   69
   70:- initialization el_wrap_if_ok.   71
   72:- meta_predicate
   73    el_addfn(+,+,+,3).   74
   75:- multifile
   76    el_setup/1,                         % +Input
   77    el_wcwidth/2,                       % +Code, -Columns
   78    prolog:complete_input/4.   79
   80
   81/** <module> BSD libedit based command line editing
   82
   83This library wraps the BSD  libedit   command  line  editor. The binding
   84provides a high level API to enable   command line editing on the Prolog
   85user streams and low level predicates  to   apply  the  library on other
   86streams and program the library.
   87
   88While reading a line, the binding  surrounds   the  prompt and the input
   89with the OSC 133 _semantic  prompt_   marks  of FinalTerm: ``ESC]133;A``
   90before the prompt, ``ESC]133;B`` before   the input, ``ESC]133;C`` after
   91the line was entered and ``ESC]133;D`` where   its output ends, which is
   92just before the next line is asked for.  A term that takes more than one
   93line is read a line at a time; the   prompt of each line after the first
   94is marked ``ESC]133;A;k=s``, which says  it   continues  an input rather
   95than starting one, so that a terminal  can keep the whole term together.
   96A terminal that understands them  (Epilog,   iTerm2,  kitty, WezTerm, VS
   97Code) can tell prompt, input and  output   apart,  which is what lets it
   98jump between prompts, act on the output   of  one command or, in Epilog,
   99move the caret to a click in the line being edited. A read that asks for
  100a single character, as get_single_char/1  and   the  debugger do, is not
  101marked: there is no line being edited   there.  A terminal that does not
  102know the marks ignores them; one that  prints them instead can be spared
  103them with el_set/2 using `prompt_marks(false)`.
  104*/
  105
  106el_wrap_if_ok :-
  107    stream_property(user_input, tty(true)),
  108    !,
  109    el_wrap.
  110el_wrap_if_ok.
  111
  112%!  el_wrap is det.
  113%!  el_wrap(+Options) is det.
  114%
  115%   Enable using editline on the standard   user streams if `user_input`
  116%   is connected to a terminal. This is   the  high level predicate used
  117%   for most purposes. The remainder of the library interface deals with
  118%   low level predicates  that  allows   for  applying  and  programming
  119%   libedit in non-standard situations.
  120%
  121%   The library is registered  with  _ProgName_   set  to  `swipl`  (see
  122%   el_wrap/4).
  123%
  124%   Options processed:
  125%
  126%     - pipes(+Boolean)
  127%       Used by Epilog windows to indicate we are reading from a Windows
  128%       named pipe in _overlapped_ mode.  Ignored on other platforms.
  129%     - history(+Size)
  130%       Size of the history.  Default is defined by the Prolog flag
  131%       `history` or `100` if this flag is not defined.
  132%     - alert_signo(+Integer)
  133%       Signal used for making thread_signal/2 work while the thread
  134%       is in a blocking system call.
  135
  136el_wrap :-
  137    el_wrap([]).
  138
  139el_wrap(_) :-
  140    el_wrapped(user_input),
  141    !.
  142el_wrap(Options) :-
  143    stream_property(user_input, tty(true)), !,
  144    findall(Opt, el_default(Opt), Defaults),
  145    merge_options(Options, Defaults, Options1),
  146    el_wrap(swipl, user_input, user_output, user_error, Options1),
  147    add_prolog_commands(user_input),
  148    ignore(el_set(user_input, wordchars("_"))),
  149    forall(el_setup(user_input), true),
  150    enable_bracketed_paste(user_input),
  151    enable_word_motion(user_input),
  152    enable_windows_eof(user_input).
  153el_wrap(_).
  154
  155el_default(history(Size)) :-
  156    current_prolog_flag(history, Value),
  157    (   integer(Value),
  158        Value >= 0
  159    ->  Size = Value
  160    ;   Value == false
  161    ->  Size = 0
  162    ).
  163:- if(current_predicate(prolog_alert_signal/2)).  164el_default(alert_signo(SigNo)) :-
  165    prolog_alert_signal(SigName, SigName),
  166    current_signal(SigName, SigNo, _Handler).
  167:- endif.  168
  169add_prolog_commands(Input) :-
  170    el_addfn(Input, complete, 'Complete atoms and files', complete),
  171    el_addfn(Input, show_completions, 'List completions', show_completions),
  172    el_addfn(Input, electric, 'Indicate matching bracket', electric),
  173    el_addfn(Input, isearch_history, 'Incremental search in history',
  174             isearch_history),
  175    el_addfn(Input, bracketed_paste, 'Handle bracketed paste', bracketed_paste),
  176    el_bind(Input, ["^I",  complete]),
  177    el_bind(Input, ["^[?", show_completions]),
  178    el_bind(Input, ["^R",  isearch_history]),
  179    bind_electric(Input),
  180    add_paste_quoted(Input),
  181    el_source(Input, _).
  182
  183%!  enable_bracketed_paste(+Input) is det.
  184%
  185%   Sync bracketed paste mode with the current editor: bind ESC[200~
  186%   to bracketed_paste/3 and enable the mode in emacs, unbind and
  187%   disable it in vi.  In vi mode ESC leaves insert mode, so the
  188%   ESC[200~ start marker cannot be dispatched as a key binding.
  189%   Called from el_wrap/1 after the el_setup/1 hook, since that hook
  190%   may switch editor with el_bind/2 `-v` or `-e`.
  191
  192enable_bracketed_paste(Input) :-
  193    (   el_get(Input, editor(vi))
  194    ->  el_bind(Input, ['-r', "\e[200~"]),
  195        el_set(Input, bracketed_paste(false))
  196    ;   el_bind(Input, ["\e[200~", bracketed_paste]),
  197        el_set(Input, bracketed_paste(true))
  198    ).
  199
  200%!  enable_word_motion(+Input) is det.
  201%
  202%   Bind the xterm Ctrl+Left/Ctrl+Right escape sequences to word
  203%   motion.  The Epilog terminal sends ``ESC[1;5D`` and ``ESC[1;5C``
  204%   for these key combinations (see packages/xpce/src/txt/terminal.c);
  205%   most xterm-compatible terminals use the same sequences.  In vi
  206%   mode the binding is installed in both the insert keymap and the
  207%   command (alternative) keymap.  Called after the el_setup/1 hooks
  208%   since those may switch editor.
  209%
  210%   This complements the readline emulation, which already binds
  211%   these sequences in its initialisation.
  212
  213enable_word_motion(Input) :-
  214    el_bind(Input, ["\e[1;5D", 'ed-prev-word']),
  215    el_bind(Input, ["\e[1;5C", 'em-next-word']),
  216    (   el_get(Input, editor(vi))
  217    ->  el_bind(Input, ['-a', "\e[1;5D", 'vi-prev-word']),
  218        el_bind(Input, ['-a', "\e[1;5C", 'vi-next-word'])
  219    ;   true
  220    ).
  221
  222%!  enable_windows_eof(+Input) is det.
  223%
  224%   Bind ``^Z`` to send end-of-file on Windows, matching the platform
  225%   convention (compare with ``^D`` on Unix).  Routed through libedit's
  226%   built-in `em-delete-or-list` (emacs) or `vi-list-or-eof` (vi), both
  227%   of which return EOF on an empty line.
  228
  229enable_windows_eof(Input) :-
  230    current_prolog_flag(windows, true),
  231    !,
  232    (   el_get(Input, editor(vi))
  233    ->  el_bind(Input, ["^Z", 'vi-list-or-eof'])
  234    ;   el_bind(Input, ["^Z", 'em-delete-or-list'])
  235    ).
  236enable_windows_eof(_).
  237
  238%!  el_wrap(+ProgName:atom, +In:stream, +Out:stream, +Error:stream) is det.
  239%!  el_wrap(+ProgName:atom, +In:stream, +Out:stream, +Error:stream, +Options) is det.
  240%
  241%   Enable editline on  the  stream-triple   <In,Out,Error>.  From  this
  242%   moment on In is a handle to the command line editor.  Options:
  243%
  244%     - pipes(true)
  245%       Windows only. Assume the I/O is using pipes rather than a
  246%       console.  This is used for the Epilog terminal.
  247%
  248%   @arg ProgName is the name of the invoking program, used when reading
  249%   the editrc(5) file to determine which settings to use.
  250
  251el_wrap(ProgName, In, Out, Error) :-
  252    el_wrap(ProgName, In, Out, Error, []).
  253
  254%!  el_setup(+In:stream) is nondet.
  255%
  256%   This hooks is called as   forall(el_setup(Input),  true) _after_ the
  257%   input stream has been wrapped, the default Prolog commands have been
  258%   added and the  default  user  setup   file  has  been  sourced using
  259%   el_source/2. It can be used to define and bind additional commands.
  260
  261%!  el_wrapped(+In:stream) is semidet.
  262%
  263%   True if In is a stream wrapped by el_wrap/3.
  264
  265%!  el_unwrap(+In:stream) is det.
  266%
  267%   Remove the libedit wrapper for In and   the related output and error
  268%   streams.
  269%
  270%   @bug The wrapper creates =|FILE*|= handles that cannot be closed and
  271%   thus wrapping and unwrapping implies a (modest) memory leak.
  272
  273%!  el_source(+In:stream, +File) is det.
  274%
  275%   Initialise editline by reading the contents of File.  If File is
  276%   unbound try =|$HOME/.editrc|=
  277
  278
  279%!  el_bind(+In:stream, +Args) is det.
  280%
  281%   Invoke the libedit `bind` command  with   the  given  arguments. The
  282%   example below lists the current key bindings.
  283%
  284%   ```
  285%   ?- el_bind(user_input, ['-a']).
  286%   ```
  287%
  288%   The predicate el_bind/2 is typically used   to bind commands defined
  289%   using el_addfn/4. Note that the C proxy   function has only the last
  290%   character of the command as context to find the Prolog binding. This
  291%   implies we cannot both bind  e.g.,  "^[?"   _and_  "?"  to  a Prolog
  292%   function.
  293%
  294%   @see editrc(5) for more information.
  295
  296%!  el_addfn(+Input:stream, +Command, +Help, :Goal) is det.
  297%
  298%   Add a new command to the command  line editor associated with Input.
  299%   Command is the name of the command,  Help is the help string printed
  300%   with e.g. =|bind -a|= (see el_bind/2)  and   Goal  is  called of the
  301%   associated key-binding is activated.  Goal is called as
  302%
  303%       call(:Goal, +Input, +Char, -Continue)
  304%
  305%   where Input is the input stream providing access to the editor, Char
  306%   the activating character and Continue must   be instantated with one
  307%   of the known continuation  codes  as   defined  by  libedit: `norm`,
  308%   `newline`, `eof`, `arghack`, `refresh`,   `refresh_beep`,  `cursor`,
  309%   `redisplay`, `error` or `fatal`. In addition, the following Continue
  310%   code is provided.
  311%
  312%     * electric(Move, TimeOut, Continue)
  313%     Show _electric caret_ at Move positions to the left of the normal
  314%     cursor positions for the given TimeOut.  Continue as defined by
  315%     the Continue value.
  316%
  317%   The registered Goal typically used el_line/2 to fetch the input line
  318%   and el_cursor/2, el_insertstr/2 and/or  el_deletestr/2 to manipulate
  319%   the input line.
  320%
  321%   Normally el_bind/2 is used to associate   the defined command with a
  322%   keyboard sequence.
  323%
  324%   @see el_set(3) =EL_ADDFN= for details.
  325
  326%!  el_set(+Input:stream, +Action) is semidet.
  327%
  328%   Interface to el_set() and el_wset().   Currently provided values for
  329%   Action are:
  330%
  331%     - wordchars(+Text)
  332%       Set the characters considered part of a _word_.  This feature
  333%       depends on el_wsey() ``EL_WORDCHARS``, which is only provided
  334%       in some recent versions of `libedit`.
  335%     - bracketed_paste(+Boolean)
  336%       Enable or disable bracketed paste mode.  When enabled, the
  337%       terminal is asked to bracket pasted text with ``ESC[200~`` /
  338%       ``ESC[201~`` before each prompt, which the default bindings
  339%       route through bracketed_paste/3.  Disabling sends the matching
  340%       ``ESC[?2004l`` sequence immediately.  enable_bracketed_paste/1
  341%       manages this based on the current editor; you normally do not
  342%       need to set it directly.
  343%     - prompt_marks(+Boolean)
  344%       Enable or disable the OSC 133 prompt marks described with this
  345%       module.  They are enabled by default and are ignored by a
  346%       terminal that does not know them; disable them for a terminal
  347%       that prints them instead.  Note that Epilog then has neither
  348%       mark nor bracketed paste to tell it that a line is being
  349%       edited if bracketed paste is disabled as well, and a click no
  350%       longer moves the caret.
  351%
  352%   This predicate fails silently of Action  is not implemented. Illegal
  353%   input raises in an exception.
  354
  355%!  el_get(+Input:stream, ?Property) is semidet.
  356%
  357%   Interface to el_get().  Currently supported Property terms:
  358%
  359%     - editor(-Editor)
  360%       Editor is unified with `emacs` or `vi`, reflecting the current
  361%       keymap selected via el_bind/2 with `-e` / `-v`.
  362%     - bracketed_paste(-Boolean)
  363%       Whether bracketed paste mode is currently enabled; see
  364%       el_set/2.
  365%     - prompt_marks(-Boolean)
  366%       Whether the prompt and the input are marked with OSC 133; see
  367%       el_set/2.
  368%
  369%   Any other Property raises a `domain_error(editline_property, _)`.
  370
  371%!  el_line(+Input:stream, -Line) is det.
  372%
  373%   Fetch the currently buffered input line. Line is a term line(Before,
  374%   After), where `Before` is  a  string   holding  the  text before the
  375%   cursor and `After` is a string holding the text after the cursor.
  376
  377%!  el_cursor(+Input:stream, +Move:integer) is det.
  378%
  379%   Move the cursor Move  character   forwards  (positive)  or backwards
  380%   (negative).
  381
  382%!  el_insertstr(+Input:stream, +Text) is det.
  383%
  384%   Insert Text at the cursor.
  385
  386%!  el_deletestr(+Input:stream, +Count) is det.
  387%
  388%   Delete Count characters before the cursor.
  389
  390%!  el_history(+In:stream, ?Action) is det.
  391%
  392%   Perform a generic action on the history. This provides an incomplete
  393%   interface to history() from libedit.  Supported actions are:
  394%
  395%     - clear
  396%       Clear the history.
  397%     - setsize(+Integer)
  398%       Set size of history to size elements.
  399%     - getsize(-Integer)
  400%       Unify Integer with the maximum size of the history.  Note that
  401%       this is _not_ the same as el_history() using ``H_GETSIZE``,
  402%       which returns the number of currently saved events. The number
  403%       of saved events may be computed from `first` and `last` or
  404%       using el_history_events/2.
  405%     - setunique(+Boolean)
  406%       Set flag that adjacent identical event strings should not be
  407%       entered into the history.
  408%     - first(-Num, -String)
  409%     - last(-Num, -String)
  410%     - curr(-Num, -String)
  411%     - prev(-Num, -String)
  412%     - next(-Num, -String)
  413%       Retrieve an event.  Num is the event number and String is the
  414%       event string.  Note that `first` is the most recent event and
  415%       `last` the oldest.
  416%     - set(Num)
  417%       Set the notion of _current_ to Num.
  418%     - prev_str(+Search, -Num, -String)
  419%     - next_str(+Search, -Num, -String)
  420%       Retrieve the previous or next event whose String starts with
  421%       Search.
  422%     - event(+Num, -String)
  423%       True when String represents event Num.   This is an extension to
  424%       the history() API, retrieving a numbered event without changing
  425%       the current notion.
  426
  427%!  el_history_events(+In:stream, -Events:list(pair)) is det.
  428%
  429%   Unify Events with a list of pairs   of  the form `Num-String`, where
  430%   `Num` is the event number  and   `String`  is  the associated string
  431%   without terminating newline.
  432
  433%!  el_add_history(+In:stream, +Line:text) is det.
  434%
  435%   Add a line to the command line history.
  436
  437%!  el_read_history(+In:stream, +File:file) is det.
  438%
  439%   Read the history saved using el_write_history/2.
  440%
  441%   @arg File is a file specification for absolute_file_name/3.
  442
  443%!  el_write_history(+In:stream, +File:file) is det.
  444%
  445%   Save editline history to File.  The   history  may be reloaded using
  446%   el_read_history/2.
  447%
  448%   @arg File is a file specification for absolute_file_name/3.
  449
  450%!  el_version(-Version)
  451%
  452%   True when Version  is ``LIBEDIT_MAJOR*10000 + LIBEDIT_MINOR*100``.
  453%   The  version is  generated from  the include  file ``histedit.h``,
  454%   which implies that the actual version of the shared library may be
  455%   different.
  456
  457%!  prolog:history(+Input, ?Action) is semidet.
  458%
  459%   Provide  the  plugable  interface  into   the  system  command  line
  460%   management.
  461
  462:- multifile
  463    prolog:history/2.  464
  465prolog:history(Input, enabled) :-
  466    !,
  467    el_wrapped(Input),
  468    el_history(Input, getsize(Size)),
  469    Size > 0.
  470prolog:history(Input, add(Line)) :-
  471    !,
  472    el_add_history(Input, Line).
  473prolog:history(Input, load(File)) :-
  474    !,
  475    compat_read_history(Input, File).
  476prolog:history(Input, save(File)) :-
  477    !,
  478    el_write_history(Input, File).
  479prolog:history(Input, events(Events)) :-
  480    !,
  481    el_history_events(Input, Events).
  482prolog:history(Input, Command) :-
  483    public_command(Command),
  484    !,
  485    el_history(Input, Command).
  486
  487public_command(first(_Num, _String)).
  488public_command(curr(_Num, _String)).
  489public_command(event(_Num, _String)).
  490public_command(prev_str(_Search, _Num, _String)).
  491public_command(clear).
  492
  493%!  compat_read_history(+Input, +File) is det.
  494%
  495%   Read the saved history. This loads both  the LibEdit and old history
  496%   format used by `swipl-win.exe` before migrating to SDL.
  497
  498compat_read_history(Input, File) :-
  499    catch(el_read_history(Input, File), error(editline(_),_), fail),
  500    !.
  501compat_read_history(Input, File) :-
  502    access_file(File, read),
  503    setup_call_cleanup(
  504        open(File, read, In, [encoding(utf8)]),
  505        read_old_history(Input, In),
  506        close(In)),
  507    !.
  508compat_read_history(_, _).
  509
  510read_old_history(Input, From) :-
  511    catch('$raw_read'(From, Line), error(_,_), fail),
  512    (   Line == end_of_file
  513    ->  true
  514    ;   string_concat(Line, '.', Event),
  515        el_add_history(Input, Event),
  516        read_old_history(Input, From)
  517    ).
  518
  519		 /*******************************
  520		 *        ELECTRIC CARET	*
  521		 *******************************/
  522
  523%!  bind_electric(+Input) is det.
  524%
  525%   Bind known close statements for electric input
  526
  527bind_electric(Input) :-
  528    forall(bracket(_Open, Close), bind_code(Input, Close, electric)),
  529    forall(quote(Close), bind_code(Input, Close, electric)).
  530
  531bind_code(Input, Code, Command) :-
  532    string_codes(Key, [Code]),
  533    el_bind(Input, [Key, Command]).
  534
  535
  536%!  electric(+Input, +Char, -Continue) is det.
  537
  538electric(Input, Char, Continue) :-
  539    string_codes(Str, [Char]),
  540    el_insertstr(Input, Str),
  541    el_line(Input, line(Before, _)),
  542    (   string_codes(Before, Codes),
  543        nesting(Codes, 0, Nesting),
  544        reverse(Nesting, [Close|RevNesting])
  545    ->  (   Close = open(_,_)                   % open quote
  546        ->  Continue = refresh
  547        ;   matching_open(RevNesting, Close, _, Index)
  548        ->  string_length(Before, Len),         % Proper match
  549            Move is Index-Len,
  550            Continue = electric(Move, 500, refresh)
  551        ;   Continue = refresh_beep             % Not properly nested
  552        )
  553    ;   Continue = refresh_beep
  554    ).
  555
  556matching_open_index(String, Index) :-
  557    string_codes(String, Codes),
  558    nesting(Codes, 0, Nesting),
  559    reverse(Nesting, [Close|RevNesting]),
  560    matching_open(RevNesting, Close, _, Index).
  561
  562matching_open([Open|Rest], Close, Rest, Index) :-
  563    Open = open(Index,_),
  564    match(Open, Close),
  565    !.
  566matching_open([Close1|Rest1], Close, Rest, Index) :-
  567    Close1 = close(_,_),
  568    matching_open(Rest1, Close1, Rest2, _),
  569    matching_open(Rest2, Close, Rest, Index).
  570
  571match(open(_,Open),close(_,Close)) :-
  572    (   bracket(Open, Close)
  573    ->  true
  574    ;   Open == Close,
  575        quote(Open)
  576    ).
  577
  578bracket(0'(, 0')).
  579bracket(0'[, 0']).
  580bracket(0'{, 0'}).
  581
  582quote(0'\').
  583quote(0'\").
  584quote(0'\`).
  585
  586nesting([], _, []).
  587nesting([H|T], I, Nesting) :-
  588    (   bracket(H, _Close)
  589    ->  Nesting = [open(I,H)|Nest]
  590    ;   bracket(_Open, H)
  591    ->  Nesting = [close(I,H)|Nest]
  592    ),
  593    !,
  594    I2 is I+1,
  595    nesting(T, I2, Nest).
  596nesting([0'0, 0'\'|T], I, Nesting) :-
  597    !,
  598    phrase(skip_code, T, T1),
  599    difflist_length(T, T1, Len),
  600    I2 is I+Len+2,
  601    nesting(T1, I2, Nesting).
  602nesting([H|T], I, Nesting) :-
  603    quote(H),
  604    !,
  605    (   phrase(skip_quoted(H), T, T1)
  606    ->  difflist_length(T, T1, Len),
  607        I2 is I+Len+1,
  608        Nesting = [open(I,H),close(I2,H)|Nest],
  609        nesting(T1, I2, Nest)
  610    ;   Nesting = [open(I,H)]                   % Open quote
  611    ).
  612nesting([_|T], I, Nesting) :-
  613    I2 is I+1,
  614    nesting(T, I2, Nesting).
  615
  616difflist_length(List, Tail, Len) :-
  617    difflist_length(List, Tail, 0, Len).
  618
  619difflist_length(List, Tail, Len0, Len) :-
  620    List == Tail,
  621    !,
  622    Len = Len0.
  623difflist_length([_|List], Tail, Len0, Len) :-
  624    Len1 is Len0+1,
  625    difflist_length(List, Tail, Len1, Len).
  626
  627skip_quoted(H) -->
  628    [H],
  629    !.
  630skip_quoted(H) -->
  631    "\\", [H],
  632    !,
  633    skip_quoted(H).
  634skip_quoted(H) -->
  635    [_],
  636    skip_quoted(H).
  637
  638skip_code -->
  639    "\\", [_],
  640    !.
  641skip_code -->
  642    [_].
  643
  644
  645		 /*******************************
  646		 *           COMPLETION		*
  647		 *******************************/
  648
  649%!  complete(+Input, +Char, -Continue) is det.
  650%
  651%   Implementation of the registered `complete`   editline function. The
  652%   predicate is called with three arguments,  the first being the input
  653%   stream used to access  the  libedit   functions  and  the second the
  654%   activating character. The last argument tells   libedit  what to do.
  655%   Consult el_set(3), =EL_ADDFN= for details.
  656
  657
  658:- dynamic
  659    last_complete/2.  660
  661complete(Input, _Char, Continue) :-
  662    el_line(Input, line(Before, After)),
  663    ensure_input_completion,
  664    prolog:complete_input(Before, After, Delete, Completions),
  665    (   Completions = [One]
  666    ->  string_length(Delete, Len),
  667        el_deletestr(Input, Len),
  668        complete_text(One, Text),
  669        el_insertstr(Input, Text),
  670        Continue = refresh
  671    ;   Completions == []
  672    ->  Continue = refresh_beep
  673    ;   get_time(Now),
  674        retract(last_complete(TLast, Before)),
  675        Now - TLast < 2
  676    ->  plain_style,
  677        nl(user_error),
  678        list_alternatives(Completions),
  679        Continue = redisplay
  680    ;   retractall(last_complete(_,_)),
  681        get_time(Now),
  682        asserta(last_complete(Now, Before)),
  683        common_competion(Completions, Extend),
  684        (   Delete == Extend
  685        ->  Continue = refresh_beep
  686        ;   string_length(Delete, Len),
  687            el_deletestr(Input, Len),
  688            el_insertstr(Input, Extend),
  689            Continue = refresh
  690        )
  691    ).
  692
  693:- dynamic
  694    input_completion_loaded/0.  695
  696ensure_input_completion :-
  697    input_completion_loaded,
  698    !.
  699ensure_input_completion :-
  700    predicate_property(prolog:complete_input(_,_,_,_),
  701                       number_of_clauses(N)),
  702    N > 0,
  703    !.
  704ensure_input_completion :-
  705    exists_source(library(console_input)),
  706    !,
  707    use_module(library(console_input), []),
  708    asserta(input_completion_loaded).
  709ensure_input_completion.
  710
  711
  712%!  show_completions(+Input, +Char, -Continue) is det.
  713%
  714%   Editline command to show possible completions.
  715
  716show_completions(Input, _Char, Continue) :-
  717    el_line(Input, line(Before, After)),
  718    prolog:complete_input(Before, After, _Delete, Completions),
  719    plain_style,
  720    nl(user_error),
  721    list_alternatives(Completions),
  722    Continue = redisplay.
  723
  724%!  prolog:line_editor_attributes(+Off, +On) is det.
  725%
  726%   Tell libedit how to switch the  decoration of the line being edited
  727%   off and on again.  The toplevel leaves the attributes of the colour
  728%   class `input` in effect while the  user is typing (see
  729%   decorate_prompt/2 in `boot/toplevel.pl`).  Erasing part of the screen
  730%   paints it with the current background colour, so libedit must switch
  731%   these attributes off before it erases  the rows the line no longer
  732%   uses, e.g. after unwrapping a long line or moving from a multi-line
  733%   history entry to a shorter one.  See EL_ATTRS in libedit.
  734%
  735%   The toplevel calls this each time it builds a prompt, so a theme
  736%   loaded at run time is picked up.  Does nothing if libedit is too old
  737%   to support this.
  738
  739prolog:line_editor_attributes(Off, On) :-
  740    el_wrapped(user_input),
  741    ignore(el_set(user_input, attrs(Off, On))).
  742
  743%!  plain_style is det.
  744%
  745%   Reset the terminal attributes before writing text that is not part of
  746%   the line being edited, such  as   the  completion alternatives or the
  747%   incremental search prompt.
  748%
  749%   The toplevel decorates the input line   by leaving the attributes of
  750%   the colour class `input` in effect while the user is typing (see
  751%   decorate_prompt/2 in `boot/toplevel.pl`).  Without this reset that
  752%   decoration also applies to the text below, and if the class has a
  753%   background colour it paints whatever we erase with `\e[K` as well.
  754%
  755%   The commands that write this text end with `redisplay`, which makes
  756%   libedit redraw the line from scratch and thus re-install the style.
  757
  758plain_style :-
  759    format(user_error, '\e[0m', []).
  760
  761complete_text(Text-_Comment, Text) :- !.
  762complete_text(Text, Text).
  763
  764%!  common_competion(+Alternatives, -Common) is det.
  765%
  766%   True when Common is the common prefix of all candidate Alternatives.
  767
  768common_competion(Alternatives, Common) :-
  769    maplist(atomic, Alternatives),
  770    !,
  771    common_prefix(Alternatives, Common).
  772common_competion(Alternatives, Common) :-
  773    maplist(complete_text, Alternatives, AltText),
  774    !,
  775    common_prefix(AltText, Common).
  776
  777%!  common_prefix(+Atoms, -Common) is det.
  778%
  779%   True when Common is the common prefix of all Atoms.
  780
  781common_prefix([A1|T], Common) :-
  782    common_prefix_(T, A1, Common).
  783
  784common_prefix_([], Common, Common).
  785common_prefix_([H|T], Common0, Common) :-
  786    common_prefix(H, Common0, Common1),
  787    common_prefix_(T, Common1, Common).
  788
  789%!  common_prefix(+A1, +A2, -Prefix:string) is det.
  790%
  791%   True when Prefix is the common prefix of the atoms A1 and A2
  792
  793common_prefix(A1, A2, Prefix) :-
  794    sub_atom(A1, 0, _, _, A2),
  795    !,
  796    Prefix = A2.
  797common_prefix(A1, A2, Prefix) :-
  798    sub_atom(A2, 0, _, _, A1),
  799    !,
  800    Prefix = A1.
  801common_prefix(A1, A2, Prefix) :-
  802    atom_codes(A1, C1),
  803    atom_codes(A2, C2),
  804    list_common_prefix(C1, C2, C),
  805    string_codes(Prefix, C).
  806
  807list_common_prefix([H|T0], [H|T1], [H|T]) :-
  808    !,
  809    list_common_prefix(T0, T1, T).
  810list_common_prefix(_, _, []).
  811
  812
  813
  814%!  list_alternatives(+Alternatives)
  815%
  816%   List possible completions at the current point.
  817%
  818%   @tbd currently ignores the Comment in Text-Comment alternatives.
  819
  820list_alternatives(Alternatives) :-
  821    maplist(atomic, Alternatives),
  822    !,
  823    length(Alternatives, Count),
  824    maplist(atom_length, Alternatives, Lengths),
  825    max_list(Lengths, Max),
  826    tty_size(_, Cols),
  827    ColW is Max+2,
  828    Columns is max(1, Cols // ColW),
  829    RowCount is (Count+Columns-1)//Columns,
  830    length(Rows, RowCount),
  831    to_matrix(Alternatives, Rows, Rows),
  832    (   RowCount > 11
  833    ->  length(First, 10),
  834        Skipped is RowCount - 10,
  835        append(First, _, Rows),
  836        maplist(write_row(ColW), First),
  837        format(user_error, '... skipped ~D rows~n', [Skipped])
  838    ;   maplist(write_row(ColW), Rows)
  839    ).
  840list_alternatives(Alternatives) :-
  841    maplist(complete_text, Alternatives, AltText),
  842    list_alternatives(AltText).
  843
  844to_matrix([], _, Rows) :-
  845    !,
  846    maplist(close_list, Rows).
  847to_matrix([H|T], [RH|RT], Rows) :-
  848    !,
  849    add_list(RH, H),
  850    to_matrix(T, RT, Rows).
  851to_matrix(List, [], Rows) :-
  852    to_matrix(List, Rows, Rows).
  853
  854add_list(Var, Elem) :-
  855    var(Var), !,
  856    Var = [Elem|_].
  857add_list([_|T], Elem) :-
  858    add_list(T, Elem).
  859
  860close_list(List) :-
  861    append(List, [], _),
  862    !.
  863
  864write_row(ColW, Row) :-
  865    length(Row, Columns),
  866    make_format(Columns, ColW, Format),
  867    format(user_error, Format, Row).
  868
  869make_format(N, ColW, Format) :-
  870    format(string(PerCol), '~~w~~t~~~d+', [ColW]),
  871    Front is N - 1,
  872    length(LF, Front),
  873    maplist(=(PerCol), LF),
  874    append(LF, ['~w~n'], Parts),
  875    atomics_to_string(Parts, Format).
  876
  877
  878		 /*******************************
  879		 *             SEARCH		*
  880		 *******************************/
  881
  882%!  isearch_history(+Input, +Char, -Continue) is det.
  883%
  884%   Incremental search through the history.  The behavior is based
  885%   on GNU readline.
  886
  887isearch_history(Input, _Char, Continue) :-
  888    el_line(Input, line(Before, After)),
  889    string_concat(Before, After, Current),
  890    string_length(Current, Len),
  891    search_print('', "", Current),
  892    search(Input, "", Current, 1, Line),
  893    el_deletestr(Input, Len),
  894    el_insertstr(Input, Line),
  895    Continue = redisplay.
  896
  897search(Input, For, Current, Nth, Line) :-
  898    el_getc(Input, Next),
  899    Next \== -1,
  900    !,
  901    search(Next, Input, For, Current, Nth, Line).
  902search(_Input, _For, _Current, _Nth, "").
  903
  904search(7, _Input, _, Current, _, Current) :-    % C-g: abort
  905    !,
  906    clear_line.
  907search(18, Input, For, Current, Nth, Line) :-   % C-r: search previous
  908    !,
  909    N2 is Nth+1,
  910    search_(Input, For, Current, N2, Line).
  911search(19, Input, For, Current, Nth, Line) :-   % C-s: search next
  912    !,
  913    N2 is max(1,Nth-1),
  914    search_(Input, For, Current, N2, Line).
  915search(127, Input, For, Current, _Nth, Line) :- % DEL/BS: shorten search
  916    sub_string(For, 0, _, 1, For1),
  917    !,
  918    search_(Input, For1, Current, 1, Line).
  919search(Char, Input, For, Current, Nth, Line) :-
  920    code_type(Char, cntrl),
  921    !,
  922    search_end(Input, For, Current, Nth, Line),
  923    el_push(Input, Char).
  924search(Char, Input, For, Current, _Nth, Line) :-
  925    format(string(For1), '~w~c', [For,Char]),
  926    search_(Input, For1, Current, 1, Line).
  927
  928search_(Input, For1, Current, Nth, Line) :-
  929    (   find_in_history(Input, For1, Current, Nth, Candidate)
  930    ->  search_print('', For1, Candidate)
  931    ;   search_print('failed ', For1, Current)
  932    ),
  933    search(Input, For1, Current, Nth, Line).
  934
  935search_end(Input, For, Current, Nth, Line) :-
  936    (   find_in_history(Input, For, Current, Nth, Line)
  937    ->  true
  938    ;   Line = Current
  939    ),
  940    clear_line.
  941
  942find_in_history(_, "", Current, _, Current) :-
  943    !.
  944find_in_history(Input, For, _, Nth, Line) :-
  945    el_history_events(Input, History),
  946    call_nth(( member(_N-Line, History),
  947               sub_string(Line, _, _, _, For)
  948             ),
  949             Nth),
  950    !.
  951
  952%   Note that we reset the attributes  (see   plain_style/0):  the search
  953%   prompt is not the line being edited and, more importantly, `\e[0K'
  954%   erases using the current background colour.
  955
  956search_print(State, Search, Current) :-
  957    format(user_error, '\r\e[0m(~wreverse-i-search)`~w\': ~w\e[0K',
  958           [State, Search, Current]).
  959
  960clear_line :-
  961    format(user_error, '\r\e[0m\e[0K', []).
  962
  963
  964                /*******************************
  965                *         PASTE QUOTED         *
  966                *******************************/
  967
  968:- meta_predicate
  969    with_quote_flags(+,+,0).  970
  971add_paste_quoted(Input) :-
  972    current_prolog_flag(gui, true),
  973    !,
  974    el_addfn(Input, paste_quoted, 'Paste as quoted atom', paste_quoted),
  975    el_bind(Input, ["^Y",  paste_quoted]).
  976add_paste_quoted(_).
  977
  978%!  paste_quoted(+Input, +Char, -Continue) is det.
  979%
  980%   Paste the selection as quoted Prolog value.   The quoting type
  981%   depends on the quote before the caret.  If there is no quote
  982%   before the caret we paste as an atom.
  983
  984paste_quoted(Input, _Char, Continue) :-
  985    clipboard_content(String),
  986    quote_text(Input, String, Quoted),
  987    el_insertstr(Input, Quoted),
  988    Continue = refresh.
  989
  990quote_text(Input, String, Value) :-
  991    el_line(Input, line(Before, _After)),
  992    (   sub_string(Before, _, 1, 0, Quote)
  993    ->  true
  994    ;   Quote = "'"
  995    ),
  996    quote_text(Input, Quote, String, Value).
  997
  998quote_text(Input, "'", Text, Quoted) =>
  999    format(string(Quoted), '~q', [Text]),
 1000    el_deletestr(Input, 1).
 1001quote_text(Input, "\"", Text, Quoted) =>
 1002    atom_string(Text, String),
 1003    with_quote_flags(
 1004        string, codes,
 1005        format(string(Quoted), '~q', [String])),
 1006    el_deletestr(Input, 1).
 1007quote_text(Input, "`", Text, Quoted) =>
 1008    atom_string(Text, String),
 1009    with_quote_flags(
 1010        codes, string,
 1011        format(string(Quoted), '~q', [String])),
 1012    el_deletestr(Input, 1).
 1013quote_text(_, _, Text, Quoted) =>
 1014    format(string(Quoted), '~q', [Text]).
 1015
 1016with_quote_flags(Double, Back, Goal) :-
 1017    setup_call_cleanup(
 1018        ( push_prolog_flag(double_quotes, Double),
 1019          push_prolog_flag(back_quotes, Back) ),
 1020        Goal,
 1021        ( pop_prolog_flag(back_quotes),
 1022          pop_prolog_flag(double_quotes) )).
 1023
 1024clipboard_content(Text) :-
 1025    current_prolog_flag(gui, true),
 1026    !,
 1027    autoload_call(in_pce_thread_sync(
 1028                      autoload_call(
 1029                          get(@(display), paste, primary, string(Text))))).
 1030clipboard_content("").
 1031
 1032
 1033                /*******************************
 1034                *       BRACKETED PASTE        *
 1035                *******************************/
 1036
 1037%!  bracketed_paste(+Input, +Char, -Continue) is det.
 1038%
 1039%   Handler for the bracketed paste start sequence ESC[200~.  Reads
 1040%   characters until the end sequence ESC[201~ is received and inserts
 1041%   the collected text literally, bypassing per-character key dispatch.
 1042%
 1043%   The terminal is asked to enable bracketed paste mode (ESC[?2004h)
 1044%   from the C layer each time a prompt is issued.
 1045
 1046bracketed_paste(Input, _Char, Continue) :-
 1047    collect_paste(Input, [], RevCodes),
 1048    reverse(RevCodes, Codes),
 1049    string_codes(Text, Codes),
 1050    el_insertstr(Input, Text),
 1051    Continue = refresh.
 1052
 1053%!  collect_paste(+Input, +RevAcc, -RevResult) is det.
 1054%
 1055%   Read characters one at a time, accumulating them in reverse order.
 1056%   Stop when the reversed accumulator starts with the end sequence
 1057%   ESC[201~ (27,91,50,48,49,126) and return the reversed content that
 1058%   precedes it.
 1059
 1060collect_paste(Input, RevCodes, Result) :-
 1061    el_getc(Input, Char),
 1062    (   Char == -1                          % EOF / error
 1063    ->  Result = RevCodes
 1064    ;   paste_char(Char, Char1),
 1065        RevCodes1 = [Char1|RevCodes],
 1066        (   RevCodes1 = [0'~,0'1,0'0,0'2,0'[,0'\e|Rest]  % ESC[201~ reversed
 1067        ->  Result = Rest
 1068        ;   collect_paste(Input, RevCodes1, Result)
 1069        )
 1070    ).
 1071
 1072%!  paste_char(+Raw, -Char) is det.
 1073%
 1074%   Translate a raw character code from bracketed paste to the intended
 1075%   code.  The tty line discipline in edit mode has INLCR set, which maps
 1076%   LF (10) to CR (13) before libedit reads it.  We reverse that here so
 1077%   pasted newlines are inserted as actual newlines.
 1078
 1079paste_char(0'\r, 0'\n) :- !.               % CR → LF (INLCR maps \n to \r in edit mode)
 1080paste_char(C,   C).
 1081
 1082
 1083                /*******************************
 1084                *           MESSAGE            *
 1085                *******************************/
 1086
 1087:- multifile prolog:error_message//1. 1088
 1089prolog:error_message(editline(Msg)) -->
 1090    [ 'editline: ~s'-[Msg] ]