textbringer 0.2.1 → 0.2.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: 906c3f715935cd762c39a92b8d92a9a7021179b4
4
- data.tar.gz: 5ba42d51b5af2086d2e9a839e07d4960f2f2f270
3
+ metadata.gz: df46cdf2ec8419a33fbfd4e23f41272bd4204ba2
4
+ data.tar.gz: 47179c38f72119aa5c4b96f01c80d28f10660531
5
5
  SHA512:
6
- metadata.gz: 2fd110a1970b6d0237c36365f8d1ff7eac130e881d5adcb7f0c0a4132bc5cdece78208397c87de0808c24c5538fea5ae492431b75b3220a132eb60b47736c42b
7
- data.tar.gz: e80b7040d0bcd2c7af8f166545f31f9c5573488fd8e5ad2c0d3d1ab838d2afcc9ed7bb7af44567e1806439e23ec9664c0280cd6b87f449f5830641386a4300a4
6
+ metadata.gz: b39cb72604f03c738a02b37fcc7c8d74bef47590ddde2293e6207f7be1147572395b760a02bcc74537872de3152967f447bcfb03b1fb83f447c274eff0322371
7
+ data.tar.gz: 864a8f20870885c9fb7c2252779cbe269f08ed34b1650b06233172fb69eac9fcdf76da5b6292150db4940ba7ce14b3f308b89c78084be811fd0f255553e8c720
data/.travis.yml CHANGED
@@ -4,11 +4,11 @@ matrix:
4
4
  - os: linux
5
5
  dist: trusty
6
6
  sudo: false
7
- rvm: 2.3.3
7
+ rvm: 2.3.4
8
8
  - os: linux
9
9
  dist: trusty
10
10
  sudo: false
11
- rvm: 2.4.0
11
+ rvm: 2.4.1
12
12
  - os: linux
13
13
  dist: trusty
14
14
  sudo: false
data/CHANGES.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.2.2
2
+
3
+ * Rename read_char to read_event and add read_char as a new method.
4
+ * Add next_tick and background for background threads.
5
+ * Add the force: option to kill_buffer.
6
+ * bind C-? ([delete] on Mac) to backward_delete_char.
7
+ Pull request #23 by moguno.
8
+ * Make commands module_functions.
9
+ * Use fiddley instead of ffi.
10
+
1
11
  ## 0.2.1
2
12
 
3
13
  * Add revert_buffer and revert_buffer_with_encoding.
data/exe/textbringer CHANGED
@@ -16,40 +16,44 @@ end
16
16
  $VERBOSE = nil
17
17
 
18
18
  Controller.current = Controller.new
19
- Window.start do
20
- begin
21
- Plugin.load_plugins
22
- load_user_config
23
- ruby_mode
24
- if ARGV.size > 0
25
- ARGV.each do |arg|
26
- find_file(arg)
19
+ begin
20
+ Window.start do
21
+ begin
22
+ Plugin.load_plugins
23
+ load_user_config
24
+ ruby_mode
25
+ if ARGV.size > 0
26
+ ARGV.each do |arg|
27
+ find_file(arg)
28
+ end
27
29
  end
28
- end
29
- if Buffer.dumped_buffers_exist?(CONFIG[:buffer_dump_dir])
30
- Window.redisplay
31
- if yes_or_no?("Dumped buffers found; restore them?")
32
- buffers = Buffer.load_dumped_buffers(CONFIG[:buffer_dump_dir])
33
- switch_to_buffer(buffers.last)
30
+ if Buffer.dumped_buffers_exist?(CONFIG[:buffer_dump_dir])
31
+ Window.redisplay
32
+ if yes_or_no?("Dumped buffers found; restore them?")
33
+ buffers = Buffer.load_dumped_buffers(CONFIG[:buffer_dump_dir])
34
+ switch_to_buffer(buffers.last)
35
+ end
34
36
  end
37
+ rescue Exception => e
38
+ show_exception(e)
35
39
  end
