ruby_language_server 0.2.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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.txt +67 -0
  3. data/FAQ_ROADMAP.md +30 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +111 -0
  6. data/Guardfile +36 -0
  7. data/LICENSE +21 -0
  8. data/Makefile +35 -0
  9. data/README.md +55 -0
  10. data/Rakefile +17 -0
  11. data/bin/console +15 -0
  12. data/bin/setup +8 -0
  13. data/exe/ruby_language_server +9 -0
  14. data/lib/ruby_language_server/code_file.rb +129 -0
  15. data/lib/ruby_language_server/completion.rb +87 -0
  16. data/lib/ruby_language_server/gem_installer.rb +24 -0
  17. data/lib/ruby_language_server/good_cop.rb +125 -0
  18. data/lib/ruby_language_server/io.rb +130 -0
  19. data/lib/ruby_language_server/line_context.rb +39 -0
  20. data/lib/ruby_language_server/location.rb +29 -0
  21. data/lib/ruby_language_server/logger.rb +14 -0
  22. data/lib/ruby_language_server/project_manager.rb +231 -0
  23. data/lib/ruby_language_server/scope_data/base.rb +23 -0
  24. data/lib/ruby_language_server/scope_data/scope.rb +104 -0
  25. data/lib/ruby_language_server/scope_data/variable.rb +25 -0
  26. data/lib/ruby_language_server/scope_parser.rb +334 -0
  27. data/lib/ruby_language_server/scope_parser_commands/rails_commands.rb +29 -0
  28. data/lib/ruby_language_server/scope_parser_commands/rake_commands.rb +31 -0
  29. data/lib/ruby_language_server/scope_parser_commands/readme.txt +9 -0
  30. data/lib/ruby_language_server/scope_parser_commands/rspec_commands.rb +26 -0
  31. data/lib/ruby_language_server/scope_parser_commands/ruby_commands.rb +70 -0
  32. data/lib/ruby_language_server/server.rb +123 -0
  33. data/lib/ruby_language_server/version.rb +5 -0
  34. data/lib/ruby_language_server.rb +14 -0
  35. data/ruby_language_server.gemspec +56 -0
  36. metadata +293 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 403609bb04e3734281518bc6a175b6e839e6b731d684dd5716f31e91bb7e1d98
