starscope 1.3.3 → 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.
@@ -1,20 +1,20 @@
1
- require "parser/current"
1
+ require 'parser/current'
2
2
 
3
3
  module Starscope::Lang
4
4
  module Ruby
5
5
  VERSION = 2
6
6
 
7
7
  def self.match_file(name)
8
- return true if name.end_with?(".rb") || name.end_with?(".rake")
8
+ return true if name.end_with?('.rb') || name.end_with?('.rake')
9
9
  File.open(name) do |f|
10
10
  head = f.read(2)
11
- return false if head.nil? || !head.start_with?("#!")
12
- return f.readline.include?("ruby")
11
+ return false if head.nil? || !head.start_with?('#!')
12
+ return f.readline.include?('ruby')
13
13
  end
14
14
  end
15
15
 
16
- def self.extract(file, &block)
17
- ast = Parser::CurrentRuby.parse_file(file)
16
+ def self.extract(path, contents, &block)
17
+ ast = Parser::CurrentRuby.parse(contents)
18
18
  extract_tree(ast, [], &block) unless ast.nil?
19
19
  end
20
20
 
@@ -29,7 +29,7 @@ module Starscope::Lang
29
29
  scope += new_scope
30
30
  end
31
31
 
32
- tree.children.each {|node| extract_tree(node, scope, &block) if node.is_a? AST::Node}
32
+ tree.children.each { |node| extract_tree(node, scope, &block) if node.is_a? AST::Node }
33
33
 
34
34
  scope.pop(new_scope.count)
35
35
  end
@@ -45,8 +45,8 @@ module Starscope::Lang
45
45
  if name.last.to_s =~ /\w+=$/
46
46
  name[-1] = name.last.to_s.chop.to_sym
47
47
  yield :assigns, name, :line_no => loc.line, :col => loc.column
48
- elsif node.children[0].nil? and node.children[1] == :require and node.children[2].type == :str
49
- yield :requires, node.children[2].children[0].split("/"),
48
+ elsif node.children[0].nil? && node.children[1] == :require && node.children[2].type == :str
49
+ yield :requires, node.children[2].children[0].split('/'),
50
50
  :line_no => loc.line, :col => loc.column
51
51
  end
52
52
 
@@ -83,7 +83,7 @@ module Starscope::Lang
83
83
  when :sym
84
84
  # handle `:foo` vs `foo: 1`
85
85
  col = if loc.begin
86
- loc.begin.column+1
86
+ loc.begin.column + 1
87
87
  else
88
88
  loc.expression.column
89
89
  end
@@ -1,10 +1,9 @@
1
1
  require 'ruby-progressbar'
2
2
 
3
3
  class Starscope::Output
4
-
5
4
  PBAR_FORMAT = '%t: %c/%C %E ||%b>%i||'
6
5
 
7
- def initialize(level, out=STDOUT)
6
+ def initialize(level, out = STDOUT)
8
7
  @out = out
9
8
  @level = level
10
9
  @pbar = nil
@@ -13,8 +12,8 @@ class Starscope::Output
13
12
  def new_pbar(title, num_items)
14
13
  if @level != :quiet
15
14
  @pbar = ProgressBar.create(:title => title, :total => num_items,
16
- :format => PBAR_FORMAT, :length => 80,
17
- :out => @out)
15
+ :format => PBAR_FORMAT, :length => 80,
16
+ :out => @out)
18
17
  end
19
18
  end
20
19
 
@@ -46,5 +45,4 @@ class Starscope::Output
46
45
  @out.puts msg
47
46
  end
48
47
  end
49
-
50
48
  end
@@ -1,10 +1,9 @@
1
1
  require 'starscope/matcher'
2
2
 
3
3
  module Starscope::Queryable
4
-
5
- def query(tables, value, filters={})
4
+ def query(tables, value, filters = {})
6
5
  tables = [tables] if tables.is_a?(Symbol)
7
- tables.each { |t| raise Starscope::DB::NoTableError, "Table '#{t}' not found" unless @tables[t] }
6
+ tables.each { |t| fail Starscope::DB::NoTableError, "Table '#{t}' not found" unless @tables[t] }
8
7
  input = Enumerator.new do |y|
9
8
  tables.each do |t|
