yoda-language-server 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 422bf18a171e2f3f5421297156a6b0ceb1721f6cd1f41027e16f2cba5c10d8e8
4
- data.tar.gz: 4651bcac93f48b305317079f61aba37cf8a2fe9d48e516357a48886915a90960
3
+ metadata.gz: b544f7f3b34a813b4315f584e191ffd2c818dc63bfd24a093eec32fc4521b391
4
+ data.tar.gz: ba9b8cbc95bdd15d90f23b3921634cf2a3de5c3d61d3a2049e1ea6028d4d880f
5
5
  SHA512:
6
- metadata.gz: 17072e331769fefa2e0f09c8d6cb1851eaa6c7afdd3159bb6e9c6724a09614d83e544e41a5bfbf2cb3a4772547ef6beffa6c389663d08cc75d374372c5fc850c
7
- data.tar.gz: f12118c68f473c909da8e3df94b0f3d30003ebefc70777520a43562de0d75e7cabc70a845483c50868a0a03a322352f3c5cf99e2b33f809cc7aa09729f31a6ef
6
+ metadata.gz: aa9a0e6cab7d9320eab849a9409706ae3a10ecac15ac5f65d380f07e7fdf5fc89482f327675d0eac4d66764b5198c007504d91ac9a58910677779383084b6f37
7
+ data.tar.gz: 325f262ab02a095ecc3005b369ffd233b0faa5837de49b9e60a563e192e0fc1cfacd8a80f0863b5a7801a897af34dae02cbe0df075aeb711405aa6cdb4e2379c
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yoda-language-server (0.5.0)
4
+ yoda-language-server (0.6.0)
5
5
  language_server-protocol (~> 3.7.0.0)
6
6
  leveldb (~> 0.1.9)
7
7
  lmdb (~> 0.4.8)
@@ -57,10 +57,10 @@ GEM
57
57
  diff-lcs (>= 1.2.0, < 2.0)
58
58
  rspec-support (~> 3.7.0)
59
59
  rspec-support (3.7.1)
60
- ruby-progressbar (1.9.0)
60
+ ruby-progressbar (1.10.0)
61
61
  stackprof (0.2.11)
62
62
  thor (0.20.0)
63
- yard (0.9.15)
63
+ yard (0.9.16)
64
64
 
65
65
  PLATFORMS
66
66
  ruby
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
- # Yoda (In Progress) [![Build Status](https://travis-ci.org/tomoasleep/yoda.svg?branch=master)](https://travis-ci.org/tomoasleep/yoda)
1
+ # Yoda [![Build Status](https://travis-ci.org/tomoasleep/yoda.svg?branch=master)](https://travis-ci.org/tomoasleep/yoda)
2
2
 
