ver 2009.11.29 → 2009.12.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 (47) hide show
  1. data/AUTHORS +6 -0
  2. data/CHANGELOG +353 -1
  3. data/LICENSE +18 -0
  4. data/MANIFEST +11 -1
  5. data/Rakefile +2 -1
  6. data/bin/ver +3 -12
  7. data/config/detect.rb +1 -1
  8. data/config/keymap/diakonos.rb +181 -0
  9. data/config/keymap/emacs.rb +24 -24
  10. data/config/keymap/vim.rb +162 -127
  11. data/config/rc.rb +29 -14
  12. data/config/syntax/Nemerle.json +3 -3
  13. data/lib/ver.rb +88 -134
  14. data/lib/ver/entry.rb +5 -0
  15. data/lib/ver/exception_view.rb +97 -0
  16. data/lib/ver/hover_completion.rb +14 -7
  17. data/lib/ver/keymap.rb +30 -1
  18. data/lib/ver/layout.rb +20 -14
  19. data/lib/ver/methods.rb +6 -15
  20. data/lib/ver/methods/bookmark.rb +189 -0
  21. data/lib/ver/methods/completion.rb +2 -2
  22. data/lib/ver/methods/control.rb +109 -26
  23. data/lib/ver/methods/ctags.rb +28 -4
  24. data/lib/ver/methods/delete.rb +85 -4
  25. data/lib/ver/methods/insert.rb +73 -52
  26. data/lib/ver/methods/move.rb +122 -35
  27. data/lib/ver/methods/open.rb +4 -43
  28. data/lib/ver/methods/search.rb +46 -17
  29. data/lib/ver/methods/select.rb +121 -24
  30. data/lib/ver/methods/undo.rb +23 -0
  31. data/lib/ver/methods/views.rb +5 -0
  32. data/lib/ver/mode.rb +18 -17
  33. data/lib/ver/status.rb +2 -2
  34. data/lib/ver/status/context.rb +166 -0
  35. data/lib/ver/text.rb +43 -81
  36. data/lib/ver/text/index.rb +24 -7
  37. data/lib/ver/undo.rb +289 -0
  38. data/lib/ver/vendor/sized_array.rb +70 -0
  39. data/lib/ver/vendor/textpow.rb +6 -1
  40. data/lib/ver/version.rb +3 -0
  41. data/lib/ver/view.rb +11 -8
  42. data/lib/ver/view/list/grep.rb +15 -4
  43. data/lib/ver/view/term.rb +9 -3
  44. data/spec/helper.rb +94 -0
  45. data/ver.gemspec +9 -6
  46. metadata +25 -5
  47. data/spec/keymap.rb +0 -224
@@ -1,6 +1,8 @@
1
1
  module VER
2
2
  # The status bar
3
3
  class Status < VER::Entry
4
+ autoload :Context, 'ver/status/context'
5
+
4
6
  attr_accessor :keymap, :view
5
7
  attr_reader :mode
6
8
 
@@ -112,7 +114,6 @@ module VER
112
114
  @history_idx = history.size - 1
113
115
  end
114
116
 
115
- p prev: [history, @history_idx]
116
117
  answer = history[@history_idx]
117
118
  return unless answer
118
119
  self.value = answer
@@ -126,7 +127,6 @@ module VER
126
127
  @history_idx = 0
127
128
  end
128
129
 
129
- p next: [history, @history_idx]
130
130
  answer = history[@history_idx]
131
131
  return unless answer
132
132
  self.value = answer