10
9
  @tables[t].each do |elem|
@@ -20,9 +19,9 @@ module Starscope::Queryable
20
19
 
21
20
  def run_query(query, input, filters)
22
21
  query = Starscope::Matcher.new(query)
23
- filters.each {|k,v| filters[k] = Starscope::Matcher.new(v)}
22
+ filters.each { |k, v| filters[k] = Starscope::Matcher.new(v) }
24
23
 
25
- results = input.select {|x| filter(x, filters)}.group_by {|x| match(x, query)}
24
+ results = input.select { |x| filter(x, filters) }.group_by { |x| match(x, query) }
26
25
 
27
26
  Starscope::Matcher::MATCH_TYPES.each do |type|
28
27
  next if results[type].nil? || results[type].empty?
@@ -40,7 +39,7 @@ module Starscope::Queryable
40
39
  end
41
40
 
42
41
  def match(record, query)
43
- name = record[:name].map {|x| x.to_s}.join('::')
42
+ name = record[:name].map(&:to_s).join('::')
44
43
 
45
44
  query.match(name)
46
45
  end
@@ -1,3 +1,3 @@
1
1
  module Starscope
2
- VERSION = "1.3.3"
2
+ VERSION = '1.4.0'
3
3
  end
@@ -3,20 +3,20 @@ require File.expand_path('../lib/starscope/version.rb', __FILE__)
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = 'starscope'
5
5
  gem.version = Starscope::VERSION
6
- gem.summary = "A code indexer and analyzer"
7
- gem.description = "A tool like the venerable cscope, but for ruby, golang and other languages"
8
- gem.authors = ["Evan Huus"]
6
+ gem.summary = 'A code indexer and analyzer'
7
+ gem.description = 'A tool like the venerable cscope, but for ruby, golang and other languages'
8
+ gem.authors = ['Evan Huus']
9
9
  gem.homepage = 'https://github.com/eapache/starscope'
10
10
  gem.email = 'eapache@gmail.com'
11
11
  gem.license = 'MIT'
12
12
  gem.files = `git ls-files`.split("\n")
13
- gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+ gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
14
14
  gem.test_files = `git ls-files -- test/*`.split("\n")
15
- gem.require_paths = ["lib"]
15
+ gem.require_paths = ['lib']
16
16
  gem.required_ruby_version = '>= 1.8.7'
17
17
 
18
18
  gem.add_dependency 'oj', '~> 2.9'
19
- gem.add_dependency 'parser', '~> 2.2.0.3'
19
+ gem.add_dependency 'parser', '~> 2.2.2'
20
20
  gem.add_dependency 'ruby-progressbar', '~> 1.5'
21
21
  gem.add_dependency 'backports', '~> 3.6'
22
22
  gem.add_development_dependency 'bundler', '~> 1.5'
@@ -0,0 +1,15 @@
1
+ 5
2
+ {
3
+ ":paths": [],
4
+ ":excludes": [],
5
+ ":langs": {
6
+ ":NoSuchLanguage": 0
7
+ },
8
+ ":files": {
9
+ "test/fixtures/sample_golang.go": {
10
+ ":last_updated": 10000000000,
11
+ ":lang": ":NoSuchLanguage"
12
+ }
13
+ }
14
+ }
15
+ {}
@@ -0,0 +1,17 @@
1
+ 5
2
+ {
3
+ ":paths": [],
4
+ ":excludes": [],
5
+ ":langs": {
6
+ ":Go": 1234,
7
+ ":Ruby": 0
8
+ },
9
+ ":files": {
10
+ "test/fixtures/sample_golang.go": {
11
+ ":last_updated": 10000000000,
12
+ ":lang": ":Go",
13
+ ":sublangs": [":Ruby"]
14
+ }
15
+ }
16
+ }
17
+ {}
@@ -0,0 +1,14 @@
1
+ <% if foo %>
2
+ malsgkjalsgkjaslfk
3
+ <% elsif bar %>
4
+ lkajlfaksjflaksjf
5
+ <% end %>
6
+
7
+
8
+ <%- case x
9
+ when :bar -%> <%= magic %> <% when :baz
10
+ when :foo %>
11
+ x and :bar
12
+ <% end %>
13
+
14
+ <%= foo %>, <%=== bar -%>, <%= baz %>
@@ -7,7 +7,6 @@ LANGS = [
7
7
  ]
