starscope 0.1.10 → 1.0.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/.travis.yml CHANGED
@@ -2,7 +2,6 @@ language: ruby
2
2
 
3
3
  rvm:
4
4
  - 1.8.7
5
- - 1.9.2
6
5
  - 1.9.3
7
6
  - 2.0.0
8
- - 2.1.0
7
+ - 2.1.1
data/CHANGELOG.md CHANGED
@@ -1,8 +1,33 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- v0.1.10 (trunk)
5
- -------------------
4
+ v1.0.0 (trunk)
5
+ --------------------
6
+
7
+ New Features:
8
+ * Preliminary export of a few advanced ctags annotations
9
+ * New -x flag to exclude files from scan (such as compiled .o files)
10
+ * New --verbose flag for additional output
11
+
12
+ Bug Fixes:
13
+ * Correctly write out migrated databases
14
+ * Be compatible with ruby 1.8 everywhere
15
+ * Fix golang parsing untyped "var" declarations
16
+ * Fix golang parsing multi-line literals
17
+ * Record assignments to ruby constants as definitions
18
+ * Fix exporting to cscope when scanned files contain invalid unicode
19
+
20
+ Improvements:
21
+ * Faster file-type matching
22
+ * Reworked option flags:
23
+ * Merged -r and -w into -f
24
+ * Split -n into --no-read, --no-write, --no-update
25
+ * New, more flexible database format
26
+ * Substantially improved searching/matching logic
27
+ * Miscellanious others via updated dependencies
28
+
29
+ v0.1.10 (2014-02-24)
30
+ --------------------
6
31
 
7
32
  Improvements:
8
33
  * Import new ruby parser version and make necessary changes so that StarScope
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- starscope (0.1.10)
5
- oj (~> 2.5)
4
+ starscope (1.0.0)
5
+ oj (~> 2.7)
6
6
  parser (~> 2.1)
7
7
  ruby-progressbar (~> 1.4)
8
8
 
@@ -12,18 +12,18 @@ GEM
12
12
  ast (1.1.0)
13
13
  coderay (1.1.0)
14
14
  method_source (0.8.2)
15
- minitest (5.2.3)
16
- oj (2.5.5)
17
- parser (2.1.5)
15
+ minitest (5.3.3)
16
+ oj (2.7.3)
17
+ parser (2.1.7)
18
18
  ast (~> 1.1)
19
19
  slop (~> 3.4, >= 3.4.5)
20
20
  pry (0.9.12.6)
21
21
  coderay (~> 1.0)
22
22
  method_source (~> 0.8)
23
23
  slop (~> 3.4)
24
- rake (10.1.1)
25
- ruby-progressbar (1.4.1)
26
- slop (3.4.7)
24
+ rake (10.2.2)
25
+ ruby-progressbar (1.4.2)
26
+ slop (3.5.0)
27
27
 
28
28
  PLATFORMS
29
29
  ruby
data/README.md CHANGED
@@ -10,31 +10,28 @@ only works for C (and sort of works for C++).
10
10
 
