{"id":18081912,"url":"https://github.com/vindarel/replic","last_synced_at":"2025-06-27T00:37:13.085Z","repository":{"id":67525223,"uuid":"119291973","full_name":"vindarel/replic","owner":"vindarel","description":"Build a terminal application in no time from an existing library.","archived":false,"fork":false,"pushed_at":"2023-01-04T17:14:43.000Z","size":133,"stargazers_count":31,"open_issues_count":2,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-05-19T05:36:08.366Z","etag":null,"topics":["common-lisp","readline"],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vindarel.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-01-28T19:48:57.000Z","updated_at":"2023-11-09T05:00:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"d55d6877-9722-42df-8193-c729042c2429","html_url":"https://github.com/vindarel/replic","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/vindarel/replic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Freplic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Freplic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Freplic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Freplic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vindarel","download_url":"https://codeload.github.com/vindarel/replic/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Freplic/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262166343,"owners_count":23269027,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["common-lisp","readline"],"created_at":"2024-10-31T13:17:12.137Z","updated_at":"2025-06-27T00:37:13.055Z","avatar_url":"https://github.com/vindarel.png","language":"Common Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"[[http://quickdocs.org/replic][file:http://quickdocs.org/badge/replic.svg]]\n\n* Replic\n\nBuilding a  readline application is  cool, but readline gives  you the\nbasics  and you  must still  build  a REPL  around it:  loop and  read\ncommands, catch a =C-c=, a =C-d=,  ask confirmation to quit, print the\ngeneral help, the help of a command, setup the completion of commands, the\ncompletion of their arguments, load an init file,\ncolorize output,...  =replic= does this for you.\n\nYou can use =replic= as a *ready-to-use executable* or as a *library*.\n\nUsing  the executable,  you can  define functions  and variables  in\n=~/.replic.lisp=,  give  them  completion  candidates,  and  use  them\nstraight away on the replic command line.\n\nWith the library you can automatically  build a\nREPL and turn functions you already have into commands in the readline prompt,\nwith the process described below.\n\nThere are a few examples below, in =src/examples.lisp= and expect more to come.\n\nThis  is an  attempt at  generalizing what  I did  several times  with\n=cl-readline=.\n\nWhat this is *not*:\n\n- this  is  not a  Lisp  REPL.  See [[https://github.com/koji-kojiro/cl-repl][cl-repl]]  for  that  (itself not  a\n  replacement for Slime ;) )\n- this is not a shell. See [[https://github.com/bradleyjensen/shcl][shcl]] or [[https://github.com/nibbula/lish][Lish]].\n\nExample applications built on replic:\n\n- [[https://github.com/vindarel/cl-torrents][cl-torrents]]\n- [[https://github.com/OpenBookStore/openbookstore][OpenBookStore]]\n- here's [[https://github.com/vindarel/lyrics/commit/0a75ae78a172b2aba427d2a5911d8034174c43fd][a commit]] that added replic capabilities to a library. The\n  library was to be used on the Lisp REPL. With the creation of an\n  executable and 5 lines of =replic= setup, we can use it in the\n  terminal, as a readline-based application with nice autocompletion.\n\n** Installation\n\nYou can  [[https://gitlab.com/vindarel/replic/-/jobs][download  the  executable]]  (a 14MB  zipped  download,  a  60Mo\nGNU/Linux  x64 self-contained  binary,  instant start-up  !), make  it\nexecutable (=chmod +x replic=) and run it: =./replic=,\n\nor build it yourself.\n\n- the library is on Quicklisp and on [[http://ultralisp.org/][Ultralisp]]:\n\n: (ql:quickload \"replic\")\n\nor clone this repo into =~/quicklisp/local-projects/=,\n\nthen build the executable with =make build=.\n\nRun it:\n\n: ./replic -h\n\n#+BEGIN_SRC text\nAvailable options:\n  -h, --help               Print this help and exit.\n  -q, --quiet              Do not load the init file.\n  -l, --load ARG           Load the given file.\n#+END_SRC\n\n: ./replic\n\nand see the available commands:\n\n: replic \u003e help\n: replic \u003e help help\n\nNow add commands in your lisp init file (see next section) or build an\napplication with it (see the Developer section).\n\n\n** User: the executable and the init file\n\nGiven the example =~/.replic.lisp= below, you can\ntry =hello \u003cname\u003e= (completion for =hello=) and =goodbye \u003cname\u003e=,\nwhere \u003cname\u003e can be completed from what was given to =hello=.\n\n#+BEGIN_SRC lisp\n(in-package :replic.user)\n\n(defparameter *names* '()\n  \"List of names (string) given to `hello`. Will be autocompleted by `goodbye`.\")\n\n(defun hello (name)\n  \"Takes only one argument. Adds the given name to the global\n  `*names*` variable, used to complete arguments of `goodbye`.\n  \"\n  (format t \"hello ~a~\u0026\" name)\n  (push name *names*))\n\n(defun goodbye (name)\n  \"Says goodbye to name, where `name` should be completed from what was given to `hello`.\"\n  (format t \"goodbye ~a~\u0026\" name))\n\n(replic.completion:add-completion \"goodbye\" (lambda () *names*))\n\n(export '(hello goodbye))\n#+END_SRC\n\n#+html: \u003cp align=\"center\"\u003e\u003cimg src=\"basic-hello-completion.png\"/\u003e\u003c/p\u003e\n\nNote that only the =export='ed  functions and parameters will be taken\ninto account.\n\nSee more examples in the =src/examples.lisp= file of this repository.\n\n\n\n*** Define a default completion function for a command's arguments\n\nFirst write a function or a variable and =export= it. It becomes a command\nin the command line interface.\n\nYou can tell a command to complete its arguments against a given\nvariable or function:\n\n#+BEGIN_SRC lisp\n(replic.completion:add-completion \"goodbye\" (lambda () *names*))\n;; or\n(replic.completion:add-completion \"goodbye\" #'my-function)\n#+END_SRC\n\nNow everytime you type =goodbye fooTAB=, the lambda function is run and\nyou get completion candidates that start with \"foo\".\n\nThe functions must return a list of strings.\n\nWhen you have many functions whose arguments should be completed similarly,\nyou can set a default completion function:\n\n#+BEGIN_SRC lisp\n(setf replic.completion:*default-command-completion* #'my-function)\n#+END_SRC\n\n*** A different completion function for each argument\n\nEach parameter of a command can be completed with its own method.\n\nLet's define a command =say= that wants first a greeting message, and\nthen a name:\n\n#+BEGIN_SRC lisp\n(defun say (verb name)\n  (format t \"~a, ~a !~\u0026\" verb name))\n#+END_SRC\n\nWe can provide the completion functions in the same order as the arguments:\n\n#+BEGIN_SRC lisp\n(replic.completion:add-completion \"say\"\n                                  (list \"greetings\" \"\\\"nice to see you\\\"\")\n                                  (lambda () *names*))\n#+end_src\n\nNow if you type =say TAB= you get the two greeting choices. After you\npick one and press TAB again, you get the names that were given to\n=hello=.\n\n\n*** Built-in commands\n\nYou get a built-in =help= command that shows the documentation of\nfunctions and variables:\n\n#+BEGIN_SRC text\nreplic \u003e help\n\nAvailable commands\n==================\nhelp       ... Print the help of all available commands.\nreload     ... NIL\nset        ... Change a variable, see its value, or see all variables.\nquit       --- Quit the application.\n\nAvailable variables\n===================\n*verbose*  ... If true, print debugging information during the program execution.\n#+END_SRC\n\nWrite a preamble and a postamble in =*help-preamble*= and =*help-postamble*=.\n\nYou can read the help of a specific command or variable (with completion):\n\n: help help\n\nThe   general    =help=   shows    the   first   paragraph    of   the\nfunctions/parameters  docstring, the  =help  \u003ccmd\u003e=  function is  more\ncomplete and shows all of it.\n\n*** Setting and seeing variables\n\n=set= can be used with zero, one or two arguments:\n\n: set\n\nshows all available variables,\n\n: set *variable*\n\nthis prints the value of this variable (use auto-completion),\n\n: set *variable* new-value\n\nand this sets a new value. \"yes\", \"true\" and \"t\" denote true.\n\nWe kept the \"earmuffs\" to denote variables.\n\n*** Configuration file\n\nReplic  reads  an  =init=-like  configuration  file.   It  searches  a\n=.replic.conf=  file  under  =~/.config/=   and  at  the  user's  home\ndirectory (=~/.replic.conf=).\n\nThese are the default parameters with their default values:\n\n#+BEGIN_SRC text\n[default]\nconfirm-exit = true\nverbose = false\nprompt = \u003e \nhistory = true\nwrite-history = true\n#+END_SRC\n\n\"true\", \"True\" and \"t\" are truthy and \"false\", \"False\" and \"nil\" are falsy.\n\nBy default, replic reads and sets the options of the =[default]= section.\n\nYou can have a section per program:\n\n#+BEGIN_SRC text\n[myprogram]\noption = val\n#+end_src\n\nOptions of config files are overriden by command line arguments.\n\n\n** Developer: using replic as a library with an existing system\n\n=replic= is in Quicklisp:\n\n: (ql:quickload \"replic\")\n\nFollow the documentation below, and see example applications on [[https://github.com/vindarel/replic/wiki][the wiki]].\n\n*** Change the prompt\n\nYou can change the prompt. It defaults to \"\u003e \". It can contain ansi colors.\n\n#+BEGIN_SRC lisp\n(setf replic:*prompt* (cl-ansi-text:green \"replic \u003e \"))\n#+END_SRC\n\nYou can  add a  prefix to it,  for example one  that changes  with the\nstate of the application (current directory,...):\n\n#+BEGIN_SRC lisp\n(setf replic:*prompt-prefix* (format t \"(~a) \" \"sthg\"))\n#+END_SRC\n\nand concatenate the two with =(replic:prompt)=.\n\n*** [optional] Load base commands (help, reload, set)\n\nIf  you want  to  have  the base  commands  (=help=, =reload=,  =set=,\n=quit=), import the base package:\n\n#+BEGIN_SRC lisp\n(replic.completion:functions-to-commands :replic.base)\n#+END_SRC\n\n*** Create commands from a package's exported functions\n\nThis is  the core of the  library.\n\nCreate  the commands  you'll  find  at the  readline  prompt from  the\n/exported/ functions and variables of a given package:\n\n#+BEGIN_SRC lisp\n(replic.completion:functions-to-commands :my-package)\n#+END_SRC\n\nTo exclude functions, use the =:exclude= list:\n\n#+BEGIN_SRC lisp\n(replic.completion:functions-to-commands :my-package :exclude '(\"main\"))\n#+END_SRC\n\nFor more control, you can create a command from one given function:\n\n#+BEGIN_SRC lisp\n(replic.completion:add-command :function :package)\n;; add a variable:\n(replic.completion:add-variable :*variable* :package)\n#+END_SRC\n\nIt is generally a  good idea to have a package  for the lisp functions\nyou'll use at the repl, and another  package for the ones that must be\ncommands at the readline interface.\n\n*** [optional] Automatically printing the result of functions\n\nA lisp function from a library usually returns some result and doesn't\nnecessarily print it. If you  want =replic= to automatically print it,\nask it like so:\n\n#+BEGIN_SRC lisp\n(replic:autoprint-results-from :my-package :exclude '(\"exclude\" \"those-functions\"))\n#+END_SRC\n\n*** [optional] Overriding the default printing of results\n\nWe export a default =print-result  (result)= function, which is called\nfor   functions  whose   results   are   printed  automatically   (see\n=autoprint-results-from= and =autoprint-results-p=).\n\nA user can override this function in his/her lisp init file:\n\n#+BEGIN_SRC lisp\n;; ~/.replic.lisp\n(in-package :replic)\n\n(defun print-result (result)\n  (format t \"=== this new result is:~\u0026\")\n  (format t \"~a~\u0026\" result))\n#+END_SRC\n\nIn doing so, you should see a warning at startup:\n\n: WARNING: redefining REPLIC:PRINT-RESULT in DEFUN\n\n*** Load a config file\n\n=replic= searches  by default  for a  =.replic.conf= (see  above). The\nfunction  =replic.config:apply-config=  takes  as  paramaters:\n\n- (warn: the parameters order was changed on Jan, 2023) an optional\n  section parameter (string), defaults do the \"default\" section.\n- an optional package name, defaults to =:replic=.\n\nIf you do this:\n\n#+BEGIN_SRC lisp\n(replic.config:apply-config)\n#+END_SRC\n\nthis will read the settings inside the \"default\" section, and it will\napply them to the parameters of the =replic= package. So, you can\nchange \"confirm-exit\" and other built-in parameters (see below).\n\nIf you have a config file with another section:\n\n#+BEGIN_SRC text\n[default]\nconfirm-exit: true\n\n[my-app]\nconfirm-exit: false\n#+end_src\n\nYou would read the \"my-app\" section with:\n\n#+BEGIN_SRC lisp\n(replic.config:apply-config \"my-app\")\n#+END_SRC\n\nthis still tries to set =replic='s default parameters.\n\n: WARN: this is less tested.\n\nIf you do:\n\n#+BEGIN_SRC lisp\n(replic.config:apply-config \"my-app\" :my-app-package)\n#+END_SRC\n\nthis will try to set the parameters of your own application.\n\nAs an optional third parameter, you can give another file name:\n\n#+BEGIN_SRC lisp\n(replic.config:apply-config :mypackage \".mysoftware.conf\")\n#+END_SRC\n\n*** Default parameters\n\nThe exported variables from the package you give as argument can be\noverriden in the config file. For example, the =:replic= package\nexports:\n\n#+BEGIN_SRC text\n  (:export :main\n           :confirm\n           :repl\n           :help\n           :set\n           :reload\n           ;; settings        ;; \u003c--- exported *parameters* start here.\n           :*help-preamble*\n           :*help-postamble*\n           :*prompt*\n           :*prompt-prefix*\n           :*confirm-exit*\n           :*write-history*\n           :*verbose*))\n#+END_SRC\n\nso we can configure:\n\n#+BEGIN_SRC text\n[default]\nwrite-history = true\nverbose = true\nprompt = my silly prompt\n#+END_SRC\n\nand so on.\n\n\n*** Start the repl\n\nStart the repl:\n\n: (replic:repl)\n\nThat's it. You didn't have to write the REPL.\n\n# For illustration,  this is [[https://github.com/vindarel/cl-torrents/commit/ebc1dba5b168dd8432bff42c52a90e3bc6e19454#diff-1b0d53aa910ad7e1016f52042eb10b53L285][the  code we  saved]] by switching  to replic\n# (not counting the extra features).\n\n\n*** Settings\n\n    Variables that are  exported from a package on the  lisp side will\n    be automacitally available  for the config file and  read when the\n    application starts up.  The rule is that in the  config file, we don't\n    use earmuffs (=*foo*= -\u003e =foo=).  Lispers shall use a lispy config\n    file anyway.\n\n    The available variables are:\n\n - =*verbose*= (bool): if true, print debugging information during the program execution.\n\n - =*confirm-exit*=   (bool):  if   true   (the   default),  ask   for\n   confirmation when a user tries to exit the program with a =C-d= (EOF).\n\n - =*prompt*= (str):  the readline prompt.  Defaults to simply  =\u003e =. Can\n   contain ansi colours (use =cl-ansi-text:green= for example).\n\n - =*confirm-exit*=  (t  or  nil):  if  =t=  (the  default),  ask  for\n   confirmation when  the user tries to  exit the command line  with a\n   =C-d= (EOF).\n\n - =*write-history*=  (t or  nil):  if =t=  (the  default), write  the\n   commands to the app's history. (this needs =cl-readline= superior\n   to may, 2018)\n\n - =*help-preamble*=: text to display at the beginning of the help.\n\n - =*help-postamble*=: text to display last.\n\n*** Other helpers\n\n- print colored output from markdown or code with pygments:\n  =(format-markdown txt :lang \"md\")=. It outputs text for a console\n  display with ansi colours. Needs [[http://pygments.org][pygments]], or\n  does nothing.\n\n** Readline settings\n\nThe [[https://tiswww.case.edu/php/chet/readline/readline.html][GNU  Readline]] library provides  settings you might  take advantage\nof. We can set the settings in the [[https://tiswww.case.edu/php/chet/readline/readline.html#SEC9][readline init file]] (=~/.inputrc= by\ndefault, obeys the =INPUTRC= environment variable).\n\nFor example, you can change the *completion behavior*. This:\n\n: TAB: menu-complete\n\ninserts  the  first completion  candidate,  even  if there  are  many,\ninstead of showing the list of choices under the prompt.\n\nIf you prefer *vi mode*:\n\n: set editing-mode vi\n\netc. See readline's documentation.\n\n** Dev\n\nThis is a  generalization on  =cl-readline=. See  also the  simple [[https://github.com/vindarel/cl-readline-example][cl-readline\nexample]].  Once you've  built two even basic readline  apps you'll want\nto factorize the common parts.\n\nWe want to store a list of commands (functions, \"verbs\") and a list of\nvariables (the ones to use with \"set\").  We want to read them from any\nLisp file, hence we need to remember the package they come from. This\nmechanism is provided through an interface in =completion.lisp=.\n\nClone this repo in QL's local projects (=~/quicklisp/local-projects=).\n\nBuild the executable:\n\n: make build\n\nYou can  build the  binary with SBCL  core compression  (see commented\n.asd).  We  passed from  a 78 to  a 18MB binay,  but the  startup time\nincreased  from 0.04  to  0.26s,  which is  noticeable.  We don't  use\ncompression by default.\n\n*** Develop and test interactively into the console\n\nBy starting a swank server in the (real) Lisp repl we can compile code\nin our editor  and try instantly in the  terminal, without re-building\nthe executable. See this [[http://turtleware.eu/posts/cl-charms-crash-course.html][cl-charms  crash course]] for now. Some details\nneed fixing.\n\nSimpler  and still  handy, you  can add  =trace= statements  into your\n=.replic.lisp=, call the  =reload= command and see  the effects. Then,\n=(untrace)= and reload.\n\n** Changelog\n\ndev\n\n- 2023-01: updated reading a section. Fix loading the ini file for\n  another app.\n  To load the ini file, use:\n\n: (replic.config:apply-config)\n\nthis will read the \"default\" section and will check the parameters of\nthe =replic= package. Optionally, you can read another section of the\nini file:\n\n#+BEGIN_SRC text\n[default]\nconfirm-exit: true\n\n[my-app]\nconfirm-exit: false\n#+end_src\n\nand load it:\n\n: (replic.config:apply-config \"my-app\")\n\n- read an option from a given section.\n\nThis:\n: (replic.config:apply-config :myprogram)\nonly reads and sets options of the \"myprogram\" section and set the\nmatching variables found in =:myprogram=.\n\n- July, 2022: added =with-rl-completion=\n- v0.12, upcoming in Quicklisp of november\n  - added: a different completion for each command argument\n  - added: completion for sentences (strings in quotes).\n- Quicklisp, october 2019\n  - fixed 0.11  regression: arguments had  to always be  surrounded by\n    quotes (sept, 14th). We can  now write =command arg1 \"second arg\"=\n    as expected.\n- v0.11 (end of june, 2019)\n  - added a declarative way to automatically print a function's\n    result. The default function can be overriden by users (in order\n    to, for example, color the output).\n  - fixed: a quoted string on the readline prompt is now understood as\n    one single argument.\n** Resources\n\n\n- [[https://github.com/vindarel/cl-readline][cl-readline]]\n- [[https://github.com/vindarel/cl-readline-example][cl-readline-example]]\n\nLearning:\n\n- [[https://github.com/LispCookbook/cl-cookbook][Common Lisp Cookbook]]\n- https://github.com/CodyReichert/awesome-cl#learning-and-tutorials\n\nGetting started:\n\n- [[https://lispcookbook.github.io/cl-cookbook/editor-support.html][Common Lisp editors (Emacs, Portacle, Vim, Lem, Atom, Sublime), notebooks, REPLs]]\n- https://lispcookbook.github.io/cl-cookbook/getting-started.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvindarel%2Freplic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvindarel%2Freplic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvindarel%2Freplic/lists"}