Class: RuVim::Buffer
- Inherits:
-
Object
- Object
- RuVim::Buffer
- Defined in:
- lib/ruvim/buffer.rb
Instance Attribute Summary collapse
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#kind ⇒ Object
readonly
Returns the value of attribute kind.
-
#lang_module ⇒ Object
Returns the value of attribute lang_module.
-
#modified ⇒ Object
writeonly
Sets the attribute modified.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#path ⇒ Object
Returns the value of attribute path.
-
#stream ⇒ Object
Returns the value of attribute stream.
Class Method Summary collapse
- .decode_text(bytes) ⇒ Object
- .ensure_regular_file!(path) ⇒ Object
- .from_file(id:, path:) ⇒ Object
- .split_lines(data) ⇒ Object
Instance Method Summary collapse
-
#append_stream_lines!(head_suffix, lines) ⇒ Object
Append pre-split lines (from background thread).
-
#append_stream_text!(text) ⇒ Object
Append externally-streamed text without touching undo history or modifiable state.
- #backspace(row, col) ⇒ Object
- #become_normal_empty_buffer! ⇒ Object
- #begin_change_group ⇒ Object
- #can_redo? ⇒ Boolean
- #can_undo? ⇒ Boolean
- #configure_special!(kind:, name: nil, readonly: true, modifiable: false) ⇒ Object
- #delete_char(row, col) ⇒ Object
- #delete_line(row) ⇒ Object
- #delete_span(start_row, start_col, end_row, end_col) ⇒ Object
- #display_name ⇒ Object
- #end_change_group ⇒ Object
- #file_buffer? ⇒ Boolean
- #finalize_async_file_load!(ended_with_newline:) ⇒ Object
-
#initialize(id:, path: nil, lines: [""], kind: :file, name: nil, readonly: false, modifiable: true) ⇒ Buffer
constructor
A new instance of Buffer.
- #insert_char(row, col, char) ⇒ Object
- #insert_lines_at(index, new_lines) ⇒ Object
- #insert_newline(row, col) ⇒ Object
- #insert_text(row, col, text) ⇒ Object
- #intro_buffer? ⇒ Boolean
- #line_at(row) ⇒ Object
- #line_block_text(start_row, count = 1) ⇒ Object
- #line_count ⇒ Object
- #line_length(row) ⇒ Object
- #lines ⇒ Object
- #modifiable=(value) ⇒ Object
- #modifiable? ⇒ Boolean
- #modified? ⇒ Boolean
- #readonly=(value) ⇒ Object
- #readonly? ⇒ Boolean
- #redo! ⇒ Object
- #reload_from_file!(path = nil) ⇒ Object
- #replace_all_lines!(new_lines) ⇒ Object
- #span_text(start_row, start_col, end_row, end_col) ⇒ Object
- #stream_command ⇒ Object
- #stream_status ⇒ Object
- #undo! ⇒ Object
- #virtual_buffer? ⇒ Boolean
- #write_to(path = nil) ⇒ Object
Constructor Details
#initialize(id:, path: nil, lines: [""], kind: :file, name: nil, readonly: false, modifiable: true) ⇒ Buffer
Returns a new instance of Buffer.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/ruvim/buffer.rb', line 67 def initialize(id:, path: nil, lines: [""], kind: :file, name: nil, readonly: false, modifiable: true) @id = id @path = path @kind = kind @name = name @lines = lines.dup @lines = [""] if @lines.empty? @modified = false @readonly = !!readonly @modifiable = !!modifiable @stream = nil @undo_stack = [] @redo_stack = [] @change_group_depth = 0 @group_before_snapshot = nil @group_changed = false @recording_suspended = false @lang_module = Lang::Base @options = {} end |
Instance Attribute Details
#id ⇒ Object (readonly)
Returns the value of attribute id.
7 8 9 |
# File 'lib/ruvim/buffer.rb', line 7 def id @id end |
#kind ⇒ Object (readonly)
Returns the value of attribute kind.
7 8 9 |
# File 'lib/ruvim/buffer.rb', line 7 def kind @kind end |
#lang_module ⇒ Object
Returns the value of attribute lang_module.
21 22 23 |
# File 'lib/ruvim/buffer.rb', line 21 def lang_module @lang_module end |
#modified=(value) ⇒ Object (writeonly)
Sets the attribute modified
10 11 12 |
# File 'lib/ruvim/buffer.rb', line 10 def modified=(value) @modified = value end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
7 8 9 |
# File 'lib/ruvim/buffer.rb', line 7 def name @name end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
9 10 11 |
# File 'lib/ruvim/buffer.rb', line 9 def @options end |
#path ⇒ Object
Returns the value of attribute path.
8 9 10 |
# File 'lib/ruvim/buffer.rb', line 8 def path @path end |
#stream ⇒ Object
Returns the value of attribute stream.
11 12 13 |
# File 'lib/ruvim/buffer.rb', line 11 def stream @stream end |
Class Method Details
.decode_text(bytes) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/ruvim/buffer.rb', line 50 def self.decode_text(bytes) s = bytes.to_s return s.dup if s.encoding == Encoding::UTF_8 && s.valid_encoding? utf8 = s.dup.force_encoding(Encoding::UTF_8) return utf8 if utf8.valid_encoding? ext = Encoding.default_external if ext && ext != Encoding::UTF_8 return utf8.encode(Encoding::UTF_8, invalid: :replace, undef: :replace) end utf8.scrub rescue StandardError bytes.to_s.dup.force_encoding(Encoding::UTF_8).scrub end |
.ensure_regular_file!(path) ⇒ Object
23 24 25 |
# File 'lib/ruvim/buffer.rb', line 23 def self.ensure_regular_file!(path) raise RuVim::CommandError, "Not a regular file: #{path}" if File.exist?(path) && !File.file?(path) end |
.from_file(id:, path:) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/ruvim/buffer.rb', line 27 def self.from_file(id:, path:) ensure_regular_file!(path) lines = if File.file?(path) data = decode_text(File.binread(path)) split_lines(data) else [""] end new(id:, path:, lines:) end |
.split_lines(data) ⇒ Object
39 40 41 42 43 44 45 46 47 48 |
# File 'lib/ruvim/buffer.rb', line 39 def self.split_lines(data) return [""] if data.empty? lines = data.split("\n", -1) if data.end_with?("\n") lines.pop end lines = [""] if lines.empty? lines end |
Instance Method Details
#append_stream_lines!(head_suffix, lines) ⇒ Object
Append pre-split lines (from background thread). head_suffix is concatenated to the current last line.
371 372 373 374 375 376 |
# File 'lib/ruvim/buffer.rb', line 371 def append_stream_lines!(head_suffix, lines) @lines[-1] = @lines[-1] + head_suffix unless head_suffix.empty? @lines.concat(lines) unless lines.empty? @lines = [""] if @lines.empty? @modified = false end |
#append_stream_text!(text) ⇒ Object
Append externally-streamed text without touching undo history or modifiable state.
357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/ruvim/buffer.rb', line 357 def append_stream_text!(text) return [@lines.length - 1, @lines[-1].length] if text.empty? parts = text.split("\n", -1) head = parts.shift || "" @lines[-1] = @lines[-1] + head @lines.concat(parts) @lines = [""] if @lines.empty? @modified = false [@lines.length - 1, @lines[-1].length] end |
#backspace(row, col) ⇒ Object
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/ruvim/buffer.rb', line 250 def backspace(row, col) if col > 0 record_change_before_mutation! line = @lines.fetch(row) @lines[row] = line[0...(col - 1)] + line[col..].to_s @modified = true return [row, col - 1] end return [row, col] if row.zero? record_change_before_mutation! prev = @lines.fetch(row - 1) cur = @lines.fetch(row) new_col = prev.length @lines[row - 1] = prev + cur @lines.delete_at(row) @modified = true [row - 1, new_col] end |
#become_normal_empty_buffer! ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/ruvim/buffer.rb', line 137 def become_normal_empty_buffer! @kind = :file @name = nil @path = nil @readonly = false @modifiable = true @stream = nil @lines = [""] @modified = false @undo_stack.clear @redo_stack.clear @change_group_depth = 0 @group_before_snapshot = nil @group_changed = false self end |
#begin_change_group ⇒ Object
162 163 164 165 |
# File 'lib/ruvim/buffer.rb', line 162 def begin_change_group @change_group_depth += 1 nil end |
#can_redo? ⇒ Boolean
158 159 160 |
# File 'lib/ruvim/buffer.rb', line 158 def can_redo? !@redo_stack.empty? end |
#can_undo? ⇒ Boolean
154 155 156 |
# File 'lib/ruvim/buffer.rb', line 154 def can_undo? !@undo_stack.empty? end |
#configure_special!(kind:, name: nil, readonly: true, modifiable: false) ⇒ Object
128 129 130 131 132 133 134 135 |
# File 'lib/ruvim/buffer.rb', line 128 def configure_special!(kind:, name: nil, readonly: true, modifiable: false) @kind = kind @name = name @readonly = !!readonly @modifiable = !!modifiable @stream = nil unless @kind == :stream self end |
#delete_char(row, col) ⇒ Object
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/ruvim/buffer.rb', line 271 def delete_char(row, col) line = @lines.fetch(row) if col < line.length record_change_before_mutation! @lines[row] = line[0...col] + line[(col + 1)..].to_s @modified = true return true end return false if row >= @lines.length - 1 record_change_before_mutation! @lines[row] = line + @lines[row + 1] @lines.delete_at(row + 1) @modified = true true end |
#delete_line(row) ⇒ Object
289 290 291 292 293 294 295 296 |
# File 'lib/ruvim/buffer.rb', line 289 def delete_line(row) row = [[row, 0].max, @lines.length - 1].min record_change_before_mutation! deleted = @lines.delete_at(row) @lines << "" if @lines.empty? @modified = true deleted end |
#delete_span(start_row, start_col, end_row, end_col) ⇒ Object
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/ruvim/buffer.rb', line 298 def delete_span(start_row, start_col, end_row, end_col) s_row, s_col, e_row, e_col = normalize_span(start_row, start_col, end_row, end_col) return false if s_row == e_row && s_col == e_col record_change_before_mutation! if s_row == e_row line = @lines.fetch(s_row) @lines[s_row] = line[0...s_col] + line[e_col..].to_s else head = @lines.fetch(s_row)[0...s_col] tail = @lines.fetch(e_row)[e_col..].to_s @lines[s_row] = head + tail (e_row - s_row).times { @lines.delete_at(s_row + 1) } end @lines << "" if @lines.empty? @modified = true true end |
#display_name ⇒ Object
124 125 126 |
# File 'lib/ruvim/buffer.rb', line 124 def display_name @name || @path || "[No Name]" end |
#end_change_group ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/ruvim/buffer.rb', line 167 def end_change_group return nil if @change_group_depth.zero? @change_group_depth -= 1 return nil unless @change_group_depth.zero? if @group_changed && @group_before_snapshot @undo_stack << @group_before_snapshot @redo_stack.clear end @group_before_snapshot = nil @group_changed = false nil end |
#file_buffer? ⇒ Boolean
112 113 114 |
# File 'lib/ruvim/buffer.rb', line 112 def file_buffer? @kind == :file end |
#finalize_async_file_load!(ended_with_newline:) ⇒ Object
378 379 380 381 382 383 384 385 |
# File 'lib/ruvim/buffer.rb', line 378 def finalize_async_file_load!(ended_with_newline:) if ended_with_newline && @lines.length > 1 && @lines[-1] == "" @lines.pop end @lines = [""] if @lines.empty? @modified = false self end |
#insert_char(row, col, char) ⇒ Object
220 221 222 223 224 225 |
# File 'lib/ruvim/buffer.rb', line 220 def insert_char(row, col, char) record_change_before_mutation! line = @lines.fetch(row) @lines[row] = line.dup.insert(col, char) @modified = true end |
#insert_lines_at(index, new_lines) ⇒ Object
339 340 341 342 343 344 345 346 347 |
# File 'lib/ruvim/buffer.rb', line 339 def insert_lines_at(index, new_lines) lines = Array(new_lines).map(&:to_s) return if lines.empty? record_change_before_mutation! idx = [[index, 0].max, @lines.length].min @lines.insert(idx, *lines) @modified = true end |
#insert_newline(row, col) ⇒ Object
239 240 241 242 243 244 245 246 247 248 |
# File 'lib/ruvim/buffer.rb', line 239 def insert_newline(row, col) record_change_before_mutation! line = @lines.fetch(row) head = line[0...col] tail = line[col..] || "" @lines[row] = head @lines.insert(row + 1, tail) @modified = true [row + 1, 0] end |
#insert_text(row, col, text) ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/ruvim/buffer.rb', line 227 def insert_text(row, col, text) text.each_char do |ch| if ch == "\n" row, col = insert_newline(row, col) else insert_char(row, col, ch) col += 1 end end [row, col] end |
#intro_buffer? ⇒ Boolean
116 117 118 |
# File 'lib/ruvim/buffer.rb', line 116 def intro_buffer? @kind == :intro end |
#line_at(row) ⇒ Object
212 213 214 |
# File 'lib/ruvim/buffer.rb', line 212 def line_at(row) @lines.fetch(row) end |
#line_block_text(start_row, count = 1) ⇒ Object
334 335 336 337 |
# File 'lib/ruvim/buffer.rb', line 334 def line_block_text(start_row, count = 1) rows = @lines[start_row, count] || [] rows.join("\n") + "\n" end |
#line_count ⇒ Object
208 209 210 |
# File 'lib/ruvim/buffer.rb', line 208 def line_count @lines.length end |
#line_length(row) ⇒ Object
216 217 218 |
# File 'lib/ruvim/buffer.rb', line 216 def line_length(row) @lines.fetch(row).length end |
#lines ⇒ Object
88 89 90 |
# File 'lib/ruvim/buffer.rb', line 88 def lines @lines end |
#modifiable=(value) ⇒ Object
108 109 110 |
# File 'lib/ruvim/buffer.rb', line 108 def modifiable=(value) @modifiable = !!value end |
#modifiable? ⇒ Boolean
104 105 106 |
# File 'lib/ruvim/buffer.rb', line 104 def modifiable? @modifiable && !@stream&.live? end |
#modified? ⇒ Boolean
92 93 94 |
# File 'lib/ruvim/buffer.rb', line 92 def modified? !!@modified end |
#readonly=(value) ⇒ Object
100 101 102 |
# File 'lib/ruvim/buffer.rb', line 100 def readonly=(value) @readonly = !!value end |
#readonly? ⇒ Boolean
96 97 98 |
# File 'lib/ruvim/buffer.rb', line 96 def readonly? @readonly end |
#redo! ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/ruvim/buffer.rb', line 195 def redo! close_change_group_if_open return false unless can_redo? current = snapshot nxt = @redo_stack.pop with_recording_suspended do restore_snapshot(nxt) end @undo_stack << current true end |
#reload_from_file!(path = nil) ⇒ Object
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
# File 'lib/ruvim/buffer.rb', line 399 def reload_from_file!(path = nil) target = path || @path raise RuVim::CommandError, "No file name" if target.nil? || target.empty? self.class.ensure_regular_file!(target) data = File.file?(target) ? self.class.decode_text(File.binread(target)) : "" @lines = self.class.split_lines(data) @path = target @modified = false @undo_stack.clear @redo_stack.clear @change_group_depth = 0 @group_before_snapshot = nil @group_changed = false target end |
#replace_all_lines!(new_lines) ⇒ Object
349 350 351 352 353 354 |
# File 'lib/ruvim/buffer.rb', line 349 def replace_all_lines!(new_lines) record_change_before_mutation! @lines = Array(new_lines).map(&:dup) @lines = [""] if @lines.empty? @modified = true end |
#span_text(start_row, start_col, end_row, end_col) ⇒ Object
319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/ruvim/buffer.rb', line 319 def span_text(start_row, start_col, end_row, end_col) s_row, s_col, e_row, e_col = normalize_span(start_row, start_col, end_row, end_col) return "" if s_row == e_row && s_col == e_col if s_row == e_row return @lines.fetch(s_row)[s_col...e_col].to_s end parts = [] parts << @lines.fetch(s_row)[s_col..].to_s ((s_row + 1)...e_row).each { |row| parts << @lines.fetch(row) } parts << @lines.fetch(e_row)[0...e_col].to_s parts.join("\n") end |
#stream_command ⇒ Object
17 18 19 |
# File 'lib/ruvim/buffer.rb', line 17 def stream_command @stream&.command end |
#stream_status ⇒ Object
13 14 15 |
# File 'lib/ruvim/buffer.rb', line 13 def stream_status @stream&.status end |
#undo! ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/ruvim/buffer.rb', line 182 def undo! close_change_group_if_open return false unless can_undo? current = snapshot prev = @undo_stack.pop with_recording_suspended do restore_snapshot(prev) end @redo_stack << current true end |
#virtual_buffer? ⇒ Boolean
120 121 122 |
# File 'lib/ruvim/buffer.rb', line 120 def virtual_buffer? !file_buffer? end |
#write_to(path = nil) ⇒ Object
387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/ruvim/buffer.rb', line 387 def write_to(path = nil) raise RuVim::CommandError, "Buffer is readonly" if readonly? target = path || @path raise RuVim::CommandError, "No file name" if target.nil? || target.empty? File.binwrite(target, @lines.join("\n")) @path = target @modified = false target end |