ver 2009.11.29 → 2009.12.14

Sign up to get free protection for your applications and to get access to all the features.
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