starscope 1.5.3 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,41 +1,43 @@
1
- module Starscope::Exportable
2
- CTAGS_DEFAULT_PATH = 'tags'.freeze
3
- CSCOPE_DEFAULT_PATH = 'cscope.out'.freeze
4
-
5
- class UnknownExportFormatError < StandardError; end
6
-
7
- def export(format, path = nil)
8
- case format
9
- when :ctags
10
- path ||= CTAGS_DEFAULT_PATH
11
- when :cscope
12
- path ||= CSCOPE_DEFAULT_PATH
13
- else
14
- raise UnknownExportFormatError
15
- end
1
+ module Starscope
2
+ module Exportable
3
+ CTAGS_DEFAULT_PATH = 'tags'.freeze
4
+ CSCOPE_DEFAULT_PATH = 'cscope.out'.freeze
5
+
6
+ class UnknownExportFormatError < StandardError; end
7
+
8
+ def export(format, path = nil)
9
+ case format
10
+ when :ctags
11
+ path ||= CTAGS_DEFAULT_PATH
12
+ when :cscope
13
+ path ||= CSCOPE_DEFAULT_PATH
14
+ else
15
+ raise UnknownExportFormatError
16
+ end
16
17
 
17
- @output.normal("Exporting to '#{path}' in format '#{format}'...")
18
- File.open(path, 'w') do |file|
19
- export_to(format, file)
18
+ @output.normal("Exporting to '#{path}' in format '#{format}'...")
19
+ path_prefix = Pathname.getwd.relative_path_from(Pathname.new(path).dirname.expand_path)
20
+ File.open(path, 'w') do |file|
21
+ export_to(format, file, path_prefix)
22
+ end
23
+ @output.normal('Export complete.')
20
24
  end
21
- @output.normal('Export complete.')
22
- end
23
25
 
24
- def export_to(format, io)
25
- case format
26
- when :ctags
27
- export_ctags(io)
28
- when :cscope
29
- export_cscope(io)
30
- else
31
- raise UnknownExportFormatError
26
+ def export_to(format, io, path_prefix)
27
+ case format
28
+ when :ctags
29
+ export_ctags(io, path_prefix)
30
+ when :cscope
31
+ export_cscope(io, path_prefix)
32
+ else
33
+ raise UnknownExportFormatError
34
+ end
32
35
  end
33
- end
34
36
 
35
- private
37
+ private
36
38
 
37
- def export_ctags(file)
38
- file.puts <<END
39
+ def export_ctags(file, path_prefix)
40
+ file.puts <<END
39
41
  !_TAG_FILE_FORMAT 2 /extended format/
40
42
  !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
41
43
  !_TAG_PROGRAM_AUTHOR Evan Huus /eapache@gmail.com/
@@ -43,240 +45,242 @@ module Starscope::Exportable
43
45
  !_TAG_PROGRAM_URL https://github.com/eapache/starscope //
44
46
  !_TAG_PROGRAM_VERSION #{Starscope::VERSION} //
45
47
  END
46
- defs = (@tables[:defs] || {}).sort_by { |x| x[:name][-1].to_s }
47
- defs.each do |record|
48
- file.puts ctag_line(record, @meta[:files][record[:file]])
48
+ defs = (@tables[:defs] || {}).sort_by { |x| x[:name][-1].to_s }
49
+ defs.each do |record|
50
+ file.puts ctag_line(record, @meta[:files][record[:file]], path_prefix)
51
+ end
49
52
  end
50
- end
51
53
 
52
- def ctag_line(rec, file)
53
- line = line_for_record(rec).gsub('/', '\/')
54
- ret = "#{rec[:name][-1]}\t#{rec[:file]}\t/^#{line}$/"
54
+ def ctag_line(rec, file, path_prefix)
55
+ line = line_for_record(rec).gsub('/', '\/')
56
+ path = File.join(path_prefix, rec[:file])
57
+ ret = "#{rec[:name][-1]}\t#{path}\t/^#{line}$/"
55
58
 
56
- ext = ctag_ext_tags(rec, file)
57
- unless ext.empty?
58
- ret << ';"'
59
- ext.sort.each do |k, v|
60
- ret << "\t#{k}:#{v}"
59
+ ext = ctag_ext_tags(rec, file)
60
+ unless ext.empty?
61
+ ret << ';"'
62
+ ext.sort.each do |k, v|
63
+ ret << "\t#{k}:#{v}"
64
+ end
61
65
  end
62
- end
63
66
 
64
- ret
65
- end
67
+ ret
68
+ end
66
69
 
67
- def ctag_ext_tags(rec, file)
68
- tag = {}
70
+ def ctag_ext_tags(rec, file)
71
+ tag = {}
69
72
 
