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.
Files changed (188) hide show
  1. data/CHANGELOG +1404 -0
  2. data/MANIFEST +187 -0
  3. data/Rakefile +30 -0
  4. data/TODO +169 -0
  5. data/bin/ver +19 -0
  6. data/config/detect.rb +109 -0
  7. data/config/keymap/vim.rb +239 -0
  8. data/config/rc.rb +0 -0
  9. data/config/scratch +3 -0
  10. data/config/syntax/ANTLR.json +275 -0
  11. data/config/syntax/ASP VB.net.json +228 -0
  12. data/config/syntax/ASP.json +135 -0
  13. data/config/syntax/ActionScript.json +168 -0
  14. data/config/syntax/Ada.json +108 -0
  15. data/config/syntax/Apache.json +342 -0
  16. data/config/syntax/AppleScript.json +721 -0
  17. data/config/syntax/Bibtex.json +253 -0
  18. data/config/syntax/Blog (HTML).json +47 -0
  19. data/config/syntax/Blog (Markdown).json +50 -0
  20. data/config/syntax/Blog (Text).json +45 -0
  21. data/config/syntax/Blog (Textile).json +45 -0
  22. data/config/syntax/Bulletin Board.json +428 -0
  23. data/config/syntax/C++.json +323 -0
  24. data/config/syntax/C.json +694 -0
  25. data/config/syntax/CSS.json +346 -0
  26. data/config/syntax/DOT.json +79 -0
  27. data/config/syntax/Diff.json +136 -0
  28. data/config/syntax/Dylan.json +104 -0
  29. data/config/syntax/Eiffel.json +128 -0
  30. data/config/syntax/Erlang.json +1636 -0
  31. data/config/syntax/F-Script.json +137 -0
  32. data/config/syntax/FXScript.json +260 -0
  33. data/config/syntax/Gri.json +141 -0
  34. data/config/syntax/HTML (Mason).json +207 -0
  35. data/config/syntax/HTML (Rails).json +52 -0
  36. data/config/syntax/HTML (Tcl).json +42 -0
  37. data/config/syntax/HTML for ASP.net.json +736 -0
  38. data/config/syntax/HTML-ASP.json +45 -0
  39. data/config/syntax/HTML.json +614 -0
  40. data/config/syntax/Haskell.json +407 -0
  41. data/config/syntax/Inform.json +80 -0
  42. data/config/syntax/Ini.json +91 -0
  43. data/config/syntax/Io.json +142 -0
  44. data/config/syntax/Java.json +745 -0
  45. data/config/syntax/JavaProperties.json +42 -0
  46. data/config/syntax/JavaScript.json +446 -0
  47. data/config/syntax/LaTeX Beamer.json +65 -0
  48. data/config/syntax/LaTeX Log.json +88 -0
  49. data/config/syntax/LaTeX.json +962 -0
  50. data/config/syntax/Lighttpd.json +93 -0
  51. data/config/syntax/Lisp.json +101 -0
  52. data/config/syntax/Literate Haskell.json +55 -0
  53. data/config/syntax/Logtalk.json +289 -0
  54. data/config/syntax/Lua.json +146 -0
  55. data/config/syntax/M.json +744 -0
  56. data/config/syntax/MEL.json +161 -0
  57. data/config/syntax/MIPS.json +114 -0
  58. data/config/syntax/Mail.json +224 -0
  59. data/config/syntax/Makefile.json +66 -0
  60. data/config/syntax/Markdown.json +644 -0
  61. data/config/syntax/Modula-3.json +80 -0
  62. data/config/syntax/Movable Type.json +348 -0
  63. data/config/syntax/OCaml.json +1391 -0
  64. data/config/syntax/Objective-C++.json +21 -0
  65. data/config/syntax/OpenGL.json +24 -0
  66. data/config/syntax/PHP.json +2184 -0
  67. data/config/syntax/Pascal.json +128 -0
  68. data/config/syntax/Perl.json +2091 -0
  69. data/config/syntax/Plain text.json +49 -0
  70. data/config/syntax/Processing.json +188 -0
  71. data/config/syntax/Quake3 Config.json +54 -0
  72. data/config/syntax/R.json +157 -0
  73. data/config/syntax/Rez.json +137 -0
  74. data/config/syntax/Ruby on Rails.json +170 -0
  75. data/config/syntax/Ruby.json +1753 -0
  76. data/config/syntax/SQL (Rails).json +31 -0
  77. data/config/syntax/SQL.json +435 -0
  78. data/config/syntax/SWIG.json +96 -0
  79. data/config/syntax/Scheme.json +359 -0
  80. data/config/syntax/Shell-Unix-Generic.json +1198 -0
  81. data/config/syntax/Slate.json +265 -0
  82. data/config/syntax/Smarty.json +110 -0
  83. data/config/syntax/Standard ML.json +322 -0
  84. data/config/syntax/Subversion commit message.json +62 -0
  85. data/config/syntax/Tcl.json +278 -0
  86. data/config/syntax/TeX Math.json +83 -0
  87. data/config/syntax/TeX.json +157 -0
  88. data/config/syntax/Textile.json +273 -0
  89. data/config/syntax/Twiki.json +436 -0
  90. data/config/syntax/Vectorscript.json +97 -0
  91. data/config/syntax/XML strict.json +148 -0
  92. data/config/syntax/XML.json +301 -0
  93. data/config/syntax/XSL.json +96 -0
  94. data/config/syntax/YAML.json +293 -0
  95. data/config/syntax/iCalendar.json +51 -0
  96. data/config/syntax/reStructuredText.json +403 -0
  97. data/config/theme/Active4D.json +260 -0
  98. data/config/theme/All Hallow's Eve.json +171 -0
  99. data/config/theme/Amy.json +359 -0
  100. data/config/theme/BBEdit.json +269 -0
  101. data/config/theme/Bespin.json +322 -0
  102. data/config/theme/Blackboard.json +215 -0
  103. data/config/theme/BoysAndGirls01.json +156 -0
  104. data/config/theme/Brilliance Black.json +1695 -0
  105. data/config/theme/Brilliance Dull.json +1451 -0
  106. data/config/theme/Classic Modified.json +288 -0
  107. data/config/theme/Cobalt.json +345 -0
  108. data/config/theme/Cool Glow.json +215 -0
  109. data/config/theme/Dawn.json +258 -0
  110. data/config/theme/Eiffel.json +270 -0
  111. data/config/theme/Espresso Libre.json +247 -0
  112. data/config/theme/Fluidvision.json +272 -0
  113. data/config/theme/IDLE.json +159 -0
  114. data/config/theme/LAZY.json +178 -0
  115. data/config/theme/Mac Classic.json +277 -0
  116. data/config/theme/MagicWB (Amiga).json +231 -0
  117. data/config/theme/Merbivore Soft.json +181 -0
  118. data/config/theme/Merbivore.json +181 -0
  119. data/config/theme/Monokai.json +177 -0
  120. data/config/theme/Notepad2.json +166 -0
  121. data/config/theme/Pastels on Dark.json +437 -0
  122. data/config/theme/RubyBlue.json +226 -0
  123. data/config/theme/Sin City 2.json +361 -0
  124. data/config/theme/Slate.json +270 -0
  125. data/config/theme/Slush & Poppies.json +232 -0
  126. data/config/theme/SpaceCadet.json +143 -0
  127. data/config/theme/Sunburst.json +415 -0
  128. data/config/theme/Twilight BG FG.json +633 -0
  129. data/config/theme/Twilight.json +321 -0
  130. data/config/theme/Whys Poignant.json +119 -0
  131. data/config/theme/Zenburnesque.json +237 -0
  132. data/config/theme/barf.json +155 -0
  133. data/config/theme/fake.json +418 -0
  134. data/config/theme/happydeluxe.json +114 -0
  135. data/config/theme/iLife 05.json +393 -0
  136. data/config/theme/iPlastic.json +177 -0
  137. data/config/theme/mintBlue Dark.json +414 -0
  138. data/config/theme/mintBlue.json +415 -0
  139. data/config/theme/monoindustrial.json +276 -0
  140. data/config/theme/starlight.json +67 -0
  141. data/config/tutorial +74 -0
  142. data/config/welcome +115 -0
  143. data/help/index.verh +14 -0
  144. data/lib/ver.rb +156 -0
  145. data/lib/ver/entry.rb +97 -0
  146. data/lib/ver/keymap.rb +96 -0
  147. data/lib/ver/layout.rb +107 -0
  148. data/lib/ver/methods.rb +19 -0
  149. data/lib/ver/methods/completion.rb +116 -0
  150. data/lib/ver/methods/control.rb +340 -0
  151. data/lib/ver/methods/insert.rb +6 -0
  152. data/lib/ver/methods/move.rb +65 -0
  153. data/lib/ver/methods/search.rb +33 -0
  154. data/lib/ver/methods/select.rb +145 -0
  155. data/lib/ver/methods/views.rb +21 -0
  156. data/lib/ver/mode.rb +160 -0
  157. data/lib/ver/options.rb +207 -0
  158. data/lib/ver/plist.rb +106 -0
  159. data/lib/ver/status.rb +67 -0
  160. data/lib/ver/syntax.rb +68 -0
  161. data/lib/ver/syntax/detector.rb +53 -0
  162. data/lib/ver/syntax/processor.rb +48 -0
  163. data/lib/ver/text.rb +374 -0
  164. data/lib/ver/textpow.rb +357 -0
  165. data/lib/ver/theme.rb +162 -0
  166. data/lib/ver/vendor/fuzzy_file_finder.rb +340 -0
  167. data/lib/ver/view.rb +163 -0
  168. data/lib/ver/view/entry.rb +28 -0
  169. data/lib/ver/view/list.rb +137 -0
  170. data/lib/ver/view/list/buffer.rb +27 -0
  171. data/lib/ver/view/list/fuzzy_file_finder.rb +44 -0
  172. data/lib/ver/view/list/syntax.rb +13 -0
  173. data/lib/ver/view/list/theme.rb +13 -0
  174. data/spec/keymap.rb +224 -0
  175. data/tasks/bacon.rake +49 -0
  176. data/tasks/changelog.rake +18 -0
  177. data/tasks/gem.rake +22 -0
  178. data/tasks/gem_installer.rake +76 -0
  179. data/tasks/grancher.rake +12 -0
  180. data/tasks/install_dependencies.rake +6 -0
  181. data/tasks/manifest.rake +4 -0
  182. data/tasks/plist2json.rake +35 -0
  183. data/tasks/rcov.rake +18 -0
  184. data/tasks/release.rake +12 -0
  185. data/tasks/reversion.rake +8 -0
  186. data/tasks/syntax_list.rake +31 -0
  187. data/ver.gemspec +29 -0
  188. metadata +241 -0
