How to support this blog?

To support this blog, you can hire me as an OmegaT consultant/trainer, or you can send translation and project management jobs my way.

Search the site:

An even newer version of my Capture app

This is a newer version of what I wrote on Sunday.

I figured a number of things.

  • I’m not going to call this app at random, so I could just as well action something in Emacs when I dismiss the dialog.
  • My default does not have to be selected. If it is not selected, I can use the fact that nothing is selected as the default.

So, I decided that dismissing the script would bring Emacs to the front, so that I can just write something without anything captured.

Also, I added an option to display the Capture interactive selector in Emacs, in case I want to capture something that’s not in the list (i.e., something that I would not capture that often).

I also put handlers, to kind of prettify the code.



The next step, as written in the script, is to combine that code with the “Open with Emacs” code that I have and check whether Finder is front, in which case I either open the selection or the enclosing folder.

I’ll try to not scratch that itch before I’m done with urgent work. . .


####################################


use AppleScript version "2.4" -- Yosemite (10.10) or later

use scripting additions

use framework "Foundation"


#########

# First, I'll be using the Foundation framework because otherwise it 

# would be non-trivial to find "the index of a given item in my list". 

# And for that I'll just need to use NSArrays. I did not figure that out 

# by myself, I found the hint on stackoverflow.

# reference: https://stackoverflow.com/a/65690139/5511978

#########


property NSArray : class "NSArray"


#########

# Then, I'm setting my paths to Emacs and emacsclient.

#########


set myEmacs to "/.../Emacs.app"

set myEmacsclient to "/.../Emacs.app/Contents/MacOS/bin/emacsclient"


#########

# Here, I create the list of template items I want to quickly access. I 

# have many more, but the others are more about long-form writing, so I 

# figured I'd be facing Emacs when I need to access them.

# This list is basically "capture", "todo", "done", "dictionary", "notes" and "drafts".

# Then I transform that into a Foundation array

#

# "capture" allows me to display the interactive template chooser.

#########


set myList to {"capture", "à faire", "fait", "dictionnaire", "notes", "brouillon"}

set myArray to NSArray's arrayWithArray:myList


#########

# Here are the template keys that I use in my Emacs interactive capture 

# template.

# Their order corresponds to the order of the above list items.

#########


set myKeys to {"c", "a", "f", "d", "n", "b"}


#########

# Now comes the interactive item selection window. That's the thing 

# that's apparently called when I call the app. All the rest takes place 

# behind the scenes.

# I can just hit Enter without selecting anything.

# I'll use that to call the default template, which is "fait".

#

# I can hit the first letter of any item for it to be selected, then I 

# hit Enter to simulate clicking on OK.

# The "capture" (c) key is special since it allows me to display the 

# Emacs interactive template selection dialog.

# I can dismiss the dialog with either hitting Escape or clicking on 

# Cancel. But since I'm not calling the dialog by mistake, I use that 

# action to just bring Emacs to the front.

#

# I'll probably use that in a future version to call Emacs on selected 

# file and folders to open them or open dired.

#

# Then I'll need to move all that into a macOS service that I call with 

# a simple shortcut so that I don't have to:

# - call Spotlight (Cmd+Space)

# - call the app (>C)

#########


try

set captureChoice to (choose from list myList with title "Emacs" with prompt "Défaut = \"fait\"" with empty selection allowed)

set captureChoice to item 1 of captureChoice

on error

if captureChoice is false then

myEmacsComesForward()

return

end if

if length of captureChoice is 0 then

set captureChoice to "fait"

end if

end try


#########

# Here comes the place where I need Foundation's arrays.

# Basically, the line says "You just selected something, find its index in the 

# list where it belongs, and get me the letter that has the same index 

# in the myKeys list."

#

# I'm adding a few parameters to see whether I'll use the contents of 

# the clipboard or not, and if I want to automatically send the template 

# key or let Emacs propose me the interactive selection dialog.

#########


