Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Prettier for Ruby (2019)

Prettier for Ruby (2019)

A lightning talk describe @prettier/plugin-ruby, the choices that went into it, how it works, and how to use it.

Kevin Newton

August 13, 2019
Tweet

More Decks by Kevin Newton

Other Decks in Programming

Transcript

  1. Prettier for Ruby github.com/prettier/plugin-ruby twitter.com/kddeisz

  2. None
  3. github.com/prettier/plugin-ruby twitter.com/kddeisz What is prettier? • A language-agnostic framework for

    building formatters • A set of language-specific parsers that build a prettier- specific intermediate representation (IR) • A printer for printing out prettier IR
  4. github.com/prettier/plugin-ruby twitter.com/kddeisz Language support • JavaScript (JSX, Flow, TypeScript, JSON)

    • HTML (Vue, Angular) • CSS (Less, SCSS, styled-components, styled-jsx) • Markdown (MDX, YAML)
  5. github.com/prettier/plugin-ruby twitter.com/kddeisz Plugin support • Java (community) • PHP (official)

    • PostgreSQL (community) • Ruby (official) • Swift (official) • TOML (community)
  6. github.com/prettier/plugin-ruby twitter.com/kddeisz @prettier/plugin-ruby • A ruby gem and a node

    module • 612 GitHub stars, 2905 downloads/week on npm • Active development
  7. github.com/prettier/plugin-ruby twitter.com/kddeisz Ruby to prettier IR • Parse the Ruby

    source using ripper • Convert the ripper output to JSON and return to node • Walk the ripper tree with node to generate prettier IR • Ripper::SCANNER_EVENTS.size = 52 • Ripper::PARSER_EVENTS.size = 132
  8. github.com/prettier/plugin-ruby twitter.com/kddeisz Prettier IR to source • Walk the prettier

    IR and print nodes as you go • At each node, call the correct print function for that node from the necessary plugin/printer • If you hit the line limit, break the outermost group
  9. github.com/prettier/plugin-ruby twitter.com/kddeisz d=[30644250780,9003106878, 30636278846,66641217692,4501790980, 671_24_603036,131_61973916,66_606629_920, 30642677916,30643069058];a,s=[],$*[0] s.each_byte{|b|a<<("%036b"%d[b. chr.to_i]).scan(/\d{6}/)} a.transpose.each{ |a|

    a.join.each_byte{\ |i|print i==49?\ ($*[1]||"#")\ :32.chr} puts }
  10. github.com/prettier/plugin-ruby twitter.com/kddeisz d = [ 30_644_250_780, 9_003_106_878, 30_636_278_846, 66_641_217_692, 4_501_790_980,

    671_24_603036, 131_61973916, 66_606629_920, 30_642_677_916, 30_643_069_058 ] a, s = [], $*[0] s.each_byte { |b| a << ('%036b' % d[b.chr.to_i]).scan(/\d{6}/) } a.transpose.each do |a| a.join.each_byte { |i| print i == 49 ? ($*[1] || '#') : 32.chr } puts end
  11. github.com/prettier/plugin-ruby twitter.com/kddeisz Ruby choices • For the most part, consistent

    with rubocop • Run on the same input it will generate the same output • It will never change the meaning of your program* *Probably
  12. github.com/prettier/plugin-ruby twitter.com/kddeisz alias with bare words class Foo alias :foo

    :bar alias bar baz end
  13. github.com/prettier/plugin-ruby twitter.com/kddeisz class Foo alias foo bar alias bar baz

    end alias with bare words
  14. github.com/prettier/plugin-ruby twitter.com/kddeisz array literals %w[alpha beta gamma delta epsilon] %i[alpha

    beta gamma delta epsilon] ['alpha', 'beta', 'gamma', 'delta', 'epsilon'] [:alpha, :beta, :gamma, :delta, :epsilon]
  15. github.com/prettier/plugin-ruby twitter.com/kddeisz %w[alpha beta gamma delta epsilon] %i[alpha beta gamma

    delta epsilon] %w[alpha beta gamma delta epsilon] %i[alpha beta gamma delta epsilon] array literals
  16. github.com/prettier/plugin-ruby twitter.com/kddeisz Symbol#to_proc [1, 2, 3, 4, 5].map do |item|

    item.to_s end
  17. github.com/prettier/plugin-ruby twitter.com/kddeisz Symbol#to_proc [1, 2, 3, 4, 5].map(&:to_s)

  18. github.com/prettier/plugin-ruby twitter.com/kddeisz do…end and {} blocks [1, 2, 3, 4,

    5].each do |item| item.to_s(:format) end
  19. github.com/prettier/plugin-ruby twitter.com/kddeisz do…end and {} blocks [1, 2, 3, 4,

    5].each { |item| item.to_s(:format) }
  20. github.com/prettier/plugin-ruby twitter.com/kddeisz ternaries foo = if bar 1 else 2

    end
  21. github.com/prettier/plugin-ruby twitter.com/kddeisz ternaries foo = bar ? 1 : 2

  22. github.com/prettier/plugin-ruby twitter.com/kddeisz rescues foo rescue nil

  23. github.com/prettier/plugin-ruby twitter.com/kddeisz rescues begin foo rescue StandardError nil end

  24. github.com/prettier/plugin-ruby twitter.com/kddeisz strings 'foo' "foo" 'foo\n' "foo\n" 'foo #{bar} baz'

    "foo #{bar} baz" 'foo \M-\C-a' "foo \M-\C-a" "#@foo" "#@@foo" "#$foo" ?f
  25. github.com/prettier/plugin-ruby twitter.com/kddeisz strings 'foo' 'foo' 'foo\n' "foo\n" 'foo #{bar} baz'

    "foo #{bar} baz" 'foo \M-\C-a' "foo \M-\C-a" "#{@foo}" "#{@@foo}" "#{$foo}" 'f'
  26. github.com/prettier/plugin-ruby twitter.com/kddeisz Ruby choices • break, next, yield, return don’t

    use parentheses
 (super will sometimes get parentheses) • no nested ternaries • decimal numbers will get underscores after 3 digits
 octal numbers will have an “o” added if it’s not there • {} lambdas for single line, do…end for multi-line • … and many more!
  27. github.com/prettier/plugin-ruby twitter.com/kddeisz Prettier options • Increase adoption • No longer

    done • Great demand from the community • Time to entrench • Compatibility reasons
  28. github.com/prettier/plugin-ruby twitter.com/kddeisz Ruby options • --add-trailing-commas = false • --inline-conditionals

    = true • --inline-loops = true • --prefer-hash-labels = true • --prefer-single-quotes = true
  29. github.com/prettier/plugin-ruby twitter.com/kddeisz Future work • Better comment handling • Better

    support for method chaining • Template language support (ERB, HAML, etc.)
  30. Prettier for Ruby github.com/prettier/plugin-ruby twitter.com/kddeisz