textbringer 1.2.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/macos.yml +10 -3
- data/.github/workflows/ubuntu.yml +5 -4
- data/.github/workflows/windows.yml +5 -3
- data/CHANGES.md +11 -0
- data/README.ja.md +104 -0
- data/README.md +12 -2
- data/exe/txtb +2 -0
- data/lib/textbringer/buffer.rb +44 -47
- data/lib/textbringer/commands/clipboard.rb +1 -2
- data/lib/textbringer/commands/files.rb +15 -0
- data/lib/textbringer/commands/help.rb +42 -1
- data/lib/textbringer/commands/misc.rb +4 -1
- data/lib/textbringer/default_output.rb +18 -0
- data/lib/textbringer/input_methods/t_code_input_method.rb +5 -1
- data/lib/textbringer/keymap.rb +3 -0
- data/lib/textbringer/modes/c_mode.rb +2 -2
- data/lib/textbringer/utils.rb +27 -0
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +0 -1
- data/lib/textbringer.rb +1 -0
- data/textbringer.gemspec +3 -1
- metadata +34 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f10c30433280a10e91f251f04f68ddd5b2cf02347de8712a9828b4488d49f7b4
|
4
|
+
data.tar.gz: fced66d8271da69b0bc8425c00f8dade0e8f4ee842bd6454d3584455a4d9cc9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b69a7f035f1d502845d50ac578e25a02546207e0a7c3bc84ffd45c7bb1b2f8f58e833540c3b1f01e617923e1f74dc1fdd26ea4e1f7ad28d476ba198585ea05be
|
7
|
+
data.tar.gz: 98363d4a9d74c8b8ff785e244f07d982214a5fd40029c5cf634c48d6e214d084d4939bf58b5bd97008051771dd96522bc78e2bb981dcca611eeafc78d6713101
|
data/.github/workflows/macos.yml
CHANGED
@@ -3,14 +3,21 @@ name: macos
|
|
3
3
|
on: [push]
|
4
4
|
|
5
5
|
jobs:
|
6
|
-
|
6
|
+
test:
|
7
7
|
runs-on: macos-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: [ head, 3.3 ]
|
8
11
|
timeout-minutes: 10
|
12
|
+
env:
|
13
|
+
RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
|
9
14
|
steps:
|
10
|
-
- uses: actions/checkout@
|
15
|
+
- uses: actions/checkout@v4
|
16
|
+
- uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
11
19
|
- name: Install dependencies
|
12
20
|
run: |
|
13
|
-
gem install bundler --no-document
|
14
21
|
bundle install
|
15
22
|
- name: Run test
|
16
23
|
run: bundle exec rake test
|
@@ -4,20 +4,21 @@ on: [push]
|
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
test:
|
7
|
+
runs-on: ubuntu-latest
|
7
8
|
strategy:
|
8
9
|
matrix:
|
9
|
-
ruby: [ head, 3.
|
10
|
-
runs-on: ubuntu-latest
|
10
|
+
ruby: [ head, 3.3, 3.2, 3.1 ]
|
11
11
|
timeout-minutes: 10
|
12
|
+
env:
|
13
|
+
RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
|
12
14
|
steps:
|
13
|
-
- uses: actions/checkout@
|
15
|
+
- uses: actions/checkout@v4
|
14
16
|
- uses: ruby/setup-ruby@v1
|
15
17
|
with:
|
16
18
|
ruby-version: ${{ matrix.ruby }}
|
17
19
|
- name: Install dependencies
|
18
20
|
run: |
|
19
21
|
sudo apt install libncursesw5-dev
|
20
|
-
gem install bundler --no-document
|
21
22
|
bundle install
|
22
23
|
- name: Run test
|
23
24
|
run: xvfb-run bundle exec rake test
|
@@ -3,14 +3,16 @@ name: windows
|
|
3
3
|
on: [push]
|
4
4
|
|
5
5
|
jobs:
|
6
|
-
|
6
|
+
test:
|
7
7
|
runs-on: windows-latest
|
8
8
|
strategy:
|
9
9
|
matrix:
|
10
|
-
ruby: [ 'mingw', 'mswin', '3.
|
10
|
+
ruby: [ 'mingw', 'mswin', '3.3' ]
|
11
11
|
timeout-minutes: 10
|
12
|
+
env:
|
13
|
+
RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
|
12
14
|
steps:
|
13
|
-
- uses: actions/checkout@
|
15
|
+
- uses: actions/checkout@v4
|
14
16
|
- name: Set up Ruby
|
15
17
|
uses: ruby/setup-ruby@v1
|
16
18
|
with:
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
This file is no longer being updated after version 1.3.0.
|
2
|
+
For changes since version 1.3.0, see <URL:https://github.com/shugo/textbringer/releases>.
|
3
|
+
|
4
|
+
## 1.3.0
|
5
|
+
|
6
|
+
* Kernel#print etc. inserts the given string into the current buffer now.
|
7
|
+
* Add find_source.
|
8
|
+
* Add describe_class and describe_method.
|
9
|
+
* Support completion for eval_expression
|
10
|
+
* Fix digraph indentation in C mode.
|
11
|
+
|
1
12
|
## 1.2.0
|
2
13
|
|
3
14
|
* Drop Ruby 2.6 support.
|
data/README.ja.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# ![Textbringer](logo/logo.png)
|
2
|
+
|
3
|
+
[![LatestVer](https://badgen.net/badge/latestver/0.1)](https://latestver.org/)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/textbringer.svg)](https://badge.fury.io/rb/textbringer)
|
5
|
+
[![ubuntu](https://github.com/shugo/textbringer/workflows/ubuntu/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Aubuntu)
|
6
|
+
[![windows](https://github.com/shugo/textbringer/workflows/windows/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Awindows)
|
7
|
+
[![macos](https://github.com/shugo/textbringer/workflows/macos/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Amacos)
|
8
|
+
|
9
|
+
TextbringerはRubyで実装されたEmacsライクなテキストエディタです。
|
10
|
+
Lispの代りにRubyで拡張することができます。
|
11
|
+
|
12
|
+
## スクリーンショット
|
13
|
+
|
14
|
+
![Screenshot](screenshot.png)
|
15
|
+
|
16
|
+
## デモ
|
17
|
+
|
18
|
+
* FizzBuzz: https://asciinema.org/a/103357
|
19
|
+
* Rubyプログラミング: https://asciinema.org/a/100156
|
20
|
+
* 日本語テキストの編集: https://asciinema.org/a/100166
|
21
|
+
|
22
|
+
## インストール
|
23
|
+
|
24
|
+
$ gem install textbringer
|
25
|
+
|
26
|
+
マルチバイト文字を使用するためにはncurseswが必要です。
|
27
|
+
Textbringerが依存するcurses.gemをインストールする前に、ncurseswをインストールしておいてください。
|
28
|
+
|
29
|
+
$ sudo apt-get install libncursesw5-dev
|
30
|
+
$ gem install curses
|
31
|
+
|
32
|
+
## 使い方
|
33
|
+
|
34
|
+
$ txtb
|
35
|
+
|
36
|
+
`Ctrl-x Ctrl-c` で終了できます。
|
37
|
+
|
38
|
+
多くのコマンドとキーバインディングは[Emacs](https://www.gnu.org/software/emacs/)に似ています。
|
39
|
+
|
40
|
+
キーバインディングを確認するには、 `F1 b` または `Alt+x describe_bindings RET` とタイプしてください。
|
41
|
+
|
42
|
+
## 設定
|
43
|
+
|
44
|
+
### メタキー
|
45
|
+
|
46
|
+
端末エミュレータでメタキーを使用するためには、以下のような設定が必要です。
|
47
|
+
|
48
|
+
#### xterm
|
49
|
+
|
50
|
+
~/.Xresourcesに以下の行を追加してください。
|
51
|
+
|
52
|
+
XTerm*metaSendsEscape: true
|
53
|
+
|
54
|
+
#### mlterm
|
55
|
+
|
56
|
+
~/.mlterm/mainに以下の行を追加してください。
|
57
|
+
|
58
|
+
mod_meta_key = alt
|
59
|
+
mod_meta_mode = esc
|
60
|
+
|
61
|
+
### 東アジアの曖昧な文字幅
|
62
|
+
|
63
|
+
[曖昧な文字](http://unicode.org/reports/tr11/#Ambiguous)を全角扱いするには、以下の設定を~/.textbringer.rbに記述してください。
|
64
|
+
|
65
|
+
CONFIG[:east_asian_ambiguous_width] = 2
|
66
|
+
|
67
|
+
ncurseswはwcwidth(3)を使用するため、LD_PRELOADハックやlocale charmapの修正が必要かもしれません。
|
68
|
+
|
69
|
+
* https://github.com/fumiyas/wcwidth-cjk
|
70
|
+
* https://github.com/hamano/locale-eaw
|
71
|
+
|
72
|
+
xterm、 mlterm、screenにはそれぞれ独自の設定項目があります。
|
73
|
+
|
74
|
+
#### xterm
|
75
|
+
|
76
|
+
~/.Xresourcesに以下の設定を追加してください。
|
77
|
+
|
78
|
+
xterm*utf8: 1
|
79
|
+
xterm*locale: true
|
80
|
+
xterm*cjkWidth: true
|
81
|
+
|
82
|
+
#### mlterm
|
83
|
+
|
84
|
+
~/.mlterm/mainに以下の設定を追加してください。
|
85
|
+
|
86
|
+
col_size_of_width_a = 2
|
87
|
+
|
88
|
+
#### screen
|
89
|
+
|
90
|
+
~/.screenrcに以下の設定を追加してください。
|
91
|
+
|
92
|
+
cjkwidth on
|
93
|
+
|
94
|
+
## プラグイン
|
95
|
+
|
96
|
+
* [Mournmail](https://github.com/shugo/mournmail): 電子メールクライアント
|
97
|
+
* [MedicineShield](https://github.com/shugo/medicine_shield): Mastodonクライアント
|
98
|
+
* [textbringer-presentation](https://github.com/shugo/textbringer-presentation): プレゼンテーションツール
|
99
|
+
* [textbringer-ghost_text](https://github.com/shugo/textbringer-ghost_text): [GhostText](https://github.com/fregante/GhostText)プラグイン
|
100
|
+
|
101
|
+
## ライセンス
|
102
|
+
|
103
|
+
[MIT License](http://opensource.org/licenses/MIT)の下でオープンソースとして利用可能です。
|
104
|
+
|
data/README.md
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# ![Textbringer](logo/logo.png)
|
2
2
|
|
3
|
+
[![LatestVer](https://badgen.net/badge/latestver/0.1)](https://latestver.org/)
|
3
4
|
[![Gem Version](https://badge.fury.io/rb/textbringer.svg)](https://badge.fury.io/rb/textbringer)
|
4
5
|
[![ubuntu](https://github.com/shugo/textbringer/workflows/ubuntu/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Aubuntu)
|
5
6
|
[![windows](https://github.com/shugo/textbringer/workflows/windows/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Awindows)
|
6
7
|
[![macos](https://github.com/shugo/textbringer/workflows/macos/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Amacos)
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
* [Japanese version](README.ja.md)
|
10
|
+
|
11
|
+
Textbringer is an Emacs-like text editor written in Ruby.
|
12
|
+
It is extensible by Ruby instead of Lisp.
|
10
13
|
|
11
14
|
## Screenshot
|
12
15
|
|
@@ -93,6 +96,13 @@ Add the following line to ~/.screenrc.
|
|
93
96
|
|
94
97
|
cjkwidth on
|
95
98
|
|
99
|
+
## Plugins
|
100
|
+
|
101
|
+
* [Mournmail](https://github.com/shugo/mournmail): a mail user agent
|
102
|
+
* [MedicineShield](https://github.com/shugo/medicine_shield): a Mastodon client
|
103
|
+
* [textbringer-presentation](https://github.com/shugo/textbringer-presentation): a presentation tool
|
104
|
+
* [textbringer-ghost_text](https://github.com/shugo/textbringer-ghost_text): a [GhostText](https://github.com/fregante/GhostText) plugin
|
105
|
+
|
96
106
|
## Development
|
97
107
|
|
98
108
|
After checking out the repo, run `bundle install` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/exe/txtb
CHANGED
@@ -4,6 +4,7 @@ require "warning"
|
|
4
4
|
|
5
5
|
Warning.ignore(/already initialized constant /)
|
6
6
|
Warning.ignore(/previous definition of /)
|
7
|
+
Warning.ignore(/not part of the default gems/)
|
7
8
|
|
8
9
|
require "textbringer"
|
9
10
|
|
@@ -26,6 +27,7 @@ end
|
|
26
27
|
Controller.current = Controller.new
|
27
28
|
begin
|
28
29
|
Window.start do
|
30
|
+
$stdout = DefaultOutput.new
|
29
31
|
begin
|
30
32
|
load_user_config("~/.textbringer/init.rb")
|
31
33
|
Plugin.load_plugins
|
data/lib/textbringer/buffer.rb
CHANGED
@@ -44,7 +44,11 @@ module Textbringer
|
|
44
44
|
e = NKF.guess(s)
|
45
45
|
case e
|
46
46
|
when Encoding::US_ASCII
|
47
|
-
|
47
|
+
if s.ascii_only?
|
48
|
+
Encoding::UTF_8
|
49
|
+
else
|
50
|
+
Encoding::ASCII_8BIT
|
51
|
+
end
|
48
52
|
when Encoding::ASCII_8BIT
|
49
53
|
s.force_encoding(Encoding::UTF_8)
|
50
54
|
if s.valid_encoding?
|
@@ -58,7 +62,14 @@ module Textbringer
|
|
58
62
|
end
|
59
63
|
}
|
60
64
|
|
61
|
-
|
65
|
+
HAS_BYTEINDEX = String.instance_methods.include?(:byteindex)
|
66
|
+
HAS_BYTESPLICE = String.instance_methods.include?(:bytesplice)
|
67
|
+
BYTESPLICE_SUPPORTS_PARTIAL_COPY =
|
68
|
+
begin
|
69
|
+
(+"foo").bytesplice(0, 2, "bar", 1, 2) == "aro"
|
70
|
+
rescue NoMethodError, ArgumentError
|
71
|
+
false
|
72
|
+
end
|
62
73
|
|
63
74
|
if !defined?(@@detect_encoding_proc)
|
64
75
|
@@detect_encoding_proc = DEFAULT_DETECT_ENCODING
|
@@ -271,13 +282,6 @@ module Textbringer
|
|
271
282
|
def file_encoding=(enc)
|
272
283
|
@file_encoding = Encoding.find(enc)
|
273
284
|
@binary = @file_encoding == Encoding::ASCII_8BIT
|
274
|
-
if STRING_HAS_BYTE_BASED_METHODS
|
275
|
-
if @binary
|
276
|
-
@contents.force_encoding(Encoding::ASCII_8BIT)
|
277
|
-
else
|
278
|
-
@contents.force_encoding(Encoding::UTF_8)
|
279
|
-
end
|
280
|
-
end
|
281
285
|
end
|
282
286
|
|
283
287
|
def binary?
|
@@ -448,7 +452,7 @@ module Textbringer
|
|
448
452
|
def to_s
|
449
453
|
result = @contents.byteslice(0...@gap_start) +
|
450
454
|
@contents.byteslice(@gap_end..-1)
|
451
|
-
if !@binary
|
455
|
+
if !@binary
|
452
456
|
result.force_encoding(Encoding::UTF_8)
|
453
457
|
end
|
454
458
|
result
|
@@ -463,7 +467,7 @@ module Textbringer
|
|
463
467
|
@contents.byteslice(user_to_gap(s), len) +
|
464
468
|
@contents.byteslice(@gap_end, e - s - len)
|
465
469
|
end
|
466
|
-
if !@binary
|
470
|
+
if !@binary
|
467
471
|
result.force_encoding(Encoding::UTF_8)
|
468
472
|
end
|
469
473
|
result
|
@@ -525,7 +529,7 @@ module Textbringer
|
|
525
529
|
if pos == point_min
|
526
530
|
column = 1
|
527
531
|
else
|
528
|
-
if
|
532
|
+
if HAS_BYTEINDEX
|
529
533
|
begin
|
530
534
|
i = @contents.byterindex("\n", user_to_gap(get_pos(pos, -1)))
|
531
535
|
rescue RangeError
|
@@ -562,7 +566,7 @@ module Textbringer
|
|
562
566
|
pos = point_min
|
563
567
|
i = 1
|
564
568
|
while i < n && pos < @contents.bytesize
|
565
|
-
if
|
569
|
+
if HAS_BYTEINDEX
|
566
570
|
pos = @contents.byteindex("\n", pos)
|
567
571
|
else
|
568
572
|
pos = @contents.index("\n", pos)
|
@@ -586,7 +590,7 @@ module Textbringer
|
|
586
590
|
pos = @point
|
587
591
|
size = s.bytesize
|
588
592
|
adjust_gap(size)
|
589
|
-
splice_contents(@point, size,
|
593
|
+
splice_contents(@point, size, s.b)
|
590
594
|
@marks.each do |m|
|
591
595
|
if m.location > @point
|
592
596
|
m.location += size
|
@@ -995,10 +999,7 @@ module Textbringer
|
|
995
999
|
|
996
1000
|
def clear
|
997
1001
|
check_read_only_flag
|
998
|
-
@contents =
|
999
|
-
if binary? || !STRING_HAS_BYTE_BASED_METHODS
|
1000
|
-
@contents.force_encoding(Encoding::ASCII_8BIT)
|
1001
|
-
end
|
1002
|
+
@contents = String.new
|
1002
1003
|
@point = @gap_start = @gap_end = 0
|
1003
1004
|
@marks.each do |m|
|
1004
1005
|
m.location = 0
|
@@ -1158,7 +1159,7 @@ module Textbringer
|
|
1158
1159
|
byteindex(true, r, @point) == @point
|
1159
1160
|
end
|
1160
1161
|
|
1161
|
-
if
|
1162
|
+
if HAS_BYTEINDEX
|
1162
1163
|
def byteindex(forward, re, pos)
|
1163
1164
|
@match_offsets = []
|
1164
1165
|
method = forward ? :byteindex : :byterindex
|
@@ -1480,9 +1481,7 @@ module Textbringer
|
|
1480
1481
|
else
|
1481
1482
|
@contents = s.encode(Encoding::UTF_8)
|
1482
1483
|
end
|
1483
|
-
|
1484
|
-
@contents.force_encoding(Encoding::ASCII_8BIT)
|
1485
|
-
end
|
1484
|
+
@contents.force_encoding(Encoding::ASCII_8BIT)
|
1486
1485
|
self.file_encoding = enc
|
1487
1486
|
begin
|
1488
1487
|
case @contents
|
@@ -1502,7 +1501,7 @@ module Textbringer
|
|
1502
1501
|
end
|
1503
1502
|
end
|
1504
1503
|
|
1505
|
-
if
|
1504
|
+
if HAS_BYTESPLICE
|
1506
1505
|
def splice_contents(*args)
|
1507
1506
|
@contents.bytesplice(*args)
|
1508
1507
|
end
|
@@ -1515,37 +1514,37 @@ module Textbringer
|
|
1515
1514
|
def adjust_gap(min_size = 0, pos = @point)
|
1516
1515
|
if @gap_start < pos
|
1517
1516
|
len = user_to_gap(pos) - @gap_end
|
1518
|
-
s = @contents.byteslice(@gap_end, len)
|
1519
1517
|
new_gap_start = @gap_start + len
|
1520
1518
|
new_gap_end = @gap_end + len
|
1521
1519
|
nul_filling_start = new_gap_start > @gap_end ? new_gap_start : @gap_end
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1520
|
+
if BYTESPLICE_SUPPORTS_PARTIAL_COPY
|
1521
|
+
@contents.bytesplice(@gap_start, len,
|
1522
|
+
@contents, @gap_end, len)
|
1523
|
+
@contents.bytesplice(nul_filling_start...new_gap_end,
|
1524
|
+
"\0" * (new_gap_end - nul_filling_start))
|
1525
|
+
else
|
1526
|
+
s = @contents.byteslice(@gap_end, len)
|
1527
|
+
splice_contents(nul_filling_start...new_gap_end,
|
1528
|
+
"\0" * (new_gap_end - nul_filling_start))
|
1529
|
+
splice_contents(@gap_start...new_gap_start, s)
|
1528
1530
|
end
|
1529
|
-
splice_contents(nul_filling_start...new_gap_end,
|
1530
|
-
"\0" * (new_gap_end - nul_filling_start))
|
1531
|
-
splice_contents(@gap_start...new_gap_start, s)
|
1532
1531
|
@gap_start = new_gap_start
|
1533
1532
|
@gap_end = new_gap_end
|
1534
1533
|
elsif @gap_start > pos
|
1535
1534
|
len = @gap_start - pos
|
1536
|
-
s = @contents.byteslice(pos, len)
|
1537
1535
|
new_gap_start = @gap_start - len
|
1538
1536
|
new_gap_end = @gap_end - len
|
1539
1537
|
nul_filling_end = new_gap_end < @gap_start ? new_gap_end : @gap_start
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1538
|
+
if BYTESPLICE_SUPPORTS_PARTIAL_COPY
|
1539
|
+
@contents.bytesplice(new_gap_end, len,
|
1540
|
+
@contents, pos, len)
|
1541
|
+
@contents.bytesplice(pos...nul_filling_end,
|
1542
|
+
"\0" * (nul_filling_end - pos))
|
1543
|
+
else
|
1544
|
+
s = @contents.byteslice(pos, len)
|
1545
|
+
splice_contents(pos...nul_filling_end, "\0" * (nul_filling_end - pos))
|
1546
|
+
splice_contents(new_gap_end...@gap_end, s)
|
1546
1547
|
end
|
1547
|
-
splice_contents(pos...nul_filling_end, "\0" * (nul_filling_end - pos))
|
1548
|
-
splice_contents(new_gap_end...@gap_end, s)
|
1549
1548
|
@gap_start = new_gap_start
|
1550
1549
|
@gap_end = new_gap_end
|
1551
1550
|
end
|
@@ -1619,7 +1618,7 @@ module Textbringer
|
|
1619
1618
|
@current_column += substring(pos, new_pos).size
|
1620
1619
|
else
|
1621
1620
|
@current_line += n
|
1622
|
-
if
|
1621
|
+
if HAS_BYTEINDEX
|
1623
1622
|
begin
|
1624
1623
|
i = @contents.byterindex("\n", user_to_gap(get_pos(new_pos, -1)))
|
1625
1624
|
rescue RangeError
|
@@ -1641,7 +1640,7 @@ module Textbringer
|
|
1641
1640
|
@current_column -= substring(new_pos, pos).size
|
1642
1641
|
else
|
1643
1642
|
@current_line -= n
|
1644
|
-
if
|
1643
|
+
if HAS_BYTEINDEX
|
1645
1644
|
begin
|
1646
1645
|
i = @contents.byterindex("\n", user_to_gap(get_pos(new_pos, - 1)))
|
1647
1646
|
rescue RangeError
|
@@ -1687,9 +1686,7 @@ module Textbringer
|
|
1687
1686
|
@contents.byteslice(0...@gap_start),
|
1688
1687
|
@contents.byteslice(@gap_end..-1)
|
1689
1688
|
].each do |s|
|
1690
|
-
|
1691
|
-
s.force_encoding(Encoding::UTF_8) unless @binary
|
1692
|
-
end
|
1689
|
+
s.force_encoding(Encoding::UTF_8) unless @binary
|
1693
1690
|
case @file_format
|
1694
1691
|
when :dos
|
1695
1692
|
s.gsub!(/\n/, "\r\n")
|
@@ -7,8 +7,7 @@ require "clipboard"
|
|
7
7
|
module Textbringer
|
8
8
|
module Commands
|
9
9
|
CLIPBOARD_AVAILABLE =
|
10
|
-
Clipboard.implementation.name != "Clipboard::
|
11
|
-
(ENV["DISPLAY"] && system("which xclip > /dev/null 2>&1"))
|
10
|
+
Clipboard.implementation.name != "Clipboard::File"
|
12
11
|
|
13
12
|
if CLIPBOARD_AVAILABLE
|
14
13
|
GLOBAL_MAP.define_key("\M-w", :clipboard_copy_region)
|
@@ -143,5 +143,20 @@ module Textbringer
|
|
143
143
|
File.dirname(Buffer.current.file_name))|
|
144
144
|
FileUtils.mkdir_p(dir_name)
|
145
145
|
end
|
146
|
+
|
147
|
+
define_command(:find_source, doc: "Open the source file.") do
|
148
|
+
|obj = read_object("Object or Class: ")|
|
149
|
+
klass = obj.is_a?(Module) ? obj : obj.class
|
150
|
+
method_name = klass.instance_methods(false).first
|
151
|
+
if method_name.nil?
|
152
|
+
raise EditorError, "Source not found"
|
153
|
+
end
|
154
|
+
method = klass.instance_method(method_name)
|
155
|
+
file_name = method.source_location.first
|
156
|
+
if !File.exist?(file_name)
|
157
|
+
raise EditorError, "Source not found"
|
158
|
+
end
|
159
|
+
find_file(method.source_location.first)
|
160
|
+
end
|
146
161
|
end
|
147
162
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
$".push("readline.rb")
|
2
|
+
require "rdoc/ri/driver"
|
3
|
+
|
1
4
|
module Textbringer
|
2
5
|
module Commands
|
3
6
|
HELP_RING = Ring.new
|
@@ -38,7 +41,7 @@ module Textbringer
|
|
38
41
|
define_command(:describe_bindings,
|
39
42
|
doc: "Display the key bindings.") do
|
40
43
|
show_help do |help|
|
41
|
-
s = ""
|
44
|
+
s = +""
|
42
45
|
if Controller.current.overriding_map
|
43
46
|
s << "Overriding Bindings:\n"
|
44
47
|
s << keymap_bindings(Controller.current.overriding_map)
|
@@ -102,6 +105,44 @@ module Textbringer
|
|
102
105
|
push_help_command([:describe_key, key])
|
103
106
|
end
|
104
107
|
|
108
|
+
define_command(:describe_class,
|
109
|
+
doc: "Display the documentation of the class.") do
|
110
|
+
|name = read_expression("Describe class: ")|
|
111
|
+
show_help do |help|
|
112
|
+
old_stdout = $stdout
|
113
|
+
$stdout = StringIO.new
|
114
|
+
begin
|
115
|
+
rdoc = RDoc::RI::Driver.new(use_stdout: true,
|
116
|
+
formatter: RDoc::Markup::ToRdoc,
|
117
|
+
interactive: false)
|
118
|
+
rdoc.display_class(name)
|
119
|
+
help.insert($stdout.string)
|
120
|
+
ensure
|
121
|
+
$stdout = old_stdout
|
122
|
+
end
|
123
|
+
end
|
124
|
+
push_help_command([:describe_class, name])
|
125
|
+
end
|
126
|
+
|
127
|
+
define_command(:describe_method,
|
128
|
+
doc: "Display the documentation of the method.") do
|
129
|
+
|name = read_expression("Describe method: ")|
|
130
|
+
show_help do |help|
|
131
|
+
old_stdout = $stdout
|
132
|
+
$stdout = StringIO.new
|
133
|
+
begin
|
134
|
+
rdoc = RDoc::RI::Driver.new(use_stdout: true,
|
135
|
+
formatter: RDoc::Markup::ToRdoc,
|
136
|
+
interactive: false)
|
137
|
+
rdoc.display_method(name)
|
138
|
+
help.insert($stdout.string)
|
139
|
+
ensure
|
140
|
+
$stdout = old_stdout
|
141
|
+
end
|
142
|
+
end
|
143
|
+
push_help_command([:describe_method, name])
|
144
|
+
end
|
145
|
+
|
105
146
|
define_command(:help_go_back, doc: "Go back to the previous help.") do
|
106
147
|
if !HELP_RING.empty?
|
107
148
|
HELP_RING.rotate(1)
|
@@ -32,7 +32,7 @@ module Textbringer
|
|
32
32
|
end
|
33
33
|
|
34
34
|
define_command(:eval_expression) do
|
35
|
-
|s =
|
35
|
+
|s = read_expression("Eval: ")|
|
36
36
|
result = eval(s, TOPLEVEL_BINDING, "(eval_expression)", 1)
|
37
37
|
message(result.inspect)
|
38
38
|
result
|
@@ -243,6 +243,9 @@ module Textbringer
|
|
243
243
|
mark = yield(global_mark_ring)
|
244
244
|
end
|
245
245
|
if mark.detached?
|
246
|
+
unless mark.file_name
|
247
|
+
raise EditorError, "The buffer has gone"
|
248
|
+
end
|
246
249
|
find_file(mark.file_name)
|
247
250
|
goto_char(mark.location)
|
248
251
|
else
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
module Textbringer
|
4
|
+
class DefaultOutput
|
5
|
+
def write(*args)
|
6
|
+
Buffer.current.insert(args.join)
|
7
|
+
end
|
8
|
+
|
9
|
+
def flush
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(mid, ...)
|
13
|
+
buffer = StringIO.new
|
14
|
+
buffer.send(mid, ...)
|
15
|
+
write(buffer.string)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -57,7 +57,11 @@ module Textbringer
|
|
57
57
|
end
|
58
58
|
if key_index.nil?
|
59
59
|
@prev_key_index = nil
|
60
|
-
|
60
|
+
if event.is_a?(String) && /\A[A-Z]\z/.match?(event)
|
61
|
+
return event.downcase
|
62
|
+
else
|
63
|
+
return event
|
64
|
+
end
|
61
65
|
end
|
62
66
|
if @prev_key_index.nil?
|
63
67
|
@prev_key_index = key_index
|
data/lib/textbringer/keymap.rb
CHANGED
@@ -153,6 +153,7 @@ module Textbringer
|
|
153
153
|
GLOBAL_MAP.define_key(?\C-_, :undo)
|
154
154
|
GLOBAL_MAP.define_key("\C-xu", :undo)
|
155
155
|
GLOBAL_MAP.define_key("\C-x\C-_", :redo_command)
|
156
|
+
GLOBAL_MAP.define_key("\M-\C-_", :redo_command)
|
156
157
|
GLOBAL_MAP.define_key("\C-t", :transpose_chars)
|
157
158
|
GLOBAL_MAP.define_key("\C-j", :newline)
|
158
159
|
GLOBAL_MAP.define_key("\C-m", :newline)
|
@@ -210,6 +211,8 @@ module Textbringer
|
|
210
211
|
GLOBAL_MAP.define_key([:f1, "b"], :describe_bindings)
|
211
212
|
GLOBAL_MAP.define_key([:f1, "f"], :describe_command)
|
212
213
|
GLOBAL_MAP.define_key([:f1, "k"], :describe_key)
|
214
|
+
GLOBAL_MAP.define_key([:f1, "c"], :describe_class)
|
215
|
+
GLOBAL_MAP.define_key([:f1, "m"], :describe_method)
|
213
216
|
GLOBAL_MAP.define_key("\C-x#", :server_edit_done)
|
214
217
|
GLOBAL_MAP.define_key("\C-\\", :toggle_input_method)
|
215
218
|
GLOBAL_MAP.handle_undefined_key do |key|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Textbringer
|
2
2
|
CONFIG[:c_indent_level] = 4
|
3
|
-
CONFIG[:c_indent_tabs_mode] =
|
3
|
+
CONFIG[:c_indent_tabs_mode] = false
|
4
4
|
CONFIG[:c_continued_statement_offset] = 4
|
5
5
|
CONFIG[:c_case_label_offset] = -4
|
6
6
|
CONFIG[:c_label_offset] = -2
|
@@ -187,7 +187,7 @@ module Textbringer
|
|
187
187
|
if text.empty?
|
188
188
|
raise EditorError, "Empty token: (#{line},#{column}) #{$~.inspect}"
|
189
189
|
end
|
190
|
-
tokens.push([[line, column], token_name, text])
|
190
|
+
tokens.push([[line, column], token_name, CANONICAL_PUNCTUATORS[text]])
|
191
191
|
lf_count = text.count("\n")
|
192
192
|
if lf_count > 0
|
193
193
|
line += lf_count
|
data/lib/textbringer/utils.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require "rbconfig"
|
2
|
+
require "irb"
|
3
|
+
require "irb/completion"
|
2
4
|
|
3
5
|
module Textbringer
|
4
6
|
module Utils
|
@@ -323,6 +325,31 @@ module Textbringer
|
|
323
325
|
end
|
324
326
|
end
|
325
327
|
|
328
|
+
if defined?(IRB::RegexpCompletor)
|
329
|
+
EXPRESSION_COMPLETOR = IRB::RegexpCompletor.new
|
330
|
+
EXPRESSION_COMPLETOR_OPTIONS = {
|
331
|
+
bind: TOPLEVEL_BINDING,
|
332
|
+
doc_namespace: false
|
333
|
+
}
|
334
|
+
else
|
335
|
+
EXPRESSION_COMPLETOR = IRB::InputCompletor
|
336
|
+
EXPRESSION_COMPLETOR_OPTIONS = {
|
337
|
+
bind: TOPLEVEL_BINDING
|
338
|
+
}
|
339
|
+
end
|
340
|
+
|
341
|
+
def read_expression(prompt = "Expression: ")
|
342
|
+
f = ->(s) {
|
343
|
+
EXPRESSION_COMPLETOR.retrieve_completion_data(s, **EXPRESSION_COMPLETOR_OPTIONS).compact
|
344
|
+
}
|
345
|
+
read_from_minibuffer(prompt, completion_proc: f)
|
346
|
+
end
|
347
|
+
|
348
|
+
def read_object(prompt = "Object: ")
|
349
|
+
s = read_expression(prompt)
|
350
|
+
eval(s)
|
351
|
+
end
|
352
|
+
|
326
353
|
HOOKS = Hash.new { |h, k| h[k] = [] }
|
327
354
|
|
328
355
|
def add_hook(name, func = nil, &block)
|
data/lib/textbringer/version.rb
CHANGED
data/lib/textbringer/window.rb
CHANGED
data/lib/textbringer.rb
CHANGED
data/textbringer.gemspec
CHANGED
@@ -19,8 +19,10 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.required_ruby_version = '>=
|
22
|
+
spec.required_ruby_version = '>= 3.1'
|
23
23
|
|
24
|
+
spec.add_runtime_dependency "nkf"
|
25
|
+
spec.add_runtime_dependency "drb"
|
24
26
|
spec.add_runtime_dependency "curses", ">= 1.2.7"
|
25
27
|
spec.add_runtime_dependency "unicode-display_width", ">= 1.1"
|
26
28
|
spec.add_runtime_dependency "clipboard", ">= 1.1"
|
metadata
CHANGED
@@ -1,15 +1,42 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: textbringer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2024-05-24 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: nkf
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: drb
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
13
40
|
- !ruby/object:Gem::Dependency
|
14
41
|
name: curses
|
15
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,6 +211,7 @@ files:
|
|
184
211
|
- CHANGES.md
|
185
212
|
- Gemfile
|
186
213
|
- LICENSE.txt
|
214
|
+
- README.ja.md
|
187
215
|
- README.md
|
188
216
|
- Rakefile
|
189
217
|
- bin/console
|
@@ -212,6 +240,7 @@ files:
|
|
212
240
|
- lib/textbringer/commands/windows.rb
|
213
241
|
- lib/textbringer/config.rb
|
214
242
|
- lib/textbringer/controller.rb
|
243
|
+
- lib/textbringer/default_output.rb
|
215
244
|
- lib/textbringer/errors.rb
|
216
245
|
- lib/textbringer/face.rb
|
217
246
|
- lib/textbringer/faces/basic.rb
|
@@ -243,7 +272,6 @@ homepage: https://github.com/shugo/textbringer
|
|
243
272
|
licenses:
|
244
273
|
- MIT
|
245
274
|
metadata: {}
|
246
|
-
post_install_message:
|
247
275
|
rdoc_options: []
|
248
276
|
require_paths:
|
249
277
|
- lib
|
@@ -251,15 +279,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
251
279
|
requirements:
|
252
280
|
- - ">="
|
253
281
|
- !ruby/object:Gem::Version
|
254
|
-
version: '
|
282
|
+
version: '3.1'
|
255
283
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
256
284
|
requirements:
|
257
285
|
- - ">="
|
258
286
|
- !ruby/object:Gem::Version
|
259
287
|
version: '0'
|
260
288
|
requirements: []
|
261
|
-
rubygems_version: 3.
|
262
|
-
signing_key:
|
289
|
+
rubygems_version: 3.6.0.dev
|
263
290
|
specification_version: 4
|
264
291
|
summary: An Emacs-like text editor
|
265
292
|
test_files: []
|