textbringer 1.0.1 → 1.1.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/ubuntu.yml +5 -9
- data/.github/workflows/windows.yml +4 -7
- data/CHANGES.md +47 -0
- data/README.md +0 -2
- data/bin/merge_mazegaki_dic +25 -0
- data/exe/textbringer +6 -5
- data/lib/textbringer.rb +4 -0
- data/lib/textbringer/buffer.rb +40 -3
- data/lib/textbringer/commands/clipboard.rb +3 -3
- data/lib/textbringer/commands/ctags.rb +3 -3
- data/lib/textbringer/commands/dabbrev.rb +1 -1
- data/lib/textbringer/commands/files.rb +18 -3
- data/lib/textbringer/commands/help.rb +25 -16
- data/lib/textbringer/commands/input_method.rb +18 -0
- data/lib/textbringer/commands/isearch.rb +23 -2
- data/lib/textbringer/commands/misc.rb +8 -3
- data/lib/textbringer/commands/windows.rb +8 -4
- data/lib/textbringer/config.rb +2 -1
- data/lib/textbringer/controller.rb +7 -1
- data/lib/textbringer/input_method.rb +63 -0
- data/lib/textbringer/input_methods/hiragana_input_method.rb +70 -0
- data/lib/textbringer/input_methods/t_code_input_method.rb +458 -0
- data/lib/textbringer/input_methods/t_code_input_method/tables.rb +64 -0
- data/lib/textbringer/keymap.rb +45 -26
- data/lib/textbringer/modes/backtrace_mode.rb +1 -1
- data/lib/textbringer/modes/buffer_list_mode.rb +1 -1
- data/lib/textbringer/modes/c_mode.rb +4 -0
- data/lib/textbringer/modes/completion_list_mode.rb +1 -1
- data/lib/textbringer/modes/help_mode.rb +1 -1
- data/lib/textbringer/modes/programming_mode.rb +24 -2
- data/lib/textbringer/modes/ruby_mode.rb +70 -12
- data/lib/textbringer/utils.rb +10 -5
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +25 -10
- data/textbringer.gemspec +4 -5
- metadata +19 -28
- data/.github/workflows/ruby-head.yml +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz: '
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0950d5d14bd72c43ec2270cbb545c0dc464b66662f85099ec5eed8dc3a5a56db'
|
4
|
+
data.tar.gz: 1874b1aa43e1ba76fc2d1c9c74f9351a3dc28a15b85c7d6a085cd8d5b669adb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63431a1a5a0425007e119cf3573b86d61d3a1df8cca7c8658ee5fd479201696adfeddb80c2a085fca1df89fa16862d47664a5f85ae31fd09fd4f36ce73058d71
|
7
|
+
data.tar.gz: 95876e6af0b0be40b684ac3b0703aaa08370b23647f50b4231d61bd5c8e9411ad0798107cc77f4cb67bcf06fbcbfdaaaf47074322933052783b29a7722d2ffed
|
@@ -3,15 +3,14 @@ name: ubuntu
|
|
3
3
|
on: [push]
|
4
4
|
|
5
5
|
jobs:
|
6
|
-
|
7
|
-
runs-on: ubuntu-latest
|
6
|
+
test:
|
8
7
|
strategy:
|
9
8
|
matrix:
|
10
|
-
ruby: [
|
9
|
+
ruby: [ head, 3.0, 2.7, 2.6 ]
|
10
|
+
runs-on: ubuntu-latest
|
11
11
|
steps:
|
12
|
-
- uses: actions/checkout@
|
13
|
-
-
|
14
|
-
uses: actions/setup-ruby@v1
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
- uses: ruby/setup-ruby@v1
|
15
14
|
with:
|
16
15
|
ruby-version: ${{ matrix.ruby }}
|
17
16
|
- name: Install dependencies
|
@@ -21,6 +20,3 @@ jobs:
|
|
21
20
|
bundle install
|
22
21
|
- name: Run test
|
23
22
|
run: xvfb-run bundle exec rake test
|
24
|
-
env:
|
25
|
-
UPLOAD_TO_CODECOV: 1
|
26
|
-
CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
|
@@ -7,16 +7,13 @@ jobs:
|
|
7
7
|
runs-on: windows-latest
|
8
8
|
strategy:
|
9
9
|
matrix:
|
10
|
-
ruby: [ '
|
10
|
+
ruby: [ 'mingw', 'mswin', '3.0', '2.7', '2.6' ]
|
11
11
|
steps:
|
12
|
-
- uses: actions/checkout@
|
12
|
+
- uses: actions/checkout@v2
|
13
13
|
- name: Set up Ruby
|
14
|
-
uses:
|
14
|
+
uses: ruby/setup-ruby@v1
|
15
15
|
with:
|
16
16
|
ruby-version: ${{ matrix.ruby }}
|
17
|
-
|
18
|
-
run: gem install bundler --no-document
|
19
|
-
- name: Install dependencies
|
20
|
-
run: bundle install
|
17
|
+
bundler-cache: true
|
21
18
|
- name: Run test
|
22
19
|
run: bundle exec rake test
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,50 @@
|
|
1
|
+
## 1.1.0
|
2
|
+
|
3
|
+
* Show unsaved buffers on exit
|
4
|
+
* Bug fixes.
|
5
|
+
|
6
|
+
## 1.0.9
|
7
|
+
|
8
|
+
* Remove mazegaki.dic and bushu.rev
|
9
|
+
|
10
|
+
## 1.0.8
|
11
|
+
|
12
|
+
* Updated mazegaki.dic
|
13
|
+
* Add licenses of dictionary data to LEGAL.txt
|
14
|
+
|
15
|
+
## 1.0.7
|
16
|
+
|
17
|
+
* Support endless method definitions in ruby-mode.
|
18
|
+
* Updated mazegaki.dic.
|
19
|
+
|
20
|
+
## 1.0.6
|
21
|
+
|
22
|
+
* Add the Hiragana input method.
|
23
|
+
* Add make_directory.
|
24
|
+
* Bug fixes.
|
25
|
+
|
26
|
+
## 1.0.5
|
27
|
+
|
28
|
+
* Support the Japanese input method T-Code.
|
29
|
+
|
30
|
+
## 1.0.4
|
31
|
+
|
32
|
+
* Support Ruby 3.0.
|
33
|
+
* Do not record backtrace of Quit (C-g).
|
34
|
+
|
35
|
+
## 1.0.3
|
36
|
+
|
37
|
+
* Fix indentation bugs.
|
38
|
+
* Fix a bug of fourground! when it is called in the main thread.
|
39
|
+
|
40
|
+
## 1.0.2
|
41
|
+
|
42
|
+
* Add isearch_quoted_insert.
|
43
|
+
* Use M- notation instead of ESC in define_key and help.
|
44
|
+
* Add indent_new_comment_line_command.
|
45
|
+
* Add find_alternate_file.
|
46
|
+
* Fix indentation bugs in the Ruby mode.
|
47
|
+
|
1
48
|
## 1.0.1
|
2
49
|
|
3
50
|
* Support pattern matching in the Ruby mode.
|
data/README.md
CHANGED
@@ -4,8 +4,6 @@
|
|
4
4
|
[![ubuntu](https://github.com/shugo/textbringer/workflows/ubuntu/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Aubuntu)
|
5
5
|
[![windows](https://github.com/shugo/textbringer/workflows/windows/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Awindows)
|
6
6
|
[![macos](https://github.com/shugo/textbringer/workflows/macos/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Amacos)
|
7
|
-
[![ruby-head](https://github.com/shugo/textbringer/workflows/ruby-head/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Aruby-head)
|
8
|
-
[![codecov](https://codecov.io/gh/shugo/textbringer/branch/master/graph/badge.svg)](https://codecov.io/gh/shugo/textbringer)
|
9
7
|
|
10
8
|
Textbringer is a member of a demon race that takes on the form of an Emacs-like
|
11
9
|
text editor.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Usage: merge_mazegaki_dic /path/to/mazegaki.dic /path/to/skkdic/SKK-JISYO.* > ~/.textbringer/tcode/mazegaki.dic
|
4
|
+
|
5
|
+
MAZEGAKI_DIC = Hash.new([])
|
6
|
+
|
7
|
+
ARGF.each_line do |line|
|
8
|
+
next if /^\p{ascii}/.match?(line)
|
9
|
+
x, y = line.split
|
10
|
+
key = x.sub(/\A(\p{hiragana}+)[a-z>]\z/, "\\1—")
|
11
|
+
values = y.split("/").map { |i|
|
12
|
+
i.sub(/;.*/, "")
|
13
|
+
}.reject { |i|
|
14
|
+
i.empty? || i == key
|
15
|
+
}
|
16
|
+
MAZEGAKI_DIC[key] |= values
|
17
|
+
end
|
18
|
+
|
19
|
+
MAZEGAKI_DIC.sort_by { |key,|
|
20
|
+
key
|
21
|
+
}.each do |key, values|
|
22
|
+
puts "#{key} /#{values.join('/')}/"
|
23
|
+
end
|
24
|
+
|
25
|
+
|
data/exe/textbringer
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
$VERBOSE = nil
|
4
|
+
|
3
5
|
require "textbringer"
|
4
6
|
|
5
7
|
include Textbringer
|
6
8
|
include Commands
|
7
9
|
|
8
|
-
def load_user_config
|
9
|
-
config_file = File.expand_path(
|
10
|
+
def load_user_config(path)
|
11
|
+
config_file = File.expand_path(path)
|
10
12
|
begin
|
11
13
|
load(config_file)
|
12
14
|
rescue LoadError
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
$VERBOSE = nil
|
17
|
-
|
18
18
|
unless STDIN.tty?
|
19
19
|
STDERR.puts("textbringer: standard input is not a tty")
|
20
20
|
exit 1
|
@@ -24,8 +24,9 @@ Controller.current = Controller.new
|
|
24
24
|
begin
|
25
25
|
Window.start do
|
26
26
|
begin
|
27
|
+
load_user_config("~/.textbringer/init.rb")
|
27
28
|
Plugin.load_plugins
|
28
|
-
load_user_config
|
29
|
+
load_user_config("~/.textbringer.rb")
|
29
30
|
ruby_mode
|
30
31
|
if ARGV.size > 0
|
31
32
|
ARGV.each do |arg|
|
data/lib/textbringer.rb
CHANGED
@@ -22,6 +22,7 @@
|
|
22
22
|
require_relative "textbringer/commands/keyboard_macro"
|
23
23
|
require_relative "textbringer/commands/fill"
|
24
24
|
require_relative "textbringer/commands/server"
|
25
|
+
require_relative "textbringer/commands/input_method"
|
25
26
|
require_relative "textbringer/commands/help"
|
26
27
|
require_relative "textbringer/mode"
|
27
28
|
require_relative "textbringer/modes/fundamental_mode"
|
@@ -32,5 +33,8 @@
|
|
32
33
|
require_relative "textbringer/modes/completion_list_mode"
|
33
34
|
require_relative "textbringer/modes/buffer_list_mode"
|
34
35
|
require_relative "textbringer/modes/help_mode"
|
36
|
+
require_relative "textbringer/input_method"
|
37
|
+
require_relative "textbringer/input_methods/t_code_input_method"
|
38
|
+
require_relative "textbringer/input_methods/hiragana_input_method"
|
35
39
|
require_relative "textbringer/plugin"
|
36
40
|
require_relative "textbringer/controller"
|
data/lib/textbringer/buffer.rb
CHANGED
@@ -11,6 +11,7 @@ class Buffer
|
|
11
11
|
attr_accessor :mode, :keymap
|
12
12
|
attr_reader :name, :file_name, :file_encoding, :file_format, :point, :marks
|
13
13
|
attr_reader :current_line, :current_column, :visible_mark
|
14
|
+
attr_reader :input_method
|
14
15
|
|
15
16
|
GAP_SIZE = 256
|
16
17
|
UNDO_LIMIT = 1000
|
@@ -240,6 +241,7 @@ def initialize(s = +"", name: nil,
|
|
240
241
|
@visible_mark = nil
|
241
242
|
@read_only = read_only
|
242
243
|
@callbacks = {}
|
244
|
+
@input_method = nil
|
243
245
|
end
|
244
246
|
|
245
247
|
def inspect
|
@@ -410,6 +412,7 @@ def save(file_name = @file_name)
|
|
410
412
|
f.flock(File::LOCK_EX)
|
411
413
|
write_to_file(f)
|
412
414
|
f.flush
|
415
|
+
f.fsync
|
413
416
|
end
|
414
417
|
@file_mtime = File.mtime(file_name)
|
415
418
|
rescue Errno::EISDIR
|
@@ -1384,6 +1387,35 @@ def insert_final_newline
|
|
1384
1387
|
end
|
1385
1388
|
end
|
1386
1389
|
|
1390
|
+
def toggle_input_method(name)
|
1391
|
+
if name.nil?
|
1392
|
+
@input_method ||= InputMethod.find(CONFIG[:default_input_method])
|
1393
|
+
else
|
1394
|
+
@input_method = InputMethod.find(name)
|
1395
|
+
end
|
1396
|
+
@input_method.toggle
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
def disable_input_method
|
1400
|
+
@input_method&.disable
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
def filter_event(event)
|
1404
|
+
if @input_method
|
1405
|
+
@input_method.filter_event(event)
|
1406
|
+
else
|
1407
|
+
event
|
1408
|
+
end
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
def input_method_status
|
1412
|
+
if @input_method&.enabled?
|
1413
|
+
@input_method.status
|
1414
|
+
else
|
1415
|
+
"--"
|
1416
|
+
end
|
1417
|
+
end
|
1418
|
+
|
1387
1419
|
private
|
1388
1420
|
|
1389
1421
|
def set_contents(s, enc)
|
@@ -1555,15 +1587,15 @@ def write_to_file(f)
|
|
1555
1587
|
|
1556
1588
|
def push_undo(action)
|
1557
1589
|
return if @undoing || @undo_limit == 0
|
1590
|
+
if !modified?
|
1591
|
+
action.version = @version
|
1592
|
+
end
|
1558
1593
|
if @composite_edit_level > 0
|
1559
1594
|
@composite_edit_actions.push(action)
|
1560
1595
|
else
|
1561
1596
|
if @undo_stack.size >= @undo_limit
|
1562
1597
|
@undo_stack[0, @undo_stack.size + 1 - @undo_limit] = []
|
1563
1598
|
end
|
1564
|
-
if !modified?
|
1565
|
-
action.version = @version
|
1566
|
-
end
|
1567
1599
|
@undo_stack.push(action)
|
1568
1600
|
@redo_stack.clear
|
1569
1601
|
end
|
@@ -1649,6 +1681,7 @@ class InsertAction < UndoableAction
|
|
1649
1681
|
def initialize(buffer, location, string)
|
1650
1682
|
super(buffer, location)
|
1651
1683
|
@string = string
|
1684
|
+
@copied = false
|
1652
1685
|
end
|
1653
1686
|
|
1654
1687
|
def undo
|
@@ -1662,6 +1695,10 @@ def redo
|
|
1662
1695
|
end
|
1663
1696
|
|
1664
1697
|
def merge(s)
|
1698
|
+
unless @copied
|
1699
|
+
@string = @string.dup
|
1700
|
+
@copied = true
|
1701
|
+
end
|
1665
1702
|
@string.concat(s)
|
1666
1703
|
end
|
1667
1704
|
end
|
@@ -11,12 +11,12 @@ module Commands
|
|
11
11
|
(ENV["DISPLAY"] && system("which xclip > /dev/null 2>&1"))
|
12
12
|
|
13
13
|
if CLIPBOARD_AVAILABLE
|
14
|
-
GLOBAL_MAP.define_key("\
|
14
|
+
GLOBAL_MAP.define_key("\M-w", :clipboard_copy_region)
|
15
15
|
GLOBAL_MAP.define_key("\C-w", :clipboard_kill_region)
|
16
16
|
GLOBAL_MAP.define_key(?\C-k, :clipboard_kill_line)
|
17
|
-
GLOBAL_MAP.define_key("\
|
17
|
+
GLOBAL_MAP.define_key("\M-d", :clipboard_kill_word)
|
18
18
|
GLOBAL_MAP.define_key("\C-y", :clipboard_yank)
|
19
|
-
GLOBAL_MAP.define_key("\
|
19
|
+
GLOBAL_MAP.define_key("\M-y", :clipboard_yank_pop)
|
20
20
|
end
|
21
21
|
|
22
22
|
define_command(:clipboard_copy_region, doc: <<~EOD) do
|
@@ -49,12 +49,12 @@ module Commands
|
|
49
49
|
when /\A\d+\z/
|
50
50
|
push_tag_mark_and_find_file(file)
|
51
51
|
goto_line(addr.to_i)
|
52
|
-
when %r'\A/\^(
|
52
|
+
when %r'\A/\^(.*?)(\$)?/\z'
|
53
|
+
re = "^" + Regexp.quote($1.gsub(/\\([\\\/])/, "\\1")) + $2.to_s
|
53
54
|
push_tag_mark_and_find_file(file)
|
54
55
|
beginning_of_buffer
|
55
56
|
n.times do
|
56
|
-
|
57
|
-
re_search_forward("^" + s + "$")
|
57
|
+
re_search_forward(re)
|
58
58
|
end
|
59
59
|
beginning_of_line
|
60
60
|
when %r'\A\?\^(.*)\$\?\z'
|
@@ -91,7 +91,7 @@ def dabbrev_regexp(stem, candidates)
|
|
91
91
|
using DabbrevExtension
|
92
92
|
|
93
93
|
module Commands
|
94
|
-
GLOBAL_MAP.define_key("\
|
94
|
+
GLOBAL_MAP.define_key("\M-/", :dabbrev_expand_command)
|
95
95
|
|
96
96
|
define_command(:dabbrev_expand_command) do
|
97
97
|
contd = Controller.current.last_command == :dabbrev_expand_command
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require "editorconfig"
|
2
|
+
require "fileutils"
|
2
3
|
|
3
4
|
module Textbringer
|
4
5
|
module Commands
|
5
6
|
define_command(:find_file, doc: "Open or create a file.") do
|
6
|
-
|file_name = read_file_name("Find file: ")|
|
7
|
+
|file_name = read_file_name("Find file: ", default: (Buffer.current.file_name ? File.dirname(Buffer.current.file_name) : Dir.pwd) + "/")|
|
7
8
|
config = EditorConfig.load_file(file_name)
|
8
9
|
buffer = Buffer.find_file(file_name)
|
9
10
|
if buffer.new_file?
|
@@ -86,12 +87,13 @@ module Commands
|
|
86
87
|
|
87
88
|
define_command(:write_file,
|
88
89
|
doc: "Save the current buffer as the specified file.") do
|
89
|
-
|file_name = read_file_name("Write file: "
|
90
|
+
|file_name = read_file_name("Write file: ",
|
91
|
+
default: Buffer.current.file_name)|
|
90
92
|
if File.directory?(file_name)
|
91
93
|
file_name = File.expand_path(Buffer.current.name, file_name)
|
92
94
|
end
|
93
95
|
if File.exist?(file_name)
|
94
|
-
unless y_or_n?("File
|
96
|
+
unless y_or_n?("File exists; overwrite?")
|
95
97
|
message("Cancelled")
|
96
98
|
next
|
97
99
|
end
|
@@ -124,5 +126,18 @@ module Commands
|
|
124
126
|
File.dirname(Buffer.current.file_name))|
|
125
127
|
Dir.chdir(dir_name)
|
126
128
|
end
|
129
|
+
|
130
|
+
define_command(:find_alternate_file, doc: "Find an alternate file.") do
|
131
|
+
|file_name = read_file_name("Find alternate file: ",
|
132
|
+
default: Buffer.current.file_name)|
|
133
|
+
find_file(file_name)
|
134
|
+
end
|
135
|
+
|
136
|
+
define_command(:make_directory, doc: "Create a new directory.") do
|
137
|
+
|dir_name = read_file_name("Make directory: ",
|
138
|
+
default: Buffer.current.file_name &&
|
139
|
+
File.dirname(Buffer.current.file_name))|
|
140
|
+
FileUtils.mkdir_p(dir_name)
|
141
|
+
end
|
127
142
|
end
|
128
143
|
end
|
@@ -21,27 +21,36 @@ def show_help
|
|
21
21
|
end
|
22
22
|
private :show_help
|
23
23
|
|
24
|
+
def keymap_bindings(keymap)
|
25
|
+
s = format("%-16s %s\n", "Key", "Binding")
|
26
|
+
s << format("%-16s %s\n", "---", "-------")
|
27
|
+
s << "\n"
|
28
|
+
keymap.each do |key_sequence, command|
|
29
|
+
if command != :self_insert
|
30
|
+
s << format("%-16s [%s]\n",
|
31
|
+
Keymap.key_sequence_string(key_sequence),
|
32
|
+
command)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
s
|
36
|
+
end
|
37
|
+
|
24
38
|
define_command(:describe_bindings,
|
25
39
|
doc: "Display the key bindings.") do
|
26
40
|
show_help do |help|
|
27
|
-
s =
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
GLOBAL_MAP,
|
33
|
-
Buffer.current.keymap,
|
34
|
-
Controller.current.overriding_map
|
35
|
-
].each do |map|
|
36
|
-
map&.each do |key_sequence, command|
|
37
|
-
bindings[key_sequence] = command
|
38
|
-
end
|
41
|
+
s = ""
|
42
|
+
if Controller.current.overriding_map
|
43
|
+
s << "Overriding Bindings:\n"
|
44
|
+
s << keymap_bindings(Controller.current.overriding_map)
|
45
|
+
s << "\n"
|
39
46
|
end
|
40
|
-
|
41
|
-
s <<
|
42
|
-
|
43
|
-
|
47
|
+
if Buffer.current.keymap
|
48
|
+
s << "Current Buffer Bindings:\n"
|
49
|
+
s << keymap_bindings(Buffer.current.keymap)
|
50
|
+
s << "\n"
|
44
51
|
end
|
52
|
+
s << "Global Bindings:\n"
|
53
|
+
s << keymap_bindings(GLOBAL_MAP)
|
45
54
|
help.insert(s)
|
46
55
|
end
|
47
56
|
push_help_command([:describe_bindings])
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Textbringer
|
2
|
+
module Commands
|
3
|
+
define_command(:toggle_input_method,
|
4
|
+
doc: "Toggel input method.") do |name = nil|
|
5
|
+
if name.nil? && current_prefix_arg
|
6
|
+
name = read_input_method_name("Input method: ")
|
7
|
+
end
|
8
|
+
Buffer.current.toggle_input_method(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def read_input_method_name(prompt, default: CONFIG[:default_input_method])
|
12
|
+
f = ->(s) {
|
13
|
+
complete_for_minibuffer(s.tr("-", "_"), InputMethod.list)
|
14
|
+
}
|
15
|
+
read_from_minibuffer(prompt, completion_proc: f, default: default)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|