utils 0.68.0 → 0.70.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +251 -18
  3. data/bin/ascii7 +28 -0
  4. data/bin/blameline +17 -0
  5. data/bin/changes +69 -5
  6. data/bin/classify +128 -7
  7. data/bin/code_comment +176 -120
  8. data/bin/commit_message +26 -2
  9. data/bin/create_cstags +18 -0
  10. data/bin/create_tags +10 -0
  11. data/bin/discover +38 -1
  12. data/bin/edit +14 -1
  13. data/bin/edit_wait +14 -0
  14. data/bin/enum +139 -15
  15. data/bin/git-empty +50 -0
  16. data/bin/git-versions +20 -0
  17. data/bin/json_check +15 -1
  18. data/bin/long_lines +11 -2
  19. data/bin/myex +38 -0
  20. data/bin/on_change +22 -0
  21. data/bin/path +21 -0
  22. data/bin/print_method +29 -1
  23. data/bin/probe +52 -4
  24. data/bin/rainbow +52 -0
  25. data/bin/rd2md +15 -0
  26. data/bin/search +83 -1
  27. data/bin/sedit +6 -0
  28. data/bin/serve +18 -3
  29. data/bin/ssh-tunnel +14 -2
  30. data/bin/strip_spaces +17 -9
  31. data/bin/sync_dir +48 -1
  32. data/bin/untest +19 -1
  33. data/bin/utils-utilsrc +42 -6
  34. data/bin/vcf2alias +33 -0
  35. data/bin/yaml_check +24 -2
  36. data/lib/utils/config_dir.rb +127 -0
  37. data/lib/utils/config_file.rb +445 -1
  38. data/lib/utils/editor.rb +215 -3
  39. data/lib/utils/finder.rb +127 -16
  40. data/lib/utils/grepper.rb +90 -1
  41. data/lib/utils/irb.rb +387 -39
  42. data/lib/utils/line_blamer.rb +28 -0
  43. data/lib/utils/line_formatter.rb +198 -0
  44. data/lib/utils/md5.rb +14 -0
  45. data/lib/utils/patterns.rb +77 -3
  46. data/lib/utils/probe_server.rb +302 -23
  47. data/lib/utils/ssh_tunnel_specification.rb +58 -0
  48. data/lib/utils/version.rb +1 -1
  49. data/lib/utils/xt/source_location_extension.rb +18 -6
  50. data/lib/utils.rb +3 -1
  51. data/tests/utils_test.rb +7 -1
  52. data/utils.gemspec +6 -6
  53. metadata +6 -8
  54. data/bin/number_files +0 -26
  55. data/lib/utils/xdg_config.rb +0 -10
  56. /data/{COPYING → LICENSE} +0 -0
data/bin/code_comment CHANGED
@@ -1,15 +1,73 @@
1
1
  #!/usr/bin/env ruby
2
+ #
3
+ # Generate YARD comments for Ruby methods using LLM assistance
4
+ #
5
+ # Usage:
6
+ # code_comment path/to/file.rb:line_number # Generate comment for method at line
7
+ #
8
+ # This script analyzes Ruby source code and generates high-quality YARD comments
9
+ # for methods using an LLM (via Ollama). It examines the surrounding context to
10
+ # identify method definitions and provides comprehensive documentation including
11
+ # descriptions, parameters, return values, and exceptions.
12
+ #
13
+ # Requires:
14
+ # - Git repository with Ruby files in lib/, spec/, or test/ directories
15
+ # - Ollama server running locally or accessible via OLLAMA_URL / OLLAMA_HOST
16
+ # - Configuration files in ~/.config/code_comment/
17
+ # - system.txt: System prompt for LLM
18
+ # - prompt.txt: Template for comment generation
19
+ # - client.json: Ollama client settings
20
+ # - options.json: Generation options (temperature, etc.)
21
+ #
22
+ # Environment variables:
23
+ # OLLAMA_URL: Base URL of Ollama server
24
+ # OLLAMA_MODEL: Model to use (default: llama3.1)
25
+ # XDG_CONFIG_HOME: Custom config directory location
26
+ # DEBUG: Set to 1 for verbose output and debug.log creation
27
+ #
28
+ # Examples:
29
+ # code_comment app/models/user.rb:42 # Document method at line 42
30
+ # code_comment lib/api/client.rb:15 # Document method at line 15
2
31
 
