utils 0.93.0 → 0.95.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4412a2649cf7a18654f0cdec0cf7fa86ba33cb15c8cf88e2ef1ba10267753cd4
4
- data.tar.gz: 30095f823b5fad6f66e79667d82f88757d47f04d28cde52fe7706c1731331d6b
3
+ metadata.gz: dd3330bad863906079106f503caa40f83af5dd978e5c4cbec9561cfbf22228b8
4
+ data.tar.gz: 1081b4d918807345b36faa44d13b1c5ec287bdaf8156edfa3090ec909c3570a3
5
5
  SHA512:
6
- metadata.gz: 0aa5db98d16b760637beb04b4fa5fcd99cb951854b9bb6f8714bc350f2351bb2acbe26fc2220e35b7caf3e079f0b19c3fa74f218bc44107ad03d98c7cbbcb5c2
7
- data.tar.gz: fdb6289ad43454ed499fbdf9ae17051d5b24e93c474dc9ddc81c1f34661135e1c5275bc9dfee6b0b505a017c5a64b9c8c8a28e1caa744f9d7ae255e4ea115951
6
+ metadata.gz: 2fcaa85ef0d3a75b97d6c019c6ffd824e519505204056760956df512e6a85dd41a44787a9a665331c21ff5477cdebbc6c97607749fcabc7d404bcc18654e7b15
7
+ data.tar.gz: 2ed4678d7c0b12d96f3d0099ae3563d553e9f571b3e1c6ee12d8d910b22cd0357795fc42ebf3383f34323c00f84c256984f716e25dda4eec91c21ac0b1f8a434
data/README.md CHANGED
@@ -52,8 +52,7 @@ gem install utils
52
52
  ### 🔍 Searching
53
53
 
54
54
  - **`blameline`** - Show git blame line for a file and line number
55
- - **`create_cstags`** - Create cscope tags file for current directory
56
- - **`create_tags`** - Create ctags tags file for current directory
55
+ - **`code_indexer`** - Create ctags/cscope tags file for current directory
57
56
  - **`discover`** - Find files with specific content patterns
58
57
  - **`git-versions`** - Show git versions of files and directories
59
58
  - **`long_lines`** - Finds long lines with author attribution
data/bin/code_indexer CHANGED
@@ -4,11 +4,22 @@ require 'utils'
4
4
  config = Utils::ConfigFile.new
5
5
  config.configure_from_paths
6
6
 
7
+ only_format = ARGV.shift
8
+
7
9
  config.code_indexer.formats.each do |format, output|
10
+ if only_format
11
+ format == only_format or next
12
+ end
8
13
  paths = config.code_indexer.paths.grep_v(/Resolving dependencies/)
9
14
  puts "Indexing code for #{format}…"
10
- cmd = [ 'starscope', '-e', format, '-f', output, '--no-read' ]
11
- config.code_indexer.verbose and cmd << '--verbose'
15
+ case format
16
+ when 'ctags'
17
+ cmd = [ 'ctags', '-f', output, '--exclude=pkg', '-R' ]
18
+ config.code_indexer.verbose and cmd << '--verbose'
19
+ when 'cscope'
20
+ cmd = [ 'starscope', '-e', format, '-f', output, '--no-read' ]
21
+ config.code_indexer.verbose and cmd << '--verbose'
22
+ end
12
23
  cmd.push(*paths)
13
24
  system(*cmd)
14
25
  puts "Done."
data/bin/discover CHANGED
@@ -82,7 +82,7 @@ Options are
82
82
  -d match also directories
83
83
  -D list all search leaf directories
84
84
  -v be verbose
85
- -n NUMBER the first NUMBER of matches is returned
85
+ -m NUMBER the maximum NUMBER of matches that is returned
86
86
  -s search by interactively inputting PATTERN
87
87
  -l list all paths in cache relative to current directory
88
88
  -L list all paths in cache as absolute pathes from root
@@ -94,14 +94,14 @@ Version is #{File.basename($0)} #{Utils::VERSION}.
94
94
  exit 1
95
95
  end
96
96
 
97
- args = go 'n:I:i:a:p:P:lLcreEvdsDgh', defaults: { ?a => '\\w' }
97
+ args = go 'm:I:i:a:p:P:lLcreEvdsDgh', defaults: { ?a => '\\w' }
98
98
  args[?h] and usage
99
99
 
100
100
  Term::ANSIColor.coloring = (STDIN.tty? && ENV['TERM'] !~ /dumb/) && !args[?c]
101
101
  STDOUT.sync = true
102
102
  config = Utils::ConfigFile.new
103
103
  config.configure_from_paths
104
- args[?n] ||= (args[?l] || args[?L]) ? 1 << 60 : config.discover.max_matches
104
+ args[?m] ||= (args[?l] || args[?L]) ? 1 << 60 : config.discover.max_matches
105
105
 
106
106
  if args[?s]
107
107
  pattern = ''
