ver 2009.10.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|