Class: Canopus::Decoration::Registry

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

Constant Summary collapse

CACHE_LIMIT =
256

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize ⇒ Registry

Returns a new instance of Registry.



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

def initialize
  @suppliers, @generations, @cache = {}, Hash.new(0), {}
  @lock, @version = Mutex.new, 0
end

Instance Attribute Details

#version ⇒ Integer (readonly)

Returns the value of attribute version.

Returns:

  • (Integer)


11
12
13
# File 'lib/canopus/decoration.rb', line 11

def version
  @version
end

Instance Method Details

#invalidate(source, buffer: nil, rows: nil) ⇒ nil

Parameters:

  • source (Symbol)
  • buffer: (Buffer, nil) (defaults to: nil)
  • rows: (Range[Integer], nil) (defaults to: nil)

Returns:

  • (nil)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/canopus/decoration.rb', line 71

def invalidate(source, buffer: nil, rows: nil)
  source = normalize_source(source)
  row_bounds = normalize_rows(rows) if rows
  @lock.synchronize do
    @version += 1
    @generations[source] += 1
    if buffer || row_bounds
      @cache.delete_if do |key, _|
        same_source = key[0] == source
        same_buffer = !buffer || key[1] == buffer.object_id
        overlaps = !row_bounds || (key[3] < row_bounds[1] && row_bounds[0] < key[4])
        same_source && same_buffer && overlaps
      end
    else
      @cache.delete_if { |key, _| key[0] == source }
    end
  end
  nil
end

#items_for(buffer, row_range, context: nil) ⇒ Array[Item]

Parameters:

  • buffer (Buffer)
  • row_range (Range[Integer])
  • context: (Editor, nil) (defaults to: nil)

Returns:



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
# File 'lib/canopus/decoration.rb', line 38

def items_for(buffer, row_range, context: nil)
  first, last = normalize_rows(row_range)
  state = [buffer.respond_to?(:version) ? buffer.version : nil,
    context&.object_id,
    context&.respond_to?(:selections) ? context.selections : buffer.respond_to?(:selections) ? buffer.selections : nil,
    context&.respond_to?(:tab_size) ? context.tab_size : nil,
    context&.respond_to?(:language_document) ? context.language_document.object_id : nil]
  suppliers = @lock.synchronize do
    @suppliers.map { |source, supplier| [source, supplier, @generations[source]] }
  end
  items = suppliers.flat_map do |source, supplier, generation|
    key = [source, buffer.object_id, state, first, last]
    cached = @lock.synchronize { @cache[key] }
    next cached if cached

    supplied = if supplier.arity == 2
      supplier.call(buffer, first...last)
    else
      supplier.call(buffer, first...last, context)
    end
    raise TypeError, "decoration supplier must return an array" unless supplied.is_a?(Array)
    supplied = supplied.map { |item| normalize_item(item, source) }.freeze
    @lock.synchronize do
      if @suppliers[source].equal?(supplier) && @generations[source] == generation
        @cache.shift while @cache.length >= CACHE_LIMIT
        @cache[key] = supplied
      end
    end
    supplied
  end
  items.each_with_index.sort_by { |item, index| [item.priority, index] }.map!(&:first).freeze
end

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

Parameters:

  • source (Symbol)

Yields:

Yield Parameters:

Yield Returns:

Returns:

  • (Symbol)

Raises:

  • (ArgumentError)


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

def register(source, &supplier)
  raise ArgumentError, "decoration supplier required" unless supplier

  source = normalize_source(source)
  @lock.synchronize do
    @suppliers[source] = supplier
    invalidate_locked(source)
  end
  source
end

#unregister(source) ⇒ Proc?

Parameters:

  • source (Symbol)

Returns:

  • (Proc, nil)


29
30
31
32
33
34
35
36
# File 'lib/canopus/decoration.rb', line 29

def unregister(source)
  source = normalize_source(source)
  @lock.synchronize do
    supplier = @suppliers.delete(source)
    invalidate_locked(source) if supplier
    supplier
  end
end