linecache2 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/NEWS ADDED
@@ -0,0 +1,63 @@
1
+ 1.4.1 2016-07-22
2
+
3
+ - Convert to pure Ruby 2.0
4
+ - Add ability to colorize source text, indexed
5
+ by a color "style". For now we just have
6
+ "dark" and "light".
7
+
8
+ tf-1.3 2015-03-08
9
+
10
+ - Remove require 'threadframe'
11
+
12
+ tf-1.3.1
13
+
14
+ - JRuby fix
15
+
16
+ tf-1.3 2016-03-83
17
+ - Remove require 'threadframe'
18
+
19
+ tf-1.2 2014-11-20
20
+
21
+ - Handle UTF-8, update rake task for this year's way to do the same thing
22
+
23
+ tf-1.0 2011-02-01
24
+
25
+ - Add syntax highlight caching
26
+
27
+ tf-0.45 2010-12-10 Phel. Mad. Release
28
+
29
+ - Work on iseq caching.
30
+ - Include "\n"'s in SHA1 calculations.
31
+ - Correct suspicious test task so it doesn't modify iteslf.
32
+
33
+ tf-0.44 2010-09-13
34
+
35
+ - First 1.9.2 release via rb-threadframe
36
+
37
+ 0.43 2008-06-12
38
+
39
+ - tolerance for finding windows extension in lib rather than ext.
40
+
41
+ 0.41
42
+ - add test/data/* to gem.
43
+
44
+ 0.4
45
+ - Credit Ryan Davis and ParseTree.
46
+
47
+ 0.3
48
+ - Add tracelines: get line numbers that can be stopped at.
49
+
50
+ - Add routines to allow line-number
51
+ remapping and filename remapping.
52
+
53
+ - Add access methods to get the number of lines in a file.
54
+
55
+ 0.2
56
+ - Make this work with ruby-debug-base. Add reload-on-change parameters.
57
+ add checkcache, cache, cached? sha1, and stat methods.
58
+
59
+ - Use SCRIPT_LINES__.
60
+
61
+ 0.1
62
+
63
+ - Initial release of LineCache, a module for reading and caching lines.
@@ -0,0 +1,28 @@
1
+ The LineCache module allows one to get any line from any file, caching
2
+ the lines and file information on first access to the file. Although
3
+ the file may be any file, the common use is when the file is a Ruby
4
+ script since parsing of the file is done to figure out where the
5
+ statement boundaries are, and we also cache syntax formatting.
6
+
7
+ The routines here may be is useful when a small random sets of lines
8
+ are read from a single file, in particular in a debugger to show
9
+ source lines.
10
+
11
+ *Example*:
12
+
13
+ ```ruby
14
+ require 'linecache'
15
+ lines = LineCache::getlines('/tmp/myruby.rb')
16
+ # The following lines have same effect as the above.
17
+ $: << '/tmp'
18
+ Dir.chdir('/tmp') {lines = LineCache::getlines('myruby.rb')
19
+
20
+ line = LineCache::getline('/tmp/myruby.rb', 6)
21
+ # Note lines[6] == line (if /tmp/myruby.rb has 6 lines)
22
+
23
+ LineCache::clear_file_cache
24
+ LineCache::clear_file_cache('/tmp/myruby.rb')
25
+ LineCache::update_cache # Check for modifications of all cached files.
26
+ ```
27
+
28
+ This code is for Ruby version 2 and greater.
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env rake
2
+ # -*- Ruby -*-
3
+ #
4
+ # For the `release` task
5
+ #
6
+ require 'bundler/gem_tasks'
7
+
8
+ require 'rake/testtask'
9
+ require 'fileutils'
10
+
11
+ ROOT_DIR = File.dirname(__FILE__)
12
+ require File.join %W(#{ROOT_DIR} lib linecache2)
13
+
14
+ Gemspec_path = 'linecache2.gemspec'
15
+ def gemspec
16
+ @gemspec ||= eval(File.read(Gemspec_path), binding,
17
+ 'lineche2.gemspec')
18
+ end
19
+
20
+ require 'rubygems/package_task'
21
+ desc "Build the gem"
22
+ task :package=>:gem
23
+ task :gem=>:gemspec do
24
+ Dir.chdir(ROOT_DIR) do
25
+ sh "gem build #{Gemspec_path}"
26
+ FileUtils.mkdir_p 'pkg'
27
+ FileUtils.mv("#{gemspec.file_name}", "pkg/")
28
+ end
29
+ end
30
+
31
+ desc "Install the gem locally"
32
+ task :install => :gem do
33
+ Dir.chdir(ROOT_DIR) do
34
+ sh %{gem install --local pkg/#{gemspec.file_name}}
35
+ end
36
+ end
37
+
38
+ desc "Test everything"
39
+ Rake::TestTask.new(:test) do |t|
40
+ t.libs << './lib'
41
+ t.pattern = 'test/test-*.rb'
42
+ t.options = '--verbose' if $VERBOSE
43
+ end
44
+ task :test => :lib
45
+
46
+ desc "same as test"
47
+ task :check => :test
48
+
49
+ desc "Create a GNU-style ChangeLog via svn2cl"
50
+ task :ChangeLog do
51
+ system('git log --pretty --numstat --summary | git2cl > ChangeLog')
52
+ end
53
+
54
+ task :default => [:test]
55
+
56
+ desc "Remove built files"
57
+ task :clean => [:clobber_package, :clobber_rdoc]
58
+
59
+ desc "Generate the gemspec"
60
+ task :generate do
61
+ puts gemspec.to_ruby
62
+ end
63
+
64
+ desc "Validate the gemspec"
65
+ task :gemspec do
66
+ gemspec.validate
67
+ end
68
+
69
+ # --------- RDoc Documentation ------
70
+ require 'rdoc/task'
71
+ desc "Generate rdoc documentation"
72
+ Rake::RDocTask.new("rdoc") do |rdoc|
73
+ rdoc.rdoc_dir = 'doc'
74
+ rdoc.title = "LineCache #{LineCache::VERSION} Documentation"
75
+
76
+ # Show source inline with line numbers
77
+ rdoc.options += %w(--inline-source --line-numbers)
78
+
79
+ # Make the README file the start page for the generated html
80
+ rdoc.options += %w(--main README)
81
+
82
+ rdoc.rdoc_files.include('lib/*.rb', 'README', 'COPYING')
83
+ end
84
+ desc "Same as rdoc"
85
+ task :doc => :rdoc
86
+
87
+ task :clobber_package do
88
+ FileUtils.rm_rf File.join(ROOT_DIR, 'pkg')
89
+ end
90
+
91
+ task :clobber_rdoc do
92
+ FileUtils.rm_rf File.join(ROOT_DIR, 'doc')
93
+ end
@@ -0,0 +1,566 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) 2007-2011, 2014-2016 Rocky Bernstein
3
+ # <rockyb@rubyforge.net>
4
+ #
5
+ # This program is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 2 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
+ # 02110-1301 USA.
19
+ #
20
+
21
+ # Author:: Rocky Bernstein (mailto:rockyb@rubyforge.net)
22
+ #
23
+ # = linecache
24
+ # A module to read and cache lines of a Ruby program.
25
+
26
+ # == SYNOPSIS
27
+ #
28
+ # The LineCache module allows one to get any line from any file,
29
+ # caching lines of the file on first access to the file. Although the
30
+ # file may be any file, the common use is when the file is a Ruby
31
+ # script since parsing of the file is done to figure out where the
32
+ # statement boundaries are.
33
+ #
34
+ # The routines here may be is useful when a small random sets of lines
35
+ # are read from a single file, in particular in a debugger to show
36
+ # source lines.
37
+ #
38
+ #
39
+ # require 'linecache2'
40
+ # lines = LineCache::getlines('/tmp/myruby.rb')
41
+ # # The following lines have same effect as the above.
42
+ # $: << '/tmp'
43
+ # Dir.chdir('/tmp') {lines = LineCache::getlines('myruby.rb')
44
+ #
45
+ # line = LineCache::getline('/tmp/myruby.rb', 6)
46
+ # # Note lines[6] == line (if /tmp/myruby.rb has 6 lines)
47
+ #
48
+ # LineCache::clear_file_cache
49
+ # LineCache::clear_file_cache('/tmp/myruby.rb')
50
+ # LineCache::update_cache # Check for modifications of all cached files.
51
+ #
52
+ # Some parts of the interface is derived from the Python module of the
53
+ # same name.
54
+ #
55
+
56
+ # Defining SCRIPT_LINES__ causes Ruby to cache the lines of files
57
+ # it reads. The key the setting of __FILE__ at the time when Ruby does
58
+ # its read. LineCache keeps a separate copy of the lines elsewhere
59
+ # and never destroys SCRIPT_LINES__
60
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
61
+
62
+ Encoding.default_external = Encoding::UTF_8
63
+ Encoding.default_internal = Encoding::UTF_8
64
+
65
+ require 'tempfile'
66
+ require 'digest/sha1'
67
+ require 'set'
68
+ require_relative 'tracelines'
69
+ require_relative 'linecache2/colors'
70
+
71
+ # = module LineCache
72
+ # A module to read and cache lines of a Ruby program.
73
+ module LineCache
74
+
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
+ @@iseq_cache = {}
82
+
83
+ # Used for CodeRay syntax highlighting
84
+ @@ruby_highlighter = nil
85
+
86
+ # Maps a string filename (a String) to a key in @@file_cache (a
87
+ # String).
88
+ #
89
+ # One important use of @@file2file_remap is mapping the a full path
90
+ # of a file into the name stored in @@file_cache or given by Ruby's
91
+ # __FILE__. Applications such as those that get input from users,
92
+ # may want canonicalize a file name before looking it up. This map
93
+ # gives a way to do that.
94
+ #
95
+ # Another related use is when a template system is used. Here we'll
96
+ # probably want to remap not only the file name but also line
97
+ # ranges. Will probably use this for that, but I'm not sure.
98
+ @@file2file_remap = {}
99
+ @@file2file_remap_lines = {}
100
+
101
+ @@iseq2file = {}
102
+
103
+ module_function
104
+
105
+ def remove_iseq_temps
106
+ @@iseq2file.values.each do |filename|
107
+ File.unlink(filename) if File.exist?(filename)
108
+ end
109
+ end
110
+ at_exit { remove_iseq_temps }
111
+
112
+
113
+ # Remove syntax-formatted lines in the cache. Use this
114
+ # when you change the CodeRay syntax or Token formatting
115
+ # and want to redo how files may have previously been
116
+ # syntax marked.
117
+ def clear_file_format_cache
118
+ @@file_cache.each_pair do |fname, cache_info|
119
+ cache_info.lines.each_pair do |format, lines|
120
+ next if :plain == format
121
+ @@file_cache[fname].lines[format] = nil
122
+ end
123
+ end
124
+ end
125
+
126
+ # Clear the file cache entirely.
127
+ def clear_file_cache(filename=nil)
128
+ if filename
129
+ if @@file_cache[filename]
130
+ @@file_cache.delete(filename)
131
+ end
132
+ else
133
+ @@file_cache = {}
134
+ @@file2file_remap = {}
135
+ @@file2file_remap_lines = {}
136
+ end
137
+ end
138
+
139
+ # Clear the iseq cache entirely.
140
+ def clear_iseq_cache()
141
+ @@iseq_cache = {}
142
+ end
143
+ module_function :clear_file_cache
144
+
145
+ # Return an array of cached file names
146
+ def cached_files()
147
+ @@file_cache.keys
148
+ end
149
+
150
+ # Discard cache entries that are out of date. If +filename+ is +nil+
151
+ # all entries in the file cache +@@file_cache+ are checked.
152
+ # If we don't have stat information about a file, which can happen
153
+ # if the file was read from __SCRIPT_LINES but no corresponding file
154
+ # is found, it will be kept. Return a list of invalidated filenames.
155
+ # nil is returned if a filename was given but not found cached.
156
+ def checkcache(filename=nil, opts={})
157
+
158
+ if !filename
159
+ filenames = @@file_cache.keys()
160
+ elsif @@file_cache.member?(filename)
161
+ filenames = [filename]
162
+ else
163
+ return nil
164
+ end
165
+
166
+ result = []
167
+ for filename in filenames
168
+ next unless @@file_cache.member?(filename)
169
+ path = @@file_cache[filename].path
170
+ if File.exist?(path)
171
+ cache_info = @@file_cache[filename].stat
172
+ stat = File.stat(path)
173
+ if cache_info
174
+ if stat &&
175
+ (cache_info.size != stat.size or cache_info.mtime != stat.mtime)
176
+ result << filename
177
+ update_cache(filename, opts)
178
+ end
179
+ else
180
+ result << filename
181
+ update_cache(filename, opts)
182
+ end
183
+ end
184
+ end
185
+ return result
186
+ end
187
+
188
+ # Cache iseq if it's not already cached.
189
+ def cache_iseq(iseq, opts={})
190
+ if !@@iseq_cache.member?(iseq)
191
+ update_iseq_cache(iseq, opts)
192
+ end
193
+ iseq
194
+ end
195
+
196
+ # Cache file name or iseq object if it's not already cached.
197
+ # Return the expanded filename for it in the cache if a filename,
198
+ # or the iseq, or nil if we can't find the file.
199
+ def cache(file_or_iseq, reload_on_change=false)
200
+ if file_or_iseq.kind_of?(String)
201
+ cache_file(file_or_iseq, reload_on_change)
202
+ else
203
+ cache_iseq(file_or_iseq)
204
+ end
205
+ end
206
+ module_function :cache
207
+
208
+ # Cache filename if it's not already cached.
209
+ # Return the expanded filename for it in the cache
210
+ # or nil if we can't find the file.
211
+ def cache_file(filename, reload_on_change=false, opts={})
212
+ if @@file_cache.member?(filename)
213
+ checkcache(filename) if reload_on_change
214
+ else
215
+ opts[:use_script_lines] = true
216
+ update_cache(filename, opts)
217
+ end
218
+ if @@file_cache.member?(filename)
219
+ @@file_cache[filename].path
220
+ else
221
+ nil
222
+ end
223
+ end
224
+
225
+ # Return true if file_or_iseq is cached
226
+ def cached?(file_or_iseq)
227
+ if file_or_iseq.kind_of?(String)
228
+ @@file_cache.member?(map_file(file_or_iseq))
229
+ else
230
+ cached_iseq?(file_or_iseq)
231
+ end
232
+ end
233
+ module_function :cached?
234
+
235
+ def cached_script?(filename)
236
+ SCRIPT_LINES__.member?(map_file(filename))
237
+ end
238
+ module_function :cached_script?
239
+
240
+ def empty?(filename)
241
+ filename=map_file(filename)
242
+ @@file_cache[filename].lines.empty?
243
+ end
244
+ module_function :empty?
245
+
246
+ # Get line +line_number+ from file named +filename+. Return nil if
247
+ # there was a problem. If a file named filename is not found, the
248
+ # function will look for it in the $: array.
249
+ #
250
+ # Examples:
251
+ #
252
+ # lines = LineCache::getline('/tmp/myfile.rb')
253
+ # # Same as above
254
+ # $: << '/tmp'
255
+ # lines = LineCache.getlines('myfile.rb')
256
+ #
257
+ def getline(file_or_iseq, line_number, opts={})
258
+ lines =
259
+ if file_or_iseq.kind_of?(String)
260
+ filename = map_file(file_or_iseq)
261
+ filename, line_number = map_file_line(filename, line_number)
262
+ getlines(filename, opts)
263
+ else
264
+ iseq_getlines(file_or_iseq)
265
+ end
266
+ if lines and (1..lines.size) === line_number
267
+ return lines[line_number-1]
268
+ else
269
+ return nil
270
+ end
271
+ end
272
+
273
+ # Read lines of +iseq+ and cache the results. However +iseq+ was
274
+ # previously cached use the results from the cache. Return nil
275
+ # if we can't get lines
276
+ def iseq_getlines(iseq, opts={})
277
+ return nil unless iseq.kind_of? RubyVM::InstructionSequence
278
+ format = opts[:output] || :plain
279
+ line_formats =
280
+ if @@iseq_cache.member?(iseq)
281
+ @@iseq_cache[iseq].lines
282
+ else
283
+ update_iseq_cache(iseq, opts)
284
+ if @@iseq_cache.member?(iseq)
285
+ @@iseq_cache[iseq].lines
286
+ else
287
+ nil
288
+ end
289
+ end
290
+ return nil unless line_formats
291
+ if format != :plain && !line_formats[format]
292
+ highlight_string(line_formats[:plain].join('')).split(/\n/)
293
+ else
294
+ line_formats[format]
295
+ end
296
+ end
297
+
298
+ # Read lines of +filename+ and cache the results. However +filename+ was
299
+ # previously cached use the results from the cache. Return nil
300
+ # if we can't get lines
301
+ def getlines(filename, opts={})
302
+ filename = map_file(filename)
303
+ checkcache(filename) if opts[:reload_on_change]
304
+ format = opts[:output] || :plain
305
+ style = opts[:style] || :light
306
+ if @@file_cache.member?(filename)
307
+ lines = @@file_cache[filename].lines
308
+ if opts[:output] && !lines[format]
309
+ lines[format] =
310
+ highlight_string(lines[:plain].join(''),
311
+ format, style.to_sym
312
+ ).split(/\n/)
313
+ end
314
+ return lines[format]
315
+ else
316
+ opts[:use_script_lines] = true
317
+ update_cache(filename, opts)
318
+ if @@file_cache.member?(filename)
319
+ return @@file_cache[filename].lines[format]
320
+ else
321
+ return nil
322
+ end
323
+ end
324
+ end
325
+
326
+ def highlight_string(string, output_type, style=:light)
327
+ # coderay just has one module variable that it uses in colorizing
328
+ # for terminals. No "style" parameters.. So we need to smash/set
329
+ # that variable before encoding.
330
+ CodeRay::Encoders::Terminal::TOKEN_COLORS.merge!(LineCache::ColorScheme[style]) if
331
+ LineCache::ColorScheme.member?(style.to_sym)
332
+ @@ruby_highlighter = CodeRay::Duo[:ruby, output_type]
333
+
334
+ @@ruby_highlighter.encode(string)
335
+ end
336
+
337
+ # Return full filename path for filename
338
+ def path(filename)
339
+ return unless filename.kind_of?(String)
340
+ filename = map_file(filename)
341
+ return nil unless @@file_cache.member?(filename)
342
+ @@file_cache[filename].path
343
+ end
344
+
345
+ def remap_file(to_file, from_file)
346
+ @@file2file_remap[to_file] = from_file
347
+ end
348
+
349
+ def remap_file_lines(from_file, to_file, range, start)
350
+ range = (range..range) if range.kind_of?(Fixnum)
351
+ to_file = from_file unless to_file
352
+ if @@file2file_remap_lines[to_file]
353
+ # FIXME: need to check for overwriting ranges: whether
354
+ # they intersect or one encompasses another.
355
+ @@file2file_remap_lines[to_file] << [from_file, range, start]
356
+ else
357
+ @@file2file_remap_lines[to_file] = [[from_file, range, start]]
358
+ end
359
+ end
360
+
361
+ # Return SHA1 of filename.
362
+ def sha1(filename)
363
+ filename = map_file(filename)
364
+ return nil unless @@file_cache.member?(filename)
365
+ return @@file_cache[filename].sha1.hexdigest if
366
+ @@file_cache[filename].sha1
367
+ sha1 = Digest::SHA1.new
368
+ @@file_cache[filename].lines[:plain].each do |line|
369
+ sha1 << line + "\n"
370
+ end
371
+ @@file_cache[filename].sha1 = sha1
372
+ sha1.hexdigest
373
+ end
374
+
375
+ # Return the number of lines in filename
376
+ def size(file_or_iseq)
377
+ cache(file_or_iseq)
378
+ if file_or_iseq.kind_of?(String)
379
+ file_or_iseq = map_file(file_or_iseq)
380
+ return nil unless @@file_cache.member?(file_or_iseq)
381
+ @@file_cache[file_or_iseq].lines[:plain].length
382
+ else
383
+ return nil unless @@iseq_cache.member?(file_or_iseq)
384
+ @@iseq_cache[file_or_iseq].lines.length
385
+ @@script_cache[file_or_iseq].lines[:plain].length
386
+ end
387
+ end
388
+
389
+ # Return File.stat in the cache for filename.
390
+ def stat(filename)
391
+ return nil unless @@file_cache.member?(filename)
392
+ @@file_cache[filename].stat
393
+ end
394
+ module_function :stat
395
+
396
+ # Return an Array of breakpoints in filename.
397
+ # The list will contain an entry for each distinct line event call
398
+ # so it is possible (and possibly useful) for a line number appear more
399
+ # than once.
400
+ def trace_line_numbers(filename, reload_on_change=false)
401
+ fullname = cache(filename, reload_on_change)
402
+ return nil unless fullname
403
+ e = @@file_cache[filename]
404
+ unless e.line_numbers
405
+ e.line_numbers =
406
+ TraceLineNumbers.lnums_for_str_array(e.lines[:plain])
407
+ e.line_numbers = false unless e.line_numbers
408
+ end
409
+ e.line_numbers
410
+ end
411
+
412
+ def map_file(file)
413
+ @@file2file_remap[file] ? @@file2file_remap[file] : file
414
+ end
415
+
416
+ def map_iseq(iseq)
417
+ if @@iseq2file[iseq]
418
+ @@iseq2file[iseq]
419
+ else
420
+ # Doc says there's new takes an optional string parameter
421
+ # But it doesn't work for me
422
+ sha1 = Digest::SHA1.new
423
+ string = iseq.eval_source
424
+ sha1 << iseq.eval_source
425
+ tempfile = Tempfile.new(["eval-#{sha1.hexdigest[0...7]}-", '.rb'])
426
+ tempfile.open.puts(string)
427
+ tempfile.close
428
+ @@iseq2file[iseq] = tempfile.path
429
+ tempfile.path
430
+ end
431
+ end
432
+
433
+ def map_file_line(file, line)
434
+ if @@file2file_remap_lines[file]
435
+ @@file2file_remap_lines[file].each do |from_file, range, start|
436
+ if range === line
437
+ from_file = from_file || file
438
+ return [from_file, start+line-range.begin]
439
+ end
440
+ end
441
+ end
442
+ return [map_file(file), line]
443
+ end
444
+ module_function :map_file_line
445
+
446
+ def iseq_is_eval?(iseq)
447
+ !!iseq.eval_source
448
+ end
449
+ module_function :iseq_is_eval?
450
+
451
+ # Update a cache entry. If something is wrong, return nil. Return
452
+ # true if the cache was updated and false if not.
453
+ def update_iseq_cache(iseq, opts)
454
+ return false unless iseq_is_eval?(iseq)
455
+ style = opts[:style] || :light
456
+ string = opts[:string] || iseq.eval_source
457
+ lines = {:plain => string.split(/\n/)}
458
+ lines[opts[:output]] = highlight_string(string, opts[:output],
459
+ style) if opts[:output]
460
+ @@iseq_cache[iseq] =
461
+ LineCacheInfo.new(nil, nil, lines, nil, opts[:sha1])
462
+ return true
463
+ end
464
+
465
+ # Update a cache entry. If something's wrong, return nil. Return
466
+ # true if the cache was updated and false if not. If
467
+ # opts[:use_script_lines] is true, use that as the source for the
468
+ # lines of the file
469
+ def update_cache(filename, opts={})
470
+
471
+ return nil unless filename
472
+
473
+ @@file_cache.delete(filename)
474
+ path = File.expand_path(filename)
475
+
476
+ if File.exist?(path)
477
+ stat = File.stat(path)
478
+ elsif File.basename(filename) == filename
479
+ # try looking through the search path.
480
+ stat = nil
481
+ for dirname in $:
482
+ path = File.join(dirname, filename)
483
+ if File.exist?(path)
484
+ stat = File.stat(path)
485
+ break
486
+ end
487
+ end
488
+ return false unless stat
489
+ end
490
+ begin
491
+ fp = File.open(path, 'r')
492
+ raw_string = fp.read
493
+ fp.rewind
494
+ lines = {:plain => fp.readlines}
495
+ fp.close()
496
+ style = opts[:style] || :light
497
+ lines[opts[:output]] =
498
+ highlight_string(raw_string, opts[:output],
499
+ style).split(/\n/) if opts[:output]
500
+ rescue
501
+ ## print '*** cannot open', path, ':', msg
502
+ return nil
503
+ end
504
+ @@file_cache[filename] = LineCacheInfo.new(File.stat(path), nil, lines,
505
+ path, nil)
506
+ @@file2file_remap[path] = filename
507
+ return true
508
+ end
509
+ end
510
+
511
+ # example usage
512
+ if __FILE__ == $0
513
+ def yes_no(var)
514
+ return var ? "" : "not "
515
+ end
516
+
517
+ lines = LineCache::getlines(__FILE__)
518
+ puts "#{__FILE__} has #{LineCache.size(__FILE__)} lines"
519
+ line = LineCache::getline(__FILE__, 6)
520
+ puts "The 6th line is\n#{line}"
521
+ line = LineCache::remap_file(__FILE__, 'another_name')
522
+ puts LineCache::getline('another_name', 7)
523
+
524
+ puts("Files cached: #{LineCache::cached_files.inspect}")
525
+ LineCache::update_cache(__FILE__)
526
+ LineCache::checkcache(__FILE__)
527
+ puts "#{__FILE__} has #{LineCache.size(__FILE__)} lines"
528
+ puts "#{__FILE__} trace line numbers:\n" +
529
+ "#{LineCache::trace_line_numbers(__FILE__).to_a.sort.inspect}"
530
+ puts("#{__FILE__} is %scached." %
531
+ yes_no(LineCache::cached?(__FILE__)))
532
+ puts LineCache::stat(__FILE__).inspect
533
+ puts "Full path: #{LineCache::path(__FILE__)}"
534
+ LineCache::checkcache # Check all files in the cache
535
+ LineCache::clear_file_cache
536
+ puts("#{__FILE__} is now %scached." %
537
+ yes_no(LineCache::cached?(__FILE__)))
538
+ digest = SCRIPT_LINES__.select{|k,v| k =~ /digest.rb$/}
539
+ puts digest.first[0] if digest
540
+ line = LineCache::getline(__FILE__, 7)
541
+ puts "The 7th line is\n#{line}"
542
+ LineCache::remap_file_lines(__FILE__, 'test2', (10..20), 6)
543
+ puts LineCache::getline('test2', 10)
544
+ puts "Remapped 10th line of test2 is\n#{line}"
545
+ # puts eval("x=1
546
+ # LineCache::getline(RubyVM::Frame.get.iseq, 1)")
547
+ # puts eval("x=2
548
+ # LineCache::getline(RubyVM::Frame.get.iseq, 2)")
549
+
550
+ # # Try new ANSI Terminal syntax coloring
551
+ LineCache::clear_file_cache(__FILE__)
552
+ LineCache::update_cache(__FILE__, :output => :term)
553
+ 50.upto(60) do |i|
554
+ line = LineCache::getline(__FILE__, i, :output => :term)
555
+ # puts line.inspect
556
+ puts line
557
+ end
558
+ puts '-' * 20
559
+ 50.upto(60) do |i|
560
+ line = LineCache::getline(__FILE__, i)
561
+ # puts line.inspect
562
+ puts line
563
+ end
564
+ ## to verify info from above.
565
+ # puts RubyVM::Frame.get.iseq.disasm
566
+ end