Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sometimes the terminal is blocked when entering commands #353

Closed
Frezrik opened this issue Aug 17, 2023 · 12 comments
Closed

Sometimes the terminal is blocked when entering commands #353

Frezrik opened this issue Aug 17, 2023 · 12 comments

Comments

@Frezrik
Copy link

Frezrik commented Aug 17, 2023

ble version:
Bash version:

GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu) [Ubuntu 18.04.6 LTS]
ble.sh, version 0.4.0-devel4+dc0cdb3 (noarch) [git 2.17.1, GNU Make 4.1, GNU Awk 4.1.4, API: 1.1 (GNU MPFR 4.0.1, GNU MP 6.1.2)]
bash-completion, version 2.8 (hash:ada42da96765409bcc301210eb17bab248f79f5a, 72566 bytes) (noarch)
locale: LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
terminal: TERM=xterm wcwidth=14.0-west/15.0-2+ri, xterm:136 (0;136;0)

I use ble in the context of compiling Android source code because I need some environment variables provided by the source code before compiling Android source code. I suspect that these environment variables conflict with ble causing this problem.
When compiling android source code, you usually start with

source build/envsetup.sh
lunch xxx

Then compile with the make command, not all make commands will block. When I try to execute the make update-api, the input to make u is blocked and the terminal can no longer respond to the input

@akinomyoga
Copy link
Owner

I suspect that these environment variables conflict with ble causing this problem.

Is there a reason that you think so? Are there other observations you made? Which specific variables are conflicting?

@Frezrik
Copy link
Author

Frezrik commented Aug 17, 2023

I suspect that these environment variables conflict with ble causing this problem.

Is there a reason that you think so? Are there other observations you made? Which specific variables are conflicting?

If I do not enter the command in the source level directory,everythin is fine。
If I don't execute source build/envsetup.sh,everythin is fine。

Is there an associative blacklist of input commands? I want to add make to the blacklist

@akinomyoga
Copy link
Owner

OK, thank you for the information. These don't seem to be sufficient reasons that we suspect environment variable conflicts specifically. There are many other factors, such as the settings and states of the current terminal handler, etc.

Is there an associative blacklist of input commands? I want to add make to the blacklist

There shouldn't be any restrictions in commands used with ble.sh. I use make daily, but I've never experienced any problems or strange behaviors within ble.sh sessions.

@akinomyoga
Copy link
Owner

akinomyoga commented Aug 17, 2023

@Frezrik Do you think the problem can be reproducible with other hosts? Have you experienced the problem in more than one environment? Or were you just faced with the problem on a specific host?

To reproduce the problem, are there any specific instructions? I'm not familiar with the Android source code. Could you briefly explain its structures and where to start? What is the version of the Android source code with which you experienced the problem? Or if you can provide a container that reproduces the problem, that might be easier.

@sebhoss
Copy link

sebhoss commented Nov 15, 2023

This happens for me as well, however in a complete different environment: I have bash completions for kubectl activated and whenever my connection to a Kubernetes cluster is slow, my terminal hangs until a response is received from the cluster. No ctrl+c or whatever unlocks it and since the default timeout for kubectl is 32 seconds this feels very annoying

@akinomyoga
Copy link
Owner

@sebhoss Thanks for the additional information. That is rather related to #121 and #193.

As ble.sh is written in Bash script, SIGINT may cancel all the processing including the necessary parts of the ble.sh codes. For this reason, ble.sh basically disables the conversion from C-c to SIGINT. Even if we enable SIGINT during the completion, it wouldn't be helpful because ble.sh's autosuggestion attempts the completion for every key input, where you would need to press C-c after every key press. In the first place, even in the plain Bash, requiring pressing C-c after attempting completion would be unreasonable and annoying. My conclusion is that blocking until pressing C-c or requiring waiting for an unrealistically long timeout (e.g. 30 seconds) would be attributed to the completion's bug. What I can do here is to add a workaround for each such completion.

@sebhoss To add a workaround, I need the implementation details of the kubectl completion. The kubectl completion seems to be emitted by running the command kubectl completion bash. Could you give me the result of kubectl completion bash?

