linecache 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/ext/trace_nums.h ADDED
@@ -0,0 +1,111 @@
1
+ /* Order is in C enum order. The below is correct for Ruby 1.8.6.
2
+ Possibly others, but there may need some adjustment here.
3
+ */
4
+ char *NODE2NAME[] =
5
+ {
6
+ "method",
7
+ "fbody",
8
+ "cfunc",
9
+ "scope",
10
+ "block",
11
+ "if",
12
+ "case",
13
+ "when",
14
+ "opt_n (-n)",
15
+ "while",
16
+ "until",
17
+ "iter",
18
+ "for",
19
+ "break",
20
+ "next",
21
+ "redo",
22
+ "retry",
23
+ "begin",
24
+ "rescue",
25
+ "resbody",
26
+ "ensure",
27
+ "and",
28
+ "or",
29
+ "not",
30
+ "masgn",
31
+ "lasgn (x=)",
32
+ "dasgn",
33
+ "dasgn_curr",
34
+ "gasgn",
35
+ "iasgn",
36
+ "cdecl",
37
+ "cvasgn",
38
+ "cvdecl",
39
+ "op_asgn1",
40
+ "op_asgn2",
41
+ "op_asgn_and",
42
+ "op_asgn_or",
43
+ "call",
44
+ "fcall",
45
+ "vcall",
46
+ "super",
47
+ "zsuper",
48
+ "array",
49
+ "zarray",
50
+ "hash",
51
+ "return",
52
+ "yield",
53
+ "lvar",
54
+ "dvar",
55
+ "gvar",
56
+ "ivar",
57
+ "const",
58
+ "cvar",
59
+ "nth_ref",
60
+ "back_ref",
61
+ "match",
62
+ "match2 (~=, !~)",
63
+ "match3 (~=, !~)",
64
+ "lit",
65
+ "str",
66
+ "dstr",
67
+ "xstr",
68
+ "dxstr",
69
+ "evstr",
70
+ "dregx",
71
+ "dregx_once",
72
+ "args",
73
+ "argscat",
74
+ "argspush",
75
+ "splat (*args)",
76
+ "to_ary",
77
+ "svalue",
78
+ "block_arg",
79
+ "block_pass",
80
+ "defn",
81
+ "defs",
82
+ "alias",
83
+ "valias",
84
+ "undef",
85
+ "class",
86
+ "module",
87
+ "sclass",
88
+ "colon2 (::)",
89
+ "colon3",
90
+ "cref",
91
+ "dot2 (..)",
92
+ "dot3 (...)",
93
+ "flip2",
94
+ "flip3",
95
+ "attrset",
96
+ "self",
97
+ "nil",
98
+ "true",
99
+ "false",
100
+ "defined?",
101
+ "newline (; or \\n)",
102
+ "postexe",
103
+ "alloca",
104
+ "dmethod",
105
+ "bmethod",
106
+ "memo",
107
+ "ifunc",
108
+ "dsym",
109
+ "attrasgn",
110
+ "last"
111
+ };
data/ext/trace_nums.o ADDED
Binary file
data/ext/trace_nums.so ADDED
Binary file
data/lib/linecache.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # $Id: linecache.rb 23 2008-01-15 22:42:39Z rockyb $
2
+ # $Id: linecache.rb 59 2008-01-28 15:35:34Z rockyb $
3
3
  #
4
4
  # Copyright (C) 2007, 2008 Rocky Bernstein <rockyb@rubyforge.net>
5
5
  #
@@ -50,74 +50,65 @@
50
50
  # same name.
51
51
  #
52
52
 
53
- require 'digest/sha1'
54
-
55
53
  # Defining SCRIPT_LINES__ causes Ruby to cache the lines of files
56
54
  # it reads. The key the setting of __FILE__ at the time when Ruby does
57
55
  # its read. LineCache keeps a separate copy of the lines elsewhere
58
- # and never destroys __SCRIPT_LINES
56
+ # and never destroys SCRIPT_LINES__
59
57
  SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
60
58
 
