textbringer 0.1.1 → 0.1.2

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.
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