8
8
 
9
9
  class Starscope::DB
10
-
11
10
  PBAR_FORMAT = '%t: %c/%C %E ||%b>%i||'
12
11
 
13
12
  class NoTableError < StandardError; end
@@ -31,12 +30,12 @@ class Starscope::DB
31
30
  elsif format <= 2
32
31
  # Old format (pre-json), so read the directories segment then rebuild
33
32
  len = stream.gets.to_i
34
- add_paths(Marshal::load(stream.read(len)))
33
+ add_paths(Marshal.load(stream.read(len)))
35
34
  elsif format < DB_FORMAT
36
35
  # Old format, so read the directories segment then rebuild
37
36
  add_paths(Oj.load(stream.gets))
38
37
  elsif format > DB_FORMAT
39
- raise UnknownDBFormatError
38
+ fail UnknownDBFormatError
40
39
  end
41
40
  end
42
41
  end
@@ -57,10 +56,10 @@ class Starscope::DB
57
56
  paths -= @paths
58
57
  return if paths.empty?
59
58
  @paths += paths
60
- files = paths.map {|p| self.class.files_from_path(p)}.flatten
59
+ files = paths.map { |p| self.class.files_from_path(p) }.flatten
61
60
  return if files.empty?
62
61
  if @progress
63
- pbar = ProgressBar.create(:title => "Building", :total => files.length, :format => PBAR_FORMAT, :length => 80)
62
+ pbar = ProgressBar.create(:title => 'Building', :total => files.length, :format => PBAR_FORMAT, :length => 80)
64
63
  end
65
64
  files.each do |f|
66
65
  add_file(f)
@@ -69,9 +68,9 @@ class Starscope::DB
69
68
  end
70
69
 
71
70
  def update
72
- new_files = (@paths.map {|p| self.class.files_from_path(p)}.flatten) - @files.keys
71
+ new_files = (@paths.map { |p| self.class.files_from_path(p) }.flatten) - @files.keys
73
72
  if @progress
74
- pbar = ProgressBar.create(:title => "Updating", :total => new_files.length + @files.length, :format => PBAR_FORMAT, :length => 80)
73
+ pbar = ProgressBar.create(:title => 'Updating', :total => new_files.length + @files.length, :format => PBAR_FORMAT, :length => 80)
75
74
  end
76
75
  changed = @files.keys.map do |f|
77
76
  changed = update_file(f)
@@ -101,13 +100,13 @@ class Starscope::DB
101
100
  if File.file?(path)
102
101
  [path]
103
102
  elsif File.directory?(path)
104
- Dir[File.join(path, "**", "*")].select {|p| File.file?(p)}
103
+ Dir[File.join(path, '**', '*')].select { |p| File.file?(p) }
105
104
  else
106
105
  []
107
106
  end
108
107
  end
109
108
 
110
- def db_by_line()
109
+ def db_by_line
111
110
  tmpdb = {}
112
111
  @tables.each do |tbl, vals|
113
112
  vals.each do |key, val|
@@ -115,21 +114,21 @@ class Starscope::DB
115
114
  if entry[:line_no]
116
115
  tmpdb[entry[:file]] ||= {}
117
116
  tmpdb[entry[:file]][entry[:line_no]] ||= []
118
- tmpdb[entry[:file]][entry[:line_no]] << {:tbl => tbl, :key => key, :entry => entry}
117
+ tmpdb[entry[:file]][entry[:line_no]] << { :tbl => tbl, :key => key, :entry => entry }
119
118
  end
120
119
  end
121
120
  end
122
121
  end
123
- return tmpdb
122
+ tmpdb
124
123
  end
125
124
 
126
125
  def add_file(file)
127
- return if not File.file? file
126
+ return unless File.file? file
128
127
 
129
128
  @files[file] = File.mtime(file).to_s
130
129
 
131
130
  LANGS.each do |lang|
132
- next if not lang.match_file file
131
+ next unless lang.match_file file
133
132
  lang.extract file do |tbl, key, args|
134
133
  key = key.to_sym
135
134
  @tables[tbl] ||= {}
