diakonos 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +172 -0
- data/README +75 -0
- data/bin/diakonos +6 -0
- data/diakonos.conf +960 -0
- data/home-on-save.rb +6 -0
- data/lib/diakonos.rb +2397 -0
- data/lib/diakonos/array.rb +10 -0
- data/lib/diakonos/bignum.rb +6 -0
- data/lib/diakonos/bookmark.rb +51 -0
- data/lib/diakonos/buffer-hash.rb +18 -0
- data/lib/diakonos/buffer.rb +1700 -0
- data/lib/diakonos/clipboard.rb +47 -0
- data/lib/diakonos/ctag.rb +28 -0
- data/lib/diakonos/enumerable.rb +15 -0
- data/lib/diakonos/finding.rb +32 -0
- data/lib/diakonos/fixnum.rb +13 -0
- data/lib/diakonos/hash.rb +101 -0
- data/lib/diakonos/keycode.rb +110 -0
- data/lib/diakonos/object.rb +6 -0
- data/lib/diakonos/readline.rb +192 -0
- data/lib/diakonos/regexp.rb +6 -0
- data/lib/diakonos/sized-array.rb +48 -0
- data/lib/diakonos/string.rb +318 -0
- data/lib/diakonos/text-mark.rb +19 -0
- data/lib/diakonos/window.rb +32 -0
- data/test/buffer-test.rb +37 -0
- data/test/clipboard-test.rb +70 -0
- data/test/diakonos-test.rb +14 -0
- data/test/hash-test.rb +404 -0
- data/test/regexp-test.rb +26 -0
- data/test/sizedarray-test.rb +113 -0
- data/test/string-test.rb +160 -0
- metadata +77 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Diakonos
|
2
|
+
|
3
|
+
class Clipboard
|
4
|
+
def initialize( max_clips )
|
5
|
+
@clips = Array.new
|
6
|
+
@max_clips = max_clips
|
7
|
+
end
|
8
|
+
|
9
|
+
def [] ( arg )
|
10
|
+
return @clips[ arg ]
|
11
|
+
end
|
12
|
+
|
13
|
+
def clip
|
14
|
+
return @clips[ 0 ]
|
15
|
+
end
|
16
|
+
|
17
|
+
# text is an array of Strings
|
18
|
+
# Returns true iff a clip was added,
|
19
|
+
# and only non-nil text can be added.
|
20
|
+
def addClip( text )
|
21
|
+
return false if text == nil
|
22
|
+
@clips.unshift text
|
23
|
+
@clips.pop if @clips.length > @max_clips
|
24
|
+
return true
|
25
|
+
end
|
26
|
+
|
27
|
+
def each
|
28
|
+
@clips.each do |clip|
|
29
|
+
yield clip
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# text is an array of Strings (lines)
|
34
|
+
# Appends the lines to the current clip.
|
35
|
+
# If no current clip, then a new clip is created.
|
36
|
+
# Returns true iff the text was successfully appended.
|
37
|
+
def appendToClip( text )
|
38
|
+
return false if text.nil?
|
39
|
+
return addClip( text ) if @clips.length == 0
|
40
|
+
last_clip = @clips[ 0 ]
|
41
|
+
last_clip.pop if last_clip[ -1 ] == ""
|
42
|
+
@clips[ 0 ] = last_clip + text
|
43
|
+
return true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Diakonos
|
2
|
+
|
3
|
+
class CTag
|
4
|
+
attr_reader :file, :command, :kind, :rest
|
5
|
+
|
6
|
+
def initialize( file, command, kind, rest )
|
7
|
+
@file = file
|
8
|
+
@command = command
|
9
|
+
@kind = kind
|
10
|
+
@rest = rest
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
return "#{@file}:#{@command} (#{@kind}) #{@rest}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def == ( other )
|
18
|
+
return (
|
19
|
+
other != nil and
|
20
|
+
@file == other.file and
|
21
|
+
@command == other.command and
|
22
|
+
@kind == other.kind and
|
23
|
+
@rest == other.rest
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Enumerable
|
2
|
+
# Returns [array-index, string-index, string-index] triples for each match.
|
3
|
+
def grep_indices( regexp )
|
4
|
+
array = Array.new
|
5
|
+
each_with_index do |element,index|
|
6
|
+
element.scan( regexp ) do |match_text|
|
7
|
+
match = Regexp.last_match
|
8
|
+
strindex = match.begin( 0 )
|
9
|
+
array.push [ index, strindex, strindex + match_text.length ]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
return array
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Diakonos
|
2
|
+
|
3
|
+
class Finding
|
4
|
+
attr_reader :start_row, :start_col, :end_row, :end_col
|
5
|
+
attr_writer :end_row, :end_col
|
6
|
+
|
7
|
+
def initialize( start_row, start_col, end_row, end_col )
|
8
|
+
@start_row = start_row
|
9
|
+
@start_col = start_col
|
10
|
+
@end_row = end_row
|
11
|
+
@end_col = end_col
|
12
|
+
end
|
13
|
+
|
14
|
+
def match( regexps, lines )
|
15
|
+
retval = true
|
16
|
+
|
17
|
+
i = @start_row + 1
|
18
|
+
regexps[ 1..-1 ].each do |re|
|
19
|
+
if lines[ i ] !~ re
|
20
|
+
retval = false
|
21
|
+
break
|
22
|
+
end
|
23
|
+
@end_row = i
|
24
|
+
@end_col = Regexp.last_match[ 0 ].length
|
25
|
+
i += 1
|
26
|
+
end
|
27
|
+
|
28
|
+
return retval
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
class Hash
|
2
|
+
# path is an array of hash keys
|
3
|
+
# This method deletes a path of hash keys, with each step in the path
|
4
|
+
# being a recursively deeper key in a hash tree.
|
5
|
+
# Returns the possibly modified hash.
|
6
|
+
def deleteKeyPath( path )
|
7
|
+
if path.length > 1
|
8
|
+
subtree = self[ path[ 0 ] ]
|
9
|
+
if subtree.respond_to?( :deleteKeyPath )
|
10
|
+
subtree.deleteKeyPath( path[ 1..-1 ] )
|
11
|
+
if subtree.empty?
|
12
|
+
delete( path[ 0 ] )
|
13
|
+
end
|
14
|
+
end
|
15
|
+
elsif path.length == 1
|
16
|
+
delete( path[ 0 ] )
|
17
|
+
end
|
18
|
+
|
19
|
+
return self
|
20
|
+
end
|
21
|
+
|
22
|
+
def setKeyPath( path, leaf )
|
23
|
+
if path.length > 1
|
24
|
+
node = self[ path[ 0 ] ]
|
25
|
+
if not node.respond_to?( :setKeyPath )
|
26
|
+
node = self[ path[ 0 ] ] = Hash.new
|
27
|
+
end
|
28
|
+
node.setKeyPath( path[ 1..-1 ], leaf )
|
29
|
+
elsif path.length == 1
|
30
|
+
self[ path[ 0 ] ] = leaf
|
31
|
+
end
|
32
|
+
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def getNode( path )
|
37
|
+
node = self[ path[ 0 ] ]
|
38
|
+
if path.length > 1
|
39
|
+
if node != nil and node.respond_to?( :getNode )
|
40
|
+
return node.getNode( path[ 1..-1 ] )
|
41
|
+
end
|
42
|
+
elsif path.length == 1
|
43
|
+
return node
|
44
|
+
end
|
45
|
+
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def getLeaf( path )
|
50
|
+
node = getNode( path )
|
51
|
+
if node.respond_to?( :getNode )
|
52
|
+
# Only want a leaf node
|
53
|
+
return nil
|
54
|
+
else
|
55
|
+
return node
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def leaves( _leaves = Set.new )
|
60
|
+
each_value do |value|
|
61
|
+
if value.respond_to?( :leaves )
|
62
|
+
_leaves.merge value.leaves( _leaves )
|
63
|
+
else
|
64
|
+
_leaves << value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
return _leaves
|
69
|
+
end
|
70
|
+
|
71
|
+
def paths_and_leaves( path_so_far = [], _paths_and_leaves = Set.new )
|
72
|
+
each do |key, value|
|
73
|
+
if value.respond_to?( :paths_and_leaves )
|
74
|
+
_paths_and_leaves.merge(
|
75
|
+
value.paths_and_leaves(
|
76
|
+
path_so_far + [ key ],
|
77
|
+
_paths_and_leaves
|
78
|
+
)
|
79
|
+
)
|
80
|
+
else
|
81
|
+
_paths_and_leaves << {
|
82
|
+
:path => path_so_far + [ key ],
|
83
|
+
:leaf => value
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
return _paths_and_leaves
|
89
|
+
end
|
90
|
+
|
91
|
+
def each_path_and_leaf( path_so_far = [] )
|
92
|
+
each do |key, value|
|
93
|
+
if value.respond_to?( :each_path_and_leaf )
|
94
|
+
value.each_path_and_leaf( path_so_far + [ key ] ) { |path, leaf| yield( path, leaf ) }
|
95
|
+
else
|
96
|
+
yield( path_so_far + [ key ], value )
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Diakonos
|
2
|
+
|
3
|
+
module KeyCode
|
4
|
+
KEYSTRINGS = [
|
5
|
+
"ctrl+space", # 0
|
6
|
+
"ctrl+a", # 1
|
7
|
+
"ctrl+b", # 2
|
8
|
+
"ctrl+c", # 3
|
9
|
+
"ctrl+d", # 4
|
10
|
+
"ctrl+e", # 5
|
11
|
+
"ctrl+f", # 6
|
12
|
+
"ctrl+g", # 7
|
13
|
+
nil, # 8
|
14
|
+
"tab", # 9
|
15
|
+
"ctrl+j", # 10
|
16
|
+
"ctrl+k", # 11
|
17
|
+
"ctrl+l", # 12
|
18
|
+
"enter", # 13
|
19
|
+
"ctrl+n", # 14
|
20
|
+
"ctrl+o", # 15
|
21
|
+
"ctrl+p", # 16
|
22
|
+
"ctrl+q", # 17
|
23
|
+
"ctrl+r", # 18
|
24
|
+
"ctrl+s", # 19
|
25
|
+
"ctrl+t", # 20
|
26
|
+
"ctrl+u", # 21
|
27
|
+
"ctrl+v", # 22
|
28
|
+
"ctrl+w", # 23
|
29
|
+
"ctrl+x", # 24
|
30
|
+
"ctrl+y", # 25
|
31
|
+
"ctrl+z", # 26
|
32
|
+
"esc", # 27
|
33
|
+
nil, # 28
|
34
|
+
nil, # 29
|
35
|
+
nil, # 30
|
36
|
+
nil, # 31
|
37
|
+
"space", # 32
|
38
|
+
33.chr, 34.chr, 35.chr, 36.chr, 37.chr, 38.chr, 39.chr,
|
39
|
+
40.chr, 41.chr, 42.chr, 43.chr, 44.chr, 45.chr, 46.chr, 47.chr, 48.chr, 49.chr,
|
40
|
+
50.chr, 51.chr, 52.chr, 53.chr, 54.chr, 55.chr, 56.chr, 57.chr, 58.chr, 59.chr,
|
41
|
+
60.chr, 61.chr, 62.chr, 63.chr, 64.chr, 65.chr, 66.chr, 67.chr, 68.chr, 69.chr,
|
42
|
+
70.chr, 71.chr, 72.chr, 73.chr, 74.chr, 75.chr, 76.chr, 77.chr, 78.chr, 79.chr,
|
43
|
+
80.chr, 81.chr, 82.chr, 83.chr, 84.chr, 85.chr, 86.chr, 87.chr, 88.chr, 89.chr,
|
44
|
+
90.chr, 91.chr, 92.chr, 93.chr, 94.chr, 95.chr, 96.chr, 97.chr, 98.chr, 99.chr,
|
45
|
+
100.chr, 101.chr, 102.chr, 103.chr, 104.chr, 105.chr, 106.chr, 107.chr, 108.chr, 109.chr,
|
46
|
+
110.chr, 111.chr, 112.chr, 113.chr, 114.chr, 115.chr, 116.chr, 117.chr, 118.chr, 119.chr,
|
47
|
+
120.chr, 121.chr, 122.chr, 123.chr, 124.chr, 125.chr, 126.chr,
|
48
|
+
"backspace" # 127
|
49
|
+
]
|
50
|
+
|
51
|
+
def keyString
|
52
|
+
if self.class == Fixnum
|
53
|
+
retval = KEYSTRINGS[ self ]
|
54
|
+
end
|
55
|
+
if retval == nil
|
56
|
+
case self
|
57
|
+
when Curses::KEY_DOWN
|
58
|
+
retval = "down"
|
59
|
+
when Curses::KEY_UP
|
60
|
+
retval = "up"
|
61
|
+
when Curses::KEY_LEFT
|
62
|
+
retval = "left"
|
63
|
+
when Curses::KEY_RIGHT
|
64
|
+
retval = "right"
|
65
|
+
when Curses::KEY_HOME
|
66
|
+
retval = "home"
|
67
|
+
when Curses::KEY_END
|
68
|
+
retval = "end"
|
69
|
+
when Curses::KEY_IC
|
70
|
+
retval = "insert"
|
71
|
+
when Curses::KEY_DC
|
72
|
+
retval = "delete"
|
73
|
+
when Curses::KEY_PPAGE
|
74
|
+
retval = "page-up"
|
75
|
+
when Curses::KEY_NPAGE
|
76
|
+
retval = "page-down"
|
77
|
+
when Curses::KEY_A1
|
78
|
+
retval = "numpad7"
|
79
|
+
when Curses::KEY_A3
|
80
|
+
retval = "numpad9"
|
81
|
+
when Curses::KEY_B2
|
82
|
+
retval = "numpad5"
|
83
|
+
when Curses::KEY_C1
|
84
|
+
retval = "numpad1"
|
85
|
+
when Curses::KEY_C3
|
86
|
+
retval = "numpad3"
|
87
|
+
when Curses::KEY_FIND
|
88
|
+
retval = "find"
|
89
|
+
when Curses::KEY_SELECT
|
90
|
+
retval = "select"
|
91
|
+
when Curses::KEY_SUSPEND
|
92
|
+
retval = "suspend"
|
93
|
+
when Curses::KEY_F0..(Curses::KEY_F0 + 24)
|
94
|
+
retval = "f" + (self - Curses::KEY_F0).to_s
|
95
|
+
when CTRL_H
|
96
|
+
retval = "ctrl+h"
|
97
|
+
when Curses::KEY_RESIZE
|
98
|
+
retval = "resize"
|
99
|
+
when RESIZE2
|
100
|
+
retval = "resize2"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
if retval == nil and self.class == Fixnum
|
104
|
+
retval = "keycode#{self}"
|
105
|
+
end
|
106
|
+
return retval
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Diakonos
|
2
|
+
|
3
|
+
class Readline
|
4
|
+
|
5
|
+
# completion_array is the array of strings that tab completion can use
|
6
|
+
def initialize( diakonos, window, initial_text = "", completion_array = nil, history = [] )
|
7
|
+
@window = window
|
8
|
+
@diakonos = diakonos
|
9
|
+
@initial_text = initial_text
|
10
|
+
@completion_array = completion_array
|
11
|
+
@list_filename = @diakonos.list_filename
|
12
|
+
|
13
|
+
@history = history
|
14
|
+
@history << initial_text
|
15
|
+
@history_index = @history.length - 1
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns nil on cancel.
|
19
|
+
def readline
|
20
|
+
@input = @initial_text
|
21
|
+
@icurx = @window.curx
|
22
|
+
@icury = @window.cury
|
23
|
+
@window.addstr @initial_text
|
24
|
+
@input_cursor = @initial_text.length
|
25
|
+
@opened_list_file = false
|
26
|
+
|
27
|
+
loop do
|
28
|
+
c = @window.getch
|
29
|
+
|
30
|
+
case c
|
31
|
+
when Curses::KEY_DC
|
32
|
+
if @input_cursor < @input.length
|
33
|
+
@window.delch
|
34
|
+
@input = @input[ 0...@input_cursor ] + @input[ (@input_cursor + 1)..-1 ]
|
35
|
+
end
|
36
|
+
when BACKSPACE, CTRL_H
|
37
|
+
# Curses::KEY_LEFT
|
38
|
+
if @input_cursor > 0
|
39
|
+
@input_cursor += -1
|
40
|
+
@window.setpos( @window.cury, @window.curx - 1 )
|
41
|
+
|
42
|
+
# Curses::KEY_DC
|
43
|
+
if @input_cursor < @input.length
|
44
|
+
@window.delch
|
45
|
+
@input = @input[ 0...@input_cursor ] + @input[ (@input_cursor + 1)..-1 ]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
when ENTER
|
49
|
+
break
|
50
|
+
when ESCAPE, CTRL_C, CTRL_D, CTRL_Q
|
51
|
+
@input = nil
|
52
|
+
break
|
53
|
+
when Curses::KEY_LEFT
|
54
|
+
if @input_cursor > 0
|
55
|
+
@input_cursor += -1
|
56
|
+
@window.setpos( @window.cury, @window.curx - 1 )
|
57
|
+
end
|
58
|
+
when Curses::KEY_RIGHT
|
59
|
+
if @input_cursor < @input.length
|
60
|
+
@input_cursor += 1
|
61
|
+
@window.setpos( @window.cury, @window.curx + 1 )
|
62
|
+
end
|
63
|
+
when Curses::KEY_HOME
|
64
|
+
@input_cursor = 0
|
65
|
+
@window.setpos( @icury, @icurx )
|
66
|
+
when Curses::KEY_END
|
67
|
+
@input_cursor = @input.length
|
68
|
+
@window.setpos( @window.cury, @icurx + @input.length )
|
69
|
+
when TAB
|
70
|
+
completeInput
|
71
|
+
when Curses::KEY_NPAGE
|
72
|
+
@diakonos.pageDown
|
73
|
+
when Curses::KEY_PPAGE
|
74
|
+
@diakonos.pageUp
|
75
|
+
when Curses::KEY_UP
|
76
|
+
if @history_index > 0
|
77
|
+
@history[ @history_index ] = @input
|
78
|
+
@history_index -= 1
|
79
|
+
@input = @history[ @history_index ]
|
80
|
+
cursorWriteInput
|
81
|
+
end
|
82
|
+
when Curses::KEY_DOWN
|
83
|
+
if @history_index < @history.length - 1
|
84
|
+
@history[ @history_index ] = @input
|
85
|
+
@history_index += 1
|
86
|
+
@input = @history[ @history_index ]
|
87
|
+
cursorWriteInput
|
88
|
+
end
|
89
|
+
when CTRL_K
|
90
|
+
@input = ""
|
91
|
+
cursorWriteInput
|
92
|
+
else
|
93
|
+
if c > 31 and c < 255 and c != BACKSPACE
|
94
|
+
if @input_cursor == @input.length
|
95
|
+
@input << c
|
96
|
+
@window.addch c
|
97
|
+
else
|
98
|
+
@input = @input[ 0...@input_cursor ] + c.chr + @input[ @input_cursor..-1 ]
|
99
|
+
@window.setpos( @window.cury, @window.curx + 1 )
|
100
|
+
redrawInput
|
101
|
+
end
|
102
|
+
@input_cursor += 1
|
103
|
+
else
|
104
|
+
@diakonos.log "Other input: #{c}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
@diakonos.closeListBuffer
|
110
|
+
|
111
|
+
@history[ -1 ] = @input
|
112
|
+
|
113
|
+
return @input
|
114
|
+
end
|
115
|
+
|
116
|
+
def redrawInput
|
117
|
+
curx = @window.curx
|
118
|
+
cury = @window.cury
|
119
|
+
@window.setpos( @icury, @icurx )
|
120
|
+
@window.addstr "%-#{ Curses::cols - curx }s%s" % [ @input, " " * ( Curses::cols - @input.length ) ]
|
121
|
+
@window.setpos( cury, curx )
|
122
|
+
@window.refresh
|
123
|
+
end
|
124
|
+
|
125
|
+
# Redisplays the input text starting at the start of the user input area,
|
126
|
+
# positioning the cursor at the end of the text.
|
127
|
+
def cursorWriteInput
|
128
|
+
if @input != nil
|
129
|
+
@input_cursor = @input.length
|
130
|
+
@window.setpos( @window.cury, @icurx + @input.length )
|
131
|
+
redrawInput
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def completeInput
|
136
|
+
if @completion_array != nil and @input.length > 0
|
137
|
+
len = @input.length
|
138
|
+
matches = @completion_array.find_all { |el| el[ 0...len ] == @input and len < el.length }
|
139
|
+
else
|
140
|
+
matches = Dir.glob( ( @input.subHome() + "*" ).gsub( /\*\*/, "*" ) )
|
141
|
+
end
|
142
|
+
|
143
|
+
if matches.length == 1
|
144
|
+
@input = matches[ 0 ]
|
145
|
+
cursorWriteInput
|
146
|
+
File.open( @list_filename, "w" ) do |f|
|
147
|
+
f.puts "(unique)"
|
148
|
+
end
|
149
|
+
if @completion_array == nil and FileTest.directory?( @input )
|
150
|
+
@input << "/"
|
151
|
+
cursorWriteInput
|
152
|
+
completeInput
|
153
|
+
end
|
154
|
+
elsif matches.length > 1
|
155
|
+
common = matches[ 0 ]
|
156
|
+
File.open( @list_filename, "w" ) do |f|
|
157
|
+
i = nil
|
158
|
+
matches.each do |match|
|
159
|
+
f.puts match
|
160
|
+
|
161
|
+
if match[ 0 ] != common[ 0 ]
|
162
|
+
common = nil
|
163
|
+
break
|
164
|
+
end
|
165
|
+
|
166
|
+
up_to = [ common.length - 1, match.length - 1 ].min
|
167
|
+
i = 1
|
168
|
+
while ( i <= up_to ) and ( match[ 0..i ] == common[ 0..i ] )
|
169
|
+
i += 1
|
170
|
+
end
|
171
|
+
common = common[ 0...i ]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
if common == nil
|
175
|
+
File.open( @list_filename, "w" ) do |f|
|
176
|
+
f.puts "(no matches)"
|
177
|
+
end
|
178
|
+
else
|
179
|
+
@input = common
|
180
|
+
cursorWriteInput
|
181
|
+
end
|
182
|
+
else
|
183
|
+
File.open( @list_filename, "w" ) do |f|
|
184
|
+
f.puts "(no matches)"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
@diakonos.openListBuffer
|
188
|
+
@window.setpos( @window.cury, @window.curx )
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|