70
- # these extensions are documented at http://ctags.sourceforge.net/FORMAT
71
- case rec[:type]
72
- when :func
73
- tag['kind'] = 'f'
74
- when :module, :class
75
- tag['kind'] = 'c'
76
- end
73
+ # these extensions are documented at http://ctags.sourceforge.net/FORMAT
74
+ case rec[:type]
75
+ when :func
76
+ tag['kind'] = 'f'
77
+ when :module, :class
78
+ tag['kind'] = 'c'
79
+ end
77
80
 
78
- tag['language'] = file[:lang]
81
+ tag['language'] = file[:lang]
79
82
 
80
- tag
81
- end
83
+ tag
84
+ end
82
85
 
83
- # cscope has this funky issue where it refuses to recognize function calls that
84
- # happen outside of a function definition - this isn't an issue in C, where all
85
- # calls must occur in a function, but in ruby et al. it is perfectly legal to
86
- # write normal code outside the "scope" of a function definition - we insert a
87
- # fake shim "global" function everywhere we can to work around this
88
- CSCOPE_GLOBAL_HACK_START = " \n\t$-\n".freeze
89
- CSCOPE_GLOBAL_HACK_STOP = " \n\t}\n".freeze
90
-
91
- # ftp://ftp.eeng.dcu.ie/pub/ee454/cygwin/usr/share/doc/mlcscope-14.1.8/html/cscope.html
92
- def export_cscope(file)
93
- buf = ''
94
- files = []
95
- db_by_line.each do |filename, lines|
96
- next if lines.empty?
97
-
98
- buf << "\t@#{filename}\n\n"
99
- buf << "0 #{CSCOPE_GLOBAL_HACK_START}\n"
100
- files << filename
101
- func_count = 0
102
-
103
- lines.sort.each do |line_no, records|
104
- line = line_for_record(records.first)
105
- toks = tokenize_line(line, records)
106
- next if toks.empty?
107
-
108
- prev = 0
109
- buf << line_no.to_s << ' '
110
- toks.each do |offset, record|
111
- next if offset < prev # this probably indicates an extractor bug
112
-
113
- # Don't export nested functions, cscope barfs on them since C doesn't
114
- # have them at all. Skipping tokens is easy; since prev isn't updated
115
- # they get turned into plain text automatically.
116
- if record[:type] == :func
117
- case record[:tbl]
118
- when :defs
119
- func_count += 1
120
- next unless func_count == 1
121
- when :end
122
- func_count -= 1
123
- next unless func_count == 0
86
+ # cscope has this funky issue where it refuses to recognize function calls that
87
+ # happen outside of a function definition - this isn't an issue in C, where all
88
+ # calls must occur in a function, but in ruby et al. it is perfectly legal to
89
+ # write normal code outside the "scope" of a function definition - we insert a
90
+ # fake shim "global" function everywhere we can to work around this
91
+ CSCOPE_GLOBAL_HACK_START = " \n\t$-\n".freeze
92
+ CSCOPE_GLOBAL_HACK_STOP = " \n\t}\n".freeze
93
+
94
+ # ftp://ftp.eeng.dcu.ie/pub/ee454/cygwin/usr/share/doc/mlcscope-14.1.8/html/cscope.html
95
+ def export_cscope(file, _path_prefix)
96
+ buf = ''
97
+ files = []
98
+ db_by_line.each do |filename, lines|
99
+ next if lines.empty?
100
+
101
+ buf << "\t@#{filename}\n\n"
102
+ buf << "0 #{CSCOPE_GLOBAL_HACK_START}\n"
103
+ files << filename
104
+ func_count = 0
105
+
106
+ lines.sort.each do |line_no, records|
107
+ line = line_for_record(records.first)
108
+ toks = tokenize_line(line, records)
109
+ next if toks.empty?
110
+
111
+ prev = 0
112
+ buf << line_no.to_s << ' '
113
+ toks.each do |offset, record|
114
+ next if offset < prev # this probably indicates an extractor bug
115
+
116
+ # Don't export nested functions, cscope barfs on them since C doesn't
117
+ # have them at all. Skipping tokens is easy; since prev isn't updated
118
+ # they get turned into plain text automatically.
119
+ if record[:type] == :func
120
+ case record[:tbl]
121
+ when :defs
122
+ func_count += 1
123
+ next unless func_count == 1
124
+ when :end
125
+ func_count -= 1
126
+ next unless func_count == 0
127
+ end
124
128
  end
125
- end
126
129
 
127
- buf << cscope_output(line, prev, offset, record)
128
- prev = offset + record[:key].length
130
+ buf << cscope_output(line, prev, offset, record)
131
+ prev = offset + record[:key].length
132
+ end
133
+ buf << cscope_plaintext(line, prev, line.length) << "\n\n"
129
134
  end
130
- buf << cscope_plaintext(line, prev, line.length) << "\n\n"
131
135
  end