61
- # require "rubygems"
62
- # require "ruby-debug" ; Debugger.start
59
+ require 'digest/sha1'
60
+ require 'set'
61
+
62
+ begin require 'rubygems' rescue LoadError end
63
+ require 'tracelines'
64
+ # require 'ruby-debug' ; Debugger.start
63
65
 
64
66
  # = module LineCache
65
67
  # Module caching lines of a file
66
68
  module LineCache
67
- LineCacheInfo = Struct.new(:stat, :lines, :fullname, :sha1)
69
+ LineCacheInfo = Struct.new(:stat, :line_numbers, :lines, :path, :sha1) unless
70
+ defined?(LineCacheInfo)
68
71
 
69
- # Get line +line_number+ from file named +filename+. Return nil if
70
- # there was a problem. If a file named filename is not found, the
71
- # function will look for it in the $: path array.
72
- #
73
- # Examples:
74
- #
75
- # lines = LineCache::getline('/tmp/myfile.rb)
76
- # # Same as above
77
- # $: << '/tmp'
78
- # lines = Dir.chdir('/tmp') do
79
- # lines = LineCache::getlines ('myfile.rb')
80
- # end
72
+ # The file cache. The key is a name as would be given by Ruby for
73
+ # __FILE__. The value is a LineCacheInfo object.
74
+ @@file_cache = {}
75
+
76
+ # Maps a string filename (a String) to a key in @@file_cache (a
77
+ # String).
81
78
  #
82
- def getline(filename, line_number, reload_on_change=true)
83
- lines = getlines(filename, reload_on_change)
84
- if (1..lines.size) === line_number
85
- return lines[line_number-1]
86
- else
87
- return nil
88
- end
89
- end
90
-
91
- module_function :getline
92
-
93
- @@file_cache = {} # the cache
94
-
79
+ # One important use of @@file2file_remap is mapping the a full path
80
+ # of a file into the name stored in @@file_cache or given by Ruby's
81
+ # __FILE__. Applications such as those that get input from users,
82
+ # may want canonicalize a file name before looking it up. This map
83
+ # gives a way to do that.
84
+ #
85
+ # Another related use is when a template system is used. Here we'll
86
+ # probably want to remap not only the file name but also line
87
+ # ranges. Will probably use this for that, but I'm not sure.
88
+ @@file2file_remap = {}
89
+ @@file2file_remap_lines = {}
90
+
95
91
  # Clear the file cache entirely.
96
92
  def clear_file_cache()
97
93
  @@file_cache = {}
94
+ @@file2file_remap = {}
95
+ @@file2file_remap_lines = {}
98
96
  end
99
-
100
97
  module_function :clear_file_cache
101
98
 
102
- # Read lines of +filename+ and cache the results. However +filename+ was
103
- # previously cached use the results from the cache.
104
- def getlines(filename, reload_on_change=false)
105
- checkcache(filename) if reload_on_change
106
- if @@file_cache.member?(filename)
107
- return @@file_cache[filename].lines
108
- else
109
- return update_cache(filename, true)
110
- end
99
+ # Return an array of cached file names
100
+ def cached_files()
101
+ @@file_cache.keys
111
102
  end
112
-
113
- module_function :getlines
103
+ module_function :cached_files
114
104
 
115
105
  # Discard cache entries that are out of date. If +filename+ is +nil+
116
106
  # all entries in the file cache +@@file_cache+ are checked.
117
107
  # If we don't have stat information about a file which can happen
118
108
  # if the file was read from __SCRIPT_LINES but no corresponding file
119
- # is found, it will be kept.
120
- def checkcache(filename=nil)
109
+ # is found, it will be kept. Return a list of invalidated filenames.
110
+ # nil is returned if a filename was given but not found cached.
111
+ def checkcache(filename=nil, use_script_lines=false)
121
112
 
122
113
  if !filename
123
114
  filenames = @@file_cache.keys()
@@ -127,20 +118,21 @@ module LineCache
127
118
  return nil
128
119
  end
129
120
 
121
+ result = []
130
122
  for filename in filenames