set myTemplateKey to item ((myArray's indexOfObject:captureChoice) + 1) of myKeys



if myTemplateKey is in {"d", "n"} then

set useBody to true

else

set useBody to false

end if


if myTemplateKey is in {"c"} then

set interactiveCapture to true

else

set interactiveCapture to false

end if


#########

# For 2 items, "dictionnaire" and "notes", I'm thinking that I'll 

# probably have found something in a document that I'm reading, I'll need

# to copy it for use later on in the capture buffer.

# In the case of "dictionary", it's a word that I want to check, and I'll 

# use that as the title of the captured item.

# In the case of "notes", it's probably a sentence, or a paragraph that 

# I want to keep in the body of a captured item.

# I won't use the selection for the other items.

# For that, I'll put the selection (the clipboard) into the &body part of

# the org-protocol command.

#########


if useBody is true then

set myTemplateBody to "&body=" & (the clipboard)

else if useBody is false then

set myTemplateBody to ""

end if


#########

# The &body part, though, is not super clearly explained in the 

# org-mode manual:

#

# %i              The selected text

#

# Basically, the value of &body is the contents of %i in the capture template.

# I don't know why the manual mentions "the selected text" since you can put

# whatever you want in &body.

#

# In my templates, I've used it this way:

#

# ("n" "choses à noter

# notes | à lire | code | inspiration" entry (file "~/org/memo.org")

# "* [%u]  %?\n%i" :empty-lines 0 :unnarrowed nil)

#

# Here, %i, the contents of the clipboard, will be inserted in the body 

# of the capture buffer, under the headline (notice the "\n"). And the cursor

#  (%?) is waiting for me inside the headline.

#

# or:

#

# ("d" "Dictionnaire" entry (file+datetree "/Users/suzume/org/dico.org")

# "* [%<%H:%M>] %i\n%?" :empty-lines 0 :unnarrowed nil)

#

# where %i will be inside the headline and the body of the capture 

# (supposedly the term definition) will come after the line-break, where

# the cursor is waiting for my input (%?).

#########



#########

# Et voilà !

# The org-protocol command can be built, and sent to emacsclient:

#########


if interactiveCapture is true then

set myCaptureCommand to myEmacsclient & " -e \"(org-capture)\" -n"

else if interactiveCapture is false then

set myCaptureCommand to myEmacsclient & " \"org-protocol://capture?template=" & myTemplateKey & myTemplateBody & "\""

end if



#########

# Here, I make sure that Emacs is in front of me.

# If Emacs is not launched, it is now launched, which is important, 

# because my setting also starts emacs-server, without which I could not 

# use emacsclient.

#########


myEmacsComesForward()


#########

# And last but not least, the org-protocol command is sent to the 

# shell, and with Emacs at the front with the cursor waiting for me,

# I can start working right away in my capture buffer.

#########


myEmacsDoesSomething(myCaptureCommand)


#############################################


on myEmacsComesForward()

try

tell application "System Events" to tell process "Emacs" to set frontmost to true

on error

tell application myEmacs

activate

end tell

end try

end myEmacsComesForward


on myEmacsDoesSomething(myCaptureCommand)

try

do shell script myCaptureCommand

on error

log "Emacs au premier plan"

end try

end myEmacsDoesSomething


Popular, if not outdated, posts...

.docx .NET .pptx .sdf .xlsx AASync accented letters Accessibility Accessibility Inspector Alan Kay alignment Apple AppleScript ApplescriptObjC AppleTrans applications Aquamacs Arabic archive Automator backup bash BBEdit Better Call Saul bug Butler C Calculator Calendar Chinese Cocoa Command line CSV CSVConverter database defaults Devon Dictionary DITA DocBook Dock Doxygen EDICT Emacs emacs lisp ergonomics Excel external disk file formats file system File2XLIFF4j Finder Fink Font français Free software FSF Fun Get A Mac git GNU GPL Guido Van Rossum Heartsome Homebrew HTML IceCat Illustrator InDesign input system ITS iWork Japanese Java Java Properties Viewer Java Web Start json keybindings keyboard Keynote killall launchd LISA lisp locale4j localisation MacPort Mail markdown MARTIF to TBX Converter Maxprograms Mono MS Office NeoOffice Numbers OASIS Ocelot ODF Okapi OLPC OLT OmegaT OnMyCommand oo2po OOXML Open Solaris OpenDocument OpenOffice.org OpenWordFast org-mode OSX Pages PDF PDFPen PlainCalc PO Preview programming python QA Quick Look QuickSilver QuickTime Player Rainbow RAM reggy regular expressions review rsync RTFCleaner Safari Santa Claus scanner Script Debugger Script Editor scripting scripting additions sdf2txt security Services shell shortcuts Skim sleep Smultron Snow Leopard Spaces Spanish spellchecking Spotlight SRX standards StarOffice Stingray Study SubEthaEdit Swordfish System Events System Preferences TBX TBXMaker Terminal text editing TextEdit TextMate TextWrangler The Tool Kit Time Capsule Time Machine tmutil TMX TMX Editor TMXValidator transifex Translate Toolkit translation Transmug troubleshooting TS TTX TXML UI Browser UI scripting Unix VBA vi Virtaal VirtualBox VLC W3C WebKit WHATWG Windows Wine Word WordFast wordpress writing Xcode XLIFF xml XO xslt YAML ZFS Zip