tddium-linecache19 0.5.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env rake
2
+ # -*- Ruby -*-
3
+ require 'rubygems'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require 'rake/testtask'
7
+ require 'rake/extensiontask'
8
+
9
+ Rake::ExtensionTask.new('trace_nums')
10
+
11
+ SO_NAME = "trace_nums.so"
12
+
13
+ # ------- Default Package ----------
14
+ PKG_VERSION = open(File.join(File.dirname(__FILE__), 'VERSION')) do
15
+ |f| f.readlines[0].chomp
16
+ end
17
+ PKG_NAME = 'linecache'
18
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
19
+ RUBY_FORGE_PROJECT = 'rocky-hacks'
20
+ RUBY_FORGE_USER = 'rockyb'
21
+
22
+ FILES = FileList[
23
+ 'AUTHORS',
24
+ 'COPYING',
25
+ 'ChangeLog',
26
+ 'NEWS',
27
+ 'README',
28
+ 'Rakefile',
29
+ 'VERSION',
30
+ 'ext/trace_nums.c',
31
+ 'ext/trace_nums.h',
32
+ 'ext/extconf.rb',
33
+ 'lib/*.rb',
34
+ 'test/*.rb',
35
+ 'test/data/*.rb',
36
+ 'test/short-file'
37
+ ]
38
+
39
+ desc "Test everything."
40
+ test_task = task :test => :lib do
41
+ Rake::TestTask.new(:test) do |t|
42
+ t.pattern = 'test/test-*.rb'
43
+ t.verbose = true
44
+ end
45
+ end
46
+
47
+ desc "Create the core ruby-debug shared library extension"
48
+ task :lib do
49
+ Dir.chdir("ext") do
50
+ system("#{Gem.ruby} extconf.rb && make")
51
+ end
52
+ end
53
+
54
+
55
+ desc "Test everything - same as test."
56
+ task :check => :test
57
+
58
+ desc "Create a GNU-style ChangeLog via svn2cl"
59
+ task :ChangeLog do
60
+ system("svn2cl --authors=svn2cl_usermap")
61
+ end
62
+
63
+ # Base GEM Specification
64
+ default_spec = Gem::Specification.new do |spec|
65
+ spec.name = "linecache"
66
+
67
+ spec.homepage = "http://rubyforge.org/projects/rocky-hacks/linecache"
68
+ spec.summary = "Read file with caching"
69
+ spec.description = <<-EOF
70
+ LineCache is a module for reading and caching lines. This may be useful for
71
+ example in a debugger where the same lines are shown many times.
72
+ EOF
73
+
74
+ spec.version = PKG_VERSION
75
+
76
+ spec.author = "R. Bernstein"
77
+ spec.email = "rockyb@rubyforge.net"
78
+ spec.platform = Gem::Platform::RUBY
79
+ spec.require_path = "lib"
80
+ spec.files = FILES.to_a
81
+ spec.extensions = ["ext/extconf.rb"]
82
+
83
+ spec.required_ruby_version = '>= 1.8.2'
84
+ spec.date = Time.now
85
+ spec.rubyforge_project = 'rocky-hacks'
86
+
87
+ # rdoc
88
+ spec.has_rdoc = true
89
+ spec.extra_rdoc_files = ['README', 'lib/linecache.rb', 'lib/tracelines.rb']
90
+
91
+ spec.test_files = FileList['test/*.rb']
92
+ end
93
+
94
+ # Rake task to build the default package
95
+ Rake::GemPackageTask.new(default_spec) do |pkg|
96
+ pkg.need_tar = true
97
+ end
98
+
99
+ task :default => [:test]
100
+
101
+ # Windows specification
102
+ win_spec = default_spec.clone
103
+ win_spec.extensions = []
104
+ ## win_spec.platform = Gem::Platform::WIN32 # deprecated
105
+ win_spec.platform = 'mswin32'
106
+ win_spec.files += ["lib/#{SO_NAME}"]
107
+
108
+ desc "Create Windows Gem"
109
+ task :win32_gem do
110
+ # Copy the win32 extension the top level directory.
111
+ current_dir = File.expand_path(File.dirname(__FILE__))
112
+ source = File.join(current_dir, "ext", "win32", SO_NAME)
113
+ target = File.join(current_dir, "lib", SO_NAME)
114
+ cp(source, target)
115
+
116
+ # Create the gem, then move it to pkg.
117
+ Gem::Builder.new(win_spec).build
118
+ gem_file = "#{win_spec.name}-#{win_spec.version}-#{win_spec.platform}.gem"
119
+ mv(gem_file, "pkg/#{gem_file}")
120
+
121
+ # Remove win extension from top level directory.
122
+ rm(target)
123
+ end
124
+
125
+ desc "Publish linecache to RubyForge."
126
+ task :publish do
127
+ require 'rake/contrib/sshpublisher'
128
+
129
+ # Get ruby-debug path.
130
+ ruby_debug_path = File.expand_path(File.dirname(__FILE__))
131
+
132
+ publisher = Rake::SshDirPublisher.new("rockyb@rubyforge.org",
133
+ "/var/www/gforge-projects/rocky-hacks/linecache", ruby_debug_path)
134
+ end
135
+
136
+ desc "Remove built files"
137
+ task :clean => [:clobber_package, :clobber_rdoc] do
138
+ cd "ext" do
139
+ if File.exists?("Makefile")
140
+ sh "make clean"
141
+ rm "Makefile"
142
+ end
143
+ derived_files = Dir.glob(".o") + Dir.glob("*.so")
144
+ rm derived_files unless derived_files.empty?
145
+ end
146
+ end
147
+
148
+ # --------- RDoc Documentation ------
149
+ desc "Generate rdoc documentation"
150
+ Rake::RDocTask.new("rdoc") do |rdoc|
151
+ rdoc.rdoc_dir = 'doc'
152
+ rdoc.title = "linecache"
153
+ # Show source inline with line numbers
154
+ rdoc.options << "--inline-source" << "--line-numbers"
155
+ # Make the readme file the start page for the generated html
156
+ rdoc.options << '--main' << 'README'
157
+ rdoc.rdoc_files.include('ext/**/*.c',
158
+ 'lib/*.rb',
159
+ 'README',
160
+ 'COPYING')
161
+ end
162
+
163
+ desc "Publish the release files to RubyForge."
164
+ task :rubyforge_upload do
165
+ `rubyforge login`
166
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} '#{PKG_NAME}-#{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.gem"
167
+ puts release_command
168
+ system(release_command)
169
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.1
@@ -0,0 +1,26 @@
1
+ require "mkmf"
2
+ require "ruby_core_source"
3
+
4
+ if RUBY_VERSION >= "1.8"
5
+ if RUBY_RELEASE_DATE < "2005-03-22"
6
+ STDERR.print("Ruby version is too old\n")
7
+ exit(1)
8
+ end
9
+ else
10
+ STDERR.print("Ruby version is too old\n")
11
+ exit(1)
12
+ end
13
+
14
+ hdrs = proc {
15
+ have_header("vm_core.h") and have_header("version.h")
16
+ }
17
+
18
+ dir_config("ruby")
19
+ if !Ruby_core_source::create_makefile_with_core(hdrs, "trace_nums19")
20
+ STDERR.print("Makefile creation failed\n")
21
+ STDERR.print("*************************************************************\n\n")
22
+ STDERR.print(" NOTE: For Ruby 1.9 installation instructions, please see:\n\n")
23
+ STDERR.print(" http://wiki.github.com/mark-moseley/ruby-debug\n\n")
24
+ STDERR.print("*************************************************************\n\n")
25
+ exit(1)
26
+ end
@@ -0,0 +1,105 @@
1
+ /*
2
+ Ruby 1.9 version: (7/20/2009, Mark Moseley, mark@fast-software.com)
3
+
4
+ Now works with Ruby-1.9.1. Tested with p129 and p243.
5
+
6
+ This does not (and can not) function identically to the 1.8 version.
7
+ Line numbers are ordered differently. But ruby-debug doesn't seem
8
+ to mind the difference.
9
+
10
+ Also, 1.9 does not number lines with a "begin" statement.
11
+
12
+ All this 1.9 version does is compile into bytecode, disassemble it
13
+ using rb_iseq_disasm(), and parse the text output. This isn't a
14
+ great solution; it will break if the disassembly format changes.
15
+ Walking the iseq tree and decoding each instruction is pretty hairy,
16
+ though, so until I have a really compelling reason to go that route,
17
+ I'll leave it at this.
18
+ */
19
+ #include <ruby.h>
20
+ #include <version.h>
21
+ #include <vm_core.h>
22
+ #include "trace_nums.h"
23
+
24
+ VALUE mTraceLineNumbers;
25
+
26
+ static inline const rb_data_type_t *
27
+ threadptr_data_type(void)
28
+ {
29
+ static const rb_data_type_t *thread_data_type;
30
+ if (!thread_data_type)
31
+ {
32
+ VALUE current_thread = rb_thread_current();
33
+ thread_data_type = RTYPEDDATA_TYPE(current_thread);
34
+ }
35
+ return thread_data_type;
36
+ }
37
+
38
+ #define ruby_threadptr_data_type *threadptr_data_type()
39
+ #define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
40
+
41
+ /* Return a list of trace hook line numbers for the string in Ruby source src*/
42
+ static VALUE
43
+ lnums_for_str(VALUE self, VALUE src)
44
+ {
45
+ VALUE result = rb_ary_new(); /* The returned array of line numbers. */
46
+ int len;
47
+ char *token;
48
+ char *disasm;
49
+ rb_thread_t *th;
50
+ VALUE iseqval;
51
+ VALUE disasm_val;
52
+
53
+ StringValue(src); /* Check that src is a string. */
54
+ th = GET_THREAD();
55
+
56
+ /* First compile to bytecode, using the method in eval_string_with_cref() in vm_eval.c */
57
+ th->parse_in_eval++;
58
+ th->mild_compile_error++;
59
+ iseqval = rb_iseq_compile(src, rb_str_new_cstr("(numbers_for_str)"), INT2FIX(1));
60
+ th->mild_compile_error--;
61
+ th->parse_in_eval--;
62
+
63
+ /* Disassemble the bytecode into text and parse into lines */
64
+ disasm_val = rb_iseq_disasm(iseqval);
65
+ if (disasm_val == Qnil)
66
+ return(result);
67
+
68
+ disasm = (char*)malloc(strlen(RSTRING_PTR(disasm_val))+1);
69
+ strcpy(disasm, RSTRING_PTR(disasm_val));
70
+
71
+ for (token = strtok(disasm, "\n"); token != NULL; token = strtok(NULL, "\n"))
72
+ {
73
+ /* look only for lines tracing RUBY_EVENT_LINE (1) */
74
+ if (strstr(token, "trace 1 ") == NULL)
75
+ continue;
76
+ len = strlen(token) - 1;
77
+ if (token[len] != ')')
78
+ continue;
79
+ len--;
80
+ if ((token[len] == '(') || (token[len] == ' '))
81
+ continue;
82
+
83
+ for (; len > 0; len--)
84
+ {
85
+ if (token[len] == ' ')
86
+ continue;
87
+ if ((token[len] >= '0') && (token[len] <= '9'))
88
+ continue;
89
+ if (token[len] == '(')
90
+ rb_ary_push(result, INT2NUM(atoi(token + len + 1))); /* trace found */
91
+
92
+ break;
93
+ }
94
+ }
95
+
96
+ free(disasm);
97
+ return result;
98
+ }
99
+
100
+ void Init_trace_nums19(void)
101
+ {
102
+ mTraceLineNumbers = rb_define_module("TraceLineNumbers");
103
+ rb_define_module_function(mTraceLineNumbers, "lnums_for_str",
104
+ lnums_for_str, 1);
105
+ }
@@ -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,412 @@
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
+ if "ruby19".respond_to?(:force_encoding)
337
+ lines.each{|l| l.force_encoding(Encoding.default_external) }
338
+ end
339
+ @@file_cache[filename] = LineCacheInfo.new(stat, nil, lines, path, nil)
340
+ @@file2file_remap[path] = filename
341
+ return true
342
+ end
343
+ end
344
+ end
345
+
346
+ if File.exist?(path)
347
+ stat = File.stat(path)
348
+ elsif File.basename(filename) == filename
349
+ # try looking through the search path.
350
+ stat = nil
351
+ for dirname in $:
352
+ path = File.join(dirname, filename)
353
+ if File.exist?(path)
354
+ stat = File.stat(path)
355
+ break
356
+ end
357
+ end
358
+ return false unless stat
359
+ end
360
+ begin
361
+ fp = File.open(path, 'r')
362
+ lines = fp.readlines()
363
+ fp.close()
364
+ rescue
365
+ ## print '*** cannot open', path, ':', msg
366
+ return nil
367
+ end
368
+ @@file_cache[filename] = LineCacheInfo.new(File.stat(path), nil, lines,
369
+ path, nil)
370
+ @@file2file_remap[path] = filename
371
+ return true
372
+ end
373
+
374
+ module_function :update_cache
375
+
376
+ end
377
+
378
+ # example usage
379
+ if __FILE__ == $0
380
+ def yes_no(var)
381
+ return var ? "" : "not "
382
+ end
383
+
384
+ lines = LineCache::getlines(__FILE__)
385
+ puts "#{__FILE__} has #{LineCache.size(__FILE__)} lines"
386
+ line = LineCache::getline(__FILE__, 6)
387
+ puts "The 6th line is\n#{line}"
388
+ line = LineCache::remap_file(__FILE__, 'another_name')
389
+ puts LineCache::getline('another_name', 7)
390
+
391
+ puts("Files cached: #{LineCache::cached_files.inspect}")
392
+ LineCache::update_cache(__FILE__)
393
+ LineCache::checkcache(__FILE__)
394
+ puts "#{__FILE__} has #{LineCache::size(__FILE__)} lines"
395
+ puts "#{__FILE__} trace line numbers:\n" +
396
+ "#{LineCache::trace_line_numbers(__FILE__).to_a.sort.inspect}"
397
+ puts("#{__FILE__} is %scached." %
398
+ yes_no(LineCache::cached?(__FILE__)))
399
+ puts LineCache::stat(__FILE__).inspect
400
+ puts "Full path: #{LineCache::path(__FILE__)}"
401
+ LineCache::checkcache # Check all files in the cache
402
+ LineCache::clear_file_cache
403
+ puts("#{__FILE__} is now %scached." %
404
+ yes_no(LineCache::cached?(__FILE__)))
405
+ digest = SCRIPT_LINES__.select{|k,v| k =~ /digest.rb$/}
406
+ puts digest.first[0] if digest
407
+ line = LineCache::getline(__FILE__, 7)
408
+ puts "The 7th line is\n#{line}"
409
+ LineCache::remap_file_lines(__FILE__, 'test2', (10..20), 6)
410
+ puts LineCache::getline('test2', 10)
411
+ puts "Remapped 10th line of test2 is\n#{line}"
412
+ end