textbringer 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b377f0a0be9ac761f63bafbc0db3798e14eefecc
4
- data.tar.gz: ff3014ee193c52e2be1b028210e8024c0c10f708
3
+ metadata.gz: df53610e7d9e7a4e7b657d38d9366a3be4752231
4
+ data.tar.gz: 07ce8c686e767f2d893f44ccb6994b8e6690abf2
5
5
  SHA512:
6
- metadata.gz: 5928dac977797697ebe072e51fa09fbc892b4e581a5197411cc70d32b9eee8615645d34a62bba7fc11e2e99cab71a63c1c8224083ee6f5b4cd411404423418b4
7
- data.tar.gz: 6895b2ad1f81511cf742aa472bb58b60d4785923de48ce1c68750915b39b6911ec80afc30dcec3fd09e0dcc142060840bfb7045bebc3aad27681fa378f8cc195
6
+ metadata.gz: 64d0421dec0fbab6af6fcb2e9c60497c959fb2d9d1bed99a74c764a8e4ac2f8622e468c452bfec492757967cb7943010529b5da2daad4866fe974cad3e67dfd3
7
+ data.tar.gz: f9f5ba83a29a959143b37d4388174828bd9414a0f2ddaef7d094075b1b8b314a8d7f708565aaf791a3d59da537eeb0bea685b57c31e40db0776fabbf9952d7ab
data/CHANGES.md ADDED
@@ -0,0 +1,14 @@
1
+ ## 0.1.2
2
+
3
+ * Use curses instead of ncursesw.
4
+ * Support Windows.
5
+ * Dump unsaved buffers to ~/.textbringer/buffer_dump when textbringer crashes.
6
+ * Many bug fixes.
7
+
8
+ ## 0.1.1
9
+
10
+ * Rename exe/tb to exe/textbringer to avoid conflict with akr/tb.
11
+
12
+ ## 0.1.0
13
+
14
+ * First version
data/exe/textbringer CHANGED
@@ -17,7 +17,6 @@ $VERBOSE = nil
17
17
 
18
18
  Controller.current = Controller.new
19
19
  Window.start do
20
- message("Type C-x C-c to exit Textbringer")
21
20
  begin
22
21
  load_user_config
23
22
  ruby_mode
@@ -26,15 +25,30 @@ Window.start do
26
25
  find_file(arg)
27
26
  end
28
27
  end
28
+ if Buffer.dumped_buffers_exist?(CONFIG[:buffer_dump_dir])
29
+ Window.redisplay
30
+ if yes_or_no?("Dumped buffers found; restore them?")
31
+ buffers = Buffer.load_dumped_buffers(CONFIG[:buffer_dump_dir])
32
+ switch_to_buffer(buffers.last)
33
+ end
34
+ end
29
35
  rescue Exception => e
30
36
  handle_exception(e)
31
37
  end
32
38
  Window.redisplay
33
- trap(:CONT) do
34
- Window.redraw
39
+ begin
40
+ trap(:CONT) { Window.redraw }
41
+ rescue ArgumentError
35
42
  end
36
- loop do
37
- Controller.current.command_loop(TOP_LEVEL_TAG)
38
- Window.redisplay
43
+ begin
44
+ loop do
45
+ Controller.current.command_loop(TOP_LEVEL_TAG)
46
+ Window.redisplay
47
+ end
48
+ rescue Exception => e
49
+ if !e.is_a?(SystemExit)
50
+ Buffer.dump_unsaved_buffers(CONFIG[:buffer_dump_dir])
51
+ end
52
+ raise
39
53
  end
40
54
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "nkf"
4
4
  require "unicode/display_width"
5
+ require "json"
6
+ require "fileutils"
5
7
 
6
8
  module Textbringer
7
9
  class Buffer
@@ -10,6 +12,7 @@ module Textbringer
10
12
  attr_accessor :mode, :keymap
11
13
  attr_reader :name, :file_name, :file_encoding, :file_format, :point, :marks
12
14
  attr_reader :current_line, :current_column, :visible_mark
15
+ attr_writer :modified
13
16
 
14
17
  GAP_SIZE = 256