11
11
  StarScope is a similar tool for [Ruby](https://www.ruby-lang.org/) and
12
12
  [Golang](http://golang.org/), with a design intended to make it easy to add
13
- support for other languages at some point within the same framework (thus the
14
- name StarScope, ie \*scope).
13
+ support for other languages within the same framework (thus the name StarScope,
14
+ i.e. \*scope).
15
15
 
16
16
  Install it as a gem:
17
17
  ```
18
18
  $ gem install starscope
19
19
  ```
20
20
 
21
- Build your database, by just running it in the project directory:
21
+ Build your database by just running it in the project directory:
22
22
  ```
23
23
  $ cd ~/my-project
24
24
  $ starscope
25
25
  ```
26
26
 
27
- Ask it things with `-q`
27
+ Ask it things directly:
28
28
  ```
29
29
  $ starscope -q calls,new # Lists all callers of new
30
30
  ```
31
31
 
32
- Export it for use with your editor
32
+ Export it to various formats for use with your editor:
33
33
  ```
34
34
  $ starscope -e ctags
35
- ```
36
- or
37
- ```
38
35
  $ starscope -e cscope
39
36
  ```
40
37
 
data/bin/starscope CHANGED
@@ -7,18 +7,30 @@ require 'optparse'
7
7
  require 'readline'
8
8
  require 'starscope'
9
9
 
10
- options = {auto: true, progress: true}
11
- DEFAULT_DB=".starscope.db"
10
+ DEFAULT_DB='.starscope.db'
11
+ DEFAULT_CTAGS='tags'
12
+ DEFAULT_CSCOPE='cscope.out'
13
+
14
+ options = {:show_progress => true,
15
+ :read => true,
16
+ :write => true,
17
+ :update => true,
18
+ :verbose => false,
19
+ :db => DEFAULT_DB
20
+ }
12
21
 
13
22
  # Options Parsing
14
23
  OptionParser.new do |opts|
15
24
  opts.banner = <<END
16
25
  Usage: starscope [options] [PATHS]
17
26
 
18
- If you don't pass any of -n, -r, -w or PATHS, default behaviour is to recurse
19
- in the current directory and build or update the database `#{DEFAULT_DB}`.
27
+ The default database is `#{DEFAULT_DB}` if you don't specify one with -f.
28
+ The default behaviour is to read and update the database.
29
+ If no database exists and no PATHS are specified, StarScope builds a new
30
+ database by recursing in the current directory.
20
31
 
21
- Query scopes must be specified with `::`, for example -q calls,File::mtime.
32
+ Scoped queries must use `::` as the scope separator, even for languages which
33
+ have their own scope syntax.
22
34
  END
23
35
 
24
36
  opts.separator "\nQueries"
@@ -39,14 +51,21 @@ END
39
51
  opts.on("-e", "--export FORMAT[,PATH]", "Export in FORMAT to PATH, (see EXPORTING)") do |export|
40
52
  options[:export] = export
41
53
  end
42
- opts.on("-n", "--no-auto", "Don't automatically update/create the DB") do
43
- options[:auto] = false
54
+ opts.on("-f", "--file FILE", "Use FILE instead of `#{DEFAULT_DB}`") do |path|
55
+ options[:db] = path
44
56
  end
45
- opts.on("-r", "--read-db PATH", "Reads the DB from PATH instead of default") do |path|
46
- options[:read] = path
57
+ opts.on("-x", "--exclude PATTERN", "Skip files matching PATTERN") do |pattern|
58
+ options[:exclude] ||= []
59
+ options[:exclude] << pattern
47
60
  end
48
- opts.on("-w", "--write-db PATH", "Writes the DB to PATH instead of default") do |path|
49
- options[:write] = path
61
+ opts.on("--no-read", "Don't read the DB from a file") do
62
+ options[:read] = false
63
+ end
64
+ opts.on("--no-write", "Don't write the DB to a file") do
65
+ options[:write] = false
66
+ end
67
+ opts.on("--no-update", "Don't update the DB") do
68
+ options[:update] = false
50
69
  end
51
70
 
52
71
  opts.separator "\nMisc"
@@ -54,22 +73,25 @@ END
54
73
  puts StarScope::VERSION
55
74
  exit
56
75
  end
76
+ opts.on("--verbose", "Print extra details") do
77
+ options[:verbose] = true
78
+ end
57
79
  opts.on("--no-progress", "Don't show progress-bar when updating DB") do
58
- options[:progress] = false
80
+ options[:show_progress] = false
59
81
  end
60
82
 
61
83
  opts.separator <<END
62
84
  \nEXPORTING
63
85
  At the moment two export formats are supported: 'ctags' and 'cscope'. If
64
- you don't specify a path, the output is written to the files 'tags' (for
65
- ctags) or 'cscope.out' (for cscope) in the current directory.
86
+ you don't specify a path, the output is written to the files '#{DEFAULT_CTAGS}' (for
87
+ ctags) or '#{DEFAULT_CSCOPE}' (for cscope) in the current directory.
66
88
  END
