hookapp 0.0.7 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +2 -2
  6. data/LICENSE.md +31 -0
  7. data/OVERVIEW.md +80 -0
  8. data/README.md +282 -21
  9. data/Rakefile +1 -1
  10. data/bin/hook +127 -10
  11. data/buildnotes.md +29 -0
  12. data/hook.rdoc +63 -4
  13. data/hookapp.gemspec +9 -8
  14. data/html/App.html +119 -0
  15. data/html/GLI.html +99 -0
  16. data/html/GLI/Commands.html +99 -0
  17. data/html/GLI/Commands/Doc.html +99 -0
  18. data/html/GLI/Commands/MarkdownDocumentListener.html +717 -0
  19. data/html/Hook.html +113 -0
  20. data/html/HookApp.html +1222 -0
  21. data/html/Hooker.html +119 -0
  22. data/html/README_rdoc.html +328 -0
  23. data/html/String.html +427 -0
  24. data/html/created.rid +9 -0
  25. data/html/css/fonts.css +167 -0
  26. data/html/css/rdoc.css +619 -0
  27. data/html/fonts/Lato-Light.ttf +0 -0
  28. data/html/fonts/Lato-LightItalic.ttf +0 -0
  29. data/html/fonts/Lato-Regular.ttf +0 -0
  30. data/html/fonts/Lato-RegularItalic.ttf +0 -0
  31. data/html/fonts/SourceCodePro-Bold.ttf +0 -0
  32. data/html/fonts/SourceCodePro-Regular.ttf +0 -0
  33. data/html/images/add.png +0 -0
  34. data/html/images/arrow_up.png +0 -0
  35. data/html/images/brick.png +0 -0
  36. data/html/images/brick_link.png +0 -0
  37. data/html/images/bug.png +0 -0
  38. data/html/images/bullet_black.png +0 -0
  39. data/html/images/bullet_toggle_minus.png +0 -0
  40. data/html/images/bullet_toggle_plus.png +0 -0
  41. data/html/images/date.png +0 -0
  42. data/html/images/delete.png +0 -0
  43. data/html/images/find.png +0 -0
  44. data/html/images/loadingAnimation.gif +0 -0
  45. data/html/images/macFFBgHack.png +0 -0
  46. data/html/images/package.png +0 -0
  47. data/html/images/page_green.png +0 -0
  48. data/html/images/page_white_text.png +0 -0
  49. data/html/images/page_white_width.png +0 -0
  50. data/html/images/plugin.png +0 -0
  51. data/html/images/ruby.png +0 -0
  52. data/html/images/tag_blue.png +0 -0
  53. data/html/images/tag_green.png +0 -0
  54. data/html/images/transparent.png +0 -0
  55. data/html/images/wrench.png +0 -0
  56. data/html/images/wrench_orange.png +0 -0
  57. data/html/images/zoom.png +0 -0
  58. data/html/index.html +308 -0
  59. data/html/js/darkfish.js +84 -0
  60. data/html/js/navigation.js +105 -0
  61. data/html/js/navigation.js.gz +0 -0
  62. data/html/js/search.js +110 -0
  63. data/html/js/search_index.js +1 -0
  64. data/html/js/search_index.js.gz +0 -0
  65. data/html/js/searcher.js +229 -0
  66. data/html/js/searcher.js.gz +0 -0
  67. data/html/table_of_contents.html +409 -0
  68. data/lib/completion/hook_completion.bash +22 -0
  69. data/lib/completion/hook_completion.fish +31 -0
  70. data/lib/completion/hook_completion.zsh +22 -0
  71. data/lib/helpers/fuzzyfilefinder +0 -0
  72. data/lib/hook.rb +5 -1
  73. data/lib/hook/hookapp.rb +489 -0
  74. data/lib/hook/hooker.rb +1 -344
  75. data/lib/hook/markdown_document_listener.rb +164 -0
  76. data/lib/hook/string.rb +60 -0
  77. data/lib/hook/version.rb +3 -1
  78. metadata +76 -12
