├── .editorconfig ├── .github ├── .markdownlint.yaml └── workflows │ ├── doxygen.yml │ ├── sync-labels.yml │ └── trunk_check.yml ├── .gitignore ├── .markdownlint.yaml ├── .trunk └── trunk.yaml ├── LICENSE ├── Makefile ├── demos ├── zui-demo-anchors ├── zui-demo-append ├── zui-demo-buttons ├── zui-demo-configure ├── zui-demo-edit ├── zui-demo-fly ├── zui-demo-ganchors ├── zui-demo-hello-world ├── zui-demo-history ├── zui-demo-list-boxes ├── zui-demo-nmap ├── zui-demo-special-text ├── zui-demo-text-fields ├── zui-demo-timeout ├── zui-demo-toggles └── zui-demo-various ├── docs ├── README.md ├── ZSTYLES.md ├── stdlib.lzui.adoc ├── syslib.lzui.adoc └── utillib.lzui.adoc ├── functions ├── -zui-list-box-loop ├── -zui-log ├── zui-event-loop ├── zui-list ├── zui-list-draw ├── zui-list-input ├── zui-list-wrapper ├── zui-process-buffer ├── zui-process-buffer2 └── zui-usetty-wrapper ├── lib ├── stdlib.lzui ├── syslib.lzui ├── utillib.lzui └── zcompile.zsh └── zui.plugin.zsh /.editorconfig: -------------------------------------------------------------------------------- 1 | # Space or Tabs? 2 | # https://stackoverflow.com/questions/35649847/objective-reasons-for-using-spaces-instead-of-tabs-for-indentation 3 | # https://stackoverflow.com/questions/12093748/how-to-use-tabs-instead-of-spaces-in-a-shell-script 4 | # 5 | # 1. What happens when I press the Tab key in my text editor? 6 | # 2. What happens when I request my editor to indent one or more lines? 7 | # 3. What happens when I view a file containing U+0009 HORIZONTAL TAB characters? 8 | # 9 | # Answers: 10 | # 11 | # 1. Pressing the Tab key should indent the current line (or selected lines) one additional level. 12 | # 2. As a secondary alternative, I can also tolerate an editor that, 13 | # like Emacs, uses this key for a context-sensitive fix-my-indentation command. 14 | # 3. Indenting one or more lines should follow the reigning convention, if consensus is sufficiently strong; otherwise, 15 | # I greatly prefer 2-space indentation at each level. U+0009 characters should shift subsequent characters to the next tab stop. 16 | # 17 | # Note: VIM users should use alternate marks [[[ and ]]] as the original ones can confuse nested substitutions, e.g.: ${${${VAR}}} 18 | # 19 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 20 | # vim: ft=zsh sw=2 ts=2 et 21 | 22 | root = true 23 | 24 | [*] 25 | charset = utf-8 26 | end_of_line = lf 27 | insert_final_newline = true 28 | trim_trailing_whitespace = true 29 | 30 | [*.{md,rst}] 31 | insert_final_newline = false 32 | trim_trailing_whitespace = false 33 | 34 | [*.{sh,bash,zsh,fish}] 35 | indent_style = space 36 | indent_size = 2 37 | tab_width = 2 38 | 39 | [Makefile] 40 | indent_style = tab 41 | indent_size = 4 42 | 43 | [*.{css,less}] 44 | indent_style = space 45 | indent_size = 2 46 | 47 | [*.{py,rb}] 48 | indent_style = space 49 | indent_size = 4 50 | 51 | [*.{go,java,scala,groovy,kotlin}] 52 | indent_style = tab 53 | indent_size = 4 54 | 55 | [*.{js,jsx,html,xml,sass,json,yml,yaml,toml}] 56 | charset = utf-8 57 | indent_style = space 58 | indent_size = 2 59 | max_line_length = 120 60 | 61 | [CHANGELOG.md] 62 | indent_style = tab 63 | indent_size = 4 -------------------------------------------------------------------------------- /.github/.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # Autoformatter friendly markdownlint config (all formatting rules disabled) 2 | default: true 3 | blank_lines: false 4 | bullet: false 5 | html: false 6 | indentation: false 7 | line_length: false 8 | spaces: false 9 | url: false 10 | whitespace: false 11 | -------------------------------------------------------------------------------- /.github/workflows/doxygen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 📖 Doxygen 3 | 4 | on: 5 | push: 6 | paths: 7 | - "functions/**" 8 | - "lib/**" 9 | pull_request: 10 | paths: 11 | - "functions/**" 12 | - "lib/**" 13 | schedule: 14 | - cron: "30 4 * * 4" 15 | workflow_dispatch: {} 16 | 17 | jobs: 18 | generate: 19 | runs-on: ubuntu-latest 20 | timeout-minutes: 30 21 | concurrency: 22 | group: ${{ github.workflow }}-${{ github.ref }} 23 | cancel-in-progress: true 24 | env: 25 | GH_TOKEN: ${{ github.token }} 26 | steps: 27 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 28 | with: 29 | token: ${{ secrets.GITHUB_TOKEN }} 30 | submodules: recursive 31 | fetch-depth: 0 32 | ref: ${{ github.event.pull_request.head.ref }} 33 | - name: "⚡ Dependencies" 34 | run: | 35 | sudo apt-get update -y 36 | sudo apt-get install -y zsh tree 37 | - name: "📦 Install Doxygen" 38 | run: | 39 | gh repo clone z-shell/zsdoc /tmp/zsdoc 40 | sudo make -C /tmp/zsdoc install 41 | - name: "♻️ Generate codebase documentation" 42 | run: make 43 | - name: "🏗 Compress codebase documentation" 44 | run: tar cvzf docs.tar.gz docs 45 | - name: "📤 Upload docs.tar.gz" 46 | uses: actions/upload-artifact@v4 47 | with: 48 | name: Codebase documentation 49 | path: docs.tar.gz 50 | - name: "♻️ Cleanup" 51 | run: | 52 | rm -rf docs.tar.gz 53 | - name: "🆗 Commit" 54 | if: ${{ github.event_name != 'pull_request' }} 55 | uses: z-shell/.github/actions/commit@main 56 | with: 57 | commitMessage: Codebase ${{ github.sha }} 58 | workDir: docs 59 | commitUserName: digital-teams[bot] 60 | commitUserEmail: actions@zshell.dev 61 | -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "♻️ Sync Labels" 3 | 4 | on: 5 | schedule: 6 | - cron: "22 2 * * 2" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | labels: 11 | name: "♻️ Sync labels" 12 | uses: z-shell/.github/.github/workflows/sync-labels.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/trunk_check.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "⭕ Trunk" 3 | on: 4 | push: 5 | branches: [main] 6 | tags: ["v*.*.*"] 7 | pull_request: 8 | types: [opened, synchronize] 9 | schedule: 10 | - cron: "0 07 * * 5" 11 | workflow_dispatch: {} 12 | 13 | jobs: 14 | check: 15 | if: github.event.schedule != '0 07 * * 5' 16 | name: "⚡" 17 | uses: z-shell/.github/.github/workflows/trunk.yml@main 18 | upload: 19 | if: github.event.schedule == '0 07 * * 5' 20 | name: "🆙" 21 | uses: z-shell/.github/.github/workflows/trunk.yml@main 22 | secrets: 23 | trunk-token: ${{ secrets.TRUNK_TOKEN }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Workspace 2 | .vscode 3 | .DS_Store 4 | 5 | # Zsh compiled script + zrecompile backup 6 | *.zwc 7 | *.zwc.old 8 | 9 | # Zsh completion-optimization dumpfile 10 | *zcompdump* 11 | 12 | # Zsh zcalc history 13 | .zcalc_history 14 | 15 | # A popular plugin manager's files 16 | ._zi 17 | .zi_lstupd 18 | 19 | # z-shell/zshelldoc tool's files 20 | zsdoc/data 21 | 22 | # robbyrussell/oh-my-zsh/plugins/per-directory-history plugin's files 23 | # (when set-up to store the history in the local directory) 24 | .directory_history 25 | 26 | # MichaelAquilina/zsh-autoswitch-virtualenv plugin's files 27 | # (for Zsh plugins using Python) 28 | .venv 29 | 30 | # Zunit tests' output 31 | /tests/_output/* 32 | !/tests/_output/.gitkeep 33 | 34 | ### Vim 35 | # Swap 36 | [._]*.s[a-v][a-z] 37 | [._]*.sw[a-p] 38 | [._]s[a-v][a-z] 39 | [._]sw[a-p] 40 | 41 | # Session 42 | Session.vim 43 | 44 | # Temporary 45 | .netrwhist 46 | *~ 47 | # Auto-generated tag files 48 | tags 49 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # Autoformatter friendly markdownlint config (all formatting rules disabled) 2 | default: true 3 | blank_lines: false 4 | bullet: false 5 | html: false 6 | indentation: false 7 | line_length: false 8 | spaces: false 9 | url: false 10 | whitespace: false 11 | -------------------------------------------------------------------------------- /.trunk/trunk.yaml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | cli: 3 | version: 0.12.1-beta 4 | repo: 5 | repo: 6 | host: github.com 7 | owner: z-shell 8 | name: zui 9 | lint: 10 | enabled: 11 | - actionlint@1.6.13 12 | - gitleaks@8.8.5 13 | - markdownlint@0.31.1 14 | - prettier@2.6.2 15 | - shfmt@3.4.0 16 | linters: 17 | - name: markdownlint 18 | command: [markdownlint, -q, --config, .github/.markdownlint.yaml, "${target}"] 19 | direct_configs: [.github/.markdownlint.yaml] 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: adoc clean 2 | 3 | %.zwc: % 4 | @lib/zcompile.zsh $< 5 | 6 | adoc: lib/stdlib.lzui.zwc lib/syslib.lzui.zwc lib/utillib.lzui.zwc 7 | @zsd -v --scomm \ 8 | --cignore '(\#[[:blank:]]FUN(C|CTION|):[[:blank:]]*[[:blank:]]{{{*|[[:blank:]]\#[[:blank:]]}}}*)' \ 9 | lib/*.lzui 10 | @cp -f zsdoc/*.adoc docs/ 11 | 12 | html: 13 | @asciidoctor zsdoc/*.lzui.adoc 14 | 15 | clean: 16 | @rm -rf zsdoc 17 | @rm -rf lib/*.zwc 18 | -------------------------------------------------------------------------------- /demos/zui-demo-anchors: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | # 3 | # Shows all types of anchors: external/regenerating, internal, 4 | # internal/regenerating, pointing outside own module 5 | 6 | -zui_std_cleanup deserialize:"zui-demo-anchors" 7 | -zui_std_init app:"zui-demo-anchors" app_name:"ZUI Anchors" 8 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 9 | -zui_std_init2 # after emulate -LR 10 | 11 | -zui_std_store_default_app_config b:border 1 12 | -zui_std_store_default_app_config s:text_mode off 13 | 14 | local internal # trick variable to make a handler no-restart 15 | 16 | # Generator for module 1 17 | demo_generator_A() { 18 | local mod="$1" ice="$2" 19 | 20 | reply=() 21 | -zui_std_anchor "aregen1" "3" "" ",mod1_ice1," "" "[${ZUI[MAGENTA]}third${ZUI[FMT_END]}]" 22 | -zui_std_anchor "anchor" "3+2" "" "" "" "[${ZUI[MAGENTA]}next${ZUI[FMT_END]}]" 23 | -zui_std_anchor "aregen2" "3+2" "" "" "" "[${ZUI[MAGENTA]}next${ZUI[FMT_END]}]" 'internal=1; -zui_std_fly_mod_regen 2 1' 24 | 25 | # Content 26 | reply=( 27 | "RESTART: Jump to ${reply[1]} line ${ZUI[CYAN]}and${ZUI[FMT_END]} regenerate" 28 | "NO-RESTART: Jump to ${reply[2]} module, regenerate ${reply[3]} module ${ZUI[CYAN]}and${ZUI[FMT_END]} jump to it" 29 | "Random number: $RANDOM" 30 | ) 31 | 32 | # Non-selectable lines Hops to jump with [ and ] Local anchors (line numbers will be translated to global) 33 | reply2=( 3 ) reply3=( 1 ) reply4=( aregen1 anchor aregen2 ) 34 | } 35 | 36 | # Generator for module 1 37 | demo_generator_B() { 38 | reply=( "First line of second module" "Random number: $RANDOM" ) 39 | 40 | # Non-selectable lines Hops to jump with [ and ] Local anchors 41 | reply2=( 1 2 ) reply3=( 1 ) reply4=( ) 42 | } 43 | 44 | ## Start application ## 45 | zui-event-loop 1:demo_generator_A 1:demo_generator_B 46 | 47 | -zui_std_cleanup serialize 48 | 49 | return 0 50 | -------------------------------------------------------------------------------- /demos/zui-demo-append: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | # 3 | # A demo showing how to add module instances to already generated document 4 | 5 | -zui_std_cleanup deserialize:"zui-demo-append" 6 | -zui_std_init app:"zui-demo-append" app_name:"ZUI On-The-Fly Append" 7 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 8 | -zui_std_init2 # after emulate -LR 9 | 10 | -zui_std_store_default_app_config b:border 1 11 | -zui_std_store_default_app_config b:status_border 1 12 | -zui_std_store_default_app_config s:text_mode off 13 | 14 | # Generator for module 1 15 | demo_generator_A() { 16 | local mod="$1" ice="$2" 17 | 18 | # Anchor only in last instance 19 | -zui_std_get_mod_factor "$mod" 20 | if [[ "$ice" = "$REPLY" ]]; then 21 | # 1+2 - from 1st line of this module, jump 2 beyond this module 22 | # Regenerate $mod/$ice, and $mod/$ice+1, first running handler 23 | local -a output 24 | -zui_std_anchor "append_next" "1+2" "" ",mod${mod}_ice${ice},mod${mod}_ice$(( ice + 1 ))," "" "${ZUI[MAGENTA]}Append next!${ZUI[FMT_END]}" "-zui_std_set_mod_factor $mod $(( REPLY + 1 ))" output 25 | fi 26 | 27 | # Content 28 | reply=( "This is module $mod, instance $ice" ${output[1]} ) 29 | 30 | # Non-selectable lines Hops to jump with [ and ] Local anchors (if output[1] not empty ...) 31 | reply2=( 1 ) reply3=( 1 ) reply4=( ${output[1]:+append_next} ) 32 | } 33 | 34 | ## Start application ## 35 | zui-event-loop 2:demo_generator_A 36 | 37 | -zui_std_cleanup serialize 38 | 39 | return 0 40 | -------------------------------------------------------------------------------- /demos/zui-demo-buttons: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | # 3 | # Shows all types of hyperlinks, from raw-links to internal-inline links 4 | 5 | -zui_std_cleanup deserialize:"zui-demo-buttons" 6 | -zui_std_init app:"zui-demo-buttons" app_name:"ZUI Buttons" 7 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 8 | -zui_std_init2 # after emulate -LR 9 | 10 | -zui_std_store_default_app_config b:border 1 11 | -zui_std_store_default_app_config b:status_border 1 12 | -zui_std_store_default_app_config s:status_size 5 13 | -zui_std_store_default_app_config s:text_mode off 14 | 15 | local internal # trick variable to make a handler no-restart 16 | local -a logs # own logs queue 17 | 18 | # Generator for module 1 19 | demo_generator_A() { 20 | local mod="$1" ice="$2" arg="$3" 21 | 22 | [[ -n "$arg" ]] && logs+=( "Regenerated with: $arg" ) 23 | 24 | reply=() 25 | -zui_std_rc_button_ext "raw_link" "" "" ",mod1_ice1," "raw-link" "${ZUI[MAGENTA]}link1${ZUI[FMT_END]}" 26 | -zui_std_rc_button_ext "external" "$mod" "$ice" "external action" "" "${ZUI[MAGENTA]}link2${ZUI[FMT_END]}" demo_external_action 27 | -zui_std_rc_button "external_inline" "${ZUI[MAGENTA]}link3${ZUI[FMT_END]}" 'reply=( ",mod1_ice1," "inline handler (external)" )' 28 | -zui_std_rc_button_ext "internal" "$mod" "$ice" "internal action" "" "${ZUI[MAGENTA]}link4${ZUI[FMT_END]}" demo_internal_action # has "internal" in name 29 | -zui_std_rc_button "internal_inline" "${ZUI[MAGENTA]}link5${ZUI[FMT_END]}" 'internal=1; -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'" "inline handler (internal)"' 30 | 31 | # Content 32 | reply=( 33 | "RESTART: Raw-link: ${reply[1]}, hyperlink with external action: ${reply[2]}, with inline (external) code: ${reply[3]}" 34 | "NO-RESTART: Hyperlink with internal action: ${reply[4]}, with inline (internal) code: ${reply[5]}" 35 | "Random number: $RANDOM" 36 | ) 37 | 38 | # Non-selectable lines Hops to jump with [ and ] Local anchors 39 | reply2=( 3 ) reply3=( 1 ) reply4=( ) 40 | } 41 | 42 | demo_external_action() { 43 | local id="$1" mod="$2" ice="$3" data3="$4" data4="$4" 44 | # Request regeneration, pass on data3 as REGENERATE_ARG 45 | reply=( ",mod${mod}_ice${ice}," "$data3" ) 46 | } 47 | 48 | demo_internal_action() { 49 | local id="$1" mod="$2" ice="$3" data3="$4" data4="$4" 50 | -zui_std_fly_mod_regen "$mod" "$ice" "$data3" 51 | } 52 | 53 | -zui-standard-status-callback() { 54 | [[ "${#logs}" -gt 0 ]] && { reply=( "Message: " "${logs[1]}" ); logs=( ${(@)logs[2,-1]} ); return 1; } 55 | # Resulting type: 0 - no message 56 | return 0 57 | } 58 | 59 | ## Start application ## 60 | zui-event-loop 1:demo_generator_A 61 | 62 | -zui_std_cleanup serialize 63 | 64 | return 0 65 | 66 | # vim:ft=zsh 67 | -------------------------------------------------------------------------------- /demos/zui-demo-configure: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-configure" 4 | -zui_std_init app:"zui-demo-configure" app_name:"ZUI Configure/Make wrapper" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config s:timeout 300 9 | -zui_std_store_default_app_config s:text_mode off 10 | 11 | local -a coproc_output 12 | local cstarted=0 mstarted=0 # denote if configure/make is running 13 | 14 | demo_generator_A() { 15 | local mod="$1" ice="$2" 16 | 17 | reply=() 18 | -zui_std_rc_button "button1" "${ZUI[MAGENTA]}Run configure${ZUI[FMT_END]}" 'internal=1; coproc 2>&1 ./configure; cstarted=1' 19 | -zui_std_rc_button "button2" "${ZUI[MAGENTA]}Get CFLAGS${ZUI[FMT_END]}" 'internal=1; get_cflags' 20 | -zui_std_rc_button "button3" "${ZUI[MAGENTA]}Run make${ZUI[FMT_END]}" 'internal=1; coproc 2>&1 make; mstarted=1' 21 | 22 | # Content 23 | reply=( "${reply[1]} ${reply[2]} ${reply[3]}" ) 24 | 25 | # Non-selectable lines Hops to jump with [ and ] Local anchors 26 | reply2=( ) reply3=( 1 ) reply4=( ) 27 | } 28 | 29 | demo_generator_B() { 30 | reply=() 31 | -zui_std_special_text "Configure/Make output" 32 | -zui_std_button "button4" "${ZUI[MAGENTA]}(clear)${ZUI[FMT_END]}" 'internal=1; coproc_output=( ); -zui_std_fly_mod_regen 2 1' 33 | reply=( "${ZUI[YELLOW]}${reply[1]}${ZUI[FMT_END]} ${reply[2]}" "${coproc_output[@]}" ) reply2=( ) reply3=( 1 ) reply4=( ) 34 | } 35 | 36 | # Read & publish configure/make output 37 | -zui-standard-timeout-callback() { 38 | (( mstarted + cstarted == 0 )) && return 39 | 40 | local line had_read=0 41 | repeat 20; do 42 | read -r -p -t 0 line 2>/dev/null && { had_read=1; coproc_output+=( "$line" ); } || break 43 | done 44 | 45 | (( had_read )) && -zui_std_fly_mod_regen 2 1 46 | (( mstarted != 0 && had_read == 0 )) && [[ "${jobtexts[*]}" != *make* ]] && { ZUI[redraw]=1; mstarted=0; -zui_std_stalog "Message: " "make ended"; } 47 | (( cstarted != 0 && had_read == 0 )) && [[ "${jobtexts[*]}" != *configure* ]] && { ZUI[redraw]=1; cstarted=0; -zui_std_stalog "Message: " "configure ended"; } 48 | } 49 | 50 | get_cflags() { 51 | local CFLAGS=`grep '^CFLAGS[[:space:]]*=' Makefile` 52 | -zui_std_stalog "Obtained: " "CFLAGS = ${CFLAGS#*=[[:space:]]##}" 53 | return 1 54 | } 55 | 56 | ## Start application ## 57 | zui-event-loop 1:demo_generator_A 1:demo_generator_B 58 | 59 | -zui_std_cleanup serialize 60 | 61 | return 0 62 | 63 | # vim:ft=zsh 64 | -------------------------------------------------------------------------------- /demos/zui-demo-edit: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | # 3 | # Text editor supporting maximum 1000 lines, 6000 with Zsh 5.3.2 4 | 5 | -zui_std_cleanup deserialize:"zui-demo-edit" 6 | -zui_std_init app:"zui-demo-edit" app_name:"ZUI Text Editor" 7 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 8 | -zui_std_init2 # after emulate -LR 9 | 10 | -zui_std_store_default_app_config s:status_size 3 11 | 12 | local edited_file="$1" width=100 offset=1 internal 13 | local -a lines logs 14 | 15 | [[ -z "$edited_file" ]] && { echo "Required argument: name of a text file to edit"; return 1; } 16 | [[ -e "$edited_file" && ! -f "$edited_file" ]] && { echo "Given file isn't regular: \`$edited_file'"; return 1; } 17 | 18 | { lines=( "${(@f)"$(<$edited_file)"}" ); } 2>/dev/null 19 | 20 | demo_generator_A() { 21 | local mod="$1" ice="$2" 22 | 23 | reply=() 24 | -zui_std_rc_button "button_save_$ice" "${ZUI[RED]}Save${ZUI[FMT_END]}" 'internal=1; print -rl -- "${lines[@]}" > "$edited_file" && logs+=( "File saved" ) || logs+=( "Save failed" )' 25 | -zui_std_rc_button "button_relo_$ice" "${ZUI[RED]}Reload${ZUI[FMT_END]}" 'internal=1; lines=( "${(@f)"$(<$edited_file)"}" ) && logs+=( "File \`$edited_file'"'"' loaded" ); -zui_std_fly_mod_regen 2 1' 26 | -zui_std_rc_button "button_addl_$ice" "${ZUI[RED]}Add line${ZUI[FMT_END]}" 'internal=1; lines+=( "" ); -zui_std_fly_mod_regen 2 1' 27 | -zui_std_rc_button "button_remo_$ice" "${ZUI[RED]}Remove line${ZUI[FMT_END]}" 'internal=1; lines[-1]=( ); -zui_std_fly_mod_regen 2 1' 28 | 29 | # Content 30 | reply=( "${ZUI[YELLOW]}$edited_file${ZUI[FMT_END]}" "${reply[*]}" ) 31 | 32 | # Non-selectable lines Hops to jump with [ and ] Local anchors 33 | reply2=( 1 ) reply3=( 1 ) reply4=( ) 34 | } 35 | 36 | demo_generator_B() { 37 | local mod="$1" ice="$2" 38 | 39 | integer size=${#lines} idx 40 | local -a output 41 | for (( idx=1; idx <= size; idx ++ )); do 42 | reply=() # use the output immediately to utilize array-append optimizations of zsh 5.3.2 43 | noglob -zui_std_text_field "tfield${idx}" width offset lines[$idx] 44 | output+=( "${reply[1]}" ) 45 | done 46 | 47 | # Content Non-selectable lines Hops to jump with [ and ] Local anchors 48 | reply=( $output ) reply2=( ) reply3=( 1 ) reply4=( ) 49 | } 50 | 51 | -zui-standard-status-callback() { [[ "${#logs}" -gt 0 ]] && { reply=( "Message: " "${logs[1]}" ); logs=( ${(@)logs[2,-1]} ); return 1; }; return 0; } 52 | 53 | ## Start application ## 54 | zui-event-loop 1:demo_generator_A 1:demo_generator_B 1:demo_generator_A 55 | 56 | -zui_std_cleanup serialize 57 | 58 | return 0 59 | 60 | # vim:ft=zsh 61 | -------------------------------------------------------------------------------- /demos/zui-demo-fly: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-fly" 4 | -zui_std_init app:"zui-demo-fly" app_name:"ZUI On-The-Fly Regeneration" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config b:border 1 9 | -zui_std_store_default_app_config b:status_border 1 10 | -zui_std_store_default_app_config s:text_mode off 11 | 12 | local internal # trick variable to make a handler no-restart 13 | 14 | # Generator for module 1 15 | demo_generator_A() { 16 | local mod="$1" ice="$2" 17 | 18 | reply=() 19 | -zui_std_rc_button_ext "restart" "" "" ",mod1_ice1," "" "${ZUI[MAGENTA]}restart${ZUI[FMT_END]}" 20 | -zui_std_rc_button "on-the-fly" "${ZUI[MAGENTA]}on-the-fly${ZUI[FMT_END]}" 'internal=1; -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 21 | 22 | # Content 23 | reply=( "${ZUI[YELLOW]}ZUI${ZUI[FMT_END]} can regenerate module instance with ${reply[1]} or ${reply[2]}!" "Random number: $RANDOM" ) 24 | 25 | # Non-selectable lines Hops to jump with [ and ] Local anchors 26 | reply2=( 2 ) reply3=( 1 ) reply4=( ) 27 | } 28 | 29 | # Show handler code in status window 30 | -zui-standard-status-callback() { 31 | local tpe="$1" selectable="$2" uniq="$3" search="$4" line="$5" segment="$6" 32 | [[ "$tpe" = "0" ]] && return 0 # Filter out non-hyperlinks 33 | 34 | shift 6; local id="$1" data1="$2" data2="$3" data3="$4" data4="$5" 35 | id="${id#(zuiiaction|zuiaction|zuicheckbox|zuieanchor|zuianchor|zuitfield)}" 36 | handler="${ZUI[zuiiaction$id]}" 37 | reply=( "Handler: " "$handler" ) 38 | 39 | # Resulting type: 1 - log message 40 | return 1 41 | } 42 | 43 | ## Start application ## 44 | zui-event-loop 1:demo_generator_A 45 | 46 | -zui_std_cleanup serialize 47 | 48 | return 0 49 | 50 | # vim:ft=zsh 51 | -------------------------------------------------------------------------------- /demos/zui-demo-ganchors: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-ganchors" 4 | -zui_std_init app:"zui-demo-ganchors" app_name:"ZUI Global Anchors" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config b:text_mode off 9 | -zui_std_store_default_app_config b:top_anchors 0 # no top-anchors 10 | 11 | demo_generator_A() { 12 | local mod="$1" ice="$2" 13 | 14 | reply=() 15 | -zui_std_get_ganchor 2 1 "[Module2]" 16 | -zui_std_get_ganchor 3 1 "[Module3]" 17 | -zui_std_get_ganchor 4 1 "[Module4/1]" 18 | -zui_std_get_ganchor 4 2 "[Module4/2]" 19 | 20 | # Content 21 | reply=( "There are no top-anchors, but you can fetch them and use in modules:" "" "Jump to ${reply[1]}, ${reply[2]}, ${reply[3]}, ${reply[4]}" ) 22 | 23 | # Non-selectable lines Hops to jump with [ and ] Local anchors 24 | reply2=( 1 2 ) reply3=( 1 ) reply4=( ) 25 | } 26 | 27 | # Generator for module 1 28 | demo_generator_B() { 29 | local mod="$1" ice="$2" 30 | 31 | # Content, no hyper-links Non-selectable lines Hops to jump with [ and ] Local anchors 32 | reply=( "-> Module $mod, instance $ice." ) reply2=( ) reply3=( 1 ) reply4=( ) 33 | } 34 | 35 | ## Start application ## 36 | zui-event-loop 1:demo_generator_A 1:demo_generator_B 1:demo_generator_B 2:demo_generator_B 37 | 38 | -zui_std_cleanup serialize 39 | 40 | return 0 41 | 42 | # vim:ft=zsh 43 | -------------------------------------------------------------------------------- /demos/zui-demo-hello-world: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-hello-world" 4 | -zui_std_init app:"zui-demo-hello-world" app_name:"ZUI Hello World" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config b:border 1 9 | 10 | # Generator for module 1 11 | demo_generator_A() { 12 | local mod="$1" ice="$2" 13 | 14 | # Content, no hyper-links 15 | reply=( "Hello World from ${ZUI[YELLOW]}ZUI${ZUI[FMT_END]}! Module $mod, instance $ice." ) 16 | 17 | # Non-selectable lines Hops to jump with [ and ] Local anchors 18 | reply2=( ) reply3=( 1 ) reply4=( ) 19 | } 20 | 21 | ## Start application ## 22 | zui-event-loop 1:demo_generator_A 23 | 24 | -zui_std_cleanup serialize 25 | 26 | return 0 27 | 28 | # vim:ft=zsh 29 | -------------------------------------------------------------------------------- /demos/zui-demo-history: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-history" 4 | -zui_std_init app:"zui-demo-history" app_name:"ZUI History" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config b:border 1 9 | -zui_std_store_default_app_config s:select_mode "quit" # Quit event loop on text-selection 10 | -zui_std_store_default_app_config s:text_mode "off" # Select whole lines 11 | -zui_std_store_default_app_config b:text_select 1 # Allow selection of text 12 | -zui_std_store_default_app_config s:status_size 2 # The size of the status window 13 | 14 | # Generator for module 1 15 | demo_generator_A() { 16 | local mod="$1" ice="$2" 17 | 18 | # Content, no hyper-links 19 | reply=( "${ZUI[YELLOW]}ZUI${ZUI[FMT_END]} history example! Module $mod, instance $ice." ) 20 | 21 | # Non-selectable lines Hops to jump with [ and ] Local anchors Spacing 22 | reply2=( ) reply3=( 1 ) reply4=( ) reply5="$mod" 23 | } 24 | 25 | ZUI[COLORING_PATTERN]="(vim|emacs|git|make)" ZUI[COLORING_MATCH_MULTIPLE]=1 26 | ZUI[START_IN_SEARCH_MODE]=1 27 | 28 | ## Start application ## 29 | # a:u- is: array, unique elements 30 | zui-event-loop 1:demo_generator_A a:u-history 1:demo_generator_A 31 | 32 | print -zr -- "$REPLY" 33 | 34 | -zui_std_cleanup serialize 35 | 36 | return 0 37 | 38 | # vim:ft=zsh 39 | -------------------------------------------------------------------------------- /demos/zui-demo-list-boxes: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-list-boxes" 4 | -zui_std_init app:"zui-demo-list-boxes" app_name:"ZUI List-Boxes" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config b:border 1 9 | -zui_std_store_default_app_config s:status_size 3 10 | -zui_std_store_default_app_config b:status_border 1 11 | 12 | local xtra # extra line of text, loaded from list box 13 | 14 | # Generator for module 1 15 | demo_generator_A() { 16 | local mod="$1" ice="$2" 17 | 18 | # Variables of the list box 19 | (( ${+ZUI[my_lbox1_width]} == 0 )) && ZUI[my_lbox1_width]=8 20 | (( ${+ZUI[my_lbox1_idx]} == 0 )) && ZUI[my_lbox1_idx]=2 21 | (( ${+ZUI[my_lbox1_opts]} == 0 )) && ZUI[my_lbox1_opts]="Option 1;Option 2;Option 3" 22 | 23 | reply=() 24 | noglob -zui_std_list_box "lbox1_$mod" ZUI[my_lbox1_width] ZUI[my_lbox1_idx] ZUI[my_lbox1_opts] "" "" "" 'xtra="Received: ${ZUI[CYAN]}${${(s:;:)ZUI[my_lbox1_opts]}[${ZUI[my_lbox1_idx]}]}${ZUI[FMT_END]}"; -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 25 | 26 | # Content 27 | reply=( "${ZUI[YELLOW]}ZUI${ZUI[FMT_END]} list-box example: <${reply[1]}>" $xtra ) 28 | 29 | # Non-selectable lines Hops to jump with [ and ] Local anchors 30 | reply2=( ${xtra:+2} ) reply3=( 1 ) reply4=( ) 31 | } 32 | 33 | ## Start application ## 34 | zui-event-loop 1:demo_generator_A 35 | 36 | -zui_std_cleanup serialize 37 | 38 | return 0 39 | 40 | # vim:ft=zsh 41 | -------------------------------------------------------------------------------- /demos/zui-demo-nmap: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-nmap" 4 | -zui_std_init app:"zui-demo-nmap" app_name:"ZUI Nmap TUI" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config s:timeout 300 9 | -zui_std_store_default_app_config s:text_mode off 10 | 11 | local -a coproc_output opts hosts 12 | local internal nstarted=0 # denote if nmap is running 13 | 14 | local cfg_dir="$HOME/.config/nmap_zui" 15 | command mkdir -p $cfg_dir 16 | command touch $cfg_dir/opts.txt $cfg_dir/hosts.txt 17 | opts=( "${(@f)"$(<$cfg_dir/opts.txt)"}" ) 18 | 19 | hosts=( "${(@f)"$(<$cfg_dir/hosts.txt)"}" ) 20 | [[ -z "${hosts[1]}" ]] && { hosts[1]="127.0.0.1"; hosts[2]="192.168.0.*"; } 21 | 22 | demo_generator_A() { 23 | local mod="$1" ice="$2" 24 | 25 | reply=() 26 | -zui_std_rc_button "Abutton1" "${ZUI[MAGENTA]}Run nmap${ZUI[FMT_END]}" 'internal=1; run_sudo; report_options; run_nmap; ZUI[redraw]=1' 27 | -zui_std_rc_button "Abutton2" "${ZUI[MAGENTA]}Add set${ZUI[FMT_END]}" 'internal=1; opts+=( "" ); -zui_std_fly_mod_regen 1 1' 28 | -zui_std_rc_button "Abutton3" "${ZUI[MAGENTA]}Remove set${ZUI[FMT_END]}" 'internal=1; opts[-1]=(); -zui_std_fly_mod_regen 1 1' 29 | -zui_std_rc_button "Abutton4" "${ZUI[MAGENTA]}Save${ZUI[FMT_END]}" 'internal=1; print -rl -- ${opts[@]} > $cfg_dir/opts.txt; opts=( ${opts[@]} ); -zui_std_fly_mod_regen 1 1' 30 | -zui_std_rc_button "Abutton5" "${ZUI[YELLOW]}Add ->${ZUI[FMT_END]}" 'internal=1; hosts+=( "${ZUI[my_host_data]}" ); print -rl -- "${hosts[@]}" > $cfg_dir/hosts.txt; -zui_std_fly_mod_regen 1 1' 31 | -zui_std_rc_button "Abutton6" "${ZUI[YELLOW]}<- Delete${ZUI[FMT_END]}" 'internal=1; hosts[${ZUI[my_select_idx]}]=(); print -rl -- "${hosts[@]}" > $cfg_dir/hosts.txt; ZUI[my_select_idx]=1; -zui_std_fly_mod_regen 1 1' 32 | 33 | ZUI[my_host_width]=35 34 | ZUI[my_host_idx]=1 35 | (( ${+ZUI[my_host_data]} == 0 )) && ZUI[my_host_data]="" 36 | noglob -zui_std_text_field "Atextfield1" ZUI[my_host_width] ZUI[my_host_idx] ZUI[my_host_data] 37 | 38 | ZUI[my_select_width]=30 39 | (( ${+ZUI[my_select_idx]} == 0 )) && ZUI[my_select_idx]=1 40 | ZUI[my_select_data]=${(j,;,)hosts} 41 | noglob -zui_std_list_box "Alistbox1" ZUI[my_select_width] ZUI[my_select_idx] ZUI[my_select_data] "" "" "" 'ZUI[my_host_data]=${${(As,;,)ZUI[my_select_data]}[${ZUI[my_select_idx]}]}' 42 | 43 | integer size=${#opts} i 44 | for (( i=1; i<=size; ++i )); do 45 | ZUI[my_line_width_$i]=50 46 | ZUI[my_line_idx_$i]=1 47 | noglob -zui_std_text_field "line_$i" ZUI[my_line_width_$i] ZUI[my_line_idx_$i] opts[$i] "" "" "" "set_opts $i" 48 | done 49 | 50 | # Content 51 | reply=( "${reply[1]}" "" 52 | "Host: ${reply[7]} ${reply[5]} Choose: ${reply[8]} ${reply[6]}" 53 | "" 54 | "${ZUI[RED]}Option-sets (edit to activate)${ZUI[FMT_END]} ${reply[2]} ${reply[3]} ${reply[4]}" 55 | "${(@)reply[9,-1]}" 56 | ) 57 | 58 | # Non-selectable lines Hops to jump with [ and ] Local anchors 59 | reply2=( ) reply3=( 1 ) reply4=( ) 60 | } 61 | 62 | demo_generator_B() { 63 | reply=() 64 | -zui_std_button "Bbutton1" "${ZUI[MAGENTA]}(clear)${ZUI[FMT_END]}" 'internal=1; coproc_output=( ); -zui_std_fly_mod_regen 2 1' 65 | -zui_std_button "Bbutton2" "${ZUI[MAGENTA]}Save${ZUI[FMT_END]}" 'internal=1; print -rl -- "${coproc_output[@]}" > ${~ZUI[my_path_data]}; -zui_std_stalog "Saved to: " ${~ZUI[my_path_data]}' 66 | 67 | ZUI[my_path_width]=20 68 | ZUI[my_path_idx]=1 69 | (( ${+ZUI[my_path_data]} == 0 )) && ZUI[my_path_data]="~/scan.txt" 70 | noglob -zui_std_text_field "Atextfield1" ZUI[my_path_width] ZUI[my_path_idx] ZUI[my_path_data] 71 | 72 | reply=( "${ZUI[YELLOW]}Nmap output${ZUI[FMT_END]} ${reply[1]} ${reply[3]} ${reply[2]}" "${coproc_output[@]}" ) reply2=( ) reply3=( 1 ) reply4=( ) 73 | } 74 | 75 | # Read & publish configure/make output 76 | -zui-standard-timeout-callback() { 77 | (( nstarted == 0 )) && return 78 | 79 | local line had_read=0 80 | repeat 20; do 81 | read -r -p -t 0 line 2>/dev/null && { had_read=1; coproc_output+=( "$line" ); } || break 82 | done 83 | 84 | (( had_read == 1 )) && -zui_std_fly_mod_regen 2 1 85 | (( had_read == 0 )) && [[ "${jobtexts[*]}" != *nmap* ]] && { ZUI[redraw]=1; nstarted=0; -zui_std_stalog "Message: " "nmap ended"; } 86 | } 87 | 88 | run_sudo() { 89 | zcurses end 90 | sudo true 91 | zcurses refresh 92 | } 93 | 94 | report_options() { 95 | -zui_std_stalog "Using options: " ${ZUI[my_nmap_options]} " to scan host(s): " ${ZUI[my_host_data]} 96 | if [[ "${#coproc_output}" -gt 0 ]]; then 97 | coproc_output+=( "${ZUI[YELLOW]}"------------------------------------------------------------------------"${ZUI[FMT_END]}" ) 98 | fi 99 | } 100 | 101 | run_nmap() { 102 | [[ -z "${ZUI[my_nmap_options]}" ]] && { -zui_std_stalog "No options selected, not running"; return; } 103 | [[ -z "${ZUI[my_host_data]}" || "${ZUI[my_host_data]}" = "&1 sudo nmap ${=ZUI[my_nmap_options]} ${ZUI[my_host_data]%%[[:space:]]##\#*} 107 | } 108 | 109 | set_opts() { 110 | local index="$1" 111 | ZUI[my_nmap_options]="${opts[index]%%[[:space:]]#\#*}" 112 | } 113 | 114 | ZUI[COLORING_PATTERN]="(PORT|STATE|SERVICE|VERSION|NetBIOS)" ZUI[COLORING_MATCH_MULTIPLE]=1 115 | ## Start application ## 116 | zui-event-loop 1:demo_generator_A 1:demo_generator_B 117 | 118 | -zui_std_cleanup serialize 119 | 120 | return 0 121 | 122 | # vim:ft=zsh:syntax=sh 123 | -------------------------------------------------------------------------------- /demos/zui-demo-special-text: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | # 3 | # Shows how to embed a text with special characters 4 | 5 | -zui_std_cleanup deserialize:"zui-demo-buttons" 6 | -zui_std_init app:"zui-demo-buttons" app_name:"ZUI Buttons" 7 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 8 | -zui_std_init2 # after emulate -LR 9 | 10 | -zui_std_store_default_app_config b:text_select 1 11 | 12 | local internal # trick variable to make a handler no-restart 13 | 14 | demo_generator_A() { 15 | local mod="$1" ice="$2" arg="$3" 16 | 17 | reply=() 18 | -zui_std_special_text "Here's" 19 | -zui_std_rc_button "button1" "${ZUI[MAGENTA]}button1${ZUI[FMT_END]}" 'internal=1; -zui_std_stalog "Button 1 pressed"' 20 | -zui_std_rc_button "button2" "${ZUI[MAGENTA]}button2${ZUI[FMT_END]}" 'internal=1; -zui_std_stalog "Button 2 pressed"' 21 | 22 | reply=( "BROKEN: Here's special text, ${reply[2]} and ${reply[3]}" 23 | "CORRECT: ${reply[1]} special text, ${reply[2]} and ${reply[3]}" ) 24 | 25 | # Non-selectable lines Hops to jump with [ and ] Local anchors 26 | reply2=( ) reply3=( 1 ) reply4=( ) 27 | } 28 | 29 | -zui-standard-status-callback() { 30 | [[ -n "${ZUI[pure_text_selected]}" ]] && { 31 | REPLY="${ZUI[pure_text_selected]}" 32 | -zui_std_get_stext "$REPLY" # no change to REPLY if no special text 33 | reply=( "Text: " "$REPLY" ) 34 | return 1 35 | } 36 | return 0 # no message 37 | } 38 | 39 | ## Start application ## 40 | zui-event-loop 1:demo_generator_A 41 | 42 | -zui_std_cleanup serialize 43 | 44 | return 0 45 | 46 | # vim:ft=zsh 47 | -------------------------------------------------------------------------------- /demos/zui-demo-text-fields: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-text-fields" 4 | -zui_std_init app:"zui-demo-text-fields" app_name:"ZUI Text Fields" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config b:border 1 9 | -zui_std_store_default_app_config s:status_size 3 10 | -zui_std_store_default_app_config b:status_border 1 11 | 12 | local xtra # extra line of text, loaded from text field 13 | 14 | # Generator for module 1 15 | demo_generator_A() { 16 | local mod="$1" ice="$2" 17 | 18 | # Variables of the text field 19 | (( ${+ZUI[my_tfield1_width]} == 0 )) && ZUI[my_tfield1_width]=50 20 | (( ${+ZUI[my_tfield1_start]} == 0 )) && ZUI[my_tfield1_start]=1 21 | (( ${+ZUI[my_tfield1_data]} == 0 )) && ZUI[my_tfield1_data]="Enter accepts, ESC cancels, UP/DOWN resizes" 22 | 23 | reply=() 24 | noglob -zui_std_text_field "tfield1_$mod" ZUI[my_tfield1_width] ZUI[my_tfield1_start] ZUI[my_tfield1_data] "" "" "" 'xtra="Received text: ${ZUI[CYAN]}${ZUI[my_tfield1_data]}${ZUI[FMT_END]}"; -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 25 | 26 | # Content 27 | reply=( "${ZUI[YELLOW]}ZUI${ZUI[FMT_END]} text-field example: ${reply[1]}" $xtra ) 28 | 29 | # Non-selectable lines Hops to jump with [ and ] Local anchors 30 | reply2=( ${xtra:+2} ) reply3=( 1 ) reply4=( ) 31 | } 32 | 33 | ## Start application ## 34 | zui-event-loop 1:demo_generator_A 35 | 36 | -zui_std_cleanup serialize 37 | 38 | return 0 39 | 40 | # vim:ft=zsh 41 | -------------------------------------------------------------------------------- /demos/zui-demo-timeout: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-timeout" 4 | -zui_std_init app:"zui-demo-timeout" app_name:"ZUI Timeout Demo" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config s:timeout 500 9 | 10 | demo_generator_A() { 11 | local mod="$1" ice="$2" 12 | 13 | # Content 14 | reply=( "${ZUI[YELLOW]}ZUI${ZUI[FMT_END]} can invoke timeout-callback when no input - random number: $RANDOM" ) 15 | 16 | # Non-selectable lines Hops to jump with [ and ] Local anchors 17 | reply2=( 2 ) reply3=( 1 ) reply4=( ) 18 | } 19 | 20 | -zui-standard-timeout-callback() { -zui_std_fly_mod_regen 1 1; } 21 | -zui-standard-status-callback() { reply=( "timeout_update: " "${ZUI[timeout_update]}" ); return 1; } 22 | 23 | ## Start application ## 24 | zui-event-loop 1:demo_generator_A 25 | 26 | -zui_std_cleanup serialize 27 | 28 | return 0 29 | 30 | # vim:ft=zsh 31 | -------------------------------------------------------------------------------- /demos/zui-demo-toggles: -------------------------------------------------------------------------------- 1 | # Started from Zle or from command line 2 | 3 | -zui_std_cleanup deserialize:"zui-demo-toggles" 4 | -zui_std_init app:"zui-demo-toggles" app_name:"ZUI Toggle Buttons" 5 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 6 | -zui_std_init2 # after emulate -LR 7 | 8 | -zui_std_store_default_app_config s:text_mode off 9 | 10 | local internal # trick variable to make a handler no-restart 11 | 12 | # Generator for module 1 13 | demo_generator_A() { 14 | local mod="$1" ice="$2" 15 | 16 | local dat1 col2 col3 17 | -zui_util_map_bools "ZUI[my_toggle1]" "dat1" "x" " " 18 | -zui_util_map_bools "ZUI[my_toggle2];ZUI[my_toggle3]" "col2;col3" "${ZUI[BG_GREEN]}" "${ZUI[GREEN]}" 19 | 20 | reply=() 21 | -zui_std_button "toggle1" "[${dat1}] Checkbox" '(( ZUI[my_toggle1]=1-ZUI[my_toggle1] )); -zui_std_fly_mod_regen 1 1; internal=1' 22 | -zui_std_rc_button "toggle2" "${col2}Toggle 1${ZUI[FMT_END]}" '(( ZUI[my_toggle2]=1-ZUI[my_toggle2] )); -zui_std_fly_mod_regen 1 1; internal=1' 23 | -zui_std_rc_button "toggle3" "${col3}Toggle 2${ZUI[FMT_END]}" '(( ZUI[my_toggle3]=1-ZUI[my_toggle3] )); -zui_std_fly_mod_regen 1 1; internal=1' 24 | 25 | # Content 26 | reply=( "${reply[1]}" "${reply[2]} ${reply[3]}" ) 27 | 28 | # Non-selectable lines Hops to jump with [ and ] Local anchors 29 | reply2=( ) reply3=( 1 ) reply4=( ) 30 | } 31 | 32 | # Show handler code in status window 33 | -zui-standard-status-callback() { 34 | local tpe="$1" selectable="$2" uniq="$3" search="$4" line="$5" segment="$6" 35 | [[ "$tpe" = "0" ]] && return 0 # Filter out non-hyperlinks 36 | 37 | shift 6; local id="$1" data1="$2" data2="$3" data3="$4" data4="$5" 38 | id="${id#(zuiiaction|zuiaction|zuicheckbox|zuieanchor|zuianchor|zuitfield)}" 39 | handler="${ZUI[zuiiaction$id]}" 40 | [[ "$id" = "toggle1" ]] && reply=( "" "" "Handler: " "$handler" ) || reply=( "Handler: " "$handler" ) 41 | 42 | # Resulting type: 1 - log message 43 | return 1 44 | } 45 | 46 | ## Start application ## 47 | zui-event-loop 1:demo_generator_A 48 | 49 | -zui_std_cleanup serialize 50 | 51 | return 0 52 | 53 | # vim:ft=zsh 54 | -------------------------------------------------------------------------------- /demos/zui-demo-various: -------------------------------------------------------------------------------- 1 | # 2 | # Started from Zle or from command line 3 | # 4 | 5 | # Cleanup $ZUI hash runtime data, deserialize previous state 6 | -zui_std_cleanup deserialize:"zui-demo-various" 7 | # Initialize ZUI app 8 | -zui_std_init app:"zui-demo-various" app_name:"ZUI Large Demo" 9 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 10 | -zui_std_init2 # after emulate -LR 11 | 12 | -zui-standard-text-select-callback() { 13 | -zui_util_strip_codes "$2" 14 | print -r -- "Obtained text selection: [$1] $REPLY" >> /tmp/zui.out 15 | } 16 | 17 | -zui_std_store_default_app_config s:status_size "5" 18 | -zui_std_store_default_app_config s:log_append "above" 19 | -zui_std_store_default_app_config b:status_pointer 1 20 | -zui_std_store_default_app_config s:text_mode "off" 21 | -zui_std_store_default_app_config b:text_select 0 22 | -zui_std_store_default_app_config s:log_colors "white cyan red green cyan red magenta yellow blue" 23 | 24 | # Settings are loaded only because they're altered on the fly 25 | # Normally zui-list, zui-list-wrapper load configuration 26 | -zui_std_load_config b:bold 0 2 'ZUI[bold]' 27 | -zui_std_load_config s:text_mode "off" 2 'ZUI[text_mode]' 28 | -zui_std_load_config b:text_select 1 2 'ZUI[text_select]' 29 | -zui_std_load_config b:status_pointer 1 2 'ZUI[status_pointer]' 30 | -zui_std_load_config s:log_append "above" 2 'ZUI[log_append]' 31 | -zui_std_load_config b:status_border 0 2 'ZUI[status_border]' 32 | 33 | ## 34 | ## Variables (parameters) 35 | ## 36 | 37 | local MATCH; local -i MBEGIN MEND 38 | local -a match mbegin mend 39 | 40 | # A variable that's initially empty, 41 | # and generator 1 uses it as line 42 | local mod1_ice1_extra_line mod2_ice1_extra_line 43 | 44 | # A special parameter to be used in handlers 45 | # that are to-eval code, not function names. 46 | # By doing "internal=1" somewhere in the code 47 | # the anchor, button will be configured as 48 | # internal action. 49 | local internal 50 | 51 | ## 52 | ## External Actions 53 | ## 54 | 55 | -demo_mod1_external_action_1() { 56 | local id="$1" mod="$2" ice="$3" 57 | local var_name="mod${mod}_ice${ice}_extra_line" 58 | : ${(P)var_name::=Function set this line, regeneration used it} 59 | 60 | # Request regeneration with no user data 61 | reply=( ",mod${mod}_ice${ice}," "" ) 62 | } 63 | 64 | ## 65 | ## Internal actions 66 | ## 67 | 68 | # Anchor internal action - data1 is index in time 69 | # of creation of the anchor (it an be changed via 70 | # on-the-fly updates submitted by stdlib function 71 | # -zui_std_submit_list_update) 72 | -zui-standard-global-anchors-callback() { 73 | local id="$1" initial_line="$2" mod="$3" ice="$4" 74 | 75 | if [[ "$mod" = "1" ]]; then 76 | -zui_util_has_default_color && { 77 | # Can reveal transparency, if terminal 78 | # has background image or is transparent 79 | zcurses bg "status" "white/default" 80 | ZUI[status_colorpair]="white/default" 81 | } 82 | 83 | # This is internal zui-list variable, 84 | # accessible because internal actions 85 | # are called from within the list 86 | ZUI[bold]=1 87 | elif [[ "$mod" = "3" && "$ice" = "1" ]]; then 88 | mod3_internal_action "$id" "$mod" "$ice" "$initial_line" "" 89 | fi 90 | } 91 | 92 | mod3_internal_action() { 93 | local id="$1" mod="$2" ice="$3" data3="$4" data4="" 94 | 95 | # Line at generation time, and at run time, 96 | # after transformations of other modules 97 | # that could shift it up or down. {...:+...} 98 | # is: if not empty, then value is ... 99 | -zui_std_fly_mod_regen "$mod" "$ice" ${data3:+"Extra line from top anchor (initial line: $data3, updated line: ${ZUI[zuianchor$id]})"} 100 | } 101 | 102 | ## 103 | ## Other callbacks 104 | ## 105 | 106 | # Optional, can be undefined. It generates additional 107 | # status text, displayed after "Current #...", mainly 108 | # from data of highlighted button. How to just pass 109 | # messages for display is shown in zui-demo-buttons. 110 | -zui-standard-status-callback() { 111 | local tpe="$1" selectable="$2" uniq="$3" search="$4" line="$5" segment="$6" 112 | shift 6 113 | 114 | # No fast log messages 115 | [[ -n "$EPOCHREALTIME" ]] && (( EPOCHREALTIME - ${ZUI[my_prev_log_time]:-(${EPOCHREALTIME}-1)} < 0.14 )) && return 0 116 | ZUI[my_prev_log_time]="$EPOCHREALTIME" 117 | 118 | local pressed="${ZUI[pressed_now]#(zuiiaction|zuiaction|zuieanchor|zuianchor|zuitfield)}" 119 | [[ "$pressed" = log_append* ]] && pressed="LOG: ${(U)ZUI[log_append]} " || pressed="${pressed:+<> }" 120 | 121 | # Zero-type means callback is called on a non-hyperlink 122 | if [[ "$tpe" = "0" ]]; then 123 | if [[ -n "${ZUI[pure_text_selected]}" ]]; then 124 | -zui_util_strip_codes "$segment" 125 | reply=( "$pressed" "Pure-text selected: " "$REPLY" ) 126 | elif [[ -n "${ZUI[line_selected]}" ]]; then 127 | -zui_util_strip_codes "$line" 128 | reply=( "$pressed" "Line selected: " "$REPLY" ) 129 | elif (( $selectable || $uniq || $search )); then 130 | # 1/0 selectable or not || 1/0 uniq mode || 1/0 non-empty search query 131 | # Any of those means no hops and no non-selectables are relevant 132 | -zui_util_strip_codes "$segment" 133 | reply=( "$pressed" "" "Text: " "$REPLY" ) 134 | else 135 | # Detect if current line is also a hop 136 | local on_hop 137 | if [[ "${(t)ZUILIST_HOP_INDICES}" = array* && -n "${ZUILIST_HOP_INDICES[(r)${ZUI[CURRENT_IDX]}]}" ]]; then 138 | on_hop=", also a hop" 139 | fi 140 | 141 | reply=( "$pressed" ) 142 | [[ -z "$pressed" ]] && reply+=( "(A non-selectable line$on_hop)" ) 143 | fi 144 | 145 | # Resulting type: 1 - log message 146 | return 1 147 | else 148 | # No message on text field input 149 | [[ -z "${ZUI[pressed_now]}" && -n "${ZUI[current_tfield]}" ]] && return 0 150 | 151 | [[ "$1" = *(tfield|lbox)* ]] && local id="$1" width="$2" index="$3" text="$4" data1="$5" data2="$6" data3="$7" || 152 | local id="$1" data1="$2" data2="$3" data3="$4" data4="$5" 153 | 154 | [[ $id = (zuiiaction|zuianchor|zuitfield|zuilbox)* ]] && local exint="NO-RESTART" || local exint="RESTART" 155 | [[ $id = (zuianchor|zuieanchor)* ]] && local aindex="Index: $data1" 156 | # A raw link? (i.e. no handler and no dynamic function) 157 | [[ $id != (zuiaction|zuiiaction|zuianchor|zuieanchor|zuitfield|zuilbox)* ]] && exint+=" " 158 | 159 | id="${id#(zuiaction|zuiiaction|zuianchor|zuieanchor|zuitfield|zuilbox)}" 160 | 161 | handler="$ZUI[zuiiaction$id]" 162 | [[ -z "$handler" ]] && handler="${ZUI[zuiaction$id]}" 163 | [[ -z "$handler" ]] && handler="${ZUI[zuitfield$id]}" 164 | [[ -z "$handler" ]] && handler="${ZUI[zuilbox$id]}" 165 | 166 | reply=( "$pressed" ) 167 | [[ -z "$pressed" ]] && reply+=( "Id: $id" "${aindex+ $aindex}" " $exint" " Handler:" " $handler" ) 168 | 169 | # Resulting type: 1 - log message 170 | return 1 171 | fi 172 | } 173 | 174 | ## 175 | ## Generators for modules 176 | ## 177 | 178 | # Generator for module 1 179 | demo_generator_A() { 180 | local mod="$1" ice="$2" user_data=${3:+ \[Obtained regeneration user data: $3\]} 181 | 182 | reply=( ) 183 | # To request regeneration via external anchor (it will jump to given line, 184 | # and also regenerate the given modules) or via raw link (button with no 185 | # handler, so it is external) pass sequence of ",modX_iceY," strings as 186 | # penultimate, and user-data as last argument. 187 | -zui_std_anchor "regenerateA_$mod" "1" "" ",mod1_ice1," "$RANDOM" "[${ZUI[MAGENTA]}Regenerate${ZUI[FMT_END]}]" 188 | -zui_std_rc_button_ext "regenerateB_$mod" "$mod" "$ice" "" "" "${ZUI[MAGENTA]}Regenerate${ZUI[FMT_END]}" -demo_mod1_external_action_1 189 | -zui_std_rc_button_ext "regenerateC_$mod" "" "" ",mod2_ice1," "" "${ZUI[MAGENTA]}Regenerate${ZUI[FMT_END]}" 190 | 191 | # Content 192 | reply=( 193 | "Restart-regeneration module (this is a non-selectable line, a header)" 194 | "${reply[1]} this module via ${ZUI[YELLOW]}list_restart${ZUI[FMT_END]} and ${ZUI[CYAN]}external_anchor${ZUI[FMT_END]}${user_data}" 195 | "${reply[2]} this module via ${ZUI[YELLOW]}list_restart${ZUI[FMT_END]} and ${ZUI[CYAN]}function_call${ZUI[FMT_END]}" 196 | "${reply[3]} the module via ${ZUI[YELLOW]}list_restart${ZUI[FMT_END]} and ${ZUI[CYAN]}raw_link${ZUI[FMT_END]}" 197 | "Random number: $RANDOM" 198 | 199 | # This is set in -demo_mod1_external_action_1 200 | $mod1_ice1_extra_line 201 | ) 202 | 203 | # Jump 2 lines after last line. Note that 204 | # the line index is written as: 205 | # - maximum local line number 206 | # - remaining lines to reach next module 207 | local -a next 208 | -zui_std_anchor "jumpA_$mod" "${#reply}+3" "" "" "" "[${ZUI[YELLOW]}NEXT${ZUI[FMT_END]}]" "" next 209 | reply[4]="${reply[4]//${next[1]}}" 210 | 211 | # Non-selectables (:+ is "if not empty, then...") 212 | reply2=( 1 5 ${mod1_ice1_extra_line:+${#reply}} ) 213 | # Hops 214 | reply3=( 1 ) 215 | # Local anchors 216 | reply4=( regenerateA_$mod jumpA_$mod ) 217 | } 218 | 219 | demo_generator_B() { 220 | local mod="$1" ice="$2" 221 | 222 | # Prepare toggle button's states. Every expression 223 | # in first string will be evaluated and mapped as 224 | # boolean to corresponding variable in second string, 225 | # using bool true - $3, bool false - $4. 226 | local col_bld col_tmd col_tsel col_lap col_sptr col_sb 227 | -zui_util_map_bools "ZUI[bold];ZUI[text_select];[[ \"${ZUI[log_append]}\" = below ]];ZUI[status_pointer];ZUI[status_border]" \ 228 | "col_bld;col_tsel;col_lap;col_sptr;col_sb" "${ZUI[BG_BLUE]}" "${ZUI[GREEN]}" 229 | 230 | reply=( ) 231 | # internal=1 is a trick to make action internal 232 | -zui_std_rc_button "bold_$mod" "${col_bld}bold${ZUI[FMT_END]}" 'internal=1; (( ZUI[bold]=1-ZUI[bold] )); -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 233 | -zui_std_rc_button "text_select_$mod" "${col_tsel}text_select${ZUI[FMT_END]}" 'internal=1; (( ZUI[text_select]=1-ZUI[text_select] )); -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 234 | -zui_std_rc_button "log_append_$mod" "${col_lap}log_append${ZUI[FMT_END]}" 'internal=1; [[ ${ZUI[log_append]} = "below" ]] && ZUI[log_append]=above || ZUI[log_append]=below; -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 235 | -zui_std_rc_button "status_pointer_$mod" "${col_sptr}status_pointer${ZUI[FMT_END]}" 'internal=1; (( ZUI[status_pointer]=1-ZUI[status_pointer] )); -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 236 | -zui_std_rc_button "status_border_$mod" "${col_sb}status_border${ZUI[FMT_END]}" 'internal=1; (( ZUI[status_border]=1-ZUI[status_border] )); -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 237 | -zui_std_rc_button "log_colors_$mod" "${ZUI[GREEN]}log_colors${ZUI[FMT_END]}" 'internal=1; zui_log_colors=( $zui_log_colors[-1] ${(@)zui_log_colors[1,-2]} )' 238 | 239 | # Variables of the list box 240 | (( ${+ZUI[my_lbox1_width]} == 0 )) && ZUI[my_lbox1_width]=5 241 | (( ${+ZUI[my_lbox1_idx]} == 0 )) && ZUI[my_lbox1_idx]=1 242 | (( ${+ZUI[my_lbox1_opts]} == 0 )) && ZUI[my_lbox1_opts]="off;hyp;nohyp;all" 243 | noglob -zui_std_list_box "lbox1_$mod" ZUI[my_lbox1_width] ZUI[my_lbox1_idx] ZUI[my_lbox1_opts] "" "" "" 'ZUI[text_mode]=${${(s:;:)ZUI[my_lbox1_opts]}[${ZUI[my_lbox1_idx]}]}' 244 | 245 | # Don't overwrite user changes 246 | (( ${+ZUI[my_tfield1_width]} == 0 )) && ZUI[my_tfield1_width]=20 247 | (( ${+ZUI[my_tfield1_start]} == 0 )) && ZUI[my_tfield1_start]=4 248 | (( ${+ZUI[my_tfield1_data]} == 0 )) && ZUI[my_tfield1_data]="An example text" 249 | noglob -zui_std_text_field "tfield1_$mod" ZUI[my_tfield1_width] ZUI[my_tfield1_start] ZUI[my_tfield1_data] "" "" "" 'mod2_ice1_extra_line="Received text (with no restart): ${ZUI[CYAN]}${ZUI[my_tfield1_data]}${ZUI[FMT_END]}"; -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 250 | 251 | (( ${+ZUI[my_tfield2_width]} == 0 )) && ZUI[my_tfield2_width]=40 252 | (( ${+ZUI[my_tfield2_start]} == 0 )) && ZUI[my_tfield2_start]=1 253 | (( ${+ZUI[my_tfield2_data]} == 0 )) && ZUI[my_tfield2_data]="I'm wider and start at the beginning" 254 | noglob -zui_std_text_field "tfield2_$mod" ZUI[my_tfield2_width] ZUI[my_tfield2_start] ZUI[my_tfield2_data] "" "" "" 'mod2_ice1_extra_line="Received 2nd text (with no restart): ${ZUI[CYAN]}${ZUI[my_tfield2_data]}${ZUI[FMT_END]}"; -zui_std_fly_mod_regen "'"$mod"'" "'"$ice"'"' 255 | 256 | # Content 257 | reply=( 258 | "Toggle ${reply[1]} with inlined (no function!) code, text mode: <${reply[7]}>, ${reply[2]}" 259 | "Toggle ${reply[3]} - status logs appended at end, or at top, rotate ${reply[6]}" 260 | "Toggle ${reply[4]} - status position pointer, ${reply[5]}" 261 | "Random number: $RANDOM" 262 | "Enter text! |${reply[8]}|, try keys UP/DOWN, [${reply[9]}]" 263 | 264 | $mod2_ice1_extra_line 265 | ) 266 | 267 | # Non-selectables Hops Local anchors # Spacing 268 | reply2=( 4 ${mod2_ice1_extra_line:+6} ) reply3=( 1 ) reply4=( ) reply5=2 269 | } 270 | 271 | # Generator for module 2 272 | demo_generator_C() { 273 | local mod="$1" ice="$2" extra_line="$3" 274 | 275 | # Optional lines 276 | local regenerate jump append_third 277 | 278 | local rand_line="Random number: $RANDOM" 279 | integer rand_line_nr=2 280 | 281 | # 1st instance? 282 | if [[ "$ice" = "1" ]]; then 283 | reply=( ) 284 | # IDs of anchors have own namespace 285 | -zui_std_rc_button_ext "regenerateD_${mod}_${ice}" "$mod" "$ice" "" "" "${ZUI[BOLD]}${ZUI[BLUE]}Regenerate${ZUI[FMT_END]}" mod3_internal_action 286 | -zui_std_anchor "jumpB_${mod}_${ice}" "1" "" "" "" "[${ZUI[YELLOW]}Jump${ZUI[FMT_END]}]" 287 | regenerate="${reply[1]} this module ${ZUI[RED]}ON-THE-FLY${ZUI[FMT_END]}, ${ZUI[YELLOW]}without${ZUI[FMT_END]} list restart" 288 | jump="${reply[2]} to 1st line" 289 | rand_line="Random number: ${ZUI[YELLOW]}$RANDOM${ZUI[FMT_END]}" 290 | rand_line_nr=4 291 | fi 292 | 293 | -zui_std_get_mod_factor "$mod" 294 | 295 | # 2nd instance? Also, is it last instance? 296 | if [[ "$ice" = "2" && "$REPLY" = "2" ]]; then 297 | reply=( ) 298 | -zui_std_anchor "append_third_${mod}_${ice}" "2+2" "" ",mod${mod}_ice${ice},mod${mod}_ice$(( ice + 1 ))," "" "${ZUI[MAGENTA]}Append third!${ZUI[FMT_END]}" "-zui_std_set_mod_factor $mod 3" 299 | append_third="${reply[1]}" 300 | fi 301 | 302 | # Content 303 | reply=( 304 | "Module 3, instance ${ZUI[CYAN]}#$ice${ZUI[FMT_END]}" 305 | $regenerate 306 | $jump 307 | "$rand_line" 308 | $extra_line 309 | $append_third 310 | ) 311 | 312 | # Non-selectables Hops Local anchors (:+ - replace if not null) 313 | # 1 is omitted 314 | reply2=( $rand_line_nr ) reply3=( 1 ) reply4=( ${jump:+jumpB_${mod}_${ice}} ${append_third:+append_third_${mod}_${ice}} ) 315 | } 316 | 317 | ## 318 | ## Start application 319 | ## 320 | 321 | # Pass configuration altered on-the-fly 322 | # so that it's not overwritten at restart 323 | zui-event-loop 1:demo_generator_A 1:demo_generator_B 2:demo_generator_C \ 324 | cfg-refresh:bold,text_mode,text_select,status_pointer,log_append,status_border 325 | 326 | # Cleanup $ZUI hash runtime data 327 | -zui_std_cleanup serialize 328 | 329 | return 0 330 | 331 | # vim:ft=zsh 332 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Logo 4 | 5 | ❮ ZI ❯ - ⬢ ZUI 6 |

