linecache 0.2 → 0.3

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/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