Skip to content

Commit cfced5b

Browse files
authored
basilisp.pprint namespace (#1258)
Fixes #513
1 parent 97eab3e commit cfced5b

File tree

8 files changed

+1369
-7
lines changed

8 files changed

+1369
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
* Added support for f-strings (#922)
1414
* Added the `aslice` macro to facilitate the use of Python style `array[start:stop:step]` slicing in Basilisp (#1248)
1515
* Added the `IPending` interface which is implemented by delays, futures, and promises (#1260)
16+
* Added the `basilisp.pprint` namespace (#513)
1617

1718
### Changed
18-
* Removed implicit support for single-use iterables in sequences, and introduced `iterator-seq` to expliciltly handle them (#1192)
19+
* Removed implicit support for single-use iterables in sequences, and introduced `iterator-seq` to explicitly handle them (#1192)
1920
* `basilisp.core/str` now delegates to the builtin Python `str` in all cases except for customizing the string output for builtin Python types (#1237)
2021
* Optimised mainstream seq-consuming functions by coercing their inputs into `seq` upfront (#1234)
2122
* Renamed `awith` and `afor` to `with-async` and `for-async` for improved clarity (#1248)
2223
* `basilisp.main.init` will only initialize the runtime environment on the first invocation (#1242)
2324
* Updated support for PyPy to 3.9 and 3.10 (#1265)
2425

2526
### Fixed
26-
* Fix a bug where protocols with methods with leading hyphens in the could not be defined (#1230)
27+
* Fix a bug where protocols with methods with leading hyphens in method names could not be defined (#1230)
2728
* Fix a bug where attempting to `:refer` a non-existent Var from another namespace would throw an unhelpful exception (#1231)
29+
* Fixed a bug where `(range 0)` would return `(0)` rather than than `()` as expected (#1258)
2830

2931
## [v0.3.8]
3032
### Added

docs/api/pprint.rst

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,119 @@
11
basilisp.pprint
22
===============
33

4+
.. lpy:currentns:: basilisp.pprint
5+
46
.. toctree::
57
:maxdepth: 2
68
:caption: Contents:
79

10+
.. _pretty_printing:
11+
12+
Pretty Printing
13+
---------------
14+
15+
Pretty printing built-in data structures is as easy as a call to :lpy:fn:`pprint`.
16+
17+
.. code-block::
18+
19+
(require '[basilisp.pprint :as pprint])
20+
(pprint/pprint (range 30))
21+
22+
The output can be configured using a number of different control variables, which are expressed as dynamic Vars.
23+
24+
- :lpy:var:`*print-base*`
25+
- :lpy:var:`*print-miser-width*`
26+
- :lpy:var:`*print-pretty*`
27+
- :lpy:var:`*print-pprint-dispatch*`
28+
- :lpy:var:`*print-radix*`
29+
- :lpy:var:`*print-right-margin*`
30+
- :lpy:var:`*print-sort-keys*`
31+
- :lpy:var:`*print-suppress-namespaces*`
32+
33+
You can pretty print the last result from the REPL using the :lpy:fn:`pp` convenience macro.
34+
35+
As an alternative, the :lpy:fn:`write` API enables a more ergonomic API for configuring the printer using keyword arguments rather than dynamic Vars.
36+
37+
.. code-block::
38+
39+
(pprint/write (ns-interns 'basilisp.pprint) :sort-keys true)
40+
;; {*current-length* #'basilisp.pprint/*current-length*
41+
;; ...
42+
;; write-out #'basilisp.pprint/write-out}
43+
44+
.. _custom_pretty_print_dispatch_function:
45+
46+
Custom Pretty Print Dispatch Function
47+
-------------------------------------
48+
49+
The default dispatch function is :lpy:fn:`simple-dispatch` which can print most builtin Basilisp types.
50+
Using the builtin macros and utilities, it is possible to create a custom dispatch function.
51+
52+
.. _pretty_printing_concepts:
53+
54+
Pretty Printing Concepts
55+
^^^^^^^^^^^^^^^^^^^^^^^^
56+
57+
The pretty printing algorithm used in ``basilisp.pprint`` is based on the XP algorithm defined in Richard Water's 1989 paper "XP: A Common Lisp Pretty Printing System" as adapted in Clojure's ``pprint`` by Tom Faulhaber.
58+
There are three basic concepts in the XP algorithm which are necessary in order to create a custom dispatch function.
59+
60+
- *Logical blocks* are groups of output that should be treated as a single unit by the pretty printer.
61+
Logical blocks can nest, so one logical block may contain 0 or more other logical blocks.
62+
For example, a vector may contain a map; the vector would be a logical block and the map would also be a logical block.
63+
``simple-dispatch`` even treats key/value pairs in associative type outputs as a logical block, so they are printed on the same line whenever possible.
64+
65+
A dispatch function can emit a logical block using the :lpy:fn:`pprint-logical-block` macro.
66+
67+
- *Conditional newlines* can be emitted anywhere a newline may need inserted into the output stream.
68+
Newlines can be one of 3 different types which hints to the pretty printer when a newline should be emitted.
69+
70+
Dispatch functions can emit newlines in any supported style using the :lpy:fn:`pprint-newline` function.
71+
72+
- ``:linear`` style newlines should be emitted whenever the enclosing logical block does not fit on a single line.
73+
Note that if any linear newline is emitted in a block, every linear newline will be emitted in that block.
74+
75+
- ``:mandatory`` style newlines are emitted in all cases.
76+
77+
- ``:miser`` style newlines are emitted only when the output will occur in the "miser" region as defined by :lpy:var:`*print-miser-width*`.
78+
This allows additional newlines to be emitted as the output nests closer to the right margin.
79+
80+
- *Indentation* commands indicate how indentation of subsequent lines in a logical block should be defined.
81+
Indentation may be defined relative to either the starting column of the current logical block or to the current column of the output.
82+
83+
Dispatch functions can control indentation using the :lpy:fn:`pprint-indent` function.
84+
85+
Pretty printing is most useful for viewing large, nested structures in a more human-friendly way.
86+
To that end, dispatch functions wishing to print any collection may want to use the :lpy:fn:`print-length-loop` macro to loop over the output, respecting the :lpy:var:`basilisp.core/*print-length*` setting.
87+
88+
Dispatch functions which may need to be called on nested elements should use :lpy:fn:`write-out` to ensure that :lpy:var:`basilisp.core/*print-level*` is respected.
89+
Scalar values can be printed with :lpy:fn:`basilisp.core/pr` or just written directly to :lpy:var:`*out*`.
90+
91+
.. _unimplemented_pprint_features:
92+
93+
Unimplemented Features
94+
----------------------
95+
96+
The following features from ``clojure.pprint`` are not currently implemented:
97+
98+
- ``:fill`` newlines
99+
- ``code-dispatch`` for printing code
100+
- ``cl-format``
101+
102+
.. _pprint_references:
103+
104+
References
105+
----------
106+
107+
- Tom Faulhaber et al.; ``clojure.pprint`` (`API <https://clojure.github.io/clojure/clojure.pprint-api.html>`_, `Documentation <https://clojure.github.io/clojure/doc/clojure/pprint/PrettyPrinting.html>`_)
108+
- Oppen, Derek; \"Prettyprinting\"; October 1980
109+
- Waters, Richard; \"XP: A Common Lisp Pretty Printing System\"; March 1989
110+
111+
.. _pprint_api:
112+
113+
API
114+
---
115+
8116
.. autonamespace:: basilisp.pprint
9117
:members:
10-
:undoc-members:
118+
:undoc-members:
119+
:exclude-members: LogicalBlock, StartBlock, EndBlock, Blob, Newline, Indent, *current-length*, *current-level*

docs/differencesfromclojure.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ Basilisp includes ports of some of the standard libraries from Clojure which sho
225225
* :lpy:ns:`basilisp.data` is a port of ``clojure.data``
226226
* :lpy:ns:`basilisp.edn` is a port of ``clojure.edn``
227227
* :lpy:ns:`basilisp.io` is a port of ``clojure.java.io``
228+
* :lpy:ns:`basilisp.pprint` is a port of ``clojure.pprint`` (excluding support for ``cl-format``)
228229
* :lpy:ns:`basilisp.set` is a port of ``clojure.set``
229230
* :lpy:ns:`basilisp.shell` is a port of ``clojure.java.shell``
230231
* :lpy:ns:`basilisp.stacktrace` is a port of ``clojure.stacktrace``

docs/interfaces.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ In day-to-day usage, you will not typically need to use these interfaces, but th
2222
.. automethod:: _lrepr
2323
.. automethod:: lrepr
2424

25-
.. lpy:currentns:: basilisp.core
25+
.. lpy:currentns:: basilisp.core
2626
2727
.. automodule:: basilisp.lang.interfaces
2828
:members:

src/basilisp/core.lpy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2589,7 +2589,7 @@
25892589
([]
25902590
(iterate inc 0))
25912591
([end]
2592-
(lazy-seq (cons 0 (range 1 end))))
2592+
(range 0 end))
25932593
([start end]
25942594
(lazy-seq
25952595
(when (< start end)

0 commit comments

Comments
 (0)