@@ -0,0 +1,166 @@
1
+ module VER
2
+ class Status
3
+ class Context < Struct.new(:text)
4
+ def filename(width = 0)
5
+ "%#{width}s" % text.filename
6
+ end
7
+ alias F filename
8
+
9
+ def basename(width = 0)
10
+ "%#{width}s" % text.filename.basename
11
+ end
12
+ alias f basename
13
+
14
+ def relative(width = 0)
15
+ "%#{width}s" % text.short_filename
16
+ end
17
+ alias r relative
18
+
19
+ def dir(width = 0)
20
+ "%#{width}s" % text.filename.directory
21
+ end
22
+ alias d dir
23
+
24
+ def line(width = 0)
25
+ "%#{width}s" % (text.count(1.0, :insert, :lines) + 1)
26
+ end
27
+ alias l line
28
+
29
+ def lines(width = 0)
30
+ "%#{width}s" % text.count(1.0, :end, :lines)
31
+ end
32
+ alias L lines
33
+
34
+ def column(width = 0)
35
+ "%#{width}s" % text.count('insert linestart', :insert, :displaychars)
36
+ end
37
+ alias c column
38
+
39
+ def percent
40
+ here = text.count(1.0, :insert, :lines)
41
+ total = text.count(1.0, :end, :lines)
42
+ percent = ((100.0 / total) * here).round
43
+
44
+ case percent
45
+ when 100; 'Bot'
46
+ when 0 ; 'Top'
47
+ else ; '%2d%%' % percent
48
+ end
49
+ end
50
+ alias P percent
51
+
52
+ def buffer(width = 0)
53
+ "%#{width}s" % text.layout.views.index(text.view)
54
+ end
55
+ alias b buffer
56
+
57
+ def buffers(width = 0)
58
+ "%#{width}s" % text.layout.views.size
59
+ end
60
+ alias B buffers
61
+
62
+ def encoding(width = 0)
63
+ "%#{width}s" % text.encoding
64
+ end
65
+ alias e encoding
66
+
67
+ def syntax(width = 0)
68
+ "%#{width}s" % text.syntax.name if text.syntax
69
+ end
70
+ alias s syntax
71
+
72
+ def mode(width = 0)
73
+ "%#{width}s" % text.keymap.mode
74
+ end
75
+ alias m mode
76
+
77
+ # format sequences:
78
+ #
79
+ # %c Current capacity (mAh)
80
+ # %r Current rate
81
+ # %b short battery status, '+', '-', '!'
82
+ # %p battery load percentage
83
+ # %m remaining time in minutes
84
+ # %h remaining time in hours
85
+ # %t remaining time in as 'H:M'
86
+ def battery(format = '[%b] %p% %t')
87
+ now = Time.now
88
+
89
+ if @battery_last
90
+ if @battery_last < (now - 60)
91
+ @battery_last = now
92
+ @battery_value = battery_build(format)
93
+ else
94
+ @battery_value
95
+ end
96
+ else
97
+ @battery_last = now
98
+ @battery_value = battery_build(format)
99
+ end
100
+ rescue => ex
101
+ puts ex, *ex.backtrace
102
+ ex.message
103
+ end
104
+
105
+ def battery_build(format)
106
+ total = {}
107
+
108
+ Dir.glob('/proc/acpi/battery/*/{state,info}') do |file|
109
+ parsed = battery_parse(file)
110
+ next unless parsed[:present] == 'yes'
111
+ # FIXME: doesn't take care of multiple batteries
112
+ total.merge!(parsed)
113
+ end
114
+
115
+ # rate might be 0
116
+ rate = total[:present_rate].to_i
117
+ capacity = total[:remaining_capacity].to_i
118
+
119
+ if rate == 0
120
+ hours, percent = 2, 100
121
+ time = hours_left = minutes_left = 'N/A'
122
+ else
123
+ hours, minutes = ((capacity * 60.0) / rate).divmod(60)
124
+ minutes = minutes.round
125
+ percent = ((100 / total[:last_full_capacity].to_f) * capacity).round
126
+ hours_left = (hours + (minutes / 60.0)).round
127
+ minutes_left = (hours / 60.0) + minutes
128
+ time = "#{hours}:#{minutes}"
129
+ end
130
+
131
+ case total[:charging_state]
132
+ when 'discharging'
133
+ b = hours < 1 ? '!' : '-'
134
+ when 'charging'
135
+ b = '+'
136
+ end
137
+
138
+ final = {
139
+ '%c' => capacity,
140
+ '%r' => rate,
141
+ '%b' => b,
142
+ '%p' => percent,
143
+ '%m' => minutes_left,
144
+ '%h' => hours_left,
145
+ '%t' => time,
146
+ }
147
+
148
+ @last = Time.now
149
+ format.gsub(/%\w/, final)
150
+ end
151
+
152
+ def battery_parse(file)
153
+ data = {}
154
+
155
+ File.open(file) do |io|
156
+ io.each_line do |line|
157
+ next unless line =~ /^([^:]+):\s*(.+)$/
158
+ data[$1.downcase.tr(' ', '_').to_sym] = $2
159
+ end
160
+ end
161
+
162
+ data
163
+ end
164
+ end
165
+ end
166
+ end
@@ -42,7 +42,7 @@ module VER
42
42
  MATCH_WORD_LEFT = /(^|\b)\S+(\b|$)/
43
43
 
44
44
  attr_accessor :keymap, :view, :status
45
- attr_reader :filename, :encoding, :pristine, :syntax
45
+ attr_reader :filename, :encoding, :pristine, :syntax, :undoer
46
46
 