67
89
 
68
90
  end.parse!
69
91
 
70
92
  def print_summary(db)
71
93
  db.summary.each do |name, count|
72
- printf("%-8s %5d keys\n", name, count)
94
+ printf("%-8s %5d records\n", name, count)
73
95
  end
74
96
  end
75
97
 
@@ -78,9 +100,11 @@ def run_query(db, table, value)
78
100
  $stderr.puts "Invalid input - no query found."
79
101
  return false
80
102
  end
81
- key, results = db.query(table.to_sym, value)
103
+ results = db.query(table.to_sym, value)
82
104
  if results
83
- puts results.map {|val| StarScope::Datum.to_s(val)}
105
+ results.sort_by {|x| x[:name].join(' ')}.each do |rec|
106
+ puts StarScope::Record.format(rec)
107
+ end
84
108
  else
85
109
  puts "No results found."
86
110
  end
@@ -91,10 +115,12 @@ rescue StarScope::DB::NoTableError
91
115
  end
92
116
 
93
117
  def dump(db, table)
94
- if table
95
- db.dump_table(table.to_sym)
96
- else
118
+ if table.nil?
97
119
  db.dump_all
120
+ elsif table.start_with?('_')
121
+ db.dump_meta(table[1..-1].to_sym)
122
+ else
123
+ db.dump_table(table.to_sym)
98
124
  end
99
125
  return true
100
126
  rescue StarScope::DB::NoTableError
@@ -102,42 +128,49 @@ rescue StarScope::DB::NoTableError
102
128
  return false
103
129
  end
104
130
 
105
- if options[:auto] and not options[:write]
106
- options[:write] = DEFAULT_DB
107
- options[:auto_write] = true
108
- end
131
+ db = StarScope::DB.new(options[:show_progress], options[:verbose])
109
132
 
110
- if File.exists?(DEFAULT_DB) and not options[:read]
111
- options[:read] = DEFAULT_DB
112
- end
133
+ db_exists = File.exists?(options[:db])
113
134
 
114
- db = StarScope::DB.new(options[:progress])
135
+ if options[:read] and db_exists
136
+ new_data = db.load(options[:db])
137
+ else
138
+ # no need to run an update if we didn't read any old data
139
+ options[:update] = false
140
+ end
115
141
 
116
- if options[:read]
117
- db.load(options[:read])
118
- if !ARGV.empty?
119
- db.add_paths(ARGV)
120
- new_data = true
121
- end
122
- elsif ARGV.empty?
123
- db.add_paths(['.'])
142
+ if options[:exclude]
143
+ db.add_excludes(options[:exclude])
124
144
  new_data = true
125
- else
145
+ end
146
+
147
+ if not ARGV.empty?
148
+ # paths specified, add them
126
149
  db.add_paths(ARGV)
127
150
  new_data = true
151
+ elsif not (options[:read] and db_exists)
152
+ # no paths were specified and the database was not read or did not exist;
153
+ # default to building a new DB in the current directory
154
+ db.add_paths(['.'])
155
+ new_data = true
128
156
  end
129
157
 
130
- changed = db.update if options[:read] and options[:auto]
158
+ updated = db.update() if options[:update]
159
+ new_data ||= updated
131
160
 
132
- db.save(options[:write]) if options[:write] and (new_data or changed or not options[:auto_write])
161
+ db.save(options[:db]) if options[:write] and (new_data or not db_exists)
133
162
 
134
163
  if options[:export]
135
164
  format, path = options[:export].split(',', 2)
136
165
  case format
137
166
  when 'ctags'
138
- db.export_ctags(path || 'tags')
167
+ File.open(path || DEFAULT_CTAGS, 'w') do |file|
168
+ db.export_ctags(file)
169
+ end
139
170
  when 'cscope'