@@ -125,7 +125,7 @@ finder = -> * {
125
125
  search = -> * {
126
126
  f = nil
127
127
  pattern = pattern.dup
128
- args[?n] = Term::ANSIColor.terminal_lines - 1
128
+ args[?m] = Term::ANSIColor.terminal_lines - 1
129
129
  found = Search.new(
130
130
  match: -> answer {
131
131
  pattern.replace answer
data/bin/on_change CHANGED
@@ -1,53 +1,118 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Monitor file for changes and execute command when modified
3
+ # Monitor a set of files (specified by glob patterns) for modifications.
4
+ # When any watched file changes, run an arbitrary shell command,
5
+ # optionally inserting the full path of that file via the placeholder %f.
4
6
  #
5
7
  # Usage:
6
- # on_change filename [command...] # Watch file and run command on changes
8
+ # ./on_change [OPTS] [COMMAND]
7
9
  #
8
- # This script monitors a file for modifications and executes a specified
9
- # command whenever the file is changed. It can be used for auto-reloading,
10
- # rebuilding, or triggering actions when source files are updated.
10
+ # Options:
11
+ # -i <glob> Glob pattern(s) of files to watch. Can be repeated
12
+ # e.g., `-i 'lib/**/*.rb' -i bin/*`.
13
+ # -d <sec> Sleep interval (seconds) when no changes are detected.
14
+ # Must be a positive integer; defaults to 1 if omitted or <=0.
15
+ # -h Show this help message and exit.
11
16
  #
12
- # If no command is provided, 'true' is executed by default.
17
+ # If [COMMAND] is not supplied the script defaults to running `true`.
13
18
  #
14
- # Special replacement:
15
- # %f in command will be replaced with the filename being monitored
19
+ # Command placeholder:
20
+ # The string `%f` inside COMMAND will be replaced by the full path of
21
+ # each file that changed. Example:
22
+ # ./on_change -i 'lib/**/*.rb' echo '%f has been modified'
16
23
  #
17
- # Examples:
18
- # on_change Gemfile bundle install
19
- # on_change tests/utils_test.rb ruby -I lib:tests %f
24
+ # It prints each executed command (for debugging) before streaming its
25
+ # output to STDOUT.
26
+
27
+ require 'set'
28
+ require 'tins/go'
29
+ include Tins::GO
30
+ require 'shellwords'
20
31
 
21
32
  # The execute method runs a command by processing its arguments and displaying
22
33
  # the output.
23
34
  #
24
35
  # @param args [ Array ] the command arguments to be executed
25
36
  def execute(*args)
26
- cmd = args.map(&:inspect) * ' '
27
- puts "Executing: #{cmd}"
37
+ cmd = Shellwords.join(args)
38
+ puts "Executing: #{cmd.inspect}"
28
39
  IO.popen(cmd, 'r') do |io|
29
40
  io.each { |l| puts l }
30
41
  end
31
42
  end
32
43
 
44
+ # Computes a set of file paths based on glob patterns specified in the global
45
+ # $opts[?i] option. It iterates over each pattern, expands it with Dir.glob,
46
+ # and merges the results into a Set to eliminate duplicates.
47
+ #
48
+ # @return [Set<String>] a set containing the unique file paths matched by the
49
+ # provided glob patterns
50
+ def compute_files
51
+ files = Set[]
52
+ $opts[?i].to_a.each do |glob|
53
+ files.merge Dir.glob(glob)
54
+ end
55
+ files
56
+ end
57
+
58
+ # The usage method displays a help message explaining command‑line options and
59
+ # exits the program with the supplied return code.
60
+ #
61
+ # @param rc [ Integer ] the exit status to use when terminating the process
62
+ def usage(rc)
63
+ puts <<~EOT
64
+ Usage: #{File.basename($0)} [OPTS] [COMMAND]
65
+
66
+ Options are
67
+
68
+ -i <glob> Glob pattern(s) of files to watch.
69
+ The option may be repeated; all matching paths will be monitored.
70
+ -d <sec> Sleep interval (in seconds) when no file changes are detected.
71
+ Defaults to 1 second if omitted or set to a non‑positive value.
72
+ -h Show this help message and exit.
73
+
74
+ If [COMMAND] is not supplied the script defaults to running `true`.
75
+
76
+ The command may contain the placeholder `%f`, which will be replaced by
77
+ the full path of each file that changed. For example:
78
+
79
+ #{File.basename($0)} -i 'lib/**/*.rb' -i 'bin/*' echo %f has been modified
80
+
81
+ EOT
82
+ exit rc
83
+ end
84
+
85
+ $opts = go 'i:d:h'
86
+
87
+ $opts[?h] and usage(0)
88
+
33
89
  argv = ARGV.dup
34
- filename = argv.shift or fail "require a filename as first argument"
35
90
  argv.empty? and argv << 'true'
36
- argv.map! { |a| a == '%f' ? filename : a }
37
- warn "Observing #{filename.inspect} for changes now and execute #{argv.inspect}."
38
- old_mtime = nil
91
+
92
+ files = compute_files
93
+ files.empty? and fail 'Require files to watch'
94
+
95
+ old_mtimes = {}
96
+ files.each do |filename|
97
+ mtime = File.mtime(filename)
98
+ old_mtimes[filename] = mtime
99
+ end
100
+
39
101
  loop do
40
- begin
41
- mtime = File.mtime(filename)
42
- if old_mtime.nil? || mtime > old_mtime
43
- execute(*argv)
44
- else
45
- sleep 0.1
102
+ files = compute_files
103
+
104
+ files.each do |filename|
105
+ begin
106
+ mtime = File.mtime(filename)
107
+ if !old_mtimes.key?(filename) || mtime > old_mtimes[filename]
108
+ execute(*argv.map { _1 == '%f' ? filename : _1 })
109
+ end
110
+ old_mtimes[filename] = mtime
111
+ rescue Interrupt
112
+ exit 0
113
+ rescue Errno::ENOENT
114
+ old_mtimes.delete(filename)
46
115
  end
47
- rescue Interrupt
48
- exit 1
49
- rescue Errno::ENOENT
50
- ensure
51
- old_mtime = mtime
52
116
  end
117
+ sleep $opts[?d].to_i.clamp(1..)
53
118
  end
data/bin/search CHANGED
@@ -200,6 +200,7 @@ def usage
200
200
 
201
201
  -n PATTERN only search files whose names match fuzzy PATTERN
202
202
  -N PATTERN only search files whose names match regexp PATTERN
203
+ -m NUMBER maximum number of files to return
203
204
  -s PATTERN skip lines that match fuzzy PATTERN
204
205
  -S PATTERN skip lines that match regexp PATTERN
205
206
  -A NUMBER displays NUMBER lines of context after the match
@@ -227,7 +228,7 @@ def usage
227
228
  exit 1
228
229
  end
229
230
 
230
- args = go 'r:p:I:A:B:C:s:S:n:N:a:i:G:P:cfFlLeEvgh'
231
+ args = go 'r:p:I:A:B:C:s:S:n:N:m:a:i:G:P:cfFlLeEvgh'
231
232
  args[?h] and usage
232
233
  pattern = ARGV.shift or usage
233
234
  roots = (ARGV.empty? ? [ Dir.pwd ] : ARGV).map { |f| File.expand_path(f) }
data/lib/utils/finder.rb CHANGED
@@ -150,9 +150,9 @@ class Utils::Finder
150
150
  end
151
151
  paths.compact!
152
152
  @paths, @output = paths.sort.transpose.values_at(-2, -1)
153
- if n = @args[?n]&.to_i
154
- @paths = @paths&.first(n) || []
155
- @output = @output&.first(n) || []
153
+ if m = @args[?m]&.to_i
154
+ @paths = @paths&.first(m) || []
155
+ @output = @output&.first(m) || []
156
156
  end
157
157
  self
158
158
  end
data/lib/utils/grepper.rb CHANGED
@@ -88,6 +88,11 @@ class Utils::Grepper
88
88
  raise ArgumentError, "needs to be an integer number >= 1"
89
89
  end
90
90
  end
91
+
92
+ if max_files = @args[?m].to_i and max_files > 0
93
+ @max_files = max_files
94
+ end
95
+
91
96
  @paths = []
92
97
  pattern_opts = opts.subhash(:pattern) | {
93
98
  :cset => @args[?a],
@@ -260,8 +265,17 @@ class Utils::Grepper
260
265
  end
261
266
  }
262
267
  find(*@roots, visit: visit) do |filename|
263
- match(filename)
268
+ if @max_files && @paths.length >= @max_files
269
+ break
270
+ else
271
+ match(filename)
272
+ end
273
+ end
274
+
275
+ if @max_files
276
+ @paths = @paths.first(@max_files)
264
277
  end
278
+
265
279
  @paths = @paths.sort_by(&:source_location)
266
280
  self
267
281
  end
@@ -33,13 +33,13 @@ else
33
33
  # The start method initializes the error logging output.
34
34
  #
35
35
  # This method writes a header message to the output indicating where the
36
- # error list will be stored, followed by a separator line made of dashes
36
+ # errors list will be stored, followed by a separator line made of dashes
37
37
  # that spans the width of the terminal.
38
38
  #
39
39
  # @param _ignore [ Object ] this parameter is ignored and exists for
40
40
  # interface compatibility
41
41
  def start(_ignore)
42
- output.puts "Storing error list in #{@errors_lst.path.inspect}: "
42
+ output.puts "Storing errors list in #{@errors_lst.path.inspect}: "
43
43
  output.puts ?━ * Tins::Terminal.columns
44
44
  end
45
45
 
data/lib/utils/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Utils
2
2
  # Utils version
3
- VERSION = '0.93.0'
3
+ VERSION = '0.95.0'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/utils.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: utils 0.93.0 ruby lib
2
+ # stub: utils 0.95.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "utils".freeze
6
- s.version = "0.93.0".freeze
6
+ s.version = "0.95.0".freeze
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.93.0
4
+ version: 0.95.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank