Class: Canopus::DisplayMap
- Inherits:
-
Object
- Object
- Canopus::DisplayMap
- Defined in:
- lib/canopus/display_map.rb,
sig/canopus.rbs,
sig/display_stages.rbs
Defined Under Namespace
Classes: LineBuilder, LineSet, OverlayMap, PendingLineSet, Row, Summary, Worker
Constant Summary collapse
- BACKGROUND_THRESHOLD =
1 << 20
- EMPTY_LINES =
Canopus::DisplayMap::LineSet.new([].freeze)
- UNWRAPPED_LINE =
Canopus::DisplayMap::PendingLineSet.new(1)
Instance Attribute Summary collapse
-
#block_map ⇒ BlockMap
readonly
Returns the value of attribute block_map.
-
#fold_map ⇒ FoldMap
readonly
Returns the value of attribute fold_map.
-
#layout_error ⇒ StandardError?
readonly
Returns the value of attribute layout_error.
-
#overlay_map ⇒ OverlayMap
readonly
Returns the value of attribute overlay_map.
-
#recomputed_lines ⇒ Integer
readonly
Returns the value of attribute recomputed_lines.
-
#tab_map ⇒ TabMap
readonly
Returns the value of attribute tab_map.
-
#tree ⇒ Denebola::Tree?
readonly
Returns the value of attribute tree.
-
#wrap_map ⇒ WrapMap
readonly
Returns the value of attribute wrap_map.
Instance Method Summary collapse
- #dispose ⇒ void
- #each_row(range = 0...row_count) {|arg0, arg1| ... } ⇒ Object
- #fold(range) ⇒ void
-
#initialize(buffer, tab_size: 4, wrap_width: nil, background_threshold: BACKGROUND_THRESHOLD) ⇒ DisplayMap
constructor
A new instance of DisplayMap.
- #insert_block(id, row:, text:, kind: :diagnostic) ⇒ void
- #pending? ⇒ Boolean
-
#poll(max_lines: 256) ⇒ Boolean
Call only from the foreground event loop, before painting.
- #progress ⇒ [Integer, Integer]
- #remove_block(id) ⇒ void
- #row(index) ⇒ Row
- #row_count ⇒ Integer
- #set_overlays(items, font: nil, font_size: 14, line_height: 20) ⇒ Boolean
- #source_row(index) ⇒ Integer
- #tab_size=(size) ⇒ void
- #to_buffer(point, bias: :left) ⇒ Integer
- #to_display(offset, bias: :left) ⇒ DisplayPoint
- #unfold(offset) ⇒ void
- #wrap_pixels(width, font: nil, font_size:, font_paths: nil, typesetter: nil) ⇒ void
- #wrap_width=(width) ⇒ void
Constructor Details
#initialize(buffer, tab_size: 4, wrap_width: nil, background_threshold: BACKGROUND_THRESHOLD) ⇒ DisplayMap
Returns a new instance of DisplayMap.
16 17 18 19 20 21 22 23 24 |
# File 'lib/canopus/display_map.rb', line 16 def initialize(buffer, tab_size: 4, wrap_width: nil, background_threshold: BACKGROUND_THRESHOLD) raise ArgumentError, "background threshold must be nonnegative or nil" unless background_threshold.nil? || (background_threshold.is_a?(Integer) && background_threshold >= 0) @background_threshold, @generation = background_threshold, 0 @buffer, @rope = buffer, buffer.rope @fold_map, , @tab_map, @wrap_map, @block_map = FoldMap.new, OverlayMap.new, TabMap.new(tab_size: tab_size), WrapMap.new(width: wrap_width), BlockMap.new rebuild @subscription = buffer.on_edit { |patch| apply(patch) } end |
Instance Attribute Details
#block_map ⇒ BlockMap (readonly)
Returns the value of attribute block_map.
13 14 15 |
# File 'lib/canopus/display_map.rb', line 13 def block_map @block_map end |
#fold_map ⇒ FoldMap (readonly)
Returns the value of attribute fold_map.
13 14 15 |
# File 'lib/canopus/display_map.rb', line 13 def fold_map @fold_map end |
#layout_error ⇒ StandardError? (readonly)
Returns the value of attribute layout_error.
14 15 16 |
# File 'lib/canopus/display_map.rb', line 14 def layout_error @layout_error end |
#overlay_map ⇒ OverlayMap (readonly)
Returns the value of attribute overlay_map.
13 14 15 |
# File 'lib/canopus/display_map.rb', line 13 def end |
#recomputed_lines ⇒ Integer (readonly)
Returns the value of attribute recomputed_lines.
13 14 15 |
# File 'lib/canopus/display_map.rb', line 13 def recomputed_lines @recomputed_lines end |
#tab_map ⇒ TabMap (readonly)
Returns the value of attribute tab_map.
13 14 15 |
# File 'lib/canopus/display_map.rb', line 13 def tab_map @tab_map end |
#tree ⇒ Denebola::Tree? (readonly)
Returns the value of attribute tree.
13 14 15 |
# File 'lib/canopus/display_map.rb', line 13 def tree @tree end |
#wrap_map ⇒ WrapMap (readonly)
Returns the value of attribute wrap_map.
13 14 15 |
# File 'lib/canopus/display_map.rb', line 13 def wrap_map @wrap_map end |
Instance Method Details
#dispose ⇒ void
This method returns an undefined value.
25 26 27 28 29 30 31 32 33 |
# File 'lib/canopus/display_map.rb', line 25 def dispose return if @disposed @disposed = true @layout_error = nil @subscription.detach stop_wrap(@wrap_map, @worker) @worker = @pending_result = nil nil end |
#each_row(range) ⇒ void #each_row(range) ⇒ Enumerator[[Row, Integer], void]
142 143 144 145 |
# File 'lib/canopus/display_map.rb', line 142 def each_row(range = 0...row_count) return enum_for(__method__, range) unless block_given? range.each { |i| yield row(i), i if i.between?(0, row_count - 1) } end |
#fold(range) ⇒ void
This method returns an undefined value.
101 102 103 104 105 106 107 108 109 |
# File 'lib/canopus/display_map.rb', line 101 def fold(range) raise Error, "folding is disabled for large read-only files" if @lazy range = range.begin...(range.end + (range.exclude_end? ? 0 : 1)) @rope.point_at(range.begin) @rope.point_at(range.end) first, last = affected_lines(@rope.point_at(range.begin).row, @rope.point_at(range.end).row) @fold_map.fold(range) replace_lines(first, last, last) end |
#insert_block(id, row:, text:, kind: :diagnostic) ⇒ void
This method returns an undefined value.
119 120 121 122 123 124 125 126 |
# File 'lib/canopus/display_map.rb', line 119 def insert_block(id, row:, text:, kind: :diagnostic) raise Error, "inline blocks are disabled for large read-only files" if @lazy raise RangeError, "block row outside buffer" unless row.between?(0, @rope.line_count - 1) previous = @block_map.blocks[id] @block_map.insert(id, row: row, text: text, kind: kind) replace_lines(previous.row, previous.row, previous.row) if previous && previous.row != row replace_lines(row, row, row) end |
#pending? ⇒ Boolean
34 |
# File 'lib/canopus/display_map.rb', line 34 def pending? = !!(!@disposed && !@failed && !@lazy && @background && (@computed_prefix < @tree.size || @tree.summary.pending_rows.positive?)) |
#poll(max_lines: 256) ⇒ Boolean
Call only from the foreground event loop, before painting. Read methods deliberately never install completed work halfway through a frame.
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 |
# File 'lib/canopus/display_map.rb', line 42 def poll(max_lines: 256) raise ArgumentError, "max_lines must be positive" unless max_lines.is_a?(Integer) && max_lines.positive? return false if @disposed || !@worker changed, consumed = false, 0 while consumed < max_lines result = @pending_result || @worker.results.pop(true) @pending_result = nil next unless result.generation == @generation if result.error @failed = true @layout_error = result.error @worker.close @worker = nil raise result.error end take = [max_lines - consumed, result.lines.length].min values = result.lines.take(take) replace_values(result.first, take, values) @computed_prefix = [@computed_prefix, result.first + take].max if result.first <= @computed_prefix take.times { |index| @row_cache.delete(result.first + index) } @recomputed_lines += take consumed += take changed = true if take < result.lines.length @pending_result = result.with(first: result.first + take, lines: result.lines.drop(take).freeze) end end changed rescue ThreadError changed end |
#progress ⇒ [Integer, Integer]
35 36 37 38 |
# File 'lib/canopus/display_map.rb', line 35 def progress return [@rope.line_count, @rope.line_count] if @lazy [@computed_prefix - @tree.prefix_summary(@computed_prefix).pending_rows, @tree.size] end |
#remove_block(id) ⇒ void
This method returns an undefined value.
127 128 129 130 |
# File 'lib/canopus/display_map.rb', line 127 def remove_block(id) block = @block_map.remove(id) replace_lines(block.row, block.row, block.row) if block end |
#row(index) ⇒ Row
131 132 133 134 135 136 137 138 139 140 |
# File 'lib/canopus/display_map.rb', line 131 def row(index) if @lazy raise RangeError, "display row outside buffer" unless index.between?(0, row_count - 1) @row_cache.shift if @row_cache.length >= 256 return @row_cache[index] ||= build_lazy_row(index) end source, _lines, prefix = locate(index) lines = display_lines(source) lines.rows[index - prefix.display_rows] end |
#row_count ⇒ Integer
73 |
# File 'lib/canopus/display_map.rb', line 73 def row_count = @lazy ? @rope.line_count : @tree.summary.display_rows |
#set_overlays(items, font: nil, font_size: 14, line_height: 20) ⇒ Boolean
90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/canopus/display_map.rb', line 90 def (items, font: nil, font_size: 14, line_height: 20) affected = .replace(items, rope: @rope, font: font, font_size: font_size, line_height: line_height) return false if affected.empty? @recomputed_lines = 0 affected.select { |row| row.between?(0, @rope.line_count - 1) } .flat_map { |row| first, last = affected_lines(row, row); (first..last).to_a }.uniq.sort.each do |row| replace_lines(row, row, row) end true end |
#source_row(index) ⇒ Integer
141 |
# File 'lib/canopus/display_map.rb', line 141 def source_row(index) = @lazy ? index : locate(index)[0] |
#tab_size=(size) ⇒ void
This method returns an undefined value.
85 86 87 88 89 |
# File 'lib/canopus/display_map.rb', line 85 def tab_size=(size) return if size == @tab_map.tab_size @tab_map = TabMap.new(tab_size: size) rebuild(reuse: true) end |
#to_buffer(point, bias: :left) ⇒ Integer
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/canopus/display_map.rb', line 146 def to_buffer(point, bias: :left) if @lazy source = point.row.clamp(0, row_count - 1) line = row(source) return @rope.line_start(source) + line.offsets[point.column.clamp(0, line.offsets.length - 1)] end row = point.row.clamp(0, row_count - 1) source, _lines, prefix = locate(row) lines = display_lines(source) line = lines.rows[row - prefix.display_rows] column = point.column.clamp(0, line.offsets.length - 1) local = line.offsets[column] @rope.line_start(source) + local end |
#to_display(offset, bias: :left) ⇒ DisplayPoint
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/canopus/display_map.rb', line 160 def to_display(offset, bias: :left) if @lazy point = @rope.point_at(offset) local = offset - @rope.line_start(point.row) line = row(point.row) unless local.between?(line.offsets.first, line.offsets.last) line = @row_cache[point.row] = build_lazy_row(point.row, from: [local - 8192, 0].max) end column = line.offsets.bsearch_index { |position| position >= local } || line.text.length return DisplayPoint.new(point.row, column) end source = @rope.point_at(offset).row loop do start = @rope.line_start(source) folded = @fold_map.ranges.find { |range| range.begin < start && range.end >= start } break unless folded source = @rope.point_at(folded.begin).row end local = offset - @rope.line_start(source) lines = display_lines(source, local: local).rows prefix = @tree.prefix_summary(source).display_rows lines.each_with_index do |line, i| next unless line.kind == :text && line.&.any? do |placement| placement.align == :before && placement.item.range.begin == offset end column = line.offsets.bsearch_index { |position| position >= local } || line.offsets.length - 1 return DisplayPoint.new(prefix + i, column) end lines.each_with_index do |line, i| next unless line.kind == :text next if local > line.offsets.last column = line.offsets.bsearch_index { |n| bias == :left ? n >= local : n > local } column = column ? (bias == :right ? [column - 1, 0].max : column) : line.offsets.length - 1 return DisplayPoint.new(prefix + i, column) end DisplayPoint.new(prefix + lines.length - 1, lines.last&.text&.length || 0) end |
#unfold(offset) ⇒ void
This method returns an undefined value.
110 111 112 113 114 115 116 117 118 |
# File 'lib/canopus/display_map.rb', line 110 def unfold(offset) affected = @fold_map.ranges.select { |range| range.cover?(offset) } return if affected.empty? first = @rope.point_at(affected.map(&:begin).min).row last = @rope.point_at(affected.map(&:end).max).row first, last = affected_lines(first, last) @fold_map.unfold(offset) replace_lines(first, last, last) end |
#wrap_pixels(width, font: nil, font_size:, font_paths: nil, typesetter: nil) ⇒ void
This method returns an undefined value.
79 80 81 82 83 84 |
# File 'lib/canopus/display_map.rb', line 79 def wrap_pixels(width, font: nil, font_size:, font_paths: nil, typesetter: nil) raise Error, "soft wrapping is disabled for large read-only files" if @lazy current = @wrap_map return if current.width == width && current.font.equal?(font || typesetter&.font) && current.font_size == font_size && current.font_paths == font_paths && current.typesetter.equal?(typesetter) replace_wrap(WrapMap.new(width: width, font: font, font_size: font_size, font_paths: font_paths, typesetter: typesetter)) end |
#wrap_width=(width) ⇒ void
This method returns an undefined value.
74 75 76 77 78 |
# File 'lib/canopus/display_map.rb', line 74 def wrap_width=(width) raise Error, "soft wrapping is disabled for large read-only files" if @lazy && width return if width == @wrap_map.width && !@wrap_map.font replace_wrap(WrapMap.new(width: width)) end |