solargraph 0.39.7

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.
Files changed (252) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +28 -0
  5. data/.yardopts +2 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +21 -0
  8. data/README.md +104 -0
  9. data/Rakefile +14 -0
  10. data/SPONSORS.md +9 -0
  11. data/bin/solargraph +5 -0
  12. data/lib/.rubocop.yml +21 -0
  13. data/lib/solargraph.rb +66 -0
  14. data/lib/solargraph/api_map.rb +745 -0
  15. data/lib/solargraph/api_map/bundler_methods.rb +27 -0
  16. data/lib/solargraph/api_map/cache.rb +66 -0
  17. data/lib/solargraph/api_map/source_to_yard.rb +81 -0
  18. data/lib/solargraph/api_map/store.rb +267 -0
  19. data/lib/solargraph/bundle.rb +26 -0
  20. data/lib/solargraph/complex_type.rb +213 -0
  21. data/lib/solargraph/complex_type/type_methods.rb +127 -0
  22. data/lib/solargraph/complex_type/unique_type.rb +75 -0
  23. data/lib/solargraph/convention.rb +38 -0
  24. data/lib/solargraph/convention/base.rb +25 -0
  25. data/lib/solargraph/convention/gemfile.rb +18 -0
  26. data/lib/solargraph/convention/gemspec.rb +25 -0
  27. data/lib/solargraph/convention/rspec.rb +24 -0
  28. data/lib/solargraph/converters/dd.rb +12 -0
  29. data/lib/solargraph/converters/dl.rb +12 -0
  30. data/lib/solargraph/converters/dt.rb +12 -0
  31. data/lib/solargraph/converters/misc.rb +1 -0
  32. data/lib/solargraph/core_fills.rb +159 -0
  33. data/lib/solargraph/diagnostics.rb +55 -0
  34. data/lib/solargraph/diagnostics/base.rb +29 -0
  35. data/lib/solargraph/diagnostics/require_not_found.rb +37 -0
  36. data/lib/solargraph/diagnostics/rubocop.rb +90 -0
  37. data/lib/solargraph/diagnostics/rubocop_helpers.rb +64 -0
  38. data/lib/solargraph/diagnostics/severities.rb +15 -0
  39. data/lib/solargraph/diagnostics/type_check.rb +54 -0
  40. data/lib/solargraph/diagnostics/update_errors.rb +41 -0
  41. data/lib/solargraph/documentor.rb +76 -0
  42. data/lib/solargraph/environ.rb +40 -0
  43. data/lib/solargraph/language_server.rb +19 -0
  44. data/lib/solargraph/language_server/completion_item_kinds.rb +35 -0
  45. data/lib/solargraph/language_server/error_codes.rb +20 -0
  46. data/lib/solargraph/language_server/host.rb +741 -0
  47. data/lib/solargraph/language_server/host/cataloger.rb +56 -0
  48. data/lib/solargraph/language_server/host/diagnoser.rb +81 -0
  49. data/lib/solargraph/language_server/host/dispatch.rb +112 -0
  50. data/lib/solargraph/language_server/host/sources.rb +156 -0
  51. data/lib/solargraph/language_server/message.rb +92 -0
  52. data/lib/solargraph/language_server/message/base.rb +85 -0
  53. data/lib/solargraph/language_server/message/cancel_request.rb +13 -0
  54. data/lib/solargraph/language_server/message/client.rb +11 -0
  55. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -0
  56. data/lib/solargraph/language_server/message/completion_item.rb +11 -0
  57. data/lib/solargraph/language_server/message/completion_item/resolve.rb +57 -0
  58. data/lib/solargraph/language_server/message/exit_notification.rb +13 -0
  59. data/lib/solargraph/language_server/message/extended.rb +21 -0
  60. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +95 -0
  61. data/lib/solargraph/language_server/message/extended/document.rb +20 -0
  62. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -0
  63. data/lib/solargraph/language_server/message/extended/download_core.rb +23 -0
  64. data/lib/solargraph/language_server/message/extended/environment.rb +25 -0
  65. data/lib/solargraph/language_server/message/extended/search.rb +20 -0
  66. data/lib/solargraph/language_server/message/initialize.rb +153 -0
  67. data/lib/solargraph/language_server/message/initialized.rb +26 -0
  68. data/lib/solargraph/language_server/message/method_not_found.rb +16 -0
  69. data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -0
  70. data/lib/solargraph/language_server/message/shutdown.rb +13 -0
  71. data/lib/solargraph/language_server/message/text_document.rb +28 -0
  72. data/lib/solargraph/language_server/message/text_document/base.rb +19 -0
  73. data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -0
  74. data/lib/solargraph/language_server/message/text_document/completion.rb +57 -0
  75. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
  76. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
  77. data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -0
  78. data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -0
  79. data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -0
  80. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +23 -0
  81. data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -0
  82. data/lib/solargraph/language_server/message/text_document/formatting.rb +78 -0
  83. data/lib/solargraph/language_server/message/text_document/hover.rb +44 -0
  84. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -0
  85. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -0
  86. data/lib/solargraph/language_server/message/text_document/references.rb +16 -0
  87. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -0
  88. data/lib/solargraph/language_server/message/text_document/signature_help.rb +29 -0
  89. data/lib/solargraph/language_server/message/workspace.rb +14 -0
  90. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +29 -0
  91. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +33 -0
  92. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -0
  93. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -0
  94. data/lib/solargraph/language_server/message_types.rb +14 -0
  95. data/lib/solargraph/language_server/request.rb +24 -0
  96. data/lib/solargraph/language_server/symbol_kinds.rb +36 -0
  97. data/lib/solargraph/language_server/transport.rb +13 -0
  98. data/lib/solargraph/language_server/transport/adapter.rb +56 -0
  99. data/lib/solargraph/language_server/transport/data_reader.rb +72 -0
  100. data/lib/solargraph/language_server/uri_helpers.rb +49 -0
  101. data/lib/solargraph/library.rb +414 -0
  102. data/lib/solargraph/location.rb +37 -0
  103. data/lib/solargraph/logging.rb +27 -0
  104. data/lib/solargraph/page.rb +83 -0
  105. data/lib/solargraph/parser.rb +26 -0
  106. data/lib/solargraph/parser/comment_ripper.rb +52 -0
  107. data/lib/solargraph/parser/legacy.rb +12 -0
  108. data/lib/solargraph/parser/legacy/class_methods.rb +109 -0
  109. data/lib/solargraph/parser/legacy/flawed_builder.rb +16 -0
  110. data/lib/solargraph/parser/legacy/node_chainer.rb +118 -0
  111. data/lib/solargraph/parser/legacy/node_methods.rb +300 -0
  112. data/lib/solargraph/parser/legacy/node_processors.rb +54 -0
  113. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +23 -0
  114. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +35 -0
  115. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +15 -0
  116. data/lib/solargraph/parser/legacy/node_processors/block_node.rb +22 -0
  117. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +25 -0
  118. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +23 -0
  119. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +63 -0
  120. data/lib/solargraph/parser/legacy/node_processors/defs_node.rb +36 -0
  121. data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +23 -0
  122. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +38 -0
  123. data/lib/solargraph/parser/legacy/node_processors/lvasgn_node.rb +28 -0
  124. data/lib/solargraph/parser/legacy/node_processors/namespace_node.rb +39 -0
  125. data/lib/solargraph/parser/legacy/node_processors/orasgn_node.rb +16 -0
  126. data/lib/solargraph/parser/legacy/node_processors/resbody_node.rb +36 -0
  127. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +21 -0
  128. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +234 -0
  129. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +18 -0
  130. data/lib/solargraph/parser/node_methods.rb +43 -0
  131. data/lib/solargraph/parser/node_processor.rb +43 -0
  132. data/lib/solargraph/parser/node_processor/base.rb +77 -0
  133. data/lib/solargraph/parser/region.rb +66 -0
  134. data/lib/solargraph/parser/rubyvm.rb +40 -0
  135. data/lib/solargraph/parser/rubyvm/class_methods.rb +150 -0
  136. data/lib/solargraph/parser/rubyvm/node_chainer.rb +135 -0
  137. data/lib/solargraph/parser/rubyvm/node_methods.rb +284 -0
  138. data/lib/solargraph/parser/rubyvm/node_processors.rb +61 -0
  139. data/lib/solargraph/parser/rubyvm/node_processors/alias_node.rb +23 -0
  140. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +62 -0
  141. data/lib/solargraph/parser/rubyvm/node_processors/begin_node.rb +15 -0
  142. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +22 -0
  143. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +22 -0
  144. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +23 -0
  145. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +64 -0
  146. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +57 -0
  147. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +23 -0
  148. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +38 -0
  149. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +39 -0
  150. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +20 -0
  151. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +27 -0
  152. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +39 -0
  153. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +31 -0
  154. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +15 -0
  155. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +45 -0
  156. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +21 -0
  157. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +15 -0
  158. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +255 -0
  159. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +18 -0
  160. data/lib/solargraph/parser/snippet.rb +13 -0
  161. data/lib/solargraph/pin.rb +39 -0
  162. data/lib/solargraph/pin/attribute.rb +49 -0
  163. data/lib/solargraph/pin/base.rb +296 -0
  164. data/lib/solargraph/pin/base_method.rb +141 -0
  165. data/lib/solargraph/pin/base_variable.rb +84 -0
  166. data/lib/solargraph/pin/block.rb +48 -0
  167. data/lib/solargraph/pin/class_variable.rb +8 -0
  168. data/lib/solargraph/pin/closure.rb +37 -0
  169. data/lib/solargraph/pin/common.rb +70 -0
  170. data/lib/solargraph/pin/constant.rb +43 -0
  171. data/lib/solargraph/pin/conversions.rb +97 -0
  172. data/lib/solargraph/pin/documenting.rb +110 -0
  173. data/lib/solargraph/pin/duck_method.rb +16 -0
  174. data/lib/solargraph/pin/global_variable.rb +8 -0
  175. data/lib/solargraph/pin/instance_variable.rb +30 -0
  176. data/lib/solargraph/pin/keyword.rb +15 -0
  177. data/lib/solargraph/pin/keyword_param.rb +8 -0
  178. data/lib/solargraph/pin/local_variable.rb +21 -0
  179. data/lib/solargraph/pin/localized.rb +43 -0
  180. data/lib/solargraph/pin/method.rb +111 -0
  181. data/lib/solargraph/pin/method_alias.rb +31 -0
  182. data/lib/solargraph/pin/namespace.rb +85 -0
  183. data/lib/solargraph/pin/parameter.rb +206 -0
  184. data/lib/solargraph/pin/proxy_type.rb +29 -0
  185. data/lib/solargraph/pin/reference.rb +14 -0
  186. data/lib/solargraph/pin/reference/extend.rb +10 -0
  187. data/lib/solargraph/pin/reference/include.rb +10 -0
  188. data/lib/solargraph/pin/reference/override.rb +29 -0
  189. data/lib/solargraph/pin/reference/prepend.rb +10 -0
  190. data/lib/solargraph/pin/reference/require.rb +14 -0
  191. data/lib/solargraph/pin/reference/superclass.rb +10 -0
  192. data/lib/solargraph/pin/singleton.rb +11 -0
  193. data/lib/solargraph/pin/symbol.rb +47 -0
  194. data/lib/solargraph/pin/yard_pin.rb +12 -0
  195. data/lib/solargraph/pin/yard_pin/constant.rb +25 -0
  196. data/lib/solargraph/pin/yard_pin/method.rb +65 -0
  197. data/lib/solargraph/pin/yard_pin/namespace.rb +27 -0
  198. data/lib/solargraph/pin/yard_pin/yard_mixin.rb +26 -0
  199. data/lib/solargraph/position.rb +112 -0
  200. data/lib/solargraph/range.rb +95 -0
  201. data/lib/solargraph/server_methods.rb +16 -0
  202. data/lib/solargraph/shell.rb +221 -0
  203. data/lib/solargraph/source.rb +533 -0
  204. data/lib/solargraph/source/chain.rb +172 -0
  205. data/lib/solargraph/source/chain/block_variable.rb +13 -0
  206. data/lib/solargraph/source/chain/call.rb +203 -0
  207. data/lib/solargraph/source/chain/class_variable.rb +13 -0
  208. data/lib/solargraph/source/chain/constant.rb +75 -0
  209. data/lib/solargraph/source/chain/global_variable.rb +13 -0
  210. data/lib/solargraph/source/chain/head.rb +35 -0
  211. data/lib/solargraph/source/chain/instance_variable.rb +13 -0
  212. data/lib/solargraph/source/chain/link.rb +67 -0
  213. data/lib/solargraph/source/chain/literal.rb +23 -0
  214. data/lib/solargraph/source/chain/or.rb +23 -0
  215. data/lib/solargraph/source/chain/variable.rb +13 -0
  216. data/lib/solargraph/source/chain/z_super.rb +184 -0
  217. data/lib/solargraph/source/change.rb +79 -0
  218. data/lib/solargraph/source/cursor.rb +164 -0
  219. data/lib/solargraph/source/encoding_fixes.rb +23 -0
  220. data/lib/solargraph/source/source_chainer.rb +189 -0
  221. data/lib/solargraph/source/updater.rb +54 -0
  222. data/lib/solargraph/source_map.rb +170 -0
  223. data/lib/solargraph/source_map/clip.rb +190 -0
  224. data/lib/solargraph/source_map/completion.rb +23 -0
  225. data/lib/solargraph/source_map/mapper.rb +199 -0
  226. data/lib/solargraph/stdlib_fills.rb +32 -0
  227. data/lib/solargraph/type_checker.rb +498 -0
  228. data/lib/solargraph/type_checker/checks.rb +95 -0
  229. data/lib/solargraph/type_checker/param_def.rb +35 -0
  230. data/lib/solargraph/type_checker/problem.rb +32 -0
  231. data/lib/solargraph/type_checker/rules.rb +53 -0
  232. data/lib/solargraph/version.rb +5 -0
  233. data/lib/solargraph/views/_method.erb +62 -0
  234. data/lib/solargraph/views/_name_type_tag.erb +10 -0
  235. data/lib/solargraph/views/_namespace.erb +24 -0
  236. data/lib/solargraph/views/document.erb +23 -0
  237. data/lib/solargraph/views/environment.erb +58 -0
  238. data/lib/solargraph/views/layout.erb +44 -0
  239. data/lib/solargraph/views/search.erb +11 -0
  240. data/lib/solargraph/workspace.rb +209 -0
  241. data/lib/solargraph/workspace/config.rb +215 -0
  242. data/lib/solargraph/yard_map.rb +420 -0
  243. data/lib/solargraph/yard_map/cache.rb +19 -0
  244. data/lib/solargraph/yard_map/core_docs.rb +170 -0
  245. data/lib/solargraph/yard_map/core_gen.rb +76 -0
  246. data/lib/solargraph/yard_map/mapper.rb +71 -0
  247. data/lib/solargraph/yard_map/rdoc_to_yard.rb +136 -0
  248. data/lib/yard-solargraph.rb +30 -0
  249. data/solargraph.gemspec +41 -0
  250. data/travis-bundler.rb +11 -0
  251. data/yardoc/2.2.2.tar.gz +0 -0
  252. metadata +575 -0
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Diagnostics
5
+ # Utility methods for the RuboCop diagnostics reporter.
6
+ #
7
+ module RubocopHelpers
8
+ module_function
9
+
10
+ # Generate command-line options for the specified filename and code.
11
+ #
12
+ # @param filename [String]
13
+ # @param code [String]
14
+ # @return [Array(Array<String>, Array<String>)]
15
+ def generate_options filename, code
16
+ args = ['-f', 'j']
17
+ rubocop_file = find_rubocop_file(filename)
18
+ args.push('-c', fix_drive_letter(rubocop_file)) unless rubocop_file.nil?
19
+ args.push filename
20
+ base_options = RuboCop::Options.new
21
+ options, paths = base_options.parse(args)
22
+ options[:stdin] = code
23
+ [options, paths]
24
+ end
25
+
26
+ # Find a RuboCop configuration file in a file's directory tree.
27
+ #
28
+ # @param filename [String]
29
+ # @return [String, nil]
30
+ def find_rubocop_file filename
31
+ return nil unless File.exist?(filename)
32
+ filename = File.realpath(filename)
33
+ dir = File.dirname(filename)
34
+ until File.dirname(dir) == dir
35
+ here = File.join(dir, '.rubocop.yml')
36
+ return here if File.exist?(here)
37
+ dir = File.dirname(dir)
38
+ end
39
+ nil
40
+ end
41
+
42
+ # RuboCop internally uses capitalized drive letters for Windows paths,
43
+ # so we need to convert the paths provided to the command.
44
+ #
45
+ # @param path [String]
46
+ # @return [String]
47
+ def fix_drive_letter path
48
+ return path unless path.match(/^[a-z]:/)
49
+ path[0].upcase + path[1..-1]
50
+ end
51
+
52
+ # @todo This is a smelly way to redirect output, but the RuboCop specs do
53
+ # the same thing.
54
+ # @return [String]
55
+ def redirect_stdout
56
+ redir = StringIO.new
57
+ $stdout = redir
58
+ yield if block_given?
59
+ $stdout = STDOUT
60
+ redir.string
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Diagnostics
5
+ # These severity constants match the DiagnosticSeverity constants in the
6
+ # language server protocol.
7
+ #
8
+ module Severities
9
+ ERROR = 1
10
+ WARNING = 2
11
+ INFORMATION = 3
12
+ HINT = 4
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Diagnostics
5
+ # TypeCheck reports methods with undefined return types, untagged
6
+ # parameters, and invalid param tags.
7
+ #
8
+ class TypeCheck < Base
9
+ def diagnose source, api_map
10
+ return [] unless args.include?('always') || api_map.workspaced?(source.filename)
11
+ severity = Diagnostics::Severities::ERROR
12
+ level = (args.reverse.find { |a| ['normal', 'typed', 'strict', 'strong'].include?(a) }) || :normal
13
+ checker = Solargraph::TypeChecker.new(source.filename, api_map: api_map, level: level.to_sym)
14
+ checker.problems
15
+ .sort { |a, b| a.location.range.start.line <=> b.location.range.start.line }
16
+ .map do |problem|
17
+ {
18
+ range: extract_first_line(problem.location, source),
19
+ severity: severity,
20
+ source: 'Typecheck',
21
+ message: problem.message
22
+ }
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ # @param location [Location]
29
+ # @param source [Source]
30
+ # @return [Hash]
31
+ def extract_first_line location, source
32
+ return location.range.to_hash if location.range.start.line == location.range.ending.line
33
+ {
34
+ start: {
35
+ line: location.range.start.line,
36
+ character: location.range.start.character
37
+ },
38
+ end: {
39
+ line: location.range.start.line,
40
+ character: last_character(location.range.start, source)
41
+ }
42
+ }
43
+ end
44
+
45
+ # @param position [Solargraph::Position]
46
+ # @param source [Solargraph::Source]
47
+ # @return [Integer]
48
+ def last_character position, source
49
+ cursor = Position.to_offset(source.code, position)
50
+ source.code.index(/[\r\n]/, cursor) || source.code.length
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Diagnostics
5
+ class UpdateErrors < Base
6
+ def diagnose source, api_map
7
+ result = []
8
+ combine_ranges(source.code, source.error_ranges).each do |range|
9
+ result.push(
10
+ range: range.to_hash,
11
+ severity: Diagnostics::Severities::ERROR,
12
+ source: 'Solargraph',
13
+ message: 'Syntax error'
14
+ )
15
+ end
16
+ result
17
+ end
18
+
19
+ private
20
+
21
+ # Combine an array of ranges by their starting lines.
22
+ #
23
+ # @param code [String]
24
+ # @param ranges [Array<Range>]
25
+ # @return [Array<Range>]
26
+ def combine_ranges code, ranges
27
+ result = []
28
+ lines = []
29
+ ranges.sort{|a, b| a.start.line <=> b.start.line}.each do |rng|
30
+ next if rng.nil? || lines.include?(rng.start.line)
31
+ lines.push rng.start.line
32
+ next if rng.start.line >= code.lines.length
33
+ scol = code.lines[rng.start.line].index(/[^\s]/) || 0
34
+ ecol = code.lines[rng.start.line].length
35
+ result.push Range.from_to(rng.start.line, scol, rng.start.line, ecol)
36
+ end
37
+ result
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
4
+ require 'json'
5
+ require 'open3'
6
+ require 'shellwords'
7
+ require 'yard'
8
+ require 'fileutils'
9
+
10
+ module Solargraph
11
+ class Documentor
12
+ RDOC_GEMS = %w[
13
+ actioncable actionmailbox actionmailer actionpack actiontext actionview
14
+ activejob activemodel activerecord activestorage activesupport railties
15
+ ]
16
+
17
+ def initialize directory, rebuild: false, out: File.new(File::NULL, 'w')
18
+ @directory = directory
19
+ @rebuild = rebuild
20
+ @out = out
21
+ end
22
+
23
+ # @return [Boolean] True if all specs were found and documented.
24
+ def document
25
+ failures = 0
26
+ Documentor.specs_from_bundle(@directory).each_pair do |name, version|
27
+ yd = YARD::Registry.yardoc_file_for_gem(name, "= #{version}")
28
+ if !yd || @rebuild
29
+ FileUtils.safe_unlink File.join(YardMap::CoreDocs.cache_dir, 'gems', "#{name}-#{version}.ser")
30
+ @out.puts "Documenting #{name} #{version}"
31
+ `yard gems #{name} #{version} #{@rebuild ? '--rebuild' : ''}`
32
+ yd = YARD::Registry.yardoc_file_for_gem(name, "= #{version}")
33
+ # HACK: Ignore errors documenting bundler
34
+ if !yd && name != 'bundler'
35
+ @out.puts "#{name} #{version} YARD documentation failed"
36
+ failures += 1
37
+ end
38
+ end
39
+ if yd && RDOC_GEMS.include?(name)
40
+ cache = File.join(Solargraph::YardMap::CoreDocs.cache_dir, 'gems', "#{name}-#{version}", 'yardoc')
41
+ if !File.exist?(cache) || @rebuild
42
+ @out.puts "Caching custom documentation for #{name} #{version}"
43
+ spec = Gem::Specification.find_by_name(name, "= #{version}")
44
+ Solargraph::YardMap::RdocToYard.run(spec)
45
+ end
46
+ end
47
+ end
48
+ if failures > 0
49
+ @out.puts "#{failures} gem#{failures == 1 ? '' : 's'} could not be documented. You might need to run `bundle install`."
50
+ end
51
+ failures == 0
52
+ rescue Solargraph::BundleNotFoundError => e
53
+ @out.puts "[#{e.class}] #{e.message}"
54
+ @out.puts "No bundled gems are available in #{@directory}"
55
+ false
56
+ end
57
+
58
+ def self.specs_from_bundle directory
59
+ Solargraph.with_clean_env do
60
+ Dir.chdir directory do
61
+ cmd = [
62
+ 'bundle', 'exec', 'ruby', '-e',
63
+ "require 'bundler'; require 'json'; puts Bundler.definition.specs_for([:default]).map { |spec| [spec.name, spec.version] }.to_h.to_json"
64
+ ]
65
+ o, e, s = Open3.capture3(*cmd)
66
+ if s.success?
67
+ o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
68
+ else
69
+ Solargraph.logger.warn e
70
+ raise BundleNotFoundError, "Failed to load gems from bundle at #{directory}"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Environ
5
+ # @return [Array<String>]
6
+ attr_reader :requires
7
+
8
+ # @return [Array<String>]
9
+ attr_reader :domains
10
+
11
+ # @return [Array<Pin::Reference::Override>]
12
+ attr_reader :overrides
13
+
14
+ # @param requires [Array<String>]
15
+ # @param domains [Array<String>]
16
+ # @param overrides [Array<Pin::Reference::Override>]
17
+ def initialize requires: [], domains: [], overrides: []
18
+ @requires = requires
19
+ @domains = domains
20
+ @overrides = overrides
21
+ end
22
+
23
+ # @return [self]
24
+ def clear
25
+ domains.clear
26
+ requires.clear
27
+ overrides.clear
28
+ self
29
+ end
30
+
31
+ # @param other [Environ]
32
+ # @return [self]
33
+ def merge other
34
+ domains.concat other.domains
35
+ requires.concat other.requires
36
+ overrides.concat other.overrides
37
+ self
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solargraph/language_server/error_codes'
4
+ require 'solargraph/language_server/completion_item_kinds'
5
+ require 'solargraph/language_server/symbol_kinds'
6
+
7
+ module Solargraph
8
+ # The LanguageServer namespace contains the classes and modules that compose
9
+ # concrete implementations of language servers.
10
+ #
11
+ module LanguageServer
12
+ autoload :Host, 'solargraph/language_server/host'
13
+ autoload :Message, 'solargraph/language_server/message'
14
+ autoload :UriHelpers, 'solargraph/language_server/uri_helpers'
15
+ autoload :MessageTypes, 'solargraph/language_server/message_types'
16
+ autoload :Request, 'solargraph/language_server/request'
17
+ autoload :Transport, 'solargraph/language_server/transport'
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module LanguageServer
5
+ # The CompletionItemKind constants for the language server protocol.
6
+ #
7
+ module CompletionItemKinds
8
+ TEXT = 1
9
+ METHOD = 2
10
+ FUNCTION = 3
11
+ CONSTRUCTOR = 4
12
+ FIELD = 5
13
+ VARIABLE = 6
14
+ CLASS = 7
15
+ INTERFACE = 8
16
+ MODULE = 9
17
+ PROPERTY = 10
18
+ UNIT = 11
19
+ VALUE = 12
20
+ ENUM = 13
21
+ KEYWORD = 14
22
+ SNIPPET = 15
23
+ COLOR = 16
24
+ FILE = 17
25
+ REFERENCE = 18
26
+ FOLDER = 19
27
+ ENUM_MEMBER = 20
28
+ CONSTANT = 21
29
+ STRUCT = 22
30
+ EVENT = 23
31
+ OPERATOR = 24
32
+ TYPE_PARAMETER = 25
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module LanguageServer
5
+ # The ErrorCode constants for the language server protocol.
6
+ #
7
+ module ErrorCodes
8
+ PARSE_ERROR = -32700
9
+ INVALID_REQUEST = -32600
10
+ METHOD_NOT_FOUND = -32601
11
+ INVALID_PARAMS = -32602
12
+ INTERNAL_ERROR = -32603
13
+ SERVER_ERROR_START = -32099
14
+ SERVER_ERROR_END = -32000
15
+ SERVER_NOT_INITIALIZED = -32002
16
+ UNKNOWN_ERROR_CODE = -32001
17
+ REQUEST_CANCELLED = -32800
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,741 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'observer'
4
+ require 'set'
5
+
6
+ module Solargraph
7
+ module LanguageServer
8
+ # The language server protocol's data provider. Hosts are responsible for
9
+ # querying the library and processing messages. They also provide thread
10
+ # safety for multi-threaded transports.
11
+ #
12
+ class Host
13
+ autoload :Diagnoser, 'solargraph/language_server/host/diagnoser'
14
+ autoload :Cataloger, 'solargraph/language_server/host/cataloger'
15
+ autoload :Sources, 'solargraph/language_server/host/sources'
16
+ autoload :Dispatch, 'solargraph/language_server/host/dispatch'
17
+
18
+ include UriHelpers
19
+ include Logging
20
+ include Dispatch
21
+ include Observable
22
+
23
+ attr_writer :client_capabilities
24
+
25
+ def initialize
26
+ @cancel_semaphore = Mutex.new
27
+ @buffer_semaphore = Mutex.new
28
+ @register_semaphore = Mutex.new
29
+ @cancel = []
30
+ @buffer = String.new
31
+ @stopped = true
32
+ @next_request_id = 0
33
+ @dynamic_capabilities = Set.new
34
+ @registered_capabilities = Set.new
35
+ end
36
+
37
+ # Start asynchronous process handling.
38
+ #
39
+ # @return [void]
40
+ def start
41
+ return unless stopped?
42
+ @stopped = false
43
+ diagnoser.start
44
+ cataloger.start
45
+ sources.start
46
+ end
47
+
48
+ # Update the configuration options with the provided hash.
49
+ #
50
+ # @param update [Hash]
51
+ # @return [void]
52
+ def configure update
53
+ return if update.nil?
54
+ options.merge! update
55
+ logger.level = LOG_LEVELS[options['logLevel']] || DEFAULT_LOG_LEVEL
56
+ end
57
+
58
+ # @return [Hash]
59
+ def options
60
+ @options ||= default_configuration
61
+ end
62
+
63
+ # Cancel the method with the specified ID.
64
+ #
65
+ # @param id [Integer]
66
+ # @return [void]
67
+ def cancel id
68
+ @cancel_semaphore.synchronize { @cancel.push id }
69
+ end
70
+
71
+ # True if the host received a request to cancel the method with the
72
+ # specified ID.
73
+ #
74
+ # @param id [Integer]
75
+ # @return [Boolean]
76
+ def cancel? id
77
+ result = false
78
+ @cancel_semaphore.synchronize { result = @cancel.include? id }
79
+ result
80
+ end
81
+
82
+ # Delete the specified ID from the list of cancelled IDs if it exists.
83
+ #
84
+ # @param id [Integer]
85
+ # @return [void]
86
+ def clear id
87
+ @cancel_semaphore.synchronize { @cancel.delete id }
88
+ end
89
+
90
+ # Start processing a request from the client. After the message is
91
+ # processed, the transport is responsible for sending the response.
92
+ #
93
+ # @param request [Hash] The contents of the message.
94
+ # @return [Solargraph::LanguageServer::Message::Base] The message handler.
95
+ def receive request
96
+ if request['method']
97
+ logger.info "Server received #{request['method']}"
98
+ logger.debug request
99
+ message = Message.select(request['method']).new(self, request)
100
+ begin
101
+ message.process
102
+ rescue StandardError => e
103
+ logger.warn "Error processing request: [#{e.class}] #{e.message}"
104
+ logger.warn e.backtrace.join("\n")
105
+ message.set_error Solargraph::LanguageServer::ErrorCodes::INTERNAL_ERROR, "[#{e.class}] #{e.message}"
106
+ end
107
+ message
108
+ elsif request['id']
109
+ # @todo What if the id is invalid?
110
+ requests[request['id']].process(request['result'])
111
+ requests.delete request['id']
112
+ else
113
+ logger.warn "Invalid message received."
114
+ logger.debug request
115
+ end
116
+ end
117
+
118
+ # Respond to a notification that a file was created in the workspace.
119
+ # The libraries will determine whether the file should be merged; see
120
+ # Solargraph::Library#create_from_disk.
121
+ #
122
+ # @param uri [String] The file uri.
123
+ # @return [Boolean] True if a library accepted the file.
124
+ def create uri
125
+ filename = uri_to_file(uri)
126
+ result = false
127
+ libraries.each do |lib|
128
+ result = true if lib.create_from_disk(filename)
129
+ end
130
+ diagnoser.schedule uri if open?(uri)
131
+ result
132
+ end
133
+
134
+ # Delete the specified file from the library.
135
+ #
136
+ # @param uri [String] The file uri.
137
+ # @return [void]
138
+ def delete uri
139
+ filename = uri_to_file(uri)
140
+ libraries.each do |lib|
141
+ lib.delete(filename)
142
+ end
143
+ send_notification "textDocument/publishDiagnostics", {
144
+ uri: uri,
145
+ diagnostics: []
146
+ }
147
+ end
148
+
149
+ # Open the specified file in the library.
150
+ #
151
+ # @param uri [String] The file uri.
152
+ # @param text [String] The contents of the file.
153
+ # @param version [Integer] A version number.
154
+ # @return [void]
155
+ def open uri, text, version
156
+ src = sources.open(uri, text, version)
157
+ libraries.each do |lib|
158
+ lib.merge src
159
+ end
160
+ diagnoser.schedule uri
161
+ end
162
+
163
+ # @param uri [String]
164
+ # @return [void]
165
+ def open_from_disk uri
166
+ sources.open_from_disk(uri)
167
+ library = library_for(uri)
168
+ # library.open_from_disk uri_to_file(uri)
169
+ diagnoser.schedule uri
170
+ end
171
+
172
+ # True if the specified file is currently open in the library.
173
+ #
174
+ # @param uri [String]
175
+ # @return [Boolean]
176
+ def open? uri
177
+ sources.include? uri
178
+ end
179
+
180
+ # Close the file specified by the URI.
181
+ #
182
+ # @param uri [String]
183
+ # @return [void]
184
+ def close uri
185
+ logger.info "Closing #{uri}"
186
+ sources.close uri
187
+ diagnoser.schedule uri
188
+ end
189
+
190
+ # @param uri [String]
191
+ # @return [void]
192
+ def diagnose uri
193
+ if sources.include?(uri)
194
+ library = library_for(uri)
195
+ if library.synchronized?
196
+ logger.info "Diagnosing #{uri}"
197
+ begin
198
+ results = library.diagnose uri_to_file(uri)
199
+ send_notification "textDocument/publishDiagnostics", {
200
+ uri: uri,
201
+ diagnostics: results
202
+ }
203
+ rescue DiagnosticsError => e
204
+ logger.warn "Error in diagnostics: #{e.message}"
205
+ options['diagnostics'] = false
206
+ send_notification 'window/showMessage', {
207
+ type: LanguageServer::MessageTypes::ERROR,
208
+ message: "Error in diagnostics: #{e.message}"
209
+ }
210
+ rescue FileNotFoundError => e
211
+ # @todo This appears to happen when an external file is open and
212
+ # scheduled for diagnosis, but the file was closed (i.e., the
213
+ # editor moved to a different file) before diagnosis started
214
+ logger.warn "Unable to diagnose #{uri} : #{e.message}"
215
+ send_notification 'textDocument/publishDiagnostics', {
216
+ uri: uri,
217
+ diagnostics: []
218
+ }
219
+ end
220
+ else
221
+ logger.info "Deferring diagnosis of #{uri}"
222
+ diagnoser.schedule uri
223
+ end
224
+ else
225
+ send_notification 'textDocument/publishDiagnostics', {
226
+ uri: uri,
227
+ diagnostics: []
228
+ }
229
+ end
230
+ end
231
+
232
+ # Update a document from the parameters of a textDocument/didChange
233
+ # method.
234
+ #
235
+ # @param params [Hash]
236
+ # @return [void]
237
+ def change params
238
+ updater = generate_updater(params)
239
+ sources.async_update params['textDocument']['uri'], updater
240
+ diagnoser.schedule params['textDocument']['uri']
241
+ end
242
+
243
+ # Queue a message to be sent to the client.
244
+ #
245
+ # @param message [String] The message to send.
246
+ # @return [void]
247
+ def queue message
248
+ @buffer_semaphore.synchronize { @buffer += message }
249
+ changed
250
+ notify_observers
251
+ end
252
+
253
+ # Clear the message buffer and return the most recent data.
254
+ #
255
+ # @return [String] The most recent data or an empty string.
256
+ def flush
257
+ tmp = ''
258
+ @buffer_semaphore.synchronize do
259
+ tmp = @buffer.clone
260
+ @buffer.clear
261
+ end
262
+ tmp
263
+ end
264
+
265
+ # Prepare a library for the specified directory.
266
+ #
267
+ # @param directory [String]
268
+ # @param name [String, nil]
269
+ # @return [void]
270
+ def prepare directory, name = nil
271
+ # No need to create a library without a directory. The generic library
272
+ # will handle it.
273
+ return if directory.nil?
274
+ logger.info "Preparing library for #{directory}"
275
+ path = ''
276
+ path = normalize_separators(directory) unless directory.nil?
277
+ begin
278
+ lib = Solargraph::Library.load(path, name)
279
+ libraries.push lib
280
+ rescue WorkspaceTooLargeError => e
281
+ send_notification 'window/showMessage', {
282
+ 'type' => Solargraph::LanguageServer::MessageTypes::WARNING,
283
+ 'message' => e.message
284
+ }
285
+ end
286
+ end
287
+
288
+ # Prepare multiple folders.
289
+ #
290
+ # @param array [Array<Hash{String => String}>]
291
+ # @return [void]
292
+ def prepare_folders array
293
+ return if array.nil?
294
+ array.each do |folder|
295
+ prepare uri_to_file(folder['uri']), folder['name']
296
+ end
297
+ end
298
+
299
+ # Remove a directory.
300
+ #
301
+ # @param directory [String]
302
+ # @return [void]
303
+ def remove directory
304
+ logger.info "Removing library for #{directory}"
305
+ # @param lib [Library]
306
+ libraries.delete_if do |lib|
307
+ next false if lib.workspace.directory != directory
308
+ true
309
+ end
310
+ end
311
+
312
+ # @param array [Array<Hash>]
313
+ # @return [void]
314
+ def remove_folders array
315
+ array.each do |folder|
316
+ remove uri_to_file(folder['uri'])
317
+ end
318
+ end
319
+
320
+ # @return [Array<String>]
321
+ def folders
322
+ libraries.map { |lib| lib.workspace.directory }
323
+ end
324
+
325
+ # Send a notification to the client.
326
+ #
327
+ # @param method [String] The message method
328
+ # @param params [Hash] The method parameters
329
+ # @return [void]
330
+ def send_notification method, params
331
+ response = {
332
+ jsonrpc: "2.0",
333
+ method: method,
334
+ params: params
335
+ }
336
+ json = response.to_json
337
+ envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
338
+ queue envelope
339
+ logger.info "Server sent #{method}"
340
+ logger.debug params
341
+ end
342
+
343
+ # Send a request to the client and execute the provided block to process
344
+ # the response. If an ID is not provided, the host will use an auto-
345
+ # incrementing integer.
346
+ #
347
+ # @param method [String] The message method
348
+ # @param params [Hash] The method parameters
349
+ # @param block [Proc] The block that processes the response
350
+ # @yieldparam [Hash] The result sent by the client
351
+ # @return [void]
352
+ def send_request method, params, &block
353
+ message = {
354
+ jsonrpc: "2.0",
355
+ method: method,
356
+ params: params,
357
+ id: @next_request_id
358
+ }
359
+ json = message.to_json
360
+ requests[@next_request_id] = Request.new(@next_request_id, &block)
361
+ envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
362
+ queue envelope
363
+ @next_request_id += 1
364
+ logger.info "Server sent #{method}"
365
+ logger.debug params
366
+ end
367
+
368
+ # Register the methods as capabilities with the client.
369
+ # This method will avoid duplicating registrations and ignore methods
370
+ # that were not flagged for dynamic registration by the client.
371
+ #
372
+ # @param methods [Array<String>] The methods to register
373
+ # @return [void]
374
+ def register_capabilities methods
375
+ logger.debug "Registering capabilities: #{methods}"
376
+ registrations = methods.select{|m| can_register?(m) and !registered?(m)}.map { |m|
377
+ @registered_capabilities.add m
378
+ {
379
+ id: m,
380
+ method: m,
381
+ registerOptions: dynamic_capability_options[m]
382
+ }
383
+ }
384
+ return if registrations.empty?
385
+ @register_semaphore.synchronize do
386
+ send_request 'client/registerCapability', {
387
+ registrations: registrations
388
+ }
389
+ end
390
+ end
391
+
392
+ # Unregister the methods with the client.
393
+ # This method will avoid duplicating unregistrations and ignore methods
394
+ # that were not flagged for dynamic registration by the client.
395
+ #
396
+ # @param methods [Array<String>] The methods to unregister
397
+ # @return [void]
398
+ def unregister_capabilities methods
399
+ logger.debug "Unregistering capabilities: #{methods}"
400
+ unregisterations = methods.select{|m| registered?(m)}.map{ |m|
401
+ @registered_capabilities.delete m
402
+ {
403
+ id: m,
404
+ method: m
405
+ }
406
+ }
407
+ return if unregisterations.empty?
408
+ @register_semaphore.synchronize do
409
+ send_request 'client/unregisterCapability', {
410
+ unregisterations: unregisterations
411
+ }
412
+ end
413
+ end
414
+
415
+ # Flag a method as available for dynamic registration.
416
+ #
417
+ # @param method [String] The method name, e.g., 'textDocument/completion'
418
+ # @return [void]
419
+ def allow_registration method
420
+ @register_semaphore.synchronize do
421
+ @dynamic_capabilities.add method
422
+ end
423
+ end
424
+
425
+ # True if the specified LSP method can be dynamically registered.
426
+ #
427
+ # @param method [String]
428
+ # @return [Boolean]
429
+ def can_register? method
430
+ @dynamic_capabilities.include?(method)
431
+ end
432
+
433
+ # True if the specified method has been registered.
434
+ #
435
+ # @param method [String] The method name, e.g., 'textDocument/completion'
436
+ # @return [Boolean]
437
+ def registered? method
438
+ @registered_capabilities.include?(method)
439
+ end
440
+
441
+ def synchronizing?
442
+ !libraries.all?(&:synchronized?)
443
+ end
444
+
445
+ # @return [void]
446
+ def stop
447
+ return if @stopped
448
+ @stopped = true
449
+ cataloger.stop
450
+ diagnoser.stop
451
+ sources.stop
452
+ changed
453
+ notify_observers
454
+ end
455
+
456
+ def stopped?
457
+ @stopped
458
+ end
459
+
460
+ # Locate multiple pins that match a completion item. The first match is
461
+ # based on the corresponding location in a library source if available.
462
+ # Subsequent matches are based on path.
463
+ #
464
+ # @param params [Hash] A hash representation of a completion item
465
+ # @return [Array<Pin::Base>]
466
+ def locate_pins params
467
+ return [] unless params['data'] && params['data']['uri']
468
+ library = library_for(params['data']['uri'])
469
+ result = []
470
+ if params['data']['location']
471
+ location = Location.new(
472
+ params['data']['location']['filename'],
473
+ Range.from_to(
474
+ params['data']['location']['range']['start']['line'],
475
+ params['data']['location']['range']['start']['character'],
476
+ params['data']['location']['range']['end']['line'],
477
+ params['data']['location']['range']['end']['character']
478
+ )
479
+ )
480
+ result.concat library.locate_pins(location).select{ |pin| pin.name == params['label'] }
481
+ end
482
+ if params['data']['path']
483
+ result.concat library.path_pins(params['data']['path'])
484
+ end
485
+ # Selecting by both location and path can result in duplicate pins
486
+ result.uniq { |p| [p.path, p.location] }
487
+ end
488
+
489
+ # @param uri [String]
490
+ # @return [String]
491
+ def read_text uri
492
+ library = library_for(uri)
493
+ filename = uri_to_file(uri)
494
+ library.read_text(filename)
495
+ end
496
+
497
+ # @param uri [String]
498
+ # @param line [Integer]
499
+ # @param column [Integer]
500
+ # @return [Solargraph::SourceMap::Completion]
501
+ def completions_at uri, line, column
502
+ library = library_for(uri)
503
+ library.completions_at uri_to_file(uri), line, column
504
+ end
505
+
506
+ # @param uri [String]
507
+ # @param line [Integer]
508
+ # @param column [Integer]
509
+ # @return [Array<Solargraph::Pin::Base>]
510
+ def definitions_at uri, line, column
511
+ library = library_for(uri)
512
+ library.definitions_at(uri_to_file(uri), line, column)
513
+ end
514
+
515
+ # @param uri [String]
516
+ # @param line [Integer]
517
+ # @param column [Integer]
518
+ # @return [Array<Solargraph::Pin::Base>]
519
+ def signatures_at uri, line, column
520
+ library = library_for(uri)
521
+ library.signatures_at(uri_to_file(uri), line, column)
522
+ end
523
+
524
+ # @param uri [String]
525
+ # @param line [Integer]
526
+ # @param column [Integer]
527
+ # @param strip [Boolean] Strip special characters from variable names
528
+ # @return [Array<Solargraph::Range>]
529
+ def references_from uri, line, column, strip: true
530
+ library = library_for(uri)
531
+ library.references_from(uri_to_file(uri), line, column, strip: strip)
532
+ end
533
+
534
+ # @param query [String]
535
+ # @return [Array<Solargraph::Pin::Base>]
536
+ def query_symbols query
537
+ result = []
538
+ (libraries + [generic_library]).each { |lib| result.concat lib.query_symbols(query) }
539
+ result.uniq
540
+ end
541
+
542
+ # @param query [String]
543
+ # @return [Array<String>]
544
+ def search query
545
+ result = []
546
+ libraries.each { |lib| result.concat lib.search(query) }
547
+ result
548
+ end
549
+
550
+ # @param query [String]
551
+ # @return [Array]
552
+ def document query
553
+ result = []
554
+ libraries.each { |lib| result.concat lib.document(query) }
555
+ result
556
+ end
557
+
558
+ # @param uri [String]
559
+ # @return [Array<Solargraph::Pin::Base>]
560
+ def document_symbols uri
561
+ library = library_for(uri)
562
+ # At this level, document symbols should be unique; e.g., a
563
+ # module_function method should return the location for Module.method
564
+ # or Module#method, but not both.
565
+ library.document_symbols(uri_to_file(uri)).uniq(&:location)
566
+ end
567
+
568
+ # Send a notification to the client.
569
+ #
570
+ # @param text [String]
571
+ # @param type [Integer] A MessageType constant
572
+ # @return [void]
573
+ def show_message text, type = LanguageServer::MessageTypes::INFO
574
+ send_notification 'window/showMessage', {
575
+ type: type,
576
+ message: text
577
+ }
578
+ end
579
+
580
+ # Send a notification with optional responses.
581
+ #
582
+ # @param text [String]
583
+ # @param type [Integer] A MessageType constant
584
+ # @param actions [Array<String>] Response options for the client
585
+ # @param block The block that processes the response
586
+ # @yieldparam [String] The action received from the client
587
+ # @return [void]
588
+ def show_message_request text, type, actions, &block
589
+ send_request 'window/showMessageRequest', {
590
+ type: type,
591
+ message: text,
592
+ actions: actions
593
+ }, &block
594
+ end
595
+
596
+ # Get a list of IDs for server requests that are waiting for responses
597
+ # from the client.
598
+ #
599
+ # @return [Array<Integer>]
600
+ def pending_requests
601
+ requests.keys
602
+ end
603
+
604
+ # @return [Hash{String => Object}]
605
+ def default_configuration
606
+ {
607
+ 'completion' => true,
608
+ 'hover' => true,
609
+ 'symbols' => true,
610
+ 'definitions' => true,
611
+ 'rename' => true,
612
+ 'references' => true,
613
+ 'autoformat' => false,
614
+ 'diagnostics' => false,
615
+ 'formatting' => false,
616
+ 'folding' => true,
617
+ 'logLevel' => 'warn'
618
+ }
619
+ end
620
+
621
+ # @param uri [String]
622
+ # @return [Array<Range>]
623
+ def folding_ranges uri
624
+ sources.find(uri).folding_ranges
625
+ end
626
+
627
+ # @return [void]
628
+ def catalog
629
+ libraries.each(&:catalog)
630
+ end
631
+
632
+ def client_capabilities
633
+ @client_capabilities ||= {}
634
+ end
635
+
636
+ private
637
+
638
+ # @return [Diagnoser]
639
+ def diagnoser
640
+ @diagnoser ||= Diagnoser.new(self)
641
+ end
642
+
643
+ # @return [Cataloger]
644
+ def cataloger
645
+ @cataloger ||= Cataloger.new(self)
646
+ end
647
+
648
+ # A hash of client requests by ID. The host uses this to keep track of
649
+ # pending responses.
650
+ #
651
+ # @return [Hash{Integer => Hash}]
652
+ def requests
653
+ @requests ||= {}
654
+ end
655
+
656
+ # @param path [String]
657
+ # @return [String]
658
+ def normalize_separators path
659
+ return path if File::ALT_SEPARATOR.nil?
660
+ path.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
661
+ end
662
+
663
+ # @param params [Hash]
664
+ # @return [Source::Updater]
665
+ def generate_updater params
666
+ changes = []
667
+ params['contentChanges'].each do |chng|
668
+ changes.push Solargraph::Source::Change.new(
669
+ (chng['range'].nil? ?
670
+ nil :
671
+ Solargraph::Range.from_to(chng['range']['start']['line'], chng['range']['start']['character'], chng['range']['end']['line'], chng['range']['end']['character'])
672
+ ),
673
+ chng['text']
674
+ )
675
+ end
676
+ Solargraph::Source::Updater.new(
677
+ uri_to_file(params['textDocument']['uri']),
678
+ params['textDocument']['version'],
679
+ changes
680
+ )
681
+ end
682
+
683
+ # @return [Hash]
684
+ def dynamic_capability_options
685
+ @dynamic_capability_options ||= {
686
+ # textDocumentSync: 2, # @todo What should this be?
687
+ 'textDocument/completion' => {
688
+ resolveProvider: true,
689
+ triggerCharacters: ['.', ':', '@']
690
+ },
691
+ # hoverProvider: true,
692
+ # definitionProvider: true,
693
+ 'textDocument/signatureHelp' => {
694
+ triggerCharacters: ['(', ',', ' ']
695
+ },
696
+ # documentFormattingProvider: true,
697
+ 'textDocument/onTypeFormatting' => {
698
+ firstTriggerCharacter: '{',
699
+ moreTriggerCharacter: ['(']
700
+ },
701
+ # documentSymbolProvider: true,
702
+ # workspaceSymbolProvider: true,
703
+ # workspace: {
704
+ # workspaceFolders: {
705
+ # supported: true,
706
+ # changeNotifications: true
707
+ # }
708
+ # }
709
+ 'textDocument/definition' => {
710
+ definitionProvider: true
711
+ },
712
+ 'textDocument/references' => {
713
+ referencesProvider: true
714
+ },
715
+ 'textDocument/rename' => {
716
+ renameProvider: prepare_rename? ? { prepareProvider: true } : true
717
+ },
718
+ 'textDocument/documentSymbol' => {
719
+ documentSymbolProvider: true
720
+ },
721
+ 'workspace/symbol' => {
722
+ workspaceSymbolProvider: true
723
+ },
724
+ 'textDocument/formatting' => {
725
+ formattingProvider: true
726
+ },
727
+ 'textDocument/foldingRange' => {
728
+ foldingRangeProvider: true
729
+ },
730
+ 'textDocument/codeAction' => {
731
+ codeActionProvider: true
732
+ }
733
+ }
734
+ end
735
+
736
+ def prepare_rename?
737
+ client_capabilities['rename'] && client_capabilities['rename']['prepareSupport']
738
+ end
739
+ end
740
+ end
741
+ end