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.

8a66c2a7197be751b21ebd35319ec797?s=128

Kevin Deisz

August 13, 2019
Tweet

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