textbringer 0.1.6 → 0.1.7

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: 88862bef486e7b3ea0b5b2a410eae26b089b3180
4
- data.tar.gz: cfa7d1c4ca4e19a7f7b648d783f0779ea9469678
3
+ metadata.gz: acc4214c3d7c5b5535435505b2504db1f80d46ba
4
+ data.tar.gz: e0ad741989368ad91ce5d2b5100896297d28a059
5
5
  SHA512:
6
- metadata.gz: '0238827e2f2a54593d541dbbc621d135f9279d153e15120742efa6e775df74e8c0c95ed39366ec2e5833c5d8df0a16cc49279e48ed9c53bdff2e8684dd82d2ac'
7
- data.tar.gz: 4d29f982b2c0f761fce5b607fc11a5081ed029e09f81a2ab43c8664f22535ddafa2346406bec655ccd67d32a824ee77f53c375bf026211c804f1b3c130168d17
6
+ metadata.gz: f4adfeefebf234a5329c2bfda93f8d9ffe4a83fc029a57579fa8e72d58802aa92be479949a1243f540047ad495efa6507dbbd1d1f61cd5c27e8e3f17225160f3
7
+ data.tar.gz: 8edab0edf73fbff79c8a16b62a9863a6aa5ee5867abbc494e9bb261c2885fc5e277ac9957731e80adf8806fbcfa15a8e11096094259f0e4ebf7390ba16e3a21e
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ end_of_line = lf
5
+
6
+ [*.rb]
7
+ indent_style = space
8
+ indent_size = 2
9
+
@@ -7,6 +7,9 @@ cache: bundler
7
7
  script:
8
8
  - xvfb-run bundle exec rake test
9
9
  - bundle exec bundle-audit check --update
10
+ env:
11
+ global:
12
+ UPLOAD_TO_CODECOV: 1
10
13
  notifications:
11
14
  email:
12
15
  on_success: never
data/CHANGES.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.1.7
2
+
3
+ * Support EditorConfig.
4
+ * Add CONFIG[:ambiguos_east_asian_width].
5
+ * Add C mode.
6
+ * Add the *Completions* buffer.
7
+ * Echo multi-character commands in the echo area.
8
+
1
9
  ## 0.1.6
2
10
 
