-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathEdit-Anywhere.kmmacros
619 lines (520 loc) · 16.1 KB
/
Edit-Anywhere.kmmacros
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>Activate</key>
<string>Normal</string>
<key>CreationDate</key>
<real>459387347.72605699</real>
<key>IsActive</key>
<true/>
<key>Macros</key>
<array>
<dict>
<key>Actions</key>
<array>
<dict>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<true/>
<key>MacroActionType</key>
<string>Comment</string>
<key>Text</key>
<string>If you want to change where the temporary file is stored, edit the EDIT_ANYWHERE_FILE variable.
Note that the filename (and path, if any) will be relative to your $HOME. So, for example, if you want to use a file named ~/Dropbox/txt/my-very-special-name-here.txt then you should set the variable to "Dropbox/txt/my-very-special-name-here.txt"
Do NOT include the /Users/YourNameHere/ part of the path, that will be added automatically later.
Do not change the variable name (EDIT_ANYWHERE_FILE) just change the part in the "To:" field.
</string>
<key>Title</key>
<string>Note: You can customize 'EDIT_ANYWHERE_FILE'</string>
</dict>
<dict>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<true/>
<key>MacroActionType</key>
<string>SetVariableToText</string>
<key>Text</key>
<string>edit_anywhere.txt</string>
<key>Variable</key>
<string>EDIT_ANYWHERE_FILE</string>
</dict>
<dict>
<key>ActionName</key>
<string>Store to the name of the active application to the variable ‘EditAnywhereApp’</string>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>MacroActionType</key>
<string>SetVariableToText</string>
<key>Text</key>
<string>%Application%1%</string>
<key>Variable</key>
<string>EditAnywhereApp</string>
</dict>
<dict>
<key>ActionName</key>
<string>Type the ⌘A Keystroke (This should trigger 'Select All Text')</string>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>KeyCode</key>
<integer>0</integer>
<key>MacroActionType</key>
<string>SimulateKeystroke</string>
<key>Modifiers</key>
<integer>256</integer>
<key>ReleaseAll</key>
<false/>
</dict>
<dict>
<key>ActionName</key>
<string>Type the ⌘A Keystroke (This should trigger 'Select All Text')</string>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>KeyCode</key>
<integer>0</integer>
<key>MacroActionType</key>
<string>SimulateKeystroke</string>
<key>Modifiers</key>
<integer>256</integer>
<key>ReleaseAll</key>
<false/>
</dict>
<dict>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>MacroActionType</key>
<string>Pause</string>
<key>Time</key>
<string>1</string>
<key>TimeOutAbortsMacro</key>
<true/>
</dict>
<dict>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>KeyCode</key>
<integer>7</integer>
<key>MacroActionType</key>
<string>SimulateKeystroke</string>
<key>Modifiers</key>
<integer>256</integer>
<key>ReleaseAll</key>
<false/>
</dict>
<dict>
<key>Action</key>
<string>ByTyping</string>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>MacroActionType</key>
<string>InsertText</string>
<key>Paste</key>
<false/>
<key>Text</key>
<string>(Waiting for EditAnywhere)</string>
</dict>
<dict>
<key>ActionName</key>
<string>Type the ⌘A Keystroke (This should trigger 'Select All Text')</string>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>KeyCode</key>
<integer>0</integer>
<key>MacroActionType</key>
<string>SimulateKeystroke</string>
<key>Modifiers</key>
<integer>256</integer>
<key>ReleaseAll</key>
<false/>
</dict>
<dict>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>MacroActionType</key>
<string>Pause</string>
<key>Time</key>
<string>.5</string>
<key>TimeOutAbortsMacro</key>
<true/>
</dict>
<dict>
<key>ActionName</key>
<string>This is where the magic happens</string>
<key>DisplayKind</key>
<string>None</string>
<key>IncludeStdErr</key>
<true/>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<true/>
<key>MacroActionType</key>
<string>ExecuteShellScript</string>
<key>Path</key>
<string>/usr/local/scripts/km-edit-anywhere-any-editor.sh</string>
<key>Text</key>
<string>#!/bin/zsh -f
## This is the app that you want to use as your external editor
## Note that it should not include the '.app' suffix
##
## Examples:
## APP='BBEdit'
## APP='TextWrangler'
## APP='MultiMarkdown Composer'
##
APP='BBEdit'
## OS X no longer has a good way to ensure that GUI apps like
## Keyboard Maestro have access to your $PATH so I often just create
## a text file in my $HOME which I can use in all my scripts. This
## checks to see if that file exists, and if it does not exist, it
## will use the PATH as defined in the second half of the if/else
if [[ -e "$HOME/.path" ]]
then
source "$HOME/.path"
else
PATH=/usr/local/scripts:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin
fi
####|####|####|####|####|####|####|####|####|####|####|####|####|####|####
#
# You should not ~need~ to edit anything below this line
#
######################################################################
## This is the name of the temporary file which will be used to keep
## your edits before it is sent back to the app where it came from.
##
## This is now based on a variable set in the Keyboard Maestro macro
##
## If, for some reason, the filename is NOT set, use a reasonable default
[[ "$KMVAR_EDIT_ANYWHERE_FILE" == "" ]] && KMVAR_EDIT_ANYWHERE_FILE='edit_anywhere.txt'
## Try to protect against someone putting '..' in the file path and
## attempting to get out of the $HOME I don't know if this is
## actually necessary, but once it occurred to me, I figured I might
## as well add it.
FILE=`echo "${HOME}/${KMVAR_EDIT_ANYWHERE_FILE}" | tr -s '.' '.'`
NAME="[Edit Anywhere via Keyboard Maestro]"
## Change this if necessary
## Used for pbcopy and pbpaste
LANG=en_US.UTF-8
zmodload zsh/datetime ## needed for strftime
function timestamp { strftime "%Y-%m-%d--%H.%M.%S" "$EPOCHSECONDS" }
# This gives us a unique reference point for when the script was launched
TIME=`strftime "%Y-%m-%d--%H.%M.%S" "$EPOCHSECONDS"`
if (( $+commands[growlnotify] ))
then
## We found the growlnotify tool installed
GROWLNOTIFY_FOUND='yes'
## We only check to see if Growl.app is running IFF we have
## found growlnotify because otherwise there's no point caring.
if [ "`pgrep -x Growl`" = "" ]
then
## Growl is NOT running
GROWL_RUNNING='no'
else
## Growl IS running
GROWL_RUNNING='yes'
fi
else
## We did NOT find the growlnotify tool installed
GROWLNOTIFY_FOUND='no'
## We just set this to 'no' because it doesn't matter, if the
## growlnotify tool isn't found too.
GROWL_RUNNING='no'
fi
if [ "$GROWL_RUNNING" = "yes" -a "$GROWLNOTIFY_FOUND" = "yes" ]
then
## If growlnotify is installed AND Growl.app is running, use Growl for notifications
function msg
{
growlnotify \
--appIcon "$APP" \
--identifier "$NAME" \
--message "$@" \
--title "$NAME"
} ## end of msg/growl
elif (( $+commands[terminal-notifier] ))
then
## OTHERWISE, use terminal-notifier for notifications, if it is installed.
function msg
{
if [[ -e "/Applications/$APP.app/Contents/Info.plist" ]]
then
## If there is an app plist where we'd expect it to be, try to get the Bundle identifier from it
TN_BUNDLE_ID=`defaults read /Applications/${APP}.app/Contents/Info CFBundleIdentifier`
fi
## If we were not able to get a bundle ID, use Keyboard Maestro's
[[ "$TN_BUNDLE_ID" == "" ]] && TN_BUNDLE_ID='com.stairways.keyboardmaestro.editor'
terminal-notifier \
-title "$NAME" \
-subtitle "`timestamp`" \
-group "$NAME" \
-sender "$TN_BUNDLE_ID" \
-activate "$TN_BUNDLE_ID" \
-message "$@"
} ## end of msg/terminal-notifier
else
function msg { echo "$NAME: $@" | tee -a "${TMPDIR-/tmp/}${NAME}.$TIME.log" }
fi
###########################################################################################
###########################################################################################
##
## Here is where the action begins
##
## What do we do if they asked for a path which does not exist?
## Well, we will try to create the folder and then paste the file in there
PARENT_DIR="$FILE:h"
[[ ! -d "$PARENT_DIR" ]] && mkdir -p "$PARENT_DIR" && msg "Created $PARENT_DIR for $FILE"
if [[ -e "$FILE" ]]
then
pbpaste >> "$FILE"
else
pbpaste > "$FILE"
fi
EXIT="$?"
if [ "$EXIT" != "0" ]
then
msg "FATAL ERROR: Failed to create file at $FILE. Exit = $EXIT. Sorry."
## if we can't create the file, send the contents of the
## pasteboard to a temp file in TextEdit at least so the user
## will not lose whatever they had there at least
/usr/bin/pbpaste | /usr/bin/open -e -f
exit 1
fi
if [[ "$KMVAR_EditAnywhereApp" == "" ]]
then
## "$KMVAR_EditAnywhereApp" is not set. ABORT.
msg "Variable KMVAR_EditAnywhereApp is empty. Giving up."
## Open the file in the default text editor
## so the user knows their text has not been lost
/usr/bin/open -t "$FILE"
exit 1
else
## Tell the user what app we took text from
## This is mostly a debugging comment and could be removed if it annoys you
msg "Will send text back to: $KMVAR_EditAnywhereApp"
fi
if (( $+commands[bbedit] ))
then
## We found the command line bbedit tool
CL_BBEDIT='yes'
else
## We did NOT find the command line bbedit tool
CL_BBEDIT='no'
fi
if [[ "$CL_BBEDIT" = "yes" && "$APP:l" == "bbedit" ]]
then
## IFF the user wants to use BBEdit and they have the bbedit
## command line tool installed, then we will use the bbedit tool
## if BBEdit is not running, launch it
pgrep -xq BBEdit || (open -a BBEdit && sleep 3)
## We will try to open the app with the bbedit command,
## but if it fails we will open TextEdit
bbedit \
--language 'Markdown' \
--create-unix \
--new-window \
--resume \
--wait \
-- "$FILE" \
|| open -n -e -W "$FILE"
else
## if we get here, either the app is NOT BBEdit
## or they do not have the `bbedit` tool installed
## We will try to open the file in the requested app
## but if the open command fails for some reason,
## we will open TextEdit
open -a "$APP" -n -W "$FILE" || open -a -n -e -W "$FILE"
fi # if app is BBEdit
EXIT="$?"
if [ "$EXIT" != "0" ]
then
## If we get here, something went wrong
msg "Unable to open $FILE in $APP."
## Show the file in the Finder, so the user can try to do something with it
/usr/bin/open -R "$FILE"
exit 1
fi
## If we get here, `open` or `bbedit` exited successfully
## Make sure that the app we want to paste into is actually running
## it *should* be, but it's possible that it either crashed or the
## user quit it in the interim, so let's check
pgrep -xq "$KMVAR_EditAnywhereApp"
EXIT="$?"
if [ "$EXIT" = "1" ]
then
## If we get here, the app is NOT running, so let's tell the user
## that we are launching it.
msg "Launching $KMVAR_EditAnywhereApp so we can paste to it"
## Here is where we actually launch the app
open -a "$KMVAR_EditAnywhereApp"
## now we need to give it a few seconds to get itself running
## This is guesswork, because we don't know how long it will
## take for the app to launch and be ready to accept input
sleep 5
fi
## Tell the user we are ready to paste information back to the source app
msg "Pasting text to $KMVAR_EditAnywhereApp"
## Activate the app
## NOTE: originally this was not happening fast enough
## for it to be ready to accept a 'paste'
## when this script finished
## so I added some extra 'padding'
## and an extra 'open' call
## This should not harm anything.
open -a "$KMVAR_EditAnywhereApp"
## wait a second
sleep 1
## No no, REALLY activate the app
open -a "$KMVAR_EditAnywhereApp"
## copy the file back to the pasteboard
pbcopy < "$FILE"
## OK, now we are done with the FILE so we have to either leave it there,
## delete it, or move it to the trash.
## I don't want to leave it there when it has already been used,
## and I don't want to delete it in case something went wrong
## So I'm going to move it to the trash instead
## AND I'm going to rename the file with the current time stamp so if I
## happen to have several of these and need to recover one,
## I can do that, as long as I haven't emptied my trash
## This will be 'edit_anywhere' without the path or extension
## unless you changed $FILE
SHORT="$FILE:t:r"
## This will be 'txt' unless you changed $FILE
EXT="$FILE:e"
/bin/mv -n "$FILE" "$HOME/.Trash/$SHORT.`timestamp`.$EXT"
exit 0
#EOF
</string>
<key>TimeOutAbortsMacro</key>
<true/>
<key>TimeOutPeriod</key>
<real>86400</real>
<key>TrimResults</key>
<true/>
<key>TrimResultsNew</key>
<true/>
<key>UseText</key>
<true/>
</dict>
<dict>
<key>ActionName</key>
<string>Paste contents of file back to "Source" app</string>
<key>Conditions</key>
<dict>
<key>ConditionList</key>
<array>
<dict>
<key>ConditionType</key>
<string>Menu</string>
<key>MenuConditionSelectionType</key>
<string>Is</string>
<key>MenuConditionType</key>
<string>IsEnabled</string>
<key>MenuModifiers</key>
<integer>256</integer>
<key>MenuShortcut</key>
<string>C</string>
<key>MenuTitle</key>
<string>Paste</string>
</dict>
</array>
<key>ConditionListMatch</key>
<string>All</string>
</dict>
<key>ElseActions</key>
<array>
<dict>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<true/>
<key>KeyCode</key>
<integer>9</integer>
<key>MacroActionType</key>
<string>SimulateKeystroke</string>
<key>Modifiers</key>
<integer>256</integer>
<key>ReleaseAll</key>
<false/>
</dict>
</array>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<false/>
<key>MacroActionType</key>
<string>IfThenElse</string>
<key>ThenActions</key>
<array>
<dict>
<key>IsActive</key>
<true/>
<key>IsDisclosed</key>
<true/>
<key>MacroActionType</key>
<string>SelectMenuItem</string>
<key>Menu</key>
<array>
<string>Edit</string>
<string>Paste</string>
</array>
<key>TargetingType</key>
<string>Front</string>
</dict>
</array>
<key>TimeOutAbortsMacro</key>
<true/>
</dict>
</array>
<key>CreationDate</key>
<real>459387347.72605699</real>
<key>IsActive</key>
<true/>
<key>ModificationDate</key>
<real>460507471.884552</real>
<key>Name</key>
<string>Edit Anywhere</string>
<key>Triggers</key>
<array>
<dict>
<key>FireType</key>
<string>Pressed</string>
<key>KeyCode</key>
<integer>11</integer>
<key>MacroTriggerType</key>
<string>HotKey</string>
<key>Modifiers</key>
<integer>6912</integer>
</dict>
</array>
<key>UID</key>
<string>63DA32F6-8BA8-49AD-B4BC-C9AC7761D87E</string>
</dict>
</array>
<key>Name</key>
<string>Global Macro Group</string>
<key>ToggleMacroUID</key>
<string>4948A9C1-5CD3-4AA1-A25D-9D29C5471ED8</string>
<key>UID</key>
<string>DA8512F7-F323-46B1-8319-752E0495CFD0</string>
</dict>
</array>
</plist>