Common Lisp: Add basic completion for toplevel symbols to the REPL
authorIqbal Ansari <iqbalansari02@yahoo.com>
Tue, 22 Aug 2017 12:58:50 +0000 (18:28 +0530)
committerIqbal Ansari <iqbalansari02@yahoo.com>
Sat, 26 Aug 2017 07:34:00 +0000 (13:04 +0530)
13 files changed:
common-lisp/src/env.lisp
common-lisp/src/step0_repl.lisp
common-lisp/src/step1_read_print.lisp
common-lisp/src/step2_eval.lisp
common-lisp/src/step3_env.lisp
common-lisp/src/step4_if_fn_do.lisp
common-lisp/src/step5_tco.lisp
common-lisp/src/step6_file.lisp
common-lisp/src/step7_quote.lisp
common-lisp/src/step8_macros.lisp
common-lisp/src/step9_try.lisp
common-lisp/src/stepA_mal.lisp
common-lisp/src/utils.lisp

index 0a2ff05..771b72c 100644 (file)
@@ -5,7 +5,8 @@
            :create-mal-env
            :get-env
            :find-env
-           :set-env))
+           :set-env
+           :mal-env-bindings))
 
 (in-package :env)
 
index 79d1800..17ba161 100644 (file)
@@ -2,6 +2,8 @@
   (:use :common-lisp)
   (:import-from :uiop
                 :getenv)
+  (:import-from :cl-readline
+                :readline)
   (:export :main))
 
 (in-package :mal)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
index 1c0bd24..871ffbd 100644 (file)
@@ -4,6 +4,8 @@
         :printer)
   (:import-from :utils
                 :getenv)
+  (:import-from :cl-readline
+                :readline)
   (:export :main))
 
 (in-package :mal)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
index ecfaedb..13723d5 100644 (file)
@@ -4,11 +4,15 @@
         :env
         :reader
         :printer)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of *repl-env*
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
   (loop do (let ((line (mal-readline "user> ")))
              (if line (mal-writeline (rep line)) (return)))))
 
index ff34662..2aa0913 100644 (file)
@@ -5,11 +5,15 @@
         :reader
         :printer
         :genhash)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
   (loop do (let ((line (mal-readline "user> ")))
              (if line (mal-writeline (rep line)) (return)))))
 
index 99fca65..0d865a8 100644 (file)
@@ -5,12 +5,16 @@
         :reader
         :printer
         :core)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
                 :listify
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
   (loop do (let ((line (mal-readline "user> ")))
              (if line (mal-writeline (rep line)) (return)))))
 
index 18150d6..b0d9ece 100644 (file)
@@ -5,12 +5,16 @@
         :reader
         :printer
         :core)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
                 :listify
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
   (loop do (let ((line (mal-readline "user> ")))
              (if line (mal-writeline (rep line)) (return)))))
 
index 152a9af..361305c 100644 (file)
@@ -5,12 +5,16 @@
         :reader
         :printer
         :core)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
                 :listify
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
   (let ((args (if argv-provided-p
                   argv
                   (cdr (utils:raw-command-line-arguments)))))
index 956b0bc..ea7d53d 100644 (file)
@@ -5,12 +5,16 @@
         :reader
         :printer
         :core)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
                 :listify
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
   (let ((args (if argv-provided-p
                   argv
                   (cdr (utils:raw-command-line-arguments)))))
index 0c2c214..cbf47eb 100644 (file)
@@ -5,12 +5,16 @@
         :reader
         :printer
         :core)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
                 :listify
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
   (let ((args (if argv-provided-p
                   argv
                   (cdr (utils:raw-command-line-arguments)))))
index bc36001..18e0060 100644 (file)
@@ -5,12 +5,16 @@
         :reader
         :printer
         :core)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
                 :listify
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
+
   (let ((args (if argv-provided-p
                   argv
                   (cdr (utils:raw-command-line-arguments)))))
index 82c6c7f..046dd9f 100644 (file)
@@ -5,12 +5,16 @@
         :reader
         :printer
         :core)
+  (:import-from :cl-readline
+                :readline
+                :register-function)
   (:import-from :genhash
                 :hashref
                 :hashmap)
   (:import-from :utils
                 :listify
-                :getenv)
+                :getenv
+                :common-prefix)
   (:export :main))
 
 (in-package :mal)
 
 (defvar *use-readline-p* nil)
 
+(defun complete-toplevel-symbols (input &rest ignored)
+  (declare (ignorable ignored))
+
+  (let (candidates)
+    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)
+       when (let ((pos (search input key))) (and pos (zerop pos)))
+       do (push key candidates))
+
+    (if (= 1 (length candidates))
+        (cons (car candidates) candidates)
+        (cons (apply #'utils:common-prefix candidates) candidates))))
+
 (defun raw-input (prompt)
   (format *standard-output* prompt)
   (force-output *standard-output*)
 
 (defun mal-readline (prompt)
   (if *use-readline-p*
-      (rl:readline :prompt prompt
-                            :add-history t
-                            :novelty-check (lambda (old new)
-                                             (not (string= old new))))
+      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)
       (raw-input prompt)))
 
 (defun mal-writeline (string)
                 *standard-output* (ext:make-stream :output :buffered t)
                 *error-output* (ext:make-stream :error :buffered t))
 
+  ;; CCL fails with a error while registering completion function
+  ;; See also https://github.com/mrkkrp/cl-readline/issues/5
+  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)
+
   (let ((args (if argv-provided-p
                   argv
                   (cdr (utils:raw-command-line-arguments)))))
index 8845f20..95eadf5 100644 (file)
@@ -5,7 +5,8 @@
            :getenv
            :read-file-string
            :raw-command-line-arguments
-           :listify))
+           :listify
+           :common-prefix))
 
 (in-package :utils)
 
@@ -27,3 +28,15 @@ is replaced with replacement."
 (defun listify (sequence)
   "Convert a sequence to a list"
   (map 'list #'identity sequence))
+
+(defun common-prefix (&rest strings)
+  (if (not strings)
+      ""
+      (let* ((char-lists (mapcar (lambda (string) (coerce string 'list)) strings))
+             (char-tuples (apply #'mapcar #'list char-lists))
+             (count 0))
+        (loop for char-tuple in char-tuples
+           while (every (lambda (char) (equal char (car char-tuple))) char-tuple)
+           do (incf count))
+
+        (subseq (car strings) 0 count))))