Class: Canopus::Provider::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/canopus/provider.rb,
sig/provider.rbs

Constant Summary collapse

MAX_PROVIDERS =

Returns:

  • (Integer)
64
MAX_ITEMS =

Returns:

  • (Integer)
10_000
MAX_STRING =

Returns:

  • (Integer)
65_536
PROVIDER_TIMEOUT =

Returns:

  • (Integer)
0.2

Instance Method Summary collapse

Constructor Details

#initialize ⇒ Registry

Returns a new instance of Registry.



14
15
16
# File 'lib/canopus/provider.rb', line 14

def initialize
  @completion, @inline, @sequence = {}, {}, 0
end

Instance Method Details

#complete(buffer, offset, context) ⇒ Array[Completion]

Parameters:

  • buffer (Buffer)
  • offset (Integer)
  • context (Object)

Returns:



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/canopus/provider.rb', line 29

def complete(buffer, offset, context)
  validate_position(buffer, offset)
  query = completion_query(context)
  ranked, jobs = [], []
  @completion.to_a.each do |source, details|
    thread = Thread.new { resolve(details.last.call(buffer, offset, context)) }
    thread.report_on_exception = false
    jobs << [source, details, thread]
  end
  jobs.sort_by! { |_source, (priority, registration, _supplier), _thread| [-priority, registration] }
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + PROVIDER_TIMEOUT
  jobs.each do |source, (priority, registration, _supplier), thread|
    break if ranked.length == MAX_ITEMS
    begin
      remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
      unless !thread.alive? || remaining.positive? && thread.join(remaining)
        raise RuntimeError, "completion provider timed out"
      end
      supplied = thread.value
      raise TypeError, "completion supplier must return an array" unless supplied.is_a?(Array)
      raise ArgumentError, "completion provider returned too many items" if supplied.length > MAX_ITEMS
      supplied = supplied.first(MAX_ITEMS - ranked.length)

      ranked.concat(supplied.each_with_index.map do |item, index|
        completion = normalize_completion(item, source, buffer)
        score = Spica.score(query, completion.filter_text || completion.label)
        [completion, score, priority, registration, index]
      end)
    rescue StandardError => error
      provider_error(context, source, error)
    end
  end

  ranked.sort_by! { |completion, score, priority, registration, index| [-score, -priority, completion.sort_text || completion.label, registration, index] }
  ranked.uniq! { |completion, _score, _priority, _registration, _index| [completion.label, completion.insert_text] }
  ranked.map!(&:first)
  ranked.freeze
ensure
  jobs&.each do |_source, _details, thread|
    thread.kill if thread.alive?
    begin
      thread.join
    rescue StandardError
      nil
    end
  end
end

#inline_completion(buffer, offset) ⇒ String?

Parameters:

  • buffer (Buffer)
  • offset (Integer)

Returns:

  • (String, nil)


87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/canopus/provider.rb', line 87

def inline_completion(buffer, offset)
  validate_position(buffer, offset)
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + PROVIDER_TIMEOUT
  @inline.sort_by { |_source, (registration, _supplier)| registration }.each do |_source, (_registration, supplier)|
    remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
    break unless remaining.positive?
    thread = Thread.new { resolve(supplier.call(buffer, offset)) }
    thread.report_on_exception = false
    begin
      remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
      break unless !thread.alive? || remaining.positive? && thread.join(remaining)
      value = thread.value
      next if value.nil?
      return normalize_string(value, "inline completion")
    rescue StandardError
      next
    ensure
      thread.kill if thread.alive?
      begin
        thread.join
      rescue StandardError
        nil
      end
    end
  end
  nil
end

#register_completion(source, priority:) {|arg0, arg1, arg2| ... } ⇒ Symbol

Parameters:

  • source (Symbol)
  • priority: (Numeric)

Yields:

Yield Parameters:

  • arg0 (Buffer)
  • arg1 (Integer)
  • arg2 (Object)

Yield Returns:

  • (Object)

Returns:

  • (Symbol)

Raises:

  • (ArgumentError)


18
19
20
21
22
23
24
25
26
27
# File 'lib/canopus/provider.rb', line 18

def register_completion(source, priority:, &supplier)
  source = normalize_source(source)
  raise ArgumentError, "completion priority must be finite" unless priority.is_a?(Numeric) && priority.real? && priority.finite?
  raise ArgumentError, "completion supplier required" unless supplier
  raise ArgumentError, "too many completion providers" if !@completion.key?(source) && @completion.length >= MAX_PROVIDERS

  @sequence += 1
  @completion[source] = [priority, @sequence, supplier]
  source
end

#register_inline_completion(source) {|arg0, arg1| ... } ⇒ Symbol

Parameters:

  • source (Symbol)

Yields:

Yield Parameters:

  • arg0 (Buffer)
  • arg1 (Integer)

Yield Returns:

  • (Object)

Returns:

  • (Symbol)

Raises:

  • (ArgumentError)


77
78
79
80
81
82
83
84
85
# File 'lib/canopus/provider.rb', line 77

def register_inline_completion(source, &supplier)
  source = normalize_source(source)
  raise ArgumentError, "inline completion supplier required" unless supplier
  raise ArgumentError, "too many inline completion providers" if !@inline.key?(source) && @inline.length >= MAX_PROVIDERS

  @sequence += 1
  @inline[source] = [@sequence, supplier]
  source
end