diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 8adc1b45..fc02f2fe 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -1,26 +1,54 @@ -name: Github Pages (rdoc) -on: [push] +name: Deploy rdoc to GitHub Pages + +on: + push: + branches: + - main + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + jobs: - build-and-deploy: + # Build job + build: runs-on: ubuntu-latest steps: - - name: Checkout 🛎️ - uses: actions/checkout@master - - - name: Set up Ruby 💎 + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Pages + uses: actions/configure-pages@v2 + - name: Set up Ruby uses: ruby/setup-ruby@v1 with: bundler-cache: true ruby-version: '3.1' - - - name: Install rdoc and generate docs 🔧 + - name: Generate docs run: | gem install rdoc - rdoc --main README.md --op rdocs --exclude={Gemfile,Rakefile,"coverage/*","vendor/*","bin/*","test/*","tmp/*"} - cp -r doc rdocs/doc + rdoc --main README.md --op _site --exclude={Gemfile,Rakefile,"coverage/*","vendor/*","bin/*","test/*","tmp/*"} + cp -r doc _site/doc + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 - - name: Deploy 🚀 - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./rdocs + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9884a16b..07c2d8b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [Unreleased] +## [3.6.0] - 2022-09-19 + +### Added + +- [#158](https://github.com/ruby-syntax-tree/syntax_tree/pull/158) - Support the ability to pass `--ignore-files` to the CLI and the Rake tasks to ignore a certain pattern of files. + ## [3.5.0] - 2022-08-26 ### Added @@ -344,7 +350,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - 🎉 Initial release! 🎉 -[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.5.0...HEAD +[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.6.0...HEAD +[3.6.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.5.0...v3.6.0 [3.5.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.4.0...v3.5.0 [3.4.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.3.0...v3.4.0 [3.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.2.1...v3.3.0 diff --git a/Gemfile.lock b/Gemfile.lock index 1d69d297..6e5f3096 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - syntax_tree (3.5.0) + syntax_tree (3.6.0) prettier_print GEM @@ -19,7 +19,7 @@ GEM rake (13.0.6) regexp_parser (2.5.0) rexml (3.2.5) - rubocop (1.35.1) + rubocop (1.36.0) json (~> 2.3) parallel (~> 1.10) parser (>= 3.1.2.1) diff --git a/README.md b/README.md index 9f25b0e7..afb65843 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ It is built with only standard library dependencies. It additionally ships with - [textDocument/inlayHint](#textdocumentinlayhint) - [syntaxTree/visualizing](#syntaxtreevisualizing) - [Plugins](#plugins) - - [Configuration](#configuration) + - [Customization](#customization) - [Languages](#languages) - [Integration](#integration) - [Rake](#rake) @@ -83,7 +83,9 @@ bundle exec stree version ## CLI -Syntax Tree ships with the `stree` CLI, which can be used to inspect and manipulate Ruby code. Below are listed all of the commands built into the CLI that you can use. Note that for all commands that operate on files, you can also pass in content through STDIN. +Syntax Tree ships with the `stree` CLI, which can be used to inspect and manipulate Ruby code. Below are listed all of the commands built into the CLI that you can use. + +For many commands, file paths are accepted after the configuration options. For all of these commands, you can alternatively pass in content through STDIN or through the `-e` option to specify an inline script. ### ast @@ -233,6 +235,12 @@ To change the print width that you are writing with, specify the `--print-width` stree write --print-width=100 path/to/file.rb ``` +To ignore certain files from a glob (in order to make it easier to specify the filepaths), you can pass the `--ignore-files` option as an additional glob, as in: + +```sh +stree write --ignore-files='db/**/*.rb' '**/*.rb' +``` + ### Configuration Any of the above CLI commands can also read configuration options from a `.streerc` file in the directory where the commands are executed. @@ -473,11 +481,11 @@ The language server additionally includes this custom request to return a textua ## Plugins -You can register additional configuration and additional languages that can flow through the same CLI with Syntax Tree's plugin system. When invoking the CLI, you pass through the list of plugins with the `--plugins` options to the commands that accept them. They should be a comma-delimited list. When the CLI first starts, it will require the files corresponding to those names. +You can register additional customization and additional languages that can flow through the same CLI with Syntax Tree's plugin system. When invoking the CLI, you pass through the list of plugins with the `--plugins` options to the commands that accept them. They should be a comma-delimited list. When the CLI first starts, it will require the files corresponding to those names. -### Configuration +### Customization -To register additional configuration, define a file somewhere in your load path named `syntax_tree/my_plugin`. Then when invoking the CLI, you will pass `--plugins=my_plugin`. To require multiple, separate them by a comma. In this way, you can modify Syntax Tree however you would like. Some plugins ship with Syntax Tree itself. They are: +To register additional customization, define a file somewhere in your load path named `syntax_tree/my_plugin`. Then when invoking the CLI, you will pass `--plugins=my_plugin`. To require multiple, separate them by a comma. In this way, you can modify Syntax Tree however you would like. Some plugins ship with Syntax Tree itself. They are: * `plugin/single_quotes` - This will change all of your string literals to use single quotes instead of the default double quotes. * `plugin/trailing_comma` - This will put trailing commas into multiline array literals, hash literals, and method calls that can support trailing commas. @@ -500,9 +508,12 @@ In this case, whenever the CLI encounters a filepath that ends with the given ex Below are listed all of the "official" language plugins hosted under the same GitHub organization, which can be used as references for how to implement other plugins. +* [bf](https://github.com/ruby-syntax-tree/syntax_tree-bf) for the [brainf*** language](https://esolangs.org/wiki/Brainfuck). +* [css](https://github.com/ruby-syntax-tree/syntax_tree-css) for the [CSS stylesheet language](https://www.w3.org/Style/CSS/). * [haml](https://github.com/ruby-syntax-tree/syntax_tree-haml) for the [Haml template language](https://haml.info/). -* [json](https://github.com/ruby-syntax-tree/syntax_tree-json) for JSON. +* [json](https://github.com/ruby-syntax-tree/syntax_tree-json) for the [JSON notation language](https://www.json.org/). * [rbs](https://github.com/ruby-syntax-tree/syntax_tree-rbs) for the [RBS type language](https://github.com/ruby/rbs). +* [xml](https://github.com/ruby-syntax-tree/syntax_tree-xml) for the [XML markup language](https://www.w3.org/XML/). ## Integration @@ -538,6 +549,17 @@ SyntaxTree::Rake::WriteTask.new do |t| end ``` +#### `ignore_files` + +If you want to ignore certain file patterns when running the command, you can pass the `ignore_files` option. This will be checked with `File.fnmatch?` against each filepath that the command would be run against. For example: + +```ruby +SyntaxTree::Rake::WriteTask.new do |t| + t.source_files = "**/*.rb" + t.ignore_files = "db/**/*.rb" +end +``` + #### `print_width` If you want to use a different print width from the default (80), you can pass that to the `print_width` field, as in: diff --git a/lib/syntax_tree/basic_visitor.rb b/lib/syntax_tree/basic_visitor.rb index 9e6a84c1..34b7876e 100644 --- a/lib/syntax_tree/basic_visitor.rb +++ b/lib/syntax_tree/basic_visitor.rb @@ -35,7 +35,7 @@ def corrections # In some setups with Ruby you can turn off DidYouMean, so we're going to # respect that setting here. - if defined?(DidYouMean) && DidYouMean.method_defined?(:correct_error) + if defined?(DidYouMean.correct_error) DidYouMean.correct_error(VisitMethodError, self) end end diff --git a/lib/syntax_tree/cli.rb b/lib/syntax_tree/cli.rb index 2de20e78..a364cd34 100644 --- a/lib/syntax_tree/cli.rb +++ b/lib/syntax_tree/cli.rb @@ -51,13 +51,15 @@ def handler def source handler.read(filepath) end + + def writable? + File.writable?(filepath) + end end # An item of work that corresponds to a script content passed via the # command line. class ScriptItem - FILEPATH = :script - attr_reader :source def initialize(source) @@ -69,7 +71,30 @@ def handler end def filepath - FILEPATH + :script + end + + def writable? + false + end + end + + # An item of work that correspond to the content passed in via stdin. + class STDINItem + def handler + HANDLERS[".rb"] + end + + def filepath + :stdin + end + + def source + $stdin.read + end + + def writable? + false end end @@ -196,7 +221,7 @@ def run(item) source = item.source formatted = item.handler.format(source, options.print_width) - File.write(filepath, formatted) if item.filepath != :script + File.write(filepath, formatted) if item.writable? color = source == formatted ? Color.gray(filepath) : filepath delta = ((Time.now - start) * 1000).round @@ -258,9 +283,14 @@ def run(item) # responsible for parsing the list and then returning the file paths at the # end. class Options - attr_reader :plugins, :print_width, :scripts, :target_ruby_version + attr_reader :ignore_files, + :plugins, + :print_width, + :scripts, + :target_ruby_version def initialize(print_width: DEFAULT_PRINT_WIDTH) + @ignore_files = "" @plugins = [] @print_width = print_width @scripts = [] @@ -279,6 +309,13 @@ def parse(arguments) def parser OptionParser.new do |opts| + # If there is a glob specified to ignore, then we'll track that here. + # Any of the CLI commands that operate on filenames will then ignore + # this set of files. + opts.on("--ignore-files=GLOB") do |glob| + @ignore_files = glob.match(/\A'(.*)'\z/) ? $1 : glob + end + # If there are any plugins specified on the command line, then load # them by requiring them here. We do this by transforming something # like @@ -386,7 +423,7 @@ def run(argv) return 1 end - # If we're not reading from stdin and the user didn't supply and + # If we're not reading from stdin and the user didn't supply any # filepaths to be read, then we exit with the usage message. if $stdin.tty? && arguments.empty? && options.scripts.empty? warn(HELP) @@ -403,12 +440,16 @@ def run(argv) Dir .glob(pattern) .each do |filepath| - queue << FileItem.new(filepath) if File.file?(filepath) + if File.readable?(filepath) && + !File.fnmatch?(options.ignore_files, filepath) + queue << FileItem.new(filepath) + end end end + options.scripts.each { |script| queue << ScriptItem.new(script) } else - queue << ScriptItem.new($stdin.read) + queue << STDINItem.new end # At the end, we're going to return whether or not this worker ever diff --git a/lib/syntax_tree/rake/check_task.rb b/lib/syntax_tree/rake/check_task.rb index 48247718..5b441a5b 100644 --- a/lib/syntax_tree/rake/check_task.rb +++ b/lib/syntax_tree/rake/check_task.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true -require "rake" -require "rake/tasklib" - -require "syntax_tree" -require "syntax_tree/cli" +require_relative "task" module SyntaxTree module Rake @@ -12,74 +8,21 @@ module Rake # # Example: # - # require 'syntax_tree/rake/check_task' + # require "syntax_tree/rake/check_task" # # SyntaxTree::Rake::CheckTask.new do |t| - # t.source_files = '{app,config,lib}/**/*.rb' + # t.source_files = "{app,config,lib}/**/*.rb" # end # # This will create task that can be run with: # - # rake stree_check + # rake stree:check # - class CheckTask < ::Rake::TaskLib - # Name of the task. - # Defaults to :"stree:check". - attr_accessor :name - - # Glob pattern to match source files. - # Defaults to 'lib/**/*.rb'. - attr_accessor :source_files - - # The set of plugins to require. - # Defaults to []. - attr_accessor :plugins - - # Max line length. - # Defaults to 80. - attr_accessor :print_width - - # The target Ruby version to use for formatting. - # Defaults to Gem::Version.new(RUBY_VERSION). - attr_accessor :target_ruby_version - - def initialize( - name = :"stree:check", - source_files = ::Rake::FileList["lib/**/*.rb"], - plugins = [], - print_width = DEFAULT_PRINT_WIDTH, - target_ruby_version = Gem::Version.new(RUBY_VERSION) - ) - @name = name - @source_files = source_files - @plugins = plugins - @print_width = print_width - @target_ruby_version = target_ruby_version - - yield self if block_given? - define_task - end - + class CheckTask < Task private - def define_task - desc "Runs `stree check` over source files" - task(name) { run_task } - end - - def run_task - arguments = ["check"] - arguments << "--plugins=#{plugins.join(",")}" if plugins.any? - - if print_width != DEFAULT_PRINT_WIDTH - arguments << "--print-width=#{print_width}" - end - - if target_ruby_version != Gem::Version.new(RUBY_VERSION) - arguments << "--target-ruby-version=#{target_ruby_version}" - end - - SyntaxTree::CLI.run(arguments + Array(source_files)) + def command + "check" end end end diff --git a/lib/syntax_tree/rake/task.rb b/lib/syntax_tree/rake/task.rb new file mode 100644 index 00000000..ea228e8f --- /dev/null +++ b/lib/syntax_tree/rake/task.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require "rake" +require "rake/tasklib" + +require "syntax_tree" +require "syntax_tree/cli" + +module SyntaxTree + module Rake + # A parent Rake task that runs a command on a set of source files. + class Task < ::Rake::TaskLib + # Name of the task. + attr_accessor :name + + # Glob pattern to match source files. + # Defaults to 'lib/**/*.rb'. + attr_accessor :source_files + + # The set of plugins to require. + # Defaults to []. + attr_accessor :plugins + + # Max line length. + # Defaults to 80. + attr_accessor :print_width + + # The target Ruby version to use for formatting. + # Defaults to Gem::Version.new(RUBY_VERSION). + attr_accessor :target_ruby_version + + # Glob pattern to ignore source files. + # Defaults to ''. + attr_accessor :ignore_files + + def initialize( + name = :"stree:#{command}", + source_files = ::Rake::FileList["lib/**/*.rb"], + plugins = [], + print_width = DEFAULT_PRINT_WIDTH, + target_ruby_version = Gem::Version.new(RUBY_VERSION), + ignore_files = "" + ) + @name = name + @source_files = source_files + @plugins = plugins + @print_width = print_width + @target_ruby_version = target_ruby_version + @ignore_files = ignore_files + + yield self if block_given? + define_task + end + + private + + # This method needs to be overridden in the child tasks. + def command + raise NotImplementedError + end + + def define_task + desc "Runs `stree #{command}` over source files" + task(name) { run_task } + end + + def run_task + arguments = [command] + arguments << "--plugins=#{plugins.join(",")}" if plugins.any? + + if print_width != DEFAULT_PRINT_WIDTH + arguments << "--print-width=#{print_width}" + end + + if target_ruby_version != Gem::Version.new(RUBY_VERSION) + arguments << "--target-ruby-version=#{target_ruby_version}" + end + + arguments << "--ignore-files=#{ignore_files}" if ignore_files != "" + + SyntaxTree::CLI.run(arguments + Array(source_files)) + end + end + end +end diff --git a/lib/syntax_tree/rake/write_task.rb b/lib/syntax_tree/rake/write_task.rb index 69ce97e7..8037792e 100644 --- a/lib/syntax_tree/rake/write_task.rb +++ b/lib/syntax_tree/rake/write_task.rb @@ -1,85 +1,28 @@ # frozen_string_literal: true -require "rake" -require "rake/tasklib" - -require "syntax_tree" -require "syntax_tree/cli" +require_relative "task" module SyntaxTree module Rake - # A Rake task that runs format on a set of source files. + # A Rake task that runs write on a set of source files. # # Example: # - # require 'syntax_tree/rake/write_task' + # require "syntax_tree/rake/write_task" # # SyntaxTree::Rake::WriteTask.new do |t| - # t.source_files = '{app,config,lib}/**/*.rb' + # t.source_files = "{app,config,lib}/**/*.rb" # end # # This will create task that can be run with: # - # rake stree_write + # rake stree:write # - class WriteTask < ::Rake::TaskLib - # Name of the task. - # Defaults to :"stree:write". - attr_accessor :name - - # Glob pattern to match source files. - # Defaults to 'lib/**/*.rb'. - attr_accessor :source_files - - # The set of plugins to require. - # Defaults to []. - attr_accessor :plugins - - # Max line length. - # Defaults to 80. - attr_accessor :print_width - - # The target Ruby version to use for formatting. - # Defaults to Gem::Version.new(RUBY_VERSION). - attr_accessor :target_ruby_version - - def initialize( - name = :"stree:write", - source_files = ::Rake::FileList["lib/**/*.rb"], - plugins = [], - print_width = DEFAULT_PRINT_WIDTH, - target_ruby_version = Gem::Version.new(RUBY_VERSION) - ) - @name = name - @source_files = source_files - @plugins = plugins - @print_width = print_width - @target_ruby_version = target_ruby_version - - yield self if block_given? - define_task - end - + class WriteTask < Task private - def define_task - desc "Runs `stree write` over source files" - task(name) { run_task } - end - - def run_task - arguments = ["write"] - arguments << "--plugins=#{plugins.join(",")}" if plugins.any? - - if print_width != DEFAULT_PRINT_WIDTH - arguments << "--print-width=#{print_width}" - end - - if target_ruby_version != Gem::Version.new(RUBY_VERSION) - arguments << "--target-ruby-version=#{target_ruby_version}" - end - - SyntaxTree::CLI.run(arguments + Array(source_files)) + def command + "write" end end end diff --git a/lib/syntax_tree/version.rb b/lib/syntax_tree/version.rb index 42aa2b6c..5883bbdf 100644 --- a/lib/syntax_tree/version.rb +++ b/lib/syntax_tree/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SyntaxTree - VERSION = "3.5.0" + VERSION = "3.6.0" end diff --git a/test/cli_test.rb b/test/cli_test.rb index aec8f820..de09b093 100644 --- a/test/cli_test.rb +++ b/test/cli_test.rb @@ -32,6 +32,12 @@ def test_ast assert_includes(result.stdio, "ident \"test\"") end + def test_ast_ignore + result = run_cli("ast", "--ignore-files='*/test*'") + assert_equal(0, result.status) + assert_empty(result.stdio) + end + def test_ast_syntax_error result = run_cli("ast", contents: "foo\n<>\nbar\n") assert_includes(result.stderr, "syntax error") @@ -117,7 +123,7 @@ def test_help_default end def test_no_arguments - $stdin.stub(:tty?, true) do + with_tty do *, stderr = capture_io { SyntaxTree::CLI.run(["check"]) } assert_includes(stderr, "stree help") end @@ -134,13 +140,17 @@ def test_no_arguments_no_tty end def test_inline_script - stdio, = capture_io { SyntaxTree::CLI.run(%w[format -e 1+1]) } - assert_equal("1 + 1\n", stdio) + with_tty do + stdio, = capture_io { SyntaxTree::CLI.run(%w[format -e 1+1]) } + assert_equal("1 + 1\n", stdio) + end end def test_multiple_inline_scripts - stdio, = capture_io { SyntaxTree::CLI.run(%w[format -e 1+1 -e 2+2]) } - assert_equal("1 + 1\n2 + 2\n", stdio) + with_tty do + stdio, = capture_io { SyntaxTree::CLI.run(%w[format -e 1+1 -e 2+2]) } + assert_equal("1 + 1\n2 + 2\n", stdio) + end end def test_generic_error @@ -241,8 +251,10 @@ def run_cli(command, *args, contents: :default) status = nil stdio, stderr = - capture_io do - status = SyntaxTree::CLI.run([command, *args, tempfile.path]) + with_tty do + capture_io do + status = SyntaxTree::CLI.run([command, *args, tempfile.path]) + end end Result.new(status: status, stdio: stdio, stderr: stderr) @@ -251,6 +263,10 @@ def run_cli(command, *args, contents: :default) tempfile.unlink end + def with_tty(&block) + $stdin.stub(:tty?, true, &block) + end + def with_config_file(contents) filepath = File.join(Dir.pwd, SyntaxTree::CLI::ConfigFile::FILENAME) File.write(filepath, contents) diff --git a/test/visitor_test.rb b/test/visitor_test.rb index 27bad364..74f3df75 100644 --- a/test/visitor_test.rb +++ b/test/visitor_test.rb @@ -40,7 +40,7 @@ def initialize end end - if defined?(DidYouMean) && DidYouMean.method_defined?(:correct_error) + if defined?(DidYouMean.correct_error) def test_visit_method_correction error = assert_raises { Visitor.visit_method(:visit_binar) } message =