140
- db.export_cscope(path || 'cscope.out')
171
+ File.open(path || DEFAULT_CSCOPE, 'w') do |file|
172
+ db.export_cscope(file)
173
+ end
141
174
  else
142
175
  puts "Unrecognized export format"
143
176
  end
@@ -174,29 +207,32 @@ end
174
207
 
175
208
  if options[:linemode]
176
209
  puts "Run your query as 'TABLE QUERY' or run '!help' for more information."
177
- while input = Readline.readline("> ", true)
178
- cmd, param = input.split(' ', 2)
179
- if cmd[0] == '!'
180
- case cmd[1..-1]
181
- when "dump"
182
- dump(db, param)
183
- when "summary"
184
- print_summary(db)
185
- when "update"
186
- changed = db.update
187
- db.save(options[:write]) if options[:write] and changed
188
- when "help"
189
- puts linemode_help
190
- when "version"
191
- puts StarScope::VERSION
192
- when "quit"
193
- exit
210
+ begin
211
+ while input = Readline.readline("> ", true)
212
+ cmd, param = input.split(' ', 2)
213
+ if cmd[0] == '!'
214
+ case cmd[1..-1]
215
+ when "dump"
216
+ dump(db, param)
217
+ when "summary"
218
+ print_summary(db)
219
+ when "update"
220
+ changed = db.update
221
+ db.save(options[:write]) if options[:write] and changed
222
+ when "help"
223
+ puts linemode_help
224
+ when "version"
225
+ puts StarScope::VERSION
226
+ when "quit"
227
+ exit
228
+ else
229
+ puts "Unknown command: '#{input}', try '!help'."
230
+ end
194
231
  else
195
- puts "Unknown command: '#{input}', try '!help'."
232
+ success = run_query(db, cmd, param)
233
+ puts "Try '!help'." unless success
196
234
  end
197
- else
198
- success = run_query(db, cmd, param)
199
- puts "Try '!help'." unless success
200
235
  end
236
+ rescue Interrupt
201
237
  end
202
238
  end
data/lib/starscope/db.rb CHANGED
@@ -1,47 +1,56 @@
1
- require 'starscope/langs/go'
2
- require 'starscope/langs/ruby'
3
- require 'starscope/datum'
4
1
  require 'date'
5
2
  require 'oj'
6
3
  require 'zlib'
7
- require 'ruby-progressbar'
4
+
5
+ require 'starscope/matcher'
6
+ require 'starscope/output'
7
+ require 'starscope/record'
8
+
9
+ require 'starscope/langs/coffeescript'
10
+ require 'starscope/langs/go'
11
+ require 'starscope/langs/lua'
12
+ require 'starscope/langs/ruby'
8
13
 
9
14
  LANGS = [
15
+ StarScope::Lang::CoffeeScript,
10
16
  StarScope::Lang::Go,
17
+ StarScope::Lang::Lua,
11
18
  StarScope::Lang::Ruby
12
19
  ]
13
20
 
14
21
  class StarScope::DB
15
22
 
16
- DB_FORMAT = 4
17
- PBAR_FORMAT = '%t: %c/%C %E ||%b>%i||'
23
+ DB_FORMAT = 5
18
24
 
19
25
  class NoTableError < StandardError; end
20
26
  class UnknownDBFormatError < StandardError; end
21
27
 
22
- def initialize(progress)
23
- @progress = progress
24
- @paths = []
25
- @files = {}
28
+ def initialize(progress, verbose)
29
+ @output = StarScope::Output.new(progress, verbose)
30
+ @meta = {:paths => [], :files => {}, :excludes => []}
26
31
  @tables = {}
27
32
  end
28
33
 
34
+ # returns true if the database had to be up-converted from an old format
29
35
  def load(file)
36
+ @output.log("Reading database from `#{file}`... ")
30
37
  File.open(file, 'r') do |file|
31
38
  Zlib::GzipReader.wrap(file) do |file|
32
39
  format = file.gets.to_i
33
40
  if format == DB_FORMAT