36
- rescue Exception => e
37
- show_exception(e)
38
- end
39
- Window.redisplay
40
- begin
41
- trap(:CONT) { Window.redraw }
42
- rescue ArgumentError
43
- end
44
- begin
45
- loop do
46
- Controller.current.command_loop(TOP_LEVEL_TAG)
47
- Window.redisplay
40
+ Window.redisplay
41
+ begin
42
+ trap(:CONT) { Window.redraw }
43
+ rescue ArgumentError
48
44
  end
49
- rescue Exception => e
50
- if !e.is_a?(SystemExit)
51
- Buffer.dump_unsaved_buffers(CONFIG[:buffer_dump_dir])
45
+ begin
46
+ loop do
47
+ Controller.current.command_loop(TOP_LEVEL_TAG)
48
+ Window.redisplay
49
+ end
50
+ rescue Exception => e
51
+ if !e.is_a?(SystemExit)
52
+ Buffer.dump_unsaved_buffers(CONFIG[:buffer_dump_dir])
53
+ end
54
+ raise
52
55
  end
53
- raise
54
56
  end
57
+ ensure
58
+ Controller.current.close
55
59
  end
@@ -26,6 +26,7 @@ module Textbringer
26
26
  def define_command(name, doc: "No documentation", &block)
27
27
  name = name.intern
28
28
  Commands.send(:define_method, name, &block)
29
+ Commands.send(:module_function, name)
29
30
  Commands.command_table[name] = Command.new(name, block, doc)
30
31
  name
31
32
  end
@@ -171,7 +171,7 @@ module Textbringer
171
171
  define_command(:quoted_insert,
172
172
  doc: "Read a character, and insert it.") do
173
173
  |n = number_prefix_arg|
174
- c = Controller.current.read_char
174
+ c = Controller.current.read_event
175
175
  if !c.is_a?(String)
176
176
  raise EditorError, "Invalid key"
177
177
  end
@@ -100,11 +100,12 @@ module Textbringer
100
100
  end
101
101
 
102
102
  define_command(:kill_buffer, doc: "Kill buffer.") do
103
- |buffer = read_buffer("Kill buffer: ", default: Buffer.current.name)|
103
+ |buffer = read_buffer("Kill buffer: ", default: Buffer.current.name),
104
+ force: false|
104
105
  if buffer.is_a?(String)
105
106
  buffer = Buffer[buffer]
106
107
  end
107
- if buffer.modified?
108
+ if !force && buffer.modified?
108
109
  next unless yes_or_no?("The last change is not saved; kill anyway?")
109
110
  message("Arioch! Arioch! Blood and souls for my Lord Arioch!")
110
111
  end
@@ -112,6 +113,11 @@ module Textbringer
112
113
  if Buffer.current.nil?
113
114
  switch_to_buffer(Buffer.other)
114
115
  end
116
+ Window.list(include_echo_area: true).each do |window|
117
+ if window.buffer == buffer
118
+ window.buffer = Buffer.current
119
+ end
120
+ end
115
121
  end
116
122
  end
117
123
  end
@@ -36,6 +36,14 @@ module Textbringer
36
36
  @recording_keyboard_macro = nil
37
37
  @last_keyboard_macro = nil
38
38
  @executing_keyboard_macro = nil
39
+ @next_tick_queue = []
40
+ @next_tick_queue_mutex = Mutex.new
41
+ @next_tick_input, @next_tick_output = IO.pipe
42
+ end
43
+
44
+ def close
45
+ @next_tick_input.close
46
+ @next_tick_output.close
39
47
  end
40
48
 
41
49
  def command_loop(tag)
@@ -43,7 +51,7 @@ module Textbringer
43
51
  loop do
44
52
  begin
45
53
  echo_input
46
- c = read_char
54
+ c = read_event
47
55
  break if c.nil?
48
56
  Window.echo_area.clear_message
49
57
  @last_key = c
@@ -92,22 +100,56 @@ module Textbringer
92
100
  end
93
101
 
