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 +4 -4
- data/CHANGES.md +14 -0
- data/exe/textbringer +20 -6
- data/lib/textbringer/buffer.rb +49 -0
- data/lib/textbringer/commands.rb +9 -15
- data/lib/textbringer/config.rb +1 -0
- data/lib/textbringer/controller.rb +4 -4
- data/lib/textbringer/keymap.rb +6 -11
- data/lib/textbringer/modes/programming_mode.rb +3 -0
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +160 -95
- data/textbringer.gemspec +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df53610e7d9e7a4e7b657d38d9366a3be4752231
|
4
|
+
data.tar.gz: 07ce8c686e767f2d893f44ccb6994b8e6690abf2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
34
|
-
Window.redraw
|
39
|
+
begin
|
40
|
+
trap(:CONT) { Window.redraw }
|
41
|
+
rescue ArgumentError
|
35
42
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
data/lib/textbringer/buffer.rb
CHANGED
@@ -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)
|
data/lib/textbringer/commands.rb
CHANGED
@@ -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
|
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?(
|
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(
|
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
|
-
|
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.
|
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
|
-
(
|
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?(
|
500
|
-
|
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
|
571
|
+
c = Controller.current.last_key
|
578
572
|
ISEARCH_STATUS[:string].concat(c)
|
579
573
|
isearch_search
|
580
574
|
end
|
data/lib/textbringer/config.rb
CHANGED
@@ -34,7 +34,7 @@ module Textbringer
|
|
34
34
|
catch(tag) do
|
35
35
|
loop do
|
36
36
|
begin
|
37
|
-
c = Window.current.
|
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.
|
79
|
+
Window.current.read_char
|
80
80
|
end
|
81
81
|
|
82
82
|
def received_keyboard_quit?
|
83
|
-
while
|
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 =
|
108
|
+
s = Curses.keyname(key)
|
109
109
|
case s
|
110
110
|
when /\AKEY_(.*)/
|
111
111
|
"<#{$1.downcase}>"
|
data/lib/textbringer/keymap.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
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
|
46
|
+
when Symbol
|
47
47
|
[key]
|
48
48
|
when String
|
49
|
-
key.
|
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
|
-
(
|
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?(
|
127
|
-
|
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
|
data/lib/textbringer/version.rb
CHANGED
data/lib/textbringer/window.rb
CHANGED
@@ -1,20 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
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
|
-
|
10
|
-
KEY_NAMES[
|
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
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
156
|
+
Curses.doupdate
|
131
157
|
end
|
132
158
|
|
133
159
|
def self.lines
|
134
|
-
|
160
|
+
Curses.lines
|
135
161
|
end
|
136
162
|
|
137
163
|
def self.columns
|
138
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
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
|
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.
|
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
|
237
|
-
key =
|
238
|
-
if key.
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
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
|
266
|
-
@window.nodelay
|
283
|
+
def read_char_nonblock
|
284
|
+
@window.nodelay = true
|
267
285
|
begin
|
268
|
-
|
286
|
+
read_char
|
269
287
|
ensure
|
270
|
-
@window.nodelay
|
288
|
+
@window.nodelay = false
|
271
289
|
end
|
272
290
|
end
|
273
291
|
|
274
292
|
def wait_input(msecs)
|
275
|
-
@window.timeout
|
293
|
+
@window.timeout = msecs
|
276
294
|
begin
|
277
|
-
c = @window.
|
278
|
-
if c
|
279
|
-
|
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
|
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.
|
333
|
+
@window.setpos(0, 0)
|
302
334
|
if current? && @buffer.visible_mark &&
|
303
335
|
@buffer.point_after_mark?(@buffer.visible_mark)
|
304
|
-
@window.attron(
|
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.
|
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(
|
343
|
+
@window.attroff(Curses::A_REVERSE)
|
312
344
|
elsif @buffer.point_before_mark?(@buffer.visible_mark)
|
313
|
-
@window.attron(
|
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(
|
352
|
+
@window.attroff(Curses::A_REVERSE)
|
321
353
|
elsif @buffer.point_before_mark?(point)
|
322
|
-
@window.attron(
|
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.
|
360
|
+
break if @window.cury == lines - 2 # lines include mode line
|
329
361
|
elsif c == "\t"
|
330
|
-
n = calc_tab_width(@window.
|
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.
|
337
|
-
@window.
|
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(
|
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.
|
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.
|
386
|
+
@window.setpos(y, x)
|
355
387
|
@window.noutrefresh
|
356
388
|
end
|
357
389
|
end
|
358
390
|
|
359
391
|
def redraw
|
360
|
-
@window.
|
361
|
-
@mode_line.
|
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.
|
368
|
-
@mode_line.
|
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.
|
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 =
|
430
|
-
@mode_line =
|
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.
|
458
|
-
@mode_line.attron(
|
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.
|
476
|
-
@mode_line.attroff(
|
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.
|
663
|
+
@window.setpos(0, 0)
|
599
664
|
if @message
|
600
|
-
@window.addstr
|
665
|
+
@window.addstr(escape(@message))
|
601
666
|
else
|
602
|
-
@window.addstr
|
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.
|
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
|
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.
|
681
|
+
y, x = @window.cury, @window.curx
|
617
682
|
end
|
618
|
-
@window.
|
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.
|
690
|
+
@window.redraw
|
626
691
|
end
|
627
692
|
|
628
693
|
def move(y, x)
|
629
694
|
@y = y
|
630
695
|
@x = x
|
631
|
-
@window.
|
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 =
|
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 "
|
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.
|
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-
|
11
|
+
date: 2017-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: curses
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
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.
|
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
|