@@ -22,31 +22,120 @@ <h1>Syntax Tree</h1>
22
22
</ nav >
23
23
< textarea id ="editor "> # frozen_string_literal: true
24
24
25
- require "json"
26
- require "pp"
27
- require "prettyprint"
25
+ require "prettier_print"
28
26
require "ripper"
29
- require "stringio"
30
27
31
- require_relative "syntax_tree/formatter"
32
28
require_relative "syntax_tree/node"
29
+ require_relative "syntax_tree/basic_visitor"
30
+ require_relative "syntax_tree/visitor"
31
+
32
+ require_relative "syntax_tree/formatter"
33
33
require_relative "syntax_tree/parser"
34
- require_relative "syntax_tree/prettyprint"
35
34
require_relative "syntax_tree/version"
36
- require_relative "syntax_tree/visitor"
37
- require_relative "syntax_tree/visitor/json_visitor"
38
- require_relative "syntax_tree/visitor/pretty_print_visitor"
39
35
36
+ # Syntax Tree is a suite of tools built on top of the internal CRuby parser. It
37
+ # provides the ability to generate a syntax tree from source, as well as the
38
+ # tools necessary to inspect and manipulate that syntax tree. It can be used to
39
+ # build formatters, linters, language servers, and more.
40
40
module SyntaxTree
41
+ # Syntax Tree the library has many features that aren't always used by the
42
+ # CLI. Requiring those features takes time, so we autoload as many constants
43
+ # as possible in order to keep the CLI as fast as possible.
44
+
45
+ autoload :DSL, "syntax_tree/dsl"
46
+ autoload :FieldVisitor, "syntax_tree/field_visitor"
47
+ autoload :Index, "syntax_tree/index"
48
+ autoload :JSONVisitor, "syntax_tree/json_visitor"
49
+ autoload :LanguageServer, "syntax_tree/language_server"
50
+ autoload :MatchVisitor, "syntax_tree/match_visitor"
51
+ autoload :Mermaid, "syntax_tree/mermaid"
52
+ autoload :MermaidVisitor, "syntax_tree/mermaid_visitor"
53
+ autoload :MutationVisitor, "syntax_tree/mutation_visitor"
54
+ autoload :Pattern, "syntax_tree/pattern"
55
+ autoload :PrettyPrintVisitor, "syntax_tree/pretty_print_visitor"
56
+ autoload :Search, "syntax_tree/search"
57
+ autoload :Translation, "syntax_tree/translation"
58
+ autoload :WithScope, "syntax_tree/with_scope"
59
+ autoload :YARV, "syntax_tree/yarv"
60
+
41
61
# This holds references to objects that respond to both #parse and #format
42
62
# so that we can use them in the CLI.
43
63
HANDLERS = {}
44
64
HANDLERS.default = SyntaxTree
45
65
46
- # This is a hook provided so that plugins can register themselves as the
47
- # handler for a particular file type.
48
- def self.register_handler(extension, handler)
49
- HANDLERS[extension] = handler
66
+ # This is the default print width when formatting. It can be overridden in the
67
+ # CLI by passing the --print-width option or here in the API by passing the
68
+ # optional second argument to ::format.
69
+ DEFAULT_PRINT_WIDTH = 80
70
+
71
+ # This is the default ruby version that we're going to target for formatting.
72
+ # It shouldn't really be changed except in very niche circumstances.
73
+ DEFAULT_RUBY_VERSION = Formatter::SemanticVersion.new(RUBY_VERSION).freeze
74
+
75
+ # The default indentation level for formatting. We allow changing this so
76
+ # that Syntax Tree can format arbitrary parts of a document.
77
+ DEFAULT_INDENTATION = 0
78
+
79
+ # Parses the given source and returns the formatted source.
80
+ def self.format(
81
+ source,
82
+ maxwidth = DEFAULT_PRINT_WIDTH,
83
+ base_indentation = DEFAULT_INDENTATION,
84
+ options: Formatter::Options.new
85
+ )
86
+ format_node(
87
+ source,
88
+ parse(source),
89
+ maxwidth,
90
+ base_indentation,
91
+ options: options
92
+ )
93
+ end
94
+
95
+ # Parses the given file and returns the formatted source.
96
+ def self.format_file(
97
+ filepath,
98
+ maxwidth = DEFAULT_PRINT_WIDTH,
99
+ base_indentation = DEFAULT_INDENTATION,
100
+ options: Formatter::Options.new
101
+ )
102
+ format(read(filepath), maxwidth, base_indentation, options: options)
103
+ end
104
+
105
+ # Accepts a node in the tree and returns the formatted source.
106
+ def self.format_node(
107
+ source,
108
+ node,
109
+ maxwidth = DEFAULT_PRINT_WIDTH,
110
+ base_indentation = DEFAULT_INDENTATION,
111
+ options: Formatter::Options.new
112
+ )
113
+ formatter = Formatter.new(source, [], maxwidth, options: options)
114
+ node.format(formatter)
115
+
116
+ formatter.flush(base_indentation)
117
+ formatter.output.join
118
+ end
119
+
120
+ # Indexes the given source code to return a list of all class, module, and
121
+ # method definitions. Used to quickly provide indexing capability for IDEs or
122
+ # documentation generation.
123
+ def self.index(source)
124
+ Index.index(source)
125
+ end
126
+
127
+ # Indexes the given file to return a list of all class, module, and method
128
+ # definitions. Used to quickly provide indexing capability for IDEs or
129
+ # documentation generation.
130
+ def self.index_file(filepath)
131
+ Index.index_file(filepath)
132
+ end
133
+
134
+ # A convenience method for creating a new mutation visitor.
135
+ def self.mutation
136
+ visitor = MutationVisitor.new
137
+ yield visitor
138
+ visitor
50
139
end
51
140
52
141
# Parses the given source and returns the syntax tree.
@@ -56,27 +145,46 @@ <h1>Syntax Tree</h1>
56
145
response unless parser.error?
57
146
end
58
147
59
- # Parses the given source and returns the formatted source.
60
- def self.format(source)
61
- formatter = Formatter.new(source, [])
62
- parse(source).format(formatter)
63
-
64
- formatter.flush
65
- formatter.output.join
148
+ # Parses the given file and returns the syntax tree.
149
+ def self.parse_file(filepath)
150
+ parse(read(filepath))
66
151
end
67
152
68
153
# Returns the source from the given filepath taking into account any potential
69
154
# magic encoding comments.
70
155
def self.read(filepath)
71
156
encoding =
72
157
File.open(filepath, "r") do |file|
158
+ break Encoding.default_external if file.eof?
159
+
73
160
header = file.readline
74
- header += file.readline if header.start_with?("#!")
161
+ header += file.readline if !file.eof? && header.start_with?("#!")
75
162
Ripper.new(header).tap(&:parse).encoding
76
163
end
77
164
78
165
File.read(filepath, encoding: encoding)
79
166
end
167
+
168
+ # This is a hook provided so that plugins can register themselves as the
169
+ # handler for a particular file type.
170
+ def self.register_handler(extension, handler)
171
+ HANDLERS[extension] = handler
172
+ end
173
+
174
+ # Searches through the given source using the given pattern and yields each
175
+ # node in the tree that matches the pattern to the given block.
176
+ def self.search(source, query, &block)
177
+ pattern = Pattern.new(query).compile
178
+ program = parse(source)
179
+
180
+ Search.new(pattern).scan(program, &block)
181
+ end
182
+
183
+ # Searches through the given file using the given pattern and yields each
184
+ # node in the tree that matches the pattern to the given block.
185
+ def self.search_file(filepath, query, &block)
186
+ search(read(filepath), query, &block)
187
+ end
80
188
end
81
189
</ textarea >
82
190
< textarea id ="output " disabled readonly > Loading...</ textarea >
0 commit comments