34
- @paths = Oj.load(file.gets)
35
- @files = Oj.load(file.gets)
36
- @tables = Oj.load(file.gets, :symbol_keys => true)
41
+ @meta = Oj.load(file.gets)
42
+ @tables = Oj.load(file.gets)
43
+ return false
37
44
  elsif format <= 2
38
45
  # Old format (pre-json), so read the directories segment then rebuild
39
46
  len = file.gets.to_i
40
47
  add_paths(Marshal::load(file.read(len)))
41
- elsif format < DB_FORMAT
48
+ return true
49
+ elsif format <= 4
42
50
  # Old format, so read the directories segment then rebuild
43
51
  add_paths(Oj.load(file.gets))
44
- elsif format > DB_FORMAT
52
+ return true
53
+ else
45
54
  raise UnknownDBFormatError
46
55
  end
47
56
  end
@@ -49,57 +58,85 @@ class StarScope::DB
49
58
  end
50
59
 
51
60
  def save(file)
61
+ @output.log("Writing database to `#{file}`...")
52
62
  File.open(file, 'w') do |file|
53
63
  Zlib::GzipWriter.wrap(file) do |file|
54
64
  file.puts DB_FORMAT
55
- file.puts Oj.dump @paths
56
- file.puts Oj.dump @files
65
+ file.puts Oj.dump @meta
57
66
  file.puts Oj.dump @tables
58
67
  end
59
68
  end
60
69
  end
61
70
 
71
+ def add_excludes(paths)
72
+ @meta[:paths] -= paths.map {|p| normalize_glob(p)}
73
+ paths = paths.map {|p| normalize_fnmatch(p)}
74
+ @meta[:excludes] += paths
75
+ @meta[:excludes].uniq!
76
+ @meta[:files].delete_if do |name, record|
77
+ if matches_exclude(paths, name)
78
+ remove_file(name)
79
+ true
80
+ else
81
+ false
82
+ end
83
+ end
84
+ end
85
+
62
86
  def add_paths(paths)
63
- paths -= @paths
64
- return if paths.empty?
65
- @paths += paths
66
- files = paths.map {|p| self.class.files_from_path(p)}.flatten
87
+ @meta[:excludes] -= paths.map {|p| normalize_fnmatch(p)}
88
+ paths = paths.map {|p| normalize_glob(p)}
89
+ @meta[:paths] += paths
90
+ @meta[:paths].uniq!
91
+ files = Dir.glob(paths).select {|f| File.file? f}
92
+ files.delete_if {|f| matches_exclude(@meta[:excludes], f)}
67
93
  return if files.empty?
68
- if @progress
69
- pbar = ProgressBar.create(:title => "Building", :total => files.length, :format => PBAR_FORMAT, :length => 80)
70
- end
71
- files.each do |f|
72
- add_file(f)
73
- pbar.increment if @progress
74
- end
94
+ @output.new_pbar("Building", files.length)
95
+ add_new_files(files)
96
+ @output.finish_pbar
75
97
  end
76
98
 
77
99
  def update
78
- new_files = (@paths.map {|p| self.class.files_from_path(p)}.flatten) - @files.keys
79
- if @progress
80
- pbar = ProgressBar.create(:title => "Updating", :total => new_files.length + @files.length, :format => PBAR_FORMAT, :length => 80)
81
- end
82
- changed = @files.keys.map do |f|
83
- changed = update_file(f)
84
- pbar.increment if @progress
85
- changed
86
- end
87
- new_files.each do |f|
88
- add_file(f)
89
- pbar.increment if @progress
100
+ new_files = (Dir.glob(@meta[:paths]).select {|f| File.file? f}) - @meta[:files].keys
101
+ new_files.delete_if {|f| matches_exclude(@meta[:excludes], f)}
102
+ @output.new_pbar("Updating", new_files.length + @meta[:files].length)
103
+ changed = false
104
+ @meta[:files].delete_if do |name, record|
105
+ @output.log("Updating `#{name}`")
106
+ ret = update_file(name)
107
+ @output.inc_pbar
108
+ changed = true if ret == :update
109
+ ret == :delete
90
110
  end