3
- Yoda is a Language Server for Ruby and provides autocompletion and code analysis (go-to-definition, code information, etc...).
4
- (To know Language Server, please see http://langserver.org/)
3
+ Yoda is a Language Server (http://langserver.org/) for Ruby and provides autocompletion and code analysis (go-to-definition, code information, etc...).
4
+
5
+ **Note: Yoda is alpha version. Please use with caution. Contributions are welcome!**
5
6
 
6
7
  ## Instation and Usage
7
8
 
@@ -27,16 +28,23 @@ $ yoda complete <path-to-your-code>:<line-num>:<char-num> # Show completion at t
27
28
  ### Atom
28
29
 
29
30
  ```
30
- apm install github.com/tomoasleep/yoda
31
+ apm install tomoasleep/yoda
31
32
  ```
32
33
 
33
34
  ### VSCode
34
35
 
35
36
  TBW
36
37
 
37
- ### Vim
38
+ ### Vim/NeoVim
38
39
 
39
- TBW
40
+ Please use language server client such as [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim).
41
+ Here is a configuration example for LanguageClient-neovim.
42
+
43
+ ```vim
44
+ let g:LanguageClient_serverCommands = {
45
+ \ 'ruby': ['yoda', 'server'],
46
+ \ }
47
+ ```
40
48
 
41
49
  ### Emacs
42
50
 
@@ -46,13 +54,13 @@ TBW
46
54
 
47
55
  ### YARD utilization
48
56
 
49
- Yoda figures structures of your source codes and library codes with YARD.
57
+ Yoda figures structures of your source codes and library codes with YARD.
50
58
  Yoda intepret YARD tags such as `@return` tags and `@param` tags and infer code types from these information.
51
59
 
52
60
  ### Indexing
53
61
 
54
- Yoda built index files for fast inference under `<your-project-dir>/.yoda` at startup.
55
- These index files contains structures of external sources (gems and standard libraries).
62
+ Yoda built index files for fast inference under `<your-project-dir>/.yoda` at startup.
63
+ These index files contains structures of external sources (gems and standard libraries).
56
64
  Your project codes are parsed at startup but does not stored in indexes.
57
65
 
58
66
  ### Supporting Features
@@ -7,6 +7,26 @@ class YodaClient extends AutoLanguageClient {
7
7
  super()
8
8
  }
9
9
 
10
+ preInitialization(connection) {
11
+ connection.onTelemetryEvent(({ type, phase, message }) => {
12
+ if (!this.busySignalService) { return; }
13
+ if (type != 'initialization') { return; }
14
+
15
+ if (this.initializationBusyMessage) {
16
+ this.initializationBusyMessage.setTitle(message);
17
+ } else {
18
+ this.initializationBusyMessage = this.busySignalService.reportBusy(message);
19
+ }
20
+ });
21
+ }
22
+
23
+ postInitialization(_server) {
24
+ if (this.initializationBusyMessage) {
25
+ this.initializationBusyMessage.dispose();
26
+ this.initializationBusyMessage = null;
27
+ }
28
+ }
29
+
10
30
  getGrammarScopes () { return ['source.ruby', 'source.rb', 'source.ruby.rails'] }
11
31
  getLanguageName () { return 'Ruby' }
12
32
  getServerName () { return 'Yoda' }
@@ -1,5 +1,7 @@
1
1
  module Yoda
2
2
  require "yoda/version"
3
+ require "yoda/logger"
4
+ require "yoda/instrument"
3
5
  require "yoda/commands"
4
6
  require "yoda/errors"
5
7
  require "yoda/evaluation"
@@ -10,3 +12,5 @@ module Yoda
10
12
  require "yoda/typing"
11
13
  require "yoda/yard_extensions"
12
14
  end
15
+
16
+ YARD::Logger.instance.io = Yoda::Logger.instance.pipeline(tag: 'YARD')
@@ -10,25 +10,42 @@ module Yoda
10
10
  require 'yoda/commands/complete'
11
11
 
12
12
  class Top < Thor
13
+ class_option :log_level, type: :string, desc: 'Set log level (debug info warn error fatal)'
14
+
13
15
  desc 'setup', 'Setup indexes for current Ruby version and project gems'
16
+ option :force_build, type: :boolean, desc: "If enabled, (re)build current project's index forcibly"
14
17
  def setup
15
- Commands::Setup.run
18
+ process_class_options
19
+ Commands::Setup.run(force_build: options[:force_build])
16
20
  end
17
21
 
18
22
  desc 'infer POSITION', 'Infer the type of value at the specified position'
19
23
  def infer(position)
24
+ process_class_options
20
25
  Commands::Infer.run(position)
21
26
  end
22
27
 
23
28
  desc 'complete POSITION', 'Provide completion candidates for the specified position'
24
29
  def complete(position)
30
+ process_class_options
25
31
  Commands::Complete.run(position)
26
32
  end
27
33
 
28
34
  desc 'server', 'Start Language Server'
29
35
  def server
36
+ process_class_options
30
37
  Server.new.run
31
38
  end
39
+
40
+ private
41
+
42
+ def process_class_options
43
+ set_log_level
44
+ end
45
+
46
+ def set_log_level
47
+ Yoda::Logger.log_level = options[:log_level].to_sym if options[:log_level]
48
+ end
32
49
  end
33
50
  end
34
51
  end
@@ -1,3 +1,5 @@
1
+ require 'ruby-progressbar'
2
+
1
3
  module Yoda
2
4
  module Commands
3
5
  class Setup < Base
@@ -7,19 +9,25 @@ module Yoda
7
9
  # @return [true, false]
8
10
  attr_reader :force_build
9
11
 
12
+ # @return [Hash{ Symbol => ProgressBar }]
13
+ attr_reader :bars
14
+
10
15
  # @param dir [String]
11
16
  def initialize(dir: nil, force_build: false)
12
17
  @dir = dir || Dir.pwd
13
18
  @force_build = force_build
19
+ @bars = {}
14
20
  end
15
21
 
16
22
  def run
17
23
  build_core_index
18
24
  if File.exist?(File.expand_path('Gemfile.lock', dir)) || force_build
19
- STDERR.puts 'Building index for the current project...'
20
- force_build ? project.rebuild_cache(progress: true) : project.build_cache(progress: true)
25
+ Logger.info 'Building index for the current project...'
26
+ Instrument.instance.hear(initialization_progress: method(:on_progress), registry_dump: method(:on_progress)) do
27
+ force_build ? project.rebuild_cache : project.build_cache
28
+ end
21
29
  else
22
- STDERR.puts 'Skipped building project index because Gemfile.lock is not exist for the current dir'
30
+ Logger.info 'Skipped building project index because Gemfile.lock is not exist for the current dir'
23
31
  end
24
32
  end
25
33
 
@@ -32,6 +40,12 @@ module Yoda
32
40
  def build_core_index
33
41
  Store::Actions::BuildCoreIndex.run unless Store::Actions::BuildCoreIndex.exists?
34
42
  end
43
+
44
+ def on_progress(phase: :save_keys, index: nil, length: nil, **params)
45
+ return unless index
46
+ bar = bars[phase] ||= ProgressBar.create(format: "%t: %c/%C |%w>%i| %e ", title: phase.to_s.gsub('_', ' '), starting_at: index, total: length)
47
+ bar.progress = index if index <= bar.total
48
+ end
35
49
  end
36
50
  end
37
51
  end
@@ -0,0 +1,113 @@
1
+ module Yoda
2
+ class Instrument
3
+ class Subscription
4
+ # @return [Instrument]
5
+ attr_reader :instrument
6
+
7
+ # @return [String]
8
+ attr_reader :name
9
+
10
+ # @return [#call]
11
+ attr_reader :callback
12
+
13
+ # @param instrument [Instrument]
14
+ # @param name [String]
15
+ # @param callback [#call]
16
+ def initialize(instrument:, name:, callback:)
17
+ @instrument = instrument
18
+ @name = name
19
+ @callback = callback
20
+ end
21
+
22
+ def unsubscribe
23
+ instrument.unsubscribe(self)
24
+ end
25
+
26
+ def call(params)
27
+ callback.call(params)
28
+ end
29
+ end
30
+
31
+ class Progress
32
+ # @return [Integer]
33
+ attr_reader :length, :index
34
+
35
+ # @return [#call]
36
+ attr_reader :callback
37
+
38
+ # @param length [Integer]
39
+ # @param callback [#call]
40
+ def initialize(length, &callback)
41
+ @length = length
42
+ @index = 0
43
+ @callback = callback
44
+ call
45
+ end
46
+
47
+ def increment
48
+ @index += 1
49
+ call
50
+ end
51
+
52
+ def call
53
+ callback.call(length: length, index: index)
54
+ end
55
+ end
56
+
57
+ # @return [Array<Subscription>]
58
+ attr_reader :subscriptions
59
+
60
+ # @return [Instrument]
61
+ def self.instance
62
+ @instance ||= new
63
+ end
64
+
65
+ def initialize
66
+ @subscriptions = []
67
+ end
68
+
69
+ # Add subscriptions and eval the given block. these subscriptions are unsubscribed after the block.
70
+ # @param subscription_hash [Hash{ Symbol, String => #call }]
71
+ def hear(subscription_hash = {})
72
+ subscriptions = subscription_hash.map { |key, value| subscribe(key, &value) }
73
+ value = yield
74
+ subscriptions.each(&:unsubscribe)
75
+ value
76
+ end
77
+
78
+ # @param name [String, Symbol]
79
+ # @param callback [#call]
80
+ # @return [Subsctiption]
81
+ def subscribe(name, &callback)
82
+ Subscription.new(instrument: self, name: name, callback: callback).tap { |subscription| subscriptions.push(subscription) }
83
+ end
84
+
85
+ # @param name [String]
86
+ # @param [String]
87
+ def emit(name, params)
88
+ Logger.trace("#{name}: #{params}")
89
+ subscriptions.select { |subscription| subscription.name === name }.each { |subscription| subscription.call(params) }
90
+ end
91
+
92
+ # @param subscription [Subscription]
93
+ def unsubscribe(subscription)
94
+ subscriptions.delete(subscription)
95
+ end
96
+
97
+ # @!group event_name
98
+
99
+ # @param phase [Symbol]
100
+ # @param message [String]
101
+ # @param index [Integer, nil]
102
+ # @param length [Integer, nil]
103
+ def initialization_progress(phase:, message:, index: nil, length: nil)
104
+ emit(:initialization_progress, phase: phase, message: message, index: index, length: length)
105
+ end
106
+
107
+ # @param index [Integer, nil]
108
+ # @param length [Integer, nil]
109
+ def registry_dump(index: nil, length: nil)
110
+ emit(:registry_dump, index: index, length: length)
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,68 @@
1
+ require 'forwardable'
2
+
3
+ module Yoda
4
+ class Logger
5
+ LOG_LEVELS = %i(trace debug info warn error fatal).freeze
6
+
7
+ class << self
8
+ extend Forwardable
9
+ def_delegators :instance, *%i(pipeline trace debug info warn error fatal log_level allow_debug? allow_info? allow_warn? allow_error? allow_fatal? log_level=)
10
+
11
+ # @return [Yoda::Logger]
12
+ def instance
13
+ @instance ||= Yoda::Logger.new(STDERR)
14
+ end
15
+
16
+ # @!macro [attach]
17
+ # @!method $1(content, tag: nil)
18
+ # @param content [String]
19
+ # @param tag [String, nil]
20
+ # @!method allow_$1?
21
+ # @return [true, false]
22
+ def define_logging_method(level)
23
+ define_method(level) do |content, tag: nil|
24
+ return unless public_send("allow_#{level}?")
25
+ prefix = "[#{level}]#{tag ? ' (' + tag + ')' : '' } "
26
+ io.puts(prefix + content.to_s.split("\n").join(prefix))
27
+ end
28
+
29
+ define_method("allow_#{level}?") do
30
+ allowed_log_levels.include?(level)
31
+ end
32
+ end
33
+ end
34
+
35
+ # @return [IO]
36
+ attr_accessor :io
37
+
38
+ # @return [Hash<Thread>]
39
+ attr_reader :threads
40
+
41
+ # @return [Symbol]
42
+ attr_accessor :log_level
43
+
44
+ LOG_LEVELS.each { |level| define_logging_method(level) }
45
+
46
+ def initialize(io)
47
+ @io = io
48
+ @threads = {}
49
+ @log_level = :info
50
+ end
51
+
52
+ def allowed_log_levels
53
+ LOG_LEVELS.drop_while { |level| level != log_level }
54
+ end
55
+
56
+ def pipeline(tag:)
57
+ threads[tag] ||= begin
58
+ r, w = IO.pipe
59
+ Thread.new do
60
+ r.each do |content|
61
+ debug(content.chomp, tag: tag)
62
+ end
63
+ end
64
+ w
65
+ end
66
+ end
67
+ end
68
+ end
@@ -3,6 +3,7 @@ require 'securerandom'
3
3
 
4
4
  module Yoda
5
5
  class Server
6
+ require 'yoda/server/notifier'
6
7
  require 'yoda/server/completion_provider'
7
8
  require 'yoda/server/signature_provider'
8
9
  require 'yoda/server/hover_provider'
@@ -47,6 +48,11 @@ module Yoda
47
48
  @after_notifications = []
48
49
  end
49
50
 
51
+ # @return [Notifier]
52
+ def notifier
53
+ @notifier ||= Notifier.new(self)
54
+ end
55
+
50
56
  def run
51
57
  reader.read do |request|
52
58
  begin
@@ -59,8 +65,8 @@ module Yoda
59
65
  end
60
66
  process_after_notifications if session&.client_initialized
61
67
  rescue StandardError => ex
62
- STDERR.puts ex
63
- STDERR.puts ex.backtrace
68
+ Logger.warn ex
69
+ Logger.warn ex.backtrace
64
70
  end
65
71
  end
66
72
  end
@@ -140,7 +146,7 @@ module Yoda
140
146
  @signature_provider = SignatureProvider.new(@session)
141
147
  @definition_provider = DefinitionProvider.new(@session)
142
148
 
143
- (InitializationProvider.new(@session).provide || []).each { |notification| after_notifications.push(notification) }
149
+ (InitializationProvider.new(session: session, notifier: notifier).provide || []).each { |notification| after_notifications.push(notification) }
144
150
 
145
151
  LSP::Interface::InitializeResult.new(
146
152
  capabilities: LSP::Interface::ServerCapabilities.new(
@@ -4,14 +4,21 @@ module Yoda
4
4
  # @return [Session]
5
5
  attr_reader :session
6
6
 
7
+ # @return [Notifier]
8
+ attr_reader :notifier
9
+
7
10
  # @param session [Session]
8
- def initialize(session)
11
+ # @param nofitier [Notifier]
12
+ def initialize(session:, notifier:)
9
13
  @session = session
14
+ @notifier = notifier
10
15
  end
11
16
 
12
17
  # @return [LanguageServer::Protocol::Interface::ShowMessageParams, nil]
13
18
  def provide
14
- errors = session.setup
19
+ errors = Instrument.instance.hear(initialization_progress: method(:notify_initialization_progress)) do
20
+ session.setup
21
+ end
15
22
  build_complete_message(errors)
16
23
  end
17
24
 
@@ -80,6 +87,10 @@ module Yoda
80
87
  Please execute `yoda setup` with Ruby version #{RUBY_VERSION}.
81
88
  EOS
82
89
  end
90
+
91
+ def notify_initialization_progress(phase: nil, message: nil, **params)
92
+ notifier.event(type: :initialization, phase: phase, message: message)
93
+ end
83
94
  end
84
95
  end
85
96
  end
@@ -0,0 +1,61 @@
1
+ module Yoda
2
+ class Server
3
+ class Notifier
4
+ # @param server [Server]
5
+ def initialize(server)
6
+ @server = server
7
+ end
8
+
9
+ # @param params [Hash]
10
+ def event(params)
11
+ server.send_notification(method: 'telemetry/event', params: params)
12
+ end
13
+
14
+ # @param type [String, Symbol]
15
+ # @param message [String]
16
+ def show_message(type:, message:)
17
+ server.send_notification(
18
+ method: 'window/showMessage',
19
+ params: LanguageServer::Protocol::Interface::ShowMessageParams.new(
20
+ type: message_type(type),
21
+ message: message,
22
+ )
23
+ )
24
+ end
25
+
26
+ # @param type [String, Symbol]
27
+ # @param message [String]
28
+ def log_message(type:, message:)
29
+ server.send_notification(
30
+ method: 'window/logMessage',
31
+ params: LanguageServer::Protocol::Interface::ShowMessageParams.new(
32
+ type: message_type(type),
33
+ message: message,
34
+ )
35
+ )
36
+ end
37
+
38
+ private
39
+
40
+ # @param type [String, Symbol]
41
+ def message_type(type)
42
+ case type.to_sym
43
+ when :error
44
+ LanguageServer::Protocol::Constant::MessageType::ERROR
45
+ when :warning
46
+ LanguageServer::Protocol::Constant::MessageType::WARNING
47
+ when :info
48
+ LanguageServer::Protocol::Constant::MessageType::INFO
49
+ when :log
50
+ LanguageServer::Protocol::Constant::MessageType::LOG
51
+ else
52
+ Logger.warn("#{type} is not valie message type")
53
+ LanguageServer::Protocol::Constant::MessageType::INFO
54
+ end
55
+ end
56
+
57
+ # @return [Server]
58
+ attr_reader :server
59
+ end
60
+ end
61
+ end
@@ -32,7 +32,10 @@ module Yoda
32
32
  end
33
33
 
34
34
  def setup
35
- Store::Actions::BuildCoreIndex.run unless Store::Actions::BuildCoreIndex.exists?
35
+ unless Store::Actions::BuildCoreIndex.exists?
36
+ Instrument.instance.initialization_progress(phase: :core, message: 'Downloading and building core index')
37
+ Store::Actions::BuildCoreIndex.run
38
+ end
36
39
  project.build_cache
37
40
  end
38
41
 
@@ -31,11 +31,11 @@ module Yoda
31
31
 
32
32
  def build_core_index
33
33
  o, e = Open3.capture2e(script_path)
34
- STDERR.puts o unless o.empty?
34
+ Logger.debug o unless o.empty?
35
35
  if e.success?
36
- STDERR.puts "Success to build yard index"
36
+ Logger.info "Success to build yard index"
37
37
  else
38
- STDERR.puts "Failed to build #{gem_name} #{gem_version}"
38
+ Logger.warn "Failed to build #{gem_name} #{gem_version}"
39
39
  end
40
40
  end
41
41
  end
@@ -42,22 +42,22 @@ module Yoda
42
42
 
43
43
  def create_dependency_doc
44
44
  if yardoc_path
45
- STDERR.puts "Gem docs for #{gem_name} #{gem_version} already exist"
45
+ Logger.info "Gem docs for #{gem_name} #{gem_version} already exist"
46
46
  return true
47
47
  end
48
- STDERR.puts "Building gem docs for #{gem_name} #{gem_version}"
48
+ Logger.info "Building gem docs for #{gem_name} #{gem_version}"
49
49
  begin
50
50
  o, e = Open3.capture2e("yard gems #{gem_name} #{gem_version}")
51
- STDERR.puts o unless o.empty?
51
+ Logger.debug o unless o.empty?
52
52
  if e.success?
53
- STDERR.puts "Done building gem docs for #{gem_name} #{gem_version}"
53
+ Logger.info "Done building gem docs for #{gem_name} #{gem_version}"
54
54
  else
55
- STDERR.puts "Failed to build #{gem_name} #{gem_version}"
55
+ Logger.warn "Failed to build #{gem_name} #{gem_version}"
56
56
  end
57
57
  rescue => ex
58
- STDERR.puts ex
59
- STDERR.puts ex.backtrace
60
- STDERR.puts "Failed to build #{gem_name} #{gem_version}"
58
+ Logger.debug ex
59
+ Logger.debug ex.backtrace
60
+ Logger.warn "Failed to build #{gem_name} #{gem_version}"
61
61
  end
62
62
  end
63
63
 
@@ -68,9 +68,9 @@ module Yoda
68
68
  begin
69
69
  YardImporter.import(yardoc_file, root_path: gem_source_path)
70
70
  rescue => ex
71
- STDERR.puts ex
72
- STDERR.puts ex.backtrace
73
- STDERR.puts "Failed to load #{yardoc_file}"
71
+ Logger.debug ex
72
+ Logger.debug ex.backtrace
73
+ Logger.warn "Failed to load #{yardoc_file}"
74
74
  nil
75
75
  end
76
76
  end
@@ -79,8 +79,8 @@ module Yoda
79
79
  def yardoc_path
80
80
  YARD::Registry.yardoc_file_for_gem(gem_name, gem_version)
81
81
  rescue Bundler::BundlerError => ex
82
- STDERR.puts ex
83
- STDERR.puts ex.backtrace
82
+ Logger.debug ex
83
+ Logger.debug ex.backtrace
84
84
  nil
85
85
  end
86
86
 
@@ -14,7 +14,15 @@ module Yoda
14
14
  end
15
15
 
16
16
  def run
17
- project_files.each { |file| ReadFile.run(registry, file) }
17
+ files = project_files
18
+ progress = Instrument::Progress.new(files.length) do |index:, length:|
19
+ Instrument.instance.initialization_progress(phase: :load_project_files, message: "Loading current project files (#{index} / #{length})", index: index, length: length)
20
+ end
21
+
22
+ files.each do |file|
23
+ ReadFile.run(registry, file)
24
+ progress.increment
25
+ end
18
26
  end
19
27
 
20
28
  private
@@ -52,6 +52,12 @@ module Yoda
52
52
  def clear
53
53
  fail NotImplementedError
54
54
  end
55
+
56
+ # @param data [Enumerator<(String, Object)>]
57
+ # @param bar [#increment, nil]
58
+ # @abstract
59
+ def batch_write(data, bar)
60
+ end
55
61
  end
56
62
  end
57
63
  end
@@ -38,7 +38,7 @@ module Yoda
38
38
  end
39
39
 
40
40
  # @param data [Enumerator<(String, Object)>]
41
- # @param bar [ProgressBar, nil]
41
+ # @param bar [#increment, nil]
42
42
  def batch_write(data, bar)
43
43
  data.each do |(k, v)|
44
44
  @db.put(k, v)
@@ -33,7 +33,7 @@ module Yoda
33
33
  end
34
34
 
35
35
  # @param data [Enumerator<(String, Object)>]
36
- # @param bar [ProgressBar, nil]
36
+ # @param bar [#increment, nil]
37
37
  def batch_write(data, bar)
38
38
  env = LMDB.new(@path, mapsize: @env.info[:mapsize], writemap: true, mapasync: true, nosync: true)
39
39
  db = env.database('main', create: true)
@@ -33,7 +33,7 @@ module Yoda
33
33
  end
34
34
 
35
35
  # @param data [Enumerator<(String, Object)>]
36
- # @param bar [ProgressBar, nil]
36
+ # @param bar [#increment, nil]
37
37
  def batch_write(data, bar)
38
38
  data.each do |(k, v)|
39
39
  put(k, v)
@@ -21,7 +21,6 @@ module Yoda
21
21
  end
22
22
 
23
23
  def setup
24
- YARD::Logger.instance(STDERR)
25
24
  make_dir
26
25
  cache.register_adapter(registry)
27
26
  end
@@ -32,17 +31,17 @@ module Yoda
32
31
  end
33
32
 
34
33
  # @return [Array<BaseError>]
35
- def build_cache(progress: false)
34
+ def build_cache
36
35
  setup
37
36
  loader = LibraryDocLoader.build_for(self)
38
- loader.run(progress: progress)
37
+ loader.run
39
38
  load_project_files
40
39
  loader.errors
41
40
  end
42
41
 
43
- def rebuild_cache(progress: false)
42
+ def rebuild_cache
44
43
  clear
45
- build_cache(progress: progress)
44
+ build_cache
46
45
  end
47
46
 
48
47
  def yoda_dir
@@ -57,6 +56,8 @@ module Yoda
57
56
  private
58
57
 
59
58
  def load_project_files
59
+ Logger.debug('Loading current project files...')
60
+ Instrument.instance.initialization_progress(phase: :load_project_files, message: 'Loading current project files')
60
61
  Actions::ReadProjectFiles.new(registry, root_path).run
61
62
  end
62
63
 
@@ -15,8 +15,7 @@ module Yoda
15
15
  # @param project [Project]
16
16
  # @return [LibraryDocLoader]
17
17
  def build_for(project)
18
- lockfile_parser = parse_gemfile_lock(project.root_path, Cache.gemfile_lock_path(project.root_path))
19
- new(registry: project.registry, gem_specs: lockfile_parser&.specs || [])
18
+ new(registry: project.registry, gem_specs: gem_specs(project))
20
19
  end
21
20
 
22
21
  private
@@ -28,6 +27,13 @@ module Yoda
28
27
  Bundler::LockfileParser.new(File.read(gemfile_lock_path))
29
28
  end
30
29
  end
30
+
31
+ def gem_specs(project)
32
+ lockfile_parser = parse_gemfile_lock(project.root_path, Cache.gemfile_lock_path(project.root_path))
33
+ (lockfile_parser&.specs || []).reject do |spec|
34
+ spec.source.is_a?(Bundler::Source::Path) && (File.expand_path(spec.source.path) == File.expand_path(project.root_path))
35
+ end
36
+ end
31
37
  end
32
38
 
33
39
  # @param registry [Registry]
@@ -38,9 +44,9 @@ module Yoda
38
44
  @errors = []
39
45
  end
40
46
 
41
- def run(progress: false)
47
+ def run
42
48
  project_status = registry.project_status || Objects::ProjectStatus.initial_build(specs: gem_specs)
43
- new_bundle_status = update_bundle(project_status.bundle, progress: progress)
49
+ new_bundle_status = update_bundle(project_status.bundle)
44
50
  registry.save_project_status(project_status.derive(bundle: new_bundle_status))
45
51
  end
46
52
 
@@ -48,11 +54,11 @@ module Yoda
48
54
 
49
55
  # @param bundle_status [Objects::ProjectStatus::BundleStatus]
50
56
  # @return [Objects::ProjectStatus::BundleStatus]
51
- def update_bundle(bundle_status, progress: false)
57
+ def update_bundle(bundle_status)
52
58
  unless bundle_status.all_present?
53
- STDERR.puts 'Constructing database for the current project.'
59
+ Logger.info 'Constructing database for the current project.'
54
60
  bundle_status = import_deps(bundle_status)
55
- registry.compress_and_save(progress: progress)
61
+ registry.compress_and_save
56
62
  end
57
63
  bundle_status
58
64
  end
@@ -61,6 +67,7 @@ module Yoda
61
67
  # @param bundle_status [Objects::ProjectStatus::BundleStatus]
62
68
  # @return [Objects::ProjectStatus::BundleStatus]
63
69
  def import_deps(bundle_status)
70
+ Instrument.instance.initialization_progress(phase: :load_core, message: 'Loading core index')
64
71
  bundle_status = import_core(bundle_status) unless bundle_status.std_status.core_present?
65
72
  bundle_status = import_std(bundle_status) unless bundle_status.std_status.std_present?
66
73
  import_gems(bundle_status)
@@ -85,16 +92,20 @@ module Yoda
85
92
  # @param bundle_status [Objects::ProjectStatus::BundleStatus]
86
93
  # @return [Objects::ProjectStatus::BundleStatus]
87
94
  def import_gems(bundle_status)
88
- gem_statuses = bundle_status.gem_statuses.map do |gem_status|
89
- if gem_status.present?
90
- gem_status
91
- else
92
- result = Actions::ImportGem.run(registry: registry, gem_name: gem_status.name, gem_version: gem_status.version)
93
- errors.push(GemImportError.new(name: gem_status.name, version: gem_status.version)) unless result
94
- gem_status.derive(present: result)
95
- end
95
+ present_gem_statuses, absent_gem_statuses = bundle_status.gem_statuses.partition { |gem_status| gem_status.present? }
96
+
97
+ progress = Instrument::Progress.new(absent_gem_statuses.length) do |index:, length:|
98
+ Instrument.instance.initialization_progress(phase: :load_gems, message: "Loading gems (#{index} / #{length})", index: index, length: length)
96
99
  end
97
- bundle_status.derive(gem_statuses: gem_statuses)
100
+
101
+ new_gem_statuses = absent_gem_statuses.map do |gem_status|
102
+ result = Actions::ImportGem.run(registry: registry, gem_name: gem_status.name, gem_version: gem_status.version)
103
+ progress.increment
104
+ errors.push(GemImportError.new(name: gem_status.name, version: gem_status.version)) unless result
105
+ gem_status.derive(present: result)
106
+ end
107
+
108
+ bundle_status.derive(gem_statuses: present_gem_statuses + new_gem_statuses)
98
109
  end
99
110
  end
100
111
  end
@@ -1,5 +1,4 @@
1
1
  require 'yard'
2
- require 'ruby-progressbar'
3
2
 
4
3
  module Yoda
5
4
  module Store
@@ -56,19 +55,18 @@ module Yoda
56
55
 
57
56
  # Store patch set data to the database.
58
57
  # old data in the database are discarded.
59
- # @param progress [true, false]
60
- def compress_and_save(progress: false)
58
+ def compress_and_save
61
59
  return unless adapter
62
60
  el_keys = patch_set.keys
63
- bar = ProgressBar.create(format: " %c/%C |%w>%i| %e ", total: el_keys.length) if progress
61
+ progress = Instrument::Progress.new(el_keys.length) { |length:, index:| Instrument.instance.registry_dump(index: index, length: length) }
64
62
 
65
63
  data = Enumerator.new do |yielder|
66
64
  el_keys.each { |key| yielder << [key, patch_set.find(key)] }
67
65
  end
68
66
 
69
- adapter.batch_write(data, bar)
67
+ adapter.batch_write(data, progress)
70
68
  adapter.sync
71
- STDERR.puts "saved #{el_keys.length} keys."
69
+ Logger.info "saved #{el_keys.length} keys."
72
70
  @patch_set = Objects::PatchSet.new
73
71
  end
74
72
 
@@ -1,3 +1,3 @@
1
1
  module Yoda
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
data/yarn.lock CHANGED
@@ -2,12 +2,46 @@
2
2
  # yarn lockfile v1
3
3
 
4
4
 
5
- atom-languageclient@^0.7.3:
6
- version "0.7.3"
7
- resolved "https://registry.yarnpkg.com/atom-languageclient/-/atom-languageclient-0.7.3.tgz#4d5c859baab3f3394dc45129935b3037719542fb"
5
+ "@types/atom@^1.28.0":
6
+ version "1.28.0"
7
+ resolved "https://registry.yarnpkg.com/@types/atom/-/atom-1.28.0.tgz#1c80cfa41381b6609e3eac4f84c013cd5069d36d"
8
8
  dependencies:
9
- vscode-jsonrpc "^3.5.0"
9
+ "@types/node" "*"
10
10
 
11
- vscode-jsonrpc@^3.5.0:
12
- version "3.5.0"
13
- resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
11
+ "@types/node@*":
12
+ version "10.9.2"
13
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.2.tgz#f0ab8dced5cd6c56b26765e1c0d9e4fdcc9f2a00"
14
+
15
+ "@types/node@^8.0.41":
16
+ version "8.10.28"
17
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.28.tgz#03bf70dd7f1de7826251331ce57beddf7f9dd253"
18
+
19
+ atom-languageclient@^0.9.5:
20
+ version "0.9.6"
21
+ resolved "https://registry.yarnpkg.com/atom-languageclient/-/atom-languageclient-0.9.6.tgz#2bbb8fad72ae183c0a7d5ac3cbeea87358ecf260"
22
+ dependencies:
23
+ "@types/atom" "^1.28.0"
24
+ "@types/node" "^8.0.41"
25
+ fuzzaldrin-plus "^0.6.0"
26
+ vscode-jsonrpc "^3.6.0"
27
+ vscode-languageserver-protocol "3.6.0-next.5"
28
+ vscode-languageserver-types "^3.6.0-next.1"
29
+
30
+ fuzzaldrin-plus@^0.6.0:
31
+ version "0.6.0"
32
+ resolved "https://registry.yarnpkg.com/fuzzaldrin-plus/-/fuzzaldrin-plus-0.6.0.tgz#832f6489fbe876769459599c914a670ec22947ee"
33
+
34
+ vscode-jsonrpc@^3.6.0, vscode-jsonrpc@^3.6.0-next.1:
35
+ version "3.6.2"
36
+ resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8"
37
+
38
+ vscode-languageserver-protocol@3.6.0-next.5:
39
+ version "3.6.0-next.5"
40
+ resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.6.0-next.5.tgz#ed2ec2db759826f753c0a13977dfb2bedc4d31b3"
41
+ dependencies:
42
+ vscode-jsonrpc "^3.6.0-next.1"
43
+ vscode-languageserver-types "^3.6.0-next.1"
44
+
45
+ vscode-languageserver-types@^3.6.0-next.1:
46
+ version "3.12.0"
47
+ resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.12.0.tgz#f96051381b6a050b7175b37d6cb5d2f2eb64b944"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yoda-language-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomoya Chiba
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-28 00:00:00.000000000 Z
11
+ date: 2018-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: yard
@@ -292,6 +292,8 @@ files:
292
292
  - lib/yoda/evaluation/current_node_explain.rb
293
293
  - lib/yoda/evaluation/evaluator.rb
294
294
  - lib/yoda/evaluation/signature_discovery.rb
295
+ - lib/yoda/instrument.rb
296
+ - lib/yoda/logger.rb
295
297
  - lib/yoda/model.rb
296
298
  - lib/yoda/model/completion_item.rb
297
299
  - lib/yoda/model/descriptions.rb
@@ -362,6 +364,7 @@ files:
362
364
  - lib/yoda/server/deserializer.rb
363
365
  - lib/yoda/server/hover_provider.rb
364
366
  - lib/yoda/server/initialization_provider.rb
367
+ - lib/yoda/server/notifier.rb
365
368
  - lib/yoda/server/session.rb
366
369
  - lib/yoda/server/signature_provider.rb
367
370
  - lib/yoda/store.rb