47
47
  # attributes for diverse functionality
48
48
  attr_accessor :selection_mode, :selection_start
@@ -50,17 +50,19 @@ module VER
50
50
  def initialize(view, options = {})
51
51
  super
52
52
  self.view = view
53
+ @options = Options.new(:text, VER.options)
53
54
 
54
- keymap_name = VER.options.keymap
55
+ keymap_name = @options.keymap
55
56
  self.keymap = Keymap.get(name: keymap_name, receiver: self)
56
57
 
57
58
  apply_mode_style(keymap.mode) # for startup
58
59
  setup_tags
59
60
 
61
+ @undoer = VER::Undo::Tree.new(self)
62
+
60
63
  self.selection_start = nil
61
64
  @pristine = true
62
65
  @syntax = nil
63
- @options = Options.new(:text, VER.options)
64
66
  @encoding = Encoding.default_internal
65
67
  @dirty_indices = []
66
68
 
@@ -91,34 +93,20 @@ module VER
91
93
  view.layout
92
94
  end
93
95
 
94
- # lines start from 1
95
- # end is maximum lines + 1
96
96
  def status_projection(into)
97
- format = "%s %s %s [%s]"
98
-
99
- top, bot = yview
100
-
101
- if top < 0.5
102
- percent = '[top]'
103
- elsif bot > 99.5
104
- percent = '[bot]'
105
- else
106
- percent = "#{bot.to_i}%"
107
- end
97
+ format = options.statusline.dup
108
98
 
109
- additional = [keymap.mode]
110
- syntax_name = syntax.name if syntax
111
- additional << syntax_name if syntax_name
112
- additional << @encoding
99
+ format.gsub!(/%([[:alpha:]]+)/, '#{\1()}')
100
+ format.gsub!(/%_([[:alpha:]]+)/, '#{(_ = \1()) ? " #{_}" : ""}')
101
+ format.gsub!(/%([+-]?\d+)([[:alpha:]]+)/, '#{\2(\1)}')
102
+ format = "%{#{format}}"
113
103
 
114
- values = [
115
- short_filename,
116
- index(:insert).idx,
117
- percent,
118
- additional.join(' | '),
119
- ]
104
+ context = Status::Context.new(self)
105
+ line = context.instance_eval(format)
120
106
 
121
- into.value = format % values
107
+ into.value = line
108
+ rescue => ex
109
+ puts ex, ex.backtrace
122
110
  end
123
111
 
124
112
  TAG_ALL_MATCHING_OPTIONS = { from: '1.0', to: 'end - 1 chars' }
@@ -183,49 +171,12 @@ module VER
183
171
  Tk::Event.generate(self, '<<Movement>>')
184
172
  end
185
173
 
186
- def refresh_selection
187
- return unless start = selection_start
188
-
189
- now = index(:insert)
190
- left, right = [start, now].sort
191
- tag_remove :sel, '1.0', 'end'
192
-
193
- case selection_mode
194
- when :select_char
195
- tag_add :sel, left, "#{right} + 1 chars"
196
- when :select_line
197
- tag_add :sel, "#{left} linestart", "#{right} lineend"
198
- when :select_block
199
- ly, lx = left.split
200
- ry, rx = right.split
201
-
202
- from_y, to_y = [ly, ry].sort
203
- from_x, to_x = [lx, rx].sort
174
+ def insert(index, string)
175
+ index = index(index) unless index.respond_to?(:to_index)
204
176
 
205
- from_y.upto to_y do |y|
206
- tag_add :sel, "#{y}.#{from_x}", "#{y}.#{to_x + 1}"
207
- end
208
- end
209
- end
210
-
211
- # fix the ruby definition of delete, Tk allows more than 2 indices
212
- def delete(*args)
213
- if args.size > 2
214
- deleted = args.each_slice(2).map{|left, right| get(left, right) }
215
- else
216
- deleted = get(*args)
177
+ undo_record do |record|
178
+ record.insert(index, string)
217
179
  end
218
-
219
- copy(deleted)
220
-
221
- execute('delete', *args)
222
-
223
- touch!(*args)
224
- end
225
-
226
- def insert(*args)
227
- super
228
- touch!(args.first)
229
180
  end
230
181
 
231
182
  # Replaces the range of characters between index1 and index2 with the given
@@ -241,9 +192,14 @@ module VER
241
192
  # are active in the text widget.
242
193
  #
243
194
  # replace index1 index2 chars ?tagList chars tagList ...?
