Mercurial > core / lisp/ffi/readline/readline.lisp
changeset 658: |
804b5ee20a46 |
parent: |
386d51cf61ca
|
child: |
da507f0274b3 |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Thu, 19 Sep 2024 23:23:02 -0400 |
permissions: |
-rw-r--r-- |
description: |
zstd completed (besides zdict), working on readline |
1 ;;; readline/readline.lisp --- Readline Alien Routines 3 ;; This implementation is based on Vindarel's cl-readline: https://github.com/vindarel/cl-readline 8 (define-alien-enum (rl-completion-type int :test eq) 10 :display-and-perform 33 13 :not-list-cmn-prefix 64) 15 (define-alien-type rl-history-entry (struct rl-history-entry 19 (macrolet ((def-rl-var (name var type) 20 `(define-alien-variable (,name ,var) ,type))) 21 (def-rl-var "rl_line_buffer" *line-buffer* c-string) 22 (def-rl-var "rl_point" *point* int) 23 (def-rl-var "rl_end" *end* int) 24 (def-rl-var "rl_mark" *mark* int) 25 (def-rl-var "rl_done" *point* boolean) 26 (def-rl-var "rl_num_chars_to_read" *num-chars-to-read* int) 27 (def-rl-var "rl_pending_input" *pending-input* int) 28 (def-rl-var "rl_dispatching" *point* boolean) 29 (def-rl-var "rl_erase_empty_line" *erase-empty-line* boolean) 30 (def-rl-var "rl_prompt" *prompt* c-string) 31 (def-rl-var "rl_display_prompt" *display-prompt* c-string) 32 (def-rl-var "rl_already_prompted" *already-prompted* boolean) 33 (def-rl-var "rl_library_version" *library-version* c-string) 34 (def-rl-var "rl_readline_version" *readline-version* int) 35 (def-rl-var "rl_gnu_readline_p" *gnu-readline-p* boolean) 36 (def-rl-var "rl_terminal_name" *terminal-name* c-string) 37 (def-rl-var "rl_readline_name" *readline-name* c-string) 38 (def-rl-var "rl_instream" *instream* (* t)) 39 (def-rl-var "rl_outstream" *outstream* (* t)) 40 (def-rl-var "rl_prefer_env_winsize" *prefer-env-winsize* boolean) 41 (def-rl-var "rl_last_func" *last-func* (* t)) 42 (def-rl-var "rl_startup_hook" *startup-hook* (* t)) 43 (def-rl-var "rl_pre_input_hook" *pre-input-hook* (* t)) 44 (def-rl-var "rl_event_hook" *event-hook* (* t)) 45 (def-rl-var "rl_getc_function" *getc-function* (* t)) 46 (def-rl-var "rl_signal_event_hook" *signal-event-hook* (* t)) 47 (def-rl-var "rl_input-available_hook" *input-available-hook* (* t)) 48 (def-rl-var "rl_redisplay_function" *redisplay-function* (* t)) 49 (def-rl-var "rl_prep_term_function" *prep-term-function* (* t)) 50 (def-rl-var "rl_deprep_term_function" *deprep-term-function* (* t)) 51 (def-rl-var "rl_executing_keymap" *executing-keymap* (* t)) 52 (def-rl-var "rl_binding_keymap" *binding-keymap* (* t)) 53 (def-rl-var "rl_executing_macro" *executing-macro* c-string) 54 (def-rl-var "rl_executing_key" *executing-key* char) 55 (def-rl-var "rl_executing_keyseq" *executing-keyseq* c-string) 56 (def-rl-var "rl_key_sequence_length" *key-sequence-length* int) 57 (def-rl-var "rl_readline_state" *readline-state* int) 58 (def-rl-var "rl_explicit_arg" *explicit-arg* boolean) 59 (def-rl-var "rl_numeric_arg" *numeric-arg* int) 60 (def-rl-var "rl_editing_mode" *editing-mode* int) 61 (def-rl-var "rl_catch_sigwinch" *catch-sigwinch* boolean) 62 (def-rl-var "rl_change_environment" *change-environment* boolean) 63 (def-rl-var "rl_attempted_completion_function" *attempted-completion-function* (* t)) 64 (def-rl-var "rl_completion_display_matches_hook" *completion-display-matches-hook* (* t)) 65 (def-rl-var "rl_basic_word_break_characters" *basic-word-break-characters* c-string) 66 (def-rl-var "rl_completer_word_break_character" *completer-word-break-characters* c-string) 67 (def-rl-var "rl_completion_query_items" *completer-query-items* int) 68 (def-rl-var "rl_completion_append_character" *completion-append-character* char) 69 (def-rl-var "rl_ignore_completion_duplicates" *ignore-completion-duplicates* boolean) 70 (def-rl-var "rl_attempted_completion_over" *attempted-completion-over* boolean) 71 (def-rl-var "rl_sort_completion_matches" *sort-completion-matches* boolean) 72 (def-rl-var "rl_completion_type" *completion-type* rl-completion-type) 73 (def-rl-var "rl_inhibit_completion" *inhibit-completion* boolean) 74 (def-rl-var "history_base" *history-base* int) 75 (def-rl-var "history_length" *history-length* int)) 78 '(:initializing ; 0x0000001 initializing 79 :initialized ; 0x0000002 initialization done 80 :termprepped ; 0x0000004 terminal is prepped 81 :readcmd ; 0x0000008 reading a command key 82 :metanext ; 0x0000010 reading input after ESC 83 :dispatching ; 0x0000020 dispatching to a command 84 :moreinput ; 0x0000040 reading more input in a command function 85 :isearch ; 0x0000080 doing incremental search 86 :nsearch ; 0x0000100 doing non-incremental search 87 :search ; 0x0000200 doing a history search 88 :numericarg ; 0x0000400 reading numeric argument 89 :macroinput ; 0x0000800 getting input from a macro 90 :macrodef ; 0x0001000 defining keyboard macro 91 :overwrite ; 0x0002000 overwrite mode 92 :completing ; 0x0004000 doing completion 93 :sighandler ; 0x0008000 in readline sighandler 94 :undoing ; 0x0010000 doing an undo 95 :inputpending ; 0x0020000 rl_execute_next called 96 :ttycsaved ; 0x0040000 tty special chars saved 97 :callback ; 0x0080000 using the callback interface 98 :vimotion ; 0x0100000 reading vi motion arg 99 :multikey ; 0x0200000 reading multiple-key command 100 :vicmdonce ; 0x0400000 entered vi command mode at least once 101 :redisplaying ; 0x0800000 updating terminal display 102 :done) ; 0x1000000 done; accepted line 103 "Possible state values for `+readline-state+'.") 105 (defvar +c-buffer-size+ 256 106 "How many bytes to allocate per Lisp string when converting list of 107 Lisp strings into array of C strings.") 109 (defun decode-version (version) 110 "Transform VERSION into two values representing major and minor numbers of 111 Readline library version." 112 (values (ldb (byte 8 8) version) 113 (ldb (byte 8 0) version))) 115 ;; (defun decode-state (state) 116 ;; "Transform Readline state STATE into list of keywords. See `+states+' for 117 ;; list of components that can appear in result list." 118 ;; (mapcan (lambda (index keyword) 119 ;; (when (logbitp index state) 121 ;; (iota (length +states+)) 124 (defmacro produce-callback (function return-type &optional func-arg-list) 125 "Return pointer to callback that calls FUNCTION. RETURN-TYPE specifies 126 return type of the function and FUNC-ARG-LIST is list of argument types (it 127 can be ommited if FUNCTION doesn't take any arguments)." 128 (let ((gensymed-list (mapcar (lambda (x) (list (gensym) x)) 130 (std:with-gensyms (temp) 133 (define-alien-callable ,temp ,return-type ,gensymed-list 134 (funcall ,function ,@(mapcar #'car gensymed-list))) 135 (alien-callable-function ',temp)))))) 137 (defun produce-callback* (function return-type &optional func-arg-list) 138 "Variant of PRODUCE-CALLBACK that should hopefully be more portable. 139 This avoids using a GENSYM as the name of a callback, and is also funcallable." 140 (let ((gensymed-list (mapcar (lambda (x) (list (gensym) x)) 142 (std:with-gensyms (temp) 145 (eval `(define-alien-callable ,temp ,return-type ,gensymed-list 146 (funcall ,function ,@(mapcar #'car gensymed-list)))) 147 (alien-callable-function temp)))))) 150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 152 (defun recent-history-line-satisfies-p (predicate) 153 "Check if the most recent history line satisfies given predicate 154 PREDICATE. Return T if there is no history saved." 155 (if (zerop *history-length*) 157 (with-alien ((s rl-history-entry)) 159 ;; TODO 2024-09-19: does SBCL know how to conver this to a lisp string automatically? 162 ;; (alien-funcall "history_get" 164 ;; (1- (+ *history-base* 165 ;; *history-length*))) 169 (define-alien-routine "readline" (* t) (prompt c-string)) 170 (define-alien-routine "add_history" void (line c-string)) 179 "Get a line from user with editing. PROMPT, if supplied, is printed before 180 reading of input. Non-NIL value of ALREADY-PROMPTED will tell Readline that 181 the application has printed prompt already. However, PROMPT must be supplied 182 in this case too, so redisplay functions can update the display properly. If 183 NUM-CHARS argument is a positive number, Readline will return after 184 accepting that many characters. If ERASE-EMPTY-LINE is not NIL, `readline' 185 will completely erase the current line, including any prompt, any time a 186 newline is typed as the only character on an otherwise-empty line. The 187 cursor is moved to the beginning of the newly-blank line. Supplying 188 ADD-HISTORY tells Readline that user's input should be added to 189 history. However, blank lines don't get into history anyway. NOVELTY-CHECK, 190 if given, must be a predicate that takes two strings: the actual line and 191 the most recent history line. Only when the predicate evaluates to non-NIL 192 value new line will be added to the history. Return value on success is the 193 actual string and NIL on failure." 194 (setf *already-prompted* already-prompted 195 *num-chars-to-read* (or num-chars 0) 196 *erase-empty-line* erase-empty-line) 197 (let* ((prompt (if prompt (string prompt) "")) 198 (ptr (readline prompt))) 202 (when (and add-history 203 (not (sequence:emptyp str)) 204 (or (not novelty-check) 205 (recent-history-line-satisfies-p 206 (std:curry novelty-check str)))) 211 ;; (defun ensure-initialization () 212 ;; "Make sure that Readline is initialized. If it's not initialized yet, 214 ;; (unless (find :initialized *readline-state*) 217 ;; (defmacro with-possible-redirection (filename append &body body) 218 ;; "If FILENAME is not NIL, try to create C file named FILENAME, 219 ;; temporarily reassign `*outstream*' to pointer to this file, perform BODY, 220 ;; then close the file and assign `*outstream*' the old value. If APPEND is not 221 ;; NIL, output will be appended to the file. Returns NIL on success and T on 223 ;; (std:with-gensyms (temp-outstream file-pointer body-fnc) 224 ;; `(flet ((,body-fnc () 227 ;; (let ((,temp-outstream *outstream*) 228 ;; (,file-pointer (foreign-funcall "fopen" 230 ;; :string (if ,append "a" "w") 232 ;; (if (null-alien ,file-pointer) 236 ;; (setf *outstream* ,file-pointer) 238 ;; (foreign-funcall "fclose" 239 ;; :pointer ,file-pointer 241 ;; (setf *outstream* ,temp-outstream))))