jimeh-solargraph 0.40.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +54 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +24 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +1003 -0
- data/Gemfile +7 -0
- data/LICENSE +21 -0
- data/README.md +123 -0
- data/Rakefile +25 -0
- data/SPONSORS.md +15 -0
- data/bin/solargraph +5 -0
- data/jimeh-solargraph.gemspec +44 -0
- data/lib/.rubocop.yml +21 -0
- data/lib/solargraph.rb +67 -0
- data/lib/solargraph/api_map.rb +752 -0
- data/lib/solargraph/api_map/bundler_methods.rb +27 -0
- data/lib/solargraph/api_map/cache.rb +70 -0
- data/lib/solargraph/api_map/source_to_yard.rb +81 -0
- data/lib/solargraph/api_map/store.rb +256 -0
- data/lib/solargraph/bench.rb +30 -0
- data/lib/solargraph/compat.rb +23 -0
- data/lib/solargraph/complex_type.rb +213 -0
- data/lib/solargraph/complex_type/type_methods.rb +127 -0
- data/lib/solargraph/complex_type/unique_type.rb +75 -0
- data/lib/solargraph/convention.rb +47 -0
- data/lib/solargraph/convention/base.rb +33 -0
- data/lib/solargraph/convention/gemfile.rb +15 -0
- data/lib/solargraph/convention/gemspec.rb +22 -0
- data/lib/solargraph/convention/rspec.rb +21 -0
- data/lib/solargraph/converters/dd.rb +12 -0
- data/lib/solargraph/converters/dl.rb +12 -0
- data/lib/solargraph/converters/dt.rb +12 -0
- data/lib/solargraph/converters/misc.rb +1 -0
- data/lib/solargraph/diagnostics.rb +55 -0
- data/lib/solargraph/diagnostics/base.rb +29 -0
- data/lib/solargraph/diagnostics/require_not_found.rb +37 -0
- data/lib/solargraph/diagnostics/rubocop.rb +90 -0
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +45 -0
- data/lib/solargraph/diagnostics/severities.rb +15 -0
- data/lib/solargraph/diagnostics/type_check.rb +54 -0
- data/lib/solargraph/diagnostics/update_errors.rb +41 -0
- data/lib/solargraph/documentor.rb +78 -0
- data/lib/solargraph/environ.rb +45 -0
- data/lib/solargraph/language_server.rb +19 -0
- data/lib/solargraph/language_server/completion_item_kinds.rb +35 -0
- data/lib/solargraph/language_server/error_codes.rb +20 -0
- data/lib/solargraph/language_server/host.rb +746 -0
- data/lib/solargraph/language_server/host/cataloger.rb +56 -0
- data/lib/solargraph/language_server/host/diagnoser.rb +81 -0
- data/lib/solargraph/language_server/host/dispatch.rb +112 -0
- data/lib/solargraph/language_server/host/sources.rb +156 -0
- data/lib/solargraph/language_server/message.rb +92 -0
- data/lib/solargraph/language_server/message/base.rb +85 -0
- data/lib/solargraph/language_server/message/cancel_request.rb +13 -0
- data/lib/solargraph/language_server/message/client.rb +11 -0
- data/lib/solargraph/language_server/message/client/register_capability.rb +15 -0
- data/lib/solargraph/language_server/message/completion_item.rb +11 -0
- data/lib/solargraph/language_server/message/completion_item/resolve.rb +57 -0
- data/lib/solargraph/language_server/message/exit_notification.rb +13 -0
- data/lib/solargraph/language_server/message/extended.rb +21 -0
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +100 -0
- data/lib/solargraph/language_server/message/extended/document.rb +20 -0
- data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -0
- data/lib/solargraph/language_server/message/extended/download_core.rb +23 -0
- data/lib/solargraph/language_server/message/extended/environment.rb +25 -0
- data/lib/solargraph/language_server/message/extended/search.rb +20 -0
- data/lib/solargraph/language_server/message/initialize.rb +153 -0
- data/lib/solargraph/language_server/message/initialized.rb +26 -0
- data/lib/solargraph/language_server/message/method_not_found.rb +16 -0
- data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -0
- data/lib/solargraph/language_server/message/shutdown.rb +13 -0
- data/lib/solargraph/language_server/message/text_document.rb +28 -0
- data/lib/solargraph/language_server/message/text_document/base.rb +19 -0
- data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -0
- data/lib/solargraph/language_server/message/text_document/completion.rb +57 -0
- data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
- data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
- data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -0
- data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -0
- data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -0
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +23 -0
- data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -0
- data/lib/solargraph/language_server/message/text_document/formatting.rb +105 -0
- data/lib/solargraph/language_server/message/text_document/hover.rb +44 -0
- data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -0
- data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -0
- data/lib/solargraph/language_server/message/text_document/references.rb +16 -0
- data/lib/solargraph/language_server/message/text_document/rename.rb +19 -0
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +29 -0
- data/lib/solargraph/language_server/message/workspace.rb +14 -0
- data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +29 -0
- data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +33 -0
- data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -0
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -0
- data/lib/solargraph/language_server/message_types.rb +14 -0
- data/lib/solargraph/language_server/request.rb +24 -0
- data/lib/solargraph/language_server/symbol_kinds.rb +36 -0
- data/lib/solargraph/language_server/transport.rb +13 -0
- data/lib/solargraph/language_server/transport/adapter.rb +56 -0
- data/lib/solargraph/language_server/transport/data_reader.rb +72 -0
- data/lib/solargraph/language_server/uri_helpers.rb +49 -0
- data/lib/solargraph/library.rb +426 -0
- data/lib/solargraph/location.rb +37 -0
- data/lib/solargraph/logging.rb +27 -0
- data/lib/solargraph/page.rb +83 -0
- data/lib/solargraph/parser.rb +26 -0
- data/lib/solargraph/parser/comment_ripper.rb +52 -0
- data/lib/solargraph/parser/legacy.rb +12 -0
- data/lib/solargraph/parser/legacy/class_methods.rb +109 -0
- data/lib/solargraph/parser/legacy/flawed_builder.rb +16 -0
- data/lib/solargraph/parser/legacy/node_chainer.rb +118 -0
- data/lib/solargraph/parser/legacy/node_methods.rb +311 -0
- data/lib/solargraph/parser/legacy/node_processors.rb +54 -0
- data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +23 -0
- data/lib/solargraph/parser/legacy/node_processors/args_node.rb +35 -0
- data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +15 -0
- data/lib/solargraph/parser/legacy/node_processors/block_node.rb +22 -0
- data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +25 -0
- data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +23 -0
- data/lib/solargraph/parser/legacy/node_processors/def_node.rb +63 -0
- data/lib/solargraph/parser/legacy/node_processors/defs_node.rb +36 -0
- data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +23 -0
- data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +38 -0
- data/lib/solargraph/parser/legacy/node_processors/lvasgn_node.rb +28 -0
- data/lib/solargraph/parser/legacy/node_processors/namespace_node.rb +39 -0
- data/lib/solargraph/parser/legacy/node_processors/orasgn_node.rb +16 -0
- data/lib/solargraph/parser/legacy/node_processors/resbody_node.rb +36 -0
- data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +21 -0
- data/lib/solargraph/parser/legacy/node_processors/send_node.rb +257 -0
- data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +18 -0
- data/lib/solargraph/parser/node_methods.rb +43 -0
- data/lib/solargraph/parser/node_processor.rb +43 -0
- data/lib/solargraph/parser/node_processor/base.rb +80 -0
- data/lib/solargraph/parser/region.rb +66 -0
- data/lib/solargraph/parser/rubyvm.rb +40 -0
- data/lib/solargraph/parser/rubyvm/class_methods.rb +150 -0
- data/lib/solargraph/parser/rubyvm/node_chainer.rb +135 -0
- data/lib/solargraph/parser/rubyvm/node_methods.rb +301 -0
- data/lib/solargraph/parser/rubyvm/node_processors.rb +62 -0
- data/lib/solargraph/parser/rubyvm/node_processors/alias_node.rb +23 -0
- data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +86 -0
- data/lib/solargraph/parser/rubyvm/node_processors/begin_node.rb +15 -0
- data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +22 -0
- data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +22 -0
- data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +23 -0
- data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +64 -0
- data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +57 -0
- data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +23 -0
- data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +38 -0
- data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +39 -0
- data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +20 -0
- data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +27 -0
- data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +39 -0
- data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +26 -0
- data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +15 -0
- data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +45 -0
- data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +21 -0
- data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +15 -0
- data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +277 -0
- data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +18 -0
- data/lib/solargraph/parser/snippet.rb +13 -0
- data/lib/solargraph/pin.rb +36 -0
- data/lib/solargraph/pin/base.rb +296 -0
- data/lib/solargraph/pin/base_variable.rb +84 -0
- data/lib/solargraph/pin/block.rb +62 -0
- data/lib/solargraph/pin/class_variable.rb +8 -0
- data/lib/solargraph/pin/closure.rb +37 -0
- data/lib/solargraph/pin/common.rb +70 -0
- data/lib/solargraph/pin/constant.rb +43 -0
- data/lib/solargraph/pin/conversions.rb +96 -0
- data/lib/solargraph/pin/documenting.rb +105 -0
- data/lib/solargraph/pin/duck_method.rb +16 -0
- data/lib/solargraph/pin/global_variable.rb +8 -0
- data/lib/solargraph/pin/instance_variable.rb +30 -0
- data/lib/solargraph/pin/keyword.rb +15 -0
- data/lib/solargraph/pin/keyword_param.rb +8 -0
- data/lib/solargraph/pin/local_variable.rb +21 -0
- data/lib/solargraph/pin/localized.rb +43 -0
- data/lib/solargraph/pin/method.rb +245 -0
- data/lib/solargraph/pin/method_alias.rb +31 -0
- data/lib/solargraph/pin/namespace.rb +85 -0
- data/lib/solargraph/pin/parameter.rb +206 -0
- data/lib/solargraph/pin/proxy_type.rb +29 -0
- data/lib/solargraph/pin/reference.rb +14 -0
- data/lib/solargraph/pin/reference/extend.rb +10 -0
- data/lib/solargraph/pin/reference/include.rb +10 -0
- data/lib/solargraph/pin/reference/override.rb +29 -0
- data/lib/solargraph/pin/reference/prepend.rb +10 -0
- data/lib/solargraph/pin/reference/require.rb +14 -0
- data/lib/solargraph/pin/reference/superclass.rb +10 -0
- data/lib/solargraph/pin/singleton.rb +11 -0
- data/lib/solargraph/pin/symbol.rb +47 -0
- data/lib/solargraph/position.rb +100 -0
- data/lib/solargraph/range.rb +95 -0
- data/lib/solargraph/server_methods.rb +16 -0
- data/lib/solargraph/shell.rb +222 -0
- data/lib/solargraph/source.rb +537 -0
- data/lib/solargraph/source/chain.rb +154 -0
- data/lib/solargraph/source/chain/block_variable.rb +13 -0
- data/lib/solargraph/source/chain/call.rb +203 -0
- data/lib/solargraph/source/chain/class_variable.rb +13 -0
- data/lib/solargraph/source/chain/constant.rb +75 -0
- data/lib/solargraph/source/chain/global_variable.rb +13 -0
- data/lib/solargraph/source/chain/head.rb +35 -0
- data/lib/solargraph/source/chain/instance_variable.rb +13 -0
- data/lib/solargraph/source/chain/link.rb +67 -0
- data/lib/solargraph/source/chain/literal.rb +23 -0
- data/lib/solargraph/source/chain/or.rb +23 -0
- data/lib/solargraph/source/chain/variable.rb +13 -0
- data/lib/solargraph/source/chain/z_super.rb +30 -0
- data/lib/solargraph/source/change.rb +79 -0
- data/lib/solargraph/source/cursor.rb +164 -0
- data/lib/solargraph/source/encoding_fixes.rb +23 -0
- data/lib/solargraph/source/source_chainer.rb +190 -0
- data/lib/solargraph/source/updater.rb +54 -0
- data/lib/solargraph/source_map.rb +188 -0
- data/lib/solargraph/source_map/clip.rb +224 -0
- data/lib/solargraph/source_map/completion.rb +23 -0
- data/lib/solargraph/source_map/mapper.rb +215 -0
- data/lib/solargraph/type_checker.rb +503 -0
- data/lib/solargraph/type_checker/checks.rb +99 -0
- data/lib/solargraph/type_checker/param_def.rb +35 -0
- data/lib/solargraph/type_checker/problem.rb +32 -0
- data/lib/solargraph/type_checker/rules.rb +57 -0
- data/lib/solargraph/version.rb +5 -0
- data/lib/solargraph/views/_method.erb +62 -0
- data/lib/solargraph/views/_name_type_tag.erb +10 -0
- data/lib/solargraph/views/_namespace.erb +24 -0
- data/lib/solargraph/views/document.erb +23 -0
- data/lib/solargraph/views/environment.erb +58 -0
- data/lib/solargraph/views/layout.erb +44 -0
- data/lib/solargraph/views/search.erb +11 -0
- data/lib/solargraph/workspace.rb +209 -0
- data/lib/solargraph/workspace/config.rb +230 -0
- data/lib/solargraph/yard_map.rb +435 -0
- data/lib/solargraph/yard_map/cache.rb +19 -0
- data/lib/solargraph/yard_map/core_docs.rb +170 -0
- data/lib/solargraph/yard_map/core_fills.rb +185 -0
- data/lib/solargraph/yard_map/core_gen.rb +76 -0
- data/lib/solargraph/yard_map/helpers.rb +16 -0
- data/lib/solargraph/yard_map/mapper.rb +77 -0
- data/lib/solargraph/yard_map/mapper/to_constant.rb +25 -0
- data/lib/solargraph/yard_map/mapper/to_method.rb +78 -0
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +27 -0
- data/lib/solargraph/yard_map/rdoc_to_yard.rb +140 -0
- data/lib/solargraph/yard_map/stdlib_fills.rb +43 -0
- data/lib/solargraph/yard_map/to_method.rb +79 -0
- data/lib/yard-solargraph.rb +30 -0
- data/yardoc/2.2.2.tar.gz +0 -0
- metadata +564 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
# A pair of positions that compose a section of text.
|
5
|
+
#
|
6
|
+
class Range
|
7
|
+
# @return [Position]
|
8
|
+
attr_reader :start
|
9
|
+
|
10
|
+
# @return [Position]
|
11
|
+
attr_reader :ending
|
12
|
+
|
13
|
+
# @param start [Position]
|
14
|
+
# @param ending [Position]
|
15
|
+
def initialize start, ending
|
16
|
+
@start = start
|
17
|
+
@ending = ending
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get a hash of the range. This representation is suitable for use in
|
21
|
+
# the language server protocol.
|
22
|
+
#
|
23
|
+
# @return [Hash<Symbol, Position>]
|
24
|
+
def to_hash
|
25
|
+
{
|
26
|
+
start: start.to_hash,
|
27
|
+
end: ending.to_hash
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
# True if the specified position is inside the range.
|
32
|
+
#
|
33
|
+
# @param position [Position, Array(Integer, Integer)]
|
34
|
+
# @return [Boolean]
|
35
|
+
def contain? position
|
36
|
+
position = Position.normalize(position)
|
37
|
+
return false if position.line < start.line || position.line > ending.line
|
38
|
+
return false if position.line == start.line && position.character < start.character
|
39
|
+
return false if position.line == ending.line && position.character > ending.character
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
# True if the range contains the specified position and the position does not precede it.
|
44
|
+
#
|
45
|
+
# @param position [Position, Array(Integer, Integer)]
|
46
|
+
# @return [Boolean]
|
47
|
+
def include? position
|
48
|
+
position = Position.normalize(position)
|
49
|
+
contain?(position) && !(position.line == start.line && position.character == start.character)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create a range from a pair of lines and characters.
|
53
|
+
#
|
54
|
+
# @param l1 [Integer] Starting line
|
55
|
+
# @param c1 [Integer] Starting character
|
56
|
+
# @param l2 [Integer] Ending line
|
57
|
+
# @param c2 [Integer] Ending character
|
58
|
+
# @return [Range]
|
59
|
+
def self.from_to l1, c1, l2, c2
|
60
|
+
Range.new(Position.new(l1, c1), Position.new(l2, c2))
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get a range from a node.
|
64
|
+
#
|
65
|
+
# @param node [RubyVM::AbstractSyntaxTree::Node, Parser::AST::Node]
|
66
|
+
# @return [Range]
|
67
|
+
def self.from_node node
|
68
|
+
if defined?(RubyVM::AbstractSyntaxTree::Node)
|
69
|
+
if node.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
70
|
+
Solargraph::Range.from_to(node.first_lineno - 1, node.first_column, node.last_lineno - 1, node.last_column)
|
71
|
+
end
|
72
|
+
elsif node.loc && node.loc.expression
|
73
|
+
from_expr(node.loc.expression)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Get a range from a Parser range, usually found in
|
78
|
+
# Parser::AST::Node#location#expression.
|
79
|
+
#
|
80
|
+
# @param expr [Parser::Source::Range]
|
81
|
+
# @return [Range]
|
82
|
+
def self.from_expr expr
|
83
|
+
from_to(expr.line, expr.column, expr.last_line, expr.last_column)
|
84
|
+
end
|
85
|
+
|
86
|
+
def == other
|
87
|
+
return false unless other.is_a?(Range)
|
88
|
+
start == other.start && ending == other.ending
|
89
|
+
end
|
90
|
+
|
91
|
+
def inspect
|
92
|
+
"#<#{self.class} #{start.inspect} to #{ending.inspect}>"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module Solargraph
|
6
|
+
module ServerMethods
|
7
|
+
# @return [Integer]
|
8
|
+
def available_port
|
9
|
+
socket = Socket.new(:INET, :STREAM, 0)
|
10
|
+
socket.bind(Addrinfo.tcp("127.0.0.1", 0))
|
11
|
+
port = socket.local_address.ip_port
|
12
|
+
socket.close
|
13
|
+
port
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module Solargraph
|
6
|
+
class Shell < Thor
|
7
|
+
include Solargraph::ServerMethods
|
8
|
+
|
9
|
+
map %w[--version -v] => :version
|
10
|
+
|
11
|
+
desc "--version, -v", "Print the version"
|
12
|
+
def version
|
13
|
+
puts Solargraph::VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'socket', 'Run a Solargraph socket server'
|
17
|
+
option :host, type: :string, aliases: :h, desc: 'The server host', default: '127.0.0.1'
|
18
|
+
option :port, type: :numeric, aliases: :p, desc: 'The server port', default: 7658
|
19
|
+
def socket
|
20
|
+
require 'backport'
|
21
|
+
port = options[:port]
|
22
|
+
port = available_port if port.zero?
|
23
|
+
Backport.run do
|
24
|
+
Signal.trap("INT") do
|
25
|
+
Backport.stop
|
26
|
+
end
|
27
|
+
Signal.trap("TERM") do
|
28
|
+
Backport.stop
|
29
|
+
end
|
30
|
+
Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter
|
31
|
+
STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'stdio', 'Run a Solargraph stdio server'
|
36
|
+
def stdio
|
37
|
+
require 'backport'
|
38
|
+
Backport.run do
|
39
|
+
Signal.trap("INT") do
|
40
|
+
Backport.stop
|
41
|
+
end
|
42
|
+
Signal.trap("TERM") do
|
43
|
+
Backport.stop
|
44
|
+
end
|
45
|
+
Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter
|
46
|
+
STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
desc 'config [DIRECTORY]', 'Create or overwrite a default configuration file'
|
51
|
+
option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
|
52
|
+
def config(directory = '.')
|
53
|
+
matches = []
|
54
|
+
if options[:extensions]
|
55
|
+
Gem::Specification.each do |g|
|
56
|
+
if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/)
|
57
|
+
require g.name
|
58
|
+
matches.push g.name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
conf = Solargraph::Workspace::Config.new.raw_data
|
63
|
+
unless matches.empty?
|
64
|
+
matches.each do |m|
|
65
|
+
conf['extensions'].push m
|
66
|
+
end
|
67
|
+
end
|
68
|
+
File.open(File.join(directory, '.solargraph.yml'), 'w') do |file|
|
69
|
+
file.puts conf.to_yaml
|
70
|
+
end
|
71
|
+
STDOUT.puts "Configuration file initialized."
|
72
|
+
end
|
73
|
+
|
74
|
+
desc 'download-core [VERSION]', 'Download core documentation'
|
75
|
+
def download_core version = nil
|
76
|
+
ver = version || Solargraph::YardMap::CoreDocs.best_download
|
77
|
+
puts "Downloading docs for #{ver}..."
|
78
|
+
Solargraph::YardMap::CoreDocs.download ver
|
79
|
+
# Clear cached documentation if it exists
|
80
|
+
FileUtils.rm_rf Dir.glob(File.join(Solargraph::YardMap::CoreDocs.cache_dir, ver, '*.ser'))
|
81
|
+
rescue ArgumentError => e
|
82
|
+
STDERR.puts "ERROR: #{e.message}"
|
83
|
+
STDERR.puts "Run `solargraph available-cores` for a list."
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
|
87
|
+
desc 'list-cores', 'List the local documentation versions'
|
88
|
+
def list_cores
|
89
|
+
puts Solargraph::YardMap::CoreDocs.versions.join("\n")
|
90
|
+
end
|
91
|
+
|
92
|
+
desc 'available-cores', 'List available documentation versions'
|
93
|
+
def available_cores
|
94
|
+
puts Solargraph::YardMap::CoreDocs.available.join("\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
desc 'clear', 'Delete all cached documentation'
|
98
|
+
long_desc %(
|
99
|
+
This command will delete all core and gem documentation from the cache.
|
100
|
+
You can also delete specific gem caches with the `uncache` command or
|
101
|
+
update documentation for specific Ruby versions with the `download-core`
|
102
|
+
command.
|
103
|
+
)
|
104
|
+
def clear
|
105
|
+
puts "Deleting the cached documentation"
|
106
|
+
Solargraph::YardMap::CoreDocs.clear
|
107
|
+
end
|
108
|
+
map 'clear-cache' => :clear
|
109
|
+
map 'clear-cores' => :clear
|
110
|
+
|
111
|
+
desc 'uncache GEM [...GEM]', "Delete cached gem documentation"
|
112
|
+
def uncache *gems
|
113
|
+
raise ArgumentError, 'No gems specified.' if gems.empty?
|
114
|
+
gems.each do |gem|
|
115
|
+
Dir[File.join(Solargraph::YardMap::CoreDocs.cache_dir, 'gems', "#{gem}-*")].each do |dir|
|
116
|
+
puts "Deleting cache: #{dir}"
|
117
|
+
FileUtils.remove_entry_secure dir
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
desc 'reporters', 'Get a list of diagnostics reporters'
|
123
|
+
def reporters
|
124
|
+
puts Solargraph::Diagnostics.reporters
|
125
|
+
end
|
126
|
+
|
127
|
+
desc 'typecheck [FILE(s)]', 'Run the type checker'
|
128
|
+
long_desc %(
|
129
|
+
Perform type checking on one or more files in a workspace. Check the
|
130
|
+
entire workspace if no files are specified.
|
131
|
+
|
132
|
+
Type checking levels are normal, typed, strict, and strong.
|
133
|
+
)
|
134
|
+
option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
|
135
|
+
option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
|
136
|
+
def typecheck *files
|
137
|
+
directory = File.realpath(options[:directory])
|
138
|
+
api_map = Solargraph::ApiMap.load(directory)
|
139
|
+
if files.empty?
|
140
|
+
files = api_map.source_maps.map(&:filename)
|
141
|
+
else
|
142
|
+
files.map! { |file| File.realpath(file) }
|
143
|
+
end
|
144
|
+
probcount = 0
|
145
|
+
filecount = 0
|
146
|
+
files.each do |file|
|
147
|
+
checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym)
|
148
|
+
problems = checker.problems
|
149
|
+
next if problems.empty?
|
150
|
+
problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
|
151
|
+
puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
|
152
|
+
filecount += 1
|
153
|
+
probcount += problems.length
|
154
|
+
end
|
155
|
+
puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
|
156
|
+
exit 1 if probcount > 0
|
157
|
+
end
|
158
|
+
|
159
|
+
desc 'scan', 'Test the workspace for problems'
|
160
|
+
long_desc %(
|
161
|
+
A scan loads the entire workspace to make sure that the ASTs and
|
162
|
+
maps do not raise errors during analysis. It does not perform any type
|
163
|
+
checking or validation; it only confirms that the analysis itself is
|
164
|
+
error-free.
|
165
|
+
)
|
166
|
+
option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
|
167
|
+
option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
|
168
|
+
def scan
|
169
|
+
require 'benchmark'
|
170
|
+
directory = File.realpath(options[:directory])
|
171
|
+
api_map = nil
|
172
|
+
time = Benchmark.measure {
|
173
|
+
api_map = Solargraph::ApiMap.load(directory)
|
174
|
+
api_map.pins.each do |pin|
|
175
|
+
begin
|
176
|
+
puts pin_description(pin) if options[:verbose]
|
177
|
+
pin.typify api_map
|
178
|
+
pin.probe api_map
|
179
|
+
rescue StandardError => e
|
180
|
+
STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
|
181
|
+
STDERR.puts "[#{e.class}]: #{e.message}"
|
182
|
+
STDERR.puts e.backtrace.join("\n")
|
183
|
+
exit 1
|
184
|
+
end
|
185
|
+
end
|
186
|
+
}
|
187
|
+
puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
|
188
|
+
end
|
189
|
+
|
190
|
+
desc 'bundle', 'Generate documentation for bundled gems'
|
191
|
+
option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
|
192
|
+
option :rebuild, type: :boolean, aliases: :r, desc: 'Rebuild existing documentation', default: false
|
193
|
+
def bundle
|
194
|
+
Documentor.new(options[:directory], rebuild: options[:rebuild], out: STDOUT).document
|
195
|
+
end
|
196
|
+
|
197
|
+
desc 'rdoc GEM [VERSION]', 'Use RDoc to cache documentation'
|
198
|
+
def rdoc gem, version = '>= 0'
|
199
|
+
spec = Gem::Specification.find_by_name(gem, version)
|
200
|
+
puts "Caching #{spec.name} #{spec.version} from RDoc"
|
201
|
+
Solargraph::YardMap::RdocToYard.run(spec)
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
# @param pin [Solargraph::Pin::Base]
|
207
|
+
# @return [String]
|
208
|
+
def pin_description pin
|
209
|
+
desc = if pin.path.nil? || pin.path.empty?
|
210
|
+
if pin.closure
|
211
|
+
"#{pin.closure.path} | #{pin.name}"
|
212
|
+
else
|
213
|
+
"#{pin.context.namespace} | #{pin.name}"
|
214
|
+
end
|
215
|
+
else
|
216
|
+
pin.path
|
217
|
+
end
|
218
|
+
desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
|
219
|
+
desc
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,537 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yard'
|
4
|
+
|
5
|
+
module Solargraph
|
6
|
+
# A Ruby file that has been parsed into an AST.
|
7
|
+
#
|
8
|
+
class Source
|
9
|
+
# autoload :FlawedBuilder, 'solargraph/source/flawed_builder'
|
10
|
+
autoload :Updater, 'solargraph/source/updater'
|
11
|
+
autoload :Change, 'solargraph/source/change'
|
12
|
+
autoload :Mapper, 'solargraph/source/mapper'
|
13
|
+
# autoload :NodeMethods, 'solargraph/source/node_methods'
|
14
|
+
autoload :EncodingFixes, 'solargraph/source/encoding_fixes'
|
15
|
+
autoload :Cursor, 'solargraph/source/cursor'
|
16
|
+
autoload :Chain, 'solargraph/source/chain'
|
17
|
+
autoload :SourceChainer, 'solargraph/source/source_chainer'
|
18
|
+
# autoload :NodeChainer, 'solargraph/source/node_chainer'
|
19
|
+
|
20
|
+
include EncodingFixes
|
21
|
+
# include NodeMethods
|
22
|
+
|
23
|
+
# @return [String]
|
24
|
+
attr_reader :filename
|
25
|
+
|
26
|
+
# @return [String]
|
27
|
+
attr_reader :code
|
28
|
+
|
29
|
+
# @return [Parser::AST::Node]
|
30
|
+
attr_reader :node
|
31
|
+
|
32
|
+
# @return [Hash{Integer => Array<String>}]
|
33
|
+
attr_reader :comments
|
34
|
+
|
35
|
+
# @todo Deprecate?
|
36
|
+
# @return [Integer]
|
37
|
+
attr_reader :version
|
38
|
+
|
39
|
+
# @param code [String]
|
40
|
+
# @param filename [String]
|
41
|
+
# @param version [Integer]
|
42
|
+
def initialize code, filename = nil, version = 0
|
43
|
+
@code = normalize(code)
|
44
|
+
@repaired = code
|
45
|
+
@filename = filename
|
46
|
+
@version = version
|
47
|
+
@domains = []
|
48
|
+
begin
|
49
|
+
# @node, @comments = Source.parse_with_comments(@code, filename)
|
50
|
+
@node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
|
51
|
+
@parsed = true
|
52
|
+
rescue Parser::SyntaxError, EncodingError => e
|
53
|
+
# @todo 100% whitespace results in a nil node, so there's no reason to parse it.
|
54
|
+
# We still need to determine whether the resulting node should be nil or a dummy
|
55
|
+
# node with a location that encompasses the range.
|
56
|
+
# @node, @comments = Source.parse_with_comments(@code.gsub(/[^\s]/, ' '), filename)
|
57
|
+
@node = nil
|
58
|
+
@comments = {}
|
59
|
+
@parsed = false
|
60
|
+
# rescue Exception => e
|
61
|
+
# Solargraph.logger.warn "[#{e.class}] #{e.message}"
|
62
|
+
# Solargraph.logger.warn e.backtrace.join("\n")
|
63
|
+
# raise "Error parsing #{filename || '(source)'}: [#{e.class}] #{e.message}"
|
64
|
+
ensure
|
65
|
+
@code.freeze
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param range [Solargraph::Range]
|
70
|
+
# @return [String]
|
71
|
+
def at range
|
72
|
+
from_to range.start.line, range.start.character, range.ending.line, range.ending.character
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param l1 [Integer]
|
76
|
+
# @param c1 [Integer]
|
77
|
+
# @param l2 [Integer]
|
78
|
+
# @param c2 [Integer]
|
79
|
+
# @return [String]
|
80
|
+
def from_to l1, c1, l2, c2
|
81
|
+
b = Solargraph::Position.line_char_to_offset(@code, l1, c1)
|
82
|
+
e = Solargraph::Position.line_char_to_offset(@code, l2, c2)
|
83
|
+
@code[b..e-1]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get the nearest node that contains the specified index.
|
87
|
+
#
|
88
|
+
# @param line [Integer]
|
89
|
+
# @param column [Integer]
|
90
|
+
# @return [AST::Node]
|
91
|
+
def node_at(line, column)
|
92
|
+
tree_at(line, column).first
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get an array of nodes containing the specified index, starting with the
|
96
|
+
# nearest node and ending with the root.
|
97
|
+
#
|
98
|
+
# @param line [Integer]
|
99
|
+
# @param column [Integer]
|
100
|
+
# @return [Array<AST::Node>]
|
101
|
+
def tree_at(line, column)
|
102
|
+
# offset = Position.line_char_to_offset(@code, line, column)
|
103
|
+
position = Position.new(line, column)
|
104
|
+
stack = []
|
105
|
+
inner_tree_at @node, position, stack
|
106
|
+
stack
|
107
|
+
end
|
108
|
+
|
109
|
+
# Start synchronizing the source. This method updates the code without
|
110
|
+
# parsing a new AST. The resulting Source object will be marked not
|
111
|
+
# synchronized (#synchronized? == false).
|
112
|
+
#
|
113
|
+
# @param updater [Source::Updater]
|
114
|
+
# @return [Source]
|
115
|
+
def start_synchronize updater
|
116
|
+
raise 'Invalid synchronization' unless updater.filename == filename
|
117
|
+
real_code = updater.write(@code)
|
118
|
+
src = Source.allocate
|
119
|
+
src.filename = filename
|
120
|
+
src.code = real_code
|
121
|
+
src.version = updater.version
|
122
|
+
src.parsed = parsed?
|
123
|
+
src.repaired = updater.repair(@repaired)
|
124
|
+
src.synchronized = false
|
125
|
+
src.node = @node
|
126
|
+
src.comments = @comments
|
127
|
+
src.error_ranges = error_ranges
|
128
|
+
src.last_updater = updater
|
129
|
+
return src.finish_synchronize unless real_code.lines.length == @code.lines.length
|
130
|
+
src
|
131
|
+
end
|
132
|
+
|
133
|
+
# Finish synchronizing a source that was updated via #start_synchronize.
|
134
|
+
# This method returns self if the source is already synchronized. Otherwise
|
135
|
+
# it parses the AST and returns a new synchronized Source.
|
136
|
+
#
|
137
|
+
# @return [Source]
|
138
|
+
def finish_synchronize
|
139
|
+
return self if synchronized?
|
140
|
+
synced = Source.new(@code, filename)
|
141
|
+
if synced.parsed?
|
142
|
+
synced.version = version
|
143
|
+
return synced
|
144
|
+
end
|
145
|
+
synced = Source.new(@repaired, filename)
|
146
|
+
synced.error_ranges.concat (error_ranges + last_updater.changes.map(&:range))
|
147
|
+
synced.code = @code
|
148
|
+
synced.synchronized = true
|
149
|
+
synced.version = version
|
150
|
+
synced
|
151
|
+
end
|
152
|
+
|
153
|
+
# Synchronize the Source with an update. This method applies changes to the
|
154
|
+
# code, parses the new code's AST, and returns the resulting Source object.
|
155
|
+
#
|
156
|
+
# @param updater [Source::Updater]
|
157
|
+
# @return [Source]
|
158
|
+
def synchronize updater
|
159
|
+
raise 'Invalid synchronization' unless updater.filename == filename
|
160
|
+
real_code = updater.write(@code)
|
161
|
+
if real_code == @code
|
162
|
+
@version = updater.version
|
163
|
+
return self
|
164
|
+
end
|
165
|
+
synced = Source.new(real_code, filename)
|
166
|
+
if synced.parsed?
|
167
|
+
synced.version = updater.version
|
168
|
+
return synced
|
169
|
+
end
|
170
|
+
incr_code = updater.repair(@repaired)
|
171
|
+
synced = Source.new(incr_code, filename)
|
172
|
+
synced.error_ranges.concat (error_ranges + updater.changes.map(&:range))
|
173
|
+
synced.code = real_code
|
174
|
+
synced.version = updater.version
|
175
|
+
synced
|
176
|
+
end
|
177
|
+
|
178
|
+
# @param position [Position, Array(Integer, Integer)]
|
179
|
+
# @return [Source::Cursor]
|
180
|
+
def cursor_at position
|
181
|
+
Cursor.new(self, position)
|
182
|
+
end
|
183
|
+
|
184
|
+
# @return [Boolean]
|
185
|
+
def parsed?
|
186
|
+
@parsed
|
187
|
+
end
|
188
|
+
|
189
|
+
def repaired?
|
190
|
+
@is_repaired ||= (@code != @repaired)
|
191
|
+
end
|
192
|
+
|
193
|
+
# @param position [Position]
|
194
|
+
# @return [Boolean]
|
195
|
+
def string_at? position
|
196
|
+
if Parser.rubyvm?
|
197
|
+
string_ranges.each do |range|
|
198
|
+
if synchronized?
|
199
|
+
return true if range.include?(position) || range.ending == position
|
200
|
+
else
|
201
|
+
return true if last_updater && last_updater.changes.one? && range.contain?(last_updater.changes.first.range.start)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
false
|
205
|
+
else
|
206
|
+
return false if Position.to_offset(code, position) >= code.length
|
207
|
+
string_nodes.each do |node|
|
208
|
+
range = Range.from_node(node)
|
209
|
+
next if range.ending.line < position.line
|
210
|
+
break if range.ending.line > position.line
|
211
|
+
return true if node.type == :str && range.include?(position) && range.start != position
|
212
|
+
return true if [:STR, :str].include?(node.type) && range.include?(position) && range.start != position
|
213
|
+
if node.type == :dstr
|
214
|
+
inner = node_at(position.line, position.column)
|
215
|
+
next if inner.nil?
|
216
|
+
inner_range = Range.from_node(inner)
|
217
|
+
next unless range.include?(inner_range.ending)
|
218
|
+
return true if inner.type == :str
|
219
|
+
inner_code = at(Solargraph::Range.new(inner_range.start, position))
|
220
|
+
return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') ||
|
221
|
+
(inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
|
222
|
+
end
|
223
|
+
break if range.ending.line > position.line
|
224
|
+
end
|
225
|
+
false
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def string_ranges
|
230
|
+
@string_ranges ||= Parser.string_ranges(node)
|
231
|
+
end
|
232
|
+
|
233
|
+
# @param position [Position]
|
234
|
+
# @return [Boolean]
|
235
|
+
def comment_at? position
|
236
|
+
comment_ranges.each do |range|
|
237
|
+
return true if range.include?(position) ||
|
238
|
+
(range.ending.line == position.line && range.ending.column < position.column)
|
239
|
+
break if range.ending.line > position.line
|
240
|
+
end
|
241
|
+
false
|
242
|
+
end
|
243
|
+
|
244
|
+
# @param name [String]
|
245
|
+
# @return [Array<Location>]
|
246
|
+
def references name
|
247
|
+
Parser.references self, name
|
248
|
+
end
|
249
|
+
|
250
|
+
# @return [Array<Range>]
|
251
|
+
def error_ranges
|
252
|
+
@error_ranges ||= []
|
253
|
+
end
|
254
|
+
|
255
|
+
# @param node [Parser::AST::Node]
|
256
|
+
# @return [String]
|
257
|
+
def code_for(node)
|
258
|
+
rng = Range.from_node(node)
|
259
|
+
b = Position.line_char_to_offset(@code, rng.start.line, rng.start.column)
|
260
|
+
e = Position.line_char_to_offset(@code, rng.ending.line, rng.ending.column)
|
261
|
+
frag = code[b..e-1].to_s
|
262
|
+
frag.strip.gsub(/,$/, '')
|
263
|
+
end
|
264
|
+
|
265
|
+
# @param node [Parser::AST::Node]
|
266
|
+
# @return [String]
|
267
|
+
def comments_for node
|
268
|
+
rng = Range.from_node(node)
|
269
|
+
stringified_comments[rng.start.line] ||= begin
|
270
|
+
buff = associated_comments[rng.start.line]
|
271
|
+
buff ? stringify_comment_array(buff) : nil
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# A location representing the file in its entirety.
|
276
|
+
#
|
277
|
+
# @return [Location]
|
278
|
+
def location
|
279
|
+
st = Position.new(0, 0)
|
280
|
+
en = Position.from_offset(code, code.length)
|
281
|
+
range = Range.new(st, en)
|
282
|
+
Location.new(filename, range)
|
283
|
+
end
|
284
|
+
|
285
|
+
FOLDING_NODE_TYPES = if Parser.rubyvm?
|
286
|
+
%i[
|
287
|
+
CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
|
288
|
+
].freeze
|
289
|
+
else
|
290
|
+
%i[
|
291
|
+
class sclass module def defs if str dstr array while unless kwbegin hash block
|
292
|
+
].freeze
|
293
|
+
end
|
294
|
+
|
295
|
+
# Get an array of ranges that can be folded, e.g., the range of a class
|
296
|
+
# definition or an if condition.
|
297
|
+
#
|
298
|
+
# See FOLDING_NODE_TYPES for the list of node types that can be folded.
|
299
|
+
#
|
300
|
+
# @return [Array<Range>]
|
301
|
+
def folding_ranges
|
302
|
+
@folding_ranges ||= begin
|
303
|
+
result = []
|
304
|
+
inner_folding_ranges node, result
|
305
|
+
result.concat foldable_comment_block_ranges
|
306
|
+
result
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def synchronized?
|
311
|
+
@synchronized = true if @synchronized.nil?
|
312
|
+
@synchronized
|
313
|
+
end
|
314
|
+
|
315
|
+
# Get a hash of comments grouped by the line numbers of the associated code.
|
316
|
+
#
|
317
|
+
# @return [Hash{Integer => Array<Parser::Source::Comment>}]
|
318
|
+
def associated_comments
|
319
|
+
@associated_comments ||= begin
|
320
|
+
result = {}
|
321
|
+
buffer = String.new('')
|
322
|
+
last = nil
|
323
|
+
@comments.each_pair do |num, snip|
|
324
|
+
if !last || num == last + 1
|
325
|
+
buffer.concat "#{snip.text}\n"
|
326
|
+
else
|
327
|
+
result[first_not_empty_from(last + 1)] = buffer.clone
|
328
|
+
buffer.replace "#{snip.text}\n"
|
329
|
+
end
|
330
|
+
last = num
|
331
|
+
end
|
332
|
+
result[first_not_empty_from(last + 1)] = buffer unless buffer.empty? || last.nil?
|
333
|
+
result
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
private
|
338
|
+
|
339
|
+
def first_not_empty_from line
|
340
|
+
cursor = line
|
341
|
+
cursor += 1 while cursor < code_lines.length && code_lines[cursor].strip.empty?
|
342
|
+
cursor = line if cursor > code_lines.length - 1
|
343
|
+
cursor
|
344
|
+
end
|
345
|
+
|
346
|
+
# @param top [Parser::AST::Node]
|
347
|
+
# @param result [Array<Range>]
|
348
|
+
# @param parent [Symbol]
|
349
|
+
# @return [void]
|
350
|
+
def inner_folding_ranges top, result = [], parent = nil
|
351
|
+
# return unless top.is_a?(::Parser::AST::Node)
|
352
|
+
return unless Parser.is_ast_node?(top)
|
353
|
+
if FOLDING_NODE_TYPES.include?(top.type)
|
354
|
+
# @todo Smelly exception for hash's first-level array in RubyVM
|
355
|
+
unless [:ARRAY, :LIST].include?(top.type) && parent == :HASH
|
356
|
+
range = Range.from_node(top)
|
357
|
+
if result.empty? || range.start.line > result.last.start.line
|
358
|
+
result.push range unless range.ending.line - range.start.line < 2
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
top.children.each do |child|
|
363
|
+
inner_folding_ranges(child, result, top.type)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Get a string representation of an array of comments.
|
368
|
+
#
|
369
|
+
# @param comments [String]
|
370
|
+
# @return [String]
|
371
|
+
def stringify_comment_array comments
|
372
|
+
ctxt = String.new('')
|
373
|
+
started = false
|
374
|
+
skip = nil
|
375
|
+
comments.lines.each { |l|
|
376
|
+
# Trim the comment and minimum leading whitespace
|
377
|
+
p = l.gsub(/^#+/, '')
|
378
|
+
if p.strip.empty?
|
379
|
+
next unless started
|
380
|
+
ctxt.concat p
|
381
|
+
else
|
382
|
+
here = p.index(/[^ \t]/)
|
383
|
+
skip = here if skip.nil? || here < skip
|
384
|
+
ctxt.concat p[skip..-1]
|
385
|
+
end
|
386
|
+
started = true
|
387
|
+
}
|
388
|
+
ctxt
|
389
|
+
end
|
390
|
+
|
391
|
+
# A hash of line numbers and their associated comments.
|
392
|
+
#
|
393
|
+
# @return [Hash{Integer => Array<String>}]
|
394
|
+
def stringified_comments
|
395
|
+
@stringified_comments ||= {}
|
396
|
+
end
|
397
|
+
|
398
|
+
# @return [Array<Parser::AST::Node>]
|
399
|
+
def string_nodes
|
400
|
+
@string_nodes ||= string_nodes_in(@node)
|
401
|
+
end
|
402
|
+
|
403
|
+
# @return [Array<Range>]
|
404
|
+
def comment_ranges
|
405
|
+
@comment_ranges ||= @comments.values.map(&:range)
|
406
|
+
end
|
407
|
+
|
408
|
+
# Get an array of foldable comment block ranges. Blocks are excluded if
|
409
|
+
# they are less than 3 lines long.
|
410
|
+
#
|
411
|
+
# @return [Array<Range>]
|
412
|
+
def foldable_comment_block_ranges
|
413
|
+
return [] unless synchronized?
|
414
|
+
result = []
|
415
|
+
grouped = []
|
416
|
+
comments.keys.each do |l|
|
417
|
+
if grouped.empty? || l == grouped.last + 1
|
418
|
+
grouped.push l
|
419
|
+
else
|
420
|
+
result.push Range.from_to(grouped.first, 0, grouped.last, 0) unless grouped.length < 3
|
421
|
+
grouped = [l]
|
422
|
+
end
|
423
|
+
end
|
424
|
+
result.push Range.from_to(grouped.first, 0, grouped.last, 0) unless grouped.length < 3
|
425
|
+
result
|
426
|
+
end
|
427
|
+
|
428
|
+
# @param n [Parser::AST::Node]
|
429
|
+
# @return [Array<Parser::AST::Node>]
|
430
|
+
def string_nodes_in n
|
431
|
+
result = []
|
432
|
+
if Parser.is_ast_node?(n)
|
433
|
+
if n.type == :str || n.type == :dstr || n.type == :STR || n.type == :DSTR
|
434
|
+
result.push n
|
435
|
+
else
|
436
|
+
n.children.each{ |c| result.concat string_nodes_in(c) }
|
437
|
+
end
|
438
|
+
end
|
439
|
+
result
|
440
|
+
end
|
441
|
+
|
442
|
+
# @param node [Parser::AST::Node]
|
443
|
+
# @param position [Position]
|
444
|
+
# @param stack [Array<Parser::AST::Node>]
|
445
|
+
# @return [void]
|
446
|
+
def inner_tree_at node, position, stack
|
447
|
+
return if node.nil?
|
448
|
+
# here = Range.from_to(node.loc.expression.line, node.loc.expression.column, node.loc.expression.last_line, node.loc.expression.last_column)
|
449
|
+
here = Range.from_node(node)
|
450
|
+
if here.contain?(position) || colonized(here, position, node)
|
451
|
+
stack.unshift node
|
452
|
+
node.children.each do |c|
|
453
|
+
next unless Parser.is_ast_node?(c)
|
454
|
+
next if !Parser.rubyvm? && c.loc.expression.nil?
|
455
|
+
inner_tree_at(c, position, stack)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def colonized range, position, node
|
461
|
+
node.type == :COLON2 &&
|
462
|
+
range.ending.line == position.line &&
|
463
|
+
range.ending.character == position.character - 2 &&
|
464
|
+
code[Position.to_offset(code, Position.new(position.line, position.character - 2)), 2] == '::'
|
465
|
+
end
|
466
|
+
|
467
|
+
protected
|
468
|
+
|
469
|
+
# @return [String]
|
470
|
+
attr_writer :filename
|
471
|
+
|
472
|
+
# @return [Integer]
|
473
|
+
attr_writer :version
|
474
|
+
|
475
|
+
# @param val [String]
|
476
|
+
# @return [String]
|
477
|
+
def code=(val)
|
478
|
+
@code_lines= nil
|
479
|
+
@code = val
|
480
|
+
end
|
481
|
+
|
482
|
+
# @return [Parser::AST::Node]
|
483
|
+
attr_writer :node
|
484
|
+
|
485
|
+
# @return [Array<Range>]
|
486
|
+
attr_writer :error_ranges
|
487
|
+
|
488
|
+
# @return [String]
|
489
|
+
attr_accessor :repaired
|
490
|
+
|
491
|
+
# @return [Boolean]
|
492
|
+
attr_writer :parsed
|
493
|
+
|
494
|
+
# @return [Array<Parser::Source::Comment>]
|
495
|
+
attr_writer :comments
|
496
|
+
|
497
|
+
# @return [Boolean]
|
498
|
+
attr_writer :synchronized
|
499
|
+
|
500
|
+
# @return [Source::Updater]
|
501
|
+
attr_accessor :last_updater
|
502
|
+
|
503
|
+
private
|
504
|
+
|
505
|
+
# @return [Array<String>]
|
506
|
+
def code_lines
|
507
|
+
@code_lines ||= code.lines
|
508
|
+
end
|
509
|
+
|
510
|
+
class << self
|
511
|
+
# @param filename [String]
|
512
|
+
# @return [Solargraph::Source]
|
513
|
+
def load filename
|
514
|
+
file = File.open(filename)
|
515
|
+
code = file.read
|
516
|
+
file.close
|
517
|
+
Source.load_string(code, filename)
|
518
|
+
end
|
519
|
+
|
520
|
+
# @param code [String]
|
521
|
+
# @param filename [String]
|
522
|
+
# @param version [Integer]
|
523
|
+
# @return [Solargraph::Source]
|
524
|
+
def load_string code, filename = nil, version = 0
|
525
|
+
Source.new code, filename, version
|
526
|
+
end
|
527
|
+
|
528
|
+
# @param comments [String]
|
529
|
+
# @return [YARD::DocstringParser]
|
530
|
+
def parse_docstring comments
|
531
|
+
# HACK: Pass a dummy code object to the parser for plugins that
|
532
|
+
# expect it not to be nil
|
533
|
+
YARD::Docstring.parser.parse(comments, YARD::CodeObjects::Base.new(:root, 'stub'))
|
534
|
+
end
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|