@@ -0,0 +1,340 @@
1
+ #--
2
+ # ==================================================================
3
+ # Author: Jamis Buck (jamis@jamisbuck.org)
4
+ # Date: 2008-10-09
5
+ #
6
+ # This file is in the public domain. Usage, modification, and
7
+ # redistribution of this file are unrestricted.
8
+ # ==================================================================
9
+ #++
10
+
11
+ # The "fuzzy" file finder provides a way for searching a directory
12
+ # tree with only a partial name. This is similar to the "cmd-T"
13
+ # feature in TextMate (http://macromates.com).
14
+ #
15
+ # Usage:
16
+ #
17
+ # finder = FuzzyFileFinder.new
18
+ # finder.search("app/blogcon") do |match|
19
+ # puts match[:highlighted_path]
20
+ # end
21
+ #
22
+ # In the above example, all files matching "app/blogcon" will be
23
+ # yielded to the block. The given pattern is reduced to a regular
24
+ # expression internally, so that any file that contains those
25
+ # characters in that order (even if there are other characters
26
+ # in between) will match.
27
+ #
28
+ # In other words, "app/blogcon" would match any of the following
29
+ # (parenthesized strings indicate how the match was made):
30
+ #
31
+ # * (app)/controllers/(blog)_(con)troller.rb
32
+ # * lib/c(ap)_(p)ool/(bl)ue_(o)r_(g)reen_(co)loratio(n)
33
+ # * test/(app)/(blog)_(con)troller_test.rb
34
+ #
35
+ # And so forth.
36
+ class FuzzyFileFinder
37
+ module Version
38
+ MAJOR = 1
39
+ MINOR = 0
40
+ TINY = 2
41
+ STRING = [MAJOR, MINOR, TINY].join(".")
42
+ end
43
+
44
+ # This is the exception that is raised if you try to scan a
45
+ # directory tree with too many entries. By default, a ceiling of
46
+ # 10,000 entries is enforced, but you can change that number via
47
+ # the +ceiling+ parameter to FuzzyFileFinder.new.
48
+ class TooManyEntries < RuntimeError; end
49
+
50
+ # Used internally to represent a run of characters within a
51
+ # match. This is used to build the highlighted version of
52
+ # a file name.
53
+ class CharacterRun < Struct.new(:string, :inside) #:nodoc:
54
+ def to_s
55
+ if inside
56
+ "(#{string})"
57
+ else
58
+ string
59
+ end
60
+ end
61
+ alias to_str to_s
62
+ end
63
+
64
+ # Used internally to represent a file within the directory tree.
65
+ class FileSystemEntry #:nodoc:
66
+ attr_reader :parent
67
+ attr_reader :name
68
+
69
+ def initialize(parent, name)
70
+ @parent = parent
71
+ @name = name
72
+ end
73
+
74
+ def path
75
+ File.join(parent.name, name)
76
+ end
77
+ end
78
+
79
+ # Used internally to represent a subdirectory within the directory
80
+ # tree.
81
+ class Directory #:nodoc:
82
+ attr_reader :name
83
+
84
+ def initialize(name, is_root=false)
85
+ @name = name
86
+ @is_root = is_root
87
+ end
88
+
89
+ def root?
90
+ is_root
91
+ end
92
+ end
93
+
94
+ # The roots directory trees to search.
95
+ attr_reader :roots
96
+
97
+ # The list of files beneath all +roots+
98
+ attr_reader :files
99
+
100
+ # The maximum number of files beneath all +roots+
101
+ attr_reader :ceiling
102
+
103
+ # The prefix shared by all +roots+.
104
+ attr_reader :shared_prefix
105
+
106
+ # Initializes a new FuzzyFileFinder. This will scan the
107
+ # given +directories+, using +ceiling+ as the maximum number
108
+ # of entries to scan. If there are more than +ceiling+ entries
109
+ # a TooManyEntries exception will be raised.
110
+ def initialize(directories=['.'], ceiling=10_000)
111
+ directories = Array(directories)
112
+ directories << "." if directories.empty?
113
+
114
+ # expand any paths with ~
115
+ root_dirnames = directories.map { |d| File.expand_path(d) }.select { |d| File.directory?(d) }.uniq
116
+
117
+ @roots = root_dirnames.map { |d| Directory.new(d, true) }
118
+ @shared_prefix = determine_shared_prefix
119
+ @shared_prefix_re = Regexp.new("^#{Regexp.escape(shared_prefix)}" + (shared_prefix.empty? ? "" : "/"))
120
+
121
+ @files = []
122
+ @ceiling = ceiling
123
+
124
+ rescan!
125
+ end
126
+
127
+ # Rescans the subtree. If the directory contents every change,
128
+ # you'll need to call this to force the finder to be aware of
129
+ # the changes.
130
+ def rescan!
131
+ @files.clear
132
+ roots.each { |root| follow_tree(root) }
133
+ end
134
+
135
+ # Takes the given +pattern+ (which must be a string) and searches
136
+ # all files beneath +root+, yielding each match.
137
+ #
138
+ # +pattern+ is interpreted thus:
139
+ #
140
+ # * "foo" : look for any file with the characters 'f', 'o', and 'o'
141
+ # in its basename (discounting directory names). The characters
142
+ # must be in that order.
143
+ # * "foo/bar" : look for any file with the characters 'b', 'a',
144
+ # and 'r' in its basename (discounting directory names). Also,
145
+ # any successful match must also have at least one directory
146
+ # element matching the characters 'f', 'o', and 'o' (in that
147
+ # order.
148
+ # * "foo/bar/baz" : same as "foo/bar", but matching two
149
+ # directory elements in addition to a file name of "baz".
150
+ #
151
+ # Each yielded match will be a hash containing the following keys:
152
+ #
153
+ # * :path refers to the full path to the file
154
+ # * :directory refers to the directory of the file
155
+ # * :name refers to the name of the file (without directory)
156
+ # * :highlighted_directory refers to the directory of the file with
157
+ # matches highlighted in parentheses.
158
+ # * :highlighted_name refers to the name of the file with matches
159
+ # highlighted in parentheses
160
+ # * :highlighted_path refers to the full path of the file with
161
+ # matches highlighted in parentheses
162
+ # * :abbr refers to an abbreviated form of :highlighted_path, where
163
+ # path segments without matches are compressed to just their first
164
+ # character.
165
+ # * :score refers to a value between 0 and 1 indicating how closely
166
+ # the file matches the given pattern. A score of 1 means the
167
+ # pattern matches the file exactly.
168
+ def search(pattern, &block)
169
+ pattern.strip!
170
+ path_parts = pattern.split("/")
171
+ path_parts.push "" if pattern[-1,1] == "/"
172
+
173
+ file_name_part = path_parts.pop || ""
174
+
175
+ if path_parts.any?
176
+ path_regex_raw = "^(.*?)" + path_parts.map { |part| make_pattern(part) }.join("(.*?/.*?)") + "(.*?)$"
177
+ path_regex = Regexp.new(path_regex_raw, Regexp::IGNORECASE)
178
+ end
179
+
180
+ file_regex_raw = "^(.*?)" << make_pattern(file_name_part) << "(.*)$"
181
+ file_regex = Regexp.new(file_regex_raw, Regexp::IGNORECASE)
182
+
183
+ path_matches = {}
184
+ files.each do |file|
185
+ path_match = match_path(file.parent, path_matches, path_regex, path_parts.length)
186
+ next if path_match[:missed]
187
+
188
+ match_file(file, file_regex, path_match, &block)
189
+ end
190
+ end
191
+
192
+ # Takes the given +pattern+ (which must be a string, formatted as
193
+ # described in #search), and returns up to +max+ matches in an
194
+ # Array. If +max+ is nil, all matches will be returned.
195
+ def find(pattern, max=nil)
196
+ results = []
197
+ search(pattern) do |match|
198
+ results << match
199
+ break if max && results.length >= max
200
+ end
201
+ return results
202
+ end
203
+
204
+ # Displays the finder object in a sane, non-explosive manner.
205
+ def inspect #:nodoc:
206
+ "#<%s:0x%x roots=%s, files=%d>" % [self.class.name, object_id, roots.map { |r| r.name.inspect }.join(", "), files.length]
207
+ end
208
+
209
+ private
210
+
211
+ # Recursively scans +directory+ and all files and subdirectories
212
+ # beneath it, depth-first.
213
+ def follow_tree(directory)
214
+ Dir.entries(directory.name).each do |entry|
215
+ next if entry[0,1] == "."
216
+ raise TooManyEntries if files.length > ceiling
217
+
218
+ full = File.join(directory.name, entry)
219
+
220
+ if File.directory?(full)
221
+ follow_tree(Directory.new(full))
222
+ else
223
+ files.push(FileSystemEntry.new(directory, entry))
224
+ end
225
+ end
226
+ end
227
+
228
+ # Takes the given pattern string "foo" and converts it to a new
229
+ # string "(f)([^/]*?)(o)([^/]*?)(o)" that can be used to create
230
+ # a regular expression.
231
+ def make_pattern(pattern)
232
+ pattern = pattern.split(//)
233
+ pattern << "" if pattern.empty?
234
+
235
+ pattern.inject("") do |regex, character|
236
+ regex << "([^/]*?)" if regex.length > 0
237
+ regex << "(" << Regexp.escape(character) << ")"
238
+ end
239
+ end
240
+
241
+ # Given a MatchData object +match+ and a number of "inside"
242
+ # segments to support, compute both the match score and the
243
+ # highlighted match string. The "inside segments" refers to how
244
+ # many patterns were matched in this one match. For a file name,
245
+ # this will always be one. For directories, it will be one for
246
+ # each directory segment in the original pattern.
247
+ def build_match_result(match, inside_segments)
248
+ runs = []
249
+ inside_chars = total_chars = 0
250
+ match.captures.each_with_index do |capture, index|
251
+ if capture.length > 0
252
+ # odd-numbered captures are matches inside the pattern.
253
+ # even-numbered captures are matches between the pattern's elements.
254
+ inside = index % 2 != 0
255
+
256
+ total_chars += capture.gsub(%r(/), "").length # ignore '/' delimiters
257
+ inside_chars += capture.length if inside
258
+
259
+ if runs.last && runs.last.inside == inside
260
+ runs.last.string << capture
261
+ else
262
+ runs << CharacterRun.new(capture, inside)
263
+ end
264
+ end
265
+ end
266
+
267
+ # Determine the score of this match.
268
+ # 1. fewer "inside runs" (runs corresponding to the original pattern)
269
+ # is better.
270
+ # 2. better coverage of the actual path name is better
271
+
272
+ inside_runs = runs.select { |r| r.inside }
273
+ run_ratio = inside_runs.length.zero? ? 1 : inside_segments / inside_runs.length.to_f
274
+
275
+ char_ratio = total_chars.zero? ? 1 : inside_chars.to_f / total_chars
276
+
277
+ score = run_ratio * char_ratio
278
+
279
+ return { :score => score, :result => runs.join }
280
+ end
281
+
282
+ # Match the given path against the regex, caching the result in +path_matches+.
283
+ # If +path+ is already cached in the path_matches cache, just return the cached
284
+ # value.
285
+ def match_path(path, path_matches, path_regex, path_segments)
286
+ return path_matches[path] if path_matches.key?(path)
287
+
288
+ matchable_name = path.name.sub(@shared_prefix_re, "")
289
+
290
+ if path_regex
291
+ match = matchable_name.match(path_regex)
292
+
293
+ path_matches[path] =
294
+ match && build_match_result(match, path_segments) ||
295
+ { :score => 1, :result => matchable_name, :missed => true }
296
+ else
297
+ path_matches[path] = { :score => 1, :result => matchable_name }
298
+ end
299
+ end
300
+
301
+ # Match +file+ against +file_regex+. If it matches, yield the match
302
+ # metadata to the block.
303
+ def match_file(file, file_regex, path_match, &block)
304
+ if file_match = file.name.match(file_regex)
305
+ match_result = build_match_result(file_match, 1)
306
+ full_match_result = File.join(path_match[:result], match_result[:result])
307
+ abbr = File.join(path_match[:result].gsub(/[^\/]+/) { |m| m.index("(") ? m : m[0,1] }, match_result[:result])
308
+
309
+ result = { :path => file.path,
310
+ :abbr => abbr,
311
+ :directory => file.parent.name,
312
+ :name => file.name,
313
+ :highlighted_directory => path_match[:result],
314
+ :highlighted_name => match_result[:result],
315
+ :highlighted_path => full_match_result,
316
+ :score => path_match[:score] * match_result[:score] }
317
+ yield result
318
+ end
319
+ end
320
+
321
+ def determine_shared_prefix
322
+ # the common case: if there is only a single root, then the entire
323
+ # name of the root is the shared prefix.
324
+ return roots.first.name if roots.length == 1
325
+
326
+ split_roots = roots.map { |root| root.name.split(%r{/}) }
327
+ segments = split_roots.map { |root| root.length }.max
328
+ master = split_roots.pop
329
+
330
+ segments.times do |segment|
331
+ if !split_roots.all? { |root| root[segment] == master[segment] }
332
+ return master[0,segment].join("/")
333
+ end
334
+ end
335
+
336
+ # shouldn't ever get here, since we uniq the root list before
337
+ # calling this method, but if we do, somehow...
338
+ return roots.first.name
339
+ end
340
+ end
data/lib/ver/view.rb ADDED
@@ -0,0 +1,163 @@
1
+ module VER
2
+ class View < TkFrame
3
+ autoload :Entry, 'ver/view/entry'
4
+ autoload :List, 'ver/view/list'
5
+
6
+ attr_reader :layout, :text, :status
7
+
8
+ def initialize(layout, options = {})
9
+ super
10
+ @layout = layout
11
+ @text = @status = @ybar = @xbar = nil
12
+ setup
13
+ end
14
+
15
+ # +-------+---+
16
+ # | | @ |
17
+ # | | y |
18
+ # | @text | b |
19
+ # | | a |
20
+ # | | r |
21
+ # +-------+---+
22
+ # | @xbar | |
23
+ # +-------+---+
24
+ # | @status |
25
+ # +-----------+
26
+ def setup
27
+ setup_text
28
+ # setup_scrollbars # enable if you really want some.
29
+ setup_status
30
+ setup_grid
31
+ setup_misc
32
+ setup_events
33
+ end
34
+
35
+ def setup_text
36
+ font = VER.options[:font]
37
+ tab_width = font.measure('0') * 2
38
+
39
+ @text = VER::Text.new(
40
+ self,
41
+ autoseparators: true, # insert separators into the undo flow
42
+ borderwidth: 0,
43
+ exportselection: true, # copy into X11 buffer automatically
44
+ insertbackground: '#0f0', # initial value (hardcoded for control mode)
45
+ font: font,
46
+ insertofftime: 0, # blinking cursor be gone!
47
+ setgrid: true, # tell the wm that this is a griddy window
48
+ tabs: tab_width,
49
+ tabstyle: :wordprocessor,
50
+ undo: true, # enable undo capabilities
51
+ wrap: :word
52
+ )
53
+ end
54
+
55
+ def setup_scrollbars
56
+ # vertical scrollbar
57
+ @ybar = Ttk::Scrollbar.new(self)
58
+ @text.yscrollbar(@ybar)
59
+
60
+ # horizontal scrollbar
61
+ @xbar = Ttk::Scrollbar.new(self)
62
+ @text.xscrollbar(@xbar)
63
+ end
64
+
65
+ def setup_status
66
+ @status = Status.new(self, font: VER.options[:font], takefocus: 0)
67
+ end
68
+
69
+ def setup_grid
70
+ TkGrid.grid @text, row: 0, column: 0, sticky: :nsew if @text
71
+ TkGrid.grid @ybar, row: 0, column: 1, sticky: :ns if @ybar
72
+ TkGrid.grid @xbar, row: 1, column: 0, sticky: :ew if @xbar
73
+ TkGrid.grid @status, row: 2, column: 0, sticky: :ew, columnspan: 2 if @status
74
+
75
+ TkGrid.columnconfigure self, 0, weight: 1
76
+ TkGrid.columnconfigure self, 1, weight: 0
77
+ TkGrid.rowconfigure self, 0, weight: 1
78
+ TkGrid.rowconfigure self, 1, weight: 0
79
+ end
80
+
81
+ def setup_misc
82
+ @text.status = @status
83
+ @text.view = self
84
+ @text.start_control_mode
85
+ @status.mode = :status_query
86
+ end
87
+
88
+ def setup_events
89
+ %w[Movement Modified Focus].each do |name|
90
+ @text.bind("<#{name}>"){|event| __send__("on_#{name.downcase}", event) }
91
+ end
92
+ end
93
+
94
+ def open_path(path)
95
+ @text.open_path(path)
96
+ end
97
+
98
+ def open_empty
99
+ @text.open_empty
100
+ end
101
+
102
+ # handling events
103
+
104
+ def on_movement(event)
105
+ @text.see :insert
106
+ @text.refresh_selection
107
+ @text.status_projection(@status)
108
+ end
109
+
110
+ def on_modified(event)
111
+ @text.see :insert
112
+ @text.refresh_highlight
113
+ @text.status_projection(@status)
114
+ end
115
+
116
+ def on_focus(event)
117
+ @text.set_window_title
118
+ end
119
+
120
+ # @text.bind '<Modified>', proc{|e| refresh; p :modified }
121
+ # @text.bind '<Undo>', proc{|e| refresh; p :undo }
122
+ # @text.bind '<Redo>', proc{|e| refresh; p :redo }
123
+ # @text.bind '<Copy>', proc{|e| p :copy }
124
+ # @text.bind '<Cut>', proc{|e| refresh; p :cut }
125
+ # @text.bind '<Paste>', proc{|e| refresh; p :paste }
126
+ # @text.bind '<PasteSelection>', proc{|e| refresh; p :paste_selection }
127
+ # @text.bind '<Movement>', proc{|e| p :movement }
128
+
129
+ def focus
130
+ text.focus
131
+ end
132
+
133
+ def create
134
+ layout.create_view do |view|
135
+ view.open_empty
136
+ end
137
+ end
138
+
139
+ def close
140
+ layout.close_view(self)
141
+ end
142
+
143
+ def focus_next
144
+ layout.focus_next(self)
145
+ end
146
+
147
+ def focus_prev
148
+ layout.focus_prev(self)
149
+ end
150
+
151
+ def destroy
152
+ [@text, @ybar, @xbar, @status].each do |widget|
153
+ widget.destroy if widget
154
+ end
155
+
156
+ super
157
+ end
158
+
159
+ def filename
160
+ text.filename
161
+ end
162
+ end
163
+ end