{"id":13442638,"url":"https://github.com/vindarel/cl-ansi-term","last_synced_at":"2026-02-12T04:09:50.169Z","repository":{"id":25352951,"uuid":"28780600","full_name":"vindarel/cl-ansi-term","owner":"vindarel","description":"Colorized output on ANSI terminals, print tables of lists, plists, hash-tables, titles, banners and more","archived":false,"fork":false,"pushed_at":"2025-01-17T00:47:51.000Z","size":568,"stargazers_count":32,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-14T00:09:47.426Z","etag":null,"topics":["ansi-terminal","common-lisp"],"latest_commit_sha":null,"homepage":"https://vindarel.github.io/cl-ansi-term/","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vindarel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["vindarel"],"ko_fi":"vindarel"}},"created_at":"2015-01-04T17:20:28.000Z","updated_at":"2025-08-08T19:57:26.000Z","dependencies_parsed_at":"2024-01-31T12:05:01.980Z","dependency_job_id":"b47a3bca-cbc3-46d6-827e-339ba997fa4f","html_url":"https://github.com/vindarel/cl-ansi-term","commit_stats":null,"previous_names":["mrkkrp/cl-ansi-term"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/vindarel/cl-ansi-term","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Fcl-ansi-term","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Fcl-ansi-term/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Fcl-ansi-term/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Fcl-ansi-term/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vindarel","download_url":"https://codeload.github.com/vindarel/cl-ansi-term/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Fcl-ansi-term/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29357943,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T01:03:07.613Z","status":"online","status_checked_at":"2026-02-12T02:00:06.911Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["ansi-terminal","common-lisp"],"created_at":"2024-07-31T03:01:48.424Z","updated_at":"2026-02-12T04:09:50.130Z","avatar_url":"https://github.com/vindarel.png","language":"Common Lisp","funding_links":["https://github.com/sponsors/vindarel","https://ko-fi.com/vindarel"],"categories":["Common Lisp","Interfaces to other package managers"],"sub_categories":["Third-party APIs"],"readme":"# cl-ansi-term\n\n[![License GPL 3](https://img.shields.io/badge/license-GPL_3-green.svg)](http://www.gnu.org/licenses/gpl-3.0.txt)\n\u003c!-- [![Build Status](https://travis-ci.org/mrkkrp/cl-ansi-term.svg?branch=master)](https://travis-ci.org/mrkkrp/cl-ansi-term) --\u003e\n[![Quicklisp](http://quickdocs.org/badge/cl-ansi-term.svg)](http://quickdocs.org/cl-ansi-term/)\n\n`cl-ansi-term` allows to print various primitives on ANSI-compliant\nterminals. It also supports coloration and effects. `cl-ansi-term` is not\nsomething like `ncurses`, because it works with primitives that you can\noutput in your terminal, as well as redirect to a file. In other words, it's\nmore about good ol' textual interface than *emulation of GUI* in terminal.\nAn example of user interface created with `cl-ansi-term`\nis [here](https://github.com/lisp-mirror/shtookovina).\n\ncl-ansi-term uses the concept of **style sheet** to manage coloration of\noutput. Define styles, give them names, specify foreground colors,\nbackground colors, and effects for every style.\n\nThe library is capable of *detecting whether the output goes to a terminal or a\nfile*. If the latter case takes place, no escape sequences will be outputted.\nIt's also possible to disable all effects and coloration.\n\n`cl-ansi-term` can print the following things:\n\n* colorized text\n* horizontal lines: `hr`\n* progress bars (see also [progressons](https://github.com/vindarel/progressons))\n* unordered lists: `u-list`\n* ordered lists: `o-list`\n\nPrint tables:\n\n* tables: `table` and `vtable` (headers in the first column)\n  * accepts:\n    * lists of lists\n    * lists of hash-tables\n    * lists of property-lists (see the `:plist t` argument and the `plists-table` function)\n    * a single element\n\n```\n+---------+---------+---------+\n|A        |1        |1        |\n+---------+---------+---------+\n|B        |2        |2        |\n+---------+---------+---------+\n|C        |3        |3        |\n+---------+---------+---------+\n```\n\nHooks are applied before and after each printing primitive, see our documentation.\n\nand\n\n* vertical space: `vspace`\n* titles in banners: `banner`\n\n\n## Installation\n\nVia Quicklisp (recommended):\n\n```common-lisp\n(ql:quickload \"cl-ansi-term\")\n```\n\nThen you can use the `term` global nickname.\n\ncl-ansi-term depends on:\n\n* alexandria\n* serapeum (new as of Nov, 2024)\n* anaphora (will be removed)\n* cl-str\n\n\n## Documentation\n\nSee the `doc` directory. The documentation is also available online:\n\nhttps://vindarel.github.io/cl-ansi-term\n\nQuick snippets:\n\n### Print ordered and unordered lists: `o-list`, `u-list`\n\n~~~lisp\n(term:o-list '((:one one-a (:one-b :one-b-1 :one-b-2)) :two))\n1. ONE\n   1. ONE-A\n   2. ONE-B\n      1. ONE-B-1\n      2. ONE-B-2\n2. TWO\n\n(term:u-list '((:one one-a (:one-b :one-b-1 :one-b-2)) :two)\n              :bullet \\\"+-\\\")\n+ ONE\n  - ONE-A\n  - ONE-B\n    + ONE-B-1\n    + ONE-B-2\n+ TWO\n~~~\n\n### Print tables: `table` and `vtable`\n\n`table` and `vtable` accept:\n\n- a list of regular lists, composed of string-designators\n- a list of hash-tables\n- a list of property-lists\n  - use `:plist t` or `(setf *prefer-plists-in-tables* t)` to help the function distinguish between a regular list of lists and a list of property-lists.\n- a single hash-table or a single property list.\n\nThe KEYS and EXCLUDE arguments allow to filter in or filter out the rows to display.\n\n\n~~~lisp\n;; The first list, headers, as the first row:\n(term:table (list '(\"name\" \"age\")\n                  '(\"me\" \"7\")))\n+---------+---------+\n|name     |age      |\n+---------+---------+\n|me       |7        |\n+---------+---------+\n\n;; Headers as the first column:\n(term:vtable (list '(\"name\" \"age\")\n                         '(\"me\" \"7\")))\n+---------+---------+\n|name     |me       |\n+---------+---------+\n|age      |7        |\n+---------+---------+\n~~~\n\nPrint lists of hash-tables or lists of plists:\n\n~~~lisp\n(defparameter *my-plist* '(:a 1 :b 2 :c 3))\n(table (list *my-plist* *my-plist*) :plist t)\n+---------+---------+---------+\n|A        |B        |C        |\n+---------+---------+---------+\n|1        |2        |3        |\n+---------+---------+---------+\n|1        |2        |3        |\n+---------+---------+---------+\n\n(vtable (list *my-plist* *my-plist*) :plist t)\n+---------+---------+---------+\n|A        |1        |1        |\n+---------+---------+---------+\n|B        |2        |2        |\n+---------+---------+---------+\n|C        |3        |3        |\n+---------+---------+---------+\n~~~\n\nSee also `hts-table`, `hts-vtable`, `plists-table` and `plists-table`.\n\nYou can choose a set of keys (headers) or exclude some of them:\n\n- `:keys` is a list of keys to display\n- `:exclude` is a list of keys to not display.\n\n*(those can be a single element)*\n\n~~~lisp\n(table (list plist plist) :plist t :exclude :c)\n=\u003e\n+---------+---------+\n|A        |B        |\n+---------+---------+\n|1        |2        |\n+---------+---------+\n|1        |2        |\n+---------+---------+\n~~~\n\n#### No borders\n\nFeel free and ignore borders with `:border-style nil`:\n\n```\nA   B\n1   2\n1   2\n```\n\n\n#### Adapting the columns' widths\n\nThe `table` function adapts to the viewport.\n\nIt respects the terminal's width (`*TERMINAL-WIDTH*`).\n\nIf one or more columns take too much width (see `*LONG-COLUMN-WIDTH`*), they are truncated and\ntheir cells content will be shortened with the unicode \\\"…\\\".\n\n~~~lisp\n(term:table '((\"name\" \"age\" \"email\")\n              (\"me\" 7 \"some@blah\")\n              (\"this name is also very very very very long and too long\" 7 \"some@with-some-very-very-very-very-very-longer.email\")))\n~~~\n\n```\n+-------------------------------------+---+-------------------------------------+\n|name                                 |age|email                                |\n+-------------------------------------+---+-------------------------------------+\n|me                                   |7  |some@blah                            |\n+-------------------------------------+---+-------------------------------------+\n|this name is also very very very ver…|7  |some@with-some-very-very-very-very-v…|\n+-------------------------------------+---+-------------------------------------+\n```\n\nEach column can have the same width or a different\none. `:column-width` can be a number or a list of numbers:\n\n\n~~~lisp\n(term:table '((\"name\" \"age\" \"email\")\n              (\"me\" 7 \"some@blah\")\n              (\"me\" 7 \"some@with-some-longer.email\"))\n             :column-width '(10 4 20))\n+---------+---+-------------------+\n|name     |age|email              |\n+---------+---+-------------------+\n|me       |7  |some@blah          |\n+---------+---+-------------------+\n|me       |7  |some@with-some-lon…|\n+---------+---+-------------------+\n~~~\n\nAlso set `:max-column-width` (defaults to 80) to change the maximum width of any column.\n\n\n### Print horizontal lines: `hr`\n\n~~~\n(term:hr :filler \"=\")\n================================================================================\n\n(term:cat-print '(:abc :def :ghi) :align :center)\n                                   ABCDEFGHI\n~~~\n\n### Print vertical space: `vspace`\n\nUse `vspace`, arguments:\n\n* `space`: defaults to 3 newlines\n* `stream`: defaults to stdout.\n\n### Print a title in a banner: `banner`, `banner-fmt`\n\nPrint a title in between 2 horizontal lines, with vertical space before and after.\n\n```lisp\n(banner \"My title\" :space 1)\n\n--------------------------------------------------------------------------------\n     My title\n--------------------------------------------------------------------------------\n\n\n```\n\n`banner-fmt` accepts TITLE with FORMAT control strings and calls FORMAT on it with ARGS.\n\n         (banner-fmt \\\"file ~a\\\" \\\"test.csv\\\")\n\n`title` is a `banner` with no borders.\n\n\n### Print text centered, with margin, with stylesheets: `cat-print` and `print-styled`\n\nPrint text with CAT-PRINT, but apply CONTROL-STRING with the arguments from ARGS, where each tilde character of CONTROL-STRING is replaced with an argument.\n\nA special syntax can be used to apply styles.\n\nExample:\n\n~~~lisp\n(term:print-styled \"~ and ~\" :args '(\"foo\" \"bar\") :align :center)\n~~~\n\nis equivalent to\n\n~~~lisp\n(term:cat-print \"foo and bar\" :align :center)\n~~~\n\nAny region of text in CONTROL-STRING can be printed in a\nspecified style following this pattern:\n\n    [text](:name-of-style)\n\nwhere :name-of-style is a downcase keyword in the style sheet.\n\nThe style of the rest of the output defaults to BASE-STYLE.\n\n**ALIGN** can be :LEFT (default), :CENTER, and :RIGHT.\n\n**MARGIN** is the length of the left margin.\n\n**FILL-COLUMN** sets the column width:\n\n~~~lisp\n(term:print \"~ and ~\" :args '(\"foo\" \"bar\") :align :center :fill-column 10)\n                                    foo and\n                                      bar\n~~~\n\nOutput goes to STREAM.\"\n\n### Print a progress bar\n\n~~~lisp\n(term:progress-bar \"test\" 82)\ntest   ##################################################################################\n~~~\n\nOn an interactive terminal, next calls erase the progress bar to print it again and have an effect of… progress.\n\nThe progress bar respects styles with BAR-STYLE, LABEL-STYLE and NUM-STYLE.\n\nSee also [progressons](https://github.com/vindarel/progressons).\n\n\n### Stylesheets and colorized text\n\nStart by defining your stylesheet.\n\n~~~lisp\n(term:update-style-sheet\n '((:header :cyan   :underline)\n   (:mark   :red    :reverse)\n   (:term   :yellow :bold)))\n~~~\n\n`:header`, `:mark` and `:term` are your own vocabulary. Anytime you\nuse functions that accept a style, reference them.\n\nExample:\n\n~~~lisp\n(term:table (list '(:name :age) '(:me 7)) :header-style :header)\n~~~\n\nTo see colors in a \"dumb\" terminal like in Emacs Slime, install the package [`slime-repl-ansi-color`](https://melpa.org/#/slime-repl-ansi-color), \"require\" it and enable it ith `M-x slime-repl-ansi-color-mode`.\n\nYou can also disable styles in non-interactive terminals with `term::*enable-effects-on-dumb-terminals*`.\n\nPlease see our online documentation.\n\n### Style individual table cells programmatically\n\n![](ansi-term-cell-styles.png)\n\n\nThe `CELL-STYLE` argument can be either a keyword, denoting a style in\nuse, either a lambda function, that can compute a style for a given\ncell.\n\n*This feature is experimental. It only works for regular a `table` (not vtable).*\n\nThe function takes two arguments: the cell value, the header, and a key :default argument.\n\nExample:\n\nBelow we print in red prices that are superior to 10,\nwe print in cyan the other prices,\nand we print in green the other cells.\n\n~~~lisp\n(update-style-sheet\n'((:color :cyan   :bold)\n    (:danger :red :bold)\n    (:green :green)\n    (:default :green)\n    ))\n\n(setf *default-cell-style* :green)\n\n(defparameter objects '((\"pk\" \"title\" \"price\")\n                          (1 \"lisp\" 9.90)\n                          (2 \"common lisp\" 100)\n                          ))\n\n(table objects\n        :cell-style (lambda (val header)\n                      (when (equal \"price\" header)\n                        (if (\u003e val 10)\n                            :danger\n                            :color))))\n~~~\n\n### Print tables to string\n\nSee `term:with-table-output-to-string`.\n\n\n### Docstrings\n\n#### `table`\n\n```\nPrint a table filling cells with OBJECTS.\n\n  OBJECTS can be:\n\n  - a list of lists of string designators with equal lengths\n.   - generally, the first list is a list of headers.\n  - a list of hash-tables\n    - the table displays the first hash-table keys and all the hash-tables values.\n    - see also HTS-TABLE\n  - a list of property-lists\n    - the table prints the keys and all the plists' values\n    - to help the TABLE understand the arguments are plists\n      and not regular lists, set the PLIST key argument to T\n      or the variable *prefer-plists-in-tables* to T.\n    - see also PLISTS-TABLE\n  - a single hash-table\n  - a single plist.\n\n  KEYS is a list of keys to display. The associated rows or columns will be displayed.\n    With list of regular lists, the default headers are considered to be in the first list.\n\n  EXCLUDE is a list of keys to NOT display.\nExample:\n\n   (table '((:A :B :C) (1 2 3)))\n\n=\u003e\n\n    +---------+---------+---------+\n    |A        |B        |C        |\n    +---------+---------+---------+\n    |1        |2        |3        |\n    +---------+---------+---------+\n    |10       |20       |30       |\n    +---------+---------+---------+\n\n\nSee VTABLE to print the table vertically.\n\nIf BORDER-STYLE is NIL, no border will be\nprinted, otherwise BORDER-STYLE is expected to be a keyword that denotes\nthe style in which borders of the table should be printed.\n\nHEADER-STYLE will be\napplied to the first row of the table (also to the first column if\nCOL-HEADER is not NIL) and CELL-STYLE will be applied to all other rows. If\nCELL-STYLE is a list, its elements will be used to differently render every\ncolumn.\n\nObjects that end with MARK-SUFFIX will be printed using MARK-STYLE.\n\nCOLUMN-WIDTH is 10 by default. It can be an integer that applies to\nall columns, or a list designator to set a different\nwidth for every column. A cell content is truncated to fit the width. See `str:*ellipsis*'\nfor the ellusion string, `(…)' by default.\n\nALIGN controls the alignmet inside a cell. It can take the values :LEFT (default value), :CENTER, and :RIGHT.\n\nMARGIN, an integer, is the left margin of the whole table.\n\nOutput goes to STREAM.\n```\n\n## See also\n\n- https://github.com/AccelerationNet/data-table\n- https://github.com/telephil/cl-ascii-table/\n- https://github.com/Shinmera/text-draw - boxes, backgrounds, lines, arrows…\n\nBlog posts:\n\n- http://40ants.com/lisp-project-of-the-day/2020/05/0082-data-table.html\n- http://40ants.com/lisp-project-of-the-day/2020/05/0084-cl-ascii-table.html\n\n## Lisp?!\n\n- https://lispcookbook.github.io/cl-cookbook/\n- https://github.com/CodyReichert/awesome-cl\n- https://lisp-journey.gitlab.io/\n- [Learn Common Lisp in videos with a code-first approach: learn CLOS, macros, condition handling, the interactive workflow and much more](https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358)\n  - thanks for your support!\n\n\u003e I have done some preliminary Common Lisp exploration prior to this course but had a lot of questions regarding practical use and development workflows. This course was amazing for this! I learned a lot of useful techniques for actually writing the code in Emacs, as well as conversational explanations of concepts that had previously confused me in text-heavy resources. Please keep up the good work and continue with this line of topics, it is well worth the price!\n\nPreston\n\n## License\n\nCopyright © 2015–2018 Mark Karpov\nCopyright © 2018–present Vindarel\n\nDistributed under GNU GPL, version 3.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvindarel%2Fcl-ansi-term","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvindarel%2Fcl-ansi-term","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvindarel%2Fcl-ansi-term/lists"}