@@ -0,0 +1,22 @@
1
+ # Source this script from ~/.zshrc
2
+ _hook_targets()
3
+ {
4
+ local cmds cur ff
5
+ if (($COMP_CWORD == 1))
6
+ then
7
+ cur="${COMP_WORDS[1]}"
8
+ cmds="$(hook help -c)"
9
+ COMPREPLY=($(compgen -W "$cmds" -- "$cur"))
10
+ COMPREPLY=( "${COMPREPLY[@]/%/ }" )
11
+ else
12
+ # get all matching files and directories
13
+ COMPREPLY=($(compgen -f -- "${COMP_WORDS[$COMP_CWORD]}"))
14
+
15
+ for ((ff=0; ff<${#COMPREPLY[@]}; ff++)); do
16
+ [[ -d ${COMPREPLY[$ff]} ]] && COMPREPLY[$ff]+='/'
17
+ [[ -f ${COMPREPLY[$ff]} ]] && COMPREPLY[$ff]+=' '
18
+ done
19
+ fi
20
+ }
21
+
22
+ complete -o bashdefault -o default -o nospace -F _hook_targets hook
@@ -1,5 +1,9 @@
1
- require 'hook/version.rb'
1
+ # frozen_string_literal: true
2
2
 
3
+ require 'hook/version.rb'
3
4
  require 'shellwords'
4
5
  require 'cgi'
6
+ require 'hook/string.rb'
7
+ require 'hook/hookapp.rb'
5
8
  require 'hook/hooker.rb'
9
+ require 'hook/markdown_document_listener'
@@ -0,0 +1,489 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+ require 'uri'
5
+ # Hook.app functions
6
+ module HookApp
7
+ # Create a single regex for validation of an
8
+ # array by first char or full match.
9
+ def format_regex(options)
10
+ fmt_rx_array = []
11
+ options.map {|fmt| fmt_rx_array.push(fmt.sub(/^(.)(.*?)$/, '\1(\2)?')) }
12
+ Regexp.new("^(#{fmt_rx_array.join('|')})$",'i')
13
+ end
14
+
15
+ # Check if format fully matches or matches the first
16
+ # character of available options.
17
+ # Return full valid format or nil
18
+ def validate_format(fmt, options)
19
+ valid_format_rx = options.map { |format| format.sub(/^(.)(.*)$/, '^\1(\2)?$') }
20
+ valid_format = nil
21
+ valid_format_rx.each_with_index do |rx, i|
22
+ cmp = Regexp.new(rx, 'i')
23
+ next unless fmt =~ cmp
24
+
25
+ valid_format = options[i]
26
+ break
27
+ end
28
+ valid_format
29
+ end
30
+
31
+ # Get a Hook bookmark for file or URL. Return bookmark hash.
32
+ def bookmark_for(url)
33
+ url.valid_hook!
34
+ raise "Invalid target: #{url}" unless url
35
+
36
+ begin
37
+ mark = `osascript <<'APPLESCRIPT'
38
+ tell application "Hook"
39
+ set _hook to make bookmark with data "#{url}"
40
+ if _hook is missing value
41
+ return ""
42
+ else
43
+ return name of _hook & "||" & address of _hook & "||" & path of _hook
44
+ end if
45
+ end tell
46
+ APPLESCRIPT`.strip
47
+ rescue p => e
48
+ raise e
49
+ end
50
+
51
+ raise "Error getting bookmark for #{url}" if mark.empty?
52
+ mark.split_hook
53
+ end
54
+
55
+ # Get bookmarks hooked to file or URL. Return array of bookmark hashes.
56
+ def get_hooks(url)
57
+ url.valid_hook!
58
+ raise "Invalid target: #{url}" unless url
59
+
60
+ hooks = `osascript <<'APPLESCRIPT'
61
+ tell application "Hook"
62
+ set _mark to make bookmark with data "#{url}"
63
+ if _mark is missing value
64
+ return ""
65
+ end if
66
+ set _hooks to hooked bookmarks of _mark
67
+ set _out to {}
68
+ repeat with _hook in _hooks
69
+ set _out to _out & (name of _hook & "||" & address of _hook & "||" & path of _hook)
70
+ end repeat
71
+ set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "^^"}
72
+ set _output to _out as string
73
+ set AppleScript's text item delimiters to astid
74
+ return _output
75
+ end tell
76
+ APPLESCRIPT`.strip
77
+ hooks.split_hooks
78
+ end
79
+
80
+ # Get a bookmark from the foreground document of specified app.
81
+ def bookmark_from_app(app, opts)
82
+ mark = `osascript <<'APPLESCRIPT'
83
+ tell application "System Events" to set front_app to name of first application process whose frontmost is true
84
+ tell application "#{app}" to activate
85
+ delay 2
86
+ tell application "Hook"
87
+ set _hook to (bookmark from active window)
88
+ set _output to (name of _hook & "||" & address of _hook & "||" & path of _hook)
89
+ end tell
90
+ tell application front_app to activate
91
+ return _output
92
+ APPLESCRIPT`.strip.split_hook
93
+ title = mark[:name].empty? ? "#{app.cap} link" : mark[:name]
94
+ output = opts[:markdown] ? "[#{title}](#{mark[:url]})" : mark[:url]
95
+
96
+ if opts[:copy]
97
+ "Copied Markdown link for '#{title}' to clipboard" if output.clip
98
+ else
99
+ output
100
+ end
101
+ end
102
+
103
+ # Search boomark names/titles. Return array of bookmark hashes.
104
+ def search_name(search)
105
+ `osascript <<'APPLESCRIPT'
106
+ set searchString to "#{search.strip}"
107
+ tell application "Hook"
108
+ set _marks to every bookmark whose name contains searchString
109
+ set _out to {}
110
+ repeat with _hook in _marks
111
+ set _out to _out & (name of _hook & "||" & address of _hook & "||" & path of _hook)
112
+ end repeat
113
+ set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "^^"}
114
+ set _output to _out as string
115
+ set AppleScript's text item delimiters to astid
116
+ return _output
117
+ end tell
118
+ APPLESCRIPT`.strip.split_hooks
119
+ end
120
+
121
+ # Search bookmark paths and addresses. Return array of bookmark hashes.
122
+ def search_path_or_address(search)
123
+ `osascript <<'APPLESCRIPT'
124
+ set searchString to "#{search.strip}"
125
+ tell application "Hook"
126
+ set _marks to every bookmark whose path contains searchString or address contains searchString
127
+ set _out to {}
128
+ repeat with _hook in _marks
129
+ set _out to _out & (name of _hook & "||" & address of _hook & "||" & path of _hook)
130
+ end repeat
131
+ set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "^^"}
132
+ set _output to _out as string
133
+ set AppleScript's text item delimiters to astid
134
+ return _output
135
+ end tell
136
+ APPLESCRIPT`.strip.split_hooks
137
+ end
138
+
139
+ # Get all known bookmarks. Return array of bookmark hashes.
140
+ def all_bookmarks
141
+ `osascript <<'APPLESCRIPT'
142
+ tell application "Hook"
143
+ set _marks to every bookmark
144
+ set _out to {}
145
+ repeat with _hook in _marks
146
+ set _out to _out & (name of _hook & "||" & address of _hook & "||" & path of _hook)
147
+ end repeat
148
+ set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "^^"}
149
+ set _output to _out as string
150
+ set AppleScript's text item delimiters to astid
151
+ return _output
152
+ end tell
153
+ APPLESCRIPT`.strip.split_hooks
154
+ end
155
+
156
+ # Search bookmarks, using both names and addresses unless options contain ":names_only".
157
+ # Return results as formatted list.
158
+ def search_bookmarks(search, opts)
159
+ unless search.nil? || search.empty?
160
+ result = search_name(search)
161
+ unless opts[:names_only]
162
+ more_results = search_path_or_address(search)
163
+ result = result.concat(more_results).uniq
164
+ end
165
+ else
166
+ result = all_bookmarks
167
+ end
168
+
169
+ separator = opts[:format] == 'paths' && opts[:null_separator] ? "\0" : "\n"
170
+
171
+ output = output_array(result, opts)
172
+
173
+ if opts[:format] =~ /^v/
174
+ "Search results for: #{search}\n---------\n" + output.join("\n")
175
+ else
176
+ output.join(separator)
177
+ end
178
+ end
179
+
180
+ # Create a bookmark for specified file/url and copy to the clipboard.
181
+ def clip_bookmark(url, opts)
182
+ mark = bookmark_for(url)
183
+ copy_bookmark(mark[:name], mark[:url], opts)
184
+ end
185
+
186
+ # Create a bookmark from specified title and url and copy to the clipboard.
187
+ def copy_bookmark(title, url, opts)
188
+ raise "No URL found" if url.empty?
189
+ title = title.empty? ? 'No title' : title
190
+ output = opts[:markdown] ? "[#{title}](#{url})" : url
191
+ output.clip
192
+ %(Copied #{opts[:markdown] ? 'Markdown link' : 'Hook URL'} for '#{title}' to clipboard)
193
+ end
194
+
195
+ # Generate a menu of available hooks for selecting one or more hooks to operate on.
196
+ # Revamped to use `fzf`, which is embedded as `lib/helpers/fuzzyfilefinder` to avoid any conflicts.
197
+ # Allows multiple selections with tab key, and type-ahead fuzzy filtering of results.
198
+ def select_hook(marks)
199
+ # intpad = marks.length.to_s.length + 1
200
+ # marks.each_with_index do |mark, i|
201
+ # STDERR.printf "%#{intpad}d) %s\n", i + 1, mark[:name]
202
+ # end
203
+ # STDERR.print 'Open which bookmark: '
204
+ # sel = STDIN.gets.strip.to_i
205
+ # raise 'Invalid selection' unless sel.positive? && sel <= marks.length
206
+
207
+ # marks[sel - 1]
208
+
209
+ options = marks.map {|mark|
210
+ if mark[:name]
211
+ id = mark[:name]
212
+ elsif mark[:path]
213
+ id = mark[:path]
214
+ elsif mark[:url]
215
+ id = mark[:url]
216
+ else
217
+ return false
218
+ end
219
+
220
+ if mark[:path]
221
+ loc = File.dirname(mark[:path])
222
+ elsif mark[:url]
223
+ url = URI.parse(mark[:url])
224
+ id = mark[:url]
225
+ loc = url.scheme + " - " + url.hostname
226
+ else
227
+ loc = ""
228
+ end
229
+
230
+ "#{id}\t#{mark[:path]}\t#{mark[:url]}\t#{loc}"
231
+ }.delete_if { |mark| !mark }
232
+
233
+ raise "Error processing available hooks" if options.empty?
234
+
235
+ args = ['--layout=reverse-list',
236
+ '--header="esc: cancel, tab: multi-select, return: open > "',
237
+ '--prompt=" Select hooks > "',
238
+ '--multi',
239
+ '--tabstop=4',
240
+ '--delimiter="\t"',
241
+ '--with-nth=1,4',
242
+ '--height=60%',
243
+ '--min-height=10'
244
+ ]
245
+
246
+ fzf = File.join(File.dirname(__FILE__), '../helpers/fuzzyfilefinder')
247
+
248
+ sel = `echo #{Shellwords.escape(options.join("\n"))} | '#{fzf}' #{args.join(' ')}`.chomp
249
+ res = sel.split(/\n/).map { |s|
250
+ ps = s.split(/\t/)
251
+ { name: ps[0], path: ps[1], url: ps[2] }
252
+ }
253
+
254
+ if res.size == 0
255
+ raise 'Cancelled (empty response)'
256
+ end
257
+
258
+ res
259
+ end
260
+
261
+ # Open the Hook GUI for browsing/performing actions on a file or url
262
+ def open_gui(url)
263
+ result = `osascript <<'APPLESCRIPT'
264
+ tell application "Hook"
265
+ set _mark to make bookmark with data "#{url.valid_hook}"
266
+ if _mark is missing value
267
+ return "Failed to create bookmark for #{url}"
268
+ else
269
+ invoke on _mark
270
+ return ""
271
+ end if
272
+ end tell
273
+ APPLESCRIPT`.strip
274
+ raise result unless result.empty?
275
+ end
276
+
277
+ # Select from a menu of available hooks and open using macOS `open`.
278
+ def open_linked(url)
279
+ marks = get_hooks(url)
280
+ if marks.empty?
281
+ warn "No hooks found for #{url}"
282
+ else
283
+ res = select_hook(marks)
284
+ res.each {|mark|
285
+ `open '#{mark[:url]}'`
286
+ }
287
+ end
288
+ end
289
+
290
+ # Link 2 or more files/urls with bi-directional hooks.
291
+ def link_files(args)
292
+ target = args.pop
293
+ target.valid_hook!
294
+ raise "Invalid target: #{target}" unless target
295
+
296
+ args.each do |file|
297
+ file.valid_hook!
298
+ raise "Invalid target: #{file}" unless file
299
+
300
+ puts "Linking #{file} and #{target}..."
301
+ `osascript <<'APPLESCRIPT'
302
+ tell application "Hook"
303
+ set _mark1 to make bookmark with data "#{file}"
304
+ set _mark2 to make bookmark with data "#{target}"
305
+ hook _mark1 and _mark2
306
+ return true
307
+ end tell
308
+ APPLESCRIPT`
309
+ end
310
+ "Linked #{args.length} files to #{target}"
311
+ end
312
+
313
+ # Copy all hooks from source file to target file
314
+ def clone_hooks(args)
315
+ target = args.pop.valid_hook
316
+ source = args[0].valid_hook
317
+
318
+ if target && source
319
+ hooks = get_hooks(source)
320
+ hooks.each do |hook|
321
+ `osascript <<'APPLESCRIPT'
322
+ tell application "Hook"
323
+ set _mark1 to make bookmark with data "#{hook[:url]}"
324
+ set _mark2 to make bookmark with data "#{target}"
325
+ hook _mark1 and _mark2
326
+ return true
327
+ end tell
328
+ APPLESCRIPT`
329
+ end
330
+ "Hooks from #{source} cloned to #{target}"
331
+ else
332
+ raise 'Invalid file specified'
333
+ end
334
+ end
335
+
336
+ # Delete all hooked files/urls from target file
337
+ def delete_all_hooks(url)
338
+ STDERR.print "Are you sure you want to delete ALL hooks from #{url} (y/N)? "
339
+ res = STDIN.gets.strip
340
+ if res =~ /^y/i
341
+ get_hooks(url).each do |hook|
342
+ `osascript <<'APPLESCRIPT'
343
+ tell application "Hook"
344
+ set _mark1 to make bookmark with data "#{hook[:url]}"
345
+ set _mark2 to make bookmark with data "#{url}"
346
+ unhook _mark1 and _mark2
347
+ return true
348
+ end tell
349
+ APPLESCRIPT`
350
+ end
351
+ "Removed all hooks from #{url}"
352
+ end
353
+ end
354
+
355
+ # Delete hooks between two files/urls
356
+ def delete_hooks(args, opts)
357
+ urls = args.map(&:valid_hook).delete_if { |url| !url }
358
+ output = []
359
+ if opts[:all]
360
+ urls.each_with_index do |url, i|
361
+ raise "Invalid target: #{args[i]}" unless url
362
+
363
+ output.push(delete_all_hooks(url))
364
+ end
365
+ return output.join("\n")
366
+ end
367
+
368
+ if urls.length == 2
369
+ source = urls[0]
370
+ target = urls[1]
371
+ `osascript <<'APPLESCRIPT'
372
+ tell application "Hook"
373
+ set _mark1 to make bookmark with data "#{source}"
374
+ set _mark2 to make bookmark with data "#{target}"
375
+ unhook _mark1 and _mark2
376
+ return true
377
+ end tell
378
+ APPLESCRIPT`
379
+ "Hook removed between #{source} and #{target}"
380
+ else
381
+ raise 'Invalid number of URLs or files specified'
382
+ end
383
+ end
384
+
385
+ # Create bi-directional links between every file/url in the list of arguments
386
+ def link_all(args)
387
+ args.each do |file|
388
+ source = file.valid_hook
389
+ link_to = args.dup.map(&:valid_hook).reject { |url| url == source }
390
+ link_to.each do |url|
391
+ `osascript <<'APPLESCRIPT'
392
+ tell application "Hook"
393
+ set _mark1 to make bookmark with data "#{source}"
394
+ set _mark2 to make bookmark with data "#{url}"
395
+ hook _mark1 and _mark2
396
+ return true
397
+ end tell
398
+ APPLESCRIPT`
399
+ end
400
+ end
401
+ "Linked #{args.length} files to each other"
402
+ end
403
+
404
+ # Get a list of all hooks on a file/url.
405
+ def linked_bookmarks(args, opts)
406
+ result = []
407
+
408
+ separator = args.length == 1 && opts[:format] == 'paths' && opts[:null_separator] ? "\0" : "\n"
409
+
410
+ if args.nil? || args.empty?
411
+ result = output_array(all_bookmarks, opts)
412
+ else
413
+ args.each do |url|
414
+ source_mark = bookmark_for(url)
415
+ filename = source_mark[:name]
416
+
417
+ case opts[:format]
418
+ when /^m/
419
+ filename = "[#{source_mark[:name]}](#{source_mark[:url]})"
420
+ filename += " <file://#{CGI.escape(source_mark[:path])}>" if source_mark[:path]
421
+ when /^p/
422
+ filename = "File: #{source_mark[:name]}"
423
+ filename += " (#{source_mark[:path]})" if source_mark[:path]
424
+ when /^h/
425
+ filename = "File: #{source_mark[:name]}"
426
+ filename += " (#{source_mark[:url]})" if source_mark[:url]
427
+ else
428
+ filename = "Bookmarks attached to #{source_mark[:path] || source_mark[:url]}"
429
+ end
430
+
431
+ hooks_arr = get_hooks(url)
432
+
433
+ output = output_array(hooks_arr, opts)
434
+ result.push({ file: filename, links: output.join(separator) })
435
+ end
436
+
437
+
438
+ if result.length > 1 || opts[:format] == 'verbose'
439
+ result.map! do |res|
440
+ "#{res[:file]}\n\n#{res[:links]}\n"
441
+ end
442
+ else
443
+ result.map! do |res|
444
+ res[:links]
445
+ end
446
+ end
447
+ end
448
+ result.join(separator)
449
+ end
450
+
451
+ # Output an array of hooks in the given format.
452
+ def output_array(hooks_arr, opts)
453
+ if !hooks_arr.empty?
454
+ hooks_arr.reject! { |h| h[:path].nil? || h[:path] == '' } if opts[:files_only]
455
+
456
+ output = []
457
+
458
+ case opts[:format]
459
+ when /^m/
460
+ hooks_arr.each do |h|
461
+ if h[:name].empty?
462
+ title = h[:url]
463
+ else
464
+ title = h[:name]
465
+ end
466
+ output.push("- [#{title}](#{h[:url]})")
467
+ end
468
+ when /^p/
469
+ hooks_arr.each do |h|
470
+ output.push(h[:path].nil? ? h[:url] : h[:path])
471
+ end
472
+ when /^h/
473
+ hooks_arr.each do |h|
474
+ output.push(h[:url])
475
+ end
476
+ else
477
+ hooks_arr.each do |h|
478
+ output.push("Title: #{h[:name]}\nPath: #{h[:path]}\nAddress: #{h[:url]}\n---------------------")
479
+ end
480
+ end
481
+ else
482
+ output = ['No bookmarks']
483
+ end
484
+
485
+ output
486
+ end
487
+ end
488
+
489
+