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 465prologhistory(Input, enabled) :- 466 !, 467 el_wrapped(Input), 468 el_history(Input, getsize(Size)), 469 Size > 0. 470prologhistory(Input, add(Line)) :- 471 !, 472 el_add_history(Input, Line). 473prologhistory(Input, load(File)) :- 474 !, 475 compat_read_history(Input, File). 476prologhistory(Input, save(File)) :- 477 !, 478 el_write_history(Input, File). 479prologhistory(Input, events(Events)) :- 480 !, 481 el_history_events(Input, Events). 482prologhistory(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 739prologline_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 1089prologerror_message(editline(Msg)) --> 1090 [ 'editline: ~s'-[Msg] ]