4
+ data.tar.gz: b7c597616a85977982dc2ddbcb8b8c7d896a1829904fa19dfbb52951da6895b6
5
+ SHA512:
6
+ metadata.gz: cfbd3c60687d08b1f9e19bc632e9d8f4ceb0f5e579e8d6b9edd7e7cf81403dac9b0c7a795fac403b550950b7c1ef615095ad94bbbefe80662390edbdd16f13b7
7
+ data.tar.gz: 784a22cdadad7ebdf6f25ce35a78e2cff90aa148dbf62c88e650ba42e7ce522df59e9758e9de166f99308e5226b9100505c73a7f9ee7c470575435072926afae
data/CHANGELOG.txt ADDED
@@ -0,0 +1,67 @@
1
+ # Changelog
2
+
3
+ #### 0.2.0 Mon Dec 24 18:19:55 PST 2018
4
+
5
+ * Support for rake files
6
+ * Support for rspec files
7
+ * Removed ripper tags & refactoring
8
+ * Better handling of parameter variables
9
+ * Probably better performance (not measured, but reasonably sure)
10
+
11
+ #### 0.1.1 Fri Dec 21 16:48:46 PST 2018
12
+
13
+ * Fix a few little issues that came to light while updating ide-ruby
14
+
15
+ #### 0.1.0 Fri Dec 21 15:19:00 PST 2018
16
+
17
+ * Refactor server & project_manager
18
+ * fix bugs with def self.method and class << self (https://github.com/kwerle/ide-ruby/issues/19)
19
+ * Add support for additional rubocop gems (https://github.com/kwerle/ide-ruby/issues/17)
20
+
21
+ #### 0.0.9 Thu Oct 18 17:48:35 PDT 2018
22
+
23
+ * Refactor & some RuboCop
24
+ * Fix most of https://github.com/kwerle/ide-ruby/issues/18
25
+ * * Rubocop issues: no inline support,
26
+ * * invalid snake_case warnings,
27
+ * * NOT missing "rubocop failed to load" indicator
28
+ * More work on completion stuff
29
+
30
+ #### 0.0.8 Thu Jul 19 00:01:25 PDT 2018
31
+
32
+ * Bump ruby version
33
+ * Fix the initial background thread
34
+ * Refactor
35
+ * Ignore setting text to the same
36
+
37
+ #### 0.0.7 Thu Jul 12 16:11:26 PDT 2018
38
+
39
+ * Whoops - forgot to merge changes in.
40
+
41
+ #### 0.0.6 Thu Jul 12 16:11:26 PDT 2018
42
+
43
+ * Fix RuboCop again. Sigh.
44
+
45
+ #### 0.0.5 Thu Jul 12 15:07:58 PDT 2018
46
+
47
+ * Parse all project files on startup
48
+ * Fix possible issue with RuboCop config
49
+
50
+ #### 0.0.4 Tue Mar 20 19:39:06 PDT 2018
51
+
52
+ * Provide support for .rubocop.yml. This will work once I update ide-ruby.
53
+
54
+ #### 0.0.3
55
+
56
+ * Uhh. What did I do? Looks like a bunch.
57
+
58
+ #### 0.0.2 Mon Nov 13 20:49:54 PST 2017
59
+
60
+ * Fix rails has_one(etc) so it gets the right parent
61
+
62
+ #### Sat Nov 11 07:59:25 PST 2017
63
+
64
+ * Outline working but buggy (double listing for parents)
65
+ * Go to definition working but lazy (have to visit a file before definitions 'known')
66
+ * Completions working in a very basic way - local scope context only
67
+ * Linter not working (not being called, NYI)
data/FAQ_ROADMAP.md ADDED
@@ -0,0 +1,30 @@
1
+ # Why docker?
2
+
3
+ Docker guarantees me a target environment. I don't have to wonder which version of ruby you have installed or if you can build all the gems. I may also scale this thing to multiple processes and a database and a cache server.
4
+
5
+ One requirement: docker.
6
+
7
+ # Why not [language_server-ruby](https://github.com/mtsmfm/language_server-ruby)
8
+
9
+ My goals are not as high. I just want this stuff working now. I hope some day there will be merging.
10
+
11
+ # A little light on tests?
12
+
13
+ Oh yeah. Tests are mostly for when you know where you're going. I'm doing a whole lot of this by the seat of my pants. OMG, please write tests.
14
+
15
+ # Next?
16
+
17
+ * Getting a definition just looks at the tags - but tags do not include parameters passed in a method - which seems like it should. We are functional, right?
18
+ * Guess a symbol's class.
19
+ * `def some_method(parent)` should guess that parent is a Parent.
20
+ * Be smarter about context and completions
21
+ * `class << self` is busted. Fix it.
22
+ * Fix the outline. Seriously - why isn't it working as expected?
23
+ * Symbol pairs. If I have typed 'foo.bar' a thousand times in my project, the next time I type 'foo.' the IDE had damn well ought to show me 'bar' as a completion option.
24
+ * It makes me very sad that I could not get this working using sockets. Tried and tried and failed.
25
+
26
+ # Then?
27
+
28
+ * Pay special attention to the project's Gemfile. Install all the gems (we can). Integrate with gem server?
29
+ * Full scan of installed gems?
30
+ * Integrate class based scope logic. If I'm in Foo < Bar then I should see Bar's methods at just slightly lower priority than Foo's.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in ruby_language_server.gemspec
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,111 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ruby_language_server (0.2.0)
5
+ amatch
6
+ bundler
7
+ etc
8
+ fuzzy_match
9
+ json
10
+ rubocop
11
+ rubocop-rspec
12
+
13
+ GEM
14
+ remote: https://rubygems.org/
15
+ specs:
16
+ amatch (0.4.0)
17
+ mize
18
+ tins (~> 1.0)
19
+ ast (2.4.0)
20
+ byebug (10.0.2)
21
+ coderay (1.1.2)
22
+ etc (1.0.1)
23
+ ffi (1.9.25)
24
+ formatador (0.2.5)
25
+ fuzzy_match (2.1.0)
26
+ guard (2.15.0)
27
+ formatador (>= 0.2.4)
28
+ listen (>= 2.7, < 4.0)
29
+ lumberjack (>= 1.0.12, < 2.0)
30
+ nenv (~> 0.1)
31
+ notiffany (~> 0.0)
32
+ pry (>= 0.9.12)
33
+ shellany (~> 0.0)
34
+ thor (>= 0.18.1)
35
+ guard-compat (1.2.1)
36
+ guard-minitest (2.4.6)
37
+ guard-compat (~> 1.2)
38
+ minitest (>= 3.0)
39
+ guard-rubocop (1.3.0)
40
+ guard (~> 2.0)
41
+ rubocop (~> 0.20)
42
+ jaro_winkler (1.5.1)
43
+ json (2.1.0)
44
+ listen (3.1.5)
45
+ rb-fsevent (~> 0.9, >= 0.9.4)
46
+ rb-inotify (~> 0.9, >= 0.9.7)
47
+ ruby_dep (~> 1.2)
48
+ lumberjack (1.0.13)
49
+ method_source (0.9.2)
50
+ minitest (5.11.3)
51
+ minitest-color (0.0.2)
52
+ minitest (~> 5)
53
+ mize (0.3.5)
54
+ protocol
55
+ nenv (0.3.0)
56
+ notiffany (0.1.1)
57
+ nenv (~> 0.1)
58
+ shellany (~> 0.0)
59
+ parallel (1.12.1)
60
+ parser (2.5.3.0)
61
+ ast (~> 2.4.0)
62
+ powerpack (0.1.2)
63
+ protocol (1.0.1)
64
+ ruby_parser (~> 3.0)
65
+ pry (0.12.2)
66
+ coderay (~> 1.1.0)
67
+ method_source (~> 0.9.0)
68
+ pry-byebug (3.6.0)
69
+ byebug (~> 10.0)
70
+ pry (~> 0.10)
71
+ rainbow (3.0.0)
72
+ rake (12.3.2)
73
+ rb-fsevent (0.10.3)
74
+ rb-inotify (0.10.0)
75
+ ffi (~> 1.0)
76
+ rubocop (0.61.1)
77
+ jaro_winkler (~> 1.5.1)
78
+ parallel (~> 1.10)
79
+ parser (>= 2.5, != 2.5.1.1)
80
+ powerpack (~> 0.1)
81
+ rainbow (>= 2.2.2, < 4.0)
82
+ ruby-progressbar (~> 1.7)
83
+ unicode-display_width (~> 1.4.0)
84
+ rubocop-rspec (1.30.1)
85
+ rubocop (>= 0.60.0)
86
+ ruby-progressbar (1.10.0)
87
+ ruby_dep (1.5.0)
88
+ ruby_parser (3.12.0)
89
+ sexp_processor (~> 4.9)
90
+ sexp_processor (4.11.0)
91
+ shellany (0.0.1)
92
+ thor (0.20.3)
93
+ tins (1.20.2)
94
+ unicode-display_width (1.4.1)
95
+
96
+ PLATFORMS
97
+ ruby
98
+
99
+ DEPENDENCIES
100
+ guard
101
+ guard-minitest
102
+ guard-rubocop
103
+ minitest
104
+ minitest-color
105
+ pry
106
+ pry-byebug
107
+ rake
108
+ ruby_language_server!
109
+
110
+ BUNDLED WITH
111
+ 1.17.3
data/Guardfile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A sample Guardfile
4
+ # More info at https://github.com/guard/guard#readme
5
+
6
+ ## Uncomment and set this to only include directories you want to watch
7
+ # directories %w(app lib config test spec features) \
8
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
9
+
10
+ ## Note: if you are using the `directories` clause above and you are not
11
+ ## watching the project directory ('.'), then you will want to move
12
+ ## the Guardfile to a watched dir and symlink it back, e.g.
13
+ #
14
+ # $ mkdir config
15
+ # $ mv Guardfile config/
16
+ # $ ln -s config/Guardfile .
17
+ #
18
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
19
+
20
+ guard :minitest, all_after_pass: true do
21
+ # with Minitest::Unit
22
+ # watch(%r{^test/(.*)\/?test_(.*)\.rb$})
23
+ # watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
24
+ # watch(%r{^test/test_helper\.rb$}) { 'test' }
25
+
26
+ # with Minitest::Spec
27
+ watch(%r{^spec/**/(.*)_spec\.rb$})
28
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
29
+ watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
30
+ end
31
+
32
+ guard :rubocop, cli: ['-c', '.rubocop_ruby_language_parser.yml'] do
33
+ watch('.rubocop_ruby_language_parser.yml')
34
+ watch(/.+\.rb$/)
35
+ watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
36
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Kurt Werle
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Makefile ADDED
@@ -0,0 +1,35 @@
1
+ PROJECT_NAME=ruby_language_server
2
+ LOCAL_LINK=-v $(PWD):/tmp/src -w /tmp/src
3
+
4
+ build:
5
+ docker build -t $(PROJECT_NAME) .
6
+
7
+ guard: build
8
+ docker run -it $(LOCAL_LINK) $(PROJECT_NAME) sh -c 'bundle && guard'
9
+
10
+ continuous_development: build
11
+ echo "You are going to want to set the ide-ruby 'Image Name' to local_ruby_language_server"
12
+ sleep 15
13
+ while (true) ; \
14
+ do \
15
+ docker build -t local_ruby_language_server . ; \
16
+ sleep 2 ; \
17
+ done
18
+
19
+ console: build
20
+ docker run -it $(LOCAL_LINK) $(PROJECT_NAME) bin/console
21
+
22
+ shell: build
23
+ docker run -it $(LOCAL_LINK) $(PROJECT_NAME) sh
24
+
25
+ # Just to make sure it works.
26
+ server: build
27
+ docker run -it $(LOCAL_LINK) $(PROJECT_NAME)
28
+
29
+ gem: build
30
+ rm -f $(PROJECT_NAME)*.gem
31
+ docker run $(LOCAL_LINK) $(PROJECT_NAME) gem build $(PROJECT_NAME)
32
+
33
+ # Requires rubygems be installed on host
34
+ gem_release: gem
35
+ gem push $(PROJECT_NAME)*.gem
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # Overview
2
+
3
+ https://github.com/kwerle/ruby_language_server
4
+
5
+ The goal of this project is to provide a [language server](https://microsoft.github.io/language-server-protocol/) implementation for ruby in ruby.
6
+
7
+ # Status
8
+
9
+ Beta. It does some stuff. Pretty stable. Used day-to-day.
10
+
11
+ Help wanted.
12
+
13
+ # Features
14
+
15
+ * Definitions
16
+ * Completions
17
+ * Lint - thanks to [RuboCop](https://github.com/bbatsov/rubocop)
18
+ * Please see the [FAQ_ROADMAP.md](./FAQ_ROADMAP.md)
19
+
20
+ # Editor Integrations
21
+
22
+ * You probably want to use one of the developed integrations:
23
+ * Atom - https://github.com/kwerle/ide-ruby
24
+ * Theia - https://github.com/kwerle/theia_ruby_language_server
25
+
26
+ # Running
27
+
28
+ `ruby_language_server` will start the program and wait for activity using LSP's STDIO interface
29
+
30
+ # Development
31
+
32
+ Clone. I love git [HubFlow](https://datasift.github.io/gitflow/).
33
+
34
+ Check out the [Makefile](Makefile). You are going to want to do
35
+ `make guard` and `make continuous_development`.
36
+
37
+ * In Atom: install the ide-ruby.
38
+ * Settings > Packages > ide-ruby > Image Name > local_ruby_language_server
39
+ * CMD-ALT-CTRL-l (that's an L) will reload the window
40
+ * CMD-ALT-i will show debugging info
41
+
42
+ Write tests and guard will run them. Make changes and reload the window. Test them out.
43
+
44
+ # Similar
45
+
46
+ * [mtsmfm/language_server-ruby](https://github.com/mtsmfm/language_server-ruby)
47
+ * [castwide/solargraph](https://github.com/castwide/solargraph)
48
+
49
+ # Authors
50
+
51
+ * [Kurt Werle](kurt@CircleW.org)
52
+
53
+ # Contributors
54
+
55
+ * *Your name here!*
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'spec'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['spec/**/*_spec.rb']
10
+ end
11
+
12
+ desc 'Run guard'
13
+ task guard: [] do
14
+ `guard`
15
+ end
16
+
17
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'ruby_language_server'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,9 @@
1
+ #!/usr/local/bin/ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH << File.join(__dir__, '..', 'lib')
5
+
6
+ require 'ruby_language_server'
7
+
8
+ server = RubyLanguageServer::Server.new
9
+ RubyLanguageServer::IO.new(server)
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'scope_data/base'
4
+ require_relative 'scope_data/scope'
5
+ require_relative 'scope_data/variable'
6
+
7
+ module RubyLanguageServer
8
+ class CodeFile
9
+ attr_reader :uri
10
+ attr_reader :text
11
+ attr_reader :lint_found
12
+
13
+ def initialize(uri, text)
14
+ RubyLanguageServer.logger.debug("CodeFile initialize #{uri}")
15
+ @uri = uri
16
+ @text = text
17
+ @refresh_root_scope = true
18
+ end
19
+
20
+ def text=(new_text)
21
+ RubyLanguageServer.logger.debug("text= for #{uri}")
22
+ if @text == new_text
23
+ RubyLanguageServer.logger.debug('IT WAS THE SAME!!!!!!!!!!!!')
24
+ return
25
+ end
26
+ @text = new_text
27
+ @refresh_root_scope = true
28
+ end
29
+
30
+ SYMBOL_KIND = {
31
+ file: 1,
32
+ 'module': 5, # 2,
33
+ namespace: 3,
34
+ package: 4,
35
+ 'class': 5,
36
+ 'method': 6,
37
+ 'singleton method': 6,
38
+ property: 7,
39
+ field: 8,
40
+ constructor: 9,
41
+ enum: 10,
42
+ interface: 11,
43
+ function: 12,
44
+ variable: 13,
45
+ constant: 14,
46
+ string: 15,
47
+ number: 16,
48
+ boolean: 17,
49
+ array: 18
50
+ }.freeze
51
+
52
+ # Find the ancestor of this scope with a name and return that. Or nil.
53
+ def ancestor_scope_name(scope)
54
+ return_scope = scope
55
+ while (return_scope = return_scope.parent)
56
+ return return_scope.name unless return_scope.name.nil?
57
+ end
58
+ end
59
+
60
+ def tags
61
+ RubyLanguageServer.logger.debug("Asking about tags for #{uri}")
62
+ return @tags = {} if text.nil? || text == ''
63
+
64
+ tags = []
65
+ root_scope.self_and_descendants.each do |scope|
66
+ next if scope.type == ScopeData::Base::TYPE_BLOCK
67
+
68
+ name = scope.name
69
+ kind = SYMBOL_KIND[scope.type] || 7
70
+ kind = 9 if name == 'initialize' # Magical special case
71
+ scope_hash = {
72
+ name: name,
73
+ kind: kind,
74
+ location: Location.hash(uri, scope.top_line)
75
+ }
76
+ container_name = ancestor_scope_name(scope)
77
+ scope_hash[:containerName] = container_name if container_name
78
+ tags << scope_hash
79
+
80
+ scope.variables.each do |variable|
81
+ name = variable.name
82
+ # We only care about counstants
83
+ next unless name =~ /^[A-Z]/
84
+
85
+ variable_hash = {
86
+ name: name,
87
+ kind: SYMBOL_KIND[:constant],
88
+ location: Location.hash(uri, variable.line),
89
+ containerName: scope.name
90
+ }
91
+ tags << variable_hash
92
+ end
93
+ end
94
+ # byebug
95
+ tags.reject! { |tag| tag[:name].nil? }
96
+ # RubyLanguageServer.logger.debug("Raw tags for #{uri}: #{tags}")
97
+ # If you don't reverse the list then atom? won't be able to find the
98
+ # container and containers will get duplicated.
99
+ @tags = tags.reverse_each do |tag|
100
+ child_tags = tags.select { |child_tag| child_tag[:containerName] == tag[:name] }
101
+ max_line = child_tags.map { |child_tag| child_tag[:location][:range][:end][:line].to_i }.max || 0
102
+ tag[:location][:range][:end][:line] = [tag[:location][:range][:end][:line], max_line].max
103
+ end
104
+ # RubyLanguageServer.logger.debug("Done with tags for #{uri}: #{@tags}")
105
+ # RubyLanguageServer.logger.debug("tags caller #{caller * ','}")
106
+ @tags
107
+ end
108
+
109
+ def diagnostics
110
+ # Maybe we should be sharing this GoodCop across instances
111
+ @good_cop ||= GoodCop.new
112
+ @good_cop.diagnostics(@text, @uri)
113
+ end
114
+
115
+ def root_scope
116
+ # RubyLanguageServer.logger.error("Asking about root_scope with #{text}")
117
+ if @refresh_root_scope
118
+ new_root_scope = ScopeParser.new(text).root_scope
119
+ @root_scope ||= new_root_scope # In case we had NONE
120
+ return @root_scope if new_root_scope.children.empty?
121
+
122
+ @root_scope = new_root_scope
123
+ @refresh_root_scope = false
124
+ @tags = nil
125
+ end
126
+ @root_scope
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLanguageServer
4
+ module Completion
5
+ COMPLETION_ITEM_KIND = {
6
+ text: 1,
7
+ method: 2,
8
+ function: 3,
9
+ constructor: 4,
10
+ field: 5,
11
+ variable: 6,
12
+ class: 7,
13
+ interface: 8,
14
+ module: 9,
15
+ property: 10,
16
+ unit: 11,
17
+ value: 12,
18
+ enum: 13,
19
+ keyword: 14,
20
+ snippet: 15,
21
+ color: 16,
22
+ file: 17,
23
+ reference: 18
24
+ }.freeze
25
+
26
+ class << self
27
+ def completion(context, context_scope, scopes)
28
+ RubyLanguageServer.logger.debug("completion(#{context}, #{context_scope.self_and_ancestors.map(&:name)}, #{scopes.map(&:name)})")
29
+ completions =
30
+ if context.length < 2
31
+ scope_completions(context.last, context_scope.self_and_ancestors)
32
+ else
33
+ scope_completions_in_target_context(context, context_scope, scopes)
34
+ end
35
+ {
36
+ isIncomplete: true,
37
+ items: completions.uniq.map do |word, hash|
38
+ {
39
+ label: word,
40
+ kind: COMPLETION_ITEM_KIND[hash[:type]]
41
+ }
42
+ end
43
+ }
44
+ end
45
+
46
+ def scope_with_name(name, scopes)
47
+ scopes.detect { |scope| scope.name == name }
48
+ end
49
+
50
+ def scope_completions_in_target_context(context, context_scope, scopes)
51
+ working_array = context.dup
52
+ context_word = working_array[-2]
53
+ if context_word.match?(/^[A-Z]/)
54
+ scope = scope_with_name(context_word, scopes)
55
+ else
56
+ context_word = context_word.split(/_/).map(&:capitalize).join('')
57
+ scope = scope_with_name(context_word, scopes)
58
+ RubyLanguageServer.logger.debug("scope_with_name: #{scope}")
59
+ end
60
+ scope ||= context_scope
61
+ RubyLanguageServer.logger.debug("scope: #{scope}")
62
+ scope_completions(context.last, scope.self_and_ancestors)
63
+ end
64
+
65
+ def scope_completions(word, scopes)
66
+ words = {}
67
+ scopes.each_with_object(words) do |scope, words_hash|
68
+ scope.children.each do |function|
69
+ words_hash[function.name] ||= {
70
+ depth: scope.depth,
71
+ type: function.type
72
+ }
73
+ end
74
+ scope.variables.each do |variable|
75
+ words_hash[variable.name] ||= {
76
+ depth: scope.depth,
77
+ type: variable.type
78
+ }
79
+ end
80
+ end
81
+ # words = words.sort_by{|word, hash| hash[:depth] }.to_h
82
+ good_words = FuzzyMatch.new(words.keys, threshold: 0.01).find_all(word).slice(0..10) || []
83
+ words = good_words.map { |w| [w, words[w]] }.to_h
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ module RubyLanguageServer
6
+ # Sole purpose is to install gems
7
+ module GemInstaller
8
+ class << self
9
+ def install_gems(gem_names)
10
+ gem_names&.compact!
11
+ gem_names&.reject! { |name| name.strip == '' }
12
+ return if gem_names.nil? || gem_names.empty?
13
+
14
+ RubyLanguageServer.logger.info("Trying to install gems #{gem_names}")
15
+ gemfile do
16
+ source 'https://rubygems.org'
17
+ gem_names.each do |gem_name|
18
+ gem gem_name
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end