131
123
  next unless @@file_cache.member?(filename)
132
- fullname = @@file_cache[filename].fullname
133
- if File.exist?(fullname)
124
+ path = @@file_cache[filename].path
125
+ if File.exist?(path)
134
126
  cache_info = @@file_cache[filename]
135
- stat = File.stat(fullname)
127
+ stat = File.stat(path)
136
128
  if stat &&
137
129
  (cache_info.size != stat.size or cache_info.mtime != stat.mtime)
138
- @@file_cache.delete(filename)
130
+ result << filename
131
+ update_cache(filename, use_script_lines)
139
132
  end
140
- else
141
- @@file_cache.delete(filename)
142
133
  end
143
134
  end
135
+ return result
144
136
  end
145
137
  module_function :checkcache
146
138
 
@@ -151,10 +143,10 @@ module LineCache
151
143
  if @@file_cache.member?(filename)
152
144
  checkcache(filename) if reload_on_change
153
145
  else
154
- return update_cache(filename, true)
146
+ update_cache(filename, true)
155
147
  end
156
148
  if @@file_cache.member?(filename)
157
- @@file_cache[filename].fullname
149
+ @@file_cache[filename].path
158
150
  else
159
151
  nil
160
152
  end
@@ -163,12 +155,90 @@ module LineCache
163
155
 
164
156
  # Return true if filename is cached
165
157
  def cached?(filename)
166
- @@file_cache.member?(filename)
158
+ @@file_cache.member?(unmap_file(filename))
167
159
  end
168
160
  module_function :cached?
161
+
162
+ def cached_script?(filename)
163
+ SCRIPT_LINES__.member?(unmap_file(filename))
164
+ end
165
+ module_function :cached_script?
169
166
 
167
+ def empty?(filename)
168
+ filename=unmap_file(filename)
169
+ @@file_cache[filename].lines.empty?
170
+ end
171
+ module_function :empty?
172
+
173
+ # Get line +line_number+ from file named +filename+. Return nil if
174
+ # there was a problem. If a file named filename is not found, the
175
+ # function will look for it in the $: path array.
176
+ #
177
+ # Examples:
178
+ #
179
+ # lines = LineCache::getline('/tmp/myfile.rb)
180
+ # # Same as above
181
+ # $: << '/tmp'
182
+ # lines = Dir.chdir('/tmp') do
183
+ # lines = LineCache::getlines ('myfile.rb')
184
+ # end
185
+ #
186
+ def getline(filename, line_number, reload_on_change=true)
187
+ filename = unmap_file(filename)
188
+ filename, line_number = unmap_file_line(filename, line_number)
189
+ lines = getlines(filename, reload_on_change)
190
+ if lines and (1..lines.size) === line_number
191
+ return lines[line_number-1]
192
+ else
193
+ return nil
194
+ end
195
+ end
196
+ module_function :getline
197
+
198
+ # Read lines of +filename+ and cache the results. However +filename+ was
199
+ # previously cached use the results from the cache. Return nil
200
+ # if we can't get lines
201
+ def getlines(filename, reload_on_change=false)
202
+ filename = unmap_file(filename)
203
+ checkcache(filename) if reload_on_change
204
+ if @@file_cache.member?(filename)
205
+ return @@file_cache[filename].lines
206
+ else
207
+ update_cache(filename, true)
208
+ return @@file_cache[filename].lines if @@file_cache.member?(filename)
209
+ end
210
+ end
211
+ module_function :getlines
212
+
213
+ # Return full filename path for filename
214
+ def path(filename)
215
+ filename = unmap_file(filename)
216
+ return nil unless @@file_cache.member?(filename)
217
+ @@file_cache[filename].path
218
+ end
219
+ module_function :path
220
+
221
+ def remap_file(from_file, to_file)
222
+ @@file2file_remap[to_file] = from_file
223
+ end
224
+ module_function :remap_file
225
+
226
+ def remap_file_lines(from_file, to_file, range, start)
227
+ range = (range..range) if range.is_a?(Fixnum)
228
+ to_file = from_file unless to_file
229
+ if @@file2file_remap_lines[to_file]
230
+ # FIXME: need to check for overwriting ranges: whether
231
+ # they intersect or one encompasses another.
232
+ @@file2file_remap_lines[to_file] << [from_file, range, start]
233
+ else
234
+ @@file2file_remap_lines[to_file] = [[from_file, range, start]]
235
+ end
236
+ end
237
+ module_function :remap_file_lines
238
+
170
239
  # Return SHA1 of filename.
