linecache2 1.4.0

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