132
- end
133
136
 
134
- buf << "\t@\n"
137
+ buf << "\t@\n"
135
138
 
136
- header = "cscope 15 #{Dir.pwd} -c "
137
- offset = format("%010d\n", header.length + 11 + buf.bytes.count)
139
+ header = "cscope 15 #{Dir.pwd} -c "
140
+ offset = format("%010d\n", header.length + 11 + buf.bytes.count)
138
141
 
139
- file.print(header)
140
- file.print(offset)
141
- file.print(buf)
142
+ file.print(header)
143
+ file.print(offset)
144
+ file.print(buf)
142
145
 
143
- file.print("#{@meta[:paths].length}\n")
144
- @meta[:paths].each { |p| file.print("#{p}\n") }
145
- file.print("0\n")
146
- file.print("#{files.length}\n")
147
- buf = ''
148
- files.each { |f| buf << f + "\n" }
149
- file.print("#{buf.length}\n#{buf}")
150
- end
146
+ file.print("#{@meta[:paths].length}\n")
147
+ @meta[:paths].each { |p| file.print("#{p}\n") }
148
+ file.print("0\n")
149
+ file.print("#{files.length}\n")
150
+ buf = ''
151
+ files.each { |f| buf << f + "\n" }
152
+ file.print("#{buf.length}\n#{buf}")
153
+ end
151
154
 
152
- def db_by_line
153
- db = {}
154
- @tables.each do |tbl, records|
155
- records.each do |record|
156
- next unless record[:line_no]
157
- record[:tbl] = tbl
158
- db[record[:file]] ||= {}
159
- db[record[:file]][record[:line_no]] ||= []
160
- db[record[:file]][record[:line_no]] << record
155
+ def db_by_line
156
+ db = {}
157
+ @tables.each do |tbl, records|
158
+ records.each do |record|
159
+ next unless record[:line_no]
160
+ record[:tbl] = tbl
161
+ db[record[:file]] ||= {}
162
+ db[record[:file]][record[:line_no]] ||= []
163
+ db[record[:file]][record[:line_no]] << record
164
+ end
161
165
  end
166
+ db
162
167
  end
163
- db
164
- end
165
168
 
166
- def tokenize_line(line, records)
167
- toks = {}
169
+ def tokenize_line(line, records)
170
+ toks = {}
168
171
 
169
- records.each do |record|
170
- key = record[:name][-1].to_s
172
+ records.each do |record|
173
+ key = record[:name][-1].to_s
171
174
 
172
- # use the column if we have it, otherwise fall back to scanning
173
- index = record[:col] || line.index(key)
175
+ # use the column if we have it, otherwise fall back to scanning
176
+ index = record[:col] || line.index(key)
174
177
 
175
- while index && !valid_index?(line, index, key)
176
- index = line.index(key, index + 1)
177
- end
178
+ while index && !valid_index?(line, index, key)
179
+ index = line.index(key, index + 1)
180
+ end
178
181
 
179
- next if index.nil?
182
+ next if index.nil?
180
183
 
181
- # Strip trailing non-word characters, otherwise cscope barfs on
182
- # function names like `include?`
183
- if key =~ /^\W*$/
184
- next unless [:defs, :end].include?(record[:tbl])
185
- else
186
- key.sub!(/\W+$/, '')
184
+ # Strip trailing non-word characters, otherwise cscope barfs on
185
+ # function names like `include?`
186
+ if key =~ /^\W*$/
187
+ next unless [:defs, :end].include?(record[:tbl])
188
+ else
189
+ key.sub!(/\W+$/, '')
190
+ end
191
+
192
+ record[:key] = key
193
+ toks[index] = record
187
194
  end
188
195
 
189
- record[:key] = key
190
- toks[index] = record
196
+ toks.sort
191
197
  end
192
198
 
193
- toks.sort
194
- end
199
+ def cscope_output(line, prev, offset, record)
200
+ buf = ''
201
+ buf << CSCOPE_GLOBAL_HACK_STOP if record[:type] == :func && record[:tbl] == :defs
202
+
203
+ record[:name][0...-1].each do |key|
204
+ # output previous components of the name (ie the Foo in Foo::bar) as unmarked symbols
205
+ key = key.to_s.sub(/\W+$/, '')
206
+ next if key.empty?
195
207
 
196
- def cscope_output(line, prev, offset, record)
197
- buf = ''
198
- buf << CSCOPE_GLOBAL_HACK_STOP if record[:type] == :func && record[:tbl] == :defs
208
+ index = line.index(key, prev)
199
209
 
200
- record[:name][0...-1].each do |key|
201
- # output previous components of the name (ie the Foo in Foo::bar) as unmarked symbols
202
- key = key.to_s.sub(/\W+$/, '')
203
- next if key.empty?
210
+ while index && index + key.length < offset && !valid_index?(line, index, key)
211
+ index = line.index(key, index + 1)
212
+ end
204
213
 
