Implement support for bash & completion flow generation

This commit is contained in:
Batuhan Taskaya 2022-05-20 10:42:38 +03:00
parent b8e0be241c
commit ee5fc59c51
9 changed files with 218 additions and 54 deletions

View File

@ -0,0 +1,53 @@
METHODS=("GET" "POST" "PUT" "DELETE" "HEAD" "OPTIONS" "PATCH" "TRACE" "CONNECT" )
NORMARG=1 # TO-DO: dynamically calculate this?
_http_complete() {
local cur_word=${COMP_WORDS[COMP_CWORD]}
local prev_word=${COMP_WORDS[COMP_CWORD - 1]}
if [[ "$cur_word" == -* ]]; then
_http_complete_options "$cur_word"
else
if (( COMP_CWORD == NORMARG + 0 )); then
_http_complete_methods "$cur_word"
fi
if (( COMP_CWORD == NORMARG + 0 )); then
_http_complete_url "$cur_word"
fi
if (( COMP_CWORD == NORMARG + 1 )) && [[ " ${METHODS[*]} " =~ " ${prev_word} " ]]; then
_http_complete_url "$cur_word"
fi
if (( COMP_CWORD >= NORMARG + 2 )); then
_httpie_complete_request_item "$cur_word"
fi
if (( COMP_CWORD >= NORMARG + 1 )) && ! [[ " ${METHODS[*]} " =~ " ${prev_word} " ]]; then
_httpie_complete_request_item "$cur_word"
fi
fi
}
complete -o default -F _http_complete http httpie.http httpie.https https
_http_complete_methods() {
local cur_word=$1
local options="GET POST PUT DELETE HEAD OPTIONS PATCH TRACE CONNECT"
COMPREPLY+=( $( compgen -W "$options" -- "$cur_word" ) )
}
_http_complete_url() {
local cur_word=$1
local options="http:// https://"
COMPREPLY+=( $( compgen -W "$options" -- "$cur_word" ) )
}
_httpie_complete_request_item() {
local cur_word=$1
COMPREPLY+=("==" "=" ":=" ":=@")
}
_http_complete_options() {
local cur_word=$1
local options="--json -j --form -f --multipart --boundary --raw --compress -x --pretty --style -s --unsorted --sorted --response-charset --response-mime --format-options --print -p --headers -h --meta -m --body -b --verbose -v --all --stream -S --output -o --download -d --continue -c --quiet -q --session --session-read-only --auth -a --auth-type -A --ignore-netrc --offline --proxy --follow -F --max-redirects --max-headers --timeout --check-status --path-as-is --chunked --verify --ssl --ciphers --cert --cert-key --cert-key-pass --ignore-stdin -I --help --manual --version --traceback --default-scheme --debug "
COMPREPLY=( $( compgen -W "$options" -- "$cur_word" ) )
}

View File