$ kubectl completion bash

@sebhoss
Copy link

sebhoss commented Nov 16, 2023

@akinomyoga yeah sure & thanks for the detailed information. Here is the completion script for kubectl:

# Copyright 2016 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# bash completion V2 for kubectl                              -*- shell-script -*-

__kubectl_debug()
{
    if [[ -n ${BASH_COMP_DEBUG_FILE-} ]]; then
        echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
    fi
}

# Macs have bash3 for which the bash-completion package doesn't include
# _init_completion. This is a minimal version of that function.
__kubectl_init_completion()
{
    COMPREPLY=()
    _get_comp_words_by_ref "$@" cur prev words cword
}

# This function calls the kubectl program to obtain the completion
# results and the directive.  It fills the 'out' and 'directive' vars.
__kubectl_get_completion_results() {
    local requestComp lastParam lastChar args

    # Prepare the command to request completions for the program.
    # Calling ${words[0]} instead of directly kubectl allows to handle aliases
    args=("${words[@]:1}")
    requestComp="${words[0]} __complete ${args[*]}"

    lastParam=${words[$((${#words[@]}-1))]}
    lastChar=${lastParam:$((${#lastParam}-1)):1}
    __kubectl_debug "lastParam ${lastParam}, lastChar ${lastChar}"

    if [[ -z ${cur} && ${lastChar} != = ]]; then
        # If the last parameter is complete (there is a space following it)
        # We add an extra empty parameter so we can indicate this to the go method.
        __kubectl_debug "Adding extra empty parameter"
        requestComp="${requestComp} ''"
    fi

    # When completing a flag with an = (e.g., kubectl -n=<TAB>)
    # bash focuses on the part after the =, so we need to remove
    # the flag part from $cur
    if [[ ${cur} == -*=* ]]; then
        cur="${cur#*=}"
    fi

    __kubectl_debug "Calling ${requestComp}"
    # Use eval to handle any environment variables and such
    out=$(eval "${requestComp}" 2>/dev/null)

    # Extract the directive integer at the very end of the output following a colon (:)
    directive=${out##*:}
    # Remove the directive
    out=${out%:*}
    if [[ ${directive} == "${out}" ]]; then
        # There is not directive specified
        directive=0
    fi
    __kubectl_debug "The completion directive is: ${directive}"
    __kubectl_debug "The completions are: ${out}"
}

__kubectl_process_completion_results() {
    local shellCompDirectiveError=1
    local shellCompDirectiveNoSpace=2
    local shellCompDirectiveNoFileComp=4
    local shellCompDirectiveFilterFileExt=8
    local shellCompDirectiveFilterDirs=16
    local shellCompDirectiveKeepOrder=32

    if (((directive & shellCompDirectiveError) != 0)); then
        # Error code.  No completion.
        __kubectl_debug "Received error from custom completion go code"
        return
    else
        if (((directive & shellCompDirectiveNoSpace) != 0)); then
            if [[ $(type -t compopt) == builtin ]]; then
                __kubectl_debug "Activating no space"
                compopt -o nospace
            else
                __kubectl_debug "No space directive not supported in this version of bash"
            fi
        fi
        if (((directive & shellCompDirectiveKeepOrder) != 0)); then
            if [[ $(type -t compopt) == builtin ]]; then
                # no sort isn't supported for bash less than < 4.4
                if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then
                    __kubectl_debug "No sort directive not supported in this version of bash"
                else
                    __kubectl_debug "Activating keep order"
                    compopt -o nosort
                fi
            else
                __kubectl_debug "No sort directive not supported in this version of bash"
            fi
        fi
        if (((directive & shellCompDirectiveNoFileComp) != 0)); then
            if [[ $(type -t compopt) == builtin ]]; then
                __kubectl_debug "Activating no file completion"
                compopt +o default
            else
                __kubectl_debug "No file completion directive not supported in this version of bash"
            fi
        fi
    fi

    # Separate activeHelp from normal completions
    local completions=()
    local activeHelp=()
    __kubectl_extract_activeHelp

    if (((directive & shellCompDirectiveFilterFileExt) != 0)); then
        # File extension filtering
        local fullFilter filter filteringCmd

        # Do not use quotes around the $completions variable or else newline
        # characters will be kept.
        for filter in ${completions[*]}; do
            fullFilter+="$filter|"
        done

        filteringCmd="_filedir $fullFilter"
        __kubectl_debug "File filtering command: $filteringCmd"
        $filteringCmd
    elif (((directive & shellCompDirectiveFilterDirs) != 0)); then
        # File completion for directories only

        local subdir
        subdir=${completions[0]}
        if [[ -n $subdir ]]; then
            __kubectl_debug "Listing directories in $subdir"
            pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return
        else
            __kubectl_debug "Listing directories in ."
            _filedir -d
        fi
    else
        __kubectl_handle_completion_types
    fi

    __kubectl_handle_special_char "$cur" :
    __kubectl_handle_special_char "$cur" =

    # Print the activeHelp statements before we finish
    if ((${#activeHelp[*]} != 0)); then
        printf "\n";
        printf "%s\n" "${activeHelp[@]}"
        printf "\n"

        # The prompt format is only available from bash 4.4.
        # We test if it is available before using it.
        if (x=${PS1@P}) 2> /dev/null; then
            printf "%s" "${PS1@P}${COMP_LINE[@]}"
        else
            # Can't print the prompt.  Just print the
            # text the user had typed, it is workable enough.
            printf "%s" "${COMP_LINE[@]}"
        fi
    fi
}

# Separate activeHelp lines from real completions.
# Fills the $activeHelp and $completions arrays.
__kubectl_extract_activeHelp() {
    local activeHelpMarker="_activeHelp_ "
    local endIndex=${#activeHelpMarker}

    while IFS='' read -r comp; do
        if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then
            comp=${comp:endIndex}
            __kubectl_debug "ActiveHelp found: $comp"
            if [[ -n $comp ]]; then
                activeHelp+=("$comp")
            fi
        else
            # Not an activeHelp line but a normal completion
            completions+=("$comp")
        fi
    done <<<"${out}"
}

__kubectl_handle_completion_types() {
    __kubectl_debug "__kubectl_handle_completion_types: COMP_TYPE is $COMP_TYPE"

    case $COMP_TYPE in
    37|42)
        # Type: menu-complete/menu-complete-backward and insert-completions
        # If the user requested inserting one completion at a time, or all
        # completions at once on the command-line we must remove the descriptions.
        # https://github.com/spf13/cobra/issues/1508
        local tab=$'\t' comp
        while IFS='' read -r comp; do
            [[ -z $comp ]] && continue
            # Strip any description
            comp=${comp%%$tab*}
            # Only consider the completions that match
            if [[ $comp == "$cur"* ]]; then
                COMPREPLY+=("$comp")
            fi
        done < <(printf "%s\n" "${completions[@]}")
        ;;

    *)
        # Type: complete (normal completion)
        __kubectl_handle_standard_completion_case
        ;;
    esac
}

__kubectl_handle_standard_completion_case() {
    local tab=$'\t' comp

    # Short circuit to optimize if we don't have descriptions
    if [[ "${completions[*]}" != *$tab* ]]; then
        IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur")
        return 0
    fi

    local longest=0
    local compline
    # Look for the longest completion so that we can format things nicely
    while IFS='' read -r compline; do
        [[ -z $compline ]] && continue
        # Strip any description before checking the length
        comp=${compline%%$tab*}
        # Only consider the completions that match
        [[ $comp == "$cur"* ]] || continue
        COMPREPLY+=("$compline")
        if ((${#comp}>longest)); then
            longest=${#comp}
        fi
    done < <(printf "%s\n" "${completions[@]}")

    # If there is a single completion left, remove the description text
    if ((${#COMPREPLY[*]} == 1)); then
        __kubectl_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
        comp="${COMPREPLY[0]%%$tab*}"
        __kubectl_debug "Removed description from single completion, which is now: ${comp}"
        COMPREPLY[0]=$comp
    else # Format the descriptions
        __kubectl_format_comp_descriptions $longest
    fi
}

__kubectl_handle_special_char()
{
    local comp="$1"
    local char=$2
    if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then
        local word=${comp%"${comp##*${char}}"}
        local idx=${#COMPREPLY[*]}
        while ((--idx >= 0)); do
            COMPREPLY[idx]=${COMPREPLY[idx]#"$word"}
        done
    fi
}

__kubectl_format_comp_descriptions()
{
    local tab=$'\t'
    local comp desc maxdesclength
    local longest=$1

    local i ci
    for ci in ${!COMPREPLY[*]}; do
        comp=${COMPREPLY[ci]}
        # Properly format the description string which follows a tab character if there is one
        if [[ "$comp" == *$tab* ]]; then
            __kubectl_debug "Original comp: $comp"
            desc=${comp#*$tab}
            comp=${comp%%$tab*}

            # $COLUMNS stores the current shell width.
            # Remove an extra 4 because we add 2 spaces and 2 parentheses.
            maxdesclength=$(( COLUMNS - longest - 4 ))

            # Make sure we can fit a description of at least 8 characters
            # if we are to align the descriptions.
            if ((maxdesclength > 8)); then
                # Add the proper number of spaces to align the descriptions
                for ((i = ${#comp} ; i < longest ; i++)); do
                    comp+=" "
                done
            else
                # Don't pad the descriptions so we can fit more text after the completion
                maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
            fi

            # If there is enough space for any description text,
            # truncate the descriptions that are too long for the shell width
            if ((maxdesclength > 0)); then
                if ((${#desc} > maxdesclength)); then
                    desc=${desc:0:$(( maxdesclength - 1 ))}
                    desc+=""
                fi
                comp+="  ($desc)"
            fi
            COMPREPLY[ci]=$comp
            __kubectl_debug "Final comp: $comp"
        fi
    done
}

__start_kubectl()
{
    local cur prev words cword split

    COMPREPLY=()

    # Call _init_completion from the bash-completion package
    # to prepare the arguments properly
    if declare -F _init_completion >/dev/null 2>&1; then
        _init_completion -n =: || return
    else
        __kubectl_init_completion -n =: || return
    fi

    __kubectl_debug
    __kubectl_debug "========= starting completion logic =========="
    __kubectl_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword"

    # The user could have moved the cursor backwards on the command-line.
    # We need to trigger completion from the $cword location, so we need
    # to truncate the command-line ($words) up to the $cword location.
    words=("${words[@]:0:$cword+1}")
    __kubectl_debug "Truncated words[*]: ${words[*]},"

    local out directive
    __kubectl_get_completion_results
    __kubectl_process_completion_results
}

if [[ $(type -t compopt) = "builtin" ]]; then
    complete -o default -F __start_kubectl kubectl
else
    complete -o default -o nospace -F __start_kubectl kubectl
fi

# ex: ts=4 sw=4 et filetype=sh

@akinomyoga
Copy link
Owner

Thank you for the information. The kubectl completion seems to be based on the Bash completion v2 of the spf13/cobra library. I added a workaround for the cobra completion in commit 595f905. @sebhoss Could you update ble.sh and check if the problem is solved?

@sebhoss
Copy link

sebhoss commented Nov 16, 2023

@akinomyoga that works perfectly - thanks a lot!

@akinomyoga
Copy link
Owner

Thanks for the confirmation!

@akinomyoga
Copy link
Owner

akinomyoga commented Jan 26, 2024

@Frezrik I recently received another report about the make completion at #389. It reported that the make completion blocks in a very large repository like the Linux kernel. I fixed the make completion not to block in such a situation.

Although the reported behavior seems different (as the problem seems to only happen after source build/envsetup.sh in your case), the fix for #389 might have changed the situation in your environment. Could you try it again?

@Frezrik
Copy link
Author

Frezrik commented Mar 1, 2024

My apologies for the delay in responding. After a period of use, occasional brief blockages of a few seconds occur, but it doesn't completely stall like before.

@Frezrik Frezrik closed this as completed Mar 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants