Security News
PyPI Introduces Digital Attestations to Strengthen Python Package Security
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
@effect/printer
Advanced tools
[![Main CI](https://github.com/Effect-TS/printer/actions/workflows/main.yml/badge.svg)](https://github.com/Effect-TS/printer/actions/workflows/main.yml) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-908a85?logo=gitpod)](http
@effect/printer
npm install @effect/printer
pnpm install @effect/printer
yarn add @effect/printer
This module defines a pretty printer to format text in a flexible and convenient way. The idea is to combine a Doc
ument out of many small components, then using a layouter to convert it to an easily renderable DocStream
, which can then be rendered to a variety of formats.
The documenists of several parts:
As a simple demonstration, let's use @effect/printer
to pretty-print the following simple Haskell type definition.
example :: Int -> Bool -> Char -> IO ()
First, let's setup the imports we need:
import * as Doc from "@effect/printer/Doc"
import * as Render from "@effect/printer/Render"
import { pipe } from "@fp-ts/data/Function"
import * as ReadonlyArray from "@fp-ts/data/ReadonlyArray"
Next, we intersperse the "->"
character between our types and add a leading "::"
character:
const prettyTypes = (types: ReadonlyArray<string>): Doc.Doc<never> => {
const symbolDocuments = pipe(
types.length - 1,
ReadonlyArray.makeBy(() => Doc.text("->")),
ReadonlyArray.prepend(Doc.text("::"))
)
const typeDocuments = types.map(Doc.text)
const documents = pipe(
symbolDocuments,
ReadonlyArray.zipWith(typeDocuments, (left, right) =>
pipe(left, Doc.catWithSpace(right))
)
)
return pipe(documents, Doc.seps, Doc.align)
}
The seps
function is one way of concatenating documents, but there are many others (e.g. vsep
, cat
and fillSep
, etc.). In our example, seps
is used to space-separate all documents if there is space remaining in the current line, and newlines if the remaining line is too short.
Next, we prepend the name to the type,
const prettyDeclaration = (
name: string,
types: ReadonlyArray<string>
): Doc.Doc<never> => {
return pipe(
Doc.text(name),
Doc.catWithSpace(prettyTypes(types))
)
}
Now we can define a document that contains some type signature:
const name = "example"
const types = ["Int", "Bool", "Char", "IO ()"]
const doc: Doc.Doc<never> = prettyDeclaration(name, types)
This document can now be printed! And as a bonus, it automatically adapts to available space.
If the page is wide enough (80
characters in this case), the definitions are space-separated.
const rendered = pipe(doc, Render.prettyDefault)
console.log(rendered)
// example :: Int -> Bool -> Char -> IO ()
If we narrow the page width to only 20
characters, the same document renders vertically aligned:
const rendered = pipe(doc, Render.pretty(20))
console.log(rendered)
// example :: Int
// -> Bool
// -> Char
// -> IO ()
Speaking of alignment, had we not used the align
combinators, the "->"
would be at the beginning of each line, and not beneath the "::"
.
╔══════════╗
║ ║ ╭────────────────────╮
║ ║ │ vsep, pretty, <+>, │
║ ║ │ nest, align, … │
║ ║ ╰─────────┬──────────╯
║ ║ │
║ Create ║ │
║ ║ │
║ ║ ▽
║ ║ ╭───────────────────╮
║ ║ │ Doc │
╠══════════╣ │ (rich document) │
║ ║ ╰─────────┬─────────╯
║ ║ │
║ ║ │ Layout algorithms
║ Layout ║ │ e.g. Layout.pretty
║ ║ ▽
║ ║ ╭───────────────────╮
║ ║ │ DocStream │
╠══════════╣ │ (simple document) │
║ ║ ╰─────────┬─────────╯
║ ║ │
║ ║ ├─────────────────────────────╮
║ ║ │ │ treeForm
║ ║ │ ▽
║ ║ │ ╭───────────────╮
║ ║ │ │ DocTree │
║ Render ║ │ ╰───────┬───────╯
║ ║ │ │
║ ║ ╭───────────────────┼─────────────────╮ ╭────────┴────────╮
║ ║ │ │ │ │ │
║ ║ ▽ ▽ ▽ ▽ ▽
║ ║ ╭───────────────╮ ╭───────────────╮ ╭───────────────╮ ╭───────────────╮
║ ║ │ ANSI terminal │ │ Plain Text │ │ other/custom │ │ HTML │
║ ║ ╰───────────────╯ ╰───────────────╯ ╰───────────────╯ ╰───────────────╯
║ ║
╚══════════╝
There are two key concepts to laying a document out: the available width, and grouping.
The layout algorithm will try to avoid exceeding the maximum width of the Doc
ument by inserting line breaks where possible. The available layout combinators make it fairly straightforward to specify where, and under what circumstances, such a line break may be inserted by the layout algorithm (for example via the seps
function).
There is also the concept of ribbon width. The ribbon is the part of a line that is printed (i.e. the line length without the leading indentation). The layout algorithms take a ribbon fraction argument, which specifies how much of a line should be filled before trying to break it up. A ribbon width of 0.5 in a document of width 80 will result in the layout algorithm trying to avoid exceeding 0.5 * 80 = 40
(ignoring current indentation depth).
A document can be group
ed, which tells the layout algorithm that it should attempt to collapse it to a single line. If the result does not fit within the constraints (given by page and ribbon widths), the document is rendered unaltered. This allows fallback renderings, so that we get nice results even when the original document would exceed the layout constraints.
Due to how the Wadler/Leijen algorithm is designed, a couple of things are unsupported right now, with a high possibility of having no sensible implementation without significantly changing the layout algorithm. In particular, this includes:
TL;DR - Use semantic annotations for Doc
, and after laying out the `Doc, only then map to backend-specific annotations.
For example, suppose you want to pretty-print some programming language code. If you want keywords to be red, you should annotate the Doc
with a type that has a Keyword
field (without any notion of color), and then after laying out the document, convert the annotations to map Keyword
to Red
(using DocStream.reAnnotate
). The alternative (which is not recommended) is directly annotating the Doc
with Red
.
While both versions would work equally well, and would create identical output, the recommended way has two significant advantages: modularity and extensibility.
Modularity: Changing the color of Keyword
s after laying out a Doc
ument means that there is only one modification needed (namely the call to DocStream.reAnnotate
) should the color of Keyword
s need to be changed in the future. If you have directly annotated Doc
uments with the color Red
, a full text search/replacement would be required should the color need to be changed.
Extensibility: Adding a different rendering of a Keyword
in the recommended version is as simple as adding a variant of DocStream.reAnnotate
to convert the Doc
annotation to something else. On the other hand, let's assume you have Red
as an annotation in the Doc
and the backend you would like to implement does not have the notion of color (think of plain text or a website where red doesn’t work well with the rest of the style). You now need to worry what to map redness to, which in this case has no canonical answer. Should it be omitted? What does red mean in the context of the new backend? Additionally, consider the case where keywords and variables have already been annotated as red, but you want to change only the color of variables.
This package is a port of https://github.com/quchen/prettyprinter
FAQs
An easy to use, extensible pretty-printer for rendering documents
The npm package @effect/printer receives a total of 14,974 weekly downloads. As such, @effect/printer popularity was classified as popular.
We found that @effect/printer demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.