94
102
  def wait_input(msecs)
103
+ # TODO: Check @next_tick_queue
95
104
  if executing_keyboard_macro?
96
105
  return @executing_keyboard_macro.first
97
106
  end
98
107
  Window.current.wait_input(msecs)
99
108
  end
100
109
 
101
- def read_char
102
- read_char_with_keyboard_macro(:read_char)
110
+ def next_tick(&block)
111
+ @next_tick_queue_mutex.synchronize do
112
+ @next_tick_queue.push(block)
113
+ end
114
+ @next_tick_output.write("\n")
115
+ end
116
+
117
+ def read_event
118
+ event = read_event_nonblock
119
+ if event
120
+ return event
121
+ end
122
+ loop do
123
+ if Window.echo_area.active?
124
+ wait_files = [STDIN]
125
+ else
126
+ wait_files = [STDIN, @next_tick_input]
127
+ end
128
+ files, = IO.select(wait_files, [], [], 1)
129
+ # KEY_RESIZE may be returned even if STDIN is not included in files.
130
+ event = read_event_nonblock
131
+ if event
132
+ return event
133
+ end
134
+ if !Window.echo_area.active? && files&.include?(@next_tick_input)
135
+ c = @next_tick_input.read_nonblock(1, exception: false)
136
+ if !c.nil? && c != :wait_readable
137
+ block = @next_tick_queue_mutex.synchronize {
138
+ @next_tick_queue.shift
139
+ }
140
+ block.call
141
+ Window.redisplay
142
+ end
143
+ end
144
+ end
103
145
  end
104
146
 
105
- def read_char_nonblock
106
- read_char_with_keyboard_macro(:read_char_nonblock)
147
+ def read_event_nonblock
148
+ read_event_with_keyboard_macro(:read_event_nonblock)
107
149
  end
108
150
 
109
151
  def received_keyboard_quit?
110
- while key = read_char_nonblock
152
+ while key = read_event_nonblock
111
153
  if GLOBAL_MAP.lookup([key]) == :keyboard_quit
112
154
  return true
113
155
  end
@@ -208,9 +250,9 @@ module Textbringer
208
250
 
209
251
  private
210
252
 
211
- def read_char_with_keyboard_macro(read_char_method)
253
+ def read_event_with_keyboard_macro(read_event_method)
212
254
  if !executing_keyboard_macro?
213
- c = call_read_char_method(read_char_method)
255
+ c = call_read_event_method(read_event_method)
214
256
  if @recording_keyboard_macro
215
257
  @recording_keyboard_macro.push(c)
216
258
  end
@@ -220,8 +262,8 @@ module Textbringer
220
262
  end
221
263
  end
222
264
 
223
- def call_read_char_method(read_char_method)
224
- Window.current.send(read_char_method)
265
+ def call_read_event_method(read_event_method)
266
+ Window.current.send(read_event_method)
225
267
  end
226
268
  end
227
269
  end
@@ -109,6 +109,7 @@ module Textbringer
109
109
  GLOBAL_MAP.define_key(?\C-d, :delete_char)
110
110
  GLOBAL_MAP.define_key(:backspace, :backward_delete_char)
111
111
  GLOBAL_MAP.define_key(?\C-h, :backward_delete_char)
112
+ GLOBAL_MAP.define_key(?\C-?, :backward_delete_char)
112
113
  GLOBAL_MAP.define_key(?\C-a, :beginning_of_line)
113
114
  GLOBAL_MAP.define_key(:home, :beginning_of_line)
114
115
  GLOBAL_MAP.define_key(?\C-e, :end_of_line)
@@ -78,8 +78,8 @@ module Textbringer
78
78
  (?<singleline_comment> \/\/ .*(?:\\\n.*)*(?<!\\)\n )
79
79
  ) |