@@ -143,13 +142,13 @@ class Starscope::DB
143
142
  @files.delete(file)
144
143
  @tables.each do |name, tbl|
145
144
  tbl.each do |key, val|
146
- val.delete_if {|dat| dat[:file] == file}
145
+ val.delete_if { |dat| dat[:file] == file }
147
146
  end
148
147
  end
149
148
  end
150
149
 
151
150
  def update_file(file)
152
- if not File.exists?(file) or not File.file?(file)
151
+ if !File.exist?(file) || !File.file?(file)
153
152
  remove_file(file)
154
153
  true
155
154
  elsif DateTime.parse(@files[file]).to_time < File.mtime(file)
@@ -160,5 +159,4 @@ class Starscope::DB
160
159
  false
161
160
  end
162
161
  end
163
-
164
162
  end
@@ -1,38 +1,37 @@
1
1
  require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
- describe "starscope executable script" do
4
-
5
- BASE = "bundle exec bin/starscope --quiet"
3
+ describe 'starscope executable script' do
4
+ BASE = 'bundle exec bin/starscope --quiet'
6
5
  EXTRACT = "#{BASE} --no-read --no-write #{FIXTURES}"
7
6
 
8
- it "must not produce help wider than 80 characters" do
7
+ it 'must not produce help wider than 80 characters' do
9
8
  `#{BASE} -h`.each_line do |line|
10
9
  line.length.must_be :<=, 80
11
10
  end
12
11
  end
13
12
 
14
- it "must produce the right version" do
13
+ it 'must produce the right version' do
15
14
  `#{BASE} -v`.chomp.must_equal Starscope::VERSION
16
15
  end
17
16
 
18
- it "must produce a valid database summary" do
17
+ it 'must produce a valid database summary' do
19
18
  lines = `#{EXTRACT} -s`.lines.to_a
20
19
  lines.length.must_equal 8
21
20
  end
22
21
 
23
- it "must produce a valid database dump" do
22
+ it 'must produce a valid database dump' do
24
23
  lines = `#{EXTRACT} -d requires`.lines.to_a
25
24
  lines[1].split.first.must_equal 'date'
26
25
  lines[2].split.first.must_equal 'zlib'
27
26
  end
28
27
 
29
- it "must correctly query the database" do
28
+ it 'must correctly query the database' do
30
29
  `#{EXTRACT} -q calls,add_file`.each_line do |line|
31
- line.split[0..2].must_equal ["Starscope", "DB", "add_file"]
30
+ line.split[0..2].must_equal %w(Starscope DB add_file)
32
31
  end
33
32
 
34
33
  `#{EXTRACT} -q lang:ruby,calls,add_file`.each_line do |line|
35
- line.split[0..2].must_equal ["Starscope", "DB", "add_file"]
34
+ line.split[0..2].must_equal %w(Starscope DB add_file)
36
35
  end
37
36
 
38
37
  `#{EXTRACT} -q lang:go,calls,add_file`.each_line do |line|
@@ -40,10 +39,10 @@ describe "starscope executable script" do
40
39
  end
41
40
  end
42
41
 
43
- it "must correctly export to cscope" do
42
+ it 'must correctly export to cscope' do
44
43
  file = Tempfile.new('starscope_test')
45
44
  begin
46
- `#{EXTRACT} -e cscope,#{file.path()}`
45
+ `#{EXTRACT} -e cscope,#{file.path}`
47
46
  $?.exitstatus.must_equal 0
48
47
  ensure
49
48
  file.close
@@ -51,15 +50,14 @@ describe "starscope executable script" do
51
50
  end
52
51
  end
53
52
 
54
- it "must correctly export to ctags" do
53
+ it 'must correctly export to ctags' do
55
54
  file = Tempfile.new('starscope_test')
56
55
  begin
57
- `#{EXTRACT} -e ctags,#{file.path()}`
56
+ `#{EXTRACT} -e ctags,#{file.path}`
58
57
  $?.exitstatus.must_equal 0
59
58
  ensure
60
59
  file.close
61
60
  file.unlink
62
61
  end
63
62
  end
64
-
65
63
  end
@@ -3,8 +3,9 @@ require 'minitest/pride'
3
3
  require 'mocha/mini_test'