7 | 8 | ## ZUI – CGI+DHTML-like User Interface Library for Zsh / ZCurses 9 | 10 | This is a RAD (Rapid Application Development) textual user interface library for Zsh. It in many aspects resembles typical CGI+(D)HTML setup. There are: 11 | 12 | - generators ran on "server" side (basic Zshell-code that is just generating text!), 13 | - event loop that turns the generated text into document with active elements (buttons, anchors, toggle buttons, text fields, list boxes), 14 | - mechanism to regenerate document parts from the original generators. 15 | 16 | So, a Zshell code generates text. It is then turned into document with hyperlinks. DHTML-like calls are possible that will regenerate document parts on the fly. Page can be also reloaded with input data, just like an HTML page. 17 | 18 | A voiced [video tutorial](https://youtu.be/TfZ8b_RS_Bg) shows how to create an application – Nmap network scanner frontend. 19 | 20 | > See also [ZSTYLES](ZSTYLES.md) 21 | 22 | ## Hello World 23 | 24 | ```zsh 25 | # Started from Zle or from command line 26 | 27 | -zui_std_cleanup deserialize:"zui-demo-hello-world" 28 | -zui_std_init app:"zui-demo-hello-world" app_name:"ZUI Hello World" 29 | emulate -LR zsh -o extendedglob -o typesetsilent -o warncreateglobal 30 | -zui_std_init2 # after emulate -LR 31 | 32 | -zui_std_store_default_app_config b:border 1 33 | 34 | demo_generator_A() { 35 | local mod="$1" ice="$2" 36 | # Content, no hyper-links 37 | reply=( "Hello World from ${ZUI[YELLOW]}ZUI${ZUI[FMT_END]}! Module $mod, instance $ice." ) 38 | # Non-selectable lines Hops to jump with [ and ] Local anchors 39 | reply2=( ) reply3=( 1 ) reply4=( ) 40 | } 41 | 42 | ## Start application ## 43 | zui-event-loop 1:demo_generator_A 44 | 45 | -zui_std_cleanup serialize 46 | ``` 47 | 48 | Other example which uses list-box: [zui-demo-list-box](https://github.com/z-shell/zui/blob/main/demos/zui-demo-list-boxes) 49 | 50 | The API is described at the [wiki](https://wiki.zshell.dev/ecosystem/plugins/zui). 51 | 52 | ## Installation 53 | 54 | **The plugin is "standalone"**, which means that only sourcing it is needed. To install, unpack `zui` somewhere and add to `zshrc`: 55 | 56 | ```zsh 57 | source {where-zui-is}/zui.plugin.zsh 58 | ``` 59 | 60 | If using a plugin manager, then `ZI` is recommended, but you can use any other too, and also install with `Oh My Zsh` (by copying directory to `~/.oh-my-zsh/custom/plugins`). 61 | 62 | ### [ZI](https://github.com/z-shell/zi) 63 | 64 | Add `zi load z-shell/zui` to your `.zshrc` file. ZI will handle the rest automatically the next time you start zsh. To update (i.e. to pull from origin) issue `zi update z-shell/zui`. 65 | 66 | ### Antigen 67 | 68 | Add `antigen bundle z-shell/zui` to your `.zshrc` file. Antigen will handle cloning the plugin for you automatically the next time you start zsh. 69 | 70 | ### Oh-My-Zsh 71 | 72 | 1. `cd ~/.oh-my-zsh/custom/plugins` 73 | 2. `git clone git@github.com:z-shell/zui.git` 74 | 3. Add `zui` to your plugin list 75 | 76 | ### Zgen 77 | 78 | Add `zgen load z-shell/zui` to your .zshrc file in the same place you're doing your other `zgen load` calls in. 79 | -------------------------------------------------------------------------------- /docs/ZSTYLES.md: -------------------------------------------------------------------------------- 1 | # Zstyles 2 | 3 | The values being set are the defaults: 4 | 5 | ```zsh 6 | zstyle ":plugin:zui" colorpair "white/black" # Default text color. For Zsh >= 5.3, color "default" 7 | # is available, it might be e.g. transparent (depends 8 | # on terminal configuration) 9 | zstyle ":plugin:zui" border "no" # No border around main window 10 | zstyle ":plugin:zui" border_cp "yellow/black" # Border (and header) color 11 | zstyle ":plugin:zui" bold "no" # No default bold 12 | 13 | zstyle ":plugin:zui" status_colorpair "white/black" # The same as "colorpair", but for status window 14 | zstyle ":plugin:zui" status_border "no" 15 | zstyle ":plugin:zui" status_border_cp "green/black" # Border color of status window 16 | zstyle ":plugin:zui" status_bold "no" 17 | 18 | zstyle ":plugin:zui" mark "red reverse lineund" # String starting with one or two color names continued 19 | # with combination of: reverse, underline, blink, bold, 20 | # lineund, linerev. Last two underline, reverse whole 21 | # active line. The rest marks active button. Uppercase 22 | # color names are for background. 23 | zstyle ":plugin:zui" altmark "red reverse" # As "mark", but for terminals without underline support 24 | zstyle ":plugin:zui" mark2 "yellow reverse" # The same as "mark", but for buttons with background color 25 | zstyle ":plugin:zui" altmark2 "yellow reverse" # The same as "altmark", but for "mark2" 26 | 27 | zstyle ":plugin:zui" status_size "4" # Height of status window, including border (drawn or not) 28 | zstyle ":plugin:zui" status_pointer "yes" # Show line indicating position in document 29 | 30 | zstyle ":plugin:zui" log_append "above" # Put log messages on top of others. Also available: "below" 31 | zstyle ":plugin:zui" log_time_format "[%H:%M] " # Display hour:minute time stamp. Set to "" to disable 32 | zstyle ":plugin:zui" log_index "yes" # Show order number of log messages 33 | zstyle ":plugin:zui" log_size "32" # How many log messages to keep in memory 34 | 35 | zstyle ":plugin:zui" top_anchors "yes" # Show anchors to each module instance at top 36 | 37 | # The colors used for log messages. First two are for message's index and time stamp 38 | zstyle ":plugin:zui" log_colors "white cyan yellow green cyan red magenta yellow blue" 39 | 40 | # Implementation Zstyles 41 | zstyle ":plugin:zui" select_mode "no-restart" # What to do on non-hyperlink selection. Will invoke -zui-standard-sel\ 42 | # ect-callback() passing segment or whole line as argument. Plus, will 43 | # set ZUI[pure_text_selected] or ZUI[line_selected]. If set to "restart" 44 | # then list restart will be performed, otherwise the same. If set to 45 | # "quit" then event loop will be exited, and REPLY will be set to line 46 | # or segment, otherwise the same 47 | zstyle ":plugin:zui" text_mode "all" # Navigate across each bit of text, not only hyperlinks. "hyp" – only 48 | # at lines with hyperlinks, "nohyp" – only at lines with no hyperlinks, 49 | # "off" - text-bit navigation fully turned off 50 | zstyle ":plugin:zui" text_select "yes" # Allow selection on non-hyperlinks (full lines when text_mode is "off" 51 | # or "hyp" – meaning text-bit mode fully turned off or enabled only for 52 | # lines with hyperlinks, leaving text-only lines undivided) 53 | zstyle ":plugin:zui" timeout "-1" # No calls to -zui-standard-timeout-callback. Denotes milliseconds. 54 | # Minimum value is 200. Time is counted when there is no user input. 55 | ``` 56 | 57 | Each application can override those via "...:app:{name}" zstyles, e.g. for application "zi": 58 | 59 | ```zsh 60 | zstyle ":plugin:zui:app:zi" colorpair "250/17" # 256 colors – zsh >= 5.3; "default" color also from this version 61 | zstyle ":plugin:zui:app:zi" border_cp "33/17" # Border (and header) color 62 | zstyle ":plugin:zui:app:zi" border "yes" 63 | zstyle ":plugin:zui:app:zi" bold "yes" 64 | zstyle ":plugin:zui:app:zi" mark "blue WHITE reverse bold lineund" 65 | zstyle ":plugin:zui:app:zi" altmark "green CYAN bold reverse" 66 | zstyle ":plugin:zui:app:zi" status_size "4" # More space for long messages 67 | 68 | zstyle ":plugin:zui:app:zi" text_mode "nohyp" # Text-navigation when no hyperlinks 69 | zstyle ":plugin:zui:app:zi" text_select "false" 70 | zstyle ":plugin:zui:app:zi" log_time_format "[%H:%M:%S] " # hour:minute:second time stamp of log messages 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/stdlib.lzui.adoc: -------------------------------------------------------------------------------- 1 | stdlib.lzui(1) 2 | ============== 3 | :compat-mode!: 4 | 5 | NAME 6 | ---- 7 | stdlib.lzui - a shell script 8 | 9 | SYNOPSIS 10 | -------- 11 | Documentation automatically generated with `zsdoc' 12 | 13 | FUNCTIONS 14 | --------- 15 | 16 | -zui_std_anchor 17 | -zui_std_button 18 | -zui_std_button_ext 19 | -zui_std_cleanup 20 | -zui_std_decode 21 | -zui_std_decode_hyperlink 22 | -zui_std_decode_list_box 23 | -zui_std_decode_text_field 24 | -zui_std_deserialize 25 | -zui_std_fly_array_refresh 26 | -zui_std_fly_mod_regen 27 | -zui_std_fly_mod_regen_ext 28 | -zui_std_get_ganchor 29 | -zui_std_get_mod_factor 30 | -zui_std_get_mod_spacing 31 | -zui_std_get_stext 32 | -zui_std_has_any_hyperlinks 33 | -zui_std_init 34 | -zui_std_init2 35 | -zui_std_is_any_hyperlink 36 | -zui_std_is_hyperlink 37 | -zui_std_is_list_box 38 | -zui_std_is_special_text 39 | -zui_std_is_text_field 40 | -zui_std_list_box 41 | -zui_std_load_config 42 | -zui_std_load_global_index_and_size 43 | -zui_std_map_replies 44 | -zui_std_pack_hyperlinks_into_box 45 | -zui_std_rc_button 46 | -zui_std_rc_button_ext 47 | -zui_std_refresh_configs 48 | -zui_std_reset_replies 49 | -zui_std_serialize 50 | -zui_std_set_mod_factor 51 | -zui_std_set_mod_spacing 52 | -zui_std_special_text 53 | -zui_std_stalog 54 | -zui_std_store_default_app_config 55 | -zui_std_strip_color_codes 56 | -zui_std_strip_meta_data 57 | -zui_std_submit_fly_from_gen_replies 58 | -zui_std_submit_hops 59 | -zui_std_submit_lanchors 60 | -zui_std_submit_list_update 61 | -zui_std_submit_nonselectables 62 | -zui_std_text_field 63 | 64 | DETAILS 65 | ------- 66 | 67 | Script Body 68 | ~~~~~~~~~~~ 69 | 70 | Has 32 line(s). No functions are called (may set up e.g. a hook, a Zle widget bound to a key, etc.). 71 | 72 | -zui_std_anchor 73 | ~~~~~~~~~~~~~~~ 74 | 75 | ____ 76 | 77 | Appends anchor hyperlink into "reply" output array 78 | (or to array given by name via $8). 79 | 80 | Arguments are initially the same as in -zui_std_\ 81 | button, except: 82 | 83 | - the first data argument (data1, $2) needs to be 84 | index of line to jump to, 85 | 86 | - you normally also want to pass instance ID as data2 87 | (module, $3) and data3 (instance, $4) if you assign 88 | a handler that is shared between modules, 89 | 90 | - instead of handler you might use data3 and data4 91 | ($4 & $5) as a module regeneration instruction, 92 | i.e. pass e.g.: ",mod2_ice1," "arg", to regenerate 93 | some module numbered 2, instance 1, with passed 94 | user-data "arg". 95 | 96 | If the handler is external (i.e. doesn't have "internal" 97 | in its name), then it might too deploy list regeneration, 98 | by doing reply=( ",mod2_ice1," "arg"), for example. 99 | 100 | Anchor of which data3 matches ",*," is set to be external. 101 | If handler doesn't have word "internal" in its name, then 102 | anchor is also set to be external. 103 | 104 | Example call: 105 | -zui_std_anchor "regen1" "4" "" ",mod1_ice2," "$RANDOM" "[${ZUI[MAGENTA]}Regen${ZUI[FMT_END]}]" 106 | 107 | Generator has instance ID (mod and ice) in $1 and $2 108 | by the design of restart-regeneration loop. So, this 109 | instructs to regenerate module 1 instance 2, with no 110 | handler call, with $RANDOM as generator's third input 111 | - regeneration user-data. "4" is the line number on 112 | which the cursor will be placed. 113 | 114 | ____ 115 | 116 | Has 18 line(s). Doesn't call other functions. 117 | 118 | Uses feature(s): _setopt_ 119 | 120 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 121 | 122 | -zui_std_button 123 | ~~~~~~~~~~~~~~~ 124 | 125 | ____ 126 | 127 | Short button function - no user data 128 | 129 | $1 - action ID 130 | $2 - text 131 | $3 - optional handler function name, can be empty text 132 | $4 - optional output variable name (defualt: 'reply') 133 | ____ 134 | 135 | Has 2 line(s). Calls functions: 136 | 137 | -zui_std_button 138 | `-- -zui_std_button_ext 139 | 140 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 141 | 142 | -zui_std_button_ext 143 | ~~~~~~~~~~~~~~~~~~~ 144 | 145 | ____ 146 | 147 | Appends hyperlink into output array. It's an action button 148 | shown without surrounding "[" and "]". 149 | 150 | $1 - action ID 151 | $2 - data1, e.g. timestamp 152 | $3 - data2, e.g. command 153 | $4 - data3, e.g. active path 154 | $5 - data4, e.g. file path, file name, URL, other data 155 | $6 - text 156 | $7 - optional handler function name, can be empty text 157 | $8 - optional output variable name (defualt: 'reply') 158 | 159 | Output array is extended by hyperlink's text (one new element) 160 | 161 | ____ 162 | 163 | Has 15 line(s). Doesn't call other functions. 164 | 165 | Called by: 166 | 167 | -zui_std_button 168 | 169 | -zui_std_cleanup 170 | ~~~~~~~~~~~~~~~~ 171 | 172 | ____ 173 | 174 | This function clears application data 175 | from $ZUI global hash. To be called at 176 | exit and at start of zui application 177 | ____ 178 | 179 | Has 58 line(s). Calls functions: 180 | 181 | -zui_std_cleanup 182 | |-- -zui_std_deserialize 183 | `-- -zui_std_serialize 184 | 185 | Uses feature(s): _unfunction_ 186 | 187 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 188 | 189 | -zui_std_decode 190 | ~~~~~~~~~~~~~~~ 191 | 192 | ____ 193 | 194 | Tries various decoding functions, testable. Returns (in REPLY) 195 | "1" if hyperlink (anchor, button, raw link), "2" if text field, 196 | "3" if list-box 197 | 198 | $1 - hyperlink 199 | $2 - output parameter name, for type (default: REPLY) 200 | $3 - output array name, for hyperlink data (default: reply) 201 | ____ 202 | 203 | Has 7 line(s). Calls functions: 204 | 205 | -zui_std_decode 206 | |-- -zui_std_decode_hyperlink 207 | |-- -zui_std_decode_list_box 208 | `-- -zui_std_decode_text_field 209 | 210 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 211 | 212 | -zui_std_decode_hyperlink 213 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 214 | 215 | ____ 216 | 217 | Decodes given button/anchor/raw hyperlink returning 218 | data contained. Testable - test to check if string 219 | was correctly decoded. 220 | 221 | $1 - hyperlink string 222 | $2 - optional output array name (default: "reply") 223 | 224 | $reply[1] - ID (of button, anchor or raw link) 225 | $reply[2] - data1 226 | $reply[3] - data2 227 | $reply[4] - data3 228 | $reply[5] - data4 229 | 230 | ____ 231 | 232 | Has 19 line(s). Doesn't call other functions. 233 | 234 | Called by: 235 | 236 | -zui_std_decode 237 | 238 | -zui_std_decode_list_box 239 | ~~~~~~~~~~~~~~~~~~~~~~~~ 240 | 241 | ____ 242 | 243 | Decodes given list-box and returns data contained. 244 | Testable - test to check if hyperlink was correctly 245 | decoded. 246 | 247 | $1 - hyperlink string 248 | $2 - optional output array name (default: "reply") 249 | 250 | $reply[1] - ID (of action) 251 | $reply[2] - width parameter name 252 | $reply[3] - start-index parameter name 253 | $reply[4] - text parameter name 254 | $reply[5] - data1 255 | $reply[6] - data2 256 | $reply[7] - data3 257 | 258 | ____ 259 | 260 | Has 22 line(s). Doesn't call other functions. 261 | 262 | Called by: 263 | 264 | -zui_std_decode 265 | syslib.lzui/-zui_sys_get_tfield_cursor_boundaries 266 | 267 | -zui_std_decode_text_field 268 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 269 | 270 | ____ 271 | 272 | Decodes given text-field and returns data contained. 273 | Testable - test to check if hyperlink was correctly 274 | decoded. 275 | 276 | $1 - hyperlink string 277 | $2 - optional output array name (default: "reply") 278 | 279 | $reply[1] - ID (of action) 280 | $reply[2] - width parameter name 281 | $reply[3] - start-index parameter name 282 | $reply[4] - text parameter name 283 | $reply[5] - data1 284 | $reply[6] - data2 285 | $reply[7] - data3 286 | 287 | ____ 288 | 289 | Has 22 line(s). Doesn't call other functions. 290 | 291 | Called by: 292 | 293 | -zui_std_decode 294 | syslib.lzui/-zui_sys_get_tfield_cursor_boundaries 295 | 296 | -zui_std_deserialize 297 | ~~~~~~~~~~~~~~~~~~~~ 298 | 299 | ____ 300 | 301 | Reads ZUI[serialized_${ZUI[app]}] and maps 302 | the content onto ZUI keys, normally my_* 303 | keys. Use this to restore application state 304 | after exit and consecutive start. 305 | ____ 306 | 307 | Has 9 line(s). Doesn't call other functions. 308 | 309 | Called by: 310 | 311 | -zui_std_cleanup 312 | 313 | -zui_std_fly_array_refresh 314 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 315 | 316 | ____ 317 | 318 | Submits on-the-fly array refresh. The given instance ID 319 | should point to an array ("a:" prefix at zui-event-loop). 320 | The array will be read again and pasted into document 321 | replacing previous content. 322 | 323 | No instance index is requested, because arrays have only 324 | single instance. 325 | 326 | $1 - module's index 327 | ____ 328 | 329 | Has 14 line(s). Calls functions: 330 | 331 | -zui_std_fly_array_refresh 332 | |-- -zui_std_map_replies 333 | |-- -zui_std_reset_replies 334 | `-- -zui_std_submit_fly_from_gen_replies 335 | |-- -zui_std_submit_hops 336 | |-- -zui_std_submit_lanchors 337 | |-- -zui_std_submit_list_update 338 | `-- -zui_std_submit_nonselectables 339 | 340 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 341 | 342 | -zui_std_fly_mod_regen 343 | ~~~~~~~~~~~~~~~~~~~~~~ 344 | 345 | ____ 346 | 347 | Submits on-the-fly module update. Needs only instance ID, 348 | the generator to run is the one specified for zui-event-loop 349 | 350 | $1 - module's index 351 | $2 - instance index 352 | $3, $4, ... - additional arguments for 353 | the generator 354 | ____ 355 | 356 | Has 12 line(s). Calls functions: 357 | 358 | -zui_std_fly_mod_regen 359 | |-- -zui_std_map_replies 360 | |-- -zui_std_reset_replies 361 | `-- -zui_std_submit_fly_from_gen_replies 362 | |-- -zui_std_submit_hops 363 | |-- -zui_std_submit_lanchors 364 | |-- -zui_std_submit_list_update 365 | `-- -zui_std_submit_nonselectables 366 | 367 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 368 | 369 | -zui_std_fly_mod_regen_ext 370 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 371 | 372 | ____ 373 | 374 | Submits on-the-fly module update. Needs 375 | generator to run and instance ID. 376 | 377 | $1 - name of generator function 378 | $2 - module's index 379 | $3 - instance index 380 | $4, $5, ... - additional arguments for 381 | the generator 382 | ____ 383 | 384 | Has 10 line(s). Calls functions: 385 | 386 | -zui_std_fly_mod_regen_ext 387 | |-- -zui_std_map_replies 388 | |-- -zui_std_reset_replies 389 | `-- -zui_std_submit_fly_from_gen_replies 390 | |-- -zui_std_submit_hops 391 | |-- -zui_std_submit_lanchors 392 | |-- -zui_std_submit_list_update 393 | `-- -zui_std_submit_nonselectables 394 | 395 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 396 | 397 | -zui_std_get_ganchor 398 | ~~~~~~~~~~~~~~~~~~~~ 399 | 400 | ____ 401 | 402 | Doesn't have data1 (normally index to jump to) nor other data, and 403 | also doesn't have handler. Returns anchor button that jumps to given 404 | GLOBAL anchor and calls its handler (which invokes -zui-standard-gl\ 405 | obal-anchors-callback). 406 | 407 | $1 - module index 408 | $2 - instance index 409 | $3 - visible text of the anchor 410 | $4 - optional output array name 411 | ____ 412 | 413 | Has 6 line(s). Doesn't call other functions. 414 | 415 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 416 | 417 | -zui_std_get_mod_factor 418 | ~~~~~~~~~~~~~~~~~~~~~~~ 419 | 420 | ____ 421 | 422 | Returns factor of given module (it's the number 423 | of instances of the module that are generated) 424 | 425 | $1 - module index 426 | $2 - output parameter name (default: REPLY) 427 | ____ 428 | 429 | Has 2 line(s). Doesn't call other functions. 430 | 431 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 432 | 433 | -zui_std_get_mod_spacing 434 | ~~~~~~~~~~~~~~~~~~~~~~~~ 435 | 436 | ____ 437 | 438 | Gets module's spacing, either from the transport place 439 | - ZUI[SPACING_${mod}_${ice}] hash field, or from the 440 | destination place - mod${1}_ice${2}_spacing parameter 441 | directly used during on-the-fly generation or event-loop 442 | generation. 443 | 444 | Testable, but errors will not happen. 445 | 446 | $1 - module index 447 | $2 - instance index 448 | $3 - "tra" or "dst" - transport or destination 449 | ____ 450 | 451 | Has 12 line(s). Doesn't call other functions. 452 | 453 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 454 | 455 | -zui_std_get_stext 456 | ~~~~~~~~~~~~~~~~~~ 457 | 458 | ____ 459 | 460 | 461 | $1 - special-text string 462 | $2 - optional output parameter name (default: "REPLY") 463 | 464 | REPLY: decoded text contained in the special string 465 | 466 | ____ 467 | 468 | Has 8 line(s). Doesn't call other functions. 469 | 470 | Called by: 471 | 472 | syslib.lzui/-zui_sys_get_tfield_cursor_boundaries 473 | 474 | -zui_std_has_any_hyperlinks 475 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 476 | 477 | ____ 478 | 479 | Checks if given text has any hyperlinks (buttons/anchors/raws, 480 | text fields, list boxes) 481 | ____ 482 | 483 | Has 3 line(s). Doesn't call other functions. 484 | 485 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 486 | 487 | -zui_std_init 488 | ~~~~~~~~~~~~~ 489 | 490 | ____ 491 | 492 | Initializes ZUI application. To be called before any emulate -L 493 | Can take two arguments, prefixed with app: or app_name:, to set 494 | ZUI[app] or ZUI[app_name] 495 | ____ 496 | 497 | Has 9 line(s). Doesn't call other functions. 498 | 499 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 500 | 501 | -zui_std_init2 502 | ~~~~~~~~~~~~~~ 503 | 504 | ____ 505 | 506 | Initializes ZUI application. To 507 | be called after any emulate -L 508 | ____ 509 | 510 | Has 9 line(s). Doesn't call other functions. 511 | 512 | Uses feature(s): _setopt_ 513 | 514 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 515 | 516 | -zui_std_is_any_hyperlink 517 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 518 | 519 | ____ 520 | 521 | Tests if given text is a hyperlink. Returns (in REPLY) 522 | "1" if plain hyperlink (anchor, button, raw link), "2" 523 | if text field, "3" if list-box 524 | 525 | $1 - hyperlink 526 | $2 - output parameter name, for type (default: REPLY) 527 | ____ 528 | 529 | Has 6 line(s). Calls functions: 530 | 531 | -zui_std_is_any_hyperlink 532 | |-- -zui_std_is_hyperlink 533 | |-- -zui_std_is_list_box 534 | `-- -zui_std_is_text_field 535 | 536 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 537 | 538 | -zui_std_is_hyperlink 539 | ~~~~~~~~~~~~~~~~~~~~~ 540 | 541 | ____ 542 | 543 | Checks if given text is a button/anchor/raw hyperlink 544 | ____ 545 | 546 | Has 1 line(s). Doesn't call other functions. 547 | 548 | Called by: 549 | 550 | -zui_std_is_any_hyperlink 551 | syslib.lzui/-zui_sys_get_tfield_cursor_boundaries 552 | 553 | -zui_std_is_list_box 554 | ~~~~~~~~~~~~~~~~~~~~ 555 | 556 | ____ 557 | 558 | Checks if given text is a list box hyperlink 559 | ____ 560 | 561 | Has 1 line(s). Doesn't call other functions. 562 | 563 | Called by: 564 | 565 | -zui_std_is_any_hyperlink 566 | 567 | -zui_std_is_special_text 568 | ~~~~~~~~~~~~~~~~~~~~~~~~ 569 | 570 | ____ 571 | 572 | Checks if given text is a button/anchor/raw hyperlink 573 | ____ 574 | 575 | Has 1 line(s). Doesn't call other functions. 576 | 577 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 578 | 579 | -zui_std_is_text_field 580 | ~~~~~~~~~~~~~~~~~~~~~~ 581 | 582 | ____ 583 | 584 | Checks if given text is a text field hyperlink 585 | ____ 586 | 587 | Has 1 line(s). Doesn't call other functions. 588 | 589 | Called by: 590 | 591 | -zui_std_is_any_hyperlink 592 | 593 | -zui_std_list_box 594 | ~~~~~~~~~~~~~~~~~ 595 | 596 | ____ 597 | 598 | Appends list-box hyperlink into output array (default: reply) 599 | 600 | $1 - action ID 601 | $2 - width parameter (min and max text width - padding with spaces) 602 | $3 - index parameter - what option is active 603 | $4 - options parameter - name of parameter holding ";" separated options 604 | $5 - data1 605 | $6 - data2 606 | $7 - data3 607 | $8 - handler (function name) 608 | $9 - optional output array name (default: reply) 609 | 610 | ____ 611 | 612 | Has 21 line(s). Doesn't call other functions. 613 | 614 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 615 | 616 | -zui_std_load_config 617 | ~~~~~~~~~~~~~~~~~~~~ 618 | 619 | ____ 620 | 621 | Loads variable from configuration if it's older than 622 | e.g. 2 seconds. The point is: I expect users to not 623 | always manage ZUI hash well: forgot to call *cleanup, 624 | not reset ZUI[app], etc. This will be covered here: 625 | if config field in ZUI is fresh, less than say 2 626 | seconds old, it means it's probably correctly aimed 627 | at the ZUI application starting. If it's older, it's 628 | a stray value from previous app. 629 | 630 | This applies only to configuration variables stored in 631 | ZUI hash ($4 = ZUI\[*\]). Other target variables are 632 | just being read, without age examination. 633 | 634 | $1 - Zstyle variable to load, with "s:" or "b:" prefix 635 | for string or boolean 636 | $2 - default value, 0 or 1 for bools 637 | $3 - time limit 638 | $4 - output parameter to fill (name) 639 | ____ 640 | 641 | Has 33 line(s). Doesn't call other functions. 642 | 643 | Uses feature(s): _zstyle_ 644 | 645 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 646 | 647 | -zui_std_load_global_index_and_size 648 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 649 | 650 | ____ 651 | 652 | Use this to quickly load variables: 653 | 654 | - mod${midx}_ice${iidx}_global_index 655 | - mod${midx}_ice${iidx}_size 656 | 657 | into parameters given by names. 658 | 659 | Has default target parameters' names 660 | (REPLY & REPLY2), this might lead to 661 | silent errors, but I prefer that to 662 | error-revealing crash.. Hm.. 663 | 664 | $1 - module index 665 | $2 - instance index 666 | $3 - output parameter name for global index 667 | $4 - output parameter name for size 668 | ____ 669 | 670 | Has 13 line(s). Doesn't call other functions. 671 | 672 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 673 | 674 | -zui_std_map_replies 675 | ~~~~~~~~~~~~~~~~~~~~ 676 | 677 | ____ 678 | 679 | Maps reply{,2..5} parameters onto module 680 | parameters: 681 | mod${midx}_ice${iidx}_output mod${midx}_ice${iidx}_size 682 | mod${midx}_ice${iidx}_nonselectables mod${midx}_ice${iidx}_hops 683 | mod${midx}_ice${iidx}_lanchors mod${midx}_ice${iidx}_spacing 684 | 685 | $1 - module index 686 | $2 - instance index 687 | ____ 688 | 689 | Has 13 line(s). Doesn't call other functions. 690 | 691 | Called by: 692 | 693 | -zui_std_fly_array_refresh 694 | -zui_std_fly_mod_regen 695 | -zui_std_fly_mod_regen_ext 696 | 697 | -zui_std_pack_hyperlinks_into_box 698 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 699 | 700 | ____ 701 | 702 | Puts given links in a box, i.e. into sequence of lines 703 | that are limited in length. The sequence is returned 704 | in reply array. The length of a link is the length of 705 | its text, i.e. it doesn't include meta-data. 706 | 707 | $1 - box width (line length) 708 | $2 - max box height (i.e. max # of lines) 709 | $3 - hyperlink 1 710 | $4 - hyperlink 2 711 | $5 - ... 712 | 713 | ____ 714 | 715 | Has 44 line(s). Doesn't call other functions. 716 | 717 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 718 | 719 | -zui_std_rc_button 720 | ~~~~~~~~~~~~~~~~~~ 721 | 722 | ____ 723 | 724 | Short rectangle button function - no user data 725 | 726 | $1 - action ID 727 | $2 - text 728 | $3 - optional handler function name, can be empty text 729 | $4 - optional output variable name (defualt: 'reply') 730 | ____ 731 | 732 | Has 2 line(s). Calls functions: 733 | 734 | -zui_std_rc_button 735 | `-- -zui_std_rc_button_ext 736 | 737 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 738 | 739 | -zui_std_rc_button_ext 740 | ~~~~~~~~~~~~~~~~~~~~~~ 741 | 742 | ____ 743 | 744 | Appends hyperlink into output array. It's an action button 745 | shown with surrounding [ and ]. 746 | 747 | Arguments are the same as in -zui_std_button_ext 748 | 749 | ____ 750 | 751 | Has 15 line(s). Doesn't call other functions. 752 | 753 | Called by: 754 | 755 | -zui_std_rc_button 756 | 757 | -zui_std_refresh_configs 758 | ~~~~~~~~~~~~~~~~~~~~~~~~ 759 | 760 | ____ 761 | 762 | Causes -zui_std_load_config to think 763 | the configuration variable is freshly 764 | loaded. Use this when restarting list 765 | and updating ZUI[config] manually, with 766 | no Zstyle update 767 | 768 | $1, $2 ... – keys in ZUI to update, config 769 | variables' names 770 | ____ 771 | 772 | Has 6 line(s). Doesn't call other functions. 773 | 774 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 775 | 776 | -zui_std_reset_replies 777 | ~~~~~~~~~~~~~~~~~~~~~~ 778 | 779 | ____ 780 | 781 | Resets parameters reply{,2..5}, i.e. sets them 782 | to empty arrays or strings 783 | ____ 784 | 785 | Has 1 line(s). Doesn't call other functions. 786 | 787 | Called by: 788 | 789 | -zui_std_fly_array_refresh 790 | -zui_std_fly_mod_regen 791 | -zui_std_fly_mod_regen_ext 792 | 793 | -zui_std_serialize 794 | ~~~~~~~~~~~~~~~~~~ 795 | 796 | ____ 797 | 798 | Stores my_* keys of ZUI hash into 799 | "serialized_${ZUI[app]}" key, which 800 | can be read with *deserialize() call 801 | ____ 802 | 803 | Has 11 line(s). Doesn't call other functions. 804 | 805 | Called by: 806 | 807 | -zui_std_cleanup 808 | 809 | -zui_std_set_mod_factor 810 | ~~~~~~~~~~~~~~~~~~~~~~~ 811 | 812 | ____ 813 | 814 | Modifies how many instances of a module should be generated. 815 | A regeneration (no on-the-fly support) should be called on 816 | the new instances. 817 | 818 | $1 - module index 819 | $2 - new factor 820 | ____ 821 | 822 | Has 2 line(s). Doesn't call other functions. 823 | 824 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 825 | 826 | -zui_std_set_mod_spacing 827 | ~~~~~~~~~~~~~~~~~~~~~~~~ 828 | 829 | ____ 830 | 831 | It sets corresponding ZUI[SPACING_$mod_$ice] variable, 832 | which then can be read in generator, and returned via 833 | reply5, and from that point actually used in drawing, 834 | either on-the-fly, or via restart to zui-event-loop. 835 | So this is only a transport of data into generator, the 836 | thing that directly sets spacing is reply5. Spacing is 837 | the number of blank lines before the module's instance. 838 | 839 | TODO: on-the-fly regeneration ignores reply5 840 | 841 | $1 - module index 842 | $2 - instance index 843 | $3 - the spacing to set (i.e. number of blank lines) 844 | ____ 845 | 846 | Has 1 line(s). Doesn't call other functions. 847 | 848 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 849 | 850 | -zui_std_special_text 851 | ~~~~~~~~~~~~~~~~~~~~~ 852 | 853 | ____ 854 | 855 | Appends special-text into output array. The text can contain special 856 | characters like ', `, (, [, space. 857 | 858 | $1 - text 859 | $2 - optional output array name 860 | 861 | ____ 862 | 863 | Has 9 line(s). Doesn't call other functions. 864 | 865 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 866 | 867 | -zui_std_stalog 868 | ~~~~~~~~~~~~~~~ 869 | 870 | Has 1 line(s). Calls functions: 871 | 872 | -zui_std_stalog 873 | `-- syslib.lzui/-zui_sys_add_message 874 | 875 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 876 | 877 | -zui_std_store_default_app_config 878 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 879 | 880 | ____ 881 | 882 | Stores given ZUI[app] configuration if it is not 883 | already set by user, i.e. if given Zstyle is empty, 884 | it is then set to given value, so that ZUI will 885 | read it as the application default, overriding ZUI 886 | global default. 887 | 888 | $1 - Zstyle variable to write, with "s:" or "b:" prefix 889 | for string or boolean 890 | $2 - value to write, 0 or 1 for bools 891 | 892 | Returns 0 if written, 1 if Zstyle was already set 893 | ____ 894 | 895 | Has 18 line(s). Doesn't call other functions. 896 | 897 | Uses feature(s): _zstyle_ 898 | 899 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 900 | 901 | -zui_std_strip_color_codes 902 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 903 | 904 | Has 1 line(s). Doesn't call other functions. 905 | 906 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 907 | 908 | -zui_std_strip_meta_data 909 | ~~~~~~~~~~~~~~~~~~~~~~~~ 910 | 911 | Has 9 line(s). Doesn't call other functions. 912 | 913 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 914 | 915 | -zui_std_submit_fly_from_gen_replies 916 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 917 | 918 | ____ 919 | 920 | Internal action can call this function in 921 | order to replace module with new version. 922 | But see call -zui_std_fly_mod_regen. 923 | 924 | It uses parameters reply{,2..5} normally 925 | returned from a module generator. It covers 926 | calls to multiple submit functions: 927 | 928 | - -zui_std_submit_list_update 929 | - -zui_std_submit_nonselectables 930 | - -zui_std_submit_hops 931 | - -zui_std_submit_lanchors 932 | 933 | $1 - module index 934 | $2 - instance index 935 | ____ 936 | 937 | Has 9 line(s). Calls functions: 938 | 939 | -zui_std_submit_fly_from_gen_replies 940 | |-- -zui_std_submit_hops 941 | |-- -zui_std_submit_lanchors 942 | |-- -zui_std_submit_list_update 943 | `-- -zui_std_submit_nonselectables 944 | 945 | Called by: 946 | 947 | -zui_std_fly_array_refresh 948 | -zui_std_fly_mod_regen 949 | -zui_std_fly_mod_regen_ext 950 | 951 | -zui_std_submit_hops 952 | ~~~~~~~~~~~~~~~~~~~~ 953 | 954 | ____ 955 | 956 | Internal action can call this function when 957 | replacing part of list to provide new hops, 958 | by submitting local indices. They will be 959 | automatically translated to global indices. 960 | 961 | $1, $2, ... - local indices of hops in the 962 | substituted fragment 963 | ____ 964 | 965 | Has 1 line(s). Doesn't call other functions. 966 | 967 | Called by: 968 | 969 | -zui_std_submit_fly_from_gen_replies 970 | 971 | -zui_std_submit_lanchors 972 | ~~~~~~~~~~~~~~~~~~~~~~~~ 973 | 974 | ____ 975 | 976 | Internal action can call this function when 977 | replacing part of list to provide new local 978 | anchors (their IDs). Their indices (stored 979 | in $ZUI) will be globalized. 980 | 981 | $1, $2, ... - IDs of anchors to be globalized 982 | ____ 983 | 984 | Has 1 line(s). Doesn't call other functions. 985 | 986 | Called by: 987 | 988 | -zui_std_submit_fly_from_gen_replies 989 | 990 | -zui_std_submit_list_update 991 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 992 | 993 | ____ 994 | 995 | Internal action can call this function in 996 | order to replace part of list with new text. 997 | 998 | $1 - module index, can be empty for stray list update 999 | $2 - instance index, can be empty as above 1000 | $3 - beginning line number of area to replace 1001 | $4 - end line number of area to replace 1002 | $5, $6, ... - new elements to replace lines $1..$2 1003 | ____ 1004 | 1005 | Has 3 line(s). Doesn't call other functions. 1006 | 1007 | Called by: 1008 | 1009 | -zui_std_submit_fly_from_gen_replies 1010 | 1011 | -zui_std_submit_nonselectables 1012 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1013 | 1014 | ____ 1015 | 1016 | Internal action can call this function when 1017 | replacing part of list to provide new local 1018 | indices that are non-selectable. They will 1019 | be translated to global indices. 1020 | 1021 | $1, $2, ... - local indices of nonselectables 1022 | in the substituted fragment 1023 | ____ 1024 | 1025 | Has 1 line(s). Doesn't call other functions. 1026 | 1027 | Called by: 1028 | 1029 | -zui_std_submit_fly_from_gen_replies 1030 | 1031 | -zui_std_text_field 1032 | ~~~~~~~~~~~~~~~~~~~ 1033 | 1034 | ____ 1035 | 1036 | Appends text-field hyperlink into output array (default: reply) 1037 | 1038 | $1 - action ID 1039 | $2 - width parameter (min and max text width - padding with "_") 1040 | $3 - start-index parameter - what part of string to show 1041 | $4 - text parameter - name of parameter holding text 1042 | $5 - data1 1043 | $6 - data2 1044 | $7 - data3 1045 | $8 - handler (function name) 1046 | $9 - optional output array name (default: reply) 1047 | 1048 | ____ 1049 | 1050 | Has 21 line(s). Doesn't call other functions. 1051 | 1052 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 1053 | 1054 | -------------------------------------------------------------------------------- /docs/syslib.lzui.adoc: -------------------------------------------------------------------------------- 1 | syslib.lzui(1) 2 | ============== 3 | :compat-mode!: 4 | 5 | NAME 6 | ---- 7 | syslib.lzui - a shell script 8 | 9 | SYNOPSIS 10 | -------- 11 | Documentation automatically generated with `zsdoc' 12 | 13 | FUNCTIONS 14 | --------- 15 | 16 | -zui_sys_add_message 17 | -zui_sys_decode_hops 18 | -zui_sys_decode_lanchors 19 | -zui_sys_decode_list_update 20 | -zui_sys_decode_nonselectables 21 | -zui_sys_gather_lanchors 22 | -zui_sys_get_match_line 23 | -zui_sys_get_tfield_cursor_boundaries 24 | -zui_sys_index_tail_width 25 | -zui_sys_map_cursor_on_string 26 | 27 | DETAILS 28 | ------- 29 | 30 | Script Body 31 | ~~~~~~~~~~~ 32 | 33 | Has 3 line(s). No functions are called (may set up e.g. a hook, a Zle widget bound to a key, etc.). 34 | 35 | -zui_sys_add_message 36 | ~~~~~~~~~~~~~~~~~~~~ 37 | 38 | ____ 39 | 40 | Appends given message to ZUI_MESSAGES 41 | 42 | $1 - message type 43 | $2 - timestamp 44 | $3, $4, ... - message bits / lines 45 | ____ 46 | 47 | Has 19 line(s). Doesn't call other functions. 48 | 49 | Called by: 50 | 51 | stdlib.lzui/-zui_std_stalog 52 | 53 | -zui_sys_decode_hops 54 | ~~~~~~~~~~~~~~~~~~~~ 55 | 56 | ____ 57 | 58 | Deserializes on-the-fly hops substitution. 59 | For internal usage. 60 | 61 | $1 - the hops' package 62 | $2 - optional target parameter name 63 | ____ 64 | 65 | Has 4 line(s). Doesn't call other functions. 66 | 67 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 68 | 69 | -zui_sys_decode_lanchors 70 | ~~~~~~~~~~~~~~~~~~~~~~~~ 71 | 72 | ____ 73 | 74 | Deserializes on-the-fly local anchors. 75 | For internal usage. 76 | 77 | $1 - the anchors' package 78 | $2 - optional target parameter name 79 | ____ 80 | 81 | Has 4 line(s). Doesn't call other functions. 82 | 83 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 84 | 85 | -zui_sys_decode_list_update 86 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 87 | 88 | ____ 89 | 90 | Deserializes on-the-fly update package. 91 | To be used rather only internally. 92 | 93 | $1 - the package 94 | $2 - optional target parameter name 95 | ____ 96 | 97 | Has 5 line(s). Doesn't call other functions. 98 | 99 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 100 | 101 | -zui_sys_decode_nonselectables 102 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 103 | 104 | ____ 105 | 106 | Deserializes on-the-fly nonselectables 107 | substitution. For internal usage. 108 | 109 | $1 - the nonselectables' package 110 | $2 - optional target parameter name 111 | ____ 112 | 113 | Has 4 line(s). Doesn't call other functions. 114 | 115 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 116 | 117 | -zui_sys_gather_lanchors 118 | ~~~~~~~~~~~~~~~~~~~~~~~~ 119 | 120 | ____ 121 | 122 | Appends mod${1}_ice${2}_lanchors into array given by name 123 | 124 | $1 - module index 125 | $2 - instance index 126 | $3 - target array name 127 | ____ 128 | 129 | Has 9 line(s). Doesn't call other functions. 130 | 131 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 132 | 133 | -zui_sys_get_match_line 134 | ~~~~~~~~~~~~~~~~~~~~~~~ 135 | 136 | ____ 137 | 138 | Returns at which index in $__list the search 139 | result that's currently highlighted is located. 140 | 141 | Allows to jump to search result, not only see it. 142 | 143 | $__slist - input variable with copy of $__list 144 | $1 - output variable to hold the established target line number 145 | ____ 146 | 147 | Has 39 line(s). Doesn't call other functions. 148 | 149 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 150 | 151 | -zui_sys_get_tfield_cursor_boundaries 152 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 153 | 154 | ____ 155 | 156 | Gets cursor minimum and maximum x position for 157 | given line and given (via id) text field. Stores 158 | them into two parameters given by name. Testable, 159 | returns if the cursor positions could have been 160 | established. 161 | 162 | Example call: 163 | -zui_sys_get_tfield_cursor_boundaries "cidx_start" "cidx_end" "${list[10]}" "${reply[@]}" 164 | 165 | $1 - output parameter name - cursor start index 166 | $2 - output parameter name - cursor end index 167 | $3 - current $list (zui-list working variable) element 168 | $4, ..., $11 - decoded __text field 169 | ____ 170 | 171 | Has 63 line(s). Calls functions: 172 | 173 | -zui_sys_get_tfield_cursor_boundaries 174 | |-- stdlib.lzui/-zui_std_decode_list_box 175 | |-- stdlib.lzui/-zui_std_decode_text_field 176 | |-- stdlib.lzui/-zui_std_get_stext 177 | |-- stdlib.lzui/-zui_std_is_hyperlink 178 | `-- utillib.lzui/-zui_util_strip_codes 179 | 180 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 181 | 182 | -zui_sys_index_tail_width 183 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 184 | 185 | ____ 186 | 187 | Returns index in given string that results 188 | in given width, when displaying the string 189 | starting from that index 190 | 191 | $1 - string 192 | $2 - expected width 193 | $3 - output parameter name for the index 194 | ____ 195 | 196 | Has 13 line(s). Doesn't call other functions. 197 | 198 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 199 | 200 | -zui_sys_map_cursor_on_string 201 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 202 | 203 | ____ 204 | 205 | Maps visual cursor position onto in-string cursor 206 | position. Supports characters with double and greater 207 | width 208 | 209 | $1 - string start display index 210 | $2 - string 211 | $3 - cursor position 212 | $4 - output parameter name 213 | ____ 214 | 215 | Has 15 line(s). Doesn't call other functions. 216 | 217 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 218 | 219 | -------------------------------------------------------------------------------- /docs/utillib.lzui.adoc: -------------------------------------------------------------------------------- 1 | utillib.lzui(1) 2 | =============== 3 | :compat-mode!: 4 | 5 | NAME 6 | ---- 7 | utillib.lzui - a shell script 8 | 9 | SYNOPSIS 10 | -------- 11 | Documentation automatically generated with `zsdoc' 12 | 13 | FUNCTIONS 14 | --------- 15 | 16 | -zui_util_circular_next 17 | -zui_util_circular_paths 18 | -zui_util_get_datetime 19 | -zui_util_get_segment 20 | -zui_util_get_time 21 | -zui_util_get_timestamp 22 | -zui_util_has_default_color 23 | -zui_util_map_bools 24 | -zui_util_resolve_path 25 | -zui_util_strip_codes 26 | -zui_util_to_cmd_line 27 | AUTOLOAD is-at-least 28 | 29 | DETAILS 30 | ------- 31 | 32 | Script Body 33 | ~~~~~~~~~~~ 34 | 35 | Has 3 line(s). No functions are called (may set up e.g. a hook, a Zle widget bound to a key, etc.). 36 | 37 | -zui_util_circular_next 38 | ~~~~~~~~~~~~~~~~~~~~~~~ 39 | 40 | ____ 41 | 42 | Returns next file to write to in circular buffer set 43 | of file names .1 .2 ... . 44 | 45 | The buffer is ordered according to modification time. 46 | 47 | $1 - base of file names in circular buffer 48 | $2 - maximum number of files in circular buffer 49 | 50 | ____ 51 | 52 | Has 16 line(s). Doesn't call other functions. 53 | 54 | Uses feature(s): _setopt_ 55 | 56 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 57 | 58 | -zui_util_circular_paths 59 | ~~~~~~~~~~~~~~~~~~~~~~~~ 60 | 61 | ____ 62 | 63 | 64 | Returns absolute file paths of given circular buffer. 65 | They are ordered from most recent to least recent. 66 | 67 | No count is obtained, so all files are returned, even 68 | actually disabled by buffer limit. 69 | 70 | $1 - name of the circular buffer 71 | 72 | ____ 73 | 74 | Has 7 line(s). Doesn't call other functions. 75 | 76 | Uses feature(s): _setopt_ 77 | 78 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 79 | 80 | -zui_util_get_datetime 81 | ~~~~~~~~~~~~~~~~~~~~~~ 82 | 83 | ____ 84 | 85 | Returns date and time. Uses datetime zsh module 86 | or date command as fallback. 87 | 88 | $REPLY - date and time string "Ymd_H.M.S" 89 | 90 | ____ 91 | 92 | Has 3 line(s). Doesn't call other functions. 93 | 94 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 95 | 96 | -zui_util_get_segment 97 | ~~~~~~~~~~~~~~~~~~~~~ 98 | 99 | ____ 100 | 101 | Return n-th (z) segment of given text 102 | $1 - text 103 | $2 - segment (default is 1) 104 | $3 - destination variable name (default is "REPLY") 105 | 106 | Can use e.g. 'reply[1]' for $3 107 | ____ 108 | 109 | Has 5 line(s). Doesn't call other functions. 110 | 111 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 112 | 113 | -zui_util_get_time 114 | ~~~~~~~~~~~~~~~~~~ 115 | 116 | ____ 117 | 118 | 119 | Returns time %H:%M, via datetime or `date` as fallback 120 | 121 | ____ 122 | 123 | Has 3 line(s). Doesn't call other functions. 124 | 125 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 126 | 127 | -zui_util_get_timestamp 128 | ~~~~~~~~~~~~~~~~~~~~~~~ 129 | 130 | ____ 131 | 132 | Returns timestamp, via datetime or `date` as fallback 133 | 134 | ____ 135 | 136 | Has 2 line(s). Doesn't call other functions. 137 | 138 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 139 | 140 | -zui_util_has_default_color 141 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 142 | 143 | ____ 144 | 145 | Returns true if the "default" color 146 | can be used with current Zsh/zcurses 147 | ____ 148 | 149 | Has 5 line(s). Calls functions: 150 | 151 | -zui_util_has_default_color 152 | `-- is-at-least 153 | 154 | Uses feature(s): _autoload_, _is-at-least_ 155 | 156 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 157 | 158 | -zui_util_map_bools 159 | ~~~~~~~~~~~~~~~~~~~ 160 | 161 | ____ 162 | 163 | Maps boolean values of expressions given in $1 164 | (string separated by ';') to parameters given 165 | via names in $2 (separated by ';'). For true, 166 | $3 is assigned to corresponding parameter, $4 167 | for false. 168 | 169 | If $1 contains [[ or ((, it is evaluated. If 170 | it is a positive number or zero, then it is 171 | treated as direct bool value. Otherwise it's 172 | treated as parameter name, and boolean value 173 | of the parameter (it has to be positive number 174 | or zero) is examined. 175 | 176 | -zui_util_map_bools "1;[[ a = b ]];ZUI[text_select]" \ 177 | "color1;color2;color3" $red $white 178 | 179 | ____ 180 | 181 | Has 19 line(s). Doesn't call other functions. 182 | 183 | Uses feature(s): _eval_ 184 | 185 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 186 | 187 | -zui_util_resolve_path 188 | ~~~~~~~~~~~~~~~~~~~~~~ 189 | 190 | ____ 191 | 192 | Resolves absolute path from current working directory and file path 193 | 194 | $1 - current working directory 195 | 196 | $2 - file path 197 | 198 | $reply[1] - dirname 199 | 200 | $reply[2] - basename 201 | 202 | ____ 203 | 204 | Has 17 line(s). Doesn't call other functions. 205 | 206 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 207 | 208 | -zui_util_strip_codes 209 | ~~~~~~~~~~~~~~~~~~~~~ 210 | 211 | ____ 212 | 213 | Strips formatting codes from text in 214 | $1, saves result into parameter REPLY 215 | 216 | $1 - text to strip codes from 217 | ____ 218 | 219 | Has 1 line(s). Doesn't call other functions. 220 | 221 | Called by: 222 | 223 | syslib.lzui/-zui_sys_get_tfield_cursor_boundaries 224 | 225 | -zui_util_to_cmd_line 226 | ~~~~~~~~~~~~~~~~~~~~~ 227 | 228 | ____ 229 | 230 | Puts given text on command line – regardless if Zle is active or not 231 | 232 | $1 - text to put on command line 233 | 234 | ____ 235 | 236 | Has 16 line(s). Doesn't call other functions. 237 | 238 | Uses feature(s): _zle_ 239 | 240 | Not called by script or any function (may be e.g. a hook, a Zle widget, etc.). 241 | 242 | is-at-least 243 | ~~~~~~~~~~~ 244 | 245 | ____ 246 | 247 | 248 | Test whether $ZSH_VERSION (or some value of your choice, if a second argument 249 | is provided) is greater than or equal to x.y.z-r (in argument one). In fact, 250 | it'll accept any dot/dash-separated string of numbers as its second argument 251 | and compare it to the dot/dash-separated first argument. Leading non-number 252 | parts of a segment (such as the "zefram" in 3.1.2-zefram4) are not considered 253 | when the comparison is done; only the numbers matter. Any left-out segments 254 | in the first argument that are present in the version string compared are 255 | considered as zeroes, eg 3 == 3.0 == 3.0.0 == 3.0.0.0 and so on. 256 | 257 | ____ 258 | 259 | Has 56 line(s). Doesn't call other functions. 260 | 261 | Called by: 262 | 263 | -zui_util_has_default_color 264 | 265 | -------------------------------------------------------------------------------- /functions/-zui-list-box-loop: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | # 8 | # Draws list-box drop-down list 9 | local __page_height="$1" __page_width="$2" __ypos="$3" __xpos="$4" __id="$5" __width_var="$6" __idx_var="$7" __opts_var="$8" __data2="$9" __data2="$10" __data3="$11" 10 | 11 | # Get maximum width 12 | local -a options 13 | options=( "${(@Ps:;:)__opts_var}" ) 14 | # Remove color marks 15 | # [all] [fg] [bg] TEXT 16 | options=( "${options[@]//(#b)([$'\03'-$'\07'$'\013'-$'\014'$'\016'-$'\031'])([$'\03'-$'\07'$'\013'-$'\014'$'\016'-$'\017']|)([$'\020'-$'\030']|)([^${ZUI[FMT_END]}]#)${ZUI[FMT_END]}/$match[4]}" ) 17 | 18 | # Compute window x-dimension and position 19 | local txt 20 | integer width=7 height=10 size="${#options}" 21 | for txt in "${options[@]}"; do 22 | (( width = ${(m)#txt} > width ? ${(m)#txt} : width )) 23 | done 24 | 25 | if (( __ypos + size + 2 > __page_height + 1 )); then 26 | if (( size + 2 > __page_height + 1 )); then 27 | height=__page_height-2 28 | (( height <= 0 )) && height=__page_height # paranoid 29 | __ypos=3 30 | else 31 | height=size 32 | (( __ypos = __page_height - size - 1 )) 33 | fi 34 | else 35 | (( __ypos+=1 )) 36 | height=size 37 | fi 38 | 39 | if (( __xpos + width + 4 > __page_width )); then 40 | # Basic protection, can be inaccurate 41 | (( __xpos=__page_width - width - 4 )) 42 | fi 43 | 44 | zcurses delwin lbox 2>/dev/null 45 | zcurses addwin lbox $(( height + 2 )) $(( width + 4 )) "$__ypos" "$__xpos" || return 1 46 | zcurses bg lbox "${ZUI[colorpair]}" 47 | 48 | # Clear input buffer 49 | zcurses timeout lbox 0 50 | zcurses input lbox key keypad 51 | zcurses timeout lbox -1 52 | key="" 53 | keypad="" 54 | 55 | integer hidx __start __end return_val=0 initial_idx=${(P)__idx_var} 56 | 57 | __start=initial_idx-height/2 58 | if (( __start <= 0 )); then 59 | __start=1 60 | __end=size 61 | if (( size > height )); then 62 | __end=height 63 | fi 64 | elif (( __start + height - 1 > size )); then 65 | __start=size-height+1 66 | __end=size 67 | else 68 | __end=initial_idx+(height-height/2)-1 69 | fi 70 | 71 | while (( 1 )); do 72 | # Draw list box 73 | zcurses clear lbox 74 | 75 | integer i count=1 76 | hidx=${(P)__idx_var} 77 | for (( i = __start; i <= __end; ++ i )); do 78 | txt="${options[i]}" 79 | zcurses move lbox "$count" 2 80 | 81 | if (( i == hidx )); then 82 | zcurses attr lbox +reverse 83 | zcurses string lbox "$txt" 84 | zcurses attr lbox -reverse 85 | else 86 | zcurses string lbox "$txt" 87 | fi 88 | 89 | (( ++ count )) 90 | done 91 | 92 | zcurses border lbox 93 | zcurses refresh lbox 94 | 95 | # Wait for input 96 | local key keypad final_key 97 | zcurses input "lbox" key keypad 98 | 99 | # Get the special (i.e. "keypad") key or regular key 100 | if [[ -n "$key" ]]; then 101 | final_key="$key" 102 | elif [[ -n "$keypad" ]]; then 103 | final_key="$keypad" 104 | fi 105 | 106 | case "$final_key" in 107 | (UP|k|BTAB) 108 | hidx=${(P)__idx_var} 109 | (( hidx = hidx > 1 ? hidx-1 : hidx )) 110 | if (( hidx < __start )); then 111 | __start=hidx 112 | __end=__start+height-1 113 | fi 114 | : ${(P)__idx_var::=$hidx} 115 | ;; 116 | (DOWN|j|$'\t') 117 | hidx=${(P)__idx_var} 118 | (( hidx = hidx < ${#options} ? hidx+1 : hidx )) 119 | if (( hidx > __end )); then 120 | __end=hidx 121 | __start=__end-height+1 122 | fi 123 | : ${(P)__idx_var::=$hidx} 124 | ;; 125 | ($'\E') 126 | : ${(P)__idx_var::=$initial_idx} 127 | return_val=1 128 | break 129 | ;; 130 | ($'\n') 131 | break 132 | ;; 133 | (??*) 134 | ;; 135 | (*) 136 | ;; 137 | esac 138 | done 139 | 140 | zcurses delwin lbox 2>/dev/null 141 | 142 | return $return_val 143 | 144 | # vim:ft=zsh 145 | -------------------------------------------------------------------------------- /functions/-zui-log: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | # 8 | # Prints log to given window 9 | # 10 | # $1 - window name 11 | # $2 - window height 12 | # $3 - window width 13 | # $4 - window colorpair 14 | # $5 - 0/1 if window has border 15 | # $6 - grep string 16 | # $7 - uniq mode 0/1 17 | # $8 - search mode 0/1 18 | # $9 - generation time 19 | # $10 - 0/1 if selectable 20 | # $11 - 0/1 if has search buffer 21 | # $12 - current index 22 | # $13 - last element 23 | 24 | # Outputs message in color, restores default color. Counts 25 | # characters in message to divide it into line-fitting parts. 26 | # Returns how much last line is filled, via "$5" parameter 27 | # name (P flag). 28 | # 29 | # $1 - color 30 | # $2 - text 31 | # $3 - line parameter name; line points at text 32 | # $4 - indentation 33 | # $5 - actual length of text in current line (output parameter name) 34 | -zuilog_counted_msg() { 35 | local color="$1" txt="$2" line_var="$3" indent="$4" actlen_var="$5" 36 | 37 | [[ -z "$txt" ]] && return 0 38 | 39 | [[ -n "$color" ]] && zcurses attr "$window" "$color/${win_colorpair#*/}" 40 | 41 | while (( ${(P)actlen_var} + ${#txt} > win_width - indent * 2 )); do 42 | integer part_count=$(( (win_width - indent * 2) - ${(P)actlen_var} )) 43 | zcurses string "$window" "${txt[1,part_count]}" 44 | : ${(P)actlen_var::=0} 45 | # Needed next line 0-based > maximum line 0-based -> abort 46 | [[ $(( ${(P)line_var} + 1 )) -gt $(( win_height - win_border - 1 )) ]] && { 47 | [[ -n "$color" ]] && zcurses attr "$window" "$win_colorpair" 48 | return 1 49 | } 50 | txt="${txt[part_count+1,-1]}" 51 | 52 | # Curses cursor: right after text. Line number: at text. 53 | if [[ -n "$txt" ]]; then 54 | : ${(P)line_var::=${(P)line_var}+1} 55 | zcurses move "$window" "${(P)line_var}" "$indent" 56 | fi 57 | done 58 | 59 | if [[ -n "$txt" ]]; then 60 | zcurses string "$window" "$txt" 61 | : ${(P)actlen_var::=${(P)actlen_var}+${#txt}} 62 | fi 63 | 64 | [[ -n "$color" ]] && zcurses attr "$window" "$win_colorpair" 65 | 66 | return 0 67 | } 68 | 69 | # Outputs a message in the bottom of the screen 70 | # "[UNIQ]", "Text", "[Grep string]", "Generation time", "$reply[@]" from callback 71 | -zuilog_output_message() { 72 | local direction="${1:-above}" line_var="$2" entry="$3" number="$4" txt 73 | integer indent=2 msg_len track_len=0 index=0 74 | 75 | local -a elems 76 | elems=( "${(@Q)${(z@)entry}}" ) 77 | msg_len="${elems[1]}" 78 | shift 3 elems 79 | 80 | (( msg_len == 0 )) && return 81 | 82 | msg_len+=${#elems[1]} 83 | 84 | (( number > 0 )) && msg_len+=3+${#number} 85 | 86 | # 87 | # Scroll 88 | # 89 | 90 | if [[ "$direction" = "below" ]]; then 91 | # (P)line_var is 0-based and it points after previous text 92 | integer __line="${(P)line_var}"+$(( (msg_len-1)/(win_width-2*indent) )) 93 | # Needed line 0-based, minus maximum line 0-based 94 | integer __scroll=__line-$(( win_height - win_border - 1 )) 95 | if (( __scroll > 0 )); then 96 | (( __scroll = __scroll > (${(P)line_var}-win_border) ? ${(P)line_var} - win_border : __scroll )) 97 | zcurses scroll "$window" +$__scroll 98 | : ${(P)line_var::=${(P)line_var}-$__scroll} 99 | fi 100 | elif [[ "$direction" = "above" ]]; then 101 | zcurses scroll "$window" -$(( 1 + (msg_len-1)/(win_width-2*indent) )) 102 | fi 103 | 104 | zcurses move "$window" "${(P)line_var}" $indent 105 | 106 | # 107 | # Print 108 | # 109 | 110 | 111 | (( number > 0 )) && -zuilog_counted_msg "${zui_log_colors[1]}" "[$number] " "$line_var" $indent track_len 112 | 113 | index=2 114 | for txt in "${elems[@]}"; do 115 | [[ -n "$txt" ]] && { -zuilog_counted_msg "${zui_log_colors[index]}" "$txt" "$line_var" $indent track_len || break; } 116 | (( ++ index )) 117 | done 118 | 119 | # Return number of lines of the message 120 | REPLY=$(( (msg_len-1)/(win_width-2*indent) + 1 )) 121 | } 122 | 123 | local window="$1" 124 | integer win_height="$2" 125 | integer win_width="$3" 126 | integer win_border="$4" 127 | local win_colorpair="$5" 128 | local grep_string="$6" 129 | integer uniq="$7" 130 | integer search="$8" 131 | local generation_time="$9" 132 | integer selectable="$10" 133 | integer search_buf="$11" 134 | integer idx="$12" 135 | integer last_element="$13" 136 | 137 | local _uniq="" _text="" _grep="" _gen="" 138 | (( uniq )) && _uniq="[-UNIQ-]" 139 | [[ -n "$grep_string" ]] && _grep="[$ZUILIST_GREP_STRING]" 140 | [[ -n "$generation_time" ]] && _gen="GENERATED IN ${generation_time}s" 141 | (( selectable + search_buf + uniq )) && _text="Current #$idx (of #$last_element entries)" || _text="---" 142 | 143 | integer last="${#ZUI_MESSAGES}" start=last-win_height+win_border line=win_border 144 | (( start = start <= 0 ? 1 : start )) 145 | 146 | integer message_num=$(( ZUI[message_count] - (last-start+1) + 1 )) 147 | (( message_num = message_num <= 0 ? 1 : message_num )) 148 | (( ZUI[log_index] == 0 )) && message_num=0 149 | 150 | zcurses clear "$window" 151 | 152 | local entry 153 | for entry in "${(@)ZUI_MESSAGES[start,last]}"; do 154 | -zuilog_output_message "${ZUI[log_append]}" "line" "$entry" "$message_num" 155 | (( message_num = message_num ? ++ message_num : 0 )) 156 | 157 | if [[ "${ZUI[log_append]}" = "below" ]]; then 158 | (( ++ line )) 159 | else 160 | (( line = win_border )) 161 | fi 162 | done 163 | 164 | 165 | if [[ "${ZUI[status_pointer]}" = "1" ]]; then 166 | if [[ "${ZUI[log_append]}" = "above" || $(( win_height - win_border - 1 )) -ge "$line" || "$REPLY" -ge $(( win_height - 2*win_border )) ]]; then 167 | zcurses scroll "$window" -1 168 | fi 169 | 170 | zcurses move "$window" "$win_border" 2 171 | zcurses clear "$window" eol 172 | 173 | local track_len=0 174 | line=0 175 | 176 | if [[ -n "$_gen" ]]; then 177 | -zuilog_counted_msg "yellow" "$_gen " line 2 track_len 178 | else 179 | for txt in $_uniq $_text $_grep; do 180 | -zuilog_counted_msg "" "$txt " line 2 track_len 181 | done 182 | fi 183 | fi 184 | # vim:ft=zsh 185 | -------------------------------------------------------------------------------- /functions/zui-event-loop: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | # 8 | # Started from zle or from command line 9 | # 10 | 11 | [[ "${ZUI[stdlib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/stdlib.lzui" 12 | [[ "${ZUI[syslib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/syslib.lzui" 13 | [[ "${ZUI[utillib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/utillib.lzui" 14 | 15 | emulate -LR zsh 16 | setopt extendedglob typesetsilent warncreateglobal 17 | 18 | [[ "${ZUI[PROMPT_SUBST]}" = "1" ]] && setopt promptsubst 19 | 20 | # Should provide the top anchors? 21 | -zui_std_load_config b:top_anchors 1 2 'ZUI[top_anchors]' 22 | 23 | ## 24 | ## Parameters 25 | ## 26 | 27 | typeset -g REPLY="" 28 | local MATCH; local -i MBEGIN MEND 29 | local -a match mbegin mend 30 | 31 | # Measure and limit generation time 32 | typeset -F SECONDS=0.0 33 | local -F zuiel_start_seconds 34 | 35 | # Replies from module generators 36 | # reply - content 37 | # reply2 - nonselectables (to be globalized) 38 | # reply3 - hops (to be globalized) 39 | # reply4 - anchors' IDs (to be globalized) 40 | local -a reply reply2 reply3 reply4 41 | local reply5 42 | 43 | # Gathers all (re-)generated local anchors, 44 | # those that are returned in reply4 and are 45 | # subject to index globalization (translation 46 | # of local in-module indices to normal, global 47 | # list indices) 48 | local -aU zuiel_regen_lanchors 49 | 50 | # Module and instance counters. Both used to 51 | # declare parameters of generated module's 52 | # instance (the parameters are distinct only 53 | # in counter embedded in them). In general 54 | # they're used to iterate modules' instances. 55 | integer zel_mod zel_ice 56 | 57 | # Used to access parameters via name 58 | local zuiel_var_name 59 | 60 | # Loop parameters 61 | local zuiel_key zuiel_val1 zuiel_val2 zuiel_val3 62 | 63 | # Set REGENERATE_ALWAYS to e.g. ,module3, 64 | # to always regenerate third module 65 | local REGENERATE="initial" REGENERATE_ARG="" REGENERATE_ALWAYS="" 66 | 67 | # Holds ordered modules and their factors 68 | local -A zuiel_module_hash 69 | 70 | # Working variable of construction loop 71 | local -a ZUIEL_OUTPUT zuiel_output zuiel_nonselectables zuiel_hops zuiel_lanchors 72 | local -a zuiel_prev_nonselectables zuiel_prev_hops 73 | local -a zuiel_top_anchors 74 | local zuiel_n zuiel_a 75 | integer ZUIEL_LINE_COUNT zuiel_size zuiel_prev_size zuiel_spacing zuiel_prev_spacing 76 | 77 | # Trick variable to make handlers internal actions 78 | local internal 79 | 80 | ### 81 | ### Parse input 82 | ### 83 | 84 | zel_mod=0 85 | for zuiel_val1; do 86 | (( ++ zel_mod )) 87 | zuiel_val2="${zuiel_val1%%:*}" 88 | zuiel_val3="${zuiel_val1#*:}" 89 | 90 | # No number, but some "prefix:" ? 91 | if [[ "$zuiel_val2" != <-> && "$zuiel_val2" != "$zuiel_val1" ]]; then 92 | # Add regeneration hook 93 | [[ "$zuiel_val2" = "regen" ]] && zuiel_module_hash[regen_${zuiel_val3}]=1 94 | [[ "$zuiel_val2" = "fly-regen" ]] && zuiel_module_hash[fly-regen_${zuiel_val3}]=1 95 | # Add configurations to refresh 96 | [[ "$zuiel_val2" = "cfg-refresh" ]] && ZUI[configs_to_refresh]="${zuiel_val3}" 97 | # Add array 98 | [[ "$zuiel_val2" = "a" ]] && zuiel_module_hash[${zel_mod}_zuia]="$zuiel_val3" 99 | continue 100 | elif [[ "$zuiel_val2" != <-> ]]; then 101 | # Without prefix, default factor is 1 102 | zuiel_val2="1" 103 | fi 104 | 105 | # Add module generator at position $module 106 | zuiel_module_hash[${zel_mod}_${zuiel_val3}]="$zuiel_val2" 107 | done 108 | 109 | ### ### 110 | ### REGENERATION REQUEST LOOP ### 111 | ### ### 112 | 113 | while (( 1 )); do 114 | 115 | # Measure generation time 116 | zuiel_start_seconds=$SECONDS 117 | 118 | # Clear the recreated-lanchors array 119 | zuiel_regen_lanchors=( ) 120 | 121 | # 122 | # Common regeneration block 123 | # 124 | 125 | # Call regeneration hook if given 126 | zuiel_key="${zuiel_module_hash[(i)regen_*]}" 127 | [[ -n "$zuiel_key" ]] && "${zuiel_key#regen_}" "$REGENERATE" "$REGENERATE_ARG" 128 | 129 | ## 130 | ## Generation sub-loop 131 | ## 132 | 133 | for (( zel_mod=1; zel_mod <= 1000; ++ zel_mod )); do 134 | # Get module key in storing hash 135 | zuiel_key="${zuiel_module_hash[(i)${zel_mod}_*]}" 136 | 137 | # Check such module exists 138 | [[ -z "$zuiel_key" ]] && break; 139 | 140 | if [[ "$zuiel_key" != [[:digit:]]##_zuia ]]; then 141 | zuiel_val1="${zuiel_module_hash[$zuiel_key]}" # factor 142 | zuiel_val2="${zuiel_key#[[:digit:]]#_}" # generator 143 | 144 | # Generate required number of instances 145 | for (( zel_ice=1; zel_ice <= zuiel_val1; ++ zel_ice )); do 146 | if [[ "$REGENERATE" = "initial" || "$REGENERATE" = *,mod${zel_mod}_ice${zel_ice},* || "$REGENERATE" = *,all,* ]]; then 147 | # Module $zel_mod, instance $zel_ice 148 | local -a mod${zel_mod}_ice${zel_ice}_output mod${zel_mod}_ice${zel_ice}_nonselectables mod${zel_mod}_ice${zel_ice}_hops 149 | local -a mod${zel_mod}_ice${zel_ice}_lanchors prev_mod${zel_mod}_ice${zel_ice}_nonselectables prev_mod${zel_mod}_ice${zel_ice}_hops 150 | integer mod${zel_mod}_ice${zel_ice}_size prev_mod${zel_mod}_ice${zel_ice}_size 151 | integer mod${zel_mod}_ice${zel_ice}_spacing prev_mod${zel_mod}_ice${zel_ice}_spacing 152 | 153 | -zui_std_reset_replies 154 | "$zuiel_val2" "$zel_mod" "$zel_ice" $REGENERATE_ARG 155 | 156 | # This function has to be called after each *_generator 157 | # function call, to copy reply{,2..5} into above (after 158 | # "# Module $zel_mod ..." line) variables 159 | -zui_std_map_replies "$zel_mod" "$zel_ice" 160 | 161 | # Save any newly created local anchors 162 | -zui_sys_gather_lanchors "$zel_mod" "$zel_ice" zuiel_regen_lanchors 163 | fi 164 | done 165 | fi 166 | done 167 | 168 | ## 169 | ## Construction sub-loop 170 | ## 171 | 172 | typeset -ga ZUILIST_NONSELECTABLE_ELEMENTS ZUILIST_HOP_INDICES 173 | ZUILIST_NONSELECTABLE_ELEMENTS=() 174 | ZUILIST_HOP_INDICES=() 175 | 176 | ZUIEL_OUTPUT=( ) 177 | ZUIEL_LINE_COUNT=0 178 | zuiel_top_anchors=( ) 179 | 180 | for (( zel_mod=1; zel_mod <= 1000; ++ zel_mod )); do 181 | zuiel_key="${zuiel_module_hash[(i)${zel_mod}_*]}" 182 | 183 | # Check such module exists 184 | [[ -z "$zuiel_key" ]] && break; 185 | 186 | # Array? 187 | if [[ "$zuiel_key" = [[:digit:]]##_zuia ]]; then 188 | # Simulate instance 1 189 | zel_ice=1 190 | zuiel_n="mod${zel_mod}_ice${zel_ice}" 191 | 192 | # Get spacing 193 | zuiel_spacing="${ZUI[SPACING_${zel_mod}_${zel_ice}]:-1}" 194 | 195 | # Add spacing to document 196 | for (( zuiel_val1 = 1; zuiel_val1 <= zuiel_spacing; ++ zuiel_val1 )); do 197 | ZUIEL_OUTPUT+=( "" ) 198 | ZUIEL_LINE_COUNT+=1 199 | # Possible + 1 for the anchors at top 200 | ZUILIST_NONSELECTABLE_ELEMENTS+=( "$(( ZUIEL_LINE_COUNT + ZUI[top_anchors] ))" ) 201 | done 202 | 203 | # Possible + 1 for the anchors at top 204 | ZUILIST_HOP_INDICES+=( "$(( ZUIEL_LINE_COUNT + 1 + ZUI[top_anchors] ))" ) 205 | 206 | zuiel_val1="${zuiel_module_hash[$zuiel_key]}" 207 | zuiel_val2="${zuiel_val1%%-*}" 208 | zuiel_val3="$ZUIEL_LINE_COUNT" 209 | if [[ "$zuiel_val1" != "$zuiel_val2" && "$zuiel_val2" = "u" ]]; then 210 | zuiel_val1="${zuiel_val1#u-}" 211 | # Append making unique 212 | ZUIEL_OUTPUT+=( "${(PA@u)zuiel_val1}" ) 213 | else 214 | # Append normally 215 | ZUIEL_OUTPUT+=( "${(PA@)zuiel_val1}" ) 216 | fi 217 | 218 | # Should probably resign from ZUIEL_LINE_COUNT 219 | ZUIEL_LINE_COUNT="${#ZUIEL_OUTPUT}" 220 | 221 | (( zuiel_val3 = ZUIEL_LINE_COUNT - zuiel_val3 )) # size 222 | (( zuiel_val3 > 0 )) && -zui_std_anchor "aglobal_m${zel_mod}_i${zel_ice}" $(( ZUIEL_LINE_COUNT - zuiel_val3 + 1 + ZUI[top_anchors] )) "$zel_mod" "$zel_ice" "" "[${ZUI[CYAN]}Data${zel_mod}${ZUI[FMT_END]}]" '(( ${+functions[-zui-standard-global-anchors-callback]} )) && { -zui-standard-global-anchors-callback "'"aglobal_m${zel_mod}_i${zel_ice}"'" "'"${zuiel_hops[1]}"'" "'"$zel_mod"'" "'"$zel_ice"'"; internal=1; }' zuiel_top_anchors 223 | 224 | # Store global index, size and spacing 225 | local ${zuiel_n}_global_index ${zuiel_n}_size prev_${zuiel_n}_size ${zuiel_n}_spacing prev_${zuiel_n}_spacing 226 | zuiel_var_name="${zuiel_n}_global_index" 227 | : ${(P)zuiel_var_name::=$(( ZUIEL_LINE_COUNT - zuiel_val3 + 1 + ZUI[top_anchors] ))} 228 | zuiel_var_name="${zuiel_n}_size" 229 | : ${(P)zuiel_var_name::=$zuiel_val3} 230 | zuiel_var_name="prev_${zuiel_n}_size" 231 | : ${(P)zuiel_var_name::=$zuiel_val3} 232 | zuiel_var_name="${zuiel_n}_spacing" 233 | : ${(P)zuiel_var_name::=$zuiel_spacing} 234 | zuiel_var_name="prev_${zuiel_n}_spacing" 235 | : ${(P)zuiel_var_name::=$zuiel_spacing} 236 | else 237 | zuiel_val1="${zuiel_module_hash[$zuiel_key]}" 238 | 239 | for (( zel_ice=1; zel_ice <= zuiel_val1; ++ zel_ice )); do 240 | zuiel_n="mod${zel_mod}_ice${zel_ice}" 241 | 242 | # Check if there are any modules 243 | zuiel_var_name="${zuiel_n}_output" 244 | [[ "${(P)+zuiel_var_name}" = "0" ]] && break 245 | 246 | # Get output 247 | zuiel_output=( "${(PA@)zuiel_var_name}" ) 248 | 249 | # Get output size 250 | zuiel_var_name="${zuiel_n}_size" 251 | zuiel_size="${(P)zuiel_var_name}" 252 | 253 | # Get module spacing 254 | zuiel_var_name="${zuiel_n}_spacing" 255 | zuiel_spacing="${(P)zuiel_var_name:-1}" 256 | 257 | # Get nonselectables 258 | zuiel_var_name="${zuiel_n}_nonselectables" 259 | zuiel_nonselectables=( "${(PA@)zuiel_var_name}" ) 260 | 261 | # Get hops 262 | zuiel_var_name="${zuiel_n}_hops" 263 | zuiel_hops=( "${(PA@)zuiel_var_name}" ) 264 | 265 | # Get local anchors 266 | zuiel_var_name="${zuiel_n}_lanchors" 267 | zuiel_lanchors=( "${(PA@)zuiel_var_name}" ) 268 | 269 | # Get previous size 270 | zuiel_var_name="prev_${zuiel_n}_size" 271 | zuiel_prev_size="${(P)zuiel_var_name}" 272 | 273 | # Get previous spacing 274 | zuiel_var_name="prev_${zuiel_n}_spacing" 275 | zuiel_prev_spacing="${(P)zuiel_var_name:-1}" 276 | 277 | # Get previous nonselectables 278 | zuiel_var_name="prev_${zuiel_n}_nonselectables" 279 | zuiel_prev_nonselectables=( "${(PA@)zuiel_var_name}" ) 280 | 281 | # Get previous hops 282 | zuiel_var_name="prev_${zuiel_n}_hops" 283 | zuiel_prev_hops=( "${(PA@)zuiel_var_name}" ) 284 | 285 | if (( zuiel_size > 0 )); then 286 | for (( zuiel_val2 = 1; zuiel_val2 <= zuiel_spacing; ++ zuiel_val2 )); do 287 | ZUIEL_OUTPUT+=( "" ) 288 | ZUIEL_LINE_COUNT+=1 289 | # Possible + 1 for the anchors at top 290 | ZUILIST_NONSELECTABLE_ELEMENTS+=( "$(( ZUIEL_LINE_COUNT + ZUI[top_anchors] ))" ) 291 | done 292 | fi 293 | 294 | # Translate returned indices to global ones 295 | # Possible + 1 for anchors at the top 296 | zuiel_nonselectables=( "${zuiel_nonselectables[@]//(#b)([[:digit:]]##)/$(( ${match[1]} + ZUIEL_LINE_COUNT + ZUI[top_anchors] ))}" ) 297 | zuiel_hops=( "${zuiel_hops[@]//(#b)([[:digit:]]##)/$(( ${match[1]} + ZUIEL_LINE_COUNT + ZUI[top_anchors] ))}" ) 298 | 299 | # Add translated, current zuiel_hops and zuiel_nonselectables 300 | ZUILIST_NONSELECTABLE_ELEMENTS+=( "${zuiel_nonselectables[@]}" ) 301 | ZUILIST_HOP_INDICES+=( "${zuiel_hops[@]}" ) 302 | 303 | # Also store the indices into prev_* arrays 304 | zuiel_var_name="prev_${zuiel_n}_nonselectables" 305 | : "${(PA)zuiel_var_name::=${zuiel_nonselectables[@]}}"; 306 | zuiel_var_name="prev_${zuiel_n}_hops" 307 | : "${(PA)zuiel_var_name::=${zuiel_hops[@]}}"; 308 | zuiel_var_name="prev_${zuiel_n}_size" 309 | : "${(P)zuiel_var_name::=${zuiel_size}}"; 310 | zuiel_var_name="prev_${zuiel_n}_spacing" 311 | : "${(P)zuiel_var_name::=${zuiel_spacing}}"; 312 | 313 | # Translate module's new local anchors, 314 | # into normal, global list indices 315 | for zuiel_a in ${zuiel_lanchors[@]}; do 316 | [[ "${+ZUI[zuianchor$zuiel_a]}" = "1" ]] && zuiel_a="zuianchor$zuiel_a" || { 317 | [[ "${+ZUI[zuieanchor$zuiel_a]}" = "1" ]] && zuiel_a="zuieanchor$zuiel_a" 318 | } 319 | # Possible + 1 for anchors at top; retain "+number" part 320 | ZUI[$zuiel_a]=$(( ${ZUI[$zuiel_a]%%[-+]*} + ZUIEL_LINE_COUNT + ZUI[top_anchors] ))"${ZUI[$zuiel_a]##[0-9]##}" 321 | done 322 | 323 | # Remove from list of anchors needing to-global 324 | # translation. This makes anchors distinct from 325 | # other data, other "...${zuiel_n}..." parameters, as 326 | # data is moved to ZUI hash once, and the array 327 | # "${zuiel_n}_lanchors" is instantly emptied. 328 | zuiel_var_name="${zuiel_n}_lanchors" 329 | : ${(PA)zuiel_var_name::=} 330 | 331 | # Create top anchors 332 | (( zuiel_size > 0 )) && -zui_std_anchor "aglobal_m${zel_mod}_i${zel_ice}" $(( ZUIEL_LINE_COUNT + 1 + ZUI[top_anchors] )) "$zel_mod" "$zel_ice" "" "[${ZUI[CYAN]}Module${zel_mod}/${zel_ice}${ZUI[FMT_END]}]" '(( ${+functions[-zui-standard-global-anchors-callback]} )) && { -zui-standard-global-anchors-callback "'"aglobal_m${zel_mod}_i${zel_ice}"'" "'"${zuiel_hops[1]}"'" "'"$zel_mod"'" "'"$zel_ice"'"; internal=1; }' zuiel_top_anchors 333 | 334 | # Store where in the list module is located, 335 | # via typical "modX_iceY..." variable 336 | local ${zuiel_n}_global_index 337 | zuiel_var_name="${zuiel_n}_global_index" 338 | # Possible + 1 for anchors at top, + 1 because 339 | # we point at next line after separator 340 | : ${(P)zuiel_var_name::=$(( ZUIEL_LINE_COUNT + 1 + ZUI[top_anchors] ))} 341 | 342 | # Shift down (up) local anchors that weren't 343 | # recreated in this regeneration loop run, 344 | # i.e. aren't in zuiel_regen_lanchors array. If an 345 | # anchor ID contains string "global" then it 346 | # will not be modified - will be considered 347 | # a fixed global anchor. 348 | if [[ $(( zuiel_size - zuiel_prev_size )) -ne 0 ]]; then 349 | for zuiel_a in ${ZUI[(I)zuianchor*~*global*]} ${ZUI[(I)zuieanchor*~*global*]}; do 350 | # Skip anchors recreated at this regeneration loop 351 | [[ "${zuiel_regen_lanchors[(r)${zuiel_a#zuianchor}]}" = ${zuiel_a#zuianchor} || 352 | "${zuiel_regen_lanchors[(r)${zuiel_a#zuieanchor}]}" = ${zuiel_a#zuieanchor} ]] && continue 353 | 354 | # Anchor after current module? The %%[-+]* removes 355 | # "+number" part used to point to other modules 356 | # while still using local anchors. This addition 357 | # doesn't change the fact that the anchor belongs 358 | # to its module, i.e. "zuiel_a" in "zuiel_a+b" matters. 359 | # 360 | # Possible + 1 for anchors at top, + 1 because we point at 361 | # module, at next line after empty separator line 362 | if [[ "${ZUI[$zuiel_a]%%[-+]*}" -gt $(( ZUIEL_LINE_COUNT + 1 + ZUI[top_anchors] + (zuiel_size - 1) )) ]]; then 363 | # Retain "+number" part 364 | ZUI[$zuiel_a]=$(( ${ZUI[$zuiel_a]%%[-+]*} + zuiel_size - zuiel_prev_size ))"${ZUI[$zuiel_a]##[0-9]##}" 365 | fi 366 | done 367 | fi 368 | 369 | ZUIEL_OUTPUT+=( "${zuiel_output[@]}" ) 370 | ZUIEL_LINE_COUNT+=zuiel_size 371 | done 372 | fi 373 | done 374 | 375 | # Reset regeneration variables and prev_current_project 376 | REGENERATE="" 377 | REGENERATE_ARG="" 378 | 379 | # 380 | # Generation time 381 | # 382 | 383 | local generation_time="$(( SECONDS - zuiel_start_seconds ))" 384 | ZUI[GENERATION_TIME]="${generation_time[1,4]}" 385 | 386 | # 387 | # Invoke the list, handle outcome 388 | # 389 | 390 | # We alter the settings on-the-fly, so 391 | # prevent zui-list from loading them 392 | -zui_std_refresh_configs "${(@s:,:)ZUI[configs_to_refresh]}" 393 | 394 | 395 | local ZUILIST_WRAPPER_BIT="" ZUILIST_WRAPPER_LINE="" zuiel_bit_set=0 zuiel_line_set=0 396 | 397 | if (( ZUI[top_anchors] )); then 398 | ZUILIST_HOP_INDICES+=( 1 ) # jump to anchors 399 | zui-usetty-wrapper zui-list-wrapper "${zuiel_top_anchors[*]}" "${ZUIEL_OUTPUT[@]}" 400 | else 401 | zui-usetty-wrapper zui-list-wrapper "${ZUIEL_OUTPUT[@]}" 402 | fi 403 | 404 | if [[ "${ZUI[text_mode]}" = (all|nohyp) ]] || -zui_std_has_any_hyperlinks "$ZUILIST_WRAPPER_LINE"; then 405 | ZUI[pure_text_selected]="$ZUILIST_WRAPPER_BIT" 406 | (( ${+functions[-zui-standard-text-select-callback]} )) && -zui-standard-text-select-callback "segment" "$ZUILIST_WRAPPER_BIT" 407 | # No selection -> quit 408 | if [[ -z "$ZUILIST_WRAPPER_BIT" || "${ZUI[select_mode]}" = "quit" ]] && ! -zui_std_is_any_hyperlink "$ZUILIST_WRAPPER_BIT"; then 409 | REPLY="$ZUILIST_WRAPPER_BIT" 410 | zle && { zle .redisplay; zle .reset-prompt; } 411 | return 0 412 | fi 413 | zuiel_bit_set=1 414 | else # No hyperlinks, and text_mode is off|hyp 415 | ZUI[line_selected]="$ZUILIST_WRAPPER_LINE" 416 | (( ${+functions[-zui-standard-text-select-callback]} )) && -zui-standard-text-select-callback "line" "$ZUILIST_WRAPPER_LINE" 417 | if [[ -z "$ZUILIST_WRAPPER_LINE" || "${ZUI[select_mode]}" = "quit" ]]; then 418 | REPLY="$ZUILIST_WRAPPER_LINE" 419 | zle && { zle -R; zle .reset-prompt; } 420 | return 0 421 | fi 422 | zuiel_line_set=1 423 | fi 424 | 425 | if (( zuiel_bit_set )); then 426 | # ID, data1, data2, data3, data4 427 | if -zui_std_decode_hyperlink "$ZUILIST_WRAPPER_BIT"; then 428 | local id="${reply[1]}" data1="${reply[2]}" data2="${reply[3]}" data3="${reply[4]}" data4="${reply[5]}" 429 | 430 | ZUI[pressed_now]="$id" 431 | 432 | integer call_hook=0 433 | if [[ "$id" = zuieanchor* ]]; then 434 | ZUI[CURRENT_IDX]=$(( ${ZUI[$id]-$data1} )) 435 | [[ "${ZUI[reset_current_segment]}" = "no" ]] || ZUI[CURRENT_SEGMENT]=1 436 | id="zuiaction${id#zuieanchor}" 437 | ZUI[SEARCH_BUFFER]="" 438 | call_hook=1 439 | elif [[ "$id" = zuiaction* ]]; then 440 | call_hook=1 441 | fi 442 | 443 | if [[ $call_hook -gt 0 && -n "${ZUI[$id]}" ]]; then 444 | reply[1]="${reply[1]#(zuiiaction|zuiaction|zuicheckbox|zuieanchor|zuianchor|zuitfield|zuilbox)}" 445 | # Call the handler with all hyper-link 446 | # data or eval code, not using the data 447 | if [[ "${ZUI[$id]}" = *(=|\(\(| )* ]]; then 448 | eval "() { ${ZUI[$id]%;}; } ${(q)reply[@]}" 449 | 450 | # Does in-line code set reply? 451 | if [[ "${ZUI[$id]}" = *reply=* ]]; then 452 | REGENERATE="${reply[1]}" 453 | REGENERATE_ARG="${reply[2]}" 454 | else 455 | REGENERATE="$data3" 456 | REGENERATE_ARG="$data4" 457 | fi 458 | else 459 | "${ZUI[$id]}" "${reply[@]}" 460 | 461 | # Handler can request regeneration 462 | REGENERATE="${reply[1]}" 463 | REGENERATE_ARG="${reply[2]}" 464 | fi 465 | 466 | else 467 | # Without handler regeneration can be requested 468 | # only by link itself, by data3 and data4 of the 469 | # link (data1 can be used by anchor - hold target 470 | # line number, while data2 optionally can hold 471 | # module number) 472 | REGENERATE="$data3" 473 | REGENERATE_ARG="$data4" 474 | fi 475 | 476 | REGENERATE+="$REGENERATE_ALWAYS" 477 | else 478 | # Non-hyperlink text-bit selection 479 | : 480 | fi 481 | else 482 | # Non-hyperlink whole-line selection 483 | : 484 | fi 485 | 486 | ### REGENERATION REQUEST LOOP ### 487 | done 488 | 489 | return 0 490 | -------------------------------------------------------------------------------- /functions/zui-list-draw: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | emulate -L zsh 8 | 9 | zmodload zsh/curses 10 | 11 | setopt typesetsilent extendedglob 12 | 13 | -zui_list_print_with_ansi() { 14 | local win="$1" text="$2" out col bcol attr chunk Xout 15 | integer text_offset="$3" max_text_len="$4" text_len=0 no_match=0 nochunk_text_len to_skip_from_chunk to_chop_off_from_chunk before_len 16 | 17 | # 1 - non-escaped text, 2 - first number in the escaped text, with ; 18 | # 3 - second number, 4 - text after whole escape text 19 | 20 | while [[ -n "$text" && "$no_match" -eq 0 ]]; do 21 | # Example: \e[0;31m 22 | if [[ "$text" = (#b)([^$'\e']#)$'\e'\[([0-9](#c0,2))(#B)(\;|)(#b)([0-9](#c0,2))[mK](*) ]]; then 23 | # Text for further processing 24 | text="$match[4]" 25 | # Text chunk to output now 26 | out="$match[1]" 27 | # Save color 28 | col="-1" 29 | (( ${match[2]:-0} >= 30 && ${match[2]:-0} <= 39 )) && col="$match[2]" 30 | (( ${match[3]:-0} >= 30 && ${match[3]:-0} <= 39 )) && col="$match[3]" 31 | # Save background color 32 | bcol="-1" 33 | (( ${match[2]:-0} >= 40 && ${match[2]:-0} <= 49 )) && bcol="$match[2]" 34 | (( ${match[3]:-0} >= 40 && ${match[3]:-0} <= 49 )) && bcol="$match[3]" 35 | # Save attribute 36 | attr="-1" 37 | (( ${match[2]:--1} >= 0 && ${match[2]:--1} <= 27 )) && attr="$match[2]" 38 | (( ${match[3]:--1} >= 0 && ${match[3]:--1} <= 27 )) && attr="$match[3]" 39 | else 40 | out="$text" 41 | no_match=1 42 | fi 43 | 44 | if [ -n "$out" ]; then 45 | ################ Expand tabs ################ 46 | chunk="$out" 47 | before_len="$text_len" 48 | Xout="" 49 | 50 | while [ -n "$chunk" ]; do 51 | [[ "$chunk" = (#b)([^$'\t']#)$'\t'(*) ]] && { 52 | (( all_text_len=((before_len+${#match[1]})/8+1)*8 )) 53 | 54 | Xout+="${(r:all_text_len-before_len:: :)match[1]}" 55 | 56 | before_len+=all_text_len-before_len 57 | chunk="$match[2]" 58 | } || { 59 | Xout+="$chunk" 60 | break 61 | } 62 | done 63 | ############################################# 64 | 65 | # Input text length without the current chunk 66 | nochunk_text_len=text_len 67 | # Input text length up to current chunk 68 | text_len+="$#Xout" 69 | 70 | # Should start displaying with this chunk? 71 | # I.e. stop skipping left part of the input text? 72 | if (( text_len > text_offset )); then 73 | to_skip_from_chunk=text_offset-nochunk_text_len 74 | 75 | # LEFT - is chunk off the left skip boundary? +1 for 1-based index in string 76 | (( to_skip_from_chunk > 0 )) && Xout="${Xout[to_skip_from_chunk+1,-1]}" 77 | 78 | # RIGHT - is text off the screen? 79 | if (( text_len-text_offset > max_text_len )); then 80 | to_chop_off_from_chunk=0+(text_len-text_offset)-max_text_len 81 | Xout="${Xout[1,-to_chop_off_from_chunk-1]}" 82 | fi 83 | 84 | [ -n "$Xout" ] && zcurses string "$win" "$Xout" 85 | fi 86 | fi 87 | 88 | if (( no_match == 0 )); then 89 | # Color code dominates - simplification 90 | # for performance 91 | if (( col >= 30 && col <= 39 )); then 92 | (( bcol >= 40 && bcol <= 49 )) && currentb="${__c[bcol-39]}" 93 | current="${__c[col-29]}" 94 | # col, bcol, attr are currently 95 | # not possible at the same time, 96 | # but this code supports that 97 | [[ "$attr" = "1" ]] && zcurses attr "$win" "$current/$currentb" +bold || zcurses attr "$win" "$current/$currentb" 98 | elif (( bcol >= 40 && bcol <= 49 )); then 99 | currentb="${__c[bcol-39]}" 100 | [[ "$attr" = "1" ]] && zcurses attr "$win" "$current/$currentb" +bold || zcurses attr "$win" "$current/$currentb" 101 | elif [[ "$attr" -eq 0 ]]; then 102 | zcurses attr "$win" "$wrk_bold" "${ZUI[colorpair]}" 103 | current="$foreground" 104 | currentb="$background" 105 | elif [[ "$attr" = "1" ]]; then 106 | zcurses attr "$win" +bold 107 | fi 108 | 109 | if [[ "$attr" != "-1" ]]; then 110 | # Bold, blink, reverse, underline 111 | [[ "$attr" = "21" ]] && zcurses attr "$win" -bold || { 112 | [[ "$attr" = "5" || "$attr" = "6" ]] && zcurses attr "$win" +blink || { 113 | [[ "$attr" = "25" ]] && zcurses attr "$win" -blink || { 114 | [[ "$attr" = "7" ]] && zcurses attr "$win" +reverse || { 115 | [[ "$attr" = "27" ]] && zcurses attr "$win" -reverse || { 116 | [[ "$attr" = "4" ]] && zcurses attr "$win" +underline || { 117 | [[ "$attr" = "24" ]] && zcurses attr "$win" -underline 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | fi 125 | fi 126 | done 127 | } 128 | 129 | integer highlight="$1" 130 | integer page_height="$2" 131 | integer page_width="$3" 132 | integer y_offset="$4" 133 | integer x_offset="$5" 134 | integer text_offset="$6" 135 | local win="$7" 136 | local active_text="$8" 137 | shift 8 138 | integer max_text_len=page_width-x_offset 139 | 140 | [[ "$active_text" = "underline" || "$active_text" = "reverse" || "$active_text" = "blink" || "$active_text" = "standout" ]] || active_text="" 141 | 142 | integer i=1 max_idx=page_height end_idx=${#} 143 | (( end_idx > max_idx )) && end_idx=max_idx 144 | 145 | [[ "${ZUI[bold]}" = "1" ]] && local wrk_bold="+bold" || local wrk_bold="-bold" 146 | zcurses attr "$win" "$wrk_bold" "${ZUI[colorpair]}" 147 | zcurses clear "$win" 148 | 149 | local foreground="${ZUI[colorpair]%/*}" background="${ZUI[colorpair]#*/}" 150 | local current="$foreground" currentb="$background" 151 | 152 | local text 153 | for text; do 154 | (( i > end_idx )) && break 155 | 156 | zcurses move "$win" "$y_offset" "$x_offset" 157 | 158 | if (( i == highlight )); then 159 | [[ -n "$active_text" ]] && zcurses attr "$win" +"$active_text" 160 | -zui_list_print_with_ansi "$win" "$text" "$text_offset" "$max_text_len" 161 | [[ -n "$active_text" ]] && zcurses attr "$win" -"$active_text" 162 | else 163 | -zui_list_print_with_ansi "$win" "$text" "$text_offset" "$max_text_len" 164 | fi 165 | zcurses clear "$win" eol 166 | 167 | y_offset+=1 168 | i+=1 169 | done 170 | 171 | (( end_idx < max_idx )) && { zcurses move "$win" "$y_offset" "$x_offset"; zcurses clear "$win" eol; } 172 | 173 | zcurses attr "$win" "${ZUI[colorpair]}" 174 | -------------------------------------------------------------------------------- /functions/zui-list-input: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | emulate -L zsh 8 | 9 | zmodload zsh/curses 10 | 11 | setopt typesetsilent extendedglob 12 | 13 | # Compute first to show index - exact page 14 | -zui_listin_compute_first_to_show_idx() { 15 | from_what_idx_list_is_shown=0+((current_idx-1)/page_height)*page_height+1 16 | } 17 | 18 | # Conditional, fully robust page-to-show computation 19 | -zui_listin_compute_exact_page_if_needed() { 20 | # Fallback: in case of any problems compute exact page 21 | if [[ "$from_what_idx_list_is_shown" -lt "1" || 22 | "$from_what_idx_list_is_shown" -gt "$last_element" ]] 23 | then 24 | from_what_idx_list_is_shown=0+((current_idx-1)/page_height)*page_height+1 25 | fi 26 | } 27 | 28 | # Compute first to show index - page is 29 | # scrolled to center to show given index 30 | -zui_listin_compute_first_to_show_idx_center() { 31 | from_what_idx_list_is_shown=current_idx-page_height/2 32 | -zui_listin_compute_exact_page_if_needed 33 | } 34 | 35 | # Calling this function means passing information 36 | # that we move up in the list (to lower index) 37 | # This means we can move page start as few as 3 38 | # elements up. 39 | -zui_listin_compute_first_to_show_idx_up_many() { 40 | if [[ "$current_idx" -lt "$from_what_idx_list_is_shown" || 41 | "$current_idx" -gt "$(( from_what_idx_list_is_shown + page_height -1 ))" ]] 42 | then 43 | from_what_idx_list_is_shown=from_what_idx_list_is_shown-3 44 | -zui_listin_compute_exact_page_if_needed 45 | fi 46 | } 47 | 48 | # Like above, but in opposite direction 49 | -zui_listin_compute_first_to_show_idx_down_many() { 50 | if [[ "$current_idx" -lt "$from_what_idx_list_is_shown" || 51 | "$current_idx" -gt "$(( from_what_idx_list_is_shown + page_height -1 ))" ]] 52 | then 53 | from_what_idx_list_is_shown=from_what_idx_list_is_shown+3 54 | -zui_listin_compute_exact_page_if_needed 55 | fi 56 | } 57 | 58 | # Searches for hyperlink in current line, 59 | # sets current_segment to first link found 60 | -zui_listin_search_for_hyperlink() { 61 | local -a segments 62 | segments=( "${(Pz@)list_var_current}" ) 63 | 64 | # Hide global $nseg 65 | local nseg="${#segments}" 66 | 67 | [[ "$current_segment" -gt "$nseg" ]] && current_segment="$nseg" 68 | [[ "$current_segment" -le 0 ]] && current_segment="1" 69 | 70 | integer left=current_segment right=current_segment 71 | 72 | # Left 73 | while ! -zui_std_is_hyperlink "${segments[left]}" && ! -zui_std_is_text_field "${segments[left]}" && ! -zui_std_is_list_box "${segments[left]}"; do 74 | (( -- left )) 75 | [[ "$left" -le 0 ]] && break 76 | done 77 | 78 | # Right 79 | while ! -zui_std_is_hyperlink "${segments[right]}" && ! -zui_std_is_text_field "${segments[right]}" && ! -zui_std_is_list_box "${segments[right]}"; do 80 | (( ++ right )) 81 | [[ "$right" -gt "$nseg" ]] && break 82 | done 83 | 84 | # Choose smaller distance 85 | if (( left >= 1 && right <= nseg && (current_segment - left) <= (right - current_segment) )); then 86 | current_segment=left 87 | elif (( right <= nseg )); then 88 | current_segment=right 89 | elif (( left >= 1 )); then 90 | current_segment=left 91 | fi 92 | } 93 | 94 | # Searches for link that is closest graphically to previous 95 | # current segment 96 | -zui_listin_search_for_hyperlink_graphical() { 97 | [[ "$current_idx" = "$prev_idx" ]] && { -zui_listin_search_for_hyperlink; return $?; } 98 | 99 | local -a segments prev_segments stri_segs stri_prev_segs 100 | local line prev_line s ss 101 | 102 | # Position and size of current segment in previous line 103 | integer target_offset target_size 104 | # Position of examined segment, if proper, then stored 105 | # as *_left or *_right 106 | integer try try_left=0 try_left_size=0 try_right=0 107 | # Index of examined segment, if proper, then stored 108 | # as *_left or *_right 109 | integer candidate_idx=0 candidate_left=0 candidate_right=0 110 | 111 | segments=( "${(Pz@)list_var_current}" ) 112 | prev_segments=( "${(Pz@)list_var_prev}" ) 113 | 114 | -zui_std_strip_meta_data "${(P)list_var_current}" 115 | line="$REPLY" 116 | -zui_std_strip_meta_data "${(P)list_var_prev}" 117 | prev_line="$REPLY" 118 | 119 | -zui_std_strip_meta_data "${prev_segments[current_segment]}" 120 | -zui_std_strip_color_codes "$REPLY" 121 | target_size="${#REPLY}" 122 | REPLY="${REPLY//\[/\\[}" 123 | REPLY="${REPLY//\]/\\]}" 124 | target_offset="${prev_line[(i)$REPLY]}" 125 | 126 | for s in "${segments[@]}"; do 127 | candidate_idx+=1 128 | 129 | if ! -zui_std_is_any_hyperlink "$s"; then 130 | continue 131 | fi 132 | 133 | -zui_std_strip_meta_data "$s" 134 | -zui_std_strip_color_codes "$REPLY" 135 | ss="$REPLY" 136 | REPLY="${REPLY//\[/\\[}" 137 | REPLY="${REPLY//\]/\\]}" 138 | try="${line[(i)$REPLY]}" 139 | 140 | if (( try <= target_offset )); then 141 | candidate_left=candidate_idx 142 | try_left=try 143 | try_left_size=${#ss} 144 | elif (( try > target_offset )); then 145 | candidate_right=candidate_idx 146 | try_right=try 147 | break 148 | fi 149 | done 150 | 151 | # Left (and possibly right) in middle of the examined button 152 | if (( try_left != 0 && try_left + try_left_size >= target_offset )); then 153 | if (( try_right != 0 && try_right < target_offset + target_size )); then 154 | # Which does more pertrude 155 | if (( target_offset + target_size - try_right > try_left + try_left_size - target_offset )); then 156 | current_segment=candidate_right 157 | else 158 | current_segment=candidate_left 159 | fi 160 | else 161 | current_segment=candidate_left 162 | fi 163 | # Right in middle of examined button 164 | elif (( try_right != 0 && try_right < target_offset + target_size )); then 165 | current_segment=candidate_right 166 | # No in middle of the button, so distance to target button 167 | # from right edge (left candidate) and left edge (right candidate) 168 | elif (( try_left != 0 && try_right != 0 && target_offset - try_left - try_left_size < try_right - target_offset )); then 169 | current_segment=candidate_left 170 | # As above, opposite result 171 | elif (( try_left != 0 && try_right != 0 && target_offset - try_left - try_left_size >= try_right - target_offset )); then 172 | current_segment=candidate_right 173 | # Single candidate - left 174 | elif (( try_left != 0 )); then 175 | current_segment=candidate_left 176 | # Single candidate - right 177 | elif (( try_right != 0 )); then 178 | current_segment=candidate_right 179 | else 180 | # Fallback method 181 | -zui_listin_search_for_hyperlink; return $? 182 | fi 183 | 184 | return 0 185 | } 186 | 187 | # Testable, searches for next (excluding current 188 | # one) hyperlink, returns 1 if not found, 0 if 189 | # found (this implies update of current_segment) 190 | # 191 | # $1 - direction: 0 - left, other - right 192 | -zui_listin_set_next_hyperlink() { 193 | local -a segments 194 | segments=( "${(Pz@)list_var_current}" ) 195 | 196 | # Hide global $nseg 197 | local nseg="${#segments}" 198 | 199 | [[ "$current_segment" -gt "$nseg" ]] && current_segment="$nseg" 200 | [[ "$current_segment" -le 0 ]] && current_segment="1" 201 | 202 | integer left=current_segment right=current_segment 203 | 204 | # Left 205 | [[ "$1" = "0" ]] && { 206 | while ! -zui_std_is_hyperlink "${segments[left]}" && ! -zui_std_is_text_field "${segments[left]}" && ! -zui_std_is_list_box "${segments[left]}" || (( left == current_segment )); do 207 | (( -- left )) 208 | [[ "$left" -le 0 ]] && break 209 | done 210 | 211 | if (( left >= 1 )); then 212 | current_segment=left 213 | return 0 214 | fi 215 | } || { 216 | # Right 217 | while ! -zui_std_is_hyperlink "${segments[right]}" && ! -zui_std_is_text_field "${segments[right]}" && ! -zui_std_is_list_box "${segments[right]}" || (( right == current_segment )); do 218 | (( ++ right )) 219 | [[ "$right" -gt "$nseg" ]] && break 220 | done 221 | 222 | if (( right <= nseg )); then 223 | current_segment=right 224 | return 0 225 | fi 226 | } 227 | 228 | return 1 229 | } 230 | 231 | typeset -ga reply 232 | reply=( -1 '' ) 233 | integer current_idx="$1" 234 | integer current_segment="$2" 235 | integer from_what_idx_list_is_shown="$3" 236 | integer page_height="$4" 237 | integer page_width="$5" 238 | integer last_element="$6" 239 | integer nseg="$7" 240 | integer hscroll="$8" 241 | local key="$9" 242 | integer search="$10" 243 | local buffer="$11" 244 | integer uniq_mode="$12" 245 | integer disable_search="$13" 246 | local list_var_current="${14}[\$current_idx]" 247 | local list_var_prev="${14}[\$prev_idx]" 248 | 249 | integer prev_idx="$current_idx" 250 | local text 251 | local -a wrk_reply 252 | 253 | # Trigger nearest hyperlink searching even 254 | # when ZUI[text_mode] yields no search 255 | integer search_for_hyperlink=0 256 | 257 | # 258 | # Listening for input 259 | # 260 | 261 | if [[ -n "${ZUI[current_tfield]}" ]]; then 262 | -zui_util_get_segment "${(P)list_var_current}" "$current_segment" 263 | if -zui_std_decode_text_field "$REPLY" wrk_reply; then 264 | case "$key" in 265 | ($'\n') 266 | ZUI[current_tfield]="" 267 | reply=( $current_idx TFIELD_LEFT ) 268 | ;; 269 | ($'\E') 270 | : ${(P)wrk_reply[3]::=${ZUI[current_tfield_initial_sidx]}} 271 | : ${(P)wrk_reply[4]::=${ZUI[current_tfield_initial_data]}} 272 | ZUI[current_tfield]="" 273 | reply=( $current_idx TFIELD_CANCELLED ) 274 | ;; 275 | (DOWN|NPAGE) 276 | text="${(P)wrk_reply[2]}" 277 | (( text = text > 5 ? text - 1 : text )) 278 | : ${(P)wrk_reply[2]::=$text} 279 | ;; 280 | (UP|PPAGE) 281 | text="${(P)wrk_reply[2]}" 282 | (( text ++ )) 283 | : ${(P)wrk_reply[2]::=$text} 284 | ;; 285 | ($'\C-K'|$'\C-U') 286 | : ${(P)wrk_reply[4]::=} 287 | : ${(P)wrk_reply[3]::=1} 288 | ZUI[cursor_x]=$(( ZUI[current_tfield_cidx_start] )) 289 | ;; 290 | (HOME|$'\C-A') 291 | ZUI[cursor_x]="${ZUI[current_tfield_cidx_start]}" 292 | : ${(P)wrk_reply[3]::=1} 293 | ;; 294 | (END|$'\C-E') 295 | ZUI[cursor_x]="${ZUI[current_tfield_cidx_end]}" 296 | text="${(P)wrk_reply[4]}" 297 | -zui_sys_index_tail_width "$text" "${(P)wrk_reply[2]}" text 298 | : ${(P)wrk_reply[3]::=$text} 299 | ;; 300 | (LEFT) 301 | text="${(P)wrk_reply[4]}" 302 | if (( ZUI[cursor_x] - ${(m)#text[${ZUI[current_tfield_instr_idx]}]} >= ZUI[current_tfield_cidx_start] )); then 303 | (( ZUI[cursor_x] -= ${(m)#text[${ZUI[current_tfield_instr_idx]}]} )) 304 | else 305 | text="${(P)wrk_reply[3]}" 306 | (( text = text > 1 ? text - 1 : text )) 307 | : ${(P)wrk_reply[3]::=$text} 308 | fi 309 | ;; 310 | (RIGHT) 311 | text="${(P)wrk_reply[4]}" 312 | if [[ "${(m)#text[${ZUI[current_tfield_instr_idx]}+1]}" -ne 0 ]] && (( ZUI[cursor_x] + ${(m)#text[${ZUI[current_tfield_instr_idx]}+1]} <= ZUI[current_tfield_cidx_end] )); then 313 | (( ZUI[cursor_x] += ${(m)#text[${ZUI[current_tfield_instr_idx]}+1]} )) 314 | else 315 | # Dont advance cursor when we're at right boundary, advance 316 | # index so that whole string is shown. Also, allow further 317 | # advancement of the index, to scroll string left. 318 | # string index + width - 1 < #string + width/2 319 | if (( ${(P)wrk_reply[3]} + ${(P)wrk_reply[2]} - 1 < ${(P)#wrk_reply[4]} + ${(P)wrk_reply[2]} / 2 )); then 320 | text="${(P)wrk_reply[3]}" 321 | (( ++ text )) 322 | : ${(P)wrk_reply[3]::=$text} 323 | fi 324 | fi 325 | ;; 326 | ($'\b'|$'\C-?'|BACKSPACE) 327 | if (( ZUI[cursor_x] > ZUI[current_tfield_cidx_start] )); then 328 | text="${(P)wrk_reply[4]}" 329 | (( ZUI[cursor_x] -= ${(m)#text[${ZUI[current_tfield_instr_idx]}]} )) 330 | text[${ZUI[current_tfield_instr_idx]}]="" 331 | : ${(P)wrk_reply[4]::=$text} 332 | fi 333 | ;; 334 | (DC) 335 | if (( ZUI[cursor_x] < ZUI[current_tfield_cidx_end] )); then 336 | text="${(P)wrk_reply[4]}" 337 | text[${ZUI[current_tfield_instr_idx]}+1]="" 338 | : ${(P)wrk_reply[4]::=$text} 339 | fi 340 | ;; 341 | (??*|$'\t') 342 | ;; 343 | (*) 344 | text="${(P)wrk_reply[4]}" 345 | if (( ZUI[current_tfield_instr_idx] )); then 346 | text[${ZUI[current_tfield_instr_idx]}]="${text[${ZUI[current_tfield_instr_idx]}]}$key" 347 | : ${(P)wrk_reply[4]::=$text} 348 | 349 | # Scroll text right if string protrudes from 350 | # right boundary of text field 351 | if (( ${(m)#text[${(P)wrk_reply[3]},-1]} > ${(P)wrk_reply[2]} )); then 352 | text="${(P)wrk_reply[3]}" 353 | (( text += ${(m)#key} )) 354 | : ${(P)wrk_reply[3]::=$text} 355 | else 356 | (( ZUI[cursor_x] += ${(m)#key} )) 357 | fi 358 | else 359 | # Prepend 360 | text="$key$text" 361 | : ${(P)wrk_reply[4]::=$text} 362 | (( ZUI[cursor_x] += ${(m)#key} )) 363 | fi 364 | ;; 365 | esac 366 | 367 | -zui_sys_get_tfield_cursor_boundaries 'ZUI[current_tfield_cidx_start]' 'ZUI[current_tfield_cidx_end]' "${(P)list_var_current}" "${wrk_reply[1]}" 368 | (( ZUI[cursor_x] = ZUI[cursor_x] > ZUI[current_tfield_cidx_end] ? ZUI[current_tfield_cidx_end] : ZUI[cursor_x] )) 369 | -zui_sys_map_cursor_on_string "${(P)wrk_reply[3]}" "${(P)wrk_reply[4]}" $(( ZUI[cursor_x] - ZUI[current_tfield_cidx_start] - 1 )) 'ZUI[current_tfield_instr_idx]' 370 | else 371 | ZUI[current_tfield]="" 372 | fi 373 | elif [[ "$search" = "0" ]]; then 374 | 375 | case "$key" in 376 | (UP|k) 377 | # Are there any elements before the current one? 378 | [[ "$current_idx" -gt 1 ]] && current_idx=current_idx-1 379 | while [[ -z "${(P)list_var_current}" && "$current_idx" -gt 1 ]]; do 380 | current_idx=current_idx-1 381 | done 382 | -zui_listin_compute_first_to_show_idx_up_many 383 | ;; 384 | (DOWN|j) 385 | # Are there any elements after the current one? 386 | [[ "$current_idx" -lt "$last_element" ]] && current_idx=current_idx+1 387 | while [[ -z "${(P)list_var_current}" && "$current_idx" -lt "$last_element" ]]; do 388 | current_idx=current_idx+1 389 | done 390 | -zui_listin_compute_first_to_show_idx_down_many 391 | ;; 392 | ($'\C-P') 393 | [[ "$current_idx" -gt 1 ]] && current_idx=current_idx-1 394 | while [[ -z "${(P)list_var_current}" && "$current_idx" -gt 1 ]]; do 395 | current_idx=current_idx-1 396 | done 397 | -zui_listin_compute_first_to_show_idx_center 398 | ;; 399 | ($'\C-N') 400 | # Are there any elements after the current one? 401 | [[ "$current_idx" -lt "$last_element" ]] && current_idx=current_idx+1 402 | while [[ -z "${(P)list_var_current}" && "$current_idx" -lt "$last_element" ]]; do 403 | current_idx=current_idx+1 404 | done 405 | -zui_listin_compute_first_to_show_idx_center 406 | ;; 407 | (PPAGE|$'\b'|$'\C-?'|BACKSPACE) 408 | current_idx=current_idx-page_height 409 | [[ "$current_idx" -lt 1 ]] && current_idx=1; 410 | -zui_listin_compute_first_to_show_idx 411 | ;; 412 | (NPAGE|" ") 413 | current_idx=current_idx+page_height 414 | [[ "$current_idx" -gt "$last_element" ]] && current_idx=last_element; 415 | -zui_listin_compute_first_to_show_idx 416 | ;; 417 | ($'\C-U') 418 | current_idx=current_idx-page_height/2 419 | [[ "$current_idx" -lt 1 ]] && current_idx=1; 420 | -zui_listin_compute_first_to_show_idx 421 | ;; 422 | ($'\C-D') 423 | current_idx=current_idx+page_height/2 424 | [[ "$current_idx" -gt "$last_element" ]] && current_idx=last_element; 425 | -zui_listin_compute_first_to_show_idx 426 | ;; 427 | (HOME|g) 428 | current_idx=1 429 | -zui_listin_compute_first_to_show_idx 430 | ;; 431 | (END|G) 432 | current_idx=last_element 433 | -zui_listin_compute_first_to_show_idx 434 | ;; 435 | ($'\n'|ENTER) 436 | # Is that element selectable? 437 | # Check for this only when there is no search 438 | if [[ -n "$buffer" || "$uniq" -eq 1 || -z ${ZUILIST_NONSELECTABLE_ELEMENTS[(r)$current_idx]} ]] 439 | then 440 | # Check if we are at hyperlink. Only then allow selection 441 | -zui_util_get_segment "${(P)list_var_current}" "$current_segment" 442 | 443 | local -a data 444 | reply=( ) 445 | if -zui_std_decode_hyperlink "$REPLY"; then 446 | if [[ "${reply[1]}" = zuianchor* || "${reply[1]}" = zuiiaction* ]]; then 447 | # This is to run internal action, 448 | # i.e. action without leaving list 449 | reply=( $current_idx HYPERLINK ) 450 | else 451 | # This hyperlink path is through 452 | # quitting list and restarting it 453 | reply=( $current_idx SELECT ) 454 | fi 455 | elif -zui_std_decode_text_field "$REPLY"; then 456 | ZUI[current_tfield]="${reply[1]}" 457 | ZUI[current_tfield_initial_sidx]="${(P)reply[3]}" 458 | ZUI[current_tfield_initial_data]="${(P)reply[4]}" 459 | -zui_sys_get_tfield_cursor_boundaries 'ZUI[current_tfield_cidx_start]' 'ZUI[current_tfield_cidx_end]' "${(P)list_var_current}" "${reply[1]}" 460 | ZUI[cursor_y]=$(( current_idx - from_what_idx_list_is_shown + 1 )) 461 | ZUI[cursor_x]=$(( ZUI[current_tfield_cidx_end] )) 462 | -zui_sys_map_cursor_on_string "${(P)reply[3]}" "${(P)reply[4]}" $(( ZUI[cursor_x] - ZUI[current_tfield_cidx_start] - 1 )) 'ZUI[current_tfield_instr_idx]' 463 | reply=( $current_idx HYPERLINK ) 464 | elif -zui_std_decode_list_box "$REPLY"; then 465 | local min_x max_x y_pos 466 | -zui_sys_get_tfield_cursor_boundaries min_x max_x "${(P)list_var_current}" "${reply[1]}" 467 | y_pos=$(( current_idx - from_what_idx_list_is_shown + 1 )) 468 | -zui-list-box-loop "$page_height" "$page_width" "$y_pos" $(( min_x + 6 )) "${reply[@]}" && { reply=( $current_idx LBOX_LEFT ); } || { reply=( -1 '' ); } 469 | else 470 | # Selecting non-hyperlink is allowed? 471 | (( ${ZUI[text_select]} )) && reply=( $current_idx SELECT ) || reply=( -1 '' ) 472 | fi 473 | fi 474 | ;; 475 | (q|Q) 476 | reply=( -1 QUIT ) 477 | ;; 478 | (/) 479 | if [[ "$disable_search" = "0" ]]; then 480 | search=1 481 | -zui_list_cursor_visibility "status" 1 482 | fi 483 | ;; 484 | ($'\C-L') 485 | reply=( -1 REDRAW ) 486 | ;; 487 | (\]) 488 | if [[ "${ZUILIST_ENABLED_EVENTS[(r)BR_MOVE_RIGHT]}" = "BR_MOVE_RIGHT" ]]; then 489 | reply=( "$current_segment" "BR_MOVE_RIGHT" ) 490 | else 491 | 492 | [[ "${(t)ZUILIST_HOP_INDICES}" = "array" || "${(t)ZUILIST_HOP_INDICES}" = "array-local" ]] && [[ -z "$buffer" && "$uniq" -eq 0 ]] && 493 | for idx in "${(n)ZUILIST_HOP_INDICES[@]}"; do 494 | if [[ "$idx" -gt "$current_idx" ]]; then 495 | current_idx=$idx 496 | -zui_listin_compute_first_to_show_idx_center 497 | break 498 | fi 499 | done 500 | 501 | fi 502 | ;; 503 | (\[) 504 | if [[ "${ZUILIST_ENABLED_EVENTS[(r)BR_MOVE_LEFT]}" = "BR_MOVE_LEFT" ]]; then 505 | reply=( "$current_segment" "BR_MOVE_LEFT" ) 506 | else 507 | 508 | [[ "${(t)ZUILIST_HOP_INDICES}" = "array" || "${(t)ZUILIST_HOP_INDICES}" = "array-local" ]] && [[ -z "$buffer" && "$uniq" -eq 0 ]] && 509 | for idx in "${(nO)ZUILIST_HOP_INDICES[@]}"; do 510 | if [[ "$idx" -lt "$current_idx" ]]; then 511 | current_idx=$idx 512 | -zui_listin_compute_first_to_show_idx_center 513 | break 514 | fi 515 | done 516 | 517 | fi 518 | ;; 519 | ('<'|'{') 520 | hscroll=hscroll-7 521 | [[ "$hscroll" -lt 0 ]] && hscroll=0 522 | ;; 523 | ('>'|'}') 524 | hscroll+=7 525 | ;; 526 | (LEFT|'h'|BTAB) 527 | if [[ "$key" = "BTAB" && ${ZUILIST_NONSELECTABLE_ELEMENTS[(r)$current_idx]} = $current_idx ]]; then 528 | [[ "$current_idx" -gt "1" ]] && { 529 | current_idx=current_idx-1 530 | current_segment="100" 531 | } 532 | while [[ -z "${(P)list_var_current}" && "$current_idx" -gt 1 ]]; do 533 | current_idx=current_idx-1 534 | done 535 | -zui_listin_compute_first_to_show_idx_up_many 536 | else 537 | # Establish exact right-edge segment 538 | if [[ "$current_segment" -gt "$nseg" ]]; then 539 | (( current_segment = nseg )) 540 | [[ "$current_segment" -le 0 ]] && current_segment=1 541 | fi 542 | 543 | if [[ "$key" = "BTAB" ]]; then 544 | if ! -zui_listin_set_next_hyperlink 0; then 545 | # No link left in current line - move 546 | # one line up, set segment to far-most, 547 | # verification at bottom will find the 548 | # nearest hyperlink 549 | [[ "$current_idx" -gt "1" ]] && { 550 | current_idx=current_idx-1 551 | current_segment="100" 552 | search_for_hyperlink=1 553 | } 554 | while [[ -z "${(P)list_var_current}" && "$current_idx" -gt 1 ]]; do 555 | current_idx=current_idx-1 556 | done 557 | -zui_listin_compute_first_to_show_idx_up_many 558 | fi 559 | else 560 | if [[ "${ZUI[text_mode]}" = "off" ]]; then 561 | # Search left accepting lack of success 562 | -zui_listin_set_next_hyperlink 0 563 | elif -zui_std_has_any_hyperlinks "${(P)list_var_current}"; then 564 | if [[ "${ZUI[text_mode]}" = (hyp|all) ]]; then 565 | (( -- current_segment )) 566 | else 567 | -zui_listin_set_next_hyperlink 0 568 | fi 569 | else 570 | if [[ "${ZUI[text_mode]}" = (nohyp|all) ]]; then 571 | (( -- current_segment )) 572 | else 573 | -zui_listin_set_next_hyperlink 0 574 | fi 575 | fi 576 | fi 577 | fi 578 | ;; 579 | (RIGHT|'l'|$'\t') 580 | if [[ "$key" = $'\t' && ${ZUILIST_NONSELECTABLE_ELEMENTS[(r)$current_idx]} = $current_idx ]]; then 581 | [[ "$current_idx" -lt "$last_element" ]] && { 582 | current_idx=current_idx+1 583 | current_segment=1 584 | } 585 | while [[ -z "${(P)list_var_current}" && "$current_idx" -lt "$last_element" ]]; do 586 | current_idx=current_idx+1 587 | done 588 | -zui_listin_compute_first_to_show_idx_down_many 589 | else 590 | if [[ "$key" = $'\t' ]]; then 591 | # Search hyperlinks right 592 | if ! -zui_listin_set_next_hyperlink 1; then 593 | # Move to next line, set current_segment 594 | # to 1, verification at bottom will find 595 | # nearest hyperlink if it exists in the 596 | # next line 597 | [[ "$current_idx" -lt "$last_element" ]] && { 598 | current_idx=current_idx+1 599 | current_segment="1" 600 | search_for_hyperlink=1 601 | } 602 | while [[ -z "${(P)list_var_current}" && "$current_idx" -lt "$last_element" ]]; do 603 | current_idx=current_idx+1 604 | done 605 | -zui_listin_compute_first_to_show_idx_down_many 606 | fi 607 | else 608 | if [[ "${ZUI[text_mode]}" = "off" ]]; then 609 | # Search right accepting lack of success 610 | -zui_listin_set_next_hyperlink 1 611 | elif -zui_std_has_any_hyperlinks "${(P)list_var_current}"; then 612 | if [[ "${ZUI[text_mode]}" = (hyp|all) ]]; then 613 | (( ++ current_segment )) 614 | else 615 | -zui_listin_set_next_hyperlink 1 616 | fi 617 | else 618 | if [[ "${ZUI[text_mode]}" = (nohyp|all) ]]; then 619 | (( ++ current_segment )) 620 | else 621 | -zui_listin_set_next_hyperlink 1 622 | fi 623 | fi 624 | fi 625 | [[ "$current_segment" -gt "$nseg" ]] && current_segment="$nseg" 626 | fi 627 | ;; 628 | (F1|F2|F3|F4) 629 | reply=( -1 "$key" ) 630 | ;; 631 | ($'\E') 632 | buffer="" 633 | ;; 634 | (o|$'\C-O') 635 | uniq_mode=1-uniq_mode 636 | ;; 637 | (*) 638 | ;; 639 | esac 640 | 641 | else 642 | 643 | case "$key" in 644 | ($'\n'|ENTER) 645 | search=0 646 | -zui_list_cursor_visibility "status" 0 647 | ;; 648 | ($'\C-L') 649 | reply=( -1 REDRAW ) 650 | ;; 651 | 652 | # 653 | # Slightly limited navigation 654 | # 655 | 656 | (UP) 657 | [[ "$current_idx" -gt 1 ]] && current_idx=current_idx-1 658 | while [[ -z "${(P)list_var_current}" && "$current_idx" -gt 1 ]]; do 659 | current_idx=current_idx-1 660 | done 661 | -zui_listin_compute_first_to_show_idx_up_many 662 | ;; 663 | (DOWN) 664 | [[ "$current_idx" -lt "$last_element" ]] && current_idx=current_idx+1 665 | while [[ -z "${(P)list_var_current}" && "$current_idx" -lt "$last_element" ]]; do 666 | current_idx=current_idx+1 667 | done 668 | -zui_listin_compute_first_to_show_idx_down_many 669 | ;; 670 | ($'\C-P') 671 | [[ "$current_idx" -gt 1 ]] && current_idx=current_idx-1 672 | while [[ -z "${(P)list_var_current}" && "$current_idx" -gt 1 ]]; do 673 | current_idx=current_idx-1 674 | done 675 | -zui_listin_compute_first_to_show_idx_center 676 | ;; 677 | ($'\C-N') 678 | # Are there any elements after the current one? 679 | [[ "$current_idx" -lt "$last_element" ]] && current_idx=current_idx+1 680 | while [[ -z "${(P)list_var_current}" && "$current_idx" -lt "$last_element" ]]; do 681 | current_idx=current_idx+1 682 | done 683 | -zui_listin_compute_first_to_show_idx_center 684 | ;; 685 | (PPAGE) 686 | current_idx=current_idx-page_height 687 | [[ "$current_idx" -lt 1 ]] && current_idx=1; 688 | -zui_listin_compute_first_to_show_idx 689 | ;; 690 | (NPAGE) 691 | current_idx=current_idx+page_height 692 | [[ "$current_idx" -gt "$last_element" ]] && current_idx=last_element; 693 | -zui_listin_compute_first_to_show_idx 694 | ;; 695 | ($'\C-U') 696 | current_idx=current_idx-page_height/2 697 | [[ "$current_idx" -lt 1 ]] && current_idx=1; 698 | -zui_listin_compute_first_to_show_idx 699 | ;; 700 | ($'\C-D') 701 | current_idx=current_idx+page_height/2 702 | [[ "$current_idx" -gt "$last_element" ]] && current_idx=last_element; 703 | -zui_listin_compute_first_to_show_idx 704 | ;; 705 | (HOME) 706 | current_idx=1 707 | -zui_listin_compute_first_to_show_idx 708 | ;; 709 | (END) 710 | current_idx=last_element 711 | -zui_listin_compute_first_to_show_idx 712 | ;; 713 | (LEFT|BTAB) 714 | if [[ "$key" = "BTAB" && ${ZUILIST_NONSELECTABLE_ELEMENTS[(r)$current_idx]} = $current_idx ]]; then 715 | [[ "$current_idx" -gt "1" ]] && { 716 | current_idx=current_idx-1 717 | current_segment="100" 718 | } 719 | while [[ -z "${(P)list_var_current}" && "$current_idx" -gt 1 ]]; do 720 | current_idx=current_idx-1 721 | done 722 | -zui_listin_compute_first_to_show_idx_up_many 723 | else 724 | # Establish exact right-edge segment 725 | if [[ "$current_segment" -gt "$nseg" ]]; then 726 | (( current_segment = nseg )) 727 | [[ "$current_segment" -le 0 ]] && current_segment=1 728 | fi 729 | 730 | if [[ "$key" = "BTAB" ]]; then 731 | if ! -zui_listin_set_next_hyperlink 0; then 732 | # No link to the left in current line - move 733 | # one line up, set segment to far-most, the 734 | # verification at bottom will find the nearest 735 | # hyperlink 736 | [[ "$current_idx" -gt "1" ]] && { 737 | current_idx=current_idx-1 738 | current_segment="100" 739 | search_for_hyperlink=1 740 | } 741 | while [[ -z "${(P)list_var_current}" && "$current_idx" -gt 1 ]]; do 742 | current_idx=current_idx-1 743 | done 744 | -zui_listin_compute_first_to_show_idx_up_many 745 | fi 746 | else 747 | if [[ "${ZUI[text_mode]}" = "off" ]]; then 748 | # Search left accepting lack of success 749 | -zui_listin_set_next_hyperlink 0 750 | elif -zui_std_has_any_hyperlinks "${(P)list_var_current}"; then 751 | if [[ "${ZUI[text_mode]}" = (hyp|all) ]]; then 752 | (( -- current_segment )) 753 | else 754 | -zui_listin_set_next_hyperlink 0 755 | fi 756 | else 757 | if [[ "${ZUI[text_mode]}" = (nohyp|all) ]]; then 758 | (( -- current_segment )) 759 | else 760 | -zui_listin_set_next_hyperlink 0 761 | fi 762 | fi 763 | fi 764 | fi 765 | ;; 766 | (RIGHT|$'\t') 767 | if [[ "$key" = $'\t' && ${ZUILIST_NONSELECTABLE_ELEMENTS[(r)$current_idx]} = $current_idx ]]; then 768 | [[ "$current_idx" -lt "$last_element" ]] && { 769 | current_idx=current_idx+1 770 | current_segment=1 771 | } 772 | while [[ -z "${(P)list_var_current}" && "$current_idx" -lt "$last_element" ]]; do 773 | current_idx=current_idx+1 774 | done 775 | -zui_listin_compute_first_to_show_idx_down_many 776 | else 777 | if [[ "$key" = $'\t' ]]; then 778 | if ! -zui_listin_set_next_hyperlink 1; then 779 | # Move to next line, set current_segment 780 | # to 1, verification at bottom will find 781 | # nearest hyperlink if it exists in the 782 | # next line 783 | [[ "$current_idx" -lt "$last_element" ]] && { 784 | current_idx=current_idx+1 785 | current_segment="1" 786 | search_for_hyperlink=1 787 | } 788 | while [[ -z "${(P)list_var_current}" && "$current_idx" -lt "$last_element" ]]; do 789 | current_idx=current_idx+1 790 | done 791 | -zui_listin_compute_first_to_show_idx_down_many 792 | fi 793 | else 794 | if [[ "${ZUI[text_mode]}" = "off" ]]; then 795 | # Search right accepting lack of success 796 | -zui_listin_set_next_hyperlink 1 797 | elif -zui_std_has_any_hyperlinks "${(P)list_var_current}"; then 798 | if [[ "${ZUI[text_mode]}" = (hyp|all) ]]; then 799 | (( ++ current_segment )) 800 | else 801 | -zui_listin_set_next_hyperlink 1 802 | fi 803 | else 804 | if [[ "${ZUI[text_mode]}" = (nohyp|all) ]]; then 805 | (( ++ current_segment )) 806 | else 807 | -zui_listin_set_next_hyperlink 1 808 | fi 809 | fi 810 | fi 811 | [[ "$current_segment" -gt "$nseg" ]] && current_segment="$nseg" 812 | fi 813 | ;; 814 | (F1|F2|F3|F4) 815 | reply=( -1 "$key" ) 816 | ;; 817 | (F4|F5|F6|F7|F8|F9|F10) 818 | # ignore 819 | ;; 820 | 821 | # 822 | # The input 823 | # 824 | 825 | ($'\b'|$'\C-?'|BACKSPACE) 826 | buffer="${buffer%?}" 827 | ;; 828 | ($'\C-W') 829 | [[ "$buffer" = "${buffer% *}" ]] && buffer="" || buffer="${buffer% *}" 830 | ;; 831 | ($'\C-K') 832 | buffer="" 833 | ;; 834 | ($'\E') 835 | buffer="" 836 | search=0 837 | -zui_list_cursor_visibility "status" 0 838 | ;; 839 | ($'\C-O') 840 | uniq_mode=1-uniq_mode 841 | ;; 842 | (*) 843 | if [[ $#key == 1 && $((#key)) -lt 31 ]]; then 844 | # ignore all other control keys 845 | else 846 | buffer+="$key" 847 | fi 848 | ;; 849 | esac 850 | 851 | fi 852 | 853 | # Make current_segment point to hyperlink, if it exists in line 854 | if -zui_std_has_any_hyperlinks "${(P)list_var_current}"; then 855 | [[ "${ZUI[text_mode]}" = (hyp|all) && "$search_for_hyperlink" = "0" ]] || -zui_listin_search_for_hyperlink_graphical 856 | else 857 | [[ "${ZUI[text_mode]}" = (nohyp|all) && "$search_for_hyperlink" = "0" ]] || -zui_listin_search_for_hyperlink_graphical 858 | fi 859 | 860 | reply[3]="$current_idx" 861 | reply[4]="$current_segment" 862 | reply[5]="$from_what_idx_list_is_shown" 863 | reply[6]="$hscroll" 864 | reply[7]="$search" 865 | reply[8]="$buffer" 866 | reply[9]="$uniq_mode" 867 | -------------------------------------------------------------------------------- /functions/zui-list-wrapper: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | emulate -LR zsh 8 | setopt extendedglob warncreateglobal 9 | 10 | local _zui_wrapper_has_terminfo=0 11 | 12 | zmodload zsh/curses 13 | zmodload zsh/terminfo 2>/dev/null && _zui_wrapper_has_terminfo=1 14 | 15 | trap "-zui_wrapper_on_exit" EXIT 16 | 17 | local term_height=${LINES:-$(tput lines)} term_width=${COLUMNS:-$(tput cols)} 18 | ZUI[term_sread_ts]="$EPOCHSECONDS" 19 | 20 | # Reason for this function is that on some systems 21 | # smcup and rmcup are not knowing why left empty 22 | -zui_wrapper_alternate_screen() { 23 | if [ "$_zui_wrapper_has_terminfo" = "1" ]; then 24 | [[ "$1" = "1" && -n "$terminfo[smcup]" ]] && return 25 | [[ "$1" = "0" && -n "$terminfo[rmcup]" ]] && return 26 | else 27 | [[ "$1" = "1" && -n `tput 2>/dev/null smcup` ]] && return 28 | [[ "$1" = "0" && -n `tput 2>/dev/null rmcup` ]] && return 29 | fi 30 | 31 | case "$TERM" in 32 | *rxvt*) 33 | [ "$1" = "1" ] && echo -n $'\x1b7\x1b[?47h' 34 | [ "$1" = "0" ] && echo -n $'\x1b[2J\x1b[?47l\x1b8' 35 | ;; 36 | *) 37 | [ "$1" = "1" ] && echo -n $'\x1b[?1049h' 38 | [ "$1" = "0" ] && echo -n $'\x1b[?1049l' 39 | # just to remember two other that work: $'\x1b7\x1b[r\x1b[?47h', $'\x1b[?47l\x1b8' 40 | ;; 41 | esac 42 | } 43 | 44 | # Cleanup before any exit 45 | -zui_wrapper_on_exit() { 46 | # -zui_wrapper_end 47 | -zui_wrapper_alternate_screen 0 48 | -zui_list_cursor_visibility "plain" 1 49 | } 50 | 51 | -zui_wrapper_init_windows() { 52 | integer create=0 53 | 54 | if [[ "$1" = 1 || -z "${zcurses_windows[(r)main]}" || -z "${zcurses_windows[(r)status]}" ]]; then 55 | create=1 56 | zcurses delwin main 2>/dev/null 57 | zcurses delwin status 2>/dev/null 58 | fi 59 | 60 | # Resize? 61 | (( $1 )) && { 62 | #zcurses 2>/dev/null resize_term "${ZUI[term_height]}" "${ZUI[term_width]}" endwin 63 | zcurses end 64 | zcurses refresh 65 | 66 | if [[ -n "$2" && -n "$3" && -n "$4" && -n "$5" ]]; then 67 | integer __main_height=$(( ZUI[term_height] - ZUI[status_size] )) 68 | : ${(P)2::=$__main_height} 69 | : ${(P)3::=${ZUI[term_width]}} 70 | : ${(P)4::=${ZUI[status_size]}} 71 | : ${(P)5::=${ZUI[term_width]}} 72 | fi 73 | } 74 | 75 | (( create )) && { 76 | zcurses refresh stdscr 77 | zcurses addwin main $(( term_height - ZUI[status_size] )) "$term_width" 0 0 78 | zcurses addwin status "${ZUI[status_size]}" "$term_width" $(( term_height - ZUI[status_size] )) 0 79 | } 80 | } 81 | 82 | -zui_wrapper_end() { 83 | zcurses delwin main 2>/dev/null 84 | zcurses delwin status 2>/dev/null 85 | zcurses end 86 | } 87 | 88 | -zui_wrapper_recreate_windows() { 89 | integer force="$1" 90 | shift 91 | 92 | if [[ "$force" = "1" || -z "$EPOCHSECONDS" || $(( EPOCHSECONDS - ZUI[term_sread_ts] )) -gt "5" ]]; then 93 | term_height=$(tput lines) 94 | term_width=$(tput cols) 95 | ZUI[term_sread_ts]="$EPOCHSECONDS" 96 | fi 97 | 98 | if [[ "${ZUI[term_height]}" -ne "$term_height" || "${ZUI[term_width]}" -ne "$term_width" ]]; then 99 | integer resize=0 100 | (( ZUI[term_height] != 0 )) && resize=1 101 | 102 | ZUI[term_height]="$term_height" 103 | ZUI[term_width]="$term_width" 104 | # Load window size 105 | -zui_std_load_config s:status_size "4" 1 'ZUI[status_size]' 106 | -zui_wrapper_init_windows "$resize" "$@" 107 | return 0 108 | fi 109 | 110 | return 1 111 | } 112 | 113 | # Standard functions library 114 | [[ "${ZUI[stdlib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/stdlib.lzui" 115 | [[ "${ZUI[syslib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/syslib.lzui" 116 | [[ "${ZUI[utillib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/utillib.lzui" 117 | 118 | # Configuration 119 | -zui_std_load_config s:select_mode "no-restart" 2 'ZUI[select_mode]' # quit, restart or callback 120 | -zui_std_load_config s:text_mode "all" 2 'ZUI[text_mode]' 121 | -zui_std_load_config b:text_select 1 2 'ZUI[text_select]' 122 | 123 | ZUI[term_width]=0 124 | ZUI[term_height]=0 125 | 126 | -zui_wrapper_alternate_screen 1 127 | zcurses init 128 | -zui_wrapper_recreate_windows 0 129 | 130 | -zui_util_get_time 131 | zui-list "main" $(( term_height-ZUI[status_size] )) $term_width "status" "${ZUI[status_size]}" "$term_width" " Welcome to ${ZUI[app_name]} $REPLY " "$@" 132 | 133 | if [[ "$REPLY" != "-1" && "$REPLY" = -(#c0,1)[0-9]## ]]; then 134 | local answer="${reply[REPLY]}" 135 | ZUILIST_WRAPPER_LINE="$answer" 136 | if [[ "${ZUI[text_mode]}" = (all|nohyp) ]] || -zui_std_has_any_hyperlinks "$answer"; then 137 | local -a segments 138 | segments=( "${(z@)answer}" ) 139 | [[ "${ZUI[CURRENT_SEGMENT]}" -gt "${#segments}" ]] && ZUI[CURRENT_SEGMENT]="${#segments}" 140 | ZUILIST_WRAPPER_BIT="${segments[${ZUI[CURRENT_SEGMENT]}]}" 141 | fi 142 | else 143 | ZUILIST_WRAPPER_BIT="" ZUILIST_WRAPPER_LINE="" 144 | fi 145 | -------------------------------------------------------------------------------- /functions/zui-process-buffer: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | # 8 | # Input: 9 | # $1 - optional buffer to process (default is $BUFFER) 10 | # $2 - optional parameter containing cursor (default is $CURSOR) 11 | # 12 | # Output: 13 | # ZUI_PB_WORDS - split of "$1" into shell words; array 14 | # ZUI_PB_WORDS_BEGINNINGS - indexes of first letters of corresponding words in ZUI_PB_WORDS 15 | # ZUI_PB_SPACES - white spaces before corresponding words in ZUI_PB_WORDS 16 | # ZUI_PB_SELECTED_WORD - index in ZUI_PB_WORDS pointing to word activated by cursor position 17 | # ZUI_PB_LEFT - left part of active word 18 | # ZUI_PB_RIGHT - right part of active word 19 | # 20 | 21 | emulate -LR zsh 22 | setopt typesetsilent extendedglob noshortloops 23 | 24 | local MBEGIN MEND MATCH mbegin mend match 25 | 26 | local buf="${1:-$BUFFER}" 27 | local cursor="${2:-$CURSOR}" 28 | 29 | # All output variables are either overwritten or cleared 30 | ZUI_PB_WORDS=( "${(Z+n+)buf}" ) 31 | ZUI_PB_SPACES=( ) 32 | ZUI_PB_WORDS_BEGINNINGS=( ) 33 | ZUI_PB_SELECTED_WORD="-1" 34 | ZUI_PB_LEFT="" 35 | ZUI_PB_RIGHT="" 36 | 37 | # (Z+n+) will return 1 element for buf that is empty or only whitespace 38 | if [[ "$buf" = ( |$'\t')# ]]; then 39 | ZUI_PB_WORDS=( ) 40 | integer nwords=0 41 | else 42 | integer nwords="${#ZUI_PB_WORDS}" 43 | fi 44 | 45 | # Remove ZUI_PB_WORDS one by one, counting characters, 46 | # computing beginning of each word, to find 47 | # place to break the word into 2 halves (for 48 | # complete_in_word option) 49 | 50 | local i word wordlen 51 | integer char_count=0 52 | 53 | # (Z) handles spaces nicely, but we need them for the user 54 | # Also compute words beginnings and the selected word 55 | for (( i=1; i<=nwords; i++ )); do 56 | # Remove spurious space generated by Z-flag when 57 | # input is an unbound '$(' (happens with zsh < 5.1) 58 | # and also real spaces gathered by an unbound '$(', 59 | # to handle them in a way normal to this loop 60 | ZUI_PB_WORDS[i]="${ZUI_PB_WORDS[i]%% ##}" 61 | word="${ZUI_PB_WORDS[i]}" 62 | 63 | # In general, $buf can start with white spaces 64 | # We will not search for them, but instead for 65 | # leading character of current shell word, 66 | # negated. This is an ambition to completely 67 | # avoid character classes 68 | 69 | # Remove white spaces 70 | buf="${buf##(#m)[^$word[1]]#}" 71 | # Count them 72 | char_count=char_count+"${#MATCH}" 73 | # This is the beginning of current word 74 | ZUI_PB_WORDS_BEGINNINGS[i]=$(( char_count + 1 )) 75 | # Remember the spaces 76 | ZUI_PB_SPACES[i]="$MATCH" 77 | 78 | # Remove the word 79 | wordlen="${#word}" 80 | [[ "${buf[1,wordlen]}" != "$word" ]] && return 1 # should not happen unless bug in (z) 81 | buf="${buf[wordlen+1,-1]}" 82 | 83 | # Spaces point to previous shell word 84 | # Visual cursor right after spaces (-ge) -> not enough to select previous word (-gt required) 85 | [[ "$ZUI_PB_SELECTED_WORD" -eq "-1" && "$char_count" -gt "$cursor" ]] && ZUI_PB_SELECTED_WORD=$(( i-1 )) 86 | 87 | # Actual characters point to current shell word 88 | # Visual cursor right after letters (-ge) -> enough to select current word 89 | char_count=char_count+"$#word" 90 | [[ "$ZUI_PB_SELECTED_WORD" -eq "-1" && "$char_count" -ge "$cursor" ]] && ZUI_PB_SELECTED_WORD="$i" 91 | done 92 | 93 | # What's left in $buf can be only white spaces 94 | char_count=char_count+"$#buf" 95 | ZUI_PB_SPACES[i]="$buf" 96 | 97 | # Visual cursor right after spaces (-ge) -> enough to select last word 98 | [[ "$ZUI_PB_SELECTED_WORD" -eq "-1" && "$char_count" -ge "$cursor" ]] && ZUI_PB_SELECTED_WORD=$(( i-1 )) 99 | 100 | # Divide active word into two halves 101 | integer diff=$(( cursor - ZUI_PB_WORDS_BEGINNINGS[ZUI_PB_SELECTED_WORD] + 1 )) 102 | word="${ZUI_PB_WORDS[ZUI_PB_SELECTED_WORD]}" 103 | ZUI_PB_LEFT="${word[1,diff]}" 104 | ZUI_PB_RIGHT="${word[diff+1,-1]}" 105 | 106 | # This function should be tested 107 | return 0 108 | -------------------------------------------------------------------------------- /functions/zui-process-buffer2: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | # 8 | # Input: 9 | # $1 - optional buffer to process (default is $BUFFER) 10 | # 11 | # Output: 12 | # ZUI_PB2_WORDS - split of "$1" into shell words; array 13 | # ZUI_PB2_QWORDS - shell words when [;|>&] didn't split 14 | # ZUI_PB2_WORDS_BEGINNINGS - indexes of first letters of corresponding words in ZUI_PB2_WORDS 15 | # ZUI_PB2_QWORDS_BEGINNINGS - the same, but in ZUI_PB2_QWORDS 16 | # ZUI_PB2_SPACES - white spaces before corresponding words in ZUI_PB2_WORDS 17 | # ZUI_PB2_QSPACES - the same, but in ZUI_PB2_QWORDS 18 | # 19 | 20 | emulate -LR zsh 21 | setopt typesetsilent extendedglob noshortloops 22 | 23 | local MBEGIN MEND MATCH mbegin mend match 24 | 25 | local buf="${1:-$BUFFER}" 26 | 27 | # All output variables are either overwritten or cleared 28 | ZUI_PB2_WORDS=( "${(Z+n+)buf}" ) 29 | ZUI_PB2_QWORDS=( ) 30 | ZUI_PB2_SPACES=( ) 31 | ZUI_PB2_QSPACES=( ) 32 | ZUI_PB2_WORDS_BEGINNINGS=( ) 33 | ZUI_PB2_QWORDS_BEGINNINGS=( ) 34 | 35 | # (Z+n+) will return 1 element for buf that is empty or only whitespace 36 | if [[ "$buf" = ( |$'\t')# ]]; then 37 | ZUI_PB2_WORDS=( ) 38 | integer nwords=0 39 | else 40 | integer nwords="${#ZUI_PB2_WORDS}" 41 | fi 42 | 43 | # Remove ZUI_PB2_WORDS one by one, counting characters, 44 | # computing beginning of each word 45 | 46 | local i word wordlen 47 | integer char_count=0 idx=0 48 | 49 | # (Z) handles spaces nicely, but we need them for the user 50 | # Also compute words beginnings 51 | for (( i=1; i<=nwords; i++ )); do 52 | idx+=1 53 | 54 | # Remove spurious space generated by Z-flag when 55 | # input is an unbound '$(' (happens with zsh < 5.1) 56 | # and also real spaces gathered by an unbound '$(', 57 | # to handle them in a way normal to this loop 58 | ZUI_PB2_WORDS[i]="${ZUI_PB2_WORDS[i]%% ##}" 59 | word="${ZUI_PB2_WORDS[i]}" 60 | 61 | # Remove white spaces 62 | buf="${buf##(#m)[^$word[1]]#}" 63 | # Count them 64 | char_count=char_count+"${#MATCH}" 65 | # The beginning of current word 66 | ZUI_PB2_WORDS_BEGINNINGS[i]=$(( char_count + 1 )) 67 | # Remember the spaces 68 | ZUI_PB2_SPACES[i]="$MATCH" 69 | 70 | if [[ "$word" = [\;\>\&\|] && -z "$MATCH" && "$idx" -gt "1" ]]; then 71 | idx=idx-1 72 | ZUI_PB2_QWORDS[idx]+="$word" 73 | elif [[ -z "$MATCH" && "$idx" -gt "1" && "${ZUI_PB2_QWORDS[idx-1]}" = *[\;\>\&\|] ]]; then 74 | idx=idx-1 75 | ZUI_PB2_QWORDS[idx]+="$word" 76 | else 77 | ZUI_PB2_QWORDS[idx]="$word" 78 | ZUI_PB2_QWORDS_BEGINNINGS[idx]=$(( char_count + 1 )) 79 | ZUI_PB2_QSPACES[idx]="$MATCH" 80 | fi 81 | 82 | # Remove the word 83 | wordlen="${#word}" 84 | [[ "${buf[1,wordlen]}" != "$word" ]] && return 1 # should not happen unless bug in (z) 85 | buf="${buf[wordlen+1,-1]}" 86 | 87 | char_count=char_count+"$#word" 88 | done 89 | 90 | # What's left in $buf can be only white spaces 91 | char_count=char_count+"${#buf}" 92 | ZUI_PB2_SPACES[i]="$buf" 93 | ZUI_PB2_QSPACES[idx+1]="$buf" 94 | 95 | # This function should be tested 96 | return 0 97 | -------------------------------------------------------------------------------- /functions/zui-usetty-wrapper: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | 8 | emulate -L zsh 9 | 10 | zmodload zsh/curses 11 | 12 | test_fd0() { 13 | true <&0 14 | } 15 | 16 | local restore=0 FD 17 | 18 | # Reattach to terminal 19 | if [ ! -t 0 ]; then 20 | # Check if can reattach to terminal in any way 21 | if [[ ! -c /dev/tty && ! -t 2 ]]; then 22 | echo "No terminal available (no /dev/tty and no terminal at stderr)" 23 | return 1 24 | fi 25 | 26 | if test_fd0 2>/dev/null; then 27 | exec {FD}<&0 28 | restore=2 29 | else 30 | restore=1 31 | fi 32 | 33 | if [[ ! -c /dev/tty ]]; then 34 | exec <&2 35 | else 36 | exec = __curx )); then 124 | (( __idx += __i - 1 - 1 )) 125 | : ${(P)__var_name::=$__idx} 126 | return 0 127 | fi 128 | __width_sum+=${(m)#__str[__idx+__i-1]} 129 | done 130 | 131 | (( __idx += __i - 1 - 1 )) 132 | : ${(P)__var_name::=$__idx} 133 | return 1 134 | } # }}} 135 | 136 | # FUNCTION: -zui_sys_index_tail_width {{{ 137 | # Returns index in given string that results 138 | # in given width, when displaying the string 139 | # starting from that index 140 | # 141 | # $1 - string 142 | # $2 - expected width 143 | # $3 - output parameter name for the index 144 | function -zui_sys_index_tail_width() { 145 | local __str="$1" __width="$2" __var_name="$3" 146 | integer __i __count=${#__str} __width_sum=0 147 | 148 | for (( __i = __count; __i >= 1; __i -- )); do 149 | __width_sum+=${(m)#__str[__i]} 150 | if (( __width_sum >= __width )); then 151 | : ${(P)__var_name::=$__i} 152 | return 0 153 | fi 154 | done 155 | 156 | : ${(P)__var_name::=1} 157 | return 1 158 | } # }}} 159 | 160 | # FUNCTION: -zui_sys_gather_lanchors {{{ 161 | # Appends mod${1}_ice${2}_lanchors into array given by name 162 | # 163 | # $1 - module index 164 | # $2 - instance index 165 | # $3 - target array name 166 | function -zui_sys_gather_lanchors() { 167 | local __var_name1="mod${1}_ice${2}_lanchors" __var_name2="${3-reply}" 168 | if [[ -z "$1" || -z "$2" ]]; then 169 | echo "-zui_std_gather_lanchors must obtain module and instance indices" 170 | return 1 171 | fi 172 | 173 | # Append and store 174 | local -a __output 175 | __output=( "${(P@)__var_name2}" "${(P@)__var_name1}" ) 176 | 177 | : ${(PA)__var_name2::=${__output[@]}} 178 | } # }}} 179 | 180 | # FUNCTION: -zui_sys_decode_list_update {{{ 181 | # Deserializes on-the-fly update package. 182 | # To be used rather only internally. 183 | # 184 | # $1 - the package 185 | # $2 - optional target parameter name 186 | function -zui_sys_decode_list_update() { 187 | local -a __segments 188 | [[ -n "$1" ]] && __segments=( "${(z@)1}" ) || __segments=( "" "" 0 0 ) 189 | local __var_name="${2-reply}" 190 | 191 | : ${(PA)__var_name::="${(Q)__segments[@]}"} 192 | } # }}} 193 | 194 | # FUNCTION: -zui_sys_decode_hops {{{ 195 | # Deserializes on-the-fly hops substitution. 196 | # For internal usage. 197 | # 198 | # $1 - the hops' package 199 | # $2 - optional target parameter name 200 | function -zui_sys_decode_hops() { 201 | local -a __hops 202 | [[ -n "$1" ]] && __hops=( "${(z@)1}" ) || __hops=( ) 203 | local __var_name="${2-reply}" 204 | : ${(PA)__var_name::=${(Q)__hops[@]}} 205 | } # }}} 206 | 207 | # FUNCTION: -zui_sys_decode_nonselectables {{{ 208 | # Deserializes on-the-fly nonselectables 209 | # substitution. For internal usage. 210 | # 211 | # $1 - the nonselectables' package 212 | # $2 - optional target parameter name 213 | function -zui_sys_decode_nonselectables() { 214 | local -a __nonselectables 215 | [[ -n "$1" ]] && __nonselectables=( "${(z@)1}" ) || __nonselectables=( ) 216 | local __var_name="${2-reply}" 217 | : ${(PA)__var_name::="${(Q)__nonselectables[@]}"} 218 | } # }}} 219 | 220 | # FUNCTION: -zui_sys_decode_lanchors {{{ 221 | # Deserializes on-the-fly local anchors. 222 | # For internal usage. 223 | # 224 | # $1 - the anchors' package 225 | # $2 - optional target parameter name 226 | function -zui_sys_decode_lanchors() { 227 | local -a __lanchors 228 | [[ -n "$1" ]] && __lanchors=( "${(z@)1}" ) || __lanchors=( ) 229 | local __var_name="${2-reply}" 230 | : ${(PA)__var_name::="${(Q)__lanchors[@]}"} 231 | } # }}} 232 | 233 | # FUNCTION: -zui_sys_add_message {{{ 234 | # Appends given message to ZUI_MESSAGES 235 | # 236 | # $1 - message type 237 | # $2 - timestamp 238 | # $3, $4, ... - message bits / lines 239 | function -zui_sys_add_message() { 240 | local tpe="$1" ts="${2:-$(date +%s)}" timestr="" 241 | 242 | if [[ -n "${ZUI[log_time_format]}" ]]; then 243 | if (( ${+builtins[strftime]} )); then 244 | strftime -s timestr "${ZUI[log_time_format]}" "$ts" 245 | else 246 | # Slow and not respecting $ts fallback 247 | # but will rescue users with weird zsh 248 | timestr=$(date "+${ZUI[log_time_format]}") 249 | fi 250 | fi 251 | 252 | # Clean space-only elements 253 | set -- "${@[3,-1]/(#s)[[:space:]]##(#e)/}" 254 | 255 | integer index msg_len=0 256 | for (( index = 1; index <= ${#}; index ++ )); do 257 | msg_len+=${#@[index]} 258 | done 259 | 260 | ZUI_MESSAGES+=( "${(q)msg_len} ${(q)tpe} ${(q)ts} ${(q)timestr} ${(j: :)${(q)@}}" ) 261 | (( ++ ZUI[message_count] )) 262 | 263 | # House-keeping 264 | (( ${#ZUI_MESSAGES} >= ZUI[log_size] + 10 )) && { ZUI_MESSAGES=( "${(@)ZUI_MESSAGES[1+10,-1]}" ); } 265 | } # }}} 266 | 267 | # FUNCTION: -zui_sys_get_match_line {{{ 268 | # Returns at which index in $__list the search 269 | # result that's currently highlighted is located. 270 | # 271 | # Allows to jump to search result, not only see it. 272 | # 273 | # $__slist - input variable with copy of $__list 274 | # $1 - output variable to hold the established target line number 275 | function -zui_sys_get_match_line() { 276 | local __var_name="${1:-REPLY}" __i 277 | [[ "${#ZUILIST_NONSELECTABLE_ELEMENTS}" -gt 0 ]] && for __i in "${(nO)ZUILIST_NONSELECTABLE_ELEMENTS[@]}"; do 278 | __slist[$__i]=() 279 | done 280 | [[ "${ZUI[UNIQ_MODE]}" -eq 1 ]] && typeset -gU __slist 281 | 282 | local search_buffer="${ZUI[SEARCH_BUFFER]%% ##}" 283 | search_buffer="${search_buffer## ##}" 284 | search_buffer="${search_buffer//(#m)[][*?|#~^()><\\]/\\$MATCH}" 285 | if [[ -n "$search_buffer" ]]; then 286 | # Base bit of this pattern is ((#s)[^$'\01']#THEWORD*|*$'\02'[^$'\01']#THEWORD*) - the word occuring not inside hyper link 287 | # Pattern will be: BIT1~^BIT2... 288 | local search_pattern=${search_buffer//(#b)([^ ]##)/(#B)((#s)[^$'\01'$'\032'$'\034']#${match[1]}*|*[$'\02'][^$'\01'$'\032'$'\034']#${match[1]}*)~^} 289 | search_pattern="${search_pattern// ##/}" 290 | search_pattern="${search_pattern%\~\^}" 291 | 292 | search_pattern="(#b)(($search_pattern)|(*))" 293 | integer __line=0 __correct=0 294 | local -a __slist2 295 | repeat 1; do 296 | __slist=( "${(@)__slist//(#i)$~search_pattern/$(( ++ __line, ${#match[2]} > 0 ? __line : 0 ))}" ) 297 | __slist=( "${(@)__slist:#0}" ) 298 | __slist2=( "${(@)__slist:#}" ) 299 | done 300 | 301 | # Use the same element but from line-number-only array 302 | __line=${__slist2[${ZUI[CURRENT_IDX]}]} 303 | 304 | # Correct for possible empty entries that weren't counted 305 | # because // substitution ignores empty elements, always 306 | integer __index=${__slist[(i)$__line]} 307 | local -a __empty 308 | __empty=( "${(@M)__slist[1,__index]:#}" ) 309 | __line+=${#__empty} 310 | 311 | [[ "${ZUI[UNIQ_MODE]}" -ne 1 ]] && [[ "${#ZUILIST_NONSELECTABLE_ELEMENTS}" -gt 0 ]] && for __i in "${(no)ZUILIST_NONSELECTABLE_ELEMENTS[@]}"; do 312 | (( __i <= __line + __correct )) && __correct+=1 313 | done 314 | 315 | __line+=__correct 316 | 317 | : ${(P)__var_name::=$__line} 318 | return 0 319 | fi 320 | 321 | return 1 322 | } # }}} 323 | -------------------------------------------------------------------------------- /lib/utillib.lzui: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | # 8 | # Library file (*.lzui) 9 | # 10 | # Utility functions 11 | # 12 | 13 | ZUI[utillib_sourced]="1" 14 | 15 | # FUNCTION: -zui_util_map_bools {{{ 16 | # Maps boolean values of expressions given in $1 17 | # (string separated by ';') to parameters given 18 | # via names in $2 (separated by ';'). For true, 19 | # $3 is assigned to corresponding parameter, $4 20 | # for false. 21 | # 22 | # If $1 contains [[ or ((, it is evaluated. If 23 | # it is a positive number or zero, then it is 24 | # treated as direct bool value. Otherwise it's 25 | # treated as parameter name, and boolean value 26 | # of the parameter (it has to be positive number 27 | # or zero) is examined. 28 | # 29 | # -zui_util_map_bools "1;[[ a = b ]];ZUI[text_select]" \ 30 | # "color1;color2;color3" $red $white 31 | # 32 | function -zui_util_map_bools() { 33 | local __exp="$1" __pm="$2" __tre="$3" __fse="$4" 34 | 35 | local -a __exps __pms 36 | __exps=( "${(@s:;:)__exp}" ) 37 | __pms=( "${(@s:;:)__pm}" ) 38 | 39 | integer __index __size=${#__pms} 40 | for (( __index=1; __index <= __size; ++ __index )); do 41 | __exp="${__exps[__index]}" 42 | __pm="${__pms[__index]}" 43 | [[ -z "${__exp##[[:space:]]##}" || -z "${__pm##[[:space:]]##}" ]] && continue 44 | if [[ "$__exp" = <-> ]] then 45 | (( $__exp )) && : ${(P)__pm::=$__tre} || : ${(P)__pm::=$__fse} 46 | elif [[ "$__exp" != *\[\[* && "$__exp" != *\(\(* ]]; then 47 | [[ "${(P)__exp}" != <-> || "${(P)__exp}" = 0## ]] && : ${(P)__pm::=$__fse} || : ${(P)__pm::=$__tre} 48 | else 49 | eval "$__exp" && : ${(P)__pm::=$__tre} || : ${(P)__pm::=$__fse} 50 | fi 51 | done 52 | } # }}} 53 | 54 | # FUNCTION: -zui_util_strip_codes {{{ 55 | # Strips formatting codes from text in 56 | # $1, saves result into parameter REPLY 57 | # 58 | # $1 - text to strip codes from 59 | function -zui_util_strip_codes() { 60 | REPLY="${1//[$'\03'-$'\07'$'\013'-$'\014'$'\016'-$'\031'$'\037']/}" 61 | } # }}} 62 | 63 | # FUNCTION: -zui_util_get_segment {{{ 64 | # Return n-th (z) segment of given text 65 | # $1 - text 66 | # $2 - segment (default is 1) 67 | # $3 - destination variable name (default is "REPLY") 68 | # 69 | # Can use e.g. 'reply[1]' for $3 70 | function -zui_util_get_segment() { 71 | local -a segs 72 | segs=( "${(z@)1}" ) 73 | local varname="${3:-REPLY}" 74 | local index="${2:-1}" 75 | : ${(P)varname::=${segs[index]}} 76 | } # }}} 77 | 78 | # FUNCTION: -zui_util_get_time {{{ 79 | # 80 | # Returns time %H:%M, via datetime or `date` as fallback 81 | # 82 | function -zui_util_get_time() { 83 | local ts 84 | ts="$EPOCHSECONDS" 85 | [[ -z "$ts" || "$ts" = "0" ]] && REPLY="$(date '+%H:%M')" || strftime -s REPLY '%H:%M' "$ts" 86 | } # }}} 87 | 88 | # FUNCTION: -zui_util_get_datetime {{{ 89 | # Returns date and time. Uses datetime zsh module 90 | # or date command as fallback. 91 | # 92 | # $REPLY - date and time string "Ymd_H.M.S" 93 | # 94 | function -zui_util_get_datetime() { 95 | local ts 96 | ts="$EPOCHSECONDS" 97 | [[ -z "$ts" || "$ts" = "0" ]] && REPLY="$(date '+%Y%m%d_%H.%M.%S')" || strftime -s REPLY '%Y%m%d_%H.%M.%S' "$ts" 98 | } # }}} 99 | 100 | # FUNCTION: -zui_util_get_timestamp {{{ 101 | # Returns timestamp, via datetime or `date` as fallback 102 | # 103 | function -zui_util_get_timestamp() { 104 | REPLY="$EPOCHSECONDS" 105 | [[ -z "$REPLY" ]] && REPLY="$(date +%s)" 106 | } # }}} 107 | 108 | # FUNCTION: -zui_util_has_default_color {{{ 109 | # Returns true if the "default" color 110 | # can be used with current Zsh/zcurses 111 | function -zui_util_has_default_color() { 112 | (( ${+zcurses_colors} )) || return 2 113 | [[ -z "${zcurses_colors[(r)default]}" ]] && return 1 114 | autoload is-at-least 115 | is-at-least 2>/dev/null 5.3 && return 0 116 | return 1 117 | } # }}} 118 | 119 | # FUNCTION: -zui_util_resolve_path {{{ 120 | # Resolves absolute path from current working directory and file path 121 | # 122 | # $1 - current working directory 123 | # 124 | # $2 - file path 125 | # 126 | # $reply[1] - dirname 127 | # 128 | # $reply[2] - basename 129 | # 130 | function -zui_util_resolve_path() { 131 | local dirpath="$1" filepath="$2" 132 | 133 | local dirpath2="${dirpath/#\~/$HOME}" 134 | # :a behaves weird, prepends paths, which are not CWD 135 | [ "${dirpath2[1]}" = "/" ] && dirpath2="${dirpath2:a}" 136 | local filepath2="${filepath/#\~/$HOME}" 137 | [ "${filepath2[1]}" = "/" ] && filepath2="${filepath2:a}" 138 | 139 | reply=() 140 | if [ "${filepath2[1]}" = "/" ]; then 141 | reply[1]="${filepath2:h}" 142 | reply[2]="${filepath2:t}" 143 | else 144 | local p="$dirpath2/$filepath2" 145 | [ "${p[1]}" = "/" ] && p="${p:a}" 146 | reply[1]="${p:h}" 147 | reply[2]="${p:t}" 148 | fi 149 | } # }}} 150 | 151 | # FUNCTION: -zui_util_to_cmd_line {{{ 152 | # Puts given text on command line - regardless 153 | # if Zle is active or not 154 | # 155 | # $1 - text to put on command line 156 | # 157 | function -zui_util_to_cmd_line() { 158 | if zle; then 159 | zle .kill-buffer 160 | BUFFER="$1" 161 | zle .redisplay 162 | zle .beginning-of-line 163 | else 164 | print -zr "$1" 165 | fi 166 | } # }}} 167 | 168 | # FUNCTION: -zui_util_circular_next {{{ 169 | # Returns next file to write to in circular buffer set 170 | # of file names .1 .2 ... . 171 | # 172 | # The buffer is ordered according to modification time. 173 | # 174 | # $1 - base of file names in circular buffer 175 | # $2 - maximum number of files in circular buffer 176 | # 177 | function -zui_util_circular_next() { 178 | setopt localoptions extendedglob 179 | 180 | # Input data 181 | local base="$1" count="$2" 182 | 183 | # Circular buffers' directory 184 | local circpath="$ZUI_CONFIG_DIR/var/circular_buffers" 185 | [[ ! -d "$circpath" ]] && command mkdir -p "$circpath" 186 | 187 | local -a circular_buffer 188 | circular_buffer=( "$circpath"/"$base".[[:digit:]]##(OmN) ) 189 | 190 | if [[ "$count" -gt "${#circular_buffer}" ]]; then 191 | integer next_index=$(( ${#circular_buffer} + 1 )) 192 | REPLY="$circpath/${base}.${next_index}" 193 | else 194 | REPLY="${circular_buffer[1]}" 195 | fi 196 | 197 | return 0 198 | } # }}} 199 | 200 | # FUNCTION: -zui_util_circular_paths {{{ 201 | # 202 | # Returns absolute file paths of given circular buffer. 203 | # They are ordered from most recent to least recent. 204 | # 205 | # No count is obtained, so all files are returned, even 206 | # actually disabled by buffer limit. 207 | # 208 | # $1 - name of the circular buffer 209 | # 210 | function -zui_util_circular_paths() { 211 | setopt localoptions extendedglob 212 | 213 | # Input data 214 | local base="$1" 215 | 216 | # Output array 217 | reply=( ) 218 | 219 | # Circular buffers' directory 220 | local circpath="$ZUI_CONFIG_DIR/var/circular_buffers" 221 | [[ ! -d "$circpath" ]] && return 1 222 | 223 | reply=( "$circpath"/"$base".[[:digit:]]##(omN) ) 224 | } # }}} 225 | 226 | # FUNCTION: -zui_util_to_cmd_line {{{ 227 | # Puts given text on command line – regardless if Zle is active or not 228 | # 229 | # $1 - text to put on command line 230 | # 231 | function -zui_util_to_cmd_line() { 232 | if zle; then 233 | zle .kill-buffer 234 | BUFFER="$1" 235 | zle .redisplay 236 | zle .beginning-of-line 237 | else 238 | print -zr "$1" 239 | fi 240 | } # }}} 241 | -------------------------------------------------------------------------------- /lib/zcompile.zsh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | zcompile "$@" 3 | -------------------------------------------------------------------------------- /zui.plugin.zsh: -------------------------------------------------------------------------------- 1 | # ============================================================================ # 2 | # [ https://github.com/z-shell ] ❮ ZI ❯ [ (c) 2022 Z-SHELL COMMUNITY ] # 3 | # ============================================================================ # 4 | # 5 | # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- 6 | # vim: ft=zsh sw=2 ts=2 et 7 | # 8 | # No plugin manager is needed to use this file. All that is needed is adding: 9 | # source {wherezui-is}/zui.plugin.zsh 10 | # 11 | # to ~/.zshrc. 12 | # 13 | 14 | 0="${(%):-%N}" # this gives immunity to functionargzero being unset 15 | ZUI_REPO_DIR="${0%/*}" 16 | 17 | # Check if ZI config directory exist. 18 | if [[ -d $ZCDR ]]; then 19 | # Use ZI default config directory. 20 | ZUI_CONFIG_DIR="${ZCDR}/zui" 21 | else 22 | # Use common values to set default working directory. 23 | ZUI_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/zi/zui" 24 | fi 25 | 26 | # Update FPATH if: 27 | # 1. Not loading with ZI 28 | # 2. Not having fpath already updated (that would equal: using other plugin manager) 29 | if [[ -z "$ZI_CUR_PLUGIN" && "${fpath[(r)$ZUI_REPO_DIR]}" != $ZUI_REPO_DIR ]]; then 30 | fpath+=( "$ZUI_REPO_DIR/functions" ) 31 | fi 32 | 33 | # Load colors 34 | [[ ${+fg_bold} = "0" || -z "${fg_bold[green]}" ]] && builtin autoload -Uz colors && colors 35 | 36 | # 37 | # Setup 38 | # 39 | 40 | # Support reloading 41 | (( ${+functions[zui-list]} )) && { unfunction -- zui-list zui-list-draw zui-list-input zui-list-wrapper -zui-log zui-event-loop -zui-list-box-loop zui-process-buffer zui-process-buffer2 zui-usetty-wrapper zui-demo-various zui-demo-hello-world zui-demo-text-fields zui-demo-fly zui-demo-append zui-demo-buttons zui-demo-anchors zui-demo-list-boxes zui-demo-history -zui_std_cleanup -zui_std_init 2>/dev/null; unset ZUI; } 42 | 43 | autoload -- zui-list zui-list-draw zui-list-input zui-list-wrapper -zui-log zui-event-loop -zui-list-box-loop 44 | autoload -- zui-process-buffer zui-process-buffer2 zui-usetty-wrapper 45 | 46 | fpath+=( "${ZUI_REPO_DIR}/demos" ) 47 | autoload -- zui-demo-hello-world zui-demo-fly zui-demo-append zui-demo-text-fields zui-demo-list-boxes zui-demo-anchors 48 | autoload -- zui-demo-ganchors zui-demo-buttons zui-demo-special-text zui-demo-history zui-demo-various zui-demo-timeout 49 | autoload -- zui-demo-configure zui-demo-edit zui-demo-toggles zui-demo-nmap 50 | 51 | zle -N zui-demo-various 52 | bindkey "^O^Z" zui-demo-various 53 | 54 | # 55 | # Global parameters 56 | # 57 | 58 | typeset -gAH ZUI 59 | typeset -ga ZUI_MESSAGES 60 | 61 | # 62 | # Load modules 63 | # 64 | 65 | zmodload -F zsh/stat b:zstat && ZUI[stat_available]="1" || ZUI[stat_available]="0" 66 | zmodload zsh/datetime && ZUI[datetime_available]="1" || ZUI[datetime_available]="0" 67 | 68 | # 69 | # Functions 70 | # 71 | 72 | # Cleanup and init stubs, to be first stdlib 73 | # functions called, sourcing the libraries 74 | 75 | (( 0 == ${+functions[-zui_std_cleanup]} )) && { 76 | function -zui_std_cleanup() { 77 | unfunction -- -zui_std_cleanup 78 | [[ "${ZUI[stdlib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/stdlib.lzui" 79 | [[ "${ZUI[syslib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/syslib.lzui" 80 | [[ "${ZUI[utillib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/utillib.lzui" 81 | -zui_std_cleanup "$@" 82 | } 83 | } 84 | 85 | (( 0 == ${+functions[-zui_std_init]} )) && { 86 | function -zui_std_init() { 87 | unfunction -- -zui_std_init 88 | [[ "${ZUI[stdlib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/stdlib.lzui" 89 | [[ "${ZUI[syslib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/syslib.lzui" 90 | [[ "${ZUI[utillib_sourced]}" != "1" ]] && source "${ZUI_REPO_DIR}/lib/utillib.lzui" 91 | -zui_std_init "$@" 92 | } 93 | } 94 | --------------------------------------------------------------------------------