15
18
  UNDO_LIMIT = 1000
@@ -1092,6 +1095,52 @@ module Textbringer
1092
1095
  insert(s)
1093
1096
  end
1094
1097
 
1098
+ def dump(path)
1099
+ File.write(path, to_s)
1100
+ metadata = {
1101
+ "name" => name,
1102
+ "file_name" => file_name,
1103
+ "file_encoding" => file_encoding.name,
1104
+ "file_format" => file_format.to_s
1105
+ }
1106
+ File.write(path + ".metadata", metadata.to_json)
1107
+ end
1108
+
1109
+ def self.load(path)
1110
+ buffer = Buffer.new(File.read(path))
1111
+ metadata = JSON.parse(File.read(path + ".metadata"))
1112
+ buffer.name = metadata["name"]
1113
+ buffer.file_name = metadata["file_name"] if metadata["file_name"]
1114
+ buffer.file_encoding = Encoding.find(metadata["file_encoding"])
1115
+ buffer.file_format = metadata["file_format"].intern
1116
+ buffer.modified = true
1117
+ buffer
1118
+ end
1119
+
1120
+ def self.dump_unsaved_buffers(dir)
1121
+ FileUtils.mkdir_p(dir)
1122
+ @@list.each do |buffer|
1123
+ if /\A\*/ !~ buffer.name && buffer.modified?
1124
+ buffer.dump(File.expand_path(buffer.object_id.to_s, dir))
1125
+ end
1126
+ end
1127
+ end
1128
+
1129
+ def self.dumped_buffers_exist?(dir)
1130
+ !Dir.glob(File.expand_path("*.metadata", dir)).empty?
1131
+ end
1132
+
1133
+ def self.load_dumped_buffers(dir)
1134
+ Dir.glob(File.expand_path("*.metadata", dir)).map do |metadata_path|
1135
+ path = metadata_path.sub(/\.metadata\z/, "")
1136
+ buffer = Buffer.load(path)
1137
+ add(buffer)
1138
+ File.unlink(metadata_path)
1139
+ File.unlink(path)
1140
+ buffer
1141
+ end
1142
+ end
1143
+
1095
1144
  private
1096
1145
 
1097
1146
  def adjust_gap(min_size = 0, pos = @point)
@@ -79,7 +79,7 @@ module Textbringer
79
79
  end
80
80
 
81
81
  define_command(:self_insert) do |n = number_prefix_arg|
82
- c = Controller.current.last_key.chr(Encoding::UTF_8)
82
+ c = Controller.current.last_key
83
83
  merge_undo = Controller.current.last_command == :self_insert
84
84
  n.times do
85
85
  Buffer.current.insert(c, merge_undo)
@@ -88,12 +88,11 @@ module Textbringer
88
88
 
89
89
  define_command(:quoted_insert) do |n = number_prefix_arg|
90
90
  c = Controller.current.read_char
91
- if !c.is_a?(Integer)
91
+ if !c.is_a?(String)
92
92
  raise "Invalid key"
93
93
  end
94
- ch = c.chr(Encoding::UTF_8)
95
94
  n.times do
96
- Buffer.current.insert(ch)
95
+ Buffer.current.insert(c)
97
96
  end
98
97
  end
99
98
 
@@ -236,7 +235,7 @@ module Textbringer
236
235
  end
237
236
 
238
237
  define_command(:suspend_textbringer) do
239
- Ncurses.endwin
238
+ Curses.close_screen
240
239
  Process.kill(:STOP, $$)
241
240
  end
242
241
 
@@ -439,7 +438,7 @@ module Textbringer
439
438
 
440
439
  define_command(:digit_argument) do
441
440
  |arg = current_prefix_arg|
442
- n = last_key.chr.to_i
441
+ n = last_key.to_i
443
442
  Controller.current.prefix_arg =
444
443
  case arg
445
444
  when Integer
@@ -491,18 +490,13 @@ module Textbringer
491
490
  end
492
491
 
493
492
  ISEARCH_MODE_MAP = Keymap.new