4
4
  require File.expand_path('../../lib/starscope.rb', __FILE__)
5
5
 
6
- FIXTURES="test/fixtures"
6
+ FIXTURES = 'test/fixtures'
7
7
 
8
8
  GOLANG_SAMPLE = "#{FIXTURES}/sample_golang.go"
9
9
  RUBY_SAMPLE = "#{FIXTURES}/sample_ruby.rb"
10
+ ERB_SAMPLE = "#{FIXTURES}/sample_erb.erb"
10
11
  EMPTY_FILE = "#{FIXTURES}/empty"
@@ -2,18 +2,15 @@ require File.expand_path('../../test_helper', __FILE__)
2
2
  require 'tempfile'
3
3
 
4
4
  describe Starscope::DB do
5
-
6
5
  before do
7
6
  @db = Starscope::DB.new(Starscope::Output.new(:quiet))
8
7
  end
9
8
 
10
- it "must raise on invalid tables" do
11
- proc {
12
- @db.records(:foo)
13
- }.must_raise Starscope::DB::NoTableError
9
+ it 'must raise on invalid tables' do
10
+ proc { @db.records(:foo) }.must_raise Starscope::DB::NoTableError
14
11
  end
15
12
 
16
- it "must add paths" do
13
+ it 'must add paths' do
17
14
  paths = [GOLANG_SAMPLE, "#{FIXTURES}/**/*"]
18
15
  @db.add_paths(paths)
19
16
 
@@ -21,7 +18,7 @@ describe Starscope::DB do
21
18
  validate(@db)
22
19
  end
23
20
 
24
- it "must add excludes" do
21
+ it 'must add excludes' do
25
22
  paths = [GOLANG_SAMPLE, "#{FIXTURES}/**/*"]
26
23
  @db.add_paths(paths)
27
24
  @db.add_excludes(["#{FIXTURES}/**"])
@@ -33,14 +30,14 @@ describe Starscope::DB do
33
30
  @db.records(:end).must_be_empty
34
31
  end
35
32
 
36
- it "must pick up new files in old paths" do
33
+ it 'must pick up new files in old paths' do
37
34
  @db.load("#{FIXTURES}/db_added_files.json")
38
35
  @db.update
39
36
 
40
37
  validate(@db)
41
38
  end
42
39
 
43
- it "must remove old files in existing paths" do
40
+ it 'must remove old files in existing paths' do
44
41
  @db.load("#{FIXTURES}/db_removed_files.json")
45
42
  @db.update
46
43
  @db.metadata(:files).keys.wont_include "#{FIXTURES}/foo"
@@ -62,7 +59,7 @@ describe Starscope::DB do
62
59
  @db.records(:calls).wont_be_empty
63
60
  end
64
61
 
65
- it "must update unchanged existing files with old extractor versions" do
62
+ it 'must update unchanged existing files with old extractor versions' do
66
63
  @db.load("#{FIXTURES}/db_old_extractor.json")
67
64
 
68
65
  cur_mtime = @db.metadata(:files)[GOLANG_SAMPLE][:last_updated]
@@ -77,22 +74,53 @@ describe Starscope::DB do
77
74
  @db.records(:calls).wont_be_empty
78
75
  end
79
76
 
80
- it "must not update file with up-to-date time and extractor" do
77
+ it 'must update unchanged existing files with old sublang extractor versions' do
78
+ @db.load("#{FIXTURES}/db_old_subextractor.json")
79
+
80
+ cur_mtime = @db.metadata(:files)[GOLANG_SAMPLE][:last_updated]
81
+ File.expects(:mtime).twice.returns(cur_mtime)
82
+ @db.update
83
+
84
+ file = @db.metadata(:files)[GOLANG_SAMPLE]
85
+ file[:last_updated].must_equal cur_mtime
86
+ file[:lang].must_equal :Go
87
+ file[:sublangs].must_be_empty
88
+ file[:lines].wont_be_empty
89
+ @db.records(:defs).wont_be_empty
90
+ @db.records(:calls).wont_be_empty
91
+ end
92
+
93
+ it 'must update unchanged existing files when the extractor has been removed' do
94
+ @db.load("#{FIXTURES}/db_missing_language.json")
95
+
96
+ cur_mtime = @db.metadata(:files)[GOLANG_SAMPLE][:last_updated]
97
+ File.expects(:mtime).twice.returns(cur_mtime)
98
+ @db.update
99
+
100
+ file = @db.metadata(:files)[GOLANG_SAMPLE]
101
+ file[:last_updated].must_equal cur_mtime
102
+ file[:lang].must_equal :Go
103
+ file[:lines].wont_be_empty
104
+ @db.records(:defs).wont_be_empty
105
+ @db.records(:calls).wont_be_empty
106
+ end
107
+
108
+ it 'must not update file with up-to-date time and extractor' do
81
109
  @db.load("#{FIXTURES}/db_up_to_date.json")
