Usage: Select a file, presss Command-I, change “Open with” to “vim-app”, click “Change All”. Then double-click any of the files of this type. It will be opened a command-line Vim.
on open theFiles
set command to {}
set firstFile to (item 1 of theFiles) as string
tell application "Finder" to set pathFolderParent to quoted form of (POSIX path of ((folder of item firstFile) as string))
set end of command to "cd" & space & (pathFolderParent as string)
set end of command to ";vi" -- vi or any other command-line text editor
set fileList to {}
repeat with i from 1 to (theFiles count)
set end of fileList to space & quoted form of (POSIX path of (item i of theFiles as string))
end repeat
set end of command to (fileList as string)
set end of command to ";exit" -- if Terminal > Settings > Profiles > Shell > When the shell exits != Don't close the window
set command to command as string
set myTab to null
tell application "Terminal"
if it is not running then
set myTab to (do script command in window 1)
else
set myTab to (do script command)
end if
activate
end tell
return
end open
Now, I want to improve it, so that I can keep it in Dock, click, and it will start a new Vim session. That is, in addition to ability to edit existing files, I want to add an option to create new ones.
For this, I tried to add an if block in the very beginning, adopted from one Automator script, but it doesn’t work. When click the vim-app in dock, nothing happens.
What is wrong there?
on open theFiles
set command to {}
if theFiles is null or theFiles is {} or ((item 1 in theFiles) as string) is "" then -- ADDED
set end of command to "vim;exit"
else
set firstFile to (item 1 of theFiles) as string
tell application "Finder" to set pathFolderParent to quoted form of (POSIX path of ((folder of item firstFile) as string))
set end of command to "cd" & space & (pathFolderParent as string)
set end of command to ";hx" -- e.g. vi or any other command-line text editor
set fileList to {}
repeat with i from 1 to (theFiles count)
set end of fileList to space & quoted form of (POSIX path of (item i of theFiles as string))
end repeat
set end of command to (fileList as string)
set end of command to ";exit" -- if Terminal > Settings > Profiles > Shell > When the shell exits != Don't close the window
end if
set command to command as string
set myTab to null
tell application "Terminal"
if it is not running then
set myTab to (do script command in window 1)
else
set myTab to (do script command)
end if
activate
end tell
return
end open
The open handler not being called, so nothing will happen here.
You need a “run” handler to get things going.
something like:
on Run
set theFiles to choose file with prompt ("Select files") of type {"txt"} default location (path to desktop) multiple selections allowed true
tell me to open theFiles as list
end run
(You can, of course, change the prompt, file type and default location as needed).
FWIW, I have a script I use with mpv (also run in the terminal) that has both open and run handlers as intakes. This way, I can drop movies on it (open handler) or leave it on the dock to work with the selected files (run handler).
Both intake handlers then call a third handler and feed the file specifications to it to actually play the movies.
Here is the relevant portion of the script. I’ve dropped the parts related to constructing an mpv command and substituted a simple text file command.
Basically, once I’ve tested it out as a script, I export it to an application. Depending on what I’m doing, I might put that on the dock or might put an alias in a folder full of files. Then I can either ‘select and click’ or ‘drag and drop’ to launch the movies. To make it more relevant to your situation, I’ve rigged it to only accept .txt files.
The script has an explicit ‘run’ handler as well as an ‘open’ handler. So depending upon whether you click on it in the dock or drop files on it, one or the other handler will get called. They’re both the same though except the run handler has to get the selected files whereas the open handler just accepts any dropped files. Whichever handler is called checks the extension and adds acceptable files to a list, which it feeds as an argument to the main handler.
The main handler (an implicit ‘run’) here checks to see whether there is a terminal window open or not, and makes one if there isn’t. It then organises the list of files into a space-separated stream to put in a shell command and runs that in the front window.
NB You can have only one ‘run’ handler in a script. This is why the main segment is in its own handler.
All it does now is run cat -b on the files. Change that line to your vim command. I’m not sure exactly what you want to do with multiple files so I went with something non-destructive and easy. I’ve added some comments so hopefully everything will make sense.
Update: Added the vim;exit line, commented out the cat line. It should now open the specified files in vim all at once in a single window/tab.
use scripting additions
-- take finder selection, generate list of text files (posix file)
-- feed list to 'vim' handler
on run
tell application "Finder" to set tinput to selection as alias list
set playList to {}
repeat with aa in tinput
if ext(aa as text) then -- if file extension is acceptable
set end of playList to POSIX path of aa
end if
end repeat
vim(playList)
end run
-- take dropped files, generate list of text files (posix file)
-- feed list to 'vim' handler
on open tinput
set playList to {}
repeat with aa in tinput
if ext(aa as text) then -- if file extension is acceptable
set end of playList to POSIX path of aa
end if
end repeat
vim(playList)
end open
on vim(fl)
set shelList to {} -- list of passed files
repeat with i from 1 to length of fl -- cycle through passed files
set end of shelList to quoted form of contents of item i of fl -- quoted file added to list
end repeat
set text item delimiters to space -- use as separator
set fileStream to (shelList as text) -- create list of space-separated quoted files for shell
-- set catmand to "cat -b " & fileStream -- command to run
set catmand to "vim " & fileStream & "; exit"
tell application "Terminal"
activate
delay 1 / 30
set winList to windows whose visible is true -- check if windows exist
if winList is {} then -- if not, make new window
do script
end if
delay 1 / 30
-- run command in front terminal
do script catmand in front window -- note that it's not 'do shell script'
end tell
set text item delimiters to ""
end vim
-- take file path, return true if extension is acceptable
on ext(tex)
set tf to false
set mvExt to {"txt"}
set text item delimiters to "."
set lti to last text item of tex
if lti is in mvExt then
set tf to true
end if
return tf
end ext
Hello, thanks. But your current solution opens the “Select files” window. Could you show how to just execute the “vim; exit” command in the shell instead?
For a regular stay-open application, the reopen handler is what gets called when reopening the app, such as from a click of the Dock icon, so add the handler and do the new session there.
You know, in fact I try to strip unncessary parts and also to “convert” from Automator to AppleScript this one:
I have a strong feeling that changing this one part
if input is null or input is {} or ((item 1 in input) as string) is "" then
-- no files, open vim without parameters
set end of command to "vim;exit"
else
so that it will work in pure AppleScript, without Automator, should be really easy. Easily than we are trying currently.
There are a few ways that an application can be opened, with or without arguments. The open handler only gets called when dropping files onto the app, so an if statement there isn’t going to do much. I’ve been cleaning up a stripped down script application sample to post in Code Exchange, so I wound up using the workflow in your original post for testing.
The following is a stay-open script application that can be double-clicked (which shows a choose dialog), files can be dropped onto the app (droplet), blank documents are opened by using an added file menu item (or the the Dock menu with the shift key), multiple selections are opened in a tabbed window, and the app can even be run from the command line using open -a with --args (I don’t think I missed much, let me know if I did). The application can also be command-dragged into the Finder window toolbar, where it will open the current selection (or a new blank document with the shift key) so that you don’t necessarily need to set the default app for a file type.
use framework "Foundation"
use scripting additions
property fileMenu : missing value -- an outlet and flag for UI setup
to run args
if fileMenu is missing value then setupUI()
doStuff(getArguments(args))
end run
to open droppedFiles -- items dropped onto the app
if fileMenu is missing value then setupUI()
doStuff(droppedFiles)
end open
to reopen addedFiles -- stay-open app reopened (open app double-clicked, Dock, etc)
if addedFiles is in {{}, current application} then -- no files, so look for other sources
doStuff(getArguments(addedFiles))
else
doStuff(addedFiles)
end if
end reopen
to setupUI() -- add an alternative to the Dock menu
if name of current application is not in {"Script Debugger", "Script Editor"} then
set fileMenu to (current application's NSApp's mainMenu's itemWithTitle:"File")'s submenu
fileMenu's removeItemAtIndex:0 -- don't need "Use Startup Screen" item
(fileMenu's addItemWithTitle:"Open New Document" action:"menuAction:" keyEquivalent:"o")'s setTarget:me
end if
end setupUI
on menuAction:sender -- manual action (blank/new document, etc)
doStuff({})
end menuAction:
to doStuff(theFiles) -- main handler to do stuff
try -- build Terminal script with arguments and do it
set command to ""
if (class of theFiles is list) and ((count theFiles) > 0) then -- new tabbed window
tell application "System Events" to set pathFolderParent to POSIX path of container of disk item ((first item of theFiles) as text) -- handle HFS or POSIX
set command to "cd " & quoted form of pathFolderParent & "; vi -p" -- vi or any other command-line text editor
set fileList to ""
repeat with anItem in theFiles
set fileList to fileList & space & quoted form of (POSIX path of anItem)
end repeat
set command to command & fileList & "; exit" -- if Terminal > Settings > Profiles > Shell > When the shell exits != Don't close the window
else
if theFiles is in {{}, current application} then -- new blank document
set command to "vi; exit"
end if
end if
if theFiles is not missing value then tell application "Terminal"
if it is not running then
set myTab to (do script command in window 1)
else
set myTab to (do script command)
end if
activate
end tell
on error errmess
display alert "Error Doing Stuff" message errmess
end try
end doStuff
to getArguments(args) -- get an argument list by going through the various sources
try
set selectedFiles to {} -- default
if args is in {me, current application} then -- app double-clicked, 'open -a' with '--args', script editor or menu
set processList to (current application's NSProcessInfo's processInfo's arguments) as list
if (count processList) > 0 and first item of processList is not "/usr/bin/osascript" then -- skip if script menu
set selectedFiles to rest of processList -- drop the first item (executable path)
end if
else if args is not in {} then -- osascript with arguments
set selectedFiles to args
end if
set shiftKey to ((current application's NSEvent's modifierFlags()) div 131072 mod 2 is 1) -- NSEventModifierFlagShift
if (not shiftKey) and (selectedFiles is {}) then -- shift key skips user selections
tell application "Finder" to if (get windows) is not {} then -- try current Finder selection
set selectedFiles to get selection as alias list -- note that this will be the app or alias if double-clicked
end if
if selectedFiles is {} or (((path to me) is in selectedFiles) and ((count selectedFiles) is 1)) then
activate me
set selectedFiles to (choose file of type "public.text" with multiple selections allowed)
end if
end if
return selectedFiles
on error errmess
display alert "Error Getting Arguments" message errmess
return missing value -- provide an alternate value for indication
end try
end getArguments