244
- def replace(index1, index2, *rest)
245
- super
246
- touch!(*index(index1).upto(index(index2)).to_a)
195
+ def replace(index1, index2, string)
196
+ index1 = index(index1) unless index1.respond_to?(:to_index)
197
+ index2 = index(index2) unless index2.respond_to?(:to_index)
198
+ return if index1 == index2
199
+
200
+ undo_record do |record|
201
+ record.replace(index1, index2, string)
202
+ end
247
203
  end
248
204
 
249
205
  def focus
@@ -282,6 +238,7 @@ module VER
282
238
 
283
239
  if @syntax = Syntax.from_filename(filename)
284
240
  defer{ syntax.highlight(self, value) }
241
+ status_projection(status) if status
285
242
  end
286
243
  end
287
244
 
@@ -296,6 +253,13 @@ module VER
296
253
  schedule_highlight!
297
254
  end
298
255
 
256
+ # TODO: maybe we can make this one faster when many lines are going to be
257
+ # highlighted at once by bundling them.
258
+ def touch!(*args)
259
+ args.each{|arg| schedule_line_highlight(arg) } if @syntax
260
+ Tk::Event.generate(self, '<<Modified>>')
261
+ end
262
+
299
263
  private
300
264
 
301
265
  def schedule_highlight!(*args)
@@ -315,22 +279,16 @@ module VER
315
279
  end
316
280
  end
317
281
 
318
- # TODO: maybe we can make this one faster when many lines are going to be
319
- # highlighted at once by bundling them.
320
- def touch!(*args)
321
- args.each{|arg| schedule_line_highlight(arg) } if @syntax
322
- Tk::Event.generate(self, '<<Modified>>')
323
- end
324
-
325
282
  def mode=(name)
326
283
  keymap.mode = mode = name.to_sym
327
- edit_separator
284
+ undo_separator
328
285
  apply_mode_style(mode)
329
286
  status_projection(status) if status
330
287
  end
331
288
 
332
289
  def apply_mode_style(mode)
333
290
  cursor = MODE_CURSOR[mode]
291
+ return unless cursor
334
292
  configure cursor
335
293
 
336
294
  return unless status && color = cursor[:insertbackground]
@@ -426,8 +384,12 @@ module VER
426
384
  end
427
385
  end
428
386
 
429
- def font(options)
430
- VER.options[:font].configure options
387
+ def font(given_options = nil)
388
+ if given_options
389
+ options.font.configure(given_options)
390
+ else
391
+ options.font
392
+ end
431
393
  end
432
394
  end
433
395
  end
@@ -19,6 +19,11 @@ module VER
19
19
  return 0 if widget.compare(self_idx, '==', other_idx)
20
20
  end
21
21
 
22
+ def ==(other)
23
+ self_idx, other_idx = idx, other.to_index.idx
24
+ widget.compare(self_idx, '==', other_idx)
25
+ end
26
+
22
27
  def delta(other)
23
28
  y_diff = other.y - y
24
29
 
@@ -38,27 +43,39 @@ module VER
38
43
  end
39
44
 
40
45
  def linestart
41
- "#{idx} linestart"
46
+ widget.index "#{idx} linestart"
42
47
  end
43
48
 
44
49
  def lineend
45
- "#{idx} lineend"
50
+ widget.index "#{idx} lineend"
46
51
  end
47
52
 
48
53
  def wordstart
49
- "#{idx} wordstart"
54
+ widget.index "#{idx} wordstart"
50
55
  end
51
56
 
52
57
  def wordend
53
- "#{idx} wordend"
58
+ widget.index "#{idx} wordend"
54
59
  end
55
60
 
56
61
  def next
57
- self.class.new(widget, "#{y + 1}.#{x}")
62
+ widget.index "#{y + 1}.#{x}"
58
63
  end
59
64
 
60
65
  def prev
61
- self.class.new(widget, "#{y - 1}.#{x}")
66
+ widget.index "#{y - 1}.#{x}"
67
+ end
68
+
69
+ def +(chars)
70
+ widget.index "#{idx} + #{chars} chars"
71
+ end
72
+
73
+ def -(other)
74
+ if other.is_a?(self.class)
75
+ widget.count(other, self, :displaychars)
76
+ else
77
+ widget.index "#{idx} - #{other.to_int} chars"
78
+ end
62
79
  end
63
80
 
64
81
  def split
@@ -66,7 +83,7 @@ module VER
66
83
  end
67
84
 
68
85
  def inspect
69
- "#<Text::Index #{self}>"
86
+ to_s # "#<Text::Index #{self}>"
70
87
  end
71
88
 
72
89
  def to_str