82
110
  @db.update
83
111
 
84
112
  file = @db.metadata(:files)[GOLANG_SAMPLE]
85
- file[:last_updated].must_equal 10000000000
113
+ file[:last_updated].must_equal 10_000_000_000
86
114
  @db.tables.must_be_empty
87
115
  end
88
116
 
89
- it "must load an old DB file" do
117
+ it 'must load an old DB file' do
90
118
  @db.load("#{FIXTURES}/db_old.json.gz")
91
119
  @db.metadata(:paths).must_equal ["#{FIXTURES}/**/*"]
92
120
  validate(@db)
93
121
  end
94
122
 
95
- it "must round-trip a database" do
123
+ it 'must round-trip a database' do
96
124
  file = Tempfile.new('starscope_test')
97
125
  begin
98
126
  @db.add_paths([FIXTURES])
@@ -106,34 +134,34 @@ describe Starscope::DB do
106
134
  end
107
135
  end
108
136
 
109
- it "must run queries" do
137
+ it 'must run queries' do
110
138
  @db.add_paths([FIXTURES])
111
- @db.query(:calls, "abc").must_equal []
112
- @db.query(:defs, "xyz").must_equal []
113
- @db.query(:calls, "add_file").length.must_equal 3
139
+ @db.query(:calls, 'abc').must_equal []
140
+ @db.query(:defs, 'xyz').must_equal []
141
+ @db.query(:calls, 'add_file').length.must_equal 3
114
142
  end
115
143
 
116
- it "must run queries on multiple tables" do
144
+ it 'must run queries on multiple tables' do
117
145
  @db.add_paths([FIXTURES])
118
- ret = @db.query([:calls, :defs], "foo")
119
- ret.length.must_equal 1
146
+ ret = @db.query([:calls, :defs], 'foo')
147
+ ret.length.must_equal 3
120
148
  ret.first[:name].last.must_equal :foo
121
149
  end
122
150
 
123
- it "must symbolize compound name" do
124
- rec = Starscope::DB.normalize_record(:foo, ["a", :b], {})
151
+ it 'must symbolize compound name' do
152
+ rec = Starscope::DB.normalize_record(:foo, ['a', :b], {})
125
153
  rec[:name].must_equal [:a, :b]
126
154
  end
127
155
 
128
- it "must symbolize and array-wrap simple name" do
129
- rec = Starscope::DB.normalize_record(:foo, "a", {})
156
+ it 'must symbolize and array-wrap simple name' do
157
+ rec = Starscope::DB.normalize_record(:foo, 'a', {})
130
158
  rec[:name].must_equal [:a]
131
159
  end
132
160
 
133
- it "must store extractor metadata returned from the `extract` call" do
161
+ it 'must store extractor metadata returned from the `extract` call' do
134
162
  extractor = mock('extractor')
135
163
  extractor.expects(:match_file).with(GOLANG_SAMPLE).returns(true)
136
- extractor.expects(:extract).with(GOLANG_SAMPLE).returns({:a => 1})
164
+ extractor.expects(:extract).with(GOLANG_SAMPLE, File.read(GOLANG_SAMPLE)).returns(:a => 1)
137
165
  extractor.expects(:name).returns('Foo')
138
166
  EXTRACTORS.stubs(:each).yields(extractor)
139
167
 
@@ -156,5 +184,4 @@ describe Starscope::DB do
156
184
  db.records(:imports).wont_be_empty
157
185
  db.records(:requires).wont_be_empty
158
186
  end
159
-
160
187
  end