rbindkeys 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +23 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +52 -0
- data/Rakefile +11 -0
- data/bin/rbindkeys +5 -0
- data/lib/rbindkeys.rb +38 -0
- data/lib/rbindkeys/bind_resolver.rb +58 -0
- data/lib/rbindkeys/bind_set.rb +10 -0
- data/lib/rbindkeys/bind_tree.rb +148 -0
- data/lib/rbindkeys/cli.rb +103 -0
- data/lib/rbindkeys/device.rb +25 -0
- data/lib/rbindkeys/device_operator.rb +78 -0
- data/lib/rbindkeys/fix_resolver.rb +26 -0
- data/lib/rbindkeys/key_bind.rb +21 -0
- data/lib/rbindkeys/key_event_handler.rb +243 -0
- data/lib/rbindkeys/key_event_handler/configurer.rb +142 -0
- data/lib/rbindkeys/log_utils.rb +51 -0
- data/lib/rbindkeys/observer.rb +140 -0
- data/lib/rbindkeys/version.rb +3 -0
- data/lib/rbindkeys/virtual_device.rb +14 -0
- data/lib/rbindkeys/window_matcher.rb +35 -0
- data/rbindkeys.gemspec +26 -0
- data/sample/emacs.rb +100 -0
- data/sample/swap_left_ctrl_and_caps.rb +4 -0
- data/spec/bind_resolver_spec.rb +101 -0
- data/spec/bind_tree_bind_spec.rb +48 -0
- data/spec/bind_tree_resolve_for_pressed_event_spec.rb +74 -0
- data/spec/bind_tree_resolve_for_released_event_spec.rb +63 -0
- data/spec/cli_spec.rb +81 -0
- data/spec/device_operator_spec.rb +57 -0
- data/spec/fix_resolver_spec.rb +57 -0
- data/spec/key_event_handler/configurer_spec.rb +201 -0
- data/spec/key_event_handler/handle_spec.rb +222 -0
- metadata +190 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require "revdev"
|
4
|
+
|
5
|
+
module Rbindkeys
|
6
|
+
|
7
|
+
class Device < Revdev::EventDevice
|
8
|
+
|
9
|
+
def release_all_key
|
10
|
+
ie = Revdev::InputEvent.new nil, 0, 0, 0
|
11
|
+
Revdev.constants.select{|c| c.match(/^(?:KEY|BTN)/)}.each do |c|
|
12
|
+
ie.type = Revdev::EV_KEY
|
13
|
+
ie.code = Revdev.const_get c
|
14
|
+
ie.value = 0
|
15
|
+
write_input_event ie
|
16
|
+
|
17
|
+
ie.type = Revdev::EV_SYN
|
18
|
+
ie.code = 0
|
19
|
+
write_input_event ie
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require 'revdev'
|
4
|
+
|
5
|
+
module Rbindkeys
|
6
|
+
|
7
|
+
# device operations like send key event, send LED event, etc.
|
8
|
+
class DeviceOperator
|
9
|
+
|
10
|
+
LOG = LogUtils.get_logger name
|
11
|
+
|
12
|
+
# real event device
|
13
|
+
attr_reader :device
|
14
|
+
|
15
|
+
# uinput device
|
16
|
+
attr_reader :virtural
|
17
|
+
|
18
|
+
# key code set which was send press event but is not send release event
|
19
|
+
attr_reader :pressed_key_set
|
20
|
+
|
21
|
+
def initialize dev, vdev
|
22
|
+
@device = dev
|
23
|
+
@virtual = vdev
|
24
|
+
@pressed_key_set = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def release_key code
|
28
|
+
send_key code, 0
|
29
|
+
end
|
30
|
+
def press_key code
|
31
|
+
send_key code, 1
|
32
|
+
end
|
33
|
+
def pressing_key code
|
34
|
+
send_key code, 2
|
35
|
+
end
|
36
|
+
|
37
|
+
def send_key code, state
|
38
|
+
send_event Revdev::EV_KEY, code, state
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_event *args
|
42
|
+
event =
|
43
|
+
case args.length
|
44
|
+
when 1 then args[0]
|
45
|
+
when 3 then
|
46
|
+
@cache_input_event ||= Revdev::InputEvent.new nil, 0, 0, 0
|
47
|
+
@cache_input_event.type = args[0]
|
48
|
+
@cache_input_event.code = args[1]
|
49
|
+
@cache_input_event.value = args[2]
|
50
|
+
@cache_input_event
|
51
|
+
else raise ArgumentError, "expect a InputEvent or 3 Fixnums (type, code, state)"
|
52
|
+
end
|
53
|
+
dev = case event.type
|
54
|
+
when Revdev::EV_KEY then @virtual
|
55
|
+
when Revdev::EV_LED then @device
|
56
|
+
else @virtual
|
57
|
+
end
|
58
|
+
|
59
|
+
update_pressed_key_set event
|
60
|
+
dev.write_input_event event
|
61
|
+
LOG.info "write\t#{KeyEventHandler.get_state_by_value event} "+
|
62
|
+
"#{event.hr_code}(#{event.code})" if LOG.info?
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_pressed_key_set event
|
66
|
+
if event.type == Revdev::EV_KEY
|
67
|
+
case event.value
|
68
|
+
when 0 then @pressed_key_set.delete event.code
|
69
|
+
when 1 then @pressed_key_set << event.code
|
70
|
+
when 2 then # do nothing
|
71
|
+
else raise UnknownKeyValue, "expect 0, 1 or 2"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module Rbindkeys
|
4
|
+
|
5
|
+
# last resolvers which return @val as the resolve result
|
6
|
+
class FixResolver < BindResolver
|
7
|
+
private_class_method :new
|
8
|
+
@@pool = {}
|
9
|
+
|
10
|
+
def self.instance val
|
11
|
+
@@pool[val] or (@@pool[val] = new val)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize val
|
15
|
+
@val = val
|
16
|
+
end
|
17
|
+
|
18
|
+
def bind input, output
|
19
|
+
raise RuntimeError, 'cannot bind any input/output'
|
20
|
+
end
|
21
|
+
|
22
|
+
def resolve code, code_set
|
23
|
+
@val
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module Rbindkeys
|
4
|
+
class KeyBind
|
5
|
+
|
6
|
+
attr_reader :input
|
7
|
+
|
8
|
+
attr_reader :output
|
9
|
+
|
10
|
+
# when a signal of any input release event was accepted,
|
11
|
+
# if @inputs_recovery is true, outputs are released and other inputs are pressed,
|
12
|
+
# if @inputs_recovery is false or nil, outputs are released.
|
13
|
+
attr_reader :input_recovery
|
14
|
+
|
15
|
+
def initialize input, output, opt = {}
|
16
|
+
@input = input
|
17
|
+
@output = output
|
18
|
+
@input_recovery = opt[:input_recovery]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require 'rbindkeys/key_event_handler/configurer'
|
4
|
+
require 'active_window_x'
|
5
|
+
|
6
|
+
module Rbindkeys
|
7
|
+
|
8
|
+
# retrive key binds with key event
|
9
|
+
class KeyEventHandler
|
10
|
+
include Revdev
|
11
|
+
|
12
|
+
LOG = LogUtils.get_logger name
|
13
|
+
|
14
|
+
# device operator
|
15
|
+
attr_reader :operator
|
16
|
+
|
17
|
+
# defaulut key bind set which retrive key binds with a key event
|
18
|
+
attr_reader :default_bind_resolver
|
19
|
+
|
20
|
+
# current key bind set which retrive key binds with a key event
|
21
|
+
attr_reader :bind_resolver
|
22
|
+
|
23
|
+
# a hash (key:WindowMatcher, val:BindResolver) to switch BindResolver
|
24
|
+
# by the title or app_name of active window
|
25
|
+
attr_reader :window_bind_resolver_map
|
26
|
+
|
27
|
+
# proccessed resolver before bind_resolver
|
28
|
+
attr_reader :pre_bind_resolver
|
29
|
+
|
30
|
+
# code set of pressed key on the event device
|
31
|
+
attr_reader :pressed_key_set
|
32
|
+
|
33
|
+
# pressed key binds
|
34
|
+
attr_reader :active_bind_set
|
35
|
+
|
36
|
+
def initialize device_operator
|
37
|
+
@operator = device_operator
|
38
|
+
@default_bind_resolver = BindResolver.new
|
39
|
+
@window_bind_resolver = nil
|
40
|
+
@bind_resolver = @default_bind_resolver
|
41
|
+
@window_bind_resolver_map = []
|
42
|
+
@pre_bind_resolver = {}
|
43
|
+
@pressed_key_set = []
|
44
|
+
@active_bind_set = []
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_config file
|
48
|
+
code = File.read file
|
49
|
+
instance_eval code, file
|
50
|
+
end
|
51
|
+
|
52
|
+
def handle event
|
53
|
+
if LOG.info?
|
54
|
+
LOG.info "" unless LOG.debug?
|
55
|
+
LOG.info "read\t#{KeyEventHandler.get_state_by_value event} "+
|
56
|
+
"#{event.hr_code}(#{event.code})"
|
57
|
+
end
|
58
|
+
|
59
|
+
# handle pre_key_bind_set
|
60
|
+
event.code = (@pre_bind_resolver[event.code] or event.code)
|
61
|
+
|
62
|
+
# swich to handle event with event.value
|
63
|
+
result =
|
64
|
+
case event.value
|
65
|
+
when 0 then handle_release_event event
|
66
|
+
when 1 then handle_press_event event
|
67
|
+
when 2 then handle_pressing_event event
|
68
|
+
else raise UnknownKeyValue, "expect 0, 1 or 2 as event.value(#{event.value})"
|
69
|
+
end
|
70
|
+
|
71
|
+
case result
|
72
|
+
when :through
|
73
|
+
fill_gap_pressed_state event if event.value == 1
|
74
|
+
@operator.send_event event
|
75
|
+
when :ignore
|
76
|
+
# ignore the original event
|
77
|
+
end
|
78
|
+
|
79
|
+
handle_pressed_keys event
|
80
|
+
|
81
|
+
LOG.info "pressed_keys real:#{@pressed_key_set.inspect} "+
|
82
|
+
"virtual:#{@operator.pressed_key_set.inspect}" if LOG.info?
|
83
|
+
end
|
84
|
+
|
85
|
+
def handle_release_event event
|
86
|
+
release_bind_set = []
|
87
|
+
@active_bind_set.reject! do |key_bind|
|
88
|
+
if key_bind.input.include? event.code
|
89
|
+
release_bind_set << key_bind
|
90
|
+
true
|
91
|
+
else
|
92
|
+
false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if release_bind_set.empty?
|
97
|
+
:through
|
98
|
+
else
|
99
|
+
release_bind_set.each do |kb|
|
100
|
+
kb.output.each {|c|@operator.release_key c}
|
101
|
+
if kb.input_recovery
|
102
|
+
kb.input.clone.delete_if {|c|c==event.code}.each {|c|@operator.press_key c}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
:ignore
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def set_bind_resolver resolver
|
110
|
+
old_resolver = @bind_resolver if LOG.info?
|
111
|
+
@bind_resolver = resolver
|
112
|
+
LOG.info "switch bind_resolver: #{old_resolver} => "+
|
113
|
+
"#{@bind_resolver}" if LOG.info?
|
114
|
+
@bind_resolver
|
115
|
+
end
|
116
|
+
|
117
|
+
def handle_press_event event
|
118
|
+
r = @bind_resolver.resolve event.code, @pressed_key_set
|
119
|
+
|
120
|
+
LOG.debug "resolve result: #{r.inspect}" if LOG.debug?
|
121
|
+
|
122
|
+
if r.kind_of? KeyBind
|
123
|
+
|
124
|
+
if @bind_resolver.two_stroke?
|
125
|
+
set_bind_resolver (@window_bind_resolver or @default_bind_resolver)
|
126
|
+
end
|
127
|
+
|
128
|
+
if r.output.kind_of? Array
|
129
|
+
r.input.clone.delete_if{|c|c==event.code}.each {|c| @operator.release_key c}
|
130
|
+
r.output.each {|c| @operator.press_key c}
|
131
|
+
@active_bind_set << r
|
132
|
+
:ignore
|
133
|
+
elsif r.output.kind_of? BindResolver
|
134
|
+
set_bind_resolver r.output
|
135
|
+
:ignore
|
136
|
+
elsif r.output.kind_of? Proc
|
137
|
+
r.output.call event, @operator
|
138
|
+
elsif r.output.kind_of? Symbol
|
139
|
+
r
|
140
|
+
end
|
141
|
+
else
|
142
|
+
r
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def handle_pressing_event event
|
147
|
+
if @active_bind_set.empty?
|
148
|
+
:through
|
149
|
+
else
|
150
|
+
@active_bind_set.each {|kb| kb.output.each {|c| @operator.pressing_key c}}
|
151
|
+
:ignore
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def fill_gap_pressed_state event
|
156
|
+
return if @operator.pressed_key_set == @pressed_key_set
|
157
|
+
sub = @pressed_key_set - @operator.pressed_key_set
|
158
|
+
if event.value == 0
|
159
|
+
sub.delete event.code
|
160
|
+
end
|
161
|
+
sub.each {|code| @operator.press_key code}
|
162
|
+
end
|
163
|
+
|
164
|
+
def handle_pressed_keys event
|
165
|
+
if event.value == 1
|
166
|
+
@pressed_key_set << event.code
|
167
|
+
@pressed_key_set.sort! # TODO do not sort. implement an bubble insertion
|
168
|
+
elsif event.value == 0
|
169
|
+
if @pressed_key_set.delete(event.code).nil?
|
170
|
+
LOG.warn "#{event.code} does not exists on @pressed_keys" if LOG.warn?
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def active_window_changed window
|
176
|
+
if not window.nil?
|
177
|
+
title = window.title
|
178
|
+
app_name = window.app_name
|
179
|
+
if LOG.info?
|
180
|
+
LOG.info "" unless LOG.debug?
|
181
|
+
LOG.info "change active_window: :class => \"#{app_name}\", :title => \"#{title}\""
|
182
|
+
end
|
183
|
+
|
184
|
+
@window_bind_resolver_map.each do |matcher, bind_resolver|
|
185
|
+
if matcher.match? app_name, title
|
186
|
+
if LOG.info?
|
187
|
+
LOG.info "=> matcher #{matcher.app_name.inspect}, #{matcher.title.inspect}"
|
188
|
+
LOG.info " bind_resolver #{bind_resolver.inspect}"
|
189
|
+
end
|
190
|
+
set_bind_resolver bind_resolver
|
191
|
+
@window_bind_resolver = bind_resolver
|
192
|
+
return
|
193
|
+
end
|
194
|
+
end
|
195
|
+
else
|
196
|
+
if LOG.info?
|
197
|
+
LOG.info "" unless LOG.debug?
|
198
|
+
LOG.info "change active_window: nil"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
LOG.info "=> no matcher" if LOG.info?
|
203
|
+
set_bind_resolver @default_bind_resolver
|
204
|
+
@window_bind_resolver = nil
|
205
|
+
return
|
206
|
+
end
|
207
|
+
|
208
|
+
class << self
|
209
|
+
# parse and normalize to Fixnum/Array
|
210
|
+
def parse_code code, depth = 0
|
211
|
+
if code.kind_of? Symbol
|
212
|
+
code = parse_symbol code
|
213
|
+
elsif code.kind_of? Array
|
214
|
+
raise ArgumentError, "expect Array is the depth less than 1" if depth >= 1
|
215
|
+
code.map!{|c| parse_code c, (depth+1)}
|
216
|
+
elsif code.kind_of? Fixnum and depth == 0
|
217
|
+
code = [code]
|
218
|
+
elsif not code.kind_of? Fixnum
|
219
|
+
raise ArgumentError, "expect Symbol / Fixnum / Array"
|
220
|
+
end
|
221
|
+
code
|
222
|
+
end
|
223
|
+
|
224
|
+
# TODO convert :j -> KEY_J, :ctrl -> KEY_LEFTCTRL
|
225
|
+
def parse_symbol sym
|
226
|
+
if not sym.kind_of? Symbol
|
227
|
+
raise ArgumentError, "expect Symbol / Fixnum / Array"
|
228
|
+
end
|
229
|
+
Revdev.const_get sym
|
230
|
+
end
|
231
|
+
|
232
|
+
def get_state_by_value ev
|
233
|
+
case ev.value
|
234
|
+
when 0 then 'released '
|
235
|
+
when 1 then 'pressed '
|
236
|
+
when 2 then 'pressing '
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
#
|
3
|
+
# a part of KeyEventHandler (see lib/rbindkeys/key_event_handler.rb)
|
4
|
+
#
|
5
|
+
|
6
|
+
module Rbindkeys
|
7
|
+
class KeyEventHandler
|
8
|
+
|
9
|
+
# pre-prosessed key codes replacement for all inputs
|
10
|
+
def pre_bind_key input, output
|
11
|
+
if not (input.kind_of? Fixnum and output.kind_of? Fixnum)
|
12
|
+
raise ArgumentError, 'expect Fixnum for input and output'
|
13
|
+
end
|
14
|
+
|
15
|
+
if @pre_bind_resolver.has_key? input
|
16
|
+
raise DuplicateNodeError, "1st arg (#{input}) was already entried"
|
17
|
+
end
|
18
|
+
|
19
|
+
LOG.info "pre_bind_key #{input.inspect},\t#{output.inspect}" if LOG.info?
|
20
|
+
|
21
|
+
@pre_bind_resolver[input] = output
|
22
|
+
[input, output]
|
23
|
+
end
|
24
|
+
|
25
|
+
# define a new key binding
|
26
|
+
def bind_key input, output=nil, resolver=@bind_resolver, &block
|
27
|
+
if input.kind_of?(Array) or input.kind_of?(Fixnum)
|
28
|
+
input = KeyEventHandler.parse_code input
|
29
|
+
else
|
30
|
+
raise ArgumentError, '1st arg expect Array / Fixnum'
|
31
|
+
end
|
32
|
+
|
33
|
+
if block_given?
|
34
|
+
output = block
|
35
|
+
elsif output.nil?
|
36
|
+
raise ArgumentError, 'expect 1 arg with a block / 2 args'
|
37
|
+
elsif output.kind_of? BindResolver
|
38
|
+
elsif output.kind_of?(Array) or output.kind_of?(Fixnum)
|
39
|
+
output = KeyEventHandler::parse_code output
|
40
|
+
elsif output == :through or output == :ignore
|
41
|
+
else
|
42
|
+
raise ArgumentError, '2nd arg expect Array / Fixnum / BindResolver / '+
|
43
|
+
'Symbol(:through/:ignore)'
|
44
|
+
end
|
45
|
+
|
46
|
+
LOG.info "bind_key #{input.inspect},\t#{output.inspect}\t#{resolver}" if LOG.info?
|
47
|
+
|
48
|
+
resolver.bind input, output
|
49
|
+
end
|
50
|
+
|
51
|
+
# setting for 2stroke key binding
|
52
|
+
# _input_ :: prefix key. (e.g. [KEY_LEFTCTRL, KEY_X] (C-x)
|
53
|
+
# _resolver_ :: upper bind_resolver for fall throught
|
54
|
+
# _block_ :: to define sub-binds on this prefix key bind
|
55
|
+
def bind_prefix_key input, resolver=@bind_resolver, &block
|
56
|
+
if not block_given?
|
57
|
+
raise ArgumentError, "expect a block"
|
58
|
+
end
|
59
|
+
|
60
|
+
input = KeyEventHandler::parse_code input
|
61
|
+
LOG.info "bind_prefix_key #{input.inspect}\t#{resolver}" if LOG.info?
|
62
|
+
tmp = input.clone
|
63
|
+
tail_input = tmp.pop
|
64
|
+
|
65
|
+
binded_resolver = resolver.just_resolve tail_input, tmp
|
66
|
+
if binded_resolver == nil
|
67
|
+
binded_resolver = BindResolver.new :ignore, true
|
68
|
+
resolver.bind input, binded_resolver
|
69
|
+
elsif not binded_resolver.kind_of? BindResolver
|
70
|
+
raise DuplicateNodeError, "the codes (#{input.inspect}) was already binded"
|
71
|
+
end
|
72
|
+
|
73
|
+
@bind_resolver = binded_resolver
|
74
|
+
yield
|
75
|
+
@bind_resolver = resolver
|
76
|
+
binded_resolver
|
77
|
+
end
|
78
|
+
|
79
|
+
def new_bind_resolver upper_resolver=@bind_resolver, &block
|
80
|
+
if not block_given?
|
81
|
+
raise ArgumentError, "expect a block"
|
82
|
+
end
|
83
|
+
|
84
|
+
new_resolver = BindResolver.new upper_resolver
|
85
|
+
|
86
|
+
old_resolver = @bind_resolver
|
87
|
+
@bind_resolver = new_resolver
|
88
|
+
yield
|
89
|
+
@bind_resolver = old_resolver
|
90
|
+
|
91
|
+
new_resolver
|
92
|
+
end
|
93
|
+
|
94
|
+
# switch bind_resolver if the active window match _arg_
|
95
|
+
#
|
96
|
+
# _upper\_resolver_ :: a upper bind_resolver, :through, :ignore
|
97
|
+
# _arg_ :: a hash or a regexp
|
98
|
+
#
|
99
|
+
# ==arg
|
100
|
+
# if a hash, which have entries :title => Regexp and/or :class => Regexp,
|
101
|
+
# was given, this bind is active when the window title match with
|
102
|
+
# :title's Regexp AND the window class match with :class's Regexp.
|
103
|
+
# if a regexp was given, this bind is active when the window title match
|
104
|
+
# with the given regexp
|
105
|
+
def window upper_resolver, arg
|
106
|
+
if upper_resolver.nil?
|
107
|
+
upper_resolver = @bind_resolver
|
108
|
+
elsif upper_resolver.kind_of? Symbol
|
109
|
+
upper_resolver = FixResolver.instance upper_resolver
|
110
|
+
elsif not upper_resolver.kind_of? BindResolver
|
111
|
+
raise ArgumentError, "1nd argument is expected to be a BindResolver or"+
|
112
|
+
" a Symbol : #{ upper_resolver.to_s}"
|
113
|
+
end
|
114
|
+
|
115
|
+
if arg.kind_of? Regexp
|
116
|
+
arg = { :title => arg }
|
117
|
+
elsif arg.kind_of? Hash
|
118
|
+
arg.each do |k, v|
|
119
|
+
if not (k.kind_of?(Symbol) and v.kind_of?(Regexp))
|
120
|
+
raise ArgumentError, 'the 2nd argument Hash must only have'+
|
121
|
+
" Symbol keys and Regexp values : #{arg.inspect}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
raise ArgumentError, "2nd argument is expected to be a Hash or a Regexp : #{arg}"
|
126
|
+
end
|
127
|
+
|
128
|
+
resolver = BindResolver.new(upper_resolver)
|
129
|
+
@window_bind_resolver_map.push [WindowMatcher.new(arg), resolver]
|
130
|
+
|
131
|
+
if block_given?
|
132
|
+
old_resolver = @bind_resolver
|
133
|
+
@bind_resolver = resolver
|
134
|
+
yield
|
135
|
+
@bind_resolver = old_resolver
|
136
|
+
end
|
137
|
+
|
138
|
+
resolver
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|