linecache19 0.5.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ const 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
+ };
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env ruby
2
+ # $Id$
3
+ #
4
+ # Copyright (C) 2007, 2008 Rocky Bernstein <rockyb@rubyforge.net>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
+ # 02110-1301 USA.
20
+ #
21
+
22
+ # Author:: Rocky Bernstein (mailto:rockyb@rubyforge.net)
23
+ #
24
+ # = linecache
25
+ # A module to read and cache lines of a Ruby program.
26
+ # == Version
27
+ # :include:VERSION
28
+
29
+ # == SYNOPSIS
30
+ #
31
+ # The LineCache module allows one to get any line from any file,
32
+ # caching lines of the file on first access to the file. Although the
33
+ # file may be any file, the common use is when the file is a Ruby
34
+ # script since parsing of the file is done to figure out where the
35
+ # statement boundaries are.
36
+ #
37
+ # The routines here may be is useful when a small random sets of lines
38
+ # are read from a single file, in particular in a debugger to show
39
+ # source lines.
40
+ #
41
+ #
42
+ # require 'linecache19'
43
+ # lines = LineCache::getlines('/tmp/myruby.rb')
44
+ # # The following lines have same effect as the above.
45
+ # $: << '/tmp'
46
+ # Dir.chdir('/tmp') {lines = LineCache::getlines('myruby.rb')
47
+ #
48
+ # line = LineCache::getline('/tmp/myruby.rb', 6)
49
+ # # Note lines[6] == line (if /tmp/myruby.rb has 6 lines)
50
+ #
51
+ # LineCache::clear_file_cache
52
+ # LineCache::clear_file_cache('/tmp/myruby.rb')
53
+ # LineCache::update_cache # Check for modifications of all cached files.
54
+ #
55
+ # Some parts of the interface is derived from the Python module of the
56
+ # same name.
57
+ #
58
+
59
+ # Defining SCRIPT_LINES__ causes Ruby to cache the lines of files
60
+ # it reads. The key the setting of __FILE__ at the time when Ruby does
61
+ # its read. LineCache keeps a separate copy of the lines elsewhere
62
+ # and never destroys SCRIPT_LINES__
63
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
64
+
65
+ require 'digest/sha1'
66
+ require 'set'
67
+
68
+ begin require 'rubygems' rescue LoadError end
69
+ require 'tracelines19'
70
+ # require 'ruby-debug' ; Debugger.start
71
+
72
+ # = module LineCache
73
+ # A module to read and cache lines of a Ruby program.
74
+ module LineCache
75
+ LineCacheInfo = Struct.new(:stat, :line_numbers, :lines, :path, :sha1) unless
76
+ defined?(LineCacheInfo)
77
+
78
+ # The file cache. The key is a name as would be given by Ruby for
79
+ # __FILE__. The value is a LineCacheInfo object.
80
+ @@file_cache = {}
81
+
82
+ # Maps a string filename (a String) to a key in @@file_cache (a
83
+ # String).
84
+ #
85
+ # One important use of @@file2file_remap is mapping the a full path
86
+ # of a file into the name stored in @@file_cache or given by Ruby's
87
+ # __FILE__. Applications such as those that get input from users,
88
+ # may want canonicalize a file name before looking it up. This map
89
+ # gives a way to do that.
90
+ #
91
+ # Another related use is when a template system is used. Here we'll
92
+ # probably want to remap not only the file name but also line
93
+ # ranges. Will probably use this for that, but I'm not sure.
94
+ @@file2file_remap = {}
95
+ @@file2file_remap_lines = {}
96
+
97
+ # Clear the file cache entirely.
98
+ def clear_file_cache()
99
+ @@file_cache = {}
100
+ @@file2file_remap = {}
101
+ @@file2file_remap_lines = {}
102
+ end
103
+ module_function :clear_file_cache
104
+
105
+ # Return an array of cached file names
106
+ def cached_files()
107
+ @@file_cache.keys
108
+ end
109
+ module_function :cached_files
110
+
111
+ # Discard cache entries that are out of date. If +filename+ is +nil+
112
+ # all entries in the file cache +@@file_cache+ are checked.
113
+ # If we don't have stat information about a file, which can happen
114
+ # if the file was read from SCRIPT_LINES__ but no corresponding file
115
+ # is found, it will be kept. Return a list of invalidated filenames.
116
+ # nil is returned if a filename was given but not found cached.
117
+ def checkcache(filename=nil, use_script_lines=false)
118
+
119
+ if !filename
120
+ filenames = @@file_cache.keys()
121
+ elsif @@file_cache.member?(filename)
122
+ filenames = [filename]
123
+ else
124
+ return nil
125
+ end
126
+
127
+ result = []
128
+ for filename in filenames
129
+ next unless @@file_cache.member?(filename)
130
+ path = @@file_cache[filename].path
131
+ if File.exist?(path)
132
+ cache_info = @@file_cache[filename].stat
133
+ stat = File.stat(path)
134
+ if stat &&
135
+ (cache_info.size != stat.size or cache_info.mtime != stat.mtime)
136
+ result << filename
137
+ update_cache(filename, use_script_lines)
138
+ end
139
+ end
140
+ end
141
+ return result
142
+ end
143
+ module_function :checkcache
144
+
145
+ # Cache filename if it's not already cached.
146
+ # Return the expanded filename for it in the cache
147
+ # or nil if we can't find the file.
148
+ def cache(filename, reload_on_change=false)
149
+ if @@file_cache.member?(filename)
150
+ checkcache(filename) if reload_on_change
151
+ else
152
+ update_cache(filename, true)
153
+ end
154
+ if @@file_cache.member?(filename)
155
+ @@file_cache[filename].path
156
+ else
157
+ nil
158
+ end
159
+ end
160
+ module_function :cache
161
+
162
+ # Return true if filename is cached
163
+ def cached?(filename)
164
+ @@file_cache.member?(unmap_file(filename))
165
+ end
166
+ module_function :cached?
167
+
168
+ def cached_script?(filename)
169
+ # In 1.8.6, the SCRIPT_LINES__ filename key can be unqualified
170
+ # In 1.9.1 it's the fully qualified name
171
+ if RUBY_VERSION < "1.9"
172
+ SCRIPT_LINES__.member?(unmap_file(filename))
173
+ else
174
+ SCRIPT_LINES__.member?(File.expand_path(unmap_file(filename)))
175
+ end
176
+ end
177
+ module_function :cached_script?
178
+
179
+ def empty?(filename)
180
+ filename=unmap_file(filename)
181
+ @@file_cache[filename].lines.empty?
182
+ end
183
+ module_function :empty?
184
+
185
+ # Get line +line_number+ from file named +filename+. Return nil if
186
+ # there was a problem. If a file named filename is not found, the
187
+ # function will look for it in the $: array.
188
+ #
189
+ # Examples:
190
+ #
191
+ # lines = LineCache::getline('/tmp/myfile.rb')
192
+ # # Same as above
193
+ # $: << '/tmp'
194
+ # lines = LineCache.getlines('myfile.rb')
195
+ #
196
+ def getline(filename, line_number, reload_on_change=true)
197
+ filename = unmap_file(filename)
198
+ filename, line_number = unmap_file_line(filename, line_number)
199
+ lines = getlines(filename, reload_on_change)
200
+ if lines and (1..lines.size) === line_number
201
+ return lines[line_number-1]
202
+ else
203
+ return nil
204
+ end
205
+ end
206
+ module_function :getline
207
+
208
+ # Read lines of +filename+ and cache the results. However +filename+ was
209
+ # previously cached use the results from the cache. Return nil
210
+ # if we can't get lines
211
+ def getlines(filename, reload_on_change=false)
212
+ filename = unmap_file(filename)
213
+ checkcache(filename) if reload_on_change
214
+ if @@file_cache.member?(filename)
215
+ return @@file_cache[filename].lines
216
+ else
217
+ update_cache(filename, true)
218
+ return @@file_cache[filename].lines if @@file_cache.member?(filename)
219
+ end
220
+ end
221
+ module_function :getlines
222
+
223
+ # Return full filename path for filename
224
+ def path(filename)
225
+ filename = unmap_file(filename)
226
+ return nil unless @@file_cache.member?(filename)
227
+ @@file_cache[filename].path
228
+ end
229
+ module_function :path
230
+
231
+ def remap_file(from_file, to_file)
232
+ @@file2file_remap[to_file] = from_file
233
+ end
234
+ module_function :remap_file
235
+
236
+ def remap_file_lines(from_file, to_file, range, start)
237
+ range = (range..range) if range.is_a?(Fixnum)
238
+ to_file = from_file unless to_file
239
+ if @@file2file_remap_lines[to_file]
240
+ # FIXME: need to check for overwriting ranges: whether
241
+ # they intersect or one encompasses another.
242
+ @@file2file_remap_lines[to_file] << [from_file, range, start]
243
+ else
244
+ @@file2file_remap_lines[to_file] = [[from_file, range, start]]
245
+ end
246
+ end
247
+ module_function :remap_file_lines
248
+
249
+ # Return SHA1 of filename.
250
+ def sha1(filename)
251
+ filename = unmap_file(filename)
252
+ return nil unless @@file_cache.member?(filename)
253
+ return @@file_cache[filename].sha1.hexdigest if
254
+ @@file_cache[filename].sha1
255
+ sha1 = Digest::SHA1.new
256
+ @@file_cache[filename].lines.each do |line|
257
+ sha1 << line
258
+ end
259
+ @@file_cache[filename].sha1 = sha1
260
+ sha1.hexdigest
261
+ end
262
+ module_function :sha1
263
+
264
+ # Return the number of lines in filename
265
+ def size(filename)
266
+ filename = unmap_file(filename)
267
+ return nil unless @@file_cache.member?(filename)
268
+ @@file_cache[filename].lines.length
269
+ end
270
+ module_function :size
271
+
272
+ # Return File.stat in the cache for filename.
273
+ def stat(filename)
274
+ return nil unless @@file_cache.member?(filename)
275
+ @@file_cache[filename].stat
276
+ end
277
+ module_function :stat
278
+
279
+ # Return an Array of breakpoints in filename.
280
+ # The list will contain an entry for each distinct line event call
281
+ # so it is possible (and possibly useful) for a line number appear more
282
+ # than once.
283
+ def trace_line_numbers(filename, reload_on_change=false)
284
+ fullname = cache(filename, reload_on_change)
285
+ return nil unless fullname
286
+ e = @@file_cache[filename]
287
+ unless e.line_numbers
288
+ e.line_numbers =
289
+ TraceLineNumbers.lnums_for_str_array(e.lines)
290
+ e.line_numbers = false unless e.line_numbers
291
+ end
292
+ e.line_numbers
293
+ end
294
+ module_function :trace_line_numbers
295
+
296
+ def unmap_file(file)
297
+ @@file2file_remap[file] ? @@file2file_remap[file] : file
298
+ end
299
+ module_function :unmap_file
300
+
301
+ def unmap_file_line(file, line)
302
+ if @@file2file_remap_lines[file]
303
+ @@file2file_remap_lines[file].each do |from_file, range, start|
304
+ if range === line
305
+ from_file = from_file || file
306
+ return [from_file, start+line-range.begin]
307
+ end
308
+ end
309
+ end
310
+ return [file, line]
311
+ end
312
+ module_function :unmap_file_line
313
+
314
+ # Update a cache entry. If something's
315
+ # wrong, return nil. Return true if the cache was updated and false
316
+ # if not. If use_script_lines is true, use that as the source for the
317
+ # lines of the file
318
+ def update_cache(filename, use_script_lines=false)
319
+
320
+ return nil unless filename
321
+
322
+ @@file_cache.delete(filename)
323
+ path = File.expand_path(filename)
324
+
325
+ if use_script_lines
326
+ list = [filename]
327
+ list << @@file2file_remap[path] if @@file2file_remap[path]
328
+ list.each do |name|
329
+ if !SCRIPT_LINES__[name].nil? && SCRIPT_LINES__[name] != true
330
+ begin
331
+ stat = File.stat(name)
332
+ rescue
333
+ stat = nil
334
+ end
335
+ lines = SCRIPT_LINES__[name]
336
+ @@file_cache[filename] = LineCacheInfo.new(stat, nil, lines, path, nil)
337
+ @@file2file_remap[path] = filename
338
+ return true
339
+ end
340
+ end
341
+ end
342
+
343
+ if File.exist?(path)
344
+ stat = File.stat(path)
345
+ elsif File.basename(filename) == filename
346
+ # try looking through the search path.
347
+ stat = nil
348
+ for dirname in $:
349
+ path = File.join(dirname, filename)
350
+ if File.exist?(path)
351
+ stat = File.stat(path)
352
+ break
353
+ end
354
+ end
355
+ return false unless stat
356
+ end
357
+ begin
358
+ fp = File.open(path, 'r')
359
+ lines = fp.readlines()
360
+ fp.close()
361
+ rescue
362
+ ## print '*** cannot open', path, ':', msg
363
+ return nil
364
+ end
365
+ @@file_cache[filename] = LineCacheInfo.new(File.stat(path), nil, lines,
366
+ path, nil)
367
+ @@file2file_remap[path] = filename
368
+ return true
369
+ end
370
+
371
+ module_function :update_cache
372
+
373
+ end
374
+
375
+ # example usage
376
+ if __FILE__ == $0
377
+ def yes_no(var)
378
+ return var ? "" : "not "
379
+ end
380
+
381
+ lines = LineCache::getlines(__FILE__)
382
+ puts "#{__FILE__} has #{LineCache.size(__FILE__)} lines"
383
+ line = LineCache::getline(__FILE__, 6)
384
+ puts "The 6th line is\n#{line}"
385
+ line = LineCache::remap_file(__FILE__, 'another_name')
386
+ puts LineCache::getline('another_name', 7)
387
+
388
+ puts("Files cached: #{LineCache::cached_files.inspect}")
389
+ LineCache::update_cache(__FILE__)
390
+ LineCache::checkcache(__FILE__)
391
+ puts "#{__FILE__} has #{LineCache::size(__FILE__)} lines"
392
+ puts "#{__FILE__} trace line numbers:\n" +
393
+ "#{LineCache::trace_line_numbers(__FILE__).to_a.sort.inspect}"
394
+ puts("#{__FILE__} is %scached." %
395
+ yes_no(LineCache::cached?(__FILE__)))
396
+ puts LineCache::stat(__FILE__).inspect
397
+ puts "Full path: #{LineCache::path(__FILE__)}"
398
+ LineCache::checkcache # Check all files in the cache
399
+ LineCache::clear_file_cache
400
+ puts("#{__FILE__} is now %scached." %
401
+ yes_no(LineCache::cached?(__FILE__)))
402
+ digest = SCRIPT_LINES__.select{|k,v| k =~ /digest.rb$/}
403
+ puts digest.first[0] if digest
404
+ line = LineCache::getline(__FILE__, 7)
405
+ puts "The 7th line is\n#{line}"
406
+ LineCache::remap_file_lines(__FILE__, 'test2', (10..20), 6)
407
+ puts LineCache::getline('test2', 10)
408
+ puts "Remapped 10th line of test2 is\n#{line}"
409
+ end