/
cli.rb
135 lines (114 loc) · 5.64 KB
/
cli.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require "optparse"
module TypeProf
module CLI
module_function
def parse(argv)
opt = OptionParser.new
opt.banner = "Usage: #{ opt.program_name } [options] files..."
output = nil
# Verbose level:
# * 0: none
# * 1: default level
# * 2: debugging level
verbose = 1
options = {}
lsp_options = {}
dir_filter = nil
gem_rbs_features = []
show_version = false
max_sec = max_iter = nil
collection_path = nil
no_collection = false
load_path_ext = []
opt.separator ""
opt.separator "Options:"
opt.on("-o OUTFILE", "Output to OUTFILE instead of stdout") {|v| output = v }
opt.on("-q", "--quiet", "Do not display progress indicator") { options[:show_indicator] = false }
opt.on("-v", "--verbose", "Alias to --show-errors") { options[:show_errors] = true }
opt.on("--version", "Display typeprof version") { show_version = true }
opt.on("-I DIR", "Add DIR to the load/require path") {|v| load_path_ext << v }
opt.on("-r FEATURE", "Require RBS of the FEATURE gem") {|v| gem_rbs_features << v }
opt.on("--collection PATH", "File path of collection configuration") { |v| collection_path = Pathname(v) }
opt.on("--no-collection", "Ignore collection configuration") { no_collection = true }
opt.on("--lsp", "LSP mode") {|v| options[:lsp] = true }
opt.separator ""
opt.separator "Analysis output options:"
opt.on("--include-dir DIR", "Include the analysis result of .rb file in DIR") do |dir|
# When `--include-dir` option is specified as the first directory option,
# typeprof will exclude any files by default unless a file path matches the explicit option
dir_filter ||= [[:exclude]]
dir_filter << [:include, File.expand_path(dir)]
end
opt.on("--exclude-dir DIR", "Exclude the analysis result of .rb file in DIR") do |dir|
# When `--exclude-dir` option is specified as the first directory option,
# typeprof will include any files by default, except Ruby's install directory and Gem directories
dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
dir_filter << [:exclude, File.expand_path(dir)]
end
opt.on("--exclude-untyped", "Exclude (comment out) all entries including untyped") {|v| options[:exclude_untyped] = v }
opt.on("--[no-]show-typeprof-version", "Display TypeProf version in a header") {|v| options[:show_typeprof_version] = v }
opt.on("--[no-]show-errors", "Display possible errors found during the analysis") {|v| options[:show_errors] = v }
opt.on("--[no-]show-untyped", "Display \"Foo | untyped\" instead of \"Foo\"") {|v| options[:show_untyped] = v }
opt.on("--[no-]show-parameter-names", "Display parameter names for methods") {|v| options[:show_parameter_names] = v }
opt.on("--[no-]show-source-locations", "Display definition source locations for methods") {|v| options[:show_source_locations] = v }
opt.separator ""
opt.separator "Analysis limit options:"
opt.on("--max-second SECOND", Float, "Limit the maxium time of analysis (in second)") {|v| max_sec = v }
opt.on("--max-iteration TIMES", Integer, "Limit the maxium instruction count of analysis") {|v| max_iter = v }
opt.separator ""
opt.separator "Advanced options:"
opt.on("--[no-]stub-execution", "Force to call all unreachable methods with \"untyped\" arguments") {|v| options[:stub_execution] = v }
opt.on("--type-depth-limit DEPTH", Integer, "Limit the maximum depth of nested types") {|v| options[:type_depth_limit] = v }
opt.on("--union-width-limit WIDTH", Integer, "Limit the maximum count of class instances in one union type") {|v| options[:union_width_limit] = v }
opt.on("--debug", "Display analysis log (for debugging purpose)") { verbose = 2 }
opt.on("--[no-]stackprof MODE", /\Acpu|wall|object\z/, "Enable stackprof (for debugging purpose)") {|v| options[:stackprof] = v.to_sym }
opt.separator ""
opt.separator "LSP options:"
opt.on("--port PORT", Integer, "Specify a port number to listen for requests on") {|v| lsp_options[:port] = v }
opt.on("--stdio", "Use stdio for LSP transport") {|v| lsp_options[:stdio] = v }
opt.parse!(argv)
$LOAD_PATH.unshift(*load_path_ext)
dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
rb_files = []
rbs_files = []
argv.each do |path|
if File.extname(path) == ".rbs"
rbs_files << path
else
rb_files << path
end
end
puts "typeprof #{ VERSION }" if show_version
if rb_files.empty? && !options[:lsp]
exit if show_version
raise OptionParser::InvalidOption.new("no input files")
end
if !options[:lsp] && !lsp_options.empty?
exit if show_version
raise OptionParser::InvalidOption.new("lsp options with non-lsp mode")
end
if !no_collection && collection_path && !collection_path.exist?
exit if show_version
raise OptionParser::InvalidOption.new("collection path not found (#{collection_path})")
end
collection_path ||= RBS::Collection::Config::PATH
collection_path = nil if no_collection
ConfigData.new(
rb_files: rb_files,
rbs_files: rbs_files,
output: output,
gem_rbs_features: gem_rbs_features,
collection_path: collection_path,
verbose: verbose,
dir_filter: dir_filter,
max_sec: max_sec,
max_iter: max_iter,
options: options,
lsp_options: lsp_options,
)
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
puts $!
exit 1
end
end
end