ohmyzsh: can't use tab-completion with Yoink Mac app

encounter the same problem as this

attached omz-debug log. gist file. please help.


Transcription from the above link:

I’m running a zsh terminal under Yosemite and have defined a simple alias to place files in Yoink:

$ alias yoink='open -a Yoink'
$ yoink my-file-to-yoink.txt

This works great but for some unknown reason the tab completion to specify the file does not work after yoink. It simple says -- no matches found --.

Is there a way I can use tab completing in combination with this alias?

Thanks.

ps: Not sure if it’s related, but I’m using zprezto.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 23 (12 by maintainers)

Most upvoted comments

Hi. Thank you for the heads-up on this, I wasn’t aware of this issue at all. I’ll fix the Info.plist with the next update, it should be out some time next week.

Thanks again!

  • Matt

Yes. open -a Yoink <file> adds it to the Yoink staging area, just like it had been drag-and-dropped there. Given that behavior, I would think they want to declare '*' as a handled extension type in CFBundleTypeExtensions, or something similar.

TL;DR

I can reproduce. I don’t think this is OMZ. The _open function (provided by zsh itself) is working normally, but Yoink.app’s metadata doesn’t declare it handles any input file types, so _open doesn’t think there are any valid file candidates for completion. Try reporting this to the Yoink developers, or write a custom _yoink function.

Details

I can reproduce this. Without Yoink installed, completing open -a Yoink ... will list a bunch of files as possible completions. But with Yoink 3.2 installed, completing open -a Yoink ... offers zero files. I also get the same behavior if I turn off Oh My Zsh, but still run compinit as part of my zsh startup.

This may be a file type and application metadata thing. When doing open -a Foo ..., _open will only list files whose extensions are valid for the particular application you’re completing against. This is done by calling the _mac_files_for_application function.

if [[ -n "$app" ]]
            then
                _wanted files expl "file for $app" _mac_files_for_application "$app"
            else

Here’s that function’s logic. It looks into the installed application’s Info.plist to see what file types it declares that it handles.

$ which _mac_files_for_application
_mac_files_for_application () {
    local -a opts
    zparseopts -D -a opts q n 1 2 P: S: r: R: W: X+: M+: F: J+: V+:
    local app_path
    _retrieve_mac_apps
    app_path="${_mac_apps[(r)*/$1(|.app)]:-$1}"
    local -a glob_patterns
    glob_patterns=()
    if [[ -f "$app_path/Contents/Info.plist" ]]
    then
        local -a exts types
        _mac_parse_info_plist
        if [[ -n "$exts[(r)\*]" ]]
        then
            glob_patterns=("*")
        else
            if (( #exts != 0 ))
            then
                glob_patterns+=("*.(${(j/|/)exts})(N)")
            fi
            if (( #types != 0 ))
            then
                glob_patterns+=("^*.[[:alnum:]]##(.Ne:_mac_rsrc_check:)")
            fi
        fi
    else
        glob_patterns=("*")
    fi
    case ${#glob_patterns} in
        (0) return 1 ;;
        (1) _files "$opts[@]" -g "$glob_patterns[1]" ;;
        (*) _files "$opts[@]" -g "{${(j/,/)glob_patterns}}" ;;
    esac
}
[~/.oh-my-zsh on ⇄ master]
$

And here’s the logic for parsing that info plist.

$ which _mac_parse_info_plist
_mac_parse_info_plist () {
    local s='
    BEGIN { RS="<" }
      /^key>/ { sub(/key>/, ""); reading_key=$0 }
      /^string>/ {
        sub(/string>/, "")
        if (reading_key == "CFBundleTypeExtensions") exts=exts " \"" $0 "\""
        if (reading_key == "CFBundleTypeOSTypes") types=types " \"" $0 "\""
      }
      END {
        print "exts=(" exts ")\ntypes=(" types ")"
      }
    '
    command awk $s "$app_path/Contents/Info.plist" | while read
    do
        eval "$REPLY"
    done
}
[~]
$

Solution

I took a look in Yoink.app’s Contents/Info.plist file. It does not contain either CFBundleTypeExtensions or CFBundleTypeOSTypes elements. Basically, I think that means it’s not declaring any types of files that it handles as inputs. So _open doesn’t think there any files that are valid for completion candidates.

I think this means that _open is acting as designed, and the Yoink app’s metadata is interacting with it in an inconvenient way. If Yoink.app really was designed to take any files as input from the command line or open invocation, it should probably declare them in its Info.plist. You may want to contact the Yoink developers about this to see if they can adjust the Info.plist, if this is a supported behavior for Yoink.

You could also fix this yourself be writing a custom _yoink function and getting that on your $FPATH for Zsh’s completion to use. To get this to work, you will need to replace your yoink alias with a yoink() function that does the same things: completion “looks inside” aliases, but not inside functions, and like Marc said, you don’t want to turn on setopt complete_aliases.


ps: Not sure if it’s related, but I’m using zprezto.

Prezto and OMZ are alternative frameworks to do the same shell configuration work. Running both of them could have unforeseen interactions and breakage, and is not supported. But I don’t think that’s relevant here.