494
- (0x20..0x7e).each do |c|
493
+ (?\x20..?\x7e).each do |c|
495
494
  ISEARCH_MODE_MAP.define_key(c, :isearch_printing_char)
496
495
  end
497
496
  ISEARCH_MODE_MAP.define_key(?\t, :isearch_printing_char)
498
497
  ISEARCH_MODE_MAP.handle_undefined_key do |key|
499
- if key.is_a?(Integer) && key > 0x80
500
- begin
501
- key.chr(Encoding::UTF_8)
502
- :isearch_printing_char
503
- rescue RangeError
504
- nil
505
- end
498
+ if key.is_a?(String) && /[\0-\x7f]/ !~ key
499
+ :isearch_printing_char
506
500
  else
507
501
  nil
508
502
  end
@@ -574,7 +568,7 @@ module Textbringer
574
568
  end
575
569
 
576
570
  define_command(:isearch_printing_char) do
577
- c = Controller.current.last_key.chr(Encoding::UTF_8)
571
+ c = Controller.current.last_key
578
572
  ISEARCH_STATUS[:string].concat(c)
579
573
  isearch_search
580
574
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Textbringer
4
4
  CONFIG = {
5
+ buffer_dump_dir: File.expand_path("~/.textbringer/buffer_dump"),
5
6
  tab_width: 8,
6
7
  indent_tabs_mode: false,
7
8
  case_fold_search: true
@@ -34,7 +34,7 @@ module Textbringer
34
34
  catch(tag) do
35
35
  loop do
36
36
  begin
37
- c = Window.current.getch
37
+ c = Window.current.read_char
38
38
  Window.echo_area.clear_message
39
39
  @last_key = c
40
40
  @key_sequence << @last_key
@@ -76,11 +76,11 @@ module Textbringer
76
76
  end
77
77
 
78
78
  def read_char
79
- Window.current.getch
79
+ Window.current.read_char
80
80
  end
81
81
 
82
82
  def received_keyboard_quit?
83
- while (key = Window.current.getch_nonblock) && key >= 0
83
+ while key = Window.current.read_char_nonblock
84
84
  if GLOBAL_MAP.lookup([key]) == :keyboard_quit
85
85
  return true
86
86
  end
@@ -105,7 +105,7 @@ module Textbringer
105
105
  case key
106
106
  when Integer
107
107
  if key < 0x80
108
- s = Ncurses.keyname(key)
108
+ s = Curses.keyname(key)
109
109
  case s
110
110
  when /\AKEY_(.*)/
111
111
  "<#{$1.downcase}>"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ncursesw"
3
+ require "curses"
4
4
 
5
5
  module Textbringer
6
6
  class Keymap
@@ -43,10 +43,10 @@ module Textbringer
43
43
 
44
44
  def kbd(key)
45
45
  case key
46
- when Integer, Symbol
46
+ when Symbol
47
47
  [key]
48
48
  when String
49
- key.unpack("C*")
49
+ key.chars
50
50
  when Array
51
51
  key
52
52
  else
@@ -80,7 +80,7 @@ module Textbringer
80
80
  GLOBAL_MAP.define_key(:end, :end_of_line)
81
81
  GLOBAL_MAP.define_key("\e<", :beginning_of_buffer)
82
82
  GLOBAL_MAP.define_key("\e>", :end_of_buffer)
83
- (0x20..0x7e).each do |c|
83
+ (?\x20..?\x7e).each do |c|
84
84
  GLOBAL_MAP.define_key(c, :self_insert)
85
85
  end
86
86
  GLOBAL_MAP.define_key(?\t, :self_insert)
@@ -123,13 +123,8 @@ module Textbringer
123
123
  GLOBAL_MAP.define_key(?\C-r, :isearch_backward)
124
124
  GLOBAL_MAP.define_key("\e!", :shell_execute)
125
125
  GLOBAL_MAP.handle_undefined_key do |key|
126
- if key.is_a?(Integer) && key > 0x80
127
- begin
128
- key.chr(Encoding::UTF_8)
129
- :self_insert
130
- rescue RangeError
131
- nil
132
- end
126
+ if key.is_a?(String) && /[\0-\x7f]/ !~ key
127
+ :self_insert
133
128
  else
134
129
  nil
135
130
  end
@@ -25,6 +25,9 @@ module Textbringer
25
25
 
26
26
  def newline_and_reindent
27
27
  n = 1
28
+ if indent_line
29
+ n += 1
30
+ end
28
31
  @buffer.save_excursion do
29
32
  pos = @buffer.point
30
33
  @buffer.beginning_of_line
@@ -1,3 +1,3 @@
1
1
  module Textbringer
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,20 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ncursesw"
3
+ require "curses"
4
4
  require "unicode/display_width"
5
+ require "fiddle/import"
5
6
 
6
7
  module Textbringer
8
+ begin
9
+ # These features should be provided by curses.gem.
10
+ module PDCurses
11
+ extend Fiddle::Importer
12
+ dlload "pdcurses.dll"
13
+ extern "unsigned long PDC_get_key_modifiers(void)"
14
+ extern "int PDC_save_key_modifiers(unsigned char)"
15
+ extern "int PDC_return_key_modifiers(unsigned char)"
16
+
17
+ KEY_MODIFIER_SHIFT = 1
18
+ KEY_MODIFIER_CONTROL = 2
19
+ KEY_MODIFIER_ALT = 4
20
+ KEY_MODIFIER_NUMLOCK = 8
21
+ end
22
+ rescue
23
+ remove_const :PDCurses
24
+ end
25
+
7
26
  class Window
8
27
  KEY_NAMES = {}
9
- Ncurses.constants.grep(/\AKEY_/).each do |name|
10
- KEY_NAMES[Ncurses.const_get(name)] =
28
+ Curses.constants.grep(/\AKEY_/).each do |name|
29
+ KEY_NAMES[Curses.const_get(name)] =
11
30
  name.slice(/\AKEY_(.*)/, 1).downcase.intern
12
31
  end
13
32
 
14
- UTF8_CHAR_LEN =
15
- Buffer::UTF8_CHAR_LEN.each_with_object(Hash.new(1)) { |(k, v), h|
16
- h[k.ord] = v
17
- }
33
+ ALT_IS_FUNCTION_KEY =
34
+ /mswin32|mingw32/ =~ RUBY_PLATFORM && /PDCurses/ =~ Curses::VERSION
35
+ if ALT_IS_FUNCTION_KEY
36
+ KEY_OFFSET = 0xec00
37
+ ALT_0 = KEY_OFFSET + 0x97
38
+ ALT_9 = KEY_OFFSET + 0xa0
39
+ ALT_A = KEY_OFFSET + 0xa1
40
+ ALT_Z = KEY_OFFSET + 0xba
41
+ ALT_NUMBER_BASE = ALT_0 - ?0.ord
42
+ ALT_ALPHA_BASE = ALT_A - ?a.ord
43
+ end
18
44
 
19
45
  @@windows = []
20
46
  @@current = nil
@@ -88,9 +114,9 @@ module Textbringer
88
114
  end
89
115
 
90
116
  def self.start
91
- Ncurses.initscr
92
- Ncurses.noecho
93
- Ncurses.raw
117
+ Curses.init_screen
118
+ Curses.noecho
119
+ Curses.raw
94
120
  begin
95
121
  window =
96
122
  Textbringer::Window.new(Window.lines - 1, Window.columns, 0, 0)
@@ -99,14 +125,14 @@ module Textbringer
99
125
  Window.current = window
100
126
  @@echo_area = Textbringer::EchoArea.new(1, Window.columns,
101
127
  Window.lines - 1, 0)
102
- Buffer.minibuffer.keymap = MINIBUFFER_LOCAL_MAP
103
- @@echo_area.buffer = Buffer.minibuffer
104
- @@windows.push(@@echo_area)
128
+ Buffer.minibuffer.keymap = MINIBUFFER_LOCAL_MAP
129
+ @@echo_area.buffer = Buffer.minibuffer
130
+ @@windows.push(@@echo_area)
105
131
  yield
106
132
  ensure
107
- Ncurses.echo
108
- Ncurses.noraw
109
- Ncurses.endwin
133
+ Curses.echo
134
+ Curses.noraw
135
+ Curses.close_screen
110
136
  end
111
137
  end
112
138
 
@@ -127,20 +153,20 @@ module Textbringer
127
153
  end
128
154
 
129
155
  def self.update
130
- Ncurses.doupdate
156
+ Curses.doupdate
131
157
  end
132
158
 
133
159
  def self.lines
134
- Ncurses.LINES
160
+ Curses.lines
135
161
  end
136
162
 
137
163
  def self.columns
138
- Ncurses.COLS
164
+ Curses.cols
139
165
  end
140
166
 
141
167
  def self.resize
142
168
  @@windows.delete_if do |window|
143
- if window.y > Window.lines - 4
169
+ if !window.echo_area? && window.y > Window.lines - 4
144
170
  window.delete
145
171
  true
146
172
  else
@@ -148,10 +174,12 @@ module Textbringer
148
174
  end
149
175
  end
150
176
  @@windows.each_with_index do |window, i|
151
- if i < @@windows.size - 1
152
- window.resize(window.lines, Window.columns)
153
- else
154
- window.resize(Window.lines - 1 - window.y, Window.columns)
177
+ unless window.echo_area?
178
+ if i < @@windows.size - 2
179
+ window.resize(window.lines, Window.columns)
180
+ else
181
+ window.resize(Window.lines - 1 - window.y, Window.columns)
182
+ end
155
183
  end
156
184
  end
157
185
  @@echo_area.move(Window.lines - 1, 0)
@@ -159,7 +187,7 @@ module Textbringer
159
187
  end
160
188
 
161
189
  def self.beep
162
- Ncurses.beep
190
+ Curses.beep
163
191
  end
164
192
 
165
193
  attr_reader :buffer, :lines, :columns, :y, :x
@@ -170,7 +198,7 @@ module Textbringer
170
198
  @y = y
171
199
  @x = x
172
200
  initialize_window(lines, columns, y, x)
173
- @window.keypad(true)
201
+ @window.keypad = true
174
202
  @window.scrollok(false)
175
203
  @window.idlok(true)
176
204
  @buffer = nil
@@ -178,6 +206,7 @@ module Textbringer
178
206
  @bottom_of_window = nil
179
207
  @point_mark = nil
180
208
  @deleted = false
209
+ @key_buffer = []
181
210
  end
182
211
 
183
212
  def echo_area?
@@ -198,7 +227,7 @@ module Textbringer
198
227
  Window.current = @@windows.first
199
228
  end
200
229
  delete_marks
201
- @window.del
230
+ @window.close
202
231
  @deleted = true
203
232
  end
204
233
  end
@@ -233,58 +262,61 @@ module Textbringer
233
262
  self == @@current
234
263
  end
235
264
 
236
- def getch
237
- key = @window.getch
238
- if key.nil?
239
- nil
240
- elsif key > 0xff
241
- KEY_NAMES[key]
242
- else
243
- len = UTF8_CHAR_LEN[key]
244
- if len == 1
245
- key
246
- else
247
- buf = [key]
248
- (len - 1).times do
249
- c = @window.getch
250
- if c.nil? || c < 0x80 || c > 0xbf
251
- raise EditorError, "Malformed UTF-8 input"
252
- end
253
- buf.push(c)
254
- end
255
- s = buf.pack("C*").force_encoding(Encoding::UTF_8)
256
- if s.valid_encoding?
257
- s.ord
258
- else
259
- raise EditorError, "Malformed UTF-8 input"
265
+ def read_char
266
+ key = get_char
267
+ if key.is_a?(Integer)
268
+ if ALT_IS_FUNCTION_KEY
269
+ if ALT_0 <= key && key <= ALT_9
270
+ @key_buffer.push((key - ALT_NUMBER_BASE).chr)
271
+ return "\e"
272
+ elsif ALT_A <= key && key <= ALT_Z
273
+ @key_buffer.push((key - ALT_ALPHA_BASE).chr)
274
+ return "\e"
260
275
  end
261
276
  end
277
+ KEY_NAMES[key] || key
278
+ else
279
+ key&.encode(Encoding::UTF_8)&.tr("\r", "\n")
262
280
  end
263
281
  end
264
282
 
265
- def getch_nonblock
266
- @window.nodelay(true)
283
+ def read_char_nonblock
284
+ @window.nodelay = true
267
285
  begin
268
- getch
286
+ read_char
269
287
  ensure
270
- @window.nodelay(false)
288
+ @window.nodelay = false
271
289
  end
272
290
  end
273
291
 
274
292
  def wait_input(msecs)
275
- @window.timeout(msecs)
293
+ @window.timeout = msecs
276
294
  begin
277
- c = @window.getch
278
- if c && c >= 0
279
- Ncurses.ungetch(c)
295
+ c = @window.get_char
296
+ if c
297
+ Curses.unget_char(c)
280
298
  end
281
299
  c
282
300
  ensure
283
- @window.timeout(-1)
301
+ @window.timeout = -1
302
+ end
303
+ end
304
+
305
+ def has_input?
306
+ @window.nodelay = true
307
+ begin
308
+ c = @window.get_char
309
+ if c
310
+ Curses.unget_char(c)
311
+ end
312
+ !c.nil?
313
+ ensure
314
+ @window.nodelay = false
284
315
  end
285
316
  end
286
317
 
287
318
  def redisplay
319
+ return if has_input?
288
320
  return if @buffer.nil?
289
321
  redisplay_mode_line
290
322
  @buffer.save_point do |saved|
@@ -298,51 +330,51 @@ module Textbringer
298
330
  y = x = 0
299
331
  @buffer.point_to_mark(@top_of_window)
300
332
  @window.erase
301
- @window.move(0, 0)
333
+ @window.setpos(0, 0)
302
334
  if current? && @buffer.visible_mark &&
303
335
  @buffer.point_after_mark?(@buffer.visible_mark)
304
- @window.attron(Ncurses::A_REVERSE)
336
+ @window.attron(Curses::A_REVERSE)
305
337
  end
306
338
  while !@buffer.end_of_buffer?
307
339
  if @buffer.point_at_mark?(point)
308
- y, x = @window.getcury, @window.getcurx
340
+ y, x = @window.cury, @window.curx
309
341
  if current? && @buffer.visible_mark
310
342
  if @buffer.point_after_mark?(@buffer.visible_mark)
311
- @window.attroff(Ncurses::A_REVERSE)
343
+ @window.attroff(Curses::A_REVERSE)
312
344
  elsif @buffer.point_before_mark?(@buffer.visible_mark)
313
- @window.attron(Ncurses::A_REVERSE)
345
+ @window.attron(Curses::A_REVERSE)
314
346
  end
315
347
  end
316
348
  end
317
349
  if current? && @buffer.visible_mark &&
318
350
  @buffer.point_at_mark?(@buffer.visible_mark)
319
351
  if @buffer.point_after_mark?(point)
320
- @window.attroff(Ncurses::A_REVERSE)
352
+ @window.attroff(Curses::A_REVERSE)
321
353
  elsif @buffer.point_before_mark?(point)
322
- @window.attron(Ncurses::A_REVERSE)
354
+ @window.attron(Curses::A_REVERSE)
323
355
  end
324
356
  end
325
357
  c = @buffer.char_after
326
358
  if c == "\n"
327
359
  @window.clrtoeol
328
- break if @window.getcury == lines - 2 # lines include mode line
360
+ break if @window.cury == lines - 2 # lines include mode line
329
361
  elsif c == "\t"
330
- n = calc_tab_width(@window.getcurx)
362
+ n = calc_tab_width(@window.curx)
331
363
  c = " " * n
332
364
  else
333
365
  c = escape(c)
334
366
  end
335
367
  @window.addstr(c)
336
- break if @window.getcury == lines - 2 && # lines include mode line
337
- @window.getcurx == columns
368
+ break if @window.cury == lines - 2 && # lines include mode line
369
+ @window.curx == columns
338
370
  @buffer.forward_char
339
371
  end
340
372
  if current? && @buffer.visible_mark
341
- @window.attroff(Ncurses::A_REVERSE)
373
+ @window.attroff(Curses::A_REVERSE)
342
374
  end
343
375
  @buffer.mark_to_point(@bottom_of_window)
344
376
  if @buffer.point_at_mark?(point)
345
- y, x = @window.getcury, @window.getcurx
377
+ y, x = @window.cury, @window.curx
346
378
  end
347
379
  if x == columns - 1
348
380
  c = @buffer.char_after(point.location)
@@ -351,28 +383,28 @@ module Textbringer
351
383
  x = 0
352
384
  end
353
385
  end
354
- @window.move(y, x)
386
+ @window.setpos(y, x)
355
387
  @window.noutrefresh
356
388
  end
357
389
  end
358
390
 
359
391
  def redraw
360
- @window.redrawwin
361
- @mode_line.redrawwin
392
+ @window.redraw
393
+ @mode_line.redraw
362
394
  end
363
395
 
364
396
  def move(y, x)
365
397
  @y = y
366
398
  @x = x
367
- @window.mvwin(y, x)
368
- @mode_line.mvwin(y + @window.getmaxy, x)
399
+ @window.move(y, x)
400
+ @mode_line.move(y + @window.maxy, x)
369
401
  end
370
402
 
371
403
  def resize(lines, columns)
372
404
  @lines = lines
373
405
  @columns = columns
374
406
  @window.resize(lines - 1, columns)
375
- @mode_line.mvwin(@y + lines - 1, @x)
407
+ @mode_line.move(@y + lines - 1, @x)
376
408
  @mode_line.resize(1, columns)
377
409
  end
378
410
 
@@ -426,8 +458,8 @@ module Textbringer
426
458
  private
427
459
 
428
460
  def initialize_window(num_lines, num_columns, y, x)
429
- @window = Ncurses::WINDOW.new(num_lines - 1, num_columns, y, x)
430
- @mode_line = Ncurses::WINDOW.new(1, num_columns, y + num_lines - 1, x)
461
+ @window = Curses::Window.new(num_lines - 1, num_columns, y, x)
462
+ @mode_line = Curses::Window.new(1, num_columns, y + num_lines - 1, x)
431
463
  end
432
464
 
433
465
  def framer
@@ -454,8 +486,8 @@ module Textbringer
454
486
 
455
487
  def redisplay_mode_line
456
488
  @mode_line.erase
457
- @mode_line.move(0, 0)
458
- @mode_line.attron(Ncurses::A_REVERSE)
489
+ @mode_line.setpos(0, 0)
490
+ @mode_line.attron(Curses::A_REVERSE)
459
491
  @mode_line.addstr("#{@buffer.name} ")
460
492
  @mode_line.addstr("[+]") if @buffer.modified?
461
493
  @mode_line.addstr("[RO]") if @buffer.read_only?
@@ -472,8 +504,8 @@ module Textbringer
472
504
  @mode_line.addstr(unicode_codepoint(c))
473
505
  @mode_line.addstr(" #{line},#{column}")
474
506
  @mode_line.addstr(" (#{@buffer.mode&.name || 'None'})")
475
- @mode_line.addstr(" " * (@mode_line.getmaxx - @mode_line.getcurx))
476
- @mode_line.attroff(Ncurses::A_REVERSE)
507
+ @mode_line.addstr(" " * (@mode_line.maxx - @mode_line.curx))
508
+ @mode_line.attroff(Curses::A_REVERSE)
477
509
  @mode_line.noutrefresh
478
510
  end
479
511
 
@@ -508,6 +540,9 @@ module Textbringer
508
540
  def beginning_of_line_and_count(max_lines)
509
541
  e = @buffer.point
510
542
  @buffer.beginning_of_line
543
+ if e - @buffer.point < @columns
544
+ return 0
545
+ end
511
546
  s = @buffer.substring(@buffer.point, e)
512
547
  bols = [@buffer.point]
513
548
  column = 0
@@ -556,6 +591,36 @@ module Textbringer
556
591
  @point_mark = nil
557
592
  end
558
593
  end
594
+
595
+ def get_char
596
+ if @key_buffer.empty?
597
+ PDCurses.PDC_save_key_modifiers(1) if defined?(PDCurses)
598
+ need_retry = false
599
+ begin
600
+ key = @window.get_char
601
+ if defined?(PDCurses)
602
+ mods = PDCurses.PDC_get_key_modifiers
603
+ if key.is_a?(String) && key.ascii_only?
604
+ if (mods & PDCurses::KEY_MODIFIER_CONTROL) != 0
605
+ key = key == ?? ? "\x7f" : (key.ord & 0x9f).chr
606
+ end
607
+ if (mods & PDCurses::KEY_MODIFIER_ALT) != 0
608
+ if key == "\0"
609
+ # Alt + `, Alt + < etc. return NUL, so ignore it.
610
+ need_retry = true
611
+ else
612
+ @key_buffer.push(key)
613
+ key = "\e"
614
+ end
615
+ end
616
+ end
617
+ end
618
+ end while need_retry
619
+ key
620
+ else
621
+ @key_buffer.shift
622
+ end
623
+ end
559
624
  end
560
625
 
561
626
  class EchoArea < Window
@@ -595,40 +660,40 @@ module Textbringer
595
660
  return if @buffer.nil?
596
661
  @buffer.save_point do |saved|
597
662
  @window.erase
598
- @window.move(0, 0)
663
+ @window.setpos(0, 0)
599
664
  if @message
600
- @window.addstr @message
665
+ @window.addstr(escape(@message))
601
666
  else
602
- @window.addstr @prompt
667
+ @window.addstr(escape(@prompt))
603
668
  @buffer.beginning_of_line
604
669
  while !@buffer.end_of_buffer?
605
670
  if @buffer.point_at_mark?(saved)
606
- y, x = @window.getcury, @window.getcurx
671
+ y, x = @window.cury, @window.curx
607
672
  end
608
673
  c = @buffer.char_after
609
674
  if c == "\n"
610
675
  break
611
676
  end
612
- @window.addstr escape(c)
677
+ @window.addstr(escape(c))
613
678
  @buffer.forward_char
614
679
  end
615
680
  if @buffer.point_at_mark?(saved)
616
- y, x = @window.getcury, @window.getcurx
681
+ y, x = @window.cury, @window.curx
617
682
  end
618
- @window.move(y, x)
683
+ @window.setpos(y, x)
619
684
  end
620
685
  @window.noutrefresh
621
686
  end
622
687
  end
623
688
 
624
689
  def redraw
625
- @window.redrawwin
690
+ @window.redraw
626
691
  end
627
692
 
628
693
  def move(y, x)
629
694
  @y = y
630
695
  @x = x
631
- @window.mvwin(y, x)
696
+ @window.move(y, x)
632
697
  end
633
698
 
634
699
  def resize(lines, columns)
@@ -640,7 +705,7 @@ module Textbringer
640
705
  private
641
706
 
642
707
  def initialize_window(num_lines, num_columns, y, x)
643
- @window = Ncurses::WINDOW.new(num_lines, num_columns, y, x)
708
+ @window = Curses::Window.new(num_lines, num_columns, y, x)
644
709
  end
645
710
  end
646
711
  end
data/textbringer.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.required_ruby_version = '>= 2.3'
23
23
 
24
- spec.add_runtime_dependency "ncursesw", "~> 1.4"
24
+ spec.add_runtime_dependency "curses", "~> 1.1"
25
25
  spec.add_runtime_dependency "unicode-display_width", "~> 1.1"
26
26
 
27
27
  spec.add_development_dependency "bundler", "~> 1.11"
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textbringer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-01-22 00:00:00.000000000 Z
11
+ date: 2017-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: ncursesw
14
+ name: curses
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.4'
19
+ version: '1.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.4'
26
+ version: '1.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: unicode-display_width
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -119,8 +119,8 @@ extra_rdoc_files: []
119
119
  files:
120
120
  - ".gitignore"
121
121
  - ".travis.yml"
122
+ - CHANGES.md
122
123
  - Gemfile
123
- - Gemfile.lock
124
124
  - LICENSE.txt
125
125
  - README.md
126
126
  - Rakefile