171
240
  def sha1(filename)
241
+ filename = unmap_file(filename)
172
242
  return nil unless @@file_cache.member?(filename)
173
243
  return @@file_cache[filename].sha1.hexdigest if
174
244
  @@file_cache[filename].sha1
@@ -181,6 +251,14 @@ module LineCache
181
251
  end
182
252
  module_function :sha1
183
253
 
254
+ # Return the number of lines in filename
255
+ def size(filename)
256
+ filename = unmap_file(filename)
257
+ return nil unless @@file_cache.member?(filename)
258
+ @@file_cache[filename].lines.length
259
+ end
260
+ module_function :size
261
+
184
262
  # Return File.stat in the cache for filename.
185
263
  def stat(filename)
186
264
  return nil unless @@file_cache.member?(filename)
@@ -188,18 +266,56 @@ module LineCache
188
266
  end
189
267
  module_function :stat
190
268
 
191
- # Update a cache entry and return its list of lines. if something's
192
- # wrong, discard the cache entry, and return an empty list.
193
- # If use_script_lines is true, try to get the
269
+ # Return an Array of breakpoints in filename.
270
+ # The list will contain an entry for each distinct line event call
271
+ # so it is possible (and possibly useful) for a line number appear more
272
+ # than once.
273
+ def trace_line_numbers(filename, reload_on_change=false)
274
+ fullname = cache(filename, reload_on_change)
275
+ return nil unless fullname
276
+ e = @@file_cache[filename]
277
+ unless e.line_numbers
278
+ e.line_numbers =
279
+ TraceLineNumbers.lnums_for_str_array(e.lines)
280
+ e.line_numbers = false unless e.line_numbers
281
+ end
282
+ e.line_numbers
283
+ end
284
+ module_function :trace_line_numbers
285
+
286
+ def unmap_file(file)
287
+ @@file2file_remap[file] ? @@file2file_remap[file] : file
288
+ end
289
+ module_function :unmap_file
290
+
291
+ def unmap_file_line(file, line)
292
+ if @@file2file_remap_lines[file]
293
+ @@file2file_remap_lines[file].each do |from_file, range, start|
294
+ if range === line
295
+ from_file = from_file || file
296
+ return [from_file, start+line-range.begin]
297
+ end
298
+ end
299
+ end
300
+ return [file, line]
301
+ end
302
+ module_function :unmap_file_line
303
+
304
+ # Update a cache entry. If something's
305
+ # wrong, return nil. Return true if the cache was updated and false
306
+ # if not. If use_script_lines is true, use that as the source for the
307
+ # lines of the file
194
308
  def update_cache(filename, use_script_lines=false)
195
309
 
196
- return [] unless filename
310
+ return nil unless filename
197
311
 
198
312
  @@file_cache.delete(filename)
199
- fullname = File.expand_path(filename)
313
+ path = File.expand_path(filename)
200
314
 
201
315
  if use_script_lines
202
- [filename, fullname].each do |name|
316
+ list = [filename]
317
+ list << @@file2file_remap[path] if @@file2file_remap[path]
318
+ list.each do |name|
203
319
  if !SCRIPT_LINES__[name].nil? && SCRIPT_LINES__[name] != true
204
320
  begin
205
321
  stat = File.stat(name)
@@ -207,39 +323,39 @@ module LineCache
207
323
  stat = nil
208
324
  end
209
325
  lines = SCRIPT_LINES__[name]
210
- @@file_cache[filename] = LineCacheInfo.new(stat, lines, fullname, nil)
211
- return lines
326
+ @@file_cache[filename] = LineCacheInfo.new(stat, nil, lines, path, nil)
327
+ @@file2file_remap[path] = filename
328
+ return true
212
329
  end