205
- index = line.index(key, prev)
214
+ next unless index && index + key.length < offset
206
215
 
207
- while index && index + key.length < offset && !valid_index?(line, index, key)
208
- index = line.index(key, index + 1)
216
+ buf << cscope_plaintext(line, prev, index) << "\n"
217
+ buf << "#{key}\n"
218
+ prev = index + key.length
209
219
  end
210
220
 
211
- next unless index && index + key.length < offset
221
+ buf << cscope_plaintext(line, prev, offset) << "\n"
222
+ buf << cscope_mark(record) << record[:key] << "\n"
212
223
 
213
- buf << cscope_plaintext(line, prev, index) << "\n"
214
- buf << "#{key}\n"
215
- prev = index + key.length
224
+ buf << CSCOPE_GLOBAL_HACK_START if record[:type] == :func && record[:tbl] == :end
225
+ buf
226
+ rescue ArgumentError
227
+ # invalid utf-8 byte sequence in the line, oh well
228
+ line
216
229
  end
217
230
 
218
- buf << cscope_plaintext(line, prev, offset) << "\n"
219
- buf << cscope_mark(record) << record[:key] << "\n"
220
-
221
- buf << CSCOPE_GLOBAL_HACK_START if record[:type] == :func && record[:tbl] == :end
222
- buf
223
- rescue ArgumentError
224
- # invalid utf-8 byte sequence in the line, oh well
225
- line
226
- end
227
-
228
- def valid_index?(line, index, key)
229
- # index is valid if the key exists at it, and the prev/next chars are not word characters
230
- ((line[index, key.length] == key) &&
231
- (index == 0 || line[index - 1] !~ /[[:word:]]/) &&
232
- (index + key.length == line.length || line[index + key.length] !~ /[[:word:]]/))
233
- end
231
+ def valid_index?(line, index, key)
232
+ # index is valid if the key exists at it, and the prev/next chars are not word characters
233
+ ((line[index, key.length] == key) &&
234
+ (index == 0 || line[index - 1] !~ /[[:word:]]/) &&
235
+ (index + key.length == line.length || line[index + key.length] !~ /[[:word:]]/))
236
+ end
234
237
 
235
- def cscope_plaintext(line, start, stop)
236
- ret = line.slice(start, stop - start)
237
- ret.lstrip! if start == 0
238
- ret.rstrip! if stop == line.length
239
- ret.gsub!(/\s+/, ' ')
240
- ret.empty? ? ' ' : ret
241
- rescue ArgumentError
242
- # invalid utf-8 byte sequence in the line, oh well
243
- line
244
- end
238
+ def cscope_plaintext(line, start, stop)
239
+ ret = line.slice(start, stop - start)
240
+ ret.lstrip! if start == 0
241
+ ret.rstrip! if stop == line.length
242
+ ret.gsub!(/\s+/, ' ')
243
+ ret.empty? ? ' ' : ret
244
+ rescue ArgumentError
245
+ # invalid utf-8 byte sequence in the line, oh well
246
+ line
247
+ end
245
248
 
246
- def cscope_mark(rec)
247
- case rec[:tbl]
248
- when :end
249
- case rec[:type]
250
- when :func
251
- ret = '}'
249
+ def cscope_mark(rec)
250
+ case rec[:tbl]
251
+ when :end
252
+ case rec[:type]
253
+ when :func
254
+ ret = '}'
255
+ else
256
+ return ''
257
+ end
258
+ when :file
259
+ ret = '@'
260
+ when :defs
261
+ case rec[:type]
262
+ when :func
263
+ ret = '$'
264
+ when :class, :module
265
+ ret = 'c'
266
+ when :type
267
+ ret = 't'
268
+ else
269
+ ret = 'g'
270
+ end
271
+ when :calls
272
+ ret = '`'
273
+ when :requires
274
+ ret = '~"'
275
+ when :imports
276
+ ret = '~<'
277
+ when :assigns
278
+ ret = '='
252
279
  else
253
280
  return ''
254
281
  end
255
- when :file
256
- ret = '@'
257
- when :defs
258
- case rec[:type]
259
- when :func
260
- ret = '$'
261
- when :class, :module
262
- ret = 'c'
263
- when :type
264
- ret = 't'
265
- else
266
- ret = 'g'
267
- end
268
- when :calls
269
- ret = '`'
270
- when :requires
271
- ret = '~"'
272
- when :imports
273
- ret = '~<'
274
- when :assigns
275
- ret = '='
276
- else
277
- return ''
278
- end
279
282
 
280
- "\t" + ret
283
+ "\t" + ret
284
+ end
281
285
  end
282
286
  end