rcurses 2.0 → 2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/rcurses.rb CHANGED
@@ -5,708 +5,14 @@
5
5
  # Web_site: http://isene.com/
6
6
  # Github: https://github.com/isene/rcurses
7
7
  # License: Public domain
8
- # Version: 2.0: Complete code refactoring
8
+ # Version: 2.1: New gem structure
9
9
 
10
10
  require 'io/console' # Basic gem for rcurses
11
11
  require 'io/wait' # stdin handling
12
- begin
13
- require 'clipboard' # Ensure the 'clipboard' gem is installed
14
- rescue LoadError
15
- # Define a fallback Clipboard module
16
- module Clipboard
17
- def self.copy(_text)
18
- # Clipboard functionality is not available
19
- puts "Clipboard functionality is not available. Please install the 'clipboard' gem to enable it."
20
- end
21
- end
22
- end
23
12
 
24
- class String
25
- # Add coloring to strings (with escaping for Readline)
26
- def fg(fg) ; color(self, "\e[38;5;#{fg}m", "\e[0m") ; end
27
- def bg(bg) ; color(self, "\e[48;5;#{bg}m", "\e[0m") ; end
28
- def fb(fg, bg); color(self, "\e[38;5;#{fg};48;5;#{bg}m"); end
29
- def b ; color(self, "\e[1m", "\e[22m") ; end
30
- def i ; color(self, "\e[3m", "\e[23m") ; end
31
- def u ; color(self, "\e[4m", "\e[24m") ; end
32
- def l ; color(self, "\e[5m", "\e[25m") ; end
33
- def r ; color(self, "\e[7m", "\e[27m") ; end
34
- # Internal function
35
- def color(text, sp, ep = "\e[0m"); "#{sp}#{text}#{ep}"; end
36
-
37
- # Use format "TEST".c("204,45,bui") to print "TEST" in bold, underline italic, fg=204 and bg=45
38
- def c(code)
39
- prop = "\e["
40
- prop += "38;5;#{code.match(/^\d+/).to_s};" if code.match(/^\d+/)
41
- prop += "48;5;#{code.match(/(?<=,)\d+/).to_s};" if code.match(/(?<=,)\d+/)
42
- prop += "1;" if code.include?('b')
43
- prop += "3;" if code.include?('i')
44
- prop += "4;" if code.include?('u')
45
- prop += "5;" if code.include?('l')
46
- prop += "7;" if code.include?('r')
47
- prop.chop! if prop[-1] == ";"
48
- prop += "m"
49
- prop += self
50
- prop += "\e[0m"
51
- prop
52
- end
53
-
54
- def pure
55
- self.gsub(/\e\[\d+(?:;\d+)*m/, '')
56
- end
57
- end
58
-
59
- module Rcurses
60
- module Cursor
61
- # Terminal cursor movement ANSI codes (inspired by https://github.com/piotrmurach/tty-cursor)
62
- module_function
63
- ESC = "\e".freeze
64
- CSI = "\e[".freeze
65
- def save # Save current position
66
- print(Gem.win_platform? ? CSI + 's' : ESC + '7')
67
- end
68
- def restore # Restore cursor position
69
- print(Gem.win_platform? ? CSI + 'u' : ESC + '8')
70
- end
71
- def pos # Query cursor current position
72
- res = ''
73
- $stdin.raw do |stdin|
74
- $stdout << CSI + '6n' # The actual ANSI get-position
75
- $stdout.flush
76
- while (c = stdin.getc) != 'R'
77
- res << c if c
78
- end
79
- end
80
- m = res.match(/(?<row>\d+);(?<col>\d+)/)
81
- return m[:row].to_i, m[:col].to_i
82
- end
83
- def rowget
84
- row, _col = pos
85
- row
86
- end
87
- def colget
88
- _row, col = pos
89
- col
90
- end
91
- def up(n = 1) # Move cursor up by n
92
- print(CSI + "#{(n || 1)}A")
93
- end
94
- def down(n = 1) # Move the cursor down by n
95
- print(CSI + "#{(n || 1)}B")
96
- end
97
- def left(n = 1) # Move the cursor backward by n
98
- print(CSI + "#{n || 1}D")
99
- end
100
- def right(n = 1) # Move the cursor forward by n
101
- print(CSI + "#{n || 1}C")
102
- end
103
- def col(n = 1) # Cursor moves to nth position horizontally in the current line
104
- print(CSI + "#{n || 1}G")
105
- end
106
- def row(n = 1) # Cursor moves to the nth position vertically in the current column
107
- print(CSI + "#{n || 1}d")
108
- end
109
- def next_line # Move cursor down to beginning of next line
110
- print(CSI + 'E' + CSI + "1G")
111
- end
112
- def prev_line # Move cursor up to beginning of previous line
113
- print(CSI + 'A' + CSI + "1G")
114
- end
115
- def clear_char(n = 1) # Erase n characters from the current cursor position
116
- print(CSI + "#{n}X")
117
- end
118
- def clear_line # Erase the entire current line and return to beginning of the line
119
- print(CSI + '2K' + CSI + "1G")
120
- end
121
- def clear_line_before # Erase from the beginning of the line up to and including the current cursor position.
122
- print(CSI + '1K')
123
- end
124
- def clear_line_after # Erase from the current position (inclusive) to the end of the line
125
- print(CSI + '0K')
126
- end
127
- def clear_screen_down # Clear screen down from current row
128
- print(CSI + 'J')
129
- end
130
- def scroll_up # Scroll display up one line
131
- print(ESC + 'M')
132
- end
133
- def scroll_down # Scroll display down one line
134
- print(ESC + 'D')
135
- end
136
- end
137
-
138
- module Rinput
139
- def getchr
140
- # Function to process key presses
141
- c = $stdin.getch
142
- case c
143
- when "\e" # ANSI escape sequences
144
- return "ESC" if !$stdin.ready?
145
- second_char = $stdin.getc
146
- case second_char
147
- when '[' # CSI
148
- third_char = $stdin.getc
149
- case third_char
150
- when 'A' then chr = "UP"
151
- when 'B' then chr = "DOWN"
152
- when 'C' then chr = "RIGHT"
153
- when 'D' then chr = "LEFT"
154
- when 'Z' then chr = "S-TAB"
155
- when '2'
156
- fourth_char = $stdin.getc
157
- chr = fourth_char == '~' ? "INS" : ""
158
- when '3'
159
- fourth_char = $stdin.getc
160
- chr = fourth_char == '~' ? "DEL" : ""
161
- when '5'
162
- fourth_char = $stdin.getc
163
- chr = fourth_char == '~' ? "PgUP" : ""
164
- when '6'
165
- fourth_char = $stdin.getc
166
- chr = fourth_char == '~' ? "PgDOWN" : ""
167
- when '1', '7'
168
- fourth_char = $stdin.getc
169
- chr = fourth_char == '~' ? "HOME" : ""
170
- when '4', '8'
171
- fourth_char = $stdin.getc
172
- chr = fourth_char == '~' ? "END" : ""
173
- else chr = ""
174
- end
175
- when 'O' # Function keys
176
- third_char = $stdin.getc
177
- case third_char
178
- when 'a' then chr = "C-UP"
179
- when 'b' then chr = "C-DOWN"
180
- when 'c' then chr = "C-RIGHT"
181
- when 'd' then chr = "C-LEFT"
182
- else chr = ""
183
- end
184
- else
185
- chr = ""
186
- end
187
- when "\r" then chr = "ENTER"
188
- when "\t" then chr = "TAB"
189
- when "\u007F", "\b" then chr = "BACK"
190
- when "\u0001" then chr = "C-A"
191
- when "\u0002" then chr = "C-B"
192
- when "\u0003" then chr = "C-C"
193
- when "\u0004" then chr = "C-D"
194
- when "\u0005" then chr = "C-E"
195
- when "\u0006" then chr = "C-F"
196
- when "\u0007" then chr = "C-G"
197
- when "\u000B" then chr = "C-K"
198
- when "\u000C" then chr = "C-L"
199
- when "\u000D" then chr = "C-M"
200
- when "\u000E" then chr = "C-N"
201
- when "\u000F" then chr = "C-O"
202
- when "\u0010" then chr = "C-P"
203
- when "\u0011" then chr = "C-Q"
204
- when "\u0012" then chr = "C-R"
205
- when "\u0014" then chr = "C-T"
206
- when "\u0015" then chr = "C-U"
207
- when "\u0016" then chr = "C-V"
208
- when "\u0018" then chr = "C-X"
209
- when "\u0019" then chr = "C-Y"
210
- when "\u001A" then chr = "C-Z"
211
- when "\u0017" then chr = "WBACK"
212
- when "\u0013" then chr = "C-S"
213
- when /[[:print:]]/ then chr = c
214
- else chr = ""
215
- end
216
- chr
217
- end
218
- end
219
-
220
- class Pane
221
- include Rcurses::Cursor
222
- include Rcurses::Rinput
223
- attr_accessor :startx, :starty, :width, :height, :fg, :bg
224
- attr_accessor :x, :y, :w, :h
225
- attr_accessor :border, :scroll, :text, :ix, :align, :prompt
226
-
227
- def initialize(startx = 1, starty = 1, width = 1, height = 1, fg = nil, bg = nil)
228
- # Using Procs or Lambdas instead of eval
229
- @startx = startx.is_a?(Proc) ? startx : -> { startx }
230
- @starty = starty.is_a?(Proc) ? starty : -> { starty }
231
- @width = width.is_a?(Proc) ? width : -> { width }
232
- @height = height.is_a?(Proc) ? height : -> { height }
233
- @fg, @bg = fg, bg
234
- @text = "" # Initialize text variable
235
- @align = "l" # Default alignment
236
- @scroll = true # Initialize scroll indicators to true
237
- @prompt = "" # Initialize prompt for editline
238
- @ix = 0 # Text index (starting text line in pane)
239
- @max_h, @max_w = IO.console.winsize
240
- end
241
-
242
- def move(x, y)
243
- @startx = -> { @x + x }
244
- @starty = -> { @y + y }
245
- refresh
246
- end
247
-
248
- def refresh(cont = @text)
249
- @max_h, @max_w = IO.console.winsize
250
-
251
- # Define the core of the ANSI escape sequence handling
252
- def split_line_with_ansi(line, w)
253
- # Define opening and closing sequences
254
- open_sequences = {
255
- "\e[1m" => "\e[22m",
256
- "\e[3m" => "\e[23m",
257
- "\e[4m" => "\e[24m",
258
- "\e[5m" => "\e[25m",
259
- "\e[7m" => "\e[27m" }
260
- # All known closing sequences
261
- close_sequences = open_sequences.values + ["\e[0m"]
262
- # Regex to match ANSI escape sequences
263
- ansi_regex = /\e\[[0-9;]*m/
264
- result = []
265
- # Tokenize the line into ANSI sequences and plain text
266
- tokens = line.scan(/(\e\[[0-9;]*m|[^\e]+)/).flatten.compact
267
- current_line = ''
268
- current_line_length = 0
269
- active_sequences = []
270
- tokens.each do |token|
271
- if token.match?(ansi_regex)
272
- # It's an ANSI sequence
273
- current_line << token
274
- if close_sequences.include?(token)
275
- # It's a closing sequence
276
- if token == "\e[0m"
277
- # Reset all sequences
278
- active_sequences.clear
279
- else
280
- # Remove the corresponding opening sequence
281
- corresponding_open = open_sequences.key(token)
282
- active_sequences.delete(corresponding_open)
283
- end
284
- else
285
- # It's an opening sequence (or any other ANSI sequence)
286
- active_sequences << token
287
- end
288
- else
289
- # It's plain text, split into words and spaces
290
- words = token.scan(/\S+\s*/)
291
- words.each do |word|
292
- word_length = word.gsub(ansi_regex, '').length
293
- if current_line_length + word_length <= w
294
- # Append word to current line
295
- current_line << word
296
- current_line_length += word_length
297
- else
298
- # Word doesn't fit in the current line
299
- if current_line_length > 0
300
- # Finish the current line and start a new one
301
- result << current_line
302
- # Start new line with active ANSI sequences
303
- current_line = active_sequences.join
304
- current_line_length = 0
305
- end
306
- # Handle long words that might need splitting
307
- while word_length > w
308
- # Split the word
309
- part = word[0, w]
310
- current_line << part
311
- result << current_line
312
- # Update word and lengths
313
- word = word[w..-1]
314
- word_length = word.gsub(ansi_regex, '').length
315
- # Start new line
316
- current_line = active_sequences.join
317
- current_line_length = 0
318
- end
319
- # Append any remaining part of the word
320
- if word_length > 0
321
- current_line << word
322
- current_line_length += word_length
323
- end
324
- end
325
- end
326
- end
327
- end
328
- # Append any remaining text in the current line
329
- result << current_line unless current_line.empty?
330
- result
331
- end
332
-
333
- # Define the main textformat function
334
- def textformat(cont)
335
- # Split the content by '\n'
336
- lines = cont.split("\n")
337
- result = []
338
- lines.each do |line|
339
- split_lines = split_line_with_ansi(line, @w)
340
- result.concat(split_lines)
341
- end
342
- result
343
- end
344
-
345
- # Start the actual refresh
346
- o_row, o_col = pos
347
- @x = @startx.call
348
- @y = @starty.call
349
- @w = @width.call
350
- @h = @height.call
351
-
352
- # Adjust pane dimensions and positions
353
- if @border # Keep panes inside screen
354
- @w = @max_w - 2 if @w > @max_w - 2
355
- @h = @max_h - 2 if @h > @max_h - 2
356
- @x = 2 if @x < 2; @x = @max_w - @w if @x + @w > @max_w
357
- @y = 2 if @y < 2; @y = @max_h - @h if @y + @h > @max_h
358
- else
359
- @w = @max_w if @w > @max_w
360
- @h = @max_h if @h > @max_h
361
- @x = 1 if @x < 1; @x = @max_w - @w + 1 if @x + @w > @max_w + 1
362
- @y = 1 if @y < 1; @y = @max_h - @h + 1 if @y + @h > @max_h + 1
363
- end
364
-
365
- col(@x); row(@y) # Cursor to start of pane
366
- fmt = [@fg, @bg].compact.join(',') # Format for printing in pane (fg,bg)
367
- @txt = cont.split("\n") # Split content into array
368
- @txt = textformat(cont) if @txt.any? { |line| line.pure.length >= @w } # Reformat lines if necessary
369
- @ix = @txt.length - 1 if @ix > @txt.length - 1; @ix = 0 if @ix < 0 # Ensure no out-of-bounds
370
-
371
- @h.times do |i| # Print pane content
372
- l = @ix + i # The current line to be printed
373
- if @txt[l].to_s != "" # Print the text line
374
- # Get padding width and half width
375
- pl = @w - @txt[l].pure.length
376
- pl = 0 if pl < 0
377
- hl = pl / 2
378
- case @align
379
- when "l"
380
- print @txt[l].c(fmt) + " ".c(fmt) * pl
381
- when "r"
382
- print " ".c(fmt) * pl + @txt[l].c(fmt)
383
- when "c"
384
- print " ".c(fmt) * hl + @txt[l].c(fmt) + " ".c(fmt) * (pl - hl)
385
- end
386
- else
387
- print " ".c(fmt) * @w
388
- end
389
- col(@x) # Cursor to start of pane
390
- row(@y + i + 1)
391
- end
392
-
393
- if @ix > 0 and @scroll # Print "more" marker at top
394
- col(@x + @w - 1); row(@y)
395
- print "▲".c(fmt)
396
- end
397
-
398
- if @txt.length - @ix > @h and @scroll # Print bottom "more" marker
399
- col(@x + @w - 1); row(@y + @h - 1)
400
- print "▼".c(fmt)
401
- end
402
-
403
- if @border # Print border if @border is set to true
404
- row(@y - 1); col(@x - 1)
405
- print ("┌" + "─" * @w + "┐").c(fmt)
406
- @h.times do |i|
407
- row(@y + i); col(@x - 1)
408
- print "│".c(fmt)
409
- col(@x + @w)
410
- print "│".c(fmt)
411
- end
412
- row(@y + @h); col(@x - 1)
413
- print ("└" + "─" * @w + "┘").c(fmt)
414
- end
415
-
416
- row(o_row)
417
- col(o_col)
418
- @txt
419
- end
420
-
421
- def textformat(cont)
422
- # Split the content by '\n'
423
- lines = cont.split("\n")
424
- result = []
425
- lines.each do |line|
426
- split_lines = split_line_with_ansi(line, @w)
427
- result.concat(split_lines)
428
- end
429
- result
430
- end
431
-
432
- def right
433
- if @pos < @txt[@ix + @line].length
434
- @pos += 1
435
- if @pos == @w
436
- @pos = 0
437
- if @line == @h - 1
438
- @ix += 1
439
- else
440
- @line += 1
441
- end
442
- end
443
- else
444
- if @line == @h - 1
445
- @ix += 1 unless @ix >= @txt.length - @h
446
- @pos = 0
447
- elsif @line + @ix + 1 < @txt.length
448
- @line += 1
449
- @pos = 0
450
- end
451
- end
452
- end
453
- def left
454
- if @pos == 0
455
- if @line == 0
456
- unless @ix == 0
457
- @ix -= 1
458
- @pos = @txt[@ix + @line].length
459
- end
460
- else
461
- @line -= 1
462
- @pos = @txt[@ix + @line].length
463
- end
464
- else
465
- @pos -= 1
466
- end
467
- end
468
- def up
469
- if @line == 0
470
- @ix -= 1 unless @ix == 0
471
- else
472
- @line -= 1
473
- end
474
- begin
475
- @pos = [@pos, @txt[@ix + @line].length].min
476
- rescue
477
- end
478
- end
479
- def down
480
- if @line == @h - 1
481
- @ix += 1 unless @ix + @line >= @txt.length - 1
482
- elsif @line + @ix + 1 < @txt.length
483
- @line += 1
484
- end
485
- begin
486
- @pos = [@pos, @txt[@ix + @line].length].min
487
- rescue
488
- end
489
- end
490
-
491
- def parse(cont)
492
- cont.gsub!(/\*(.+?)\*/, '\1'.b)
493
- cont.gsub!(/\/(.+?)\//, '\1'.i)
494
- cont.gsub!(/_(.+?)_/, '\1'.u)
495
- cont.gsub!(/#(.+?)#/, '\1'.r)
496
- cont.gsub!(/<([^|]+)\|([^>]+)>/) do
497
- text = $2; codes = $1
498
- text.c(codes)
499
- end
500
- cont
501
- end
502
-
503
- def edit
504
- begin
505
- # Switch to raw mode without echoing input
506
- STDIN.raw!
507
- # Prepare content for editing, replacing newlines with a placeholder
508
- content = @text.pure.gsub("\n", "¬\n")
509
- # Initialize cursor position and indices
510
- @ix = 0 # Starting index of text lines displayed in the pane
511
- @line = 0 # Current line number relative to the pane's visible area
512
- @pos = 0 # Position within the current line (character index)
513
- @txt = refresh(content)
514
- input_char = ''
515
-
516
- while input_char != 'ESC' # Continue until ESC is pressed
517
- row(@y + @line) # Move cursor to the correct row
518
- col(@x + @pos) # Move cursor to the correct column
519
-
520
- input_char = getchr # Read user input
521
- case input_char
522
- when 'C-L' # Left justify
523
- @align = 'l'
524
- when 'C-R' # Right justify
525
- @align = 'r'
526
- when 'C-C' # Center justify
527
- @align = 'c'
528
- when 'C-Y' # Copy pane content to clipboard
529
- Clipboard.copy(@text.pure)
530
- when 'C-S' # Save edited text back to @text and exit
531
- content = content.gsub('¬', "\n")
532
- content = parse(content)
533
- @text = content
534
- input_char = 'ESC'
535
- when 'DEL' # Delete character at current position
536
- posx = calculate_posx
537
- content.slice!(posx)
538
- when 'BACK' # Backspace (delete character before current position)
539
- if @pos > 0
540
- left
541
- posx = calculate_posx
542
- content.slice!(posx)
543
- end
544
- when 'WBACK' # Word backspace
545
- while @pos > 0 && content[calculate_posx - 1] != ' '
546
- left
547
- posx = calculate_posx
548
- content.slice!(posx)
549
- end
550
- when 'C-K' # Kill line (delete from cursor to end of line)
551
- line_start_pos = calculate_line_start_pos
552
- line_length = @txt[@ix + @line]&.length || 0
553
- content.slice!(line_start_pos + @pos, line_length - @pos)
554
- when 'UP' # Move cursor up one line
555
- up
556
- when 'DOWN' # Move cursor down one line
557
- down
558
- when 'RIGHT' # Move cursor right one character
559
- right
560
- when 'LEFT' # Move cursor left one character
561
- left
562
- when 'HOME' # Move to start of line
563
- @pos = 0
564
- when 'END' # Move to end of line
565
- current_line_length = @txt[@ix + @line]&.length || 0
566
- @pos = current_line_length
567
- when 'C-HOME' # Move to start of pane
568
- @ix = 0
569
- @line = 0
570
- @pos = 0
571
- when 'C-END' # Move to end of pane
572
- total_lines = @txt.length
573
- @ix = [total_lines - @h, 0].max
574
- @line = [@h - 1, total_lines - @ix - 1].min
575
- current_line_length = @txt[@ix + @line]&.length || 0
576
- @pos = current_line_length
577
- when 'ENTER' # Insert newline at current position
578
- posx = calculate_posx
579
- content.insert(posx, "¬\n")
580
- right
581
- when /^.$/ # Insert character at current position
582
- posx = calculate_posx
583
- content.insert(posx, input_char)
584
- right
585
- else
586
- # Handle unrecognized input if necessary
587
- end
588
-
589
- # Handle pasted input (additional characters in the buffer)
590
- while IO.select([$stdin], nil, nil, 0)
591
- input_char = $stdin.read_nonblock(1) rescue nil
592
- break unless input_char
593
- posx = calculate_posx
594
- content.insert(posx, input_char)
595
- right
596
- end
597
-
598
- @txt = refresh(content) # Refresh the pane with the current content
599
- end
600
- ensure
601
- # Restore terminal mode
602
- STDIN.cooked!
603
- end
604
- end
605
-
606
- def editline
607
- begin
608
- # Switch to raw mode without echo
609
- STDIN.raw!
610
-
611
- # Initialize position and dimensions
612
- @x = @startx.call
613
- @y = @starty.call
614
- @w = @width.call
615
- @h = @height.call
616
- # Ensure pane is within screen bounds
617
- @x = [[@x, 1].max, @max_w - @w + 1].min
618
- @y = [[@y, 1].max, @max_h - @h + 1].min
619
-
620
- @scroll = false
621
- row(@y)
622
-
623
- fmt = [@fg, @bg].compact.join(',')
624
- col(@x)
625
- print @prompt.c(fmt) # Print prompt at the pane's starting position
626
-
627
- prompt_len = @prompt.pure.length
628
- content_len = @w - prompt_len
629
- cont = @text.pure.slice(0, content_len)
630
- @pos = cont.length # Set initial cursor position at the end of content
631
- chr = ''
632
-
633
- while chr != 'ESC' # Continue until ESC is pressed
634
- col(@x + prompt_len) # Set cursor at start of content
635
- cont = cont.slice(0, content_len) # Trim content to max length
636
- print cont.ljust(content_len).c(fmt) # Print content, left-justified
637
- col(@x + prompt_len + @pos) # Set cursor to current position
638
-
639
- chr = getchr # Read user input
640
- case chr
641
- when 'LEFT'
642
- @pos -= 1 if @pos > 0
643
- when 'RIGHT'
644
- @pos += 1 if @pos < cont.length
645
- when 'HOME'
646
- @pos = 0
647
- when 'END'
648
- @pos = cont.length
649
- when 'DEL'
650
- cont[@pos] = '' if @pos < cont.length
651
- when 'BACK'
652
- if @pos > 0
653
- @pos -= 1
654
- cont[@pos] = ''
655
- end
656
- when 'WBACK'
657
- while @pos > 0 && cont[@pos - 1] != ' '
658
- @pos -= 1
659
- cont[@pos] = ''
660
- end
661
- when 'C-K'
662
- cont = ''
663
- @pos = 0
664
- when 'ENTER'
665
- @text = parse(cont)
666
- chr = 'ESC'
667
- when /^.$/
668
- if @pos < content_len
669
- cont.insert(@pos, chr)
670
- @pos += 1
671
- end
672
- end
673
-
674
- # Handle pasted input
675
- while IO.select([$stdin], nil, nil, 0)
676
- chr = $stdin.read_nonblock(1) rescue nil
677
- break unless chr
678
- if @pos < content_len
679
- cont.insert(@pos, chr)
680
- @pos += 1
681
- end
682
- end
683
- end
684
- ensure
685
- # Restore terminal mode
686
- STDIN.cooked!
687
- end
688
- end
689
-
690
- private
691
-
692
- # Calculates the position in the content string corresponding to the current cursor position
693
- def calculate_posx
694
- total_length = 0
695
- (@ix + @line).times do |i|
696
- total_length += @txt[i].pure.length + 1 # +1 for the newline character
697
- end
698
- total_length += @pos
699
- total_length
700
- end
701
- # Calculates the starting position of the current line in the content string
702
- def calculate_line_start_pos
703
- total_length = 0
704
- (@ix + @line).times do |i|
705
- total_length += @txt[i].pure.length + 1 # +1 for the newline character
706
- end
707
- total_length
708
- end
709
- end
710
- end
13
+ require_relative 'string_extensions'
14
+ require_relative 'rcurses/cursor'
15
+ require_relative 'rcurses/input'
16
+ require_relative 'rcurses/pane'
711
17
 
712
18
  # vim: set sw=2 sts=2 et filetype=ruby fdm=syntax fdn=2 fcs=fold\:\ :