ver 2009.10.14
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1404 -0
- data/MANIFEST +187 -0
- data/Rakefile +30 -0
- data/TODO +169 -0
- data/bin/ver +19 -0
- data/config/detect.rb +109 -0
- data/config/keymap/vim.rb +239 -0
- data/config/rc.rb +0 -0
- data/config/scratch +3 -0
- data/config/syntax/ANTLR.json +275 -0
- data/config/syntax/ASP VB.net.json +228 -0
- data/config/syntax/ASP.json +135 -0
- data/config/syntax/ActionScript.json +168 -0
- data/config/syntax/Ada.json +108 -0
- data/config/syntax/Apache.json +342 -0
- data/config/syntax/AppleScript.json +721 -0
- data/config/syntax/Bibtex.json +253 -0
- data/config/syntax/Blog (HTML).json +47 -0
- data/config/syntax/Blog (Markdown).json +50 -0
- data/config/syntax/Blog (Text).json +45 -0
- data/config/syntax/Blog (Textile).json +45 -0
- data/config/syntax/Bulletin Board.json +428 -0
- data/config/syntax/C++.json +323 -0
- data/config/syntax/C.json +694 -0
- data/config/syntax/CSS.json +346 -0
- data/config/syntax/DOT.json +79 -0
- data/config/syntax/Diff.json +136 -0
- data/config/syntax/Dylan.json +104 -0
- data/config/syntax/Eiffel.json +128 -0
- data/config/syntax/Erlang.json +1636 -0
- data/config/syntax/F-Script.json +137 -0
- data/config/syntax/FXScript.json +260 -0
- data/config/syntax/Gri.json +141 -0
- data/config/syntax/HTML (Mason).json +207 -0
- data/config/syntax/HTML (Rails).json +52 -0
- data/config/syntax/HTML (Tcl).json +42 -0
- data/config/syntax/HTML for ASP.net.json +736 -0
- data/config/syntax/HTML-ASP.json +45 -0
- data/config/syntax/HTML.json +614 -0
- data/config/syntax/Haskell.json +407 -0
- data/config/syntax/Inform.json +80 -0
- data/config/syntax/Ini.json +91 -0
- data/config/syntax/Io.json +142 -0
- data/config/syntax/Java.json +745 -0
- data/config/syntax/JavaProperties.json +42 -0
- data/config/syntax/JavaScript.json +446 -0
- data/config/syntax/LaTeX Beamer.json +65 -0
- data/config/syntax/LaTeX Log.json +88 -0
- data/config/syntax/LaTeX.json +962 -0
- data/config/syntax/Lighttpd.json +93 -0
- data/config/syntax/Lisp.json +101 -0
- data/config/syntax/Literate Haskell.json +55 -0
- data/config/syntax/Logtalk.json +289 -0
- data/config/syntax/Lua.json +146 -0
- data/config/syntax/M.json +744 -0
- data/config/syntax/MEL.json +161 -0
- data/config/syntax/MIPS.json +114 -0
- data/config/syntax/Mail.json +224 -0
- data/config/syntax/Makefile.json +66 -0
- data/config/syntax/Markdown.json +644 -0
- data/config/syntax/Modula-3.json +80 -0
- data/config/syntax/Movable Type.json +348 -0
- data/config/syntax/OCaml.json +1391 -0
- data/config/syntax/Objective-C++.json +21 -0
- data/config/syntax/OpenGL.json +24 -0
- data/config/syntax/PHP.json +2184 -0
- data/config/syntax/Pascal.json +128 -0
- data/config/syntax/Perl.json +2091 -0
- data/config/syntax/Plain text.json +49 -0
- data/config/syntax/Processing.json +188 -0
- data/config/syntax/Quake3 Config.json +54 -0
- data/config/syntax/R.json +157 -0
- data/config/syntax/Rez.json +137 -0
- data/config/syntax/Ruby on Rails.json +170 -0
- data/config/syntax/Ruby.json +1753 -0
- data/config/syntax/SQL (Rails).json +31 -0
- data/config/syntax/SQL.json +435 -0
- data/config/syntax/SWIG.json +96 -0
- data/config/syntax/Scheme.json +359 -0
- data/config/syntax/Shell-Unix-Generic.json +1198 -0
- data/config/syntax/Slate.json +265 -0
- data/config/syntax/Smarty.json +110 -0
- data/config/syntax/Standard ML.json +322 -0
- data/config/syntax/Subversion commit message.json +62 -0
- data/config/syntax/Tcl.json +278 -0
- data/config/syntax/TeX Math.json +83 -0
- data/config/syntax/TeX.json +157 -0
- data/config/syntax/Textile.json +273 -0
- data/config/syntax/Twiki.json +436 -0
- data/config/syntax/Vectorscript.json +97 -0
- data/config/syntax/XML strict.json +148 -0
- data/config/syntax/XML.json +301 -0
- data/config/syntax/XSL.json +96 -0
- data/config/syntax/YAML.json +293 -0
- data/config/syntax/iCalendar.json +51 -0
- data/config/syntax/reStructuredText.json +403 -0
- data/config/theme/Active4D.json +260 -0
- data/config/theme/All Hallow's Eve.json +171 -0
- data/config/theme/Amy.json +359 -0
- data/config/theme/BBEdit.json +269 -0
- data/config/theme/Bespin.json +322 -0
- data/config/theme/Blackboard.json +215 -0
- data/config/theme/BoysAndGirls01.json +156 -0
- data/config/theme/Brilliance Black.json +1695 -0
- data/config/theme/Brilliance Dull.json +1451 -0
- data/config/theme/Classic Modified.json +288 -0
- data/config/theme/Cobalt.json +345 -0
- data/config/theme/Cool Glow.json +215 -0
- data/config/theme/Dawn.json +258 -0
- data/config/theme/Eiffel.json +270 -0
- data/config/theme/Espresso Libre.json +247 -0
- data/config/theme/Fluidvision.json +272 -0
- data/config/theme/IDLE.json +159 -0
- data/config/theme/LAZY.json +178 -0
- data/config/theme/Mac Classic.json +277 -0
- data/config/theme/MagicWB (Amiga).json +231 -0
- data/config/theme/Merbivore Soft.json +181 -0
- data/config/theme/Merbivore.json +181 -0
- data/config/theme/Monokai.json +177 -0
- data/config/theme/Notepad2.json +166 -0
- data/config/theme/Pastels on Dark.json +437 -0
- data/config/theme/RubyBlue.json +226 -0
- data/config/theme/Sin City 2.json +361 -0
- data/config/theme/Slate.json +270 -0
- data/config/theme/Slush & Poppies.json +232 -0
- data/config/theme/SpaceCadet.json +143 -0
- data/config/theme/Sunburst.json +415 -0
- data/config/theme/Twilight BG FG.json +633 -0
- data/config/theme/Twilight.json +321 -0
- data/config/theme/Whys Poignant.json +119 -0
- data/config/theme/Zenburnesque.json +237 -0
- data/config/theme/barf.json +155 -0
- data/config/theme/fake.json +418 -0
- data/config/theme/happydeluxe.json +114 -0
- data/config/theme/iLife 05.json +393 -0
- data/config/theme/iPlastic.json +177 -0
- data/config/theme/mintBlue Dark.json +414 -0
- data/config/theme/mintBlue.json +415 -0
- data/config/theme/monoindustrial.json +276 -0
- data/config/theme/starlight.json +67 -0
- data/config/tutorial +74 -0
- data/config/welcome +115 -0
- data/help/index.verh +14 -0
- data/lib/ver.rb +156 -0
- data/lib/ver/entry.rb +97 -0
- data/lib/ver/keymap.rb +96 -0
- data/lib/ver/layout.rb +107 -0
- data/lib/ver/methods.rb +19 -0
- data/lib/ver/methods/completion.rb +116 -0
- data/lib/ver/methods/control.rb +340 -0
- data/lib/ver/methods/insert.rb +6 -0
- data/lib/ver/methods/move.rb +65 -0
- data/lib/ver/methods/search.rb +33 -0
- data/lib/ver/methods/select.rb +145 -0
- data/lib/ver/methods/views.rb +21 -0
- data/lib/ver/mode.rb +160 -0
- data/lib/ver/options.rb +207 -0
- data/lib/ver/plist.rb +106 -0
- data/lib/ver/status.rb +67 -0
- data/lib/ver/syntax.rb +68 -0
- data/lib/ver/syntax/detector.rb +53 -0
- data/lib/ver/syntax/processor.rb +48 -0
- data/lib/ver/text.rb +374 -0
- data/lib/ver/textpow.rb +357 -0
- data/lib/ver/theme.rb +162 -0
- data/lib/ver/vendor/fuzzy_file_finder.rb +340 -0
- data/lib/ver/view.rb +163 -0
- data/lib/ver/view/entry.rb +28 -0
- data/lib/ver/view/list.rb +137 -0
- data/lib/ver/view/list/buffer.rb +27 -0
- data/lib/ver/view/list/fuzzy_file_finder.rb +44 -0
- data/lib/ver/view/list/syntax.rb +13 -0
- data/lib/ver/view/list/theme.rb +13 -0
- data/spec/keymap.rb +224 -0
- data/tasks/bacon.rake +49 -0
- data/tasks/changelog.rake +18 -0
- data/tasks/gem.rake +22 -0
- data/tasks/gem_installer.rake +76 -0
- data/tasks/grancher.rake +12 -0
- data/tasks/install_dependencies.rake +6 -0
- data/tasks/manifest.rake +4 -0
- data/tasks/plist2json.rake +35 -0
- data/tasks/rcov.rake +18 -0
- data/tasks/release.rake +12 -0
- data/tasks/reversion.rake +8 -0
- data/tasks/syntax_list.rake +31 -0
- data/ver.gemspec +29 -0
- metadata +241 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module VER
|
2
|
+
module Methods
|
3
|
+
module Move
|
4
|
+
def go_char_left(count = 1)
|
5
|
+
mark_set :insert, "insert - #{count} char"
|
6
|
+
end
|
7
|
+
|
8
|
+
def go_char_right(count = 1)
|
9
|
+
mark_set :insert, "insert + #{count} char"
|
10
|
+
end
|
11
|
+
|
12
|
+
def go_line_up(count = 1)
|
13
|
+
mark_set :insert, "insert - #{count} line"
|
14
|
+
end
|
15
|
+
|
16
|
+
def go_line_down(count = 1)
|
17
|
+
mark_set :insert, "insert + #{count} line"
|
18
|
+
end
|
19
|
+
|
20
|
+
def go_word_left
|
21
|
+
mark_set :insert, 'insert - 1 char'
|
22
|
+
mark_set :insert, 'insert wordstart'
|
23
|
+
end
|
24
|
+
|
25
|
+
def go_word_right
|
26
|
+
mark_set :insert, 'insert + 1 char'
|
27
|
+
mark_set :insert, 'insert wordend'
|
28
|
+
end
|
29
|
+
|
30
|
+
def go_beginning_of_line
|
31
|
+
mark_set :insert, 'insert linestart'
|
32
|
+
end
|
33
|
+
|
34
|
+
def go_end_of_line
|
35
|
+
mark_set :insert, 'insert lineend'
|
36
|
+
end
|
37
|
+
|
38
|
+
def go_line(number = 0)
|
39
|
+
mark_set :insert, "#{number}.0"
|
40
|
+
end
|
41
|
+
|
42
|
+
def go_end_of_file
|
43
|
+
mark_set :insert, :end
|
44
|
+
end
|
45
|
+
|
46
|
+
def go_page_up
|
47
|
+
height = Tk.root.winfo_height
|
48
|
+
linespace = cget(:font).metrics(:linespace)
|
49
|
+
diff = height / linespace
|
50
|
+
|
51
|
+
mark_set :insert, "insert - #{diff} line"
|
52
|
+
see :insert
|
53
|
+
end
|
54
|
+
|
55
|
+
def go_page_down
|
56
|
+
height = Tk.root.winfo_height
|
57
|
+
linespace = cget(:font).metrics(:linespace)
|
58
|
+
diff = height / linespace
|
59
|
+
|
60
|
+
mark_set :insert, "insert + #{diff} line"
|
61
|
+
see :insert
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module VER
|
2
|
+
module Methods
|
3
|
+
module Search
|
4
|
+
def status_search
|
5
|
+
status_ask 'Search term: ' do |term|
|
6
|
+
tag_all_matching(:search, Regexp.new(term))
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def search_next
|
11
|
+
from, to = tag_nextrange('search', 'insert + 1 chars', 'end')
|
12
|
+
mark_set(:insert, from) if from
|
13
|
+
end
|
14
|
+
|
15
|
+
def search_prev
|
16
|
+
from, to = tag_prevrange('search', 'insert - 1 chars', '0.0')
|
17
|
+
mark_set(:insert, from) if from
|
18
|
+
end
|
19
|
+
|
20
|
+
def search_next_word_under_cursor
|
21
|
+
word = get('insert wordstart', 'insert wordend')
|
22
|
+
tag_all_matching(:search, word)
|
23
|
+
search_next
|
24
|
+
end
|
25
|
+
|
26
|
+
def search_prev_word_under_cursor
|
27
|
+
word = get('insert wordstart', 'insert wordend')
|
28
|
+
tag_all_matching(:search, word)
|
29
|
+
search_prev
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module VER
|
2
|
+
module Methods
|
3
|
+
module Select
|
4
|
+
def start_selection_mode(name)
|
5
|
+
self.mode = name
|
6
|
+
@selection_start = index(:insert).split('.').map(&:to_i)
|
7
|
+
refresh_selection
|
8
|
+
end
|
9
|
+
|
10
|
+
def switch_selection_mode(name)
|
11
|
+
self.mode = name
|
12
|
+
refresh_selection
|
13
|
+
end
|
14
|
+
|
15
|
+
%w[char line block].each do |suffix|
|
16
|
+
name = "select_#{suffix}"
|
17
|
+
define_method "start_#{name}_mode" do
|
18
|
+
start_selection_mode name
|
19
|
+
end
|
20
|
+
|
21
|
+
define_method "switch_#{name}_mode" do
|
22
|
+
switch_selection_mode name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete_selection
|
27
|
+
queue = tag_ranges(:sel).flatten
|
28
|
+
delete(*queue)
|
29
|
+
mark_set(:insert, queue.first)
|
30
|
+
|
31
|
+
clear_selection
|
32
|
+
start_control_mode
|
33
|
+
end
|
34
|
+
|
35
|
+
def indent_selection
|
36
|
+
each_selected_line do |y, fx, tx|
|
37
|
+
tx = fx + 2
|
38
|
+
next if get("#{y}.#{fx}", "#{y}.#{tx}").empty?
|
39
|
+
insert("#{y}.#{fx}", ' ')
|
40
|
+
end
|
41
|
+
|
42
|
+
edit_separator
|
43
|
+
refresh_selection
|
44
|
+
end
|
45
|
+
|
46
|
+
def unindent_selection
|
47
|
+
queue = []
|
48
|
+
|
49
|
+
each_selected_line do |y, fx, tx|
|
50
|
+
tx = fx + 2
|
51
|
+
left, right = "#{y}.#{fx}", "#{y}.#{tx}"
|
52
|
+
next unless get(left, right) == ' '
|
53
|
+
queue << left << right
|
54
|
+
end
|
55
|
+
|
56
|
+
delete(*queue)
|
57
|
+
edit_separator
|
58
|
+
refresh_selection
|
59
|
+
end
|
60
|
+
|
61
|
+
def selection_evaluate
|
62
|
+
tag_ranges(:sel).each do |from, to|
|
63
|
+
code = get(from, to)
|
64
|
+
|
65
|
+
begin
|
66
|
+
result = eval(code)
|
67
|
+
insert("#{to} lineend", "\n%p" % [result])
|
68
|
+
rescue => exception
|
69
|
+
insert("#{to} lineend", "\n%p" % [exception])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def copy_selection
|
75
|
+
chunks = tag_ranges(:sel).map{|sel| get(*sel) }
|
76
|
+
copy(chunks.size == 1 ? chunks.first : chunks)
|
77
|
+
clear_selection
|
78
|
+
start_control_mode
|
79
|
+
end
|
80
|
+
|
81
|
+
def pipe_selection
|
82
|
+
status_ask 'Pipe command: ' do |cmd|
|
83
|
+
pipe_selection_execute(cmd)
|
84
|
+
clear_selection
|
85
|
+
start_control_mode
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def comment_selection
|
90
|
+
each_selected_line do |y, fx, tx|
|
91
|
+
insert("#{y}.0 linestart", '# ')
|
92
|
+
end
|
93
|
+
|
94
|
+
edit_separator
|
95
|
+
refresh_selection
|
96
|
+
end
|
97
|
+
|
98
|
+
def uncomment_selection
|
99
|
+
each_selected_line do |y, fx, tx|
|
100
|
+
delete("#{y}.0 linestart", "#{y}.0 linestart + 2 chars")
|
101
|
+
end
|
102
|
+
|
103
|
+
edit_separator
|
104
|
+
refresh_selection
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def each_selection
|
110
|
+
tag_ranges(:sel).each do |sel|
|
111
|
+
(fy, fx), (ty, tx) = sel.map{|pos| pos.split('.').map(&:to_i) }
|
112
|
+
yield fy, fx, ty, tx
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def each_selected_line
|
117
|
+
each_selection do |fy, fx, ty, tx|
|
118
|
+
fy.upto(ty) do |y|
|
119
|
+
yield y, fx, tx
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def pipe_selection_execute(*cmd)
|
125
|
+
require 'open3'
|
126
|
+
|
127
|
+
Open3.popen3(*cmd) do |si, sose, thread|
|
128
|
+
queue = []
|
129
|
+
tag_ranges(:sel).each do |from, to|
|
130
|
+
si.write(get(from, to))
|
131
|
+
queue << from << to
|
132
|
+
end
|
133
|
+
|
134
|
+
si.close
|
135
|
+
output = sose.read
|
136
|
+
|
137
|
+
return if queue.empty?
|
138
|
+
|
139
|
+
delete(*queue)
|
140
|
+
insert(queue.first, output)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module VER
|
2
|
+
module Methods
|
3
|
+
module Views
|
4
|
+
def view_create
|
5
|
+
view.create
|
6
|
+
end
|
7
|
+
|
8
|
+
def view_close
|
9
|
+
view.close
|
10
|
+
end
|
11
|
+
|
12
|
+
def view_focus_next
|
13
|
+
view.focus_next
|
14
|
+
end
|
15
|
+
|
16
|
+
def view_focus_prev
|
17
|
+
view.focus_prev
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/ver/mode.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
module VER
|
2
|
+
class Mode
|
3
|
+
MERGER = proc{|key, v1, v2|
|
4
|
+
if v1.respond_to?(:merge) && v2.respond_to?(:merge)
|
5
|
+
v1.merge(v2, &MERGER)
|
6
|
+
else
|
7
|
+
v2
|
8
|
+
end
|
9
|
+
}
|
10
|
+
|
11
|
+
attr_accessor :callback, :name, :arguments
|
12
|
+
|
13
|
+
def initialize(name, callback)
|
14
|
+
@name, @callback = name, callback
|
15
|
+
@stack = []
|
16
|
+
@map = {}
|
17
|
+
@ancestors = []
|
18
|
+
@missing = nil
|
19
|
+
@arguments = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def inherits(*others)
|
23
|
+
others.flatten.each do |other|
|
24
|
+
ancestor = find_ancestor(other.to_sym)
|
25
|
+
@ancestors.delete ancestor
|
26
|
+
@ancestors.unshift ancestor
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_ancestor(name)
|
31
|
+
callback.modes[name.to_sym]
|
32
|
+
end
|
33
|
+
|
34
|
+
def ancestors(*done, &block)
|
35
|
+
yield self
|
36
|
+
|
37
|
+
@ancestors.each do |ancestor|
|
38
|
+
next if done.include?(ancestor)
|
39
|
+
yield ancestor
|
40
|
+
ancestor.ancestors(done + [self], &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def missing(sym)
|
45
|
+
@missing = sym
|
46
|
+
end
|
47
|
+
|
48
|
+
def map(sym, *keychains)
|
49
|
+
keychains.each do |keychain|
|
50
|
+
bind(keychain.flatten, sym)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alias to map
|
54
|
+
|
55
|
+
def bind(keychain, action_name = nil, &block)
|
56
|
+
keychain = keychain.dup
|
57
|
+
total = hash = {}
|
58
|
+
|
59
|
+
while key = keychain.shift
|
60
|
+
register key
|
61
|
+
|
62
|
+
if keychain.empty?
|
63
|
+
hash[key] = block || action_name
|
64
|
+
else
|
65
|
+
hash = hash[key] = {}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@map.replace @map.merge(total, &MERGER)
|
70
|
+
end
|
71
|
+
|
72
|
+
def register(key)
|
73
|
+
callback.register(key)
|
74
|
+
end
|
75
|
+
|
76
|
+
def enter_keys(*keys)
|
77
|
+
keys.flatten.each{|key| enter_key(key) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def enter_key(key)
|
81
|
+
@stack << key
|
82
|
+
|
83
|
+
ancestors do |ancestor|
|
84
|
+
result = ancestor.attempt_execute(@stack)
|
85
|
+
|
86
|
+
case result
|
87
|
+
when nil # nothing matched yet, but possible in future
|
88
|
+
return nil
|
89
|
+
when false # nothing possible
|
90
|
+
# try next one
|
91
|
+
when true # executed
|
92
|
+
@stack.clear
|
93
|
+
return true
|
94
|
+
else
|
95
|
+
raise "%p is not a valid result" % [result]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# no ancestors or all failed
|
100
|
+
@stack.clear
|
101
|
+
enter_missing(key)
|
102
|
+
rescue => ex
|
103
|
+
puts ex, *ex.backtrace
|
104
|
+
@stack.clear
|
105
|
+
end
|
106
|
+
|
107
|
+
def enter_missing(key)
|
108
|
+
execute(@missing, key) if @missing
|
109
|
+
end
|
110
|
+
|
111
|
+
def attempt_execute(original_stack)
|
112
|
+
if arguments
|
113
|
+
stack, arg = Mode.split_stack(original_stack)
|
114
|
+
else
|
115
|
+
stack, arg = original_stack, nil
|
116
|
+
end
|
117
|
+
|
118
|
+
if stack.empty?
|
119
|
+
arg ? nil : false
|
120
|
+
else
|
121
|
+
executable = stack.inject(@map){|keys, key| keys.fetch(key) }
|
122
|
+
|
123
|
+
execute(executable, *arg)
|
124
|
+
end
|
125
|
+
rescue KeyError
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def execute(executable, *arg)
|
130
|
+
case executable
|
131
|
+
when Hash
|
132
|
+
return nil
|
133
|
+
when Symbol
|
134
|
+
callback.send(executable, *arg)
|
135
|
+
when Array
|
136
|
+
callback.send(*executable, *arg)
|
137
|
+
when Proc
|
138
|
+
executable.call(*arg)
|
139
|
+
else
|
140
|
+
return false
|
141
|
+
end
|
142
|
+
|
143
|
+
true
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.split_stack(stack)
|
147
|
+
return stack, nil if stack[0] == '0'
|
148
|
+
|
149
|
+
pivot = stack.index{|c| c !~ /\d/ }
|
150
|
+
|
151
|
+
if pivot == 0
|
152
|
+
return stack, nil
|
153
|
+
elsif pivot
|
154
|
+
return stack[pivot..-1], stack[0..pivot].join.to_i
|
155
|
+
else
|
156
|
+
return [], stack.join.to_i
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/ver/options.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
module VER
|
2
|
+
# Provides a minimal DSL to describe options with defaults and metadata.
|
3
|
+
#
|
4
|
+
# The example below should demonstrate the major features, note that key
|
5
|
+
# lookup wanders up the hierarchy until there is a match found or the parent
|
6
|
+
# of the Options class is itself, in which case nil will be returned.
|
7
|
+
#
|
8
|
+
# Usage:
|
9
|
+
#
|
10
|
+
# class Calculator
|
11
|
+
# @options = Options.new(:foo)
|
12
|
+
# def self.options; @options; end
|
13
|
+
#
|
14
|
+
# options.dsl do
|
15
|
+
# o "Which method to use", :method, :plus
|
16
|
+
# o "Default arguments", :args, [1, 2]
|
17
|
+
# sub(:minus){ o("Default arguments", :args, [4, 3]) }
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def self.calculate(method = nil, *args)
|
21
|
+
# method ||= options[:method]
|
22
|
+
# args = args.empty? ? options[method, :args] : args
|
23
|
+
# self.send(method, *args)
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def self.plus(n1, n2)
|
27
|
+
# n1 + n2
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# def self.minus(n1, n2)
|
31
|
+
# n1 - n2
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# Calculator.calculate
|
36
|
+
# # => 3
|
37
|
+
# Calculator.options[:method] = :minus
|
38
|
+
# # => :minus
|
39
|
+
# Calculator.calculate
|
40
|
+
# # => 1
|
41
|
+
# Calculator.calculate(:plus, 4, 5)
|
42
|
+
# # => 9
|
43
|
+
#
|
44
|
+
class Options
|
45
|
+
def initialize(name, parent = self)
|
46
|
+
@name, @parent, = name, parent
|
47
|
+
@hash = {}
|
48
|
+
yield(self) if block_given?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Shortcut for instance_eval
|
52
|
+
def dsl(&block)
|
53
|
+
instance_eval(&block) if block
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create a new Options instance with +name+ and pass +block+ on to its #dsl.
|
58
|
+
# Assigns the new instance to the +name+ Symbol on current instance.
|
59
|
+
def sub(name, &block)
|
60
|
+
name = name.to_sym
|
61
|
+
|
62
|
+
case found = @hash[name]
|
63
|
+
when Options
|
64
|
+
found.dsl(&block)
|
65
|
+
else
|
66
|
+
found = @hash[name] = Options.new(name, self).dsl(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
found
|
70
|
+
end
|
71
|
+
|
72
|
+
# Store an option in the Options instance.
|
73
|
+
#
|
74
|
+
# @param [#to_s] doc describing the purpose of this option
|
75
|
+
# @param [#to_sym] key used to access
|
76
|
+
# @param [Object] value may be anything
|
77
|
+
# @param [Hash] other optional Hash containing meta-data
|
78
|
+
# :doc, :value keys will be ignored
|
79
|
+
def option(doc, key, value, other = {}, &block)
|
80
|
+
trigger = block || other[:trigger]
|
81
|
+
convert = {:doc => doc.to_s, :value => value}
|
82
|
+
convert[:trigger] = trigger if trigger
|
83
|
+
@hash[key.to_sym] = other.merge(convert)
|
84
|
+
end
|
85
|
+
alias o option
|
86
|
+
|
87
|
+
# To avoid lookup on the parent, we can set a default to the internal Hash.
|
88
|
+
# Parameters as in {Options#o}, but without the +key+.
|
89
|
+
def default(doc, value, other = {})
|
90
|
+
@hash.default = other.merge(:doc => doc, :value => value)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Add a block that will be called when a new value is set.
|
94
|
+
def trigger(key, &block)
|
95
|
+
@hash[key.to_sym][:trigger] = block
|
96
|
+
end
|
97
|
+
|
98
|
+
# Try to retrieve the corresponding Hash for the passed keys, will try to
|
99
|
+
# retrieve the key from a parent if no match is found on the current
|
100
|
+
# instance. If multiple keys are passed it will try to find a matching
|
101
|
+
# child and pass the request on to it.
|
102
|
+
def get(key, *keys)
|
103
|
+
if keys.empty?
|
104
|
+
if value = @hash[key.to_sym]
|
105
|
+
value
|
106
|
+
elsif @parent != self
|
107
|
+
@parent.get(key)
|
108
|
+
else
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
elsif sub_options = get(key)
|
112
|
+
sub_options.get(*keys)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# @param [Array] keys
|
117
|
+
# @param [Object] value
|
118
|
+
def set_value(keys, value)
|
119
|
+
got = get(*keys)
|
120
|
+
return got[:value] = value if got
|
121
|
+
raise(IndexError, "There is no option available for %p" % [keys])
|
122
|
+
end
|
123
|
+
|
124
|
+
# Retrieve only the :value from the value hash if found via +keys+.
|
125
|
+
def [](*keys)
|
126
|
+
if value = get(*keys)
|
127
|
+
value.is_a?(Hash) ? value[:value] : value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Assign new :value to the value hash on the current instance.
|
132
|
+
#
|
133
|
+
# TODO: allow arbitrary assignments
|
134
|
+
def []=(key, value)
|
135
|
+
ks = key.to_sym
|
136
|
+
if @hash.has_key? ks
|
137
|
+
ns = @hash[ks]
|
138
|
+
ns[:value] = value
|
139
|
+
ns[:trigger].call(value) if ns[:trigger].respond_to?(:call)
|
140
|
+
elsif existing = get(key)
|
141
|
+
option(existing[:doc].to_s.dup, key, value)
|
142
|
+
else
|
143
|
+
raise(ArgumentError, "No key for %p exists" % [key])
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def method_missing(meth, *args)
|
148
|
+
case meth.to_s
|
149
|
+
when /^(.*)=$/
|
150
|
+
self[$1] = args.first
|
151
|
+
else
|
152
|
+
self[meth]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def merge!(hash)
|
157
|
+
hash.each_pair do |key, value|
|
158
|
+
set_value(key.to_s.split('.'), value)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def to_hash
|
163
|
+
@hash
|
164
|
+
end
|
165
|
+
|
166
|
+
def each_option(&block)
|
167
|
+
@hash.each(&block)
|
168
|
+
end
|
169
|
+
|
170
|
+
def each_pair
|
171
|
+
@hash.each do |key, values|
|
172
|
+
yield(key, self[key])
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def inspect
|
177
|
+
@hash.inspect
|
178
|
+
end
|
179
|
+
|
180
|
+
def pretty_print(q)
|
181
|
+
q.pp_hash @hash
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# extend your class with this
|
186
|
+
module Optioned
|
187
|
+
def self.included(into)
|
188
|
+
into.extend(SingletonMethods)
|
189
|
+
|
190
|
+
snaked = into.name.split('::').last
|
191
|
+
snaked = snaked.gsub(/\B[A-Z][^A-Z]/, '_\&').downcase.gsub(' ', '_')
|
192
|
+
|
193
|
+
options = VER.options.sub(snaked)
|
194
|
+
into.instance_variable_set(:@options, options)
|
195
|
+
end
|
196
|
+
|
197
|
+
module SingletonMethods
|
198
|
+
attr_reader :options
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def options
|
204
|
+
self.class.options
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|