From Automator to AppleScript: Help me with a small "if" block

The following script can be compiled as application and then you can use command-line Vim to open specific file types from Finder.

osacompile -o /Applications/vim-app.app vim-app.applescript

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).

1 Like

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.

1 Like

Could you post it here? Or even better, to show how you would change my own script?

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
2 Likes

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.

1 Like

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
1 Like

Wow, this is incredible! Thank you! Please, give me some time to integrate my script there!