hiiro 0.1.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/bin/h ADDED
@@ -0,0 +1,415 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pry"
4
+
5
+ # Use the gem if installed, otherwise define inline
6
+ begin
7
+ require "hiiro"
8
+ rescue LoadError
9
+ require "fileutils"
10
+ require "yaml"
11
+ end
12
+
13
+ class Hiiro
14
+ def self.init(*args, plugins: [], logging: false, **values, &block)
15
+ args = $ARGV if args.empty?
16
+
17
+ new($0, *args, logging: logging, **values).tap do |hiiro|
18
+ hiiro.load_plugins(*plugins)
19
+
20
+ hiiro.add_subcmd(:edit, **values) { |*args|
21
+ system(ENV['EDITOR'] || 'nvim', hiiro.bin)
22
+ }
23
+
24
+ block.call(hiiro) if block
25
+ end
26
+ end
27
+
28
+ def self.load_env
29
+ Config.plugin_files.each do |plugin_file|
30
+ require plugin_file
31
+ end
32
+ end
33
+
34
+ attr_reader :bin, :bin_name, :all_args
35
+ attr_reader :subcmd, :args
36
+ attr_reader :loaded_plugins
37
+ attr_reader :logging
38
+ attr_reader :global_values
39
+
40
+ def initialize(bin, *args, logging: false, **values)
41
+ @bin = bin
42
+ @bin_name = File.basename(bin)
43
+ @all_args = args
44
+ @subcmd, *@args = args # normally i would never do this
45
+ @loaded_plugins = []
46
+ @logging = logging
47
+ @global_values = values
48
+ end
49
+
50
+ def run
51
+ result = runner.run(*args)
52
+
53
+ handle_result(result)
54
+
55
+ exit 1
56
+ rescue => e
57
+ puts "ERROR: #{e.message}"
58
+ puts e.backtrace
59
+ exit 1
60
+ end
61
+
62
+ def handle_result(result)
63
+ exit 0 if result.nil? || result
64
+
65
+ exit 1
66
+ end
67
+
68
+ def runnable?
69
+ runner
70
+ end
71
+
72
+ def runner
73
+ runners.runner || runners.default_subcommand
74
+ end
75
+
76
+ def runners
77
+ @runners ||= Runners.new(self)
78
+ end
79
+
80
+ def add_default(**values, &handler)
81
+ runners.add_subcommand(:DEFAULT, handler, **global_values, **values)
82
+ end
83
+
84
+ def add_subcommand(name, **values, &handler)
85
+ runners.add_subcommand(name, handler, **global_values, **values)
86
+ end
87
+ alias add_subcmd add_subcommand
88
+
89
+ def full_name
90
+ runner&.full_name || [bin_name, subcmd].join(?-)
91
+ end
92
+
93
+ def subcommand_names
94
+ runners.subcommand_names
95
+ end
96
+
97
+ def pins = @pins ||= Pin.new(self)
98
+
99
+ def load_plugins(*plugins)
100
+ plugins.flatten.each { |plugin| load_plugin(plugin) }
101
+ end
102
+
103
+ def load_plugin(plugin_const)
104
+ return if @loaded_plugins.include?(plugin_const)
105
+
106
+ plugin_const.load(self)
107
+ @loaded_plugins.push(plugin_const)
108
+ end
109
+
110
+ def help
111
+ ambiguous = runners.ambiguous_matches
112
+
113
+ if ambiguous.any?
114
+ puts "Ambiguous subcommand #{subcmd.inspect}!"
115
+ puts ""
116
+ puts "Did you mean one of these?"
117
+ list_runners(ambiguous)
118
+ else
119
+ subcmd_msg = "Subcommand required!"
120
+ subcmd_msg = "Subcommand #{subcmd.inspect} not found!" if subcmd
121
+
122
+ puts subcmd_msg
123
+ puts ""
124
+ puts "Possible subcommands:"
125
+ list_runners(runners.all_runners)
126
+ end
127
+ end
128
+
129
+ def list_runners(list)
130
+ max_name = list.map { |r| r.subcommand_name.length }.max || 0
131
+ max_type = list.map { |r| r.type.to_s.length }.max || 0
132
+ max_params = list.map { |r| r.params_string.to_s.length }.max || 0
133
+
134
+ list.each do |r|
135
+ name = r.subcommand_name.ljust(max_name)
136
+ type = "(#{r.type})".ljust(max_type + 2)
137
+ params = r.params_string
138
+ params_col = params ? params.ljust(max_params) : ''.ljust(max_params)
139
+ location = r.location
140
+ puts " #{name} #{params_col} #{type} #{location}"
141
+ end
142
+ end
143
+
144
+ def log(message)
145
+ return unless logging
146
+
147
+ puts "[Hiiro: #{bin_name} #{(runner&.subcommand_name || subcmd).inspect}]: #{message}"
148
+ end
149
+
150
+ def parsed_args
151
+ i = Args.new(*args)
152
+ end
153
+
154
+ def get_value(name)
155
+ runner&.values&.[](name)
156
+ end
157
+
158
+ class Config
159
+ class << self
160
+ def plugin_files
161
+ Dir.glob(File.join(plugin_dir, '*'))
162
+ end
163
+
164
+ def plugin_dir
165
+ config_dir('plugins')
166
+ end
167
+
168
+ def config_dir(subdir=nil)
169
+ File.join(Dir.home, '.config/hiiro', *[subdir].compact).tap do |config_path|
170
+ FileUtils.mkdir_p(config_path) unless Dir.exist?(config_path)
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ class Runners
177
+ attr_reader :hiiro, :bin_name, :subcmd, :subcommands
178
+
179
+ def initialize(hiiro)
180
+ @hiiro = hiiro
181
+ @bin_name = hiiro.bin_name
182
+ @subcmd = hiiro.subcmd
183
+ @subcommands = {}
184
+ end
185
+
186
+ def runner
187
+ return default_subcommand if subcmd.to_s == ''
188
+
189
+ exact_runner || unambiguous_runner
190
+ end
191
+
192
+ def default_subcommand
193
+ Subcommand.new(bin_name, :default, lambda { hiiro.help; false })
194
+ end
195
+
196
+ def subcommand_names
197
+ all_runners.map(&:subcommand_name)
198
+ end
199
+
200
+ def all_runners
201
+ [*all_bins, *subcommands.values]
202
+ end
203
+
204
+ def paths
205
+ @paths ||= ENV['PATH'].split(?:).uniq
206
+ end
207
+
208
+ def all_bins
209
+ pattern = format('{%s}/%s-*', paths.join(?,), bin_name)
210
+
211
+ Dir.glob(pattern).map { |path| Bin.new(bin_name, path) }
212
+ end
213
+
214
+ def add_subcommand(name, handler, **values)
215
+ @subcommands[name] = Subcommand.new(bin_name, name, handler, values)
216
+ end
217
+
218
+ def exact_runner
219
+ all_runners.find { |r| r.exact_match?(subcmd) }
220
+ end
221
+
222
+ def unambiguous_runner
223
+ return nil if subcmd.nil?
224
+
225
+ matches = matching_runners
226
+ return matches.first if matches.count == 1
227
+
228
+ nil
229
+ end
230
+
231
+ def ambiguous_matches
232
+ return [] if subcmd.nil?
233
+
234
+ matches = matching_runners
235
+ return matches if matches.count > 1
236
+
237
+ []
238
+ end
239
+
240
+ def matching_runners
241
+ remove_child_runners(all_matching_runners)
242
+ end
243
+
244
+ def all_matching_runners
245
+ all_runners.select { |r| r.match?(subcmd) }
246
+ end
247
+
248
+ def remove_child_runners(list)
249
+ list.reject do |r|
250
+ list.any? { |other| r != other && r.full_name.start_with?(other.full_name + ?-) }
251
+ end
252
+ end
253
+
254
+ # if i wanted to reduce the lines of code, i could combine Bin/Subcommand
255
+ # but i would have to introduce a new attr and arg to the constructor: @type
256
+ # then for methods like #full_name and #run i would have to branch off of
257
+ # @type
258
+ class Bin
259
+ attr_reader :bin_name, :path, :name
260
+ alias full_name name
261
+
262
+ def initialize(bin_name, path)
263
+ @bin_name = bin_name
264
+ @path = path
265
+ @name = File.basename(path)
266
+ end
267
+
268
+ def run(*args)
269
+ system(path, *args)
270
+ end
271
+
272
+ def exact_match?(subcmd)
273
+ subcommand_name == subcmd.to_s
274
+ end
275
+
276
+ def match?(subcmd)
277
+ subcommand_name.start_with?(subcmd.to_s)
278
+ end
279
+
280
+ def subcommand_name
281
+ name.sub("#{bin_name}-", '')
282
+ end
283
+
284
+ def values
285
+ {}
286
+ end
287
+
288
+ def type
289
+ :bin
290
+ end
291
+
292
+ def location
293
+ path
294
+ end
295
+
296
+ def params_string
297
+ nil
298
+ end
299
+ end
300
+
301
+ class Subcommand
302
+ attr_reader :bin_name, :name, :handler, :values
303
+ alias subcommand_name name
304
+
305
+ def initialize(bin_name, name, handler, values={})
306
+ @bin_name = bin_name
307
+ @name = name.to_s
308
+ @handler = handler
309
+ @values = values || {}
310
+ end
311
+
312
+ def run(*args)
313
+ handler.call(*args)
314
+ end
315
+
316
+ def exact_match?(subcmd)
317
+ name == subcmd.to_s
318
+ end
319
+
320
+ def match?(subcmd)
321
+ name.start_with?(subcmd.to_s)
322
+ end
323
+
324
+ def full_name
325
+ [bin_name, name].join(?-)
326
+ end
327
+
328
+ def type
329
+ :subcommand
330
+ end
331
+
332
+ def location
333
+ handler.source_location&.join(':')
334
+ end
335
+
336
+ def params_string
337
+ return nil unless handler.respond_to?(:parameters)
338
+
339
+ params = handler.parameters
340
+ return nil if params.empty?
341
+ return nil if params == [[:rest]] || params == [[:rest, :args]]
342
+
343
+ params.map { |type, name|
344
+ case type
345
+ when :req then "<#{name}>"
346
+ when :opt then "[#{name}]"
347
+ when :rest then "[*#{name}]" if name
348
+ when :keyreq then "<#{name}:>"
349
+ when :key then "[#{name}:]"
350
+ when :keyrest then "[**#{name}]" if name
351
+ when :block then "[&#{name}]" if name
352
+ end
353
+ }.compact.join(' ')
354
+ end
355
+ end
356
+ end
357
+
358
+ class Args
359
+ attr_reader :raw_args
360
+
361
+ def initialize(*raw_args)
362
+ @raw_args = raw_args
363
+ end
364
+
365
+ def flags
366
+ @flags ||= proc {
367
+ raw_args.select { |arg|
368
+ arg.match?(/^-[^-]/)
369
+ }.flat_map { |arg|
370
+ arg.sub(/^-/, '').chars
371
+ }
372
+ }.call
373
+ end
374
+
375
+ def flag?(flag)
376
+ flags.include?(flag)
377
+ end
378
+
379
+ def flag_value(flag)
380
+ found_flag = false
381
+ raw_args.each do |arg|
382
+ if found_flag
383
+ return arg
384
+ end
385
+
386
+ if arg.match?(/^-\w*#{flag}/)
387
+ found_flag = true
388
+ end
389
+ end
390
+
391
+ nil
392
+ end
393
+
394
+ def values
395
+ raw_args.reject do |arg|
396
+ arg.match?(/^-/)
397
+ end
398
+ end
399
+ end
400
+ end
401
+
402
+ Hiiro.load_env
403
+
404
+
405
+ # only run the runner if being called from the commandline
406
+ if __FILE__ == $0
407
+ hiiro = Hiiro.init(*ARGV, plugins: [Pins, Project, Task], cwd: Dir.pwd)
408
+
409
+ hiiro.add_subcommand(:ping) { |*args|
410
+ puts "pong"
411
+ }
412
+
413
+ hiiro.run
414
+ end
415
+
data/bin/h-buffer ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ load File.join(Dir.home, 'bin', 'h')
4
+
5
+ o = Hiiro.init(*ARGV, plugins: [Pins])
6
+
7
+ o.add_subcmd(:ls) { |*args|
8
+ system('tmux', 'list-buffers', *args)
9
+ }
10
+
11
+ o.add_subcmd(:show) { |*args|
12
+ system('tmux', 'show-buffer', *args)
13
+ }
14
+
15
+ o.add_subcmd(:save) { |*args|
16
+ system('tmux', 'save-buffer', *args)
17
+ }
18
+
19
+ o.add_subcmd(:load) { |*args|
20
+ system('tmux', 'load-buffer', *args)
21
+ }
22
+
23
+ o.add_subcmd(:set) { |*args|
24
+ system('tmux', 'set-buffer', *args)
25
+ }
26
+
27
+ o.add_subcmd(:paste) { |*args|
28
+ system('tmux', 'paste-buffer', *args)
29
+ }
30
+
31
+ o.add_subcmd(:delete) { |*args|
32
+ system('tmux', 'delete-buffer', *args)
33
+ }
34
+
35
+ o.add_subcmd(:choose) { |*args|
36
+ system('tmux', 'choose-buffer', *args)
37
+ }
38
+
39
+ o.add_subcmd(:clear) { |*args|
40
+ # Delete all buffers
41
+ buffers = `tmux list-buffers -F '\#{buffer_name}'`.strip.split("\n")
42
+ buffers.each { |buf| system('tmux', 'delete-buffer', '-b', buf) }
43
+ puts "Cleared #{buffers.count} buffers"
44
+ }
45
+
46
+ o.run
data/bin/h-pane ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ load File.join(Dir.home, 'bin', 'h')
4
+
5
+ o = Hiiro.init(*ARGV, plugins: [Pins])
6
+
7
+ o.add_subcmd(:ls) { |*args|
8
+ system('tmux', 'list-panes', *args)
9
+ }
10
+
11
+ o.add_subcmd(:lsa) { |*args|
12
+ system('tmux', 'list-panes', '-a', *args)
13
+ }
14
+
15
+ o.add_subcmd(:split) { |*args|
16
+ system('tmux', 'split-window', *args)
17
+ }
18
+
19
+ o.add_subcmd(:splitv) { |*args|
20
+ system('tmux', 'split-window', '-v', *args)
21
+ }
22
+
23
+ o.add_subcmd(:splith) { |*args|
24
+ system('tmux', 'split-window', '-h', *args)
25
+ }
26
+
27
+ o.add_subcmd(:kill) { |*args|
28
+ system('tmux', 'kill-pane', *args)
29
+ }
30
+
31
+ o.add_subcmd(:swap) { |*args|
32
+ system('tmux', 'swap-pane', *args)
33
+ }
34
+
35
+ o.add_subcmd(:zoom) { |*args|
36
+ system('tmux', 'resize-pane', '-Z', *args)
37
+ }
38
+
39
+ o.add_subcmd(:capture) { |*args|
40
+ system('tmux', 'capture-pane', *args)
41
+ }
42
+
43
+ o.add_subcmd(:select) { |*args|
44
+ system('tmux', 'select-pane', *args)
45
+ }
46
+
47
+ o.add_subcmd(:move) { |*args|
48
+ system('tmux', 'move-pane', *args)
49
+ }
50
+
51
+ o.add_subcmd(:break) { |*args|
52
+ system('tmux', 'break-pane', *args)
53
+ }
54
+
55
+ o.add_subcmd(:join) { |*args|
56
+ system('tmux', 'join-pane', *args)
57
+ }
58
+
59
+ o.add_subcmd(:resize) { |*args|
60
+ system('tmux', 'resize-pane', *args)
61
+ }
62
+
63
+ o.run
data/bin/h-plugin ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+
5
+ load File.join(Dir.home, 'bin', 'h')
6
+
7
+ BASE_DIR = File.join(Dir.home, '.config/hiiro/plugins')
8
+ o = Hiiro.init(*ARGV, plugins: [Tmux, Pins], dir: BASE_DIR)
9
+
10
+ def plugin_files
11
+ Dir.glob(File.join(BASE_DIR, '**/*'))
12
+ end
13
+
14
+ o.add_subcmd(:path) { |*args|
15
+ print o.get_value(:dir)
16
+ }
17
+
18
+ o.add_subcmd(:ls) { |*args|
19
+ puts plugin_files
20
+ }
21
+
22
+ o.add_subcmd(:edit) { |*args|
23
+ if args.none?
24
+ system(ENV['EDITOR'] || 'safe_nvim', __FILE__)
25
+ else
26
+ plugins = plugin_files.select{|f| args.any?{|arg| File.basename(f).start_with?(arg) } }
27
+
28
+ if plugins.none?
29
+ puts "No matching plugins found for: #{args.map(&:inspect).join(' ')}"
30
+ else
31
+ system(ENV['EDITOR'] || 'safe_nvim', *plugins)
32
+ end
33
+ end
34
+ }
35
+
36
+ o.add_subcmd(:rg) { |*args|
37
+ Dir.chdir(o.get_value(:dir))
38
+ system('rg', '-S', *args)
39
+ }
40
+
41
+ o.add_subcmd(:rgall) { |*args|
42
+ Dir.chdir(o.get_value(:dir))
43
+ system('rg', '-S', '--no-ignore-vcs', *args)
44
+ }
45
+
46
+ begin
47
+ o.run
48
+ rescue => e
49
+ binding.pry
50
+ end
51
+
data/bin/h-session ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ load File.join(Dir.home, 'bin', 'h')
4
+
5
+ o = Hiiro.init(*ARGV, plugins: [Pins])
6
+
7
+ o.add_subcmd(:ls) { |*args|
8
+ system('tmux', 'list-sessions', *args)
9
+ }
10
+
11
+ o.add_subcmd(:new) { |*args|
12
+ system('tmux', 'new-session', *args)
13
+ }
14
+
15
+ o.add_subcmd(:kill) { |*args|
16
+ system('tmux', 'kill-session', *args)
17
+ }
18
+
19
+ o.add_subcmd(:attach) { |*args|
20
+ system('tmux', 'attach-session', *args)
21
+ }
22
+
23
+ o.add_subcmd(:rename) { |*args|
24
+ system('tmux', 'rename-session', *args)
25
+ }
26
+
27
+ o.add_subcmd(:switch) { |*args|
28
+ system('tmux', 'switch-client', *args)
29
+ }
30
+
31
+ o.add_subcmd(:detach) { |*args|
32
+ system('tmux', 'detach-client', *args)
33
+ }
34
+
35
+ o.add_subcmd(:has) { |*args|
36
+ system('tmux', 'has-session', *args)
37
+ }
38
+
39
+ o.add_subcmd(:info) { |*args|
40
+ system('tmux', 'display-message', '-p', '#{session_name}: #{session_windows} windows, #{session_attached} attached', *args)
41
+ }
42
+
43
+ o.run