@ -27,6 +27,7 @@ fi
if (( CURRENT >= NORMARG + 1 )) && ! [[ ${METHODS[(ie)$predecessor]} -le ${#METHODS} ]]; then
_httpie_request_item && ret=0
fi
fi
return $ret

View File

@ -0,0 +1,40 @@
METHODS=({% for method in methods -%} "{{ method }}" {% endfor -%})
NORMARG=1 # TO-DO: dynamically calculate this?
_http_complete() {
local cur_word=${COMP_WORDS[COMP_CWORD]}
local prev_word=${COMP_WORDS[COMP_CWORD - 1]}
if [[ "$cur_word" == -* ]]; then
_http_complete_options "$cur_word"
else
{% for flow_item in generate_flow() -%}
{{ compile_bash(flow_item) | indent(width=8) }}
{% endfor %}
fi
}
complete -o default -F _http_complete http httpie.http httpie.https https
_http_complete_methods() {
local cur_word=$1
local options="{{' '.join(methods)}}"
COMPREPLY+=( $( compgen -W "$options" -- "$cur_word" ) )
}
_http_complete_url() {
local cur_word=$1
local options="http:// https://"
COMPREPLY+=( $( compgen -W "$options" -- "$cur_word" ) )
}
_httpie_complete_request_item() {
local cur_word=$1
COMPREPLY+=("==" "=" ":=" ":=@")
}
_http_complete_options() {
local cur_word=$1
local options="{% for argument in arguments -%} {{ ' '.join(argument.aliases) }} {% endfor -%}"
COMPREPLY=( $( compgen -W "$options" -- "$cur_word" ) )
}

View File

@ -13,8 +13,8 @@ _httpie_params () {
if ! [[ $current == -* ]]; then
{% for flow_item in generate_flow() -%}
{{ compile_zsh(flow_item) }}
{% endfor -%}
{{ compile_zsh(flow_item) | indent(width=8) }}
{% endfor %}
fi
return $ret

View File

@ -1,20 +0,0 @@
_http_complete() {
local cur_word=${COMP_WORDS[COMP_CWORD]}
local prev_word=${COMP_WORDS[COMP_CWORD - 1]}
if [[ "$cur_word" == -* ]]; then
_http_complete_options "$cur_word"
fi
}
complete -o default -F _http_complete http httpie.http httpie.https https
_http_complete_options() {
local cur_word=$1
local options="-j --json -f --form --pretty -s --style -p --print
-v --verbose -h --headers -b --body -S --stream -o --output -d --download
-c --continue --session --session-read-only -a --auth --auth-type --proxy
--follow --verify --cert --cert-key --timeout --check-status --ignore-stdin
--help --version --traceback --debug --raw"
COMPREPLY=( $( compgen -W "$options" -- "$cur_word" ) )
}

View File

@ -0,0 +1,83 @@
from enum import Enum
from functools import singledispatch
from completion_flow import (
And,
Check,
Condition,
If,
Node,
Not,
Suggest,
Suggestion,
Variable,
generate_flow,
)
class BashVariable(str, Enum):
CURRENT = 'COMP_CWORD'
NORMARG = 'NORMARG'
CURRENT_WORD = 'cur_word'
PREDECESSOR = 'prev_word'
METHODS = 'METHODS'
SUGGESTION_TO_FUNCTION = {
Suggestion.METHOD: '_http_complete_methods',
Suggestion.URL: '_http_complete_url',
Suggestion.REQUEST_ITEM: '_httpie_complete_request_item',
}
@singledispatch
def compile_bash(node: Node) -> ...:
raise NotImplementedError(f'{type(node)} is not supported')
@compile_bash.register(If)
def compile_if(node: If) -> str:
check = compile_bash(node.check)
action = compile_bash(node.action)
return f'if {check}; then\n {action}\nfi'
@compile_bash.register(Check)
def compile_check(node: Check) -> str:
args = [
BashVariable(arg.name) if isinstance(arg, Variable) else arg
for arg in node.args
]
if node.condition is Condition.POSITION_EQ:
return f'(( {BashVariable.CURRENT} == {BashVariable.NORMARG} + {args[0]} ))'
elif node.condition is Condition.POSITION_GE:
return f'(( {BashVariable.CURRENT} >= {BashVariable.NORMARG} + {args[0]} ))'
elif node.condition is Condition.CONTAINS_PREDECESSOR:
parts = [
'[[ ',
'" ${',
BashVariable.METHODS,
'[*]} " =~ " ${',
BashVariable.PREDECESSOR,
'} " ]]',
]
return ''.join(parts)
@compile_bash.register(And)
def compile_and(node: And) -> str:
return ' && '.join(compile_bash(check) for check in node.checks)
@compile_bash.register(Not)
def compile_not(node: Not) -> str:
return f'! {compile_bash(node.check)}'
@compile_bash.register(Suggest)
def compile_suggest(node: Suggest) -> str:
return (
SUGGESTION_TO_FUNCTION[node.suggestion]
+ f' "${BashVariable.CURRENT_WORD}"'
)

View File

@ -1,6 +1,6 @@
from dataclasses import dataclass, field
from enum import Enum, auto
from typing import List, Iterator
from typing import Iterator, List
class Condition(Enum):

View File

@ -1,18 +1,17 @@
from atexit import register
import functools
import string
import textwrap
from jinja2 import Template
from atexit import register
from pathlib import Path
from typing import Any, Dict, Callable, TypeVar
from typing import Any, Callable, Dict, TypeVar
from completion_flow import generate_flow
from jinja2 import Template
from httpie.cli.constants import SEPARATOR_FILE_UPLOAD
from httpie.cli.definition import options
from httpie.cli.options import Argument, ParserSpec
from completion_flow import generate_flow
T = TypeVar('T')
EXTRAS_DIR = Path(__file__).parent.parent.parent
@ -202,7 +201,7 @@ def zsh_completer(spec: ParserSpec) -> Dict[str, Any]:
return {
'escape_zsh': escape_zsh,
'serialize_argument_to_zsh': serialize_argument_to_zsh,
'compile_zsh': compile_zsh
'compile_zsh': compile_zsh,
}
@ -213,6 +212,15 @@ def fish_completer(spec: ParserSpec) -> Dict[str, Any]:
}
@use_template('bash')
def fish_completer(spec: ParserSpec) -> Dict[str, Any]:
from bash import compile_bash
return {
'compile_bash': compile_bash,
}
def main():
for shell_type, completer in COMPLETERS.items():
print(f'Generating {shell_type} completer.')

View File

@ -1,17 +1,16 @@
from functools import singledispatch
from enum import Enum
from lib2to3.pgen2.pgen import generate_grammar
from functools import singledispatch
from completion_flow import (
Node,
Check,
Suggest,
Variable,
Condition,
Suggestion,
If,
And,
Check,
Condition,
If,
Node,
Not,
generate_flow,
Suggest,
Suggestion,
Variable,
)