91
- changed.any? || !new_files.empty?
111
+ add_new_files(new_files)
112
+ @output.finish_pbar
113
+ changed || !new_files.empty?
92
114
  end
93
115
 
94
116
  def dump_table(table)
95
117
  raise NoTableError if not @tables[table]
96
118
  puts "== Table: #{table} =="
97
- @tables[table].sort_by{|k,v| k.downcase}.each do |val, data|
98
- puts "#{val}"
99
- data.each do |datum|
100
- print "\t"
101
- puts StarScope::Datum.to_s(datum)
119
+ @tables[table].sort {|a,b|
120
+ a[:name][-1].to_s.downcase <=> b[:name][-1].to_s.downcase
121
+ }.each do |record|
122
+ puts StarScope::Record.format(record)
123
+ end
124
+ end
125
+
126
+ def dump_meta(key)
127
+ if key == :meta
128
+ puts "== Metadata Summary =="
129
+ @meta.each do |k, v|
130
+ puts "#{k}: #{v.count}"
102
131
  end
132
+ return
133
+ end
134
+ raise NoTableError if not @meta[key]
135
+ puts "== Metadata: #{key} =="
136
+ if @meta[key].is_a? Array
137
+ @meta[key].sort.each {|x| puts x}
138
+ else
139
+ @meta[key].sort.each {|k,v| puts "#{k}: #{v}"}
103
140
  end
104
141
  end
105
142
 
@@ -111,74 +148,56 @@ class StarScope::DB
111
148
  ret = {}
112
149
 
113
150
  @tables.each_key do |key|
114
- ret[key] = @tables[key].keys.count
151
+ ret[key] = @tables[key].count
115
152
  end
116
153
 
117
154
  ret
118
155
  end
119
156
 
120
157
  def query(table, value)
121
- fqn = value.split("::")
122
158
  raise NoTableError if not @tables[table]
123
- key = fqn.last
124
- results = @tables[table][key.to_sym] || []
125
- if results.empty?
126
- matcher = Regexp.new(key, Regexp::IGNORECASE)
127
- @tables[table].each do |k,v|
128
- if matcher.match(k)
129
- results << v
130
- end
131
- end
132
- results.flatten!
133
- end
134
- return results if results.empty?
135
- results.sort! do |a,b|
136
- StarScope::Datum.score_match(b, fqn) <=> StarScope::Datum.score_match(a, fqn)
137
- end
138
- best_score = StarScope::Datum.score_match(results[0], fqn)
139
- results = results.select do |result|
140
- best_score - StarScope::Datum.score_match(result, fqn) < 4
141
- end
142
- return fqn.last.to_sym, results
159
+ input = @tables[table]
160
+ StarScope::Matcher.new(value, input).query()
143
161
  end
144
162
 
145
- def export_ctags(filename)
146
- File.open(filename, 'w') do |file|
147
- file.puts <<END
148
- !_TAG_FILE_FORMAT 2 //
163
+ def export_ctags(file)
164
+ file.puts <<END
165
+ !_TAG_FILE_FORMAT 2 /extended format/
149
166
  !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
150
- !_TAG_PROGRAM_AUTHOR Evan Huus //
151
- !_TAG_PROGRAM_NAME Starscope //
167
+ !_TAG_PROGRAM_AUTHOR Evan Huus /eapache@gmail.com/
168
+ !_TAG_PROGRAM_NAME StarScope //
152
169
  !_TAG_PROGRAM_URL https://github.com/eapache/starscope //
153
170
  !_TAG_PROGRAM_VERSION #{StarScope::VERSION} //
154
171
  END
