{"id":19358069,"url":"https://github.com/faveod/oppen-ruby","last_synced_at":"2026-06-14T12:32:00.874Z","repository":{"id":256364765,"uuid":"855038806","full_name":"Faveod/oppen-ruby","owner":"Faveod","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-14T15:52:43.000Z","size":157,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-07-29T14:06:40.428Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Faveod.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2024-09-10T07:42:00.000Z","updated_at":"2025-07-29T12:10:31.000Z","dependencies_parsed_at":"2024-11-10T07:10:17.488Z","dependency_job_id":"8cb72197-5388-4306-896c-68a9e0be7203","html_url":"https://github.com/Faveod/oppen-ruby","commit_stats":null,"previous_names":["faveod/oppen-ruby"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/Faveod/oppen-ruby","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Faveod%2Foppen-ruby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Faveod%2Foppen-ruby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Faveod%2Foppen-ruby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Faveod%2Foppen-ruby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Faveod","download_url":"https://codeload.github.com/Faveod/oppen-ruby/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Faveod%2Foppen-ruby/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34322073,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"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":[],"created_at":"2024-11-10T07:10:14.981Z","updated_at":"2026-06-14T12:32:00.847Z","avatar_url":"https://github.com/Faveod.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Oppen's Pretty Printer\n[![CI badge]][CI]\n[![Docs latest badge]][Docs latest]\n[![rubygems.org badge]][rubygems.org]\n\n[CI badge]: https://github.com/Faveod/oppen-ruby/actions/workflows/test.yml/badge.svg\n[CI]: https://github.com/Faveod/oppen-ruby/actions/workflows/test.yml\n[Docs latest badge]: https://github.com/Faveod/oppen-ruby/actions/workflows/docs.yml/badge.svg\n[Docs latest]: https://faveod.github.io/oppen-ruby/\n[rubygems.org badge]: https://img.shields.io/gem/v/oppen?label=rubygems.org\n[rubygems.org]: https://rubygems.org/gems/oppen\n\nAn implementation of the pretty printing algorithm described by\n[Derek C. Oppen](https://dl.acm.org/doi/pdf/10.1145/357114.357115).\n\nWe also provide an API similar to\n[`ruby/prettyprint`](https://github.com/ruby/prettyprint), which we call\n`Wadler`, in reference to Philip Wadler's paper, [_A prettier\nprinter_](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf),\nthe basis for `prettyprint`. This can be really helpful if you decide to\ntransition from `ruby/prettyprint` to this gem.\n\n`Wadler` is implemented on top of `Oppen`, and it provides more options than\n`ruby/prettyprint`, notably:\n1. [Consistent](examples/wadler_group/consistent.rb) and [inconsistent](examples/wadler_group/inconsistent.rb) breaking.\n1. [Explicit breaking](examples/wadler_break_and_breakable/break.rb), which is achievable in `ruby/prettyprint` with some monkeypatching.\n1. [Trimming of trailing whitespaces](examples/oppen_and_wadler_customization/whitespace.rb).\n1. [Display a `String` on line break](examples/wadler_break_and_breakable/line_continuation.rb).\n1. A bunch of helper methods to simplify common patterns like [surrounding](examples/wadler_utils/surround.rb) or\n[separating](examples/wadler_utils/surround.rb) tokens.\n\n## Oppen vs Wadler\n\n`Wadler` calls `Oppen` under the hood, so it's not a separate implementation,\nand it's not calling ruby's `prettyprint`.\n\nBoth implementations have their use cases:\n- Oppen gives more control over tokens sent to the printer.\n- Wadler gives a more _\"functional\"_ API, which is far nicer to work with.\n\nThat being said, both APIs in this gem can achieve the same results, especially\non consistent and inconsistent breaking.\n\n## Oppen's API Example\n\n```ruby\ntokens = [\n  Oppen.begin_inconsistent,\n  Oppen.string('Hello'),\n  Oppen.break(', '),\n  Oppen.string('World!'),\n  Oppen.line_break,\n  Oppen.string('How are you doing?'),\n  Oppen.end,\n  Oppen.eof,\n]\n\nputs Oppen.print(tokens:)\n# Hello, World!\n#   How are you doing?\n```\n\n## Wadler's API Example\n\n```ruby\nout = Oppen::Wadler.new(width: 20)\n\nout.group(indent: 2) {\n  out.group {\n    out.text('def').breakable.text('foo')\n  }\n  out.parens_break_none {\n    out.separate(%w[bar baz bat qux], ',', break_type: :inconsistent) { |param|\n      out.text(param)\n    }\n  }\n}\nout.group(indent: 2) {\n  out\n    .break\n    .nest(indent: 2) {\n      out\n        .text('puts')\n        .breakable(line_continuation: ' \\\\')\n        .text('42')\n  }\n}\nout.break.text('end')\n\nputs out.output\n# def foo(bar, baz,\n#   bat, qux)\n#   puts \\\n#     42\n# end\n```\n\n## More Examples\n\nAn easy way to add colors to the output on the terminal is wrap `oppen` and expose your own vocabulary:\n\n```ruby\nrequire 'colored'\nclass ColoredTty\n  KW_PALETTE = { Hello: :red, World: :green }.freeze\n  def initialize(...) = @out = Oppen::Wadler.new(...)\n  def breakable(...) = @out.breakable(...) \u0026\u0026 self\n  def keyword(value, width: value.length) = @out.text(value.send(KW_PALETTE[value.to_sym] || :white), width:) \u0026\u0026 self\n  def output = @out.output\n  def text(...) = @out.text(...) \u0026\u0026 self\nend\n\nout = ColoredTty.new(width: 12)\nout.keyword('Hello').breakable.text('World')\n\nputs out.output\n# \\e[31mHello\\e[0m World\n```\n\nThe same idea can be applied an adapted to make an HTML printer; all you need to take care of is the correct width of the text to preserve the width of the text and get an output identical to that of the tty colored printer.\n\nCheck out the [examples/](examples/README.md) folder for more details on how to use the Oppen and Wadler APIs.\n\n## Difference With Oppen's Original Algorithm\n\n1. We took the liberty to rename functions to make the API more modern and closer to\nwhat we expect when writing Ruby code.  All correspondences with the algorithm\nas described in Oppen's paper are noted in the comments of classes and methods.\n1. We do not raise exceptions when we overflow the margin. The only exceptions\nthat we raise indicate a bug in the implementation. Please report them.\n1. The stacks described by the algorithm do not have a fixed size in our\nimplementation: we upsize them when they are full.\n1. We can optionally trim trailing whitespaces (this feature is on by default for the `Wadler` API).\n1. We added support for an additional new line anchors, see [examples/configs/indent_anchor.rb](examples/configs/indent_anchor.rb).\n1. We added support for eager printing of `groups`; see [examples/configs/eager_print.rb](examples/configs/eager_print.rb).\n1. We introduced a new token (`Whitespace`) and added more customizations to one of the originals (`Break`).\n\nFor more insight on how Oppen's algorithm works, check out [docs/oppen_algorithm.md](docs/oppen_algorithm.md).\n\n## Related Projects\n\n1. [`ruby/prettyprint`](https://github.com/ruby/prettyprint)\n1. [rustc implementation](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_pretty/pp/index.html)\n1. [`stevej2608/oppen-pretty-printer`](https://github.com/stevej2608/oppen-pretty-printer) as a library.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaveod%2Foppen-ruby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffaveod%2Foppen-ruby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaveod%2Foppen-ruby/lists"}