#!/usr/bin/env zsh
################################################################################
#
# This file is autoloaded by .zshrc, and actually loaded when executed
#
################################################################################
#
# Filename:      cd
# Description:   cd with magic
#                If trying to cd to a file, cd to files directory instead
#
# Authors:       Dennis Eriksen <d@ennis.no>
# Bug-Reports:   Email <idgatt@dnns.no>
# License:       This file is licensed under the BSD-3-Clause license.
#
emulate -L zsh

# Idea from from https://github.com/mika/zsh-pony#smart-cd
# But this is smarter, since it works more like the builtin.

# cd has three "modes"
#
# cd [-qsLP] [arg]
#     Change the current directory to arg, or to the value of $HOME if arg is
#     not specified. If arg is ‘-’, change to the previous directory.
#
# cd [ -qsLP ] old new
#     Substitutes the string new for the string old in the name of the current
#     directory ($PWD), and tries to change to this new directory
#
# cd [ -qsLP ] {+|-}n
#     Extracts an entry from the directory stack, and changes to that directory
#
# This function aims to only work in the first mode.

# Regex to check for flags
local regex='^-[qsLP]*$'

# If at least one argument, and
# last argument (${argv[-1]}) is not a flag
if (( ARGC )) && [[ ! ${argv[-1]} =~ $regex ]]; then

  # Count arguments. Start at one because we just checked if the last argument
  # was a flag.
  local -i arguments=1

  # Check all the other arguments if they're flags.
  if (( ARGC > 1 )); then
    for i in {$(( ARGC - 1 ))..1}; do
      # If argument is not empty, and does not match regex
      if [[ -n ${argv[-$i]} && ! ${argv[-$i]} =~ $regex ]]; then
        # Count as argument
        let arguments+=1
      fi
    done
  fi

  # If only one actual argument, and
  # last argument is not a directory, and
  # it contains a slash, and
  # it either exists (as a file) or its base directory is a directory
  if (( arguments == 1 )) &&
     [[ ! -d ${argv[-1]} ]] &&
     [[ ${argv[-1]} == */* ]] &&
     [[ -e ${argv[-1]} || -d ${argv[-1]:h} ]]; then

    # If the q-flag was not used
    [[ ${argv:1:-1} != *q* ]] && print -- "Correcting ${argv[-1]} to ${argv[-1]:h}"

    # Remove trailing pathname (everything after last /), so we can cd to the
    # directory of the specified file
    argv[-1]=${argv[-1]:h}
  fi
fi

# Run builtin cd
builtin cd $@

# END OF FILE #################################################################