155
- defs = (@tables[:defs] || {}).sort
156
- defs.each do |key, val|
157
- val.each do |entry|
158
- file.puts StarScope::Datum.ctag_line(entry)
159
- end
160
- end
172
+ defs = (@tables[:defs] || {}).sort_by {|x| x[:name][-1].to_s}
173
+ defs.each do |record|
174
+ file.puts StarScope::Record.ctag_line(record)
161
175
  end
162
176
  end
163
177
 
164
178
  # ftp://ftp.eeng.dcu.ie/pub/ee454/cygwin/usr/share/doc/mlcscope-14.1.8/html/cscope.html
165
- def export_cscope(filename)
179
+ def export_cscope(file)
166
180
  buf = ""
167
181
  files = []
168
- db_by_line().each do |file, lines|
182
+ db_by_line().each do |filename, lines|
169
183
  if not lines.empty?
170
- buf << "\t@#{file}\n\n"
171
- files << file
184
+ buf << "\t@#{filename}\n\n"
185
+ files << filename
172
186
  end
173
- lines.sort.each do |line_no, vals|
174
- line = vals.first[:entry][:line].strip.gsub(/\s+/, ' ')
187
+ lines.sort.each do |line_no, records|
188
+ begin
189
+ line = records.first[:line].strip.gsub(/\s+/, ' ')
190
+ rescue ArgumentError
191
+ # invalid utf-8 byte sequence in the line, just do our best
192
+ line = records.first[:line]
193
+ end
175
194
  toks = {}
176
195
 
177
- vals.each do |val|
178
- index = line.index(val[:key].to_s)
196
+ records.each do |record|
197
+ index = line.index(record[:name][-1].to_s)
179
198
  while index
180
- toks[index] = val
181
- index = line.index(val[:key].to_s, index + 1)
199
+ toks[index] = record
200
+ index = line.index(record[:name][-1].to_s, index + 1)
182
201
  end
183
202
  end
184
203
 
@@ -186,11 +205,11 @@ END
186
205
 
187
206
  prev = 0
188
207
  buf << line_no.to_s << " "
189
- toks.sort().each do |offset, val|
208
+ toks.sort().each do |offset, record|
190
209
  buf << line.slice(prev...offset) << "\n"
191
- buf << StarScope::Datum.cscope_mark(val[:tbl], val[:entry])
192
- buf << val[:key].to_s << "\n"
193
- prev = offset + val[:key].to_s.length
210
+ buf << StarScope::Record.cscope_mark(record[:tbl], record)
211
+ buf << record[:name][-1].to_s << "\n"
212
+ prev = offset + record[:name][-1].to_s.length
194
213
  end
195
214
  buf << line.slice(prev..-1) << "\n\n"
196
215
  end
@@ -199,86 +218,100 @@ END
199
218
  buf << "\t@\n"
200
219
 
201
220
  header = "cscope 15 #{Dir.pwd} -c "
202
- offset = "%010d\n" % (header.length + 11 + buf.length)
203
-
204
- File.open(filename, 'w') do |file|
205
- file.print(header)
206
- file.print(offset)
207
- file.print(buf)
208
-
209
- file.print("#{@paths.length}\n")
210
- @paths.each {|p| file.print("#{p}\n")}
211
- file.print("0\n")
212
- file.print("#{files.length}\n")
213
- buf = ""
214
- files.each {|f| buf << f + "\n"}
215
- file.print("#{buf.length}\n#{buf}")
216
- end
221
+ offset = "%010d\n" % (header.length + 11 + buf.bytes.to_a.length)
222
+
223
+ file.print(header)
224
+ file.print(offset)
225
+ file.print(buf)
226
+
227
+ file.print("#{@meta[:paths].length}\n")
228
+ @meta[:paths].each {|p| file.print("#{p}\n")}
229
+ file.print("0\n")
230
+ file.print("#{files.length}\n")
231
+ buf = ""
232
+ files.each {|f| buf << f + "\n"}
233
+ file.print("#{buf.length}\n#{buf}")
217
234
  end
218
235
 
219
236
  private
220
237
 