80
80
  (?<partial_comment>
81
- (?<multiline_comment> \/\* (?:.|\n)* ) |
82
- (?<singleline_comment> \/\/ .*? \\\n (?:.|\n)* )
81
+ (?<partial_multiline_comment> \/\* (?:.|\n)* ) |
82
+ (?<partial_singleline_comment> \/\/ .*? \\\n (?:.|\n)* )
83
83
  ) |
84
84
  (?<keyword>
85
85
  (?: #{KEYWORDS.join("|")} ) \b
@@ -177,10 +177,9 @@ module Textbringer
177
177
 
178
178
  def lex(s)
179
179
  tokens = []
180
- pos = 0
181
180
  line = 1
182
181
  column = 0
183
- while pos < s.size && s.index(TOKEN_REGEXP, pos)
182
+ s.scan(TOKEN_REGEXP) do
184
183
  text = $&
185
184
  token_name = TOKEN_NAMES.find { |name| $~[name] }
186
185
  if text.empty?
@@ -194,7 +193,6 @@ module Textbringer
194
193
  else
195
194
  column += text.size
196
195
  end
197
- pos += text.size
198
196
  end
199
197
  tokens
200
198
  end
@@ -2,9 +2,14 @@
2
2
 
3
3
  module Textbringer
4
4
  module Plugin
5
+ class << self
6
+ attr_accessor :directory
7
+ end
8
+
9
+ @directory = File.expand_path("~/.textbringer/plugins")
10
+
5
11
  def self.load_plugins
6
- files = Gem.find_files("textbringer_plugin.rb")
7
- files.group_by { |file|
12
+ files = Gem.find_files("textbringer_plugin.rb").group_by { |file|
8
13
  file.slice(/([^\/]+)-[\w.]+\/lib\/textbringer_plugin\.rb\z/, 1)
9
14
  }.map { |gem, versions|
10
15
  versions.sort_by { |version|
@@ -12,10 +17,10 @@ module Textbringer
12
17
  1)
13
18
  Gem::Version.create(v)
14
19
  }.last
15
- }.each do |file|
20
+ } + Dir.glob(File.join(directory, "*/**/textbringer_plugin.rb"))
21
+ files.each do |file|
16
22
  load(file)
17
23
  end
18
24
  end
19
25
  end
20
26
  end
21
-
@@ -49,8 +49,32 @@ module Textbringer
49
49
  sleep(secs)
50
50
  end
51
51
 
52
+ def background
53
+ Thread.start do
54
+ begin
55
+ yield
56
+ rescue Exception => e
57
+ next_tick do
58
+ raise e
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def next_tick(*args, &block)
65
+ Controller.current.next_tick(*args, &block)
66
+ end
67
+
68
+ def read_event
69
+ Controller.current.read_event
70
+ end
71
+
52
72
  def read_char
53
- Controller.current.read_char
73
+ event = Controller.current.read_event
74
+ if !event.is_a?(String)
75
+ raise EditorError, "Non character event: #{event.inspect}"
76
+ end
77
+ event
54
78
  end
55
79
 
56
80
  def received_keyboard_quit?
@@ -1,3 +1,3 @@
1
1
  module Textbringer
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -322,7 +322,7 @@ module Textbringer
322
322
  self == @@current
323
323
  end
324
324
 
325
- def read_char
325
+ def read_event
326
326
  key = get_char
327
327
  if key.is_a?(Integer)
328
328
  if PDCurses.dll_loaded?
@@ -340,10 +340,10 @@ module Textbringer
340
340
  end
341
341
  end
342
342
 
343
- def read_char_nonblock
343
+ def read_event_nonblock
344
344
  @window.nodelay = true
345
345
  begin
346
- read_char
346
+ read_event
347
347
  ensure
348
348
  @window.nodelay = false
349
349
  end
@@ -553,7 +553,7 @@ module Textbringer
553
553
 
554
554
  def scroll_up
555
555
  if @bottom_of_window.location == @buffer.point_max
556
- raise EditorError, "End of buffer"
556
+ raise RangeError, "End of buffer"
557
557
  end
558
558
  @buffer.point_to_mark(@bottom_of_window)
559
559
  @buffer.previous_line
@@ -563,7 +563,7 @@ module Textbringer
563
563
 
564
564
  def scroll_down
565
565
  if @top_of_window.location == @buffer.point_min
566
- raise EditorError, "Beginning of buffer"
566
+ raise RangeError, "Beginning of buffer"
567
567
  end
568
568
  @buffer.point_to_mark(@top_of_window)
569
569
  @buffer.next_line
@@ -928,7 +928,8 @@ module Textbringer
928
928
  if width > max_width
929
929
  @buffer.forward_char
930
930
  break
931
- elsif width == max_width || @buffer.beginning_of_line?
931
+ elsif width == max_width || @buffer.beginning_of_line? ||
932
+ @buffer.point_at_mark?(@top_of_window)
932
933
  break
933
934
  end
934
935
  @buffer.backward_char
data/textbringer.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency "curses", "~> 1.2"
25
25
  spec.add_runtime_dependency "unicode-display_width", "~> 1.1"
26
26
  spec.add_runtime_dependency "clipboard", "~> 1.1"
27
- spec.add_runtime_dependency "ffi"
27
+ spec.add_runtime_dependency "fiddley", ">= 0.0.5"
28
28
  spec.add_runtime_dependency "editorconfig"
29
29
 
30
30
  spec.add_development_dependency "bundler", "~> 1.14"
@@ -33,7 +33,5 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency "simplecov"
34
34
  spec.add_development_dependency "codecov"
35
35
  spec.add_development_dependency "bundler-audit"
36
- spec.add_development_dependency "guard"
37
- spec.add_development_dependency "guard-shell"
38
36
  spec.add_development_dependency "ripper-tags"
39
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textbringer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.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-03-27 00:00:00.000000000 Z
11
+ date: 2017-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: curses
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.1'
55
55
  - !ruby/object:Gem::Dependency
56
- name: ffi
56
+ name: fiddley
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 0.0.5
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 0.0.5
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: editorconfig
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -164,34 +164,6 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
- - !ruby/object:Gem::Dependency
168
- name: guard
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - ">="
172
- - !ruby/object:Gem::Version
173
- version: '0'
174
- type: :development
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - ">="
179
- - !ruby/object:Gem::Version
180
- version: '0'
181
- - !ruby/object:Gem::Dependency
182
- name: guard-shell
183
- requirement: !ruby/object:Gem::Requirement
184
- requirements:
185
- - - ">="
186
- - !ruby/object:Gem::Version
187
- version: '0'
188
- type: :development
189
- prerelease: false
190
- version_requirements: !ruby/object:Gem::Requirement
191
- requirements:
192
- - - ">="
193
- - !ruby/object:Gem::Version
194
- version: '0'
195
167
  - !ruby/object:Gem::Dependency
196
168
  name: ripper-tags
197
169
  requirement: !ruby/object:Gem::Requirement
@@ -221,7 +193,6 @@ files:
221
193
  - ".travis.yml"
222
194
  - CHANGES.md
223
195
  - Gemfile
224
- - Guardfile
225
196
  - LICENSE.txt
226
197
  - README.md
227
198
  - Rakefile
data/Guardfile DELETED
@@ -1,22 +0,0 @@
1
- # A sample Guardfile
2
- # More info at https://github.com/guard/guard#readme
3
-
4
- ## Uncomment and set this to only include directories you want to watch
5
- # directories %w(app lib config test spec features) \
6
- # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
-
8
- ## Note: if you are using the `directories` clause above and you are not
9
- ## watching the project directory ('.'), then you will want to move
10
- ## the Guardfile to a watched dir and symlink it back, e.g.
11
- #
12
- # $ mkdir config
13
- # $ mv Guardfile config/
14
- # $ ln -s config/Guardfile .
15
- #
16
- # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
-
18
- guard :shell do
19
- watch(%r'^(lib|test)/.+\.rb$') do
20
- `bundle exec tbtags -R`
21
- end
22
- end