ruby_language_server 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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