3
32
  require 'ollama'
4
33
  include Ollama
5
34
  require 'utils'
6
- include Utils::XDGConfig
7
35
 
8
- def fetch_method(filename_linenumber)
36
+ META_METHOD = /(
37
+ (class_)? # Optional "class_" prefix
38
+ attr( # "attr" keyword
39
+ _reader| # reader variant
40
+ _writer| # writer variant
41
+ _accessor # accessor variant
42
+ )? # Optional variant
43
+ | # OR
44
+ dsl_(accessor|reader) # DSL accessor patterns
45
+ | # OR
46
+ constant # Constant definitions
47
+ | # OR
48
+ config # config settings
49
+ | # OR
50
+ (instance_)? # Optional "instance_" prefix
51
+ thread( # "thread" keyword
52
+ _local| # local variant
53
+ _global # global variant
54
+ )? # Optional variant
55
+ )\s+:/x
56
+
57
+ def fetch_construct(filename_linenumber)
9
58
  result = ''
10
59
  source_location = filename_linenumber.source_location
11
60
  lf = Tins::LinesFile.for_filename(source_location.filename, source_location.linenumber)
12
- if spaces = lf.match_backward(/^(\s*|.*?(private|protected)\s+)def\s+(?:\S+?)(?:\(|\s*$)/)&.first
61
+ if /^(?:\s*)(class|module)/ =~ lf.line
62
+ return lf.line, $1.to_sym
63
+ end
64
+ if /^(?:\s*)(?:[A-Z]\w*)\s*=\s*(Class|Module)\.new/ =~ lf.line
65
+ return lf.line, $1.downcase.to_sym
66
+ end
67
+ if lf.line =~ META_METHOD
68
+ return lf.line, :method
69
+ end
70
+ if spaces = lf.match_backward(/^(\s*?)(\S.*?\s+)?def\s+/)&.first
13
71
  line_number_begin = lf.line_number
14
72
  lf.match_forward(/^#{spaces}end/)
15
73
  line_number_end = lf.line_number
@@ -18,137 +76,134 @@ def fetch_method(filename_linenumber)
18
76
  result << lf.line
19
77
  end
20
78
  end
21
- result
79
+ return result, :method
22
80
  end
23
81
 
24
- def fetch_file(filename_linenumber)
25
- source_location = filename_linenumber.source_location
26
- File.read(source_location.filename)
82
+ def build_method_prompt(construct_type, construct, file_contents)
83
+ default_prompt = <<~EOT
84
+ Look at this code to document it:
85
+
86
+ %{file_contents}
87
+
88
+ Here's an example for how you should document a %{construct_type}.
89
+
90
+ # The foo %{construct_type} computes the bar result by processing…
91
+ # then it returns the result.
92
+ #
93
+ # @param first [ String ] the foo string
94
+ # @param second [ Integer ] the number of bars lengthy detailed mutltiline
95
+ # detailed explanation including a newline.
96
+ # @param third [ TrueClass, FalseClass ]
97
+ #
98
+ # @yield [ a, b ]
99
+ #
100
+ # @raise [ ArgumentError ] if block argument wasn't provided
101
+ # @raise [ ArgumentError ] if second parameter was too small.
102
+ #
103
+ # @return [ ProcessResult ]
104
+
105
+ And this is the %{construct_type} you should document:
106
+
107
+ %{construct}
108
+
109
+ Output a YARD comment for this %{construct_type}:
110
+
111
+ Format requirements:
112
+
113
+ 1. Focus on providing a description of the %{construct_type}'s purpose
114
+ without including any code snippets.
115
+ 2. You should omit the @raise if you are not sure.
116
+ 3. Never use `, `ruby, ```, ```ruby in your response.
117
+ 4. Never add any other remarks or explanation to your response.
118
+ 5. Start each line of your comment with a single # character.
119
+ EOT
120
+ $config.read('method-prompt.txt', default: default_prompt) % {
121
+ construct_type:, construct:, file_contents:,
122
+ }
27
123
  end
28
124
 
29
- filename_linenumber = ARGV.shift or fail "require file_name as second argument"
30
- method = fetch_method(filename_linenumber)
31
- method_indent = method[/\A( *)/, 1].size
32
- #file = fetch_file(filename_linenumber)
33
- files = Dir['{lib,spec,test}/**/*.rb']
34
- base_url = ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
35
- model = ENV.fetch('OLLAMA_MODEL', 'llama3.1')
36
- #file = File.read(file_name)
37
- #call_sites = %x(cscope -L -3 "#{method_name}" $(find . -name '*.rb') | awk '{ print $1 ":" $3 }').lines.map(&:chomp).uniq
38
- #methods = call_sites.map { fetch_method(_1) } * ?\n
125
+ def build_class_module_prompt(construct_type, construct, file_contents)
126
+ default_prompt = <<~EOT
127
+ Look at this code to document it:
39
128
 
40
- cheatsheet = <<EOT
41
- Documenting Code with YARD
129
+ %{file_contents}
42
130
 
43
- By default, YARD is compatible with the same RDoc syntax most Ruby developers
44
- are already familiar with. However, one of the biggest advantages of YARD is
45
- the extended meta-data syntax, commonly known as "tags", that you can use to
46
- express small bits of information in a structured and formal manner. While RDoc
47
- syntax expects you to describe your method in a completely free-form manner,
48
- YARD recommends declaring your parameters, return types, etc. with the @tag
49
- syntax, which makes outputting the documentation more consistent and easier to
50
- read. Consider the RDoc documentation for a method to_format:
131
+ Here's an example for how you should document a %{construct_type}.
51
132
 
52
- # Converts the object into textual markup given a specific `format`
53
- # (defaults to `:html`)
54
- #
55
- # == Parameters:
56
- # format::
57
- # A Symbol declaring the format to convert the object to. This
58
- # can be `:text` or `:html`.
59
- #
60
- # == Returns:
61
- # A string representing the object in a specified
62
- # format.
63
- #
64
- def to_format(format = :html)
65
- # format the object
66
- end
133
+ # A brief description of what this %{construct_type} does.
134
+ #
135
+ # @example
136
+ # MyClass.new do |obj|
137
+ # obj.method
138
+ # end
67
139
 
68
- While this may seem easy enough to read and understand, it's hard for a machine
69
- to properly pull this data back out of our documentation. Also we've tied our
70
- markup to our content, and now our documentation becomes hard to maintain if we
71
- decide later to change our markup style (maybe we don't want the ":" suffix on
72
- our headers anymore).
140
+ And this is the %{construct_type} you should document:
73
141
 
74
- In YARD, we would simply define our method as:
142
+ %{construct}
75
143
 
76
- # Converts the object into textual markup given a specific format.
77
- #
78
- # @param format [Symbol] the format type, `:text` or `:html`
79
- # @return [String] the object converted into the expected format.
80
- # @raise [CannotFormatException] the object cannot be formatted
81
- def to_format(format = :html)
82
- # format the object
83
- end
144
+ Output a YARD comment for this %{construct_type}:
84
145
 
85
- Using tags we can add semantic metadata to our code without worrying about
86
- presentation. YARD will handle presentation for us when we decide to generate
87
- documentation later.
88
- EOT
146
+ Format requirements:
89
147
 
90
- system = <<EOT
91
- Provide high-quality YARD comments for the given Ruby method, including a brief
92
- description of its purpose, parameters, raised exceptions (and why), and
93
- return values, assuming an expert-level understanding of the programming
94
- concepts involved.
95
- EOT
96
- system = nil
97
-
98
- prompt = <<EOT
99
- Look at this code to document it:
100
-
101
- #{files.map { File.read(_1) } * ?\n}
102
-
103
- Here's an example for how you should document a method.
104
-
105
- # The foo method computes the bar result by processing…
106
- # then it returns the result.
107
- #
108
- # @param first [ String ] the foo string
109
- # @param second [ Integer ] the number of bars lengthy detailed mutltiline
110
- # detailed explanation including a newline.
111
- # @param third [ TrueClass, FalseClass ]
112
- #
113
- # @yield [ a, b ]
114
- #
115
- # @raise [ ArgumentError ] if block argument wasn't provided
116
- # @raise [ ArgumentError ] if second parameter was too small.
117
- #
118
- # @example
119
- # bar.foo("blub", 4) { |a, b| … } #=> …
120
- #
121
- # @return [ ProcessResult ]
122
- def foo(first, second, third: false, &block)
123
- block or raise ArgumentError, 'no &block'
124
- a = first * 2
125
- if second < 0
126
- raise ArgumentError, 'too small'
127
- end
128
- b = "foo" * second
129
- if third
130
- a = a * b
131
- end
132
- block_result = block.(a, b)
133
- result = process block_result
134
- return result
135
- end
148
+ 1. Focus on providing a description of the %{construct_type}'s purpose
149
+ without including any code snippets.
150
+ 2. Include @example tag if there are notable usage patterns for this
151
+ construct.
152
+ 3. Never use `, `ruby, ```, ```ruby in your response.
153
+ 4. Never add any other remarks or explanation to your response.
154
+ 5. Start each line of your comment with a single # character.
155
+ EOT
156
+ $config.read('class-module-prompt.txt', default: default_prompt) % {
157
+ construct_type:, construct:, file_contents:,
158
+ }
159
+ end
136
160
 
137
- And this is the method you should document:
161
+ def build_prompt(construct_type, construct, file_contents)
162
+ case construct_type
163
+ when :method
164
+ build_method_prompt(construct_type, construct, file_contents)
165
+ when :class, :module
166
+ build_class_module_prompt(construct_type, construct, file_contents)
167
+ else
168
+ fail "unknown construct_type #{construct_type.inspect}"
169
+ end
170
+ end
138
171
 
139
- #{method}
172
+ filename_linenumber = ARGV.shift or fail "require file_name as second argument"
173
+ $config = Utils::ConfigDir.new('code_comment', env_var: 'XDG_CONFIG_HOME')
174
+ files = Dir['{lib,spec,test}/**/*.rb']
175
+ base_url = ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
176
+ model = ENV.fetch('OLLAMA_MODEL', 'llama3.1')
177
+ construct, construct_type = fetch_construct(filename_linenumber)
178
+ construct_indent = construct[/\A( *)/, 1].size
179
+ file_contents = files.map { _1 + ":\n" + File.read(_1) } * ?\n
180
+ #call_sites = %x(cscope -L -3 "#{method_name}" $(find . -name '*.rb') | awk '{ print $1 ":" $3 }').lines.map(&:chomp).uniq
181
+ #methods = call_sites.map { fetch_method(_1) } * ?\n
182
+
183
+ default_system = <<~EOT
184
+ **Guidelines**
140
185
 
141
- Output a YARD comment for this method:
186
+ - When answering, assume an expert-level understanding of the programming
187
+ concepts involved.
142
188
 
143
- Format requirements:
189
+ - If given a **method**, provide high-quality YARD comments including:
190
+ * A brief description of its purpose
191
+ * Parameter documentation (@param)
192
+ * Return value documentation (@return)
193
+ * Exception documentation (@raise) when appropriate
194
+ * Avoid version information (@since)
144
195
 
145
- 1. Focus on providing a description of the method's purpose, without including any code snippets.
146
- 2. Never use `, `ruby, ```, ```ruby in your response.
147
- 3. Never add any other remarks or explanation to your response.
148
- 4. Start each line of your comment with a single # character.
196
+ - If given a **class or module**, provide high-quality YARD comments including:
197
+ * A brief description of its purpose
198
+ * Public interface documentation
199
+ * @example usage patterns when helpful
200
+ * Avoid version information (@since)
149
201
  EOT
202
+ system = $config.read('system.txt', default: default_system)
150
203
 
151
- options = Ollama::Options.new(
204
+ prompt = build_prompt(construct_type, construct, file_contents)
205
+
206
+ default_options = JSON(
152
207
  #repeat_penalty: 1.8,
153
208
  num_ctx: 16384,
154
209
  num_predict: 512,
@@ -159,6 +214,11 @@ options = Ollama::Options.new(
159
214
  min_p: 0.1,
160
215
  )
161
216
 
217
+ client_config = Client::Config.load_from_json $config + 'client.json'
218
+ client_config.base_url = base_url
219
+ ollama = Client.configure_with(client_config)
220
+ options = JSON.parse($config.read('options.json', default: default_options))
221
+
162
222
  if ENV['DEBUG'].to_i == 1
163
223
  File.open('debug.log', ?w) do |log|
164
224
  log.puts "system:\n#{system}"
@@ -168,9 +228,5 @@ if ENV['DEBUG'].to_i == 1
168
228
  end
169
229
  end
170
230
 
171
- config = xdg_config('code_comment')
172
- client_config = Client::Config.load_from_json "#{config}/client.json"
173
- client_config.base_url = base_url
174
- ollama = Client.configure_with(client_config)
175
231
  response = ollama.generate(model:, system:, prompt:, options:, stream: false, think: false).response
176
- puts response.gsub(/^/) { ' ' * method_indent }
232
+ puts response.gsub(/^/) { ' ' * construct_indent }
data/bin/commit_message CHANGED
@@ -1,9 +1,33 @@
1
1
  #!/usr/bin/env ruby
2
+ #
3
+ # Git Commit Message Generator
4
+ #
5
+ # Generates AI-powered commit messages based on git changes using Ollama.
6
+ # Reads configuration from $XDG_CONFIG_HOME/commit_message/ directory.
7
+ #
8
+ # Usage:
9
+ # commit_message
10
+ #
11
+ # The script automatically:
12
+ # - Determines current git branch name
13
+ # - Loads configuration files (client.json, options.json, system.txt, prompt.txt)
14
+ # - Generates commit message using ollama_cli with the current diff as input
15
+ #
16
+ # Requirements:
17
+ # - Git repository in current directory
18
+ # - ollama_cli tool installed
19
+ # - Ollama server running
20
+ # - Configuration files in $XDG_CONFIG_HOME/commit_message/
21
+ #
22
+ # Configuration files:
23
+ # - client.json: Ollama API client configuration
24
+ # - options.json: Generation options
25
+ # - system.txt: System prompt for AI model
26
+ # - prompt.txt: Commit message template, contains %{branch} and %{stdin}
2
27
 
3
28
  require 'utils'
4
- include Utils::XDGConfig
5
29
 
6
- config = xdg_config('commit_message')
30
+ config = Utils::ConfigDir.new('commit_message', env_var: 'XDG_CONFIG_HOME')
7
31
 
8
32
  branch = `git rev-parse --abbrev-ref HEAD`.chomp
9
33
  exec 'ollama_cli', '-c', "#{config}/client.json",
data/bin/create_cstags CHANGED
@@ -1,4 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
+ #
3
+ # Create cscope database for project files
4
+ #
5
+ # Usage:
6
+ # create_cstags # Generate cscope.out database in current directory
7
+ #
8
+ # This script generates a cscope database (cscope.out) by collecting all
9
+ # project files and building an index for efficient cross-reference searching.
10
+ # It uses bundle paths and project roots to determine which files to include.
11
+ #
12
+ # Requires:
13
+ # - cscope command-line tool
14
+ #
15
+ # The script will:
16
+ # 1. Collect all files from project roots (including bundle paths)
17
+ # 2. Generate cscope.out database with cross-references
18
+ # 3. Show progress using infobar visualization
19
+ # 4. Report the size of the created database
2
20
 
3
21
  require 'utils'
4
22
  require 'infobar'
data/bin/create_tags CHANGED
@@ -1,4 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
+ #
3
+ # Creates ctags index for Ruby project including bundled gems
4
+ #
5
+ # This script generates a tags file that enables code navigation in editors
6
+ # like Vim. It automatically includes:
7
+ # - Current directory (.)
8
+ # - All gem paths from bundle list
9
+ # - Excludes pkg directory to avoid vendor code
10
+ #
11
+ # Usage: Run directly from project root
2
12
 
3
13
  require 'infobar'
4
14
 
data/bin/discover CHANGED
@@ -1,13 +1,43 @@
1
1
  #!/usr/bin/env ruby
2
+ #
3
+ # File Discovery and Search Tool
4
+ #
5
+ # Interactive file discovery and search utility with advanced filtering and
6
+ # editing capabilities. Combines file system traversal with intelligent pattern
7
+ # matching and Vim integration.
8
+ #
9
+ # Features:
10
+ # - Interactive pattern input with live search results
11
+ # - File system discovery with directory traversal
12
+ # - Vim integration for editing discovered files
13
+ # - Configurable search filters (regexp, fuzzy, case-insensitive)
14
+ # - Search index management and caching
15
+ # - Directory listing and leaf directory identification
16
+ # - Character set filtering for patterns
17
+ # - Interactive selection when multiple matches exist
18
+ #
19
+ # The tool creates a searchable index of your file system (which is also used
20
+ # by the search tool) and provides interactive discovery of files matching your
21
+ # criteria, with seamless Vim
22
+ # integration.
2
23
 
3
24
  require 'utils'
4
25
  include Utils
5
- require 'tins/xt'
6
26
  include Tins::GO
7
27
  require 'search_ui'
8
28
  include SearchUI
9
29
  require 'pathname'
10
30
 
31
+ # The edit_files method opens editor windows for specified file paths.
32
+ #
33
+ # This method allows users to edit one or more files using the configured
34
+ # editor. It provides options to pick a specific file from multiple paths or
35
+ # edit all files directly. The method supports waiting for the editor process
36
+ # to complete before returning control.
37
+ #
38
+ # @param paths [ Array<String> ] the file paths to be edited
39
+ # @param pick [ TrueClass, FalseClass ] whether to prompt for file selection
40
+ # @param wait [ TrueClass, FalseClass ] whether to wait for the editor to finish
11
41
  def edit_files(*paths, pick: false, wait: true)
12
42
  editor = Utils::Editor.new
13
43
  if pick
@@ -25,6 +55,13 @@ def edit_files(*paths, pick: false, wait: true)
25
55
  end
26
56
  end
27
57
 
58
+ # The usage method displays the help message and version information for the
59
+ # application.
60
+ #
61
+ # This method prints a formatted usage message to standard output, including
62
+ # the application name, available options, and version number. It also shows
63
+ # detailed descriptions of each command-line option and their expected
64
+ # arguments.
28
65
  def usage
29
66
  puts <<-EOT
30
67
  Usage: #{File.basename($0)} [OPTS] [PATTERN] [PATHS]
data/bin/edit CHANGED
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
+ #
3
+ # Enhanced vim editor launcher with advanced feature
4
+ #
5
+ # This script is a sophisticated wrapper around vim that provides enhanced
6
+ # editing capabilities including server management, git integration, and
7
+ # batch file handling.
2
8
 
3
9
  require 'tins/xt'
4
10
  include Tins::GO
@@ -6,6 +12,13 @@ require 'utils'
6
12
  include Utils
7
13
  require 'tempfile'
8
14
 
15
+ # The usage method displays the command-line interface help text and version
16
+ # information.
17
+ #
18
+ # This method outputs a formatted help message that describes the available
19
+ # options and usage patterns for the command-line tool, including detailed
20
+ # explanations of each flag and their purposes. It also displays the current
21
+ # version of the tool.
9
22
  def usage
10
23
  puts <<-EOT
11
24
  Usage: #{File.basename($0)} [OPTS] [PATHS]
@@ -49,7 +62,7 @@ if $opt[?l]
49
62
  elsif $opt[?s]
50
63
  begin
51
64
  until STDIN.eof?
52
- line = STDIN.gets
65
+ line = STDIN.gets.chomp
53
66
  line = line.sub(/.*?([^:\s]+:)/, '\1')
54
67
  editor.edit(line)
55
68
  end
data/bin/edit_wait CHANGED
@@ -1,3 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
+ #
3
+ # Wait for editor to close files before returning control
4
+ #
5
+ # Usage:
6
+ # edit_wait file1 file2 # Wait for editor to close specified files
7
+ #
8
+ # This script is a simple wrapper that calls 'edit -w' with all provided arguments.
9
+ # The '-w' flag makes the editor wait until the file(s) are closed before returning.
10
+ #
11
+ #
12
+ # Examples:
13
+ # edit_wait README.md # Edit README.md and wait for closure
14
+ # edit_wait file1.txt file2.txt # Edit multiple files and wait for all to close
15
+
2
16
 
3
17
  exec 'edit', '-w', *$*