Class: Rvim::Folds

Inherits:
Object
  • Object
show all
Defined in:
lib/rvim/folds.rb

Defined Under Namespace

Classes: Fold

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeFolds

Returns a new instance of Folds.



12
13
14
# File 'lib/rvim/folds.rb', line 12

def initialize
  @folds = []
end

Class Method Details

.from_indent(buffer_of_lines, shiftwidth) ⇒ Object

Build top-level fold ranges from indentation. A run of consecutive lines with leading-space-count >= shiftwidth becomes one fold, anchored at the previous less-indented line. Blank lines inside a run extend it.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/rvim/folds.rb', line 141

def self.from_indent(buffer_of_lines, shiftwidth)
  return [] if shiftwidth <= 0

  ranges = []
  in_fold = false
  fold_start = nil
  buffer_of_lines.each_with_index do |line, i|
    text = line.to_s
    if text.strip.empty?
      # blank lines continue the current fold
      next
    end

    indent = text.bytes.take_while { |b| b == 0x20 }.size
    indented = indent >= shiftwidth

    if indented
      unless in_fold
        fold_start = [i - 1, 0].max
        in_fold = true
      end
    elsif in_fold
      ranges << [fold_start, i - 1]
      in_fold = false
      fold_start = nil
    end
  end
  ranges << [fold_start, buffer_of_lines.size - 1] if in_fold && fold_start

  ranges
end

.from_markers(buffer_of_lines) ⇒ Object

Scan a buffer for … }} fold markers and return [[start, end], …]. Stack-based; mismatched markers are skipped.



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/rvim/folds.rb', line 178

def self.from_markers(buffer_of_lines)
  ranges = []
  stack = []
  buffer_of_lines.each_with_index do |line, i|
    text = line.to_s
    text.scan(/(#{OPEN_MARKER}|#{CLOSE_MARKER})/) do |(marker, _)|
      if marker.start_with?('{')
        stack << i
      else
        start = stack.pop
        ranges << [start, i] if start && start < i
      end
    end
  end
  ranges
end

Instance Method Details

#add(start_line, end_line, closed: true, level: nil) ⇒ Object

Add a fold spanning [start_line, end_line] inclusive. Rejects if the range overlaps an existing fold (no nesting in v1).



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/rvim/folds.rb', line 18

def add(start_line, end_line, closed: true, level: nil)
  return nil if start_line > end_line

  # Allow proper containment (nesting). Reject only PARTIAL overlap.
  @folds.each do |f|
    next if contains?(f.start_line, f.end_line, start_line, end_line)
    next if contains?(start_line, end_line, f.start_line, f.end_line)
    return nil if ranges_overlap?(f.start_line, f.end_line, start_line, end_line)
  end

  fold = Fold.new(start_line, end_line, closed, level)
  @folds << fold
  @folds.sort_by!(&:start_line)
  fold
end

#at_line(line) ⇒ Object



38
39
40
41
42
# File 'lib/rvim/folds.rb', line 38

def at_line(line)
  # Innermost (smallest range) fold containing the line.
  matches = @folds.select { |f| f.start_line <= line && line <= f.end_line }
  matches.min_by { |f| f.end_line - f.start_line }
end

#clearObject



80
81
82
# File 'lib/rvim/folds.rb', line 80

def clear
  @folds.clear
end

#close(line) ⇒ Object



62
63
64
65
66
# File 'lib/rvim/folds.rb', line 62

def close(line)
  f = at_line(line)
  f.closed = true if f
  f
end

#close_allObject



88
89
90
# File 'lib/rvim/folds.rb', line 88

def close_all
  @folds.each { |f| f.closed = true }
end

#closed_at?(line) ⇒ Boolean

Returns:

  • (Boolean)


51
52
53
54
# File 'lib/rvim/folds.rb', line 51

def closed_at?(line)
  f = at_line(line)
  !f.nil? && f.closed
end

#each(&block) ⇒ Object



92
93
94
# File 'lib/rvim/folds.rb', line 92

def each(&block)
  @folds.each(&block)
end

#empty?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/rvim/folds.rb', line 96

def empty?
  @folds.empty?
end

#hidden?(line) ⇒ Boolean

True iff some closed fold contains line and line is not THAT fold’s start. Walks all folds (not just innermost) so a closed outer fold hides every interior line including inner folds’ start_lines.

Returns:

  • (Boolean)


47
48
49
# File 'lib/rvim/folds.rb', line 47

def hidden?(line)
  @folds.any? { |f| f.closed && f.start_line < line && line <= f.end_line }
end

#open(line) ⇒ Object



56
57
58
59
60
# File 'lib/rvim/folds.rb', line 56

def open(line)
  f = at_line(line)
  f.closed = false if f
  f
end

#open_allObject



84
85
86
# File 'lib/rvim/folds.rb', line 84

def open_all
  @folds.each { |f| f.closed = false }
end

#remove(line) ⇒ Object



74
75
76
77
78
# File 'lib/rvim/folds.rb', line 74

def remove(line)
  f = at_line(line)
  @folds.delete(f) if f
  f
end

#shift_after(line, delta) ⇒ Object

Shift fold positions after a line insertion/removal. line is the insertion point; delta is +N for inserts, -N for deletes.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/rvim/folds.rb', line 106

def shift_after(line, delta)
  survivors = []
  @folds.each do |f|
    if delta > 0
      f.start_line += delta if f.start_line > line
      f.end_line += delta if f.end_line >= line
      survivors << f
    else
      # delete: collapse or drop folds intersecting the removed range
      removed_start = line
      removed_end = line - delta - 1
      if removed_end < f.start_line
        f.start_line += delta
        f.end_line += delta
        survivors << f
      elsif removed_start > f.end_line
        survivors << f
      else
        # range intersects fold — drop it for v1 (simpler than remapping)
      end
    end
  end
  @folds = survivors
end

#sizeObject



100
101
102
# File 'lib/rvim/folds.rb', line 100

def size
  @folds.size
end

#toggle(line) ⇒ Object



68
69
70
71
72
# File 'lib/rvim/folds.rb', line 68

def toggle(line)
  f = at_line(line)
  f.closed = !f.closed if f
  f
end