{"id":13995681,"url":"https://github.com/bkase/DoctorPretty","last_synced_at":"2025-07-22T22:31:58.155Z","repository":{"id":63905925,"uuid":"91941668","full_name":"bkase/DoctorPretty","owner":"bkase","description":"Wadler's \"A prettier printer\" embedded pretty-printer DSL for Swift","archived":false,"fork":false,"pushed_at":"2021-10-14T18:46:07.000Z","size":51,"stargazers_count":192,"open_issues_count":1,"forks_count":9,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-11-28T17:44:47.299Z","etag":null,"topics":["dsl","functional","ios","prettier","prettier-printer","pretty-print","swift"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bkase.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-21T07:58:53.000Z","updated_at":"2023-12-19T05:15:08.000Z","dependencies_parsed_at":"2022-11-28T19:35:40.387Z","dependency_job_id":null,"html_url":"https://github.com/bkase/DoctorPretty","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkase%2FDoctorPretty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkase%2FDoctorPretty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkase%2FDoctorPretty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkase%2FDoctorPretty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bkase","download_url":"https://codeload.github.com/bkase/DoctorPretty/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227190298,"owners_count":17745243,"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":["dsl","functional","ios","prettier","prettier-printer","pretty-print","swift"],"created_at":"2024-08-09T14:03:32.814Z","updated_at":"2024-11-29T18:30:50.233Z","avatar_url":"https://github.com/bkase.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":[],"readme":"## Doctor Pretty\n\n\u003e A Swift implementation of the [A prettier printer (Wadler 2003)](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf) ported from [wl-pprint-annotated](https://github.com/minad/wl-pprint-annotated/blob/master/src/Text/PrettyPrint/Annotated/WL.hs)\n\n[![Build Status](https://travis-ci.org/bkase/DoctorPretty.svg?branch=master)](https://travis-ci.org/bkase/DoctorPretty)\n\n## What is this\n\nA pretty printer is the dual of a parser -- take some arbitrary AST and output it to a String. This library is a collection of combinators and a primitive called a `Doc` to describe pretty printing some data much like a parser combinator library provides combinators and primitives to describe parsing test into an AST. Interestingly, this implementation efficiently finds the _best_ pretty print. You encode your knowledge of what the _best_ means with your use of various `Doc` combinators.\n\nFor example: Let's say we have some internal structured representation of this Swift code:\n\n```swift\nfunc aLongFunction(foo: String, bar: Int, baz: Long) -\u003e (String, Int, Long) {\n    sideEffect()\n    return (foo, bar, baz)\n}\n```\n\nWith this library the description that pretty prints the above at a page width of 120 characters. Also prints:\n\nAt a page-width of 40 characters:\n\n```swift\nfunc aLongFunction(\n    foo: String, bar: Int, baz: Long\n) -\u003e (String, Int, Long) {\n    sideEffect()\n    return (foo, bar, baz)\n}\n```\n\nand at a page-width of 20 characters:\n\n```swift\nfunc aLongFunction(\n    foo: String,\n    bar: Int,\n    baz: Long\n) -\u003e (\n    String,\n    Int,\n    Long\n) {\n    sideEffect()\n    return (\n        foo,\n        bar,\n        baz\n    )\n}\n```\n\nSee the encoding of this particular document in the [`testSwiftExample` test case](Tests/DoctorPrettyTests/DoctorPrettyTests.swift).\n\n## What would I use this for?\n\nIf you're outputting text and you care about the width of the page. Serializing to a `Doc` lets you capture your line-break logic and how your output string looks in one go.\n\nWhy would you output text and care about page width?\n\n1. You're building a `gofmt`-type tool for Swift (Note: `gofmt` doesn't pretty-print based on a width, `refmt` (Reason) and `prettier` (JavaScript) do)\n2. You're writing some sort of codegen tool to output Swift code\n3. You're building a source-to-source transpiler\n2. You're outputing help messages in a terminal window for some commandline app (I'm planning to use this for https://github.com/bkase/swift-optparse-applicative)\n\n## What is this, actually\n\nA Swift implementation of the [A prettier printer (Wadler 2003)](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf) paper (including generally accepted modern enhancements ala [wl-pprint-annotated](https://github.com/minad/wl-pprint-annotated/blob/master/src/Text/PrettyPrint/Annotated/WL.hs). This implementation is close to a direct port of [wl-pprint-annotated](https://github.com/minad/wl-pprint-annotated/blob/master/src/Text/PrettyPrint/Annotated/WL.hs) with some influence from [scala-optparse-applicative's Doc](https://github.com/bmjames/scala-optparse-applicative/blob/master/src/main/scala/net/bmjames/opts/types/Doc.scala) and a few extra Swiftisms.\n\n## Basic Usage\n\n`Doc` is very composable. First of all it's a [`monoid`](https://www.youtube.com/watch?v=6z9QjDUKkCs) with an `.empty` document and the `.concat` case which just puts two documents next to each other. We also have a primitive called `grouped`, which tries this document on a single line, but if it doesn't fit then breaks it up on new-lines. From there we build all high-level combinators up.\n\n`x \u003c%\u003e y` concats x and y with a space in between if it fits, otherwise puts a line.\n\n```swift\n.text(\"foo\") \u003c%\u003e .text(\"bar\")\n```\n\npretty-prints under a large page-width:\n\n```\nfoo bar\n```\n\nbut when the page-width is set to `5` prints:\n\n```\nfoo\nbar\n```\n\nHere are a few more combinators:\n\n```swift\n/// Concats x and y with a space in between\nstatic func \u003c+\u003e(x: Doc, y: Doc) -\u003e Doc\n\n/// Behaves like `space` if the output fits the page\n/// Otherwise it behaves like line\nstatic var softline\n\n/// Concats x and y together if it fits\n/// Otherwise puts a line in between\nstatic func \u003c%%\u003e(x: Doc, y: Doc) -\u003e Doc\n\n/// Behaves like `zero` if the output fits the page\n/// Otherwise it behaves like line\nstatic var softbreak: Doc\n\n/// Puts a line between x and y that can be flattened to a space\nstatic func \u003c\u0026\u003e(x: Doc, y: Doc) -\u003e Doc\n\n/// Puts a line between x and y that can be flattened with no space\nstatic func \u003c\u0026\u0026\u003e(x: Doc, y: Doc) -\u003e Doc\n```\n\nThere are also combinators for turning collections of documents into \"collection\"-like pretty-printed primitives such as a square-bracket separated lists:\n\n```swift\n.text(\"let x =\") \u003c%\u003e [ \"foo\", \"bar\", \"baz\" ].map(Doc.text).list(indent: 4)\n```\n\nPretty-prints at page-width 80 to:\n\n```swift\nlet x = [foo, bar, baz]\n```\n\nand at page-width 10 to:\n\n```swift\nlet x = [\n    foo,\n    bar,\n    baz\n]\n```\n\nSee the source for more documentation, I have included descriptive doc-comments to explain all the operators (mostly taken from [wl-pprint-annotated](https://github.com/minad/wl-pprint-annotated/blob/master/src/Text/PrettyPrint/Annotated/WL.hs)).\n\n## How do I actually pretty print my documents?\n\n`Doc` has two rendering methods for now: `renderPrettyDefault` prints with a page-width of 100 and `renderPretty` lets you control the page-width.\n\nThese methods don't return `String`s directly -- they return `SimpleDoc` a low-level IR that is close to a string, but high-enough that you can efficiently output to some other output system like stdout or a file.\n\nFor now, `SimpleDoc` has `displayString()` which outputs a `String`, and:\n\n```swift\nfunc display\u003cM: Monoid\u003e(readString: (String) -\u003e M) -\u003e M\n```\n\n`display` takes a function that can turn a string into a monoid and then smashes everything together. Because this works for any monoid, you just need to provide a monoid instance for your output formatter (to write to stdout or to a file).\n\n## Installation\n\nWith Swift Package Manager, put this inside your `Package.swift`:\n\n```swift\n.Package(url: \"https://github.com/bkase/DoctorPretty.git\",\n         majorVersion: 0, minor: 5)\n```\n\n## How does it work?\n\nRead the [A prettier printer (Wadler 2003) paper](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf).\n\n`Doc` is a recursive enum that captures text and new lines. The interesting case is `.union(longerLines: Doc, shorterLines: Doc)`. This case reifies the notion of \"try the longer lines first, then the shorter lines\". We can build all sorts of high-level combinators that combine `Doc`s in different ways that eventually reduce to a few strategic `.union`s.\n\nThe renderer keeps a work-list and each rule removes or adds pieces of work to the list and recurses until the list is empty. The best-fit metric proceeds greedily for now, but can be swapped out easily for a more intelligent algorithm in the future.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbkase%2FDoctorPretty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbkase%2FDoctorPretty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbkase%2FDoctorPretty/lists"}