function __funced_md5
    if type -q md5sum
        # GNU systems
        echo (md5sum $argv[1] | string split ' ')[1]
        return 0
    else if type -q md5
        # BSD systems
        md5 -q $argv[1]
        return 0
    end
    return 1
end

function funced --description 'Edit function definition'
    set -l editor
    # Check VISUAL first since theoretically EDITOR could be ed
    if set -q VISUAL
        set editor $VISUAL
    else if set -q EDITOR
        set editor $EDITOR
    end
    set -l interactive
    set -l funcname
    while set -q argv[1]
        switch $argv[1]
            case -h --help
                __fish_print_help funced
                return 0

            case -e --editor
                set editor $argv[2]
                set -e argv[2]

            case -i --interactive
                set interactive 1

            case --
                set funcname $funcname $argv[2]
                set -e argv[2]

            case '-*'
                set_color red
                printf (_ "%s: Unknown option %s\n") funced $argv[1]
                set_color normal
                return 1

            case '*' '.*'
                set funcname $funcname $argv[1]
        end
        set -e argv[1]
    end

    if test (count $funcname) -ne 1
        set_color red
        echo (_ "funced: You must specify one function name")
        set_color normal
        return 1
    end

    set -l init
    switch $funcname
        case '-*'
            set init function -- $funcname\n\nend
        case '*'
            set init function $funcname\n\nend
    end

    # Break editor up to get its first command (i.e. discard flags)
    if test -n "$editor"
        set -l editor_cmd
        eval set editor_cmd $editor
        if not type -q -f "$editor_cmd[1]"
            echo (_ "funced: The value for \$EDITOR '$editor' could not be used because the command '$editor_cmd[1]' could not be found")
            set editor fish
        end
    end

    # If no editor is specified, use fish
    if test -z "$editor"
        set editor fish
    end

    if begin
            set -q interactive[1]
            or test "$editor" = fish
        end
        set -l IFS
        if functions -q -- $funcname
            # Shadow IFS here to avoid array splitting in command substitution
            set init (functions -- $funcname | fish_indent --no-indent)
        end

        set -l prompt 'printf "%s%s%s> " (set_color green) '$funcname' (set_color normal)'
        # Unshadow IFS since the fish_title breaks otherwise
        set -e IFS
        if read -p $prompt -c "$init" -s cmd
            # Shadow IFS _again_ to avoid array splitting in command substitution
            set -l IFS
            eval (echo -n $cmd | fish_indent)
        end
        return 0
    end

    # OSX mktemp is rather restricted - no suffix, no way to automatically use TMPDIR
    # Create a directory so we can use a ".fish" suffix for the file - makes editors pick up that it's a fish file
    set -q TMPDIR
    or set -l TMPDIR /tmp
    set -l tmpdir (mktemp -d $TMPDIR/fish.XXXXXX)
    set -l tmpname $tmpdir/$funcname.fish

    if functions -q -- $funcname
        functions -- $funcname >$tmpname
    else
        echo $init >$tmpname
    end

    # Repeatedly edit until it either parses successfully, or the user cancels
    # If the editor command itself fails, we assume the user cancelled or the file
    # could not be edited, and we do not try again
    while true
        set -l checksum (__funced_md5 "$tmpname")

        if not eval $editor $tmpname
            echo (_ "Editing failed or was cancelled")
        else
            # Verify the checksum (if present) to detect potential problems
            # with the editor command
            if set -q checksum[1]
                set -l new_checksum (__funced_md5 "$tmpname")
                if test "$new_checksum" = "$checksum"
                    echo (_ "Editor exited but the function was not modified")
                end
            end

            if not source $tmpname
                # Failed to source the function file. Prompt to try again.
                echo # add a line between the parse error and the prompt
                set -l repeat
                set -l prompt (_ 'Edit the file again\? [Y/n]')
                while test -z "$repeat"
                    read -p "echo $prompt\  " repeat
                end
                if not contains $repeat n N no NO No nO
                    continue
                end
                echo (_ "Cancelled function editing")
            end
        end
        break
    end

    set -l stat $status
    rm $tmpname >/dev/null
    and rmdir $tmpdir >/dev/null
    return $stat
end
