Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ruby-syntax-tree/syntax_tree
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: cb72efc
Choose a base ref
...
head repository: ruby-syntax-tree/syntax_tree
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 974cdcb
Choose a head ref

Commits on Oct 13, 2022

  1. Copy the full SHA
    aaa7643 View commit details

Commits on Oct 16, 2022

  1. Copy the full SHA
    3150301 View commit details
  2. Copy the full SHA
    ed0c475 View commit details
  3. Copy the full SHA
    840ebab View commit details

Commits on Oct 17, 2022

  1. Copy the full SHA
    b5d226b View commit details
  2. More micro-optimizations

    kddnewton committed Oct 17, 2022
    Copy the full SHA
    7b6dbeb View commit details
  3. Copy the full SHA
    929726b View commit details
  4. each_line instead of split

    kddnewton committed Oct 17, 2022
    Copy the full SHA
    b5154ac View commit details
  5. Copy the full SHA
    e31dded View commit details
  6. Copy the full SHA
    b117c9b View commit details
  7. Reformat

    kddnewton committed Oct 17, 2022
    Copy the full SHA
    f5ac5fe View commit details
  8. Bump prettier_print from 0.1.0 to 1.0.0

    Bumps [prettier_print](https://github.com/ruby-syntax-tree/prettier_print) from 0.1.0 to 1.0.0.
    - [Release notes](https://github.com/ruby-syntax-tree/prettier_print/releases)
    - [Changelog](https://github.com/ruby-syntax-tree/prettier_print/blob/main/CHANGELOG.md)
    - [Commits](ruby-syntax-tree/prettier_print@v0.1.0...v1.0.0)
    
    ---
    updated-dependencies:
    - dependency-name: prettier_print
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Oct 17, 2022
    Copy the full SHA
    a640ea4 View commit details
  9. Merge pull request #171 from ruby-syntax-tree/dependabot/bundler/pret…

    …tier_print-1.0.0
    
    Bump prettier_print from 0.1.0 to 1.0.0
    github-actions[bot] authored Oct 17, 2022
    Copy the full SHA
    7aa9312 View commit details
  10. Add WithEnvironment module to track locals

    Co-authored-by: Alexandre Terrasa <Morriar@users.noreply.github.com>
    Co-authored-by: Stan Lo <st0012@users.noreply.github.com>
    Co-authored-by: Emily Samp <egiurleo@users.noreply.github.com>
    Co-authored-by: Kaan Ozkan <KaanOzkan@users.noreply.github.com>
    Co-authored-by: Adison Lampert <adisonlampert@users.noreply.github.com>
    Co-authored-by: Dirceu Tiegs <dirceu@users.noreply.github.com>
    7 people committed Oct 17, 2022
    Copy the full SHA
    7003178 View commit details
  11. Copy the full SHA
    95b2584 View commit details
  12. Fix up rubocop violations

    kddnewton committed Oct 17, 2022
    Copy the full SHA
    89081df View commit details
  13. Copy the full SHA
    8f15bbb View commit details
  14. Copy the full SHA
    aa0573d View commit details
  15. Copy the full SHA
    7b2bc9b View commit details
  16. Copy the full SHA
    727b991 View commit details
  17. Merge pull request #169 from jez/jez-repeat-ignore-files

    Allow passing `--ignore-files` multiple times
    kddnewton authored Oct 17, 2022
    Copy the full SHA
    2fc045b View commit details
  18. Merge pull request #157 from vinistock/vs/add_with_environment_mixin

    Add WithEnvironment mixin for visitors
    kddnewton authored Oct 17, 2022
    Copy the full SHA
    002cb71 View commit details
  19. Bump to version 4.0.0

    kddnewton committed Oct 17, 2022
    Copy the full SHA
    66edef8 View commit details
  20. Copy the full SHA
    974cdcb View commit details
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -55,6 +55,9 @@ Style/IdenticalConditionalBranches:
Style/IfInsideElse:
Enabled: false

Style/IfWithBooleanLiteralBranches:
Enabled: false

Style/KeywordParametersOrder:
Enabled: false

16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a

## [Unreleased]

## [4.0.0] - 2022-10-17

### Added

- [#169](https://github.com/ruby-syntax-tree/syntax_tree/pull/169) - You can now pass `--ignore-files` multiple times.
- [#157](https://github.com/ruby-syntax-tree/syntax_tree/pull/157) - We now support tracking local variable definitions throughout the visitor. This allows you to access scope information while visiting the tree.
- [#170](https://github.com/ruby-syntax-tree/syntax_tree/pull/170) - There is now an undocumented `STREE_FAST_FORMAT` environment variable checked when formatting. It has the effect of turning _off_ formatting call chains and ternaries in special ways. This improves performance quite a bit. I'm leaving it undocumented because ideally we just improve the performance as a whole. This is meant as a stopgap until we get there.

### Changed

- [#170](https://github.com/ruby-syntax-tree/syntax_tree/pull/170) - We now require at least version `1.0.0` of `prettier_print`. This is to take advantage of the first-class string support in the doc tree.
- [#170](https://github.com/ruby-syntax-tree/syntax_tree/pull/170) - Pattern matching has been removed from usage internal to this library (excluding the language server). This should hopefully enable runtimes that don't have pattern matching fully implemented yet (e.g., TruffleRuby) to run this gem.

## [3.6.3] - 2022-10-11

### Changed
@@ -370,7 +383,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.6.3...HEAD
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v4.0.0...HEAD
[4.0.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.6.3...v4.0.0
[3.6.3]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.6.2...v3.6.3
[3.6.2]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.6.1...v3.6.2
[3.6.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.6.0...v3.6.1
10 changes: 5 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
PATH
remote: .
specs:
syntax_tree (3.6.3)
prettier_print
syntax_tree (4.0.0)
prettier_print (>= 1.0.0)

GEM
remote: https://rubygems.org/
@@ -14,10 +14,10 @@ GEM
parallel (1.22.1)
parser (3.1.2.1)
ast (~> 2.4.1)
prettier_print (0.1.0)
prettier_print (1.0.0)
rainbow (3.1.1)
rake (13.0.6)
regexp_parser (2.5.0)
regexp_parser (2.6.0)
rexml (3.2.5)
rubocop (1.36.0)
json (~> 2.3)
@@ -38,7 +38,7 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
unicode-display_width (2.2.0)
unicode-display_width (2.3.0)

PLATFORMS
arm64-darwin-21
42 changes: 32 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -368,16 +368,16 @@ program = SyntaxTree.parse("1 + 1")
puts program.construct_keys

# SyntaxTree::Program[
# statements: SyntaxTree::Statements[
# body: [
# SyntaxTree::Binary[
# left: SyntaxTree::Int[value: "1"],
# operator: :+,
# right: SyntaxTree::Int[value: "1"]
# ]
# ]
# ]
# ]
# statements: SyntaxTree::Statements[
# body: [
# SyntaxTree::Binary[
# left: SyntaxTree::Int[value: "1"],
# operator: :+,
# right: SyntaxTree::Int[value: "1"]
# ]
# ]
# ]
# ]
```

## Visitor
@@ -447,6 +447,28 @@ end

The visitor defined above will error out unless it's only visiting a `SyntaxTree::Int` node. This is useful in a couple of ways, e.g., if you're trying to define a visitor to handle the whole tree but it's currently a work-in-progress.

### WithEnvironment

The `WithEnvironment` module can be included in visitors to automatically keep track of local variables and arguments
defined inside each environment. A `current_environment` accessor is made availble to the request, allowing it to find
all usages and definitions of a local.

```ruby
class MyVisitor < Visitor
include WithEnvironment

def visit_ident(node)
# find_local will return a Local for any local variables or arguments present in the current environment or nil if
# the identifier is not a local
local = current_environment.find_local(node)

puts local.type # print the type of the local (:variable or :argument)
puts local.definitions # print the array of locations where this local is defined
puts local.usages # print the array of locations where this local occurs
end
end
```

## Language server

Syntax Tree additionally ships with a language server conforming to the [language server protocol](https://microsoft.github.io/language-server-protocol/). It can be invoked through the CLI by running:
11 changes: 5 additions & 6 deletions bin/profile
Original file line number Diff line number Diff line change
@@ -6,22 +6,21 @@ require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "stackprof"
gem "prettier_print"
end

$:.unshift(File.expand_path("../lib", __dir__))
require "syntax_tree"

GC.disable

StackProf.run(mode: :cpu, out: "tmp/profile.dump", raw: true) do
filepath = File.expand_path("../lib/syntax_tree/node.rb", __dir__)
SyntaxTree.format(File.read(filepath))
Dir[File.join(RbConfig::CONFIG["libdir"], "**/*.rb")].each do |filepath|
SyntaxTree.format(SyntaxTree.read(filepath))
end
end

GC.enable

File.open("tmp/flamegraph.html", "w") do |file|
report = Marshal.load(IO.binread("tmp/profile.dump"))
StackProf::Report.new(report).print_text
StackProf::Report.new(report).print_d3_flamegraph(file)
end

16 changes: 14 additions & 2 deletions lib/syntax_tree.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# frozen_string_literal: true

require "delegate"
require "etc"
require "json"
require "pp"
@@ -10,7 +9,6 @@

require_relative "syntax_tree/formatter"
require_relative "syntax_tree/node"
require_relative "syntax_tree/parser"
require_relative "syntax_tree/version"

require_relative "syntax_tree/basic_visitor"
@@ -19,6 +17,20 @@
require_relative "syntax_tree/visitor/json_visitor"
require_relative "syntax_tree/visitor/match_visitor"
require_relative "syntax_tree/visitor/pretty_print_visitor"
require_relative "syntax_tree/visitor/environment"
require_relative "syntax_tree/visitor/with_environment"

require_relative "syntax_tree/parser"

# We rely on Symbol#name being available, which is only available in Ruby 3.0+.
# In case we're running on an older Ruby version, we polyfill it here.
unless :+.respond_to?(:name)
class Symbol # rubocop:disable Style/Documentation
def name
to_s.freeze
end
end
end

# Syntax Tree is a suite of tools built on top of the internal CRuby parser. It
# provides the ability to generate a syntax tree from source, as well as the
6 changes: 3 additions & 3 deletions lib/syntax_tree/cli.rb
Original file line number Diff line number Diff line change
@@ -290,7 +290,7 @@ class Options
:target_ruby_version

def initialize(print_width: DEFAULT_PRINT_WIDTH)
@ignore_files = ""
@ignore_files = []
@plugins = []
@print_width = print_width
@scripts = []
@@ -313,7 +313,7 @@ def parser
# 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
@ignore_files << (glob.match(/\A'(.*)'\z/) ? $1 : glob)
end

# If there are any plugins specified on the command line, then load
@@ -434,7 +434,7 @@ def run(argv)
.glob(pattern)
.each do |filepath|
if File.readable?(filepath) &&
!File.fnmatch?(options.ignore_files, filepath)
options.ignore_files.none? { File.fnmatch?(_1, filepath) }
queue << FileItem.new(filepath)
end
end
75 changes: 67 additions & 8 deletions lib/syntax_tree/formatter.rb
Original file line number Diff line number Diff line change
@@ -62,21 +62,39 @@ def format(node, stackable: true)
# If there are comments, then we're going to format them around the node
# so that they get printed properly.
if node.comments.any?
leading, trailing = node.comments.partition(&:leading?)
trailing = []
last_leading = nil

# Print all comments that were found before the node.
leading.each do |comment|
comment.format(self)
breakable(force: true)
# First, we're going to print all of the comments that were found before
# the node. We'll also gather up any trailing comments that we find.
node.comments.each do |comment|
if comment.leading?
comment.format(self)
breakable(force: true)
last_leading = comment
else
trailing << comment
end
end

# If the node has a stree-ignore comment right before it, then we're
# going to just print out the node as it was seen in the source.
doc =
if leading.last&.ignore?
if last_leading&.ignore?
range = source[node.location.start_char...node.location.end_char]
separator = -> { breakable(indent: false, force: true) }
seplist(range.split(/\r?\n/, -1), separator) { |line| text(line) }
first = true

range.each_line(chomp: true) do |line|
if first
first = false
else
breakable_return
end

text(line)
end

breakable_return if range.end_with?("\n")
else
node.format(self)
end
@@ -101,12 +119,53 @@ def format_each(nodes)
nodes.each { |node| format(node) }
end

def grandparent
stack[-3]
end

def parent
stack[-2]
end

def parents
stack[0...-1].reverse_each
end

# This is a simplified version of prettyprint's group. It doesn't provide
# any of the more advanced options because we don't need them and they take
# up expensive computation time.
def group
contents = []
doc = Group.new(0, contents: contents)

groups << doc
target << doc

with_target(contents) { yield }
groups.pop
doc
end

# A similar version to the super, except that it calls back into the
# separator proc with the instance of `self`.
def seplist(list, sep = nil, iter_method = :each)
first = true
list.__send__(iter_method) do |*v|
if first
first = false
elsif sep
sep.call(self)
else
comma_breakable
end
yield(*v)
end
end

# This is a much simplified version of prettyprint's text. It avoids
# calculating width by pushing the string directly onto the target.
def text(string)
target << string
end
end
end
Loading