213
330
  end
214
331
  end
215
332
 
216
- if File.exist?(fullname)
217
- stat = File.stat(fullname)
218
- else
219
- basename = File.basename(filename)
220
-
333
+ if File.exist?(path)
334
+ stat = File.stat(path)
335
+ elsif File.basename(filename) == filename
221
336
  # try looking through the search path.
222
337
  stat = nil
223
338
  for dirname in $:
224
- fullname = File.join(dirname, basename)
225
- if File.exist?(fullname)
226
- stat = File.stat(fullname)
339
+ path = File.join(dirname, filename)
340
+ if File.exist?(path)
341
+ stat = File.stat(path)
227
342
  break
228
343
  end
229
344
  end
230
- return [] unless stat
345
+ return false unless stat
231
346
  end
232
347
  begin
233
- fp = File.open(fullname, 'r')
348
+ fp = File.open(path, 'r')
234
349
  lines = fp.readlines()
235
350
  fp.close()
236
351
  rescue
237
- ## print '*** cannot open', fullname, ':', msg
238
- return []
352
+ ## print '*** cannot open', path, ':', msg
353
+ return nil
239
354
  end
240
- @@file_cache[filename] = LineCacheInfo.new(File.stat(fullname), lines,
241
- fullname, nil)
242
- return lines
355
+ @@file_cache[filename] = LineCacheInfo.new(File.stat(path), nil, lines,
356
+ path, nil)
357
+ @@file2file_remap[path] = filename
358
+ return true
243
359
  end
244
360
 
245
361
  module_function :update_cache
@@ -247,24 +363,37 @@ module LineCache
247
363
  end
248
364
 
249
365
  # example usage
250
- if __FILE__ == $0 or
251
- ($DEBUG and ['rcov'].include?(File.basename($0)))
366
+ if __FILE__ == $0
252
367
  def yes_no(var)
253
368
  return var ? "" : "not "
254
369
  end
255
370
 
256
371
  lines = LineCache::getlines(__FILE__)
257
- puts "#{__FILE__} has #{lines.size} lines"
372
+ puts "#{__FILE__} has #{LineCache.size(__FILE__)} lines"
258
373
  line = LineCache::getline(__FILE__, 6)
259
374
  puts "The 6th line is\n#{line}"
375
+ line = LineCache::remap_file(__FILE__, 'another_name')
376
+ puts LineCache::getline('another_name', 7)
377
+
378
+ puts("Files cached: #{LineCache::cached_files.inspect}")
260
379
  LineCache::update_cache(__FILE__)
261
380
  LineCache::checkcache(__FILE__)
381
+ puts "#{__FILE__} has #{LineCache::size(__FILE__)} lines"
382
+ puts "#{__FILE__} trace line numbers:\n" +
383
+ "#{LineCache::trace_line_numbers(__FILE__).to_a.sort.inspect}"
262
384
  puts("#{__FILE__} is %scached." %
263
385
  yes_no(LineCache::cached?(__FILE__)))
264
- LineCache::stat(__FILE__).inspect
386
+ puts LineCache::stat(__FILE__).inspect
387
+ puts "Full path: #{LineCache::path(__FILE__)}"
265
388
  LineCache::checkcache # Check all files in the cache
266
389
  LineCache::clear_file_cache
267
390
  puts("#{__FILE__} is now %scached." %
268
391
  yes_no(LineCache::cached?(__FILE__)))
269
-
392
+ digest = SCRIPT_LINES__.select{|k,v| k =~ /digest.rb$/}
393
+ puts digest.first[0] if digest
394
+ line = LineCache::getline(__FILE__, 7)
395
+ puts "The 7th line is\n#{line}"
396
+ LineCache::remap_file_lines(__FILE__, 'test2', (10..20), 6)
397
+ puts LineCache::getline('test2', 10)
398
+ puts "Remapped 10th line of test2 is\n#{line}"
270
399
  end