221
- def self.files_from_path(path)
222
- if File.file?(path)
223
- [path]
238
+ def add_new_files(files)
239
+ files.each do |file|
240
+ @output.log("Adding `#{file}`")
241
+ @meta[:files][file] = {}
242
+ parse_file(file)
243
+ @output.inc_pbar
244
+ end
245
+ end
246
+
247
+ # File.fnmatch treats a "**" to match files and directories recursively
248
+ def normalize_fnmatch(path)
249
+ if path == "."
250
+ "**"
251
+ elsif File.directory?(path)
252
+ File.join(path, "**")
253
+ else
254
+ path
255
+ end
256
+ end
257
+
258
+ # Dir.glob treats a "**" to only match directories recursively; you need
259
+ # "**/*" to match all files recursively
260
+ def normalize_glob(path)
261
+ if path == "."
262
+ File.join("**", "*")
224
263
  elsif File.directory?(path)
225
- Dir[File.join(path, "**", "*")].select {|p| File.file?(p)}
264
+ File.join(path, "**", "*")
226
265
  else
227
- []
266
+ path
228
267
  end
229
268
  end
230
269
 
231
270
  def db_by_line()
232
- tmpdb = {}
233
- @tables.each do |tbl, vals|
234
- vals.each do |key, val|
235
- val.each do |entry|
236
- if entry[:line_no]
237
- tmpdb[entry[:file]] ||= {}
238
- tmpdb[entry[:file]][entry[:line_no]] ||= []
239
- tmpdb[entry[:file]][entry[:line_no]] << {:tbl => tbl, :key => key, :entry => entry}
240
- end
241
- end
271
+ db = {}
272
+ @tables.each do |tbl, records|
273
+ records.each do |record|
274
+ next if not record[:line_no]
275
+ record[:tbl] = tbl
276
+ db[record[:file]] ||= {}
277
+ db[record[:file]][record[:line_no]] ||= []
278
+ db[record[:file]][record[:line_no]] << record
242
279
  end
243
280
  end
244
- return tmpdb
281
+ return db
245
282
  end
246
283
 
247
- def add_file(file)
248
- return if not File.file? file
284
+ def matches_exclude(patterns, file)
285
+ patterns.map {|p| File.fnmatch(p, file)}.any?
286
+ end
249
287
 
250
- @files[file] = File.mtime(file).to_s
288
+ def parse_file(file)
289
+ @meta[:files][file][:last_updated] = File.mtime(file).to_i
251
290
 
252
291
  LANGS.each do |lang|
253
292
  next if not lang.match_file file
254
- lang.extract file do |tbl, key, args|
255
- key = key.to_sym
256
- @tables[tbl] ||= {}
257
- @tables[tbl][key] ||= []
258
- @tables[tbl][key] << StarScope::Datum.build(file, key, args)
293
+ lang.extract file do |tbl, name, args|
294
+ @tables[tbl] ||= []
295
+ @tables[tbl] << StarScope::Record.build(file, name, args)
259
296
  end
297
+ @meta[:files][file][:lang] = lang.name.split('::').last.to_sym
260
298
  end
261
299
  end
262
300
 
263
301
  def remove_file(file)
264
- @files.delete(file)
265
302
  @tables.each do |name, tbl|
266
- tbl.each do |key, val|
267
- val.delete_if {|dat| dat[:file] == file}
268
- end
303
+ tbl.delete_if {|val| val[:file] == file}
269
304
  end
270
305
  end
271
306
 
272
307
  def update_file(file)
273
308
  if not File.exists?(file) or not File.file?(file)
274
309
  remove_file(file)
275
- true
276
- elsif DateTime.parse(@files[file]).to_time.to_i < File.mtime(file).to_i
310
+ :delete
311
+ elsif @meta[:files][file][:last_updated] < File.mtime(file).to_i
277
312
  remove_file(file)
278
- add_file(file)
279
- true
280
- else
281
- false
313
+ parse_file(file)
314
+ :update
282
315
  end
283
316
  end
284
317