3
11
  * Fix bugs of clipboard commands.
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # Textbringer
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/textbringer.svg)](https://badge.fury.io/rb/textbringer)
4
+ [![Dependency Status](https://gemnasium.com/shugo/textbringer.svg)](https://gemnasium.com/shugo/textbringer)
4
5
  [![Build Status](https://travis-ci.org/shugo/textbringer.svg?branch=master)](https://travis-ci.org/shugo/textbringer)
5
6
  [![Build status](https://ci.appveyor.com/api/projects/status/n20vtpfgcgii5jtc?svg=true)](https://ci.appveyor.com/project/shugo31737/textbringer)
6
7
  [![codecov](https://codecov.io/gh/shugo/textbringer/branch/master/graph/badge.svg)](https://codecov.io/gh/shugo/textbringer)
7
8
 
8
- Textbringer is a member of a demon race that takes on the form of a text
9
- editor.
9
+ Textbringer is a member of a demon race that takes on the form of an Emacs-like
10
+ text editor.
10
11
 
11
12
  ## Demo
12
13
 
@@ -30,27 +31,60 @@ minor versions.
30
31
 
31
32
  You can quit the editor by C-x C-c.
32
33
 
33
- ## Configuration of terminal emulators
34
+ ## Configuration
34
35
 
35
- ### xterm
36
+ ### Meta key
37
+
38
+ You need the following configuration of terminal emulators to use meta key.
39
+
40
+ #### xterm
36
41
 
37
42
  Add the following line to ~/.Xresources.
38
43
 
39
44
  XTerm*metaSendsEscape: true
40
45
 
41
- ### mlterm
46
+ #### mlterm
42
47
 
43
48
  Add the following lines to ~/.mlterm/main.
44
49
 
45
50
  mod_meta_key = alt
46
51
  mod_meta_mode = esc
47
- col_size_of_width_a = 1
48
52
 
49
- ### screen
53
+ ### East asian ambiguous width
54
+
55
+ Add the following line to ~/.textbringer.rb to treat
56
+ [ambiguous characters](http://unicode.org/reports/tr11/#Ambiguous)
57
+ as fullwidth.
58
+
59
+ CONFIG[:east_asian_ambiguous_width] = 2
60
+
61
+ You also need a LD_PRELOAD hack or a modified locale charmap because ncursesw
62
+ uses wcwidth(3).
63
+
64
+ * https://github.com/fumiyas/wcwidth-cjk
65
+ * https://github.com/hamano/locale-eaw
66
+
67
+ xterm, mlterm and screen have their own configuration options.
68
+
69
+ #### xterm
70
+
71
+ Add the following lines to ~/.Xresources.
72
+
73
+ xterm*utf8: 1
74
+ xterm*locale: true
75
+ xterm*cjkWidth: true
76
+
77
+ #### mlterm
78
+
79
+ Add the following line to ~/.mlterm/main.
80
+
81
+ col_size_of_width_a = 2
82
+
83
+ #### screen
50
84
 
51
85
  Add the following line to ~/.screenrc.
52
86
 
53
- cjkwidth off
87
+ cjkwidth on
54
88
 
55
89
  ## Development
56
90
 
@@ -4,10 +4,13 @@ install:
4
4
  - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
5
5
  - gem install bundler --no-document
6
6
  - bundle install
7
+ - ps: Invoke-WebRequest -Uri http://curl.haxx.se/ca/cacert.pem -OutFile C:\projects\textbringer\cacert.pem
7
8
  build: off
8
9
  test_script:
9
10
  - rake
10
11
  deploy: off
11
12
  environment:
13
+ UPLOAD_TO_CODECOV: 1
14
+ SSL_CERT_FILE: C:\projects\textbringer\cacert.pem
12
15
  matrix:
13
16
  - ruby_version: "23-x64"
@@ -19,5 +19,7 @@ require_relative "textbringer/mode"
19
19
  require_relative "textbringer/modes/fundamental_mode"
20
20
  require_relative "textbringer/modes/programming_mode"
21
21
  require_relative "textbringer/modes/ruby_mode"
22
+ require_relative "textbringer/modes/c_mode"
22
23
  require_relative "textbringer/modes/backtrace_mode"
24
+ require_relative "textbringer/modes/completion_list_mode"
23
25
  require_relative "textbringer/controller"
@@ -158,8 +158,20 @@ module Textbringer
158
158
  end
159
159
 
160
160
  def self.display_width(s)
161
- # ncurses seems to treat ambiguous east asian characters as narrow.
162
- Unicode::DisplayWidth.of(s, 1)
161
+ Unicode::DisplayWidth.of(s, CONFIG[:east_asian_ambiguous_width])
162
+ end
163
+
164
+ def expand_tab(s)
165
+ # TOOD: Support multibyte characters
166
+ tw = self[:tab_width]
167
+ fmt = "A#{tw}"
168
+ s.b.gsub(/([^\t]{#{tw}})|([^\t]*)\t/n) {
169
+ [$+].pack(fmt)
170
+ }.force_encoding(Encoding::UTF_8)
171
+ end
172
+
173
+ def display_width(s)
174
+ Buffer.display_width(expand_tab(s))
163
175
  end
164
176
 
165
177
  # s might not be copied.
@@ -615,42 +627,22 @@ module Textbringer
615
627
  end
616
628
 
617
629
  def next_line(n = 1)
618
- if @goal_column
619
- column = @goal_column
620
- else
621
- prev_point = @point
622
- beginning_of_line
623
- column = Buffer.display_width(substring(@point, prev_point))
624
- end
630
+ column = get_goal_column
625
631
  n.times do
626
632
  end_of_line
627
633
  forward_char
628
- s = @point
629
- while !end_of_line? &&
630
- Buffer.display_width(substring(s, @point)) < column
631
- forward_char
632
- end
634
+ adjust_column(column)
633
635
  end
634
636
  @goal_column = column
635
637
  end
636
638
 
637
639
  def previous_line(n = 1)
638
- if @goal_column
639
- column = @goal_column
640
- else
641
- prev_point = @point
642
- beginning_of_line
643
- column = Buffer.display_width(substring(@point, prev_point))
644
- end
640
+ column = get_goal_column
645
641
  n.times do
646
642
  beginning_of_line
647
643
  backward_char
648
644
  beginning_of_line
649
- s = @point
650
- while !end_of_line? &&
651
- Buffer.display_width(substring(s, @point)) < column
652
- forward_char
653
- end
645
+ adjust_column(column)
654
646
  end
655
647
  @goal_column = column
656
648
  end
@@ -968,8 +960,12 @@ module Textbringer
968
960
  end
969
961
 
970
962
  def looking_at?(re)
971
- # TODO: optimization
972
- byteindex(true, re, @point) == @point
963
+ if re.is_a?(Regexp)
964
+ r = Regexp.new("\\G(?:#{re.source})", re.options)
965
+ else
966
+ r = "\\G(?:#{re})"
967
+ end
968
+ byteindex(true, r, @point) == @point
973
969
  end
974
970
 
975
971
  def byteindex(forward, re, pos)
@@ -1300,6 +1296,28 @@ module Textbringer
1300
1296
  end
1301
1297
  end
1302
1298
 
1299
+ def get_goal_column
1300
+ if @goal_column
1301
+ @goal_column
1302
+ else
1303
+ prev_point = @point
1304
+ beginning_of_line
1305
+ display_width(substring(@point, prev_point))
1306
+ end
1307
+ end
1308
+
1309
+ def adjust_column(column)
1310
+ s = @point
1311
+ w = 0
1312
+ while !end_of_line? &&
1313
+ (w = display_width(substring(s, @point))) < column
1314
+ forward_char
1315
+ end
1316
+ if w > column
1317
+ backward_char
1318
+ end
1319
+ end
1320
+
1303
1321
  def write_to_file(f)
1304
1322
  [@contents[0...@gap_start], @contents[@gap_end..-1]].each do |s|
1305
1323
  s.force_encoding(Encoding::UTF_8) unless @binary
@@ -55,9 +55,7 @@ module Textbringer
55
55
  define_command(:self_insert) do |n = number_prefix_arg|
56
56
  c = Controller.current.last_key
57
57
  merge_undo = Controller.current.last_command == :self_insert
58
- n.times do
59
- Buffer.current.insert(c, merge_undo)
60
- end
58
+ Buffer.current.insert(c * n, merge_undo)
61
59
  end
62
60
 
63
61
  define_command(:quoted_insert) do |n = number_prefix_arg|
@@ -65,9 +63,7 @@ module Textbringer
65
63
  if !c.is_a?(String)
66
64
  raise EditorError, "Invalid key"
67
65
  end
68
- n.times do
69
- Buffer.current.insert(c)
70
- end
66
+ Buffer.current.insert(c * n)
71
67
  end
72
68
 
73
69
  define_command(:kill_line) do
@@ -90,12 +86,12 @@ module Textbringer
90
86
 
91
87
  define_command(:undo) do
92
88
  Buffer.current.undo
93
- message("Undo!")
89
+ message("Undo!") unless Window.echo_area.current?
94
90
  end
95
91
 
96
92
  define_command(:redo) do
97
93
  Buffer.current.redo
98
- message("Redo!")
94
+ message("Redo!") unless Window.echo_area.current?
99
95
  end
100
96
  end
101
97
  end
@@ -5,7 +5,7 @@ require "clipboard"
5
5
  module Textbringer
6
6
  module Commands
7
7
  CLIPBOARD_AVAILABLE =
8
- Clipboard.implementation.name != :Linux ||
8
+ Clipboard.implementation.name != "Clipboard::Linux" ||
9
9
  (ENV["DISPLAY"] && system("which xclip > /dev/null 2>&1"))
10
10
 
11
11
  if CLIPBOARD_AVAILABLE
@@ -14,6 +14,7 @@ module Textbringer
14
14
  GLOBAL_MAP.define_key(?\C-k, :clipboard_kill_line)
15
15
  GLOBAL_MAP.define_key("\ed", :clipboard_kill_word)
16
16
  GLOBAL_MAP.define_key("\C-y", :clipboard_yank)
17
+ GLOBAL_MAP.define_key("\ey", :clipboard_yank_pop)
17
18
  end
18
19
 
19
20
  define_command(:clipboard_copy_region) do
@@ -37,12 +38,17 @@ module Textbringer
37
38
  end
38
39
 
39
40
  define_command(:clipboard_yank) do
40
- s = Clipboard.paste.encode(Encoding::UTF_8)
41
+ s = Clipboard.paste.encode(Encoding::UTF_8).gsub(/\r\n/, "\n")
41
42
  if !s.empty? && (KILL_RING.empty? || KILL_RING.current != s)
42
43
  KILL_RING.push(s)
43
44
  end
44
45
  yank
45
46
  Controller.current.this_command = :yank
46
47
  end
48
+
49
+ define_command(:clipboard_yank_pop) do
50
+ yank_pop
51
+ Clipboard.copy(KILL_RING.current)
52
+ end
47
53
  end
48
54
  end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "editorconfig"
4
+
3
5
  module Textbringer
4
6
  module Commands
5
7
  define_command(:find_file) do
6
8
  |file_name = read_file_name("Find file: ")|
9
+ config = EditorConfig.load_file(file_name)
7
10
  buffer = Buffer.find_file(file_name)
8
11
  if buffer.new_file?
9
12
  message("New file")
@@ -20,6 +23,29 @@ module Textbringer
20
23
  m.interpreter_name_pattern =~ shebang)
21
24
  } || FundamentalMode
22
25
  send(mode.command_name)
26
+ if config.key?("charset")
27
+ Buffer.current.file_encoding = config["charset"]
28
+ end
29
+ if config.key?("end_of_line")
30
+ Buffer.current.file_format =
31
+ case config["end_of_line"]
32
+ when "lf"
33
+ :unix
34
+ when "crlf"
35
+ :dos
36
+ when "cr"
37
+ :mac
38
+ end
39
+ end
40
+ if config.key?("indent_style")
41
+ Buffer.current[:indent_tabs_mode] = config["indent_style"] == "tab"
42
+ end
43
+ if config.key?("indent_size")
44
+ Buffer.current[:indent_level] = config["indent_size"].to_i
45
+ end
46
+ if config.key?("tab_width")
47
+ Buffer.current[:tab_width] = config["tab_width"].to_i
48
+ end
23
49
  end
24
50
 
25
51
  define_command(:save_buffer) do
@@ -77,15 +77,66 @@ module Textbringer
77
77
  raise Quit
78
78
  end
79
79
 
80
+ def update_completions(xs)
81
+ if xs.size > 1
82
+ if COMPLETION[:original_buffer].nil?
83
+ COMPLETION[:completions_window] = Window.windows[-2]
84
+ COMPLETION[:original_buffer] =
85
+ COMPLETION[:completions_window].buffer
86
+ end
87
+ completions = Buffer.find_or_new("*Completions*", undo_limit: 0)
88
+ if !completions.mode.is_a?(CompletionListMode)
89
+ completions.apply_mode(CompletionListMode)
90
+ end
91
+ completions.read_only = false
92
+ begin
93
+ completions.clear
94
+ xs.each do |x|
95
+ completions.insert(x + "\n")
96
+ end
97
+ COMPLETION[:completions_window].buffer = completions
98
+ ensure
99
+ completions.read_only = true
100
+ end
101
+ else
102
+ if COMPLETION[:original_buffer]
103
+ COMPLETION[:completions_window].buffer =
104
+ COMPLETION[:original_buffer]
105
+ end
106
+ end
107
+ end
108
+ private :update_completions
109
+
110
+ def complete_minibuffer_with_string(s)
111
+ minibuffer = Buffer.minibuffer
112
+ if s.start_with?(minibuffer.to_s)
113
+ minibuffer.insert(s[minibuffer.to_s.size..-1])
114
+ else
115
+ minibuffer.delete_region(minibuffer.point_min,
116
+ minibuffer.point_max)
117
+ minibuffer.insert(s)
118
+ end
119
+ end
120
+ private :complete_minibuffer_with_string
121
+
80
122
  define_command(:complete_minibuffer) do
81
123
  minibuffer = Buffer.minibuffer
82
124
  completion_proc = minibuffer[:completion_proc]
83
125
  if completion_proc
84
- s = completion_proc.call(minibuffer.to_s)
126
+ xs = completion_proc.call(minibuffer.to_s)
127
+ update_completions(xs)
128
+ if xs.empty?
129
+ message("No match", sit_for: 1)
130
+ return
131
+ end
132
+ y, *ys = xs
133
+ s = y.size.downto(1).lazy.map { |i|
134
+ y[0, i]
135
+ }.find { |i|
136
+ ys.all? { |j| j.start_with?(i) }
137
+ }
85
138
  if s
86
- minibuffer.delete_region(minibuffer.point_min,
87
- minibuffer.point_max)
88
- minibuffer.insert(s)
139
+ complete_minibuffer_with_string(s)
89
140
  end
90
141
  end
91
142
  end
@@ -184,18 +235,11 @@ module Textbringer
184
235
  Window.redisplay
185
236
  signals = [:INT, :TERM, :KILL]
186
237
  begin
187
- if /mswin32|mingw32/ =~ RUBY_PLATFORM
188
- opts = {}
189
- else
190
- opts = {pgroup: true}
191
- end
238
+ opts = /mswin32|mingw32/ =~ RUBY_PLATFORM ? {} : {pgroup: true}
192
239
  Open3.popen2e(cmd, opts) do |input, output, wait_thread|
193
240
  input.close
194
241
  loop do
195
242
  status = output.wait_readable(0.5)
196
- if status == false
197
- break # EOF
198
- end
199
243
  if status
200
244
  begin
201
245
  s = output.read_nonblock(1024).force_encoding("utf-8").
@@ -228,8 +272,6 @@ module Textbringer
228
272
  elsif status.signaled?
229
273
  signame = Signal.signame(status.termsig)
230
274
  message("Process #{pid} was killed by #{signame}")
231
- else
232
- message("Process #{pid} exited")
233
275
  end
234
276
  end
235
277
  ensure