From 1cdf0216e113d1ba4ecca15d0ef0e8920a76d14c Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 22 Apr 2022 12:41:53 -0400 Subject: [PATCH] Reformat --- CHANGELOG.md | 10 +- README.md | 16 +- Rakefile | 10 +- bin/console | 6 +- bin/debug | 14 +- bin/lex | 2 +- bin/sexp | 6 +- exe/rbprettier | 4 +- lib/prettier.rb | 18 +- lib/prettier/rake/task.rb | 10 +- prettier.gemspec | 40 ++-- src/haml/parser.rb | 143 ------------- src/haml/printer.ts | 427 ------------------------------------- src/plugin.ts | 6 +- src/ruby/nodes/blocks.ts | 131 ------------ src/server.rb | 81 +++---- src/utils/getChildNodes.ts | 326 ---------------------------- test/js/rbs/rbs.test.ts | 8 +- test/js/setupTests.ts | 23 +- test/rb/rake_test.rb | 10 +- test/rb/test_helper.rb | 6 +- test/rb/version_test.rb | 2 +- 22 files changed, 138 insertions(+), 1161 deletions(-) delete mode 100644 src/haml/parser.rb delete mode 100644 src/haml/printer.ts delete mode 100644 src/ruby/nodes/blocks.ts delete mode 100644 src/utils/getChildNodes.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 138d08c5..e53b53c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -320,7 +320,6 @@ The comment in the above example should stay in place. ```ruby begin - rescue Foo, Bar # comment end @@ -476,7 +475,8 @@ return (a or b) if c? - kddnewton - Support for the `nokw_param` node for specifying when methods should no accept keywords, as in: ```ruby -def foo(**nil); end +def foo(**nil) +end ``` - kddnewton - Support for the `args_forward` node for forwarding all types of arguments, as in: @@ -522,9 +522,9 @@ will now be printed as: ```ruby Config::Download.new( - 'prettier', - filename: 'prettier.yml', - url: 'https://raw.githubusercontent.com/...' + "prettier", + filename: "prettier.yml", + url: "https://raw.githubusercontent.com/..." ).perform ``` diff --git a/README.md b/README.md index 18aaa392..6b72a543 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ d = [ 30_643_069_058 ] a, s = [], $*[0] -s.each_byte { |b| a << ('%036b' % d[b.chr.to_i]).scan(/\d{6}/) } +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 } + a.join.each_byte { |i| printi == 49 ? ($*[1] || "#") : 32.chr } puts end ``` @@ -83,7 +83,7 @@ This plugin currently supports formatting the following kinds of files: Add this line to your application's Gemfile: ```ruby -gem 'prettier' +gem "prettier" ``` And then execute: @@ -128,11 +128,11 @@ The `prettier` executable is now installed and ready for use: Below are the options (from [`src/plugin.js`](src/plugin.js)) that `@prettier/plugin-ruby` currently supports: -| API Option | CLI Option | Default | Description | -| ------------------ | ---------------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------ | -| `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). | -| `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). | -| `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). | +| API Option | CLI Option | Default | Description | +| --------------- | ------------------ | :-----: | --------------------------------------------------------------------------------------------------- | +| `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). | +| `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). | +| `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). | Any of these can be added to your existing [prettier configuration file](https://prettier.io/docs/en/configuration.html). For example: diff --git a/Rakefile b/Rakefile index b2132881..a4eb3b0f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,12 +1,12 @@ # frozen_string_literal: true -require 'bundler/gem_tasks' -require 'rake/testtask' +require "bundler/gem_tasks" +require "rake/testtask" Rake::TestTask.new(:test) do |t| - t.libs << 'test/rb' - t.libs << 'lib' - t.test_files = FileList['test/rb/**/*_test.rb'] + t.libs << "test/rb" + t.libs << "lib" + t.test_files = FileList["test/rb/**/*_test.rb"] end task default: :test diff --git a/bin/console b/bin/console index 5d2f5a89..05b9a575 100755 --- a/bin/console +++ b/bin/console @@ -1,7 +1,7 @@ #!/usr/bin/env ruby -require 'bundler/setup' -require 'prettier' +require "bundler/setup" +require "prettier" -require 'irb' +require "irb" IRB.start(__FILE__) diff --git a/bin/debug b/bin/debug index 2f80a61d..15d16356 100755 --- a/bin/debug +++ b/bin/debug @@ -1,23 +1,23 @@ #!/usr/bin/env ruby -require 'bundler/inline' +require "bundler/inline" gemfile do - source 'https://rubygems.org' - gem 'sinatra', require: 'sinatra/base' - gem 'webrick' + source "https://rubygems.org" + gem "sinatra", require: "sinatra/base" + gem "webrick" end -require_relative '../src/ruby/parser' +require_relative "../src/ruby/parser" class App < Sinatra::Base HTML = DATA.read - get '/' do + get "/" do HTML end - post '/ast' do + post "/ast" do response = Prettier::Parser.parse(request.body.read) response ? JSON.fast_generate(response) : halt(422) rescue Prettier::Parser::ParserError diff --git a/bin/lex b/bin/lex index b0fdc359..58af05a6 100755 --- a/bin/lex +++ b/bin/lex @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -require 'ripper' +require "ripper" source = File.file?(ARGV[0]) ? File.read(ARGV[0]) : ARGV[0].gsub('\\n', "\n") pp Ripper.lex(source) diff --git a/bin/sexp b/bin/sexp index 348ec402..1c18538c 100755 --- a/bin/sexp +++ b/bin/sexp @@ -1,10 +1,10 @@ #!/usr/bin/env ruby -require_relative '../src/ruby/parser' +require_relative "../src/ruby/parser" source = if !ARGV[0] - File.read('test.rb') + File.read("test.rb") elsif File.file?(ARGV[0]) File.read(ARGV[0]) else @@ -13,7 +13,7 @@ source = parsed = SyntaxTree.parse(source) -puts '=== SOURCE === ' +puts "=== SOURCE === " puts source puts "\n=== COMMENTS ===" diff --git a/exe/rbprettier b/exe/rbprettier index 3f3ba279..f5b653a3 100755 --- a/exe/rbprettier +++ b/exe/rbprettier @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true -$:.unshift(File.expand_path(File.join('..', 'lib'), __dir__)) -require 'prettier' +$:.unshift(File.expand_path(File.join("..", "lib"), __dir__)) +require "prettier" exit(Prettier.run(ARGV)) diff --git a/lib/prettier.rb b/lib/prettier.rb index 973112e7..3a41899e 100644 --- a/lib/prettier.rb +++ b/lib/prettier.rb @@ -1,19 +1,19 @@ # frozen_string_literal: true -require 'json' unless defined?(JSON) -require 'open3' +require "json" unless defined?(JSON) +require "open3" module Prettier - PLUGIN = -File.expand_path('..', __dir__) - BINARY = -File.join(PLUGIN, 'node_modules', 'prettier', 'bin-prettier.js') - VERSION = -JSON.parse(File.read(File.join(PLUGIN, 'package.json')))['version'] + PLUGIN = -File.expand_path("..", __dir__) + BINARY = -File.join(PLUGIN, "node_modules", "prettier", "bin-prettier.js") + VERSION = -JSON.parse(File.read(File.join(PLUGIN, "package.json")))["version"] def self.run(args) - quoted = args.map { |arg| arg.start_with?('-') ? arg : "\"#{arg}\"" } - command = "node #{BINARY} --plugin \"#{PLUGIN}\" #{quoted.join(' ')}" + quoted = args.map { |arg| arg.start_with?("-") ? arg : "\"#{arg}\"" } + command = "node #{BINARY} --plugin \"#{PLUGIN}\" #{quoted.join(" ")}" stdout, stderr, status = - Open3.capture3({ 'RBPRETTIER' => '1' }, command, stdin_data: STDIN) + Open3.capture3({ "RBPRETTIER" => "1" }, command, stdin_data: STDIN) STDOUT.puts(stdout) # If we completed successfully, then just exit out. @@ -30,7 +30,7 @@ def self.run(args) If you installed this dependency through git instead of from rubygems, it does not install the necessary files by default. To fix this you can either install them yourself by cd-ing into the directory where this gem - is located (#{File.expand_path('..', __dir__)}) and running: + is located (#{File.expand_path("..", __dir__)}) and running: `yarn && yarn prepublishOnly` or diff --git a/lib/prettier/rake/task.rb b/lib/prettier/rake/task.rb index a5591297..35932601 100644 --- a/lib/prettier/rake/task.rb +++ b/lib/prettier/rake/task.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'rake' -require 'rake/tasklib' +require "rake" +require "rake/tasklib" module Prettier module Rake @@ -35,7 +35,7 @@ class Task < ::Rake::TaskLib def initialize(name = :prettier) @name = name @write = true - @source_files = 'lib/**/*.rb' + @source_files = "lib/**/*.rb" yield self if block_given? define_task @@ -44,12 +44,12 @@ def initialize(name = :prettier) private def define_task - desc 'Runs prettier over source files' + desc "Runs prettier over source files" task(name) { run_task } end def run_task - Prettier.run([('--write' if write), source_files].compact) + Prettier.run([("--write" if write), source_files].compact) exit($?.exitstatus) if $?&.exited? end end diff --git a/prettier.gemspec b/prettier.gemspec index 3e4931d9..7ad78393 100644 --- a/prettier.gemspec +++ b/prettier.gemspec @@ -1,38 +1,38 @@ # frozen_string_literal: true -require 'json' unless defined?(JSON) -package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) +require "json" unless defined?(JSON) +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) Gem::Specification.new do |spec| - spec.name = 'prettier' - spec.version = package['version'] - spec.authors = [package['author']] + spec.name = "prettier" + spec.version = package["version"] + spec.authors = [package["author"]] - spec.summary = package['description'] - spec.homepage = package['homepage'] - spec.license = package['license'] + spec.summary = package["description"] + spec.homepage = package["homepage"] + spec.license = package["license"] spec.files = Dir.chdir(__dir__) do %w[LICENSE bin/console package.json rubocop.yml] + - Dir['{{exe,lib,dist}/**/*,*.md}'] + + Dir["{{exe,lib,dist}/**/*,*.md}"] + Dir[ - 'node_modules/prettier/{package.json,index.js,cli.js,doc.js,bin-prettier.js,third-party.js,parser-*.js}' + "node_modules/prettier/{package.json,index.js,cli.js,doc.js,bin-prettier.js,third-party.js,parser-*.js}" ] end - spec.required_ruby_version = '>= 2.7.3' + spec.required_ruby_version = ">= 2.7.3" - spec.bindir = 'exe' - spec.executables = 'rbprettier' + spec.bindir = "exe" + spec.executables = "rbprettier" spec.require_paths = %w[lib] - spec.add_dependency 'syntax_tree' - spec.add_dependency 'syntax_tree-haml' - spec.add_dependency 'syntax_tree-rbs' - spec.add_dependency 'rbs', '~> 2' + spec.add_dependency "syntax_tree" + spec.add_dependency "syntax_tree-haml" + spec.add_dependency "syntax_tree-rbs" + spec.add_dependency "rbs", "~> 2" - spec.add_development_dependency 'bundler' - spec.add_development_dependency 'minitest' - spec.add_development_dependency 'rake' + spec.add_development_dependency "bundler" + spec.add_development_dependency "minitest" + spec.add_development_dependency "rake" end diff --git a/src/haml/parser.rb b/src/haml/parser.rb deleted file mode 100644 index cd8d9796..00000000 --- a/src/haml/parser.rb +++ /dev/null @@ -1,143 +0,0 @@ -# frozen_string_literal: true - -require 'ripper' - -begin - require 'haml' -rescue LoadError - # If we can't load the haml gem, then we're going to provide a shim parser - # that will warn and bail out. - class Prettier::HAMLParser - def self.parse(text) - warn( - 'The `haml` gem could not be loaded. Please ensure you have it ' \ - 'installed and that it is available in the gem path.' - ) - - false - end - end - - return -end - -class Haml::Parser::ParseNode - class DeepAttributeParser - def parse(string) - Haml::AttributeParser.available? ? parse_value(string) : string - end - - private - - def literal(string, level) - level == 0 ? string : "&#{string}" - end - - def parse_value(string, level = 0) - response = Ripper.sexp(string) - return literal(string, level) unless response - - case response[1][0][0] - when :hash - hash = Haml::AttributeParser.parse(string) - - if hash - # Explicitly not using Enumerable#to_h here to support Ruby 2.5 - hash.each_with_object({}) do |(key, value), response| - response[key] = parse_value(value, level + 1) - end - else - literal(string, level) - end - when :string_literal - string[1...-1] - else - literal(string, level) - end - end - end - - ESCAPE = /Haml::Helpers.html_escape\(\((.+)\)\)/.freeze - - # If a node comes in as the plain type but starts with one of the special - # characters that haml parses, then we need to escape it with a \ when - # printing. So here we make a regexp pattern to check if the node needs to be - # escaped. - special_chars = - Haml::Parser::SPECIAL_CHARACTERS.map { |char| Regexp.escape(char) } - - SPECIAL_START = /\A(?:#{special_chars.join('|')})/ - - def as_json - case type - when :comment, :doctype, :silent_script - to_h.tap do |json| - json.delete(:parent) - json[:children] = children.map(&:as_json) - end - when :filter, :haml_comment - to_h.tap { |json| json.delete(:parent) } - when :plain - to_h.tap do |json| - json.delete(:parent) - json[:children] = children.map(&:as_json) - - text = json[:value][:text] - json[:value][:text] = "\\#{text}" if text.match?(SPECIAL_START) - end - when :root - to_h.tap do |json| - json[:children] = children.map(&:as_json) - - # We need this information in the printer to know how to lay out - # multi-line attributes. - json[:supports_multiline] = - Gem::Version.new(Haml::VERSION) >= Gem::Version.new('5.2') - end - when :script - to_h.tap do |json| - json.delete(:parent) - json[:children] = children.map(&:as_json) - - if json[:value][:text].match?(ESCAPE) - json[:value][:text].gsub!(ESCAPE) { $1 } - json[:value].merge!(escape_html: 'escape_html', interpolate: true) - end - end - when :tag - to_h.tap do |json| - json.delete(:parent) - - # For some reason this is actually using a symbol to represent a null - # object ref instead of nil itself, so just replacing it here for - # simplicity in the printer - json[:value][:object_ref] = nil if json[:value][:object_ref] == :nil - - # Get a reference to the dynamic attributes hash - dynamic_attributes = value[:dynamic_attributes].to_h - - # If we have any in the old style, then we're going to pass it through - # the deep attribute parser filter. - if dynamic_attributes[:old] - dynamic_attributes[:old] = - DeepAttributeParser.new.parse(dynamic_attributes[:old]) - end - - json.merge!( - children: children.map(&:as_json), - value: value.merge(dynamic_attributes: dynamic_attributes) - ) - end - else - raise ArgumentError, "Unsupported type: #{type}" - end - end -end - -module Prettier - class HAMLParser - def self.parse(source) - Haml::Parser.new({}).call(source).as_json - end - end -end diff --git a/src/haml/printer.ts b/src/haml/printer.ts deleted file mode 100644 index 78aa4c4e..00000000 --- a/src/haml/printer.ts +++ /dev/null @@ -1,427 +0,0 @@ -import type { Plugin, HAML } from "../types"; -import prettier from "../prettier"; -import embed from "./embed"; - -const { - align, - fill, - group, - hardline, - ifBreak, - indent, - join, - line, - makeString, - softline -} = prettier; - -const docTypes = { - basic: "Basic", - frameset: "Frameset", - mobile: "Mobile", - rdfa: "RDFa", - strict: "Strict", - xml: "XML" -} as const; - -const docVersions = ["1.1", "5"]; - -// Prints out a hash key according to the configured prettier options. -function printHashKey(key: string, opts: Plugin.Options) { - let quoted = key; - const joiner = opts.rubyHashLabel ? ":" : " =>"; - - if (key.includes(":") || key.includes("-") || key.includes("@")) { - const quote = opts.rubySingleQuote ? "'" : '"'; - quoted = `${quote}${key}${quote}`; - } - - return `${opts.rubyHashLabel ? "" : ":"}${quoted}${joiner}`; -} - -// Prints out the value inside of a hash key-value pair according to the -// configured prettier options. -function printHashValue(value: string | number, opts: Plugin.Options) { - if (typeof value !== "string") { - return value.toString(); - } - - // This is a very special syntax created by the parser to let us know that - // this should be printed literally instead of as a string. - if (value.startsWith("&")) { - return value.slice(1); - } - - const quote = - opts.rubySingleQuote && !value.includes("#{") && !value.includes("'") - ? "'" - : '"'; - - return makeString(value, quote); -} - -// This will print an attributes object to a Doc node. It handles nesting on -// multiple levels and will print out according to whether or not the version of -// HAML being used supports multi-line attributes. -function printAttributes( - object: HAML.TagAttrs, - opts: Plugin.Options & { supportsMultiline: boolean; headerLength: number }, - level = 0 -) { - if (typeof object !== "object") { - return printHashValue(object, opts); - } - - const boundary = level === 0 ? softline : line; - const parts: Plugin.Doc[] = Object.keys(object).map((key) => [ - printHashKey(key, opts), - " ", - printAttributes(object[key], opts, level + 1) - ]); - - // If we have support for multi-line attributes laid out like a regular hash, - // then we print them that way here. - if (opts.supportsMultiline) { - return group([ - "{", - indent(group([boundary, join([",", line], parts)])), - boundary, - "}" - ]); - } - - // Otherwise, if we only have one attribute, then just print it inline - // regardless of how long it is. - if (parts.length === 0) { - return group(["{", parts[0], "}"]); - } - - // Otherwise, depending on how long the line is it will split the content into - // multi-line attributes that old Haml understands. - return group([ - "{", - parts[0], - ",", - align(opts.headerLength + 1, [line, join([",", line], parts.slice(1))]), - "}" - ]); -} - -// A utility function used in a silent script that is meant to determine if a -// child node is a continuation of a parent node (as in a when clause within a -// case statement or an else clause within an if). -function isContinuation( - parentNode: HAML.SilentScript, - childNode: HAML.AnyNode -) { - if (childNode.type !== "silent_script") { - return false; - } - - const parent = parentNode.value.keyword; - const child = childNode.value.keyword; - - return ( - (parent === "case" && ["when", "else"].includes(child)) || - (["if", "unless"].includes(parent) && ["elsif", "else"].includes(child)) - ); -} - -const printer: Plugin.PrinterConfig = { - embed, - // This is our printer's main print function that will switch on the type of - // node and print it out by returning a Doc tree. - print(path, opts, print) { - const node = path.getValue(); - - switch (node.type) { - // https://haml.info/docs/yardoc/file.REFERENCE.html#html-comments- - case "comment": { - const { value } = node; - const parts = ["/"]; - - if (value.revealed) { - parts.push("!"); - } - - if (value.conditional) { - parts.push(value.conditional); - } else if (value.text) { - parts.push(" ", value.text); - } - - return printWithChildren(node, group(parts)); - } - // https://haml.info/docs/yardoc/file.REFERENCE.html#doctype- - case "doctype": { - const { value } = node; - const parts = ["!!!"]; - - if (value.type in docTypes) { - parts.push(docTypes[value.type as keyof typeof docTypes]); - } else if (value.version && docVersions.includes(value.version)) { - parts.push(value.version); - } else { - parts.push(value.type); - } - - if (value.encoding) { - parts.push(value.encoding); - } - - return group(join(" ", parts)); - } - // https://haml.info/docs/yardoc/file.REFERENCE.html#filters - case "filter": - return group([ - ":", - node.value.name, - indent([hardline, join(hardline, node.value.text.trim().split("\n"))]) - ]); - // https://haml.info/docs/yardoc/file.REFERENCE.html#haml-comments-- - case "haml_comment": { - const { value } = node; - const parts: Plugin.Doc[] = ["-#"]; - - if (value.text) { - if (opts.originalText.split("\n")[node.line - 1].trim() === "-#") { - const lines = value.text.trim().split("\n"); - - parts.push(indent([hardline, join(hardline, lines)])); - } else { - parts.push(" ", value.text.trim()); - } - } - - return parts; - } - // https://haml.info/docs/yardoc/file.REFERENCE.html#plain-text - case "plain": - return node.value.text; - // The root node in the AST that we build in the parser. - case "root": { - const nodePath = path as Plugin.Path; - - return [join(hardline, nodePath.map(print, "children")), hardline]; - } - // https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby - case "script": { - const { value } = node; - const parts = []; - - if (value.escape_html) { - parts.unshift("&"); - } - - if (value.preserve) { - parts.push("~"); - } else if (!value.interpolate) { - parts.push("="); - } - - if (value.escape_html && !value.preserve && value.interpolate) { - parts.push(" ", value.text.trim().slice(1, -1)); - } else { - parts.push(" ", value.text.trim()); - } - - return printWithChildren(node, group(parts)); - } - // https://haml.info/docs/yardoc/file.REFERENCE.html#running-ruby-- - case "silent_script": { - const parts: Plugin.Doc[] = [`- ${node.value.text.trim()}`]; - - if (node.children.length > 0) { - const nodePath = path as Plugin.Path; - - parts.push( - nodePath.map((childPath) => { - const child = childPath.getValue(); - const concated = [hardline, print(childPath)]; - - return isContinuation(node, child) ? concated : indent(concated); - }, "children") - ); - } - - return group(parts); - } - // https://haml.info/docs/yardoc/file.REFERENCE.html#element-name- - case "tag": { - const { value } = node; - const { attributes, dynamic_attributes } = value; - const parts = []; - - // If we have a tag that isn't a div, then we need to print out that - // name of that tag first. If it is a div, first we'll check if there - // are any other things that would force us to print out the div - // explicitly, and otherwise we'll leave it off. - if (value.name !== "div") { - parts.push(`%${value.name}`); - } - - // If we have a class attribute, then we're going to print that here - // using the special class syntax. - if (attributes.class) { - parts.push(`.${attributes.class.replace(/ /g, ".")}`); - } - - // If we have an id attribute, then we're going to print that here using - // the special id syntax. - if (attributes.id) { - parts.push(`#${attributes.id}`); - } - - // If we're using dynamic attributes on this tag, then they come in as a - // string that looks like the output of Hash#inspect from Ruby. So here - // we're going to split it all up and print it out nicely. - if (dynamic_attributes.new) { - const docs: Plugin.Doc[] = []; - - dynamic_attributes.new - .slice(1, -2) - .split(",") - .forEach((pair, index) => { - if (index !== 0) { - docs.push(line); - } - docs.push(join("=", pair.slice(1).split('" => '))); - }); - - parts.push( - group(["(", align(parts.join("").length + 1, fill(docs)), ")"]) - ); - } - - // If there are any static attributes that are not class or id (because - // we already took care of those), then we're going to print them out - // here. - const staticAttributes = Object.keys(attributes).filter( - (name) => !["class", "id"].includes(name) - ); - - if (staticAttributes.length > 0) { - const docs = staticAttributes.reduce((accum, key) => { - const doc = `${printHashKey(key, opts)} ${printHashValue( - attributes[key], - opts - )}`; - - return accum.length === 0 ? [doc] : [...accum, ",", line, doc]; - }, [] as Plugin.Doc[]); - - parts.push( - group(["{", align(parts.join("").length + 1, fill(docs)), "}"]) - ); - } - - // If there are dynamic attributes that don't use the newer syntax, then - // we're going to print them out here. - if (dynamic_attributes.old) { - if (parts.length === 0) { - parts.push("%div"); - } - - if (typeof dynamic_attributes.old === "string") { - parts.push(dynamic_attributes.old); - } else { - // This is kind of a total hack in that I don't think you're - // really supposed to directly use `path.stack`, but it's the - // easiest way to get the root node without having to know how - // many levels deep we are. - const root = path.stack[0] as HAML.Root; - - parts.push( - printAttributes(dynamic_attributes.old, { - ...opts, - supportsMultiline: root.supports_multiline, - headerLength: parts.join("").length - }) - ); - } - } - - // https://haml.info/docs/yardoc/file.REFERENCE.html#object-reference- - if (value.object_ref) { - if (parts.length === 0) { - parts.push("%div"); - } - parts.push(value.object_ref); - } - - // https://haml.info/docs/yardoc/file.REFERENCE.html#whitespace-removal--and- - if (value.nuke_outer_whitespace) { - parts.push(">"); - } - - if (value.nuke_inner_whitespace) { - parts.push("<"); - } - - // https://haml.info/docs/yardoc/file.REFERENCE.html#empty-void-tags- - if (value.self_closing) { - parts.push("/"); - } - - if (value.value) { - let contents: Plugin.Doc[]; - - if (value.parse && value.value.match(/#[{$@]/)) { - // There's a weird case here where if the value includes - // interpolation and it's marked as { parse: true }, then we don't - // actually want the = prefix, and we want to remove extra escaping. - contents = [ - ifBreak("", " "), - value.value.slice(1, -1).replace(/\\"/g, '"') - ]; - } else if (value.parse) { - contents = ["= ", value.value]; - } else { - contents = [ifBreak("", " "), value.value]; - } - - return printWithChildren( - node, - group([group(parts), indent([softline, ...contents])]) - ); - } - - // In case none of the other if statements have matched and we're - // printing a div, we need to explicitly add it back into the array. - if (parts.length === 0 && value.name === "div") { - parts.push("%div"); - } - - return printWithChildren(node, group(parts)); - } - default: - throw new Error(`Unsupported node encountered: ${(node as any).type}`); - } - - // It's common to a couple of nodes to attach nested child nodes on the - // children property. This utility prints them out grouped together with - // their parent node docs. - function printWithChildren( - node: HAML.Comment | HAML.Script | HAML.Tag, - docs: Plugin.Doc - ) { - if (node.children.length === 0) { - return docs; - } - - const nodePath = path as Plugin.Path; - - return group([ - docs, - indent([hardline, join(hardline, nodePath.map(print, "children"))]) - ]); - } - }, - // This function handles adding the format pragma to a source string. This is - // an optional workflow for incremental adoption. - insertPragma(text) { - return `-# @format${text.startsWith("-#") ? "\n" : "\n\n"}${text}`; - } -}; - -export default printer; diff --git a/src/plugin.ts b/src/plugin.ts index 14531719..4a6ff5ab 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -2,11 +2,11 @@ import type { Plugin, SupportLanguage } from "prettier"; import parseSync from "./parseSync"; interface ExtendedSupportLanguage extends SupportLanguage { - interpreters?: string[] + interpreters?: string[]; } interface ExtendedPlugin extends Omit { - languages: ExtendedSupportLanguage[] + languages: ExtendedSupportLanguage[]; } /* @@ -142,7 +142,7 @@ const plugin: ExtendedPlugin = { }, insertPragma(text) { return `# @format${text.startsWith("#") ? "\n" : "\n\n"}${text}`; - }, + } }, rbs: { print(path) { diff --git a/src/ruby/nodes/blocks.ts b/src/ruby/nodes/blocks.ts deleted file mode 100644 index 27f1ec2e..00000000 --- a/src/ruby/nodes/blocks.ts +++ /dev/null @@ -1,131 +0,0 @@ -import type { Plugin, Ruby } from "../../types"; -import prettier from "../../prettier"; -import { hasAncestor } from "../../utils"; - -const { breakParent, group, ifBreak, indent, join, removeLines, softline } = - prettier; - -export const printBlockVar: Plugin.Printer = ( - path, - opts, - print -) => { - const parts = ["|", removeLines(path.call(print, "params"))]; - - // The second part of this node is a list of optional block-local variables - if (path.getValue().locals.length > 0) { - parts.push("; ", join(", ", path.map(print, "locals"))); - } - - parts.push("| "); - return parts; -}; - -// You have to go through the main print function if you could potentially have -// comments attached. So we're doing this weird reflection on the printed docs -// to retroactively change the printed keyword depending on if we're using -// braces or not. Ideally we wouldn't do this, we would instead do this -// reflection in the child printer, but this keeps the logic to just this file -// and contains it, so keeping it here for now. -function printBlockBegin( - path: Plugin.Path, - print: Plugin.Print, - useBraces: boolean -) { - let docs = print(path); - const doc = useBraces && !path.getValue().comments ? "{" : "do"; - - if (Array.isArray(docs)) { - docs[1] = doc; - } else { - docs = doc; - } - - return docs; -} - -type Block = Ruby.BraceBlock | Ruby.DoBlock; - -function printBlock(braces: boolean): Plugin.Printer { - return function printBlockWithBraces(path, opts, print) { - const node = path.getValue(); - let stmts: Ruby.AnyNode[]; - - if (node.type === "brace_block") { - stmts = node.stmts.body; - } else if ((node as any).bodystmt.type === "statements") { - // This is here to fix an issue in JRuby where it doesn't correctly - // support rescue/else/ensure inside blocks. - stmts = (node as any).bodystmt.body; - } else { - stmts = node.bodystmt.stmts.body; - } - - let doBlockBody: Plugin.Doc = ""; - if ( - stmts.length !== 1 || - stmts[0].type !== "void_stmt" || - stmts[0].comments - ) { - doBlockBody = indent([ - softline, - path.call(print, node.type === "brace_block" ? "stmts" : "bodystmt") - ]); - } - - // If this block is nested underneath a command or command_call node, then - // we can't use `do...end` because that will get associated with the parent - // node as opposed to the current node (because of the difference in - // operator precedence). Instead, we still use a multi-line format but - // switch to using braces instead. - const useBraces = braces && hasAncestor(path, ["command", "command_call"]); - - const doBlock = [ - " ", - path.call( - (beginPath) => printBlockBegin(beginPath, print, useBraces), - node.type === "brace_block" ? "lbrace" : "keyword" - ), - node.block_var ? [" ", path.call(print, "block_var")] : "", - doBlockBody, - [softline, useBraces ? "}" : "end"] - ]; - - // We can hit this next pattern if within the block the only statement is a - // comment. - if ( - stmts.length === 1 && - stmts[0].type === "void_stmt" && - stmts[0].comments - ) { - return [breakParent, doBlock]; - } - - const blockReceiver = (path.getParentNode() as Ruby.MethodAddBlock).call; - - // If the parent node is a command node, then there are no parentheses - // around the arguments to that command, so we need to break the block - if (["command", "command_call"].includes(blockReceiver.type)) { - return [breakParent, doBlock]; - } - - const hasBody = stmts.some(({ type }) => type !== "void_stmt"); - const braceBlock = [ - " ", - path.call( - (beginPath) => printBlockBegin(beginPath, print, true), - node.type === "brace_block" ? "lbrace" : "keyword" - ), - hasBody || node.block_var ? " " : "", - node.block_var ? path.call(print, "block_var") : "", - path.call(print, node.type === "brace_block" ? "stmts" : "bodystmt"), - hasBody ? " " : "", - "}" - ]; - - return group(ifBreak(doBlock, braceBlock)); - }; -} - -export const printBraceBlock = printBlock(true); -export const printDoBlock = printBlock(false); diff --git a/src/server.rb b/src/server.rb index 8c813e8d..dfb08aa3 100644 --- a/src/server.rb +++ b/src/server.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true -require 'bundler/setup' -require 'socket' -require 'json' -require 'fileutils' -require 'open3' +require "bundler/setup" +require "socket" +require "json" +require "fileutils" +require "open3" -require 'syntax_tree' -require 'syntax_tree/haml' -require 'syntax_tree/rbs' +require "syntax_tree" +require "syntax_tree/haml" +require "syntax_tree/rbs" # Make sure we trap these signals to be sure we get the quit command coming from # the parent node process @@ -16,13 +16,13 @@ trap(:INT) { quit = true } trap(:TERM) { quit = true } -if Signal.list.key?('QUIT') && RUBY_ENGINE != 'jruby' +if Signal.list.key?("QUIT") && RUBY_ENGINE != "jruby" trap(:QUIT) { quit = true } end # The information variable stores the actual connection information, which will # either be an IP address and port or a path to a unix socket file. -information = '' +information = "" # The candidates array is a list of potential programs that could be used to # connect to our server. We'll run through them after the server starts to find @@ -32,7 +32,7 @@ if Gem.win_platform? # If we're on windows, we're going to start up a TCP server. The 0 here means # to bind to some available port. - server = TCPServer.new('127.0.0.1', 0) + server = TCPServer.new("127.0.0.1", 0) address = server.local_address # Ensure that we close the server when this process exits. @@ -54,7 +54,7 @@ end information = server.local_address.unix_path - candidates = ['nc -w 3 -U', 'ncat -w 3 -U'] + candidates = ["nc -w 3 -U", "ncat -w 3 -U"] end # This is the actual listening thread that will be acting as our server. We have @@ -68,23 +68,30 @@ # Start up a new thread that will handle each successive connection. Thread.new(server.accept_nonblock) do |socket| - parser, source = socket.read.split('|', 2) - - # First, ensure we get the right encoding for this string. - encoding = - source.each_line do |line| - # Skip past the first line if it contains a shebang so that we get - # to the encoding line. - next if line.start_with?("#!") - - # If the line isn't an encoding magic comment, we're going to - # default to UTF-8. - break "UTF-8" unless line.include?("coding") - - # Otherwise, we're going to use Ripper to try to extract out the - # encoding from the magic comment. - break Ripper.new(line).tap(&:parse).encoding + parser, source = socket.read.split("|", 2) + + # First, ensure we get the right encoding for this string. To do this + # we're going to find all of the comments at the top of the file first + # that contain comments. Then we'll pass them through ripper to find the + # encoding. + lines = [] + + source.each_line do |line| + if line.start_with?(/\s*#/) + lines << line + else + break end + end + + # We're going to default to UTF-8 in case we can't find the correct + # encoding. + encoding = "UTF-8" + comments = lines.join + + if comments.include?("coding") + encoding = Ripper.new(comments).tap(&:parse).encoding + end # Now that we have our guessed encoding, we're going to force it into # the string so that it gets parsed correctly. @@ -92,20 +99,20 @@ response = case parser - when 'ping' - 'pong' - when 'ruby' + when "ping" + "pong" + when "ruby" SyntaxTree.format(source) - when 'rbs' + when "rbs" SyntaxTree::RBS.format(source) - when 'haml' + when "haml" SyntaxTree::Haml.format(source) end if response socket.write(JSON.fast_generate(response)) else - socket.write('{ "error": true }') + socket.write("{ \"error\": true }") end rescue SyntaxTree::Parser::ParseError => error loc = { start: { line: error.lineno, column: error.column } } @@ -137,9 +144,9 @@ # We do not care about stderr here, so throw it away stdout, _stderr, status = - Open3.capture3("#{candidate} #{information}", stdin_data: 'ping') + Open3.capture3("#{candidate} #{information}", stdin_data: "ping") - candidate if JSON.parse(stdout) == 'pong' && status.exitstatus == 0 + candidate if JSON.parse(stdout) == "pong" && status.exitstatus == 0 rescue StandardError # We don't actually care if this fails, because we'll just skip that # connection option. @@ -155,7 +162,7 @@ # Default to running the netcat.js script that we ship with the plugin. It's a # good fallback as it will always work, but it is slower than the other options. -prefix ||= "node #{File.expand_path('netcat.js', __dir__)}" +prefix ||= "node #{File.expand_path("netcat.js", __dir__)}" # Write out our connection information to the file given as the first argument # to this script. diff --git a/src/utils/getChildNodes.ts b/src/utils/getChildNodes.ts deleted file mode 100644 index e6c771bd..00000000 --- a/src/utils/getChildNodes.ts +++ /dev/null @@ -1,326 +0,0 @@ -import type { Ruby } from "../types"; - -type AnyNode = Ruby.AnyNode | Ruby.VoidStmt; -type ChildNode = AnyNode | null; - -function throwBadNode(node: never): never; -function throwBadNode(node: AnyNode) { - throw new Error(`Unknown node ${node.type}`); -} - -function getChildNodes(node: AnyNode | Ruby.Comment): ChildNode[] { - switch (node.type) { - case "CHAR": - case "__end__": - case "backref": - case "backtick": - case "comment": - case "const": - case "cvar": - case "float": - case "gvar": - case "heredoc_beg": - case "ident": - case "imaginary": - case "int": - case "ivar": - case "kw": - case "label": - case "lbrace": - case "lparen": - case "op": - case "period": - case "rational": - case "tstring_content": - return []; - case "BEGIN": - return [node.lbrace, node.stmts]; - case "END": - return [node.lbrace, node.stmts]; - case "access_ctrl": - return [node.value]; - case "alias": - return [node.left, node.right]; - case "aref": - return [node.collection, node.index]; - case "aref_field": - return [node.collection, node.index]; - case "arg_paren": - return [node.args]; - case "args": - return node.parts; - case "args_add_block": - return [node.args, node.block]; - case "args_forward": - return []; - case "arg_star": - return [node.value]; - case "array": - return [node.cnts]; - case "aryptn": - return [node.constant, ...node.reqs, node.rest, ...node.posts]; - case "assign": - return [node.target, node.value]; - case "assoc": - return [node.key, node.value]; - case "assoc_splat": - return [node.value]; - case "assoclist_from_args": - return node.assocs; - case "bare_assoc_hash": - return node.assocs; - case "begin": - return [node.bodystmt]; - case "binary": - return [node.left, node.right]; - case "block_var": - return [node.params, ...node.locals]; - case "blockarg": - return [node.name]; - case "bodystmt": - return [node.stmts, node.rsc, node.els, node.ens]; - case "brace_block": - return [node.lbrace, node.block_var, node.stmts]; - case "break": - return [node.args]; - case "call": { - const childNodes: ChildNode[] = [node.receiver]; - - if (node.op !== "::") { - childNodes.push(node.op); - } - - if (node.message !== "call") { - childNodes.push(node.message); - } - - return childNodes; - } - case "case": - return [node.value, node.cons]; - case "class": - return [node.constant, node.superclass, node.bodystmt]; - case "command": - return [node.message, node.args]; - case "command_call": - return [node.receiver, node.message, node.args]; - case "const_path_field": - return [node.parent, node.constant]; - case "const_path_ref": - return [node.parent, node.constant]; - case "const_ref": - return [node.constant]; - case "def": - return [node.name, node.params, node.bodystmt]; - case "def_endless": - return [node.name, node.paren, node.stmt]; - case "defined": - return [node.value]; - case "defs": - return [node.target, node.op, node.name, node.params, node.bodystmt]; - case "do_block": - return [node.keyword, node.block_var, node.bodystmt]; - case "dot2": - return [node.left, node.right]; - case "dot3": - return [node.left, node.right]; - case "dyna_symbol": - return node.parts; - case "else": - return [node.stmts]; - case "elsif": - return [node.pred, node.stmts, node.cons]; - case "ensure": - return [node.keyword, node.stmts]; - case "excessed_comma": - return []; - case "fcall": - return [node.value]; - case "field": { - const childNodes: ChildNode[] = [node.parent]; - - if (node.op !== "::") { - childNodes.push(node.op); - } - - childNodes.push(node.name); - return childNodes; - } - case "fndptn": - return [node.constant, node.left, ...node.values, node.right]; - case "for": - return [node.index, node.collection, node.stmts]; - case "hash": - return [node.cnts]; - case "heredoc": - return [node.beging, ...node.parts]; - case "hshptn": { - const childNodes: ChildNode[] = [node.constant]; - - node.keywords.forEach(([key, value]) => { - childNodes.push(key, value); - }); - - childNodes.push(node.kwrest); - return childNodes; - } - case "if": - return [node.pred, node.stmts, node.cons]; - case "ifop": - return [node.pred, node.tthy, node.flsy]; - case "if_mod": - return [node.stmt, node.pred]; - case "in": - return [node.pattern, node.stmts, node.cons]; - case "kwrest_param": - return [node.name]; - case "lambda": - return [node.params, node.stmts]; - case "massign": - return [node.target, node.value]; - case "method_add_arg": - return [node.call, node.args]; - case "method_add_block": - return [node.call, node.block]; - case "mlhs": - return node.parts; - case "mlhs_paren": - return [node.cnts]; - case "module": - return [node.constant, node.bodystmt]; - case "mrhs": - return node.parts; - case "mrhs_add_star": - return [node.mrhs, node.star]; - case "mrhs_new_from_args": - return [node.args]; - case "next": - return [node.args]; - case "not": - return [node.value]; - case "opassign": - return [node.target, node.op, node.value]; - case "params": { - let childNodes: ChildNode[] = [...node.reqs]; - - node.opts.forEach(([key, value]) => { - childNodes.push(key, value); - }); - - childNodes.push(node.rest); - childNodes = childNodes.concat(node.posts); - - node.keywords.forEach(([key, value]) => { - childNodes.push(key); - - if (value) { - childNodes.push(value); - } - }); - - if (node.kwrest && node.kwrest !== "nil") { - childNodes.push(node.kwrest); - } - - if (node.block) { - childNodes.push(node.block); - } - - return childNodes; - } - case "paren": - return [node.lparen, node.cnts]; - case "program": - return [node.stmts]; - case "qsymbols": - return []; - case "qwords": - return []; - case "rassign": - return [node.value, node.op, node.pattern]; - case "redo": - return []; - case "regexp_literal": - return node.parts; - case "rescue": - return [node.extn, node.stmts, node.cons]; - case "rescue_ex": - return [node.extns, node.var]; - case "rescue_mod": - return [node.stmt, node.value]; - case "rest_param": - return [node.name]; - case "retry": - return []; - case "return": - return [node.args]; - case "return0": - return []; - case "sclass": - return [node.target, node.bodystmt]; - case "statements": - return node.body; - case "string_concat": - return [node.left, node.right]; - case "string_dvar": - return [node.var]; - case "string_embexpr": - return [node.stmts]; - case "string_literal": - return node.parts; - case "super": - return [node.args]; - case "symbol_literal": - return [node.value]; - case "symbols": - return []; - case "top_const_field": - return [node.constant]; - case "top_const_ref": - return [node.constant]; - case "unary": - return [node.value]; - case "undef": - return node.syms; - case "unless": - return [node.pred, node.stmts, node.cons]; - case "unless_mod": - return [node.stmt, node.pred]; - case "until": - return [node.pred, node.stmts]; - case "until_mod": - return [node.stmt, node.pred]; - case "var_alias": - return [node.left, node.right]; - case "var_field": - return [node.value]; - case "var_ref": - return [node.value]; - case "vcall": - return [node.value]; - case "void_stmt": - return []; - case "when": - return [node.args, node.stmts, node.cons]; - case "while": - return [node.pred, node.stmts]; - case "while_mod": - return [node.stmt, node.pred]; - case "word": - return node.parts; - case "words": - return []; - case "xstring_literal": - return node.parts; - case "yield": - return [node.args]; - case "yield0": - return []; - case "zsuper": - return []; - default: - throwBadNode(node); - } -} - -export default getChildNodes; diff --git a/test/js/rbs/rbs.test.ts b/test/js/rbs/rbs.test.ts index d1a8918c..a1e6dd86 100644 --- a/test/js/rbs/rbs.test.ts +++ b/test/js/rbs/rbs.test.ts @@ -235,11 +235,15 @@ describe("rbs", () => { }); test("unescapes double quotes when using single quotes", () => { - expect(rbs(`T: "super \\" duper"`)).toChangeFormat(`T: "super \\" duper"`); + expect(rbs(`T: "super \\" duper"`)).toChangeFormat( + `T: "super \\" duper"` + ); }); test("unescapes single quotes when using double quotes", () => { - expect(rbs(`T: 'super \\' duper'`)).toChangeFormat(`T: 'super \\' duper'`); + expect(rbs(`T: 'super \\' duper'`)).toChangeFormat( + `T: 'super \\' duper'` + ); }); test("maintains escape sequences when using double quotes", () => { diff --git a/test/js/setupTests.ts b/test/js/setupTests.ts index 818c2592..593ecb94 100644 --- a/test/js/setupTests.ts +++ b/test/js/setupTests.ts @@ -1,25 +1,18 @@ import prettier from "prettier"; -import type * as Prettier from "prettier"; import type { Code } from "./types"; import plugin from "../../src/plugin"; -type Config = Partial & { - printer: Omit & { printComment: () => any } -}>; - function normalize(code: Code) { const string = typeof code === "string" ? code : code.code; return string.replace(/\r?\n/g, "\n").trim(); } -function checkFormat(before: Code, after: Code, config: Config) { +function checkFormat(before: Code, after: Code) { const originalText = typeof before === "string" ? before : before.code; const formatted = prettier.format(originalText, { parser: typeof before === "string" ? "ruby" : before.parser, - originalText, - plugins: [plugin as any as string], - ...config + plugins: [plugin as any as string] }); const expected = normalize(after); @@ -32,11 +25,11 @@ function checkFormat(before: Code, after: Code, config: Config) { } expect.extend({ - toChangeFormat(before: Code, after: Code, config: Config = {}) { - return checkFormat(before, after, config); + toChangeFormat(before: Code, after: Code) { + return checkFormat(before, after); }, - toMatchFormat(before: Code, config: Config = {}) { - return checkFormat(before, before, config); + toMatchFormat(before: Code) { + return checkFormat(before, before); } }); @@ -45,8 +38,8 @@ declare global { namespace jest { // eslint-disable-next-line @typescript-eslint/no-unused-vars interface Matchers { - toChangeFormat(after: Code, config?: Config): CustomMatcherResult; - toMatchFormat(config?: Config): CustomMatcherResult; + toChangeFormat(after: Code): CustomMatcherResult; + toMatchFormat(): CustomMatcherResult; } } } diff --git a/test/rb/rake_test.rb b/test/rb/rake_test.rb index ab3335c3..65891544 100644 --- a/test/rb/rake_test.rb +++ b/test/rb/rake_test.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -require 'test_helper' -require 'prettier/rake/task' +require "test_helper" +require "prettier/rake/task" class RakeTest < Minitest::Test Invoke = Struct.new(:args) def test_task - source_files = '{app,config,lib}/**/*.rb' + source_files = "{app,config,lib}/**/*.rb" Prettier::Rake::Task.new do |t| t.name = :format t.write = true @@ -16,9 +16,9 @@ def test_task invoke = nil Prettier.stub(:run, ->(args) { invoke = Invoke.new(args) }) do - Rake::Task['format'].invoke + Rake::Task["format"].invoke end - assert_equal ['--write', source_files], invoke.args + assert_equal ["--write", source_files], invoke.args end end diff --git a/test/rb/test_helper.rb b/test/rb/test_helper.rb index 4b93f91d..f223f7f0 100644 --- a/test/rb/test_helper.rb +++ b/test/rb/test_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -$LOAD_PATH.unshift(File.expand_path('../../lib', __dir__)) +$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__)) -require 'prettier' -require 'minitest/autorun' +require "prettier" +require "minitest/autorun" diff --git a/test/rb/version_test.rb b/test/rb/version_test.rb index 9d55356e..289590be 100644 --- a/test/rb/version_test.rb +++ b/test/rb/version_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'test_helper' +require "test_helper" class VersionTest < Minitest::Test def test_version