summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBatuhan Taskaya <isidentical@gmail.com>2022-05-20 10:42:38 +0300
committerBatuhan Taskaya <isidentical@gmail.com>2022-05-20 10:42:38 +0300
commitee5fc59c5186bf7e9e50d063f758a890c9b06494 (patch)
tree497aea416fbe45746ef342aab71fd42c6a9f6cdb
parentb8e0be241ccce86521445fcaae107f373c26ef92 (diff)
Implement support for bash & completion flow generationisidentical/feature/shell-completion
-rw-r--r--extras/completion/completion.bash53
-rw-r--r--extras/completion/completion.zsh21
-rw-r--r--extras/completion/templates/completion.bash.j240
-rwxr-xr-xextras/completion/templates/completion.zsh.j24
-rw-r--r--extras/httpie-completion.bash20
-rw-r--r--extras/scripts/completion/bash.py83
-rw-r--r--extras/scripts/completion/completion_flow.py2
-rw-r--r--extras/scripts/completion/generate_completion.py20
-rw-r--r--extras/scripts/completion/zsh.py15
9 files changed, 211 insertions, 47 deletions
diff --git a/extras/completion/completion.bash b/extras/completion/completion.bash
new file mode 100644
index 00000000..50497f8d
--- /dev/null
+++ b/extras/completion/completion.bash
@@ -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" ) )
+} \ No newline at end of file
diff --git a/extras/completion/completion.zsh b/extras/completion/completion.zsh
index 02ca886c..a482668a 100644
--- a/extras/completion/completion.zsh
+++ b/extras/completion/completion.zsh
@@ -13,21 +13,22 @@ _httpie_params () {
if ! [[ $current == -* ]]; then
if (( CURRENT == NORMARG + 0 )); then
- _httpie_method && ret=0
-fi
+ _httpie_method && ret=0
+ fi
if (( CURRENT == NORMARG + 0 )); then
- _httpie_url && ret=0
-fi
+ _httpie_url && ret=0
+ fi
if (( CURRENT == NORMARG + 1 )) && [[ ${METHODS[(ie)$predecessor]} -le ${#METHODS} ]]; then
- _httpie_url && ret=0
-fi
+ _httpie_url && ret=0
+ fi
if (( CURRENT >= NORMARG + 2 )); then
- _httpie_request_item && ret=0
-fi
+ _httpie_request_item && ret=0
+ fi
if (( CURRENT >= NORMARG + 1 )) && ! [[ ${METHODS[(ie)$predecessor]} -le ${#METHODS} ]]; then
- _httpie_request_item && ret=0
-fi
+ _httpie_request_item && ret=0
fi
+
+ fi
return $ret
diff --git a/extras/completion/templates/completion.bash.j2 b/extras/completion/templates/completion.bash.j2
new file mode 100644
index 00000000..e2c52902
--- /dev/null
+++ b/extras/completion/templates/completion.bash.j2
@@ -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" ) )
+}
diff --git a/extras/completion/templates/completion.zsh.j2 b/extras/completion/templates/completion.zsh.j2
index 2fb5b241..fe7002e8 100755
--- a/extras/completion/templates/completion.zsh.j2
+++ b/extras/completion/templates/completion.zsh.j2
@@ -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
diff --git a/extras/httpie-completion.bash b/extras/httpie-completion.bash
deleted file mode 100644
index 6abbc217..00000000
--- a/extras/httpie-completion.bash
+++ /dev/null
@@ -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" ) )
-}
diff --git a/extras/scripts/completion/bash.py b/extras/scripts/completion/bash.py
new file mode 100644
index 00000000..444c6705
--- /dev/null
+++ b/extras/scripts/completion/bash.py
@@ -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}"'
+ )
diff --git a/extras/scripts/completion/completion_flow.py b/extras/scripts/completion/completion_flow.py
index f6d1faa4..9c98127e 100644
--- a/extras/scripts/completion/completion_flow.py
+++ b/extras/scripts/completion/completion_flow.py
@@ -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):
diff --git a/extras/scripts/completion/generate_completion.py b/extras/scripts/completion/generate_completion.py
index fef64f72..8d848f38 100644
--- a/extras/scripts/completion/generate_completion.py
+++ b/extras/scripts/completion/generate_completion.py
@@ -1,18 +1,17 @@
-from atexit import register
import functools
import string
import textwrap
+from atexit import register
+from pathlib import Path
+from typing import Any, Callable, Dict, TypeVar
+from completion_flow import generate_flow
from jinja2 import Template
-from pathlib import Path
-from typing import Any, Dict, Callable, TypeVar
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.')
diff --git a/extras/scripts/completion/zsh.py b/extras/scripts/completion/zsh.py
index 1a952e4f..ac18f86d 100644
--- a/extras/scripts/completion/zsh.py
+++ b/extras/scripts/completion/zsh.py
@@ -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,
+ And,
Check,
- Suggest,
- Variable,
Condition,
- Suggestion,
If,
- And,
+ Node,
Not,
- generate_flow,
+ Suggest,
+ Suggestion,
+ Variable,
)