Skip to content

Commit d1e1703

Browse files
committed
public poc dev version
1 parent de76297 commit d1e1703

File tree

10 files changed

+174
-85
lines changed

10 files changed

+174
-85
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.idea
22
.vscode
3+
goprompt

Makefile

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,42 @@ CURRENT_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
33

44
ZSH_PROMPT_SETUP_SCRIPT := $(CURRENT_DIR)/plugin/zsh/prompt_asynczle_setup.zsh
55

6+
USR_BIN_DIR := $(HOME)/bin
7+
USR_ZSH_DIR := $(HOME)/.local/share/zsh-funcs
8+
9+
build:
10+
go build -o "goprompt" ./cmd/goprompt
11+
12+
.PHONY: install
613
install:
7-
go install ./cmd/goprompt
14+
mkdir -p "$(USR_BIN_DIR)"
15+
go build -o "$(USR_BIN_DIR)/goprompt" ./cmd/goprompt
16+
mkdir -p "$(USR_ZSH_DIR)"
17+
cp "$(ZSH_PROMPT_SETUP_SCRIPT)" "$(USR_ZSH_DIR)/prompt_asynczle_setup"
18+
$(MAKE) setup
819

9-
prompt.source:
10-
@echo ". $(ZSH_PROMPT_SETUP_SCRIPT)"
20+
.PHONY: setup
21+
setup:
22+
@echo '# SETUP:' >&2
23+
@echo '# ------------------------------------------------------------------------------' >&2
24+
@echo '# Assuming GoPrompt installed in $(USR_BIN_DIR)' >&2
25+
@echo '# and zsh func in $(USR_ZSH_DIR)' >&2
26+
@echo '# ------------------------------------------------------------------------------' >&2
27+
@echo "# $$ make setup >> ~/.zshrc" >&2
28+
@echo '# ------------------------------------------------------------------------------' >&2
29+
@echo "# Add this to your ~/.zshenv" >&2
30+
@echo '# ------------------------------------------------------------------------------' >&2
31+
@echo ''
32+
@echo '# PROMPT_ASYNC_ZLE: ------------------------------------------------------------'
33+
@echo 'path+=( "$(USR_BIN_DIR)" )'
34+
@echo 'fpath+=( "$(USR_ZSH_DIR)" )'
35+
@echo 'autoload -Uz promptinit'
36+
@echo 'promptinit && prompt_asynczle_setup'
37+
@echo '# ------------------------------------------------------------------------------'
1138

39+
.PHONY: try
1240
try: install
41+
@echo '>> THIS NEEDS EXTRA CONFIG <<'
42+
@echo '>> FOR DEVELOPMENT ONLY <<'
1343
ZSH_DISABLE_PROMPT=Y ZSH_EXTRA_SOURCE="$(ZSH_PROMPT_SETUP_SCRIPT)" zsh
1444

15-
exec: install
16-
ZSH_DISABLE_PROMPT=Y ZSH_EXTRA_SOURCE="$(ZSH_PROMPT_SETUP_SCRIPT)" exec zsh -l

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Async ZLE Based Prompt
2+
3+
(Implemented In GoLang)
4+
5+
## TLDR
6+
7+
This is a non-blocking asynchronous prompt based on ZLE File Descriptor Handlers.
8+
9+
The prompt query and rendering can be done via any command as long as it follows a line delimited protocol to communicate between the query and rendering components.
10+
11+
12+
![Demo Of GoPrompt With ZLE](./assets/Kapture%202022-07-26%20at%2010.45.33.gif "Capture")
13+
14+
## Reference
15+
16+
You can find the ZSH/ZLE integration in:
17+
18+
* [prompt_asynczle_setup.zsh](./plugin/zsh/prompt_asynczle_setup.zsh)
19+
20+
And the main POC query/rendering logic is implemented in GO
21+
22+
* [goprompt](./cmd/goprompt)
23+
24+
## Install
25+
26+
```
27+
$ eval "$(gimme 1.18.3)"
28+
$ make install
29+
$ make setup >> ~/.zshrc
30+
```
31+
32+
Assuming GoPrompt installed in `~/bin` and zsh func in `~/.local/share/zsh-funcs`
33+
34+
`make setup` will add the following to your `~/.zshrc`:
35+
36+
```
37+
# PROMPT_ASYNC_ZLE: ------------------------------------------------------------
38+
path+=( "$HOME/bin" )
39+
fpath+=( "$HOME/.local/share/zsh-funcs" )
40+
autoload -Uz promptinit
41+
promptinit && prompt_asynczle_setup
42+
# ------------------------------------------------------------------------------
43+
```
1.72 MB
Loading

cmd/goprompt/cmdQuery.go

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"strings"
7+
"sync"
78
"time"
89

910
ps "github.com/mitchellh/go-ps"
@@ -65,10 +66,19 @@ func timeFMT(ts time.Time) string {
6566
}
6667

6768
func cmdQueryRun(_ *cobra.Command, _ []string) error {
68-
printCH := make(chan shellKV)
69-
defer close(printCH)
69+
tasks := new(AsyncTaskDispatcher)
7070

71-
go shellKVStaggeredPrinter(printCH, 20*time.Millisecond, 600*time.Millisecond)
71+
printCH := make(chan shellKV)
72+
printerWG := new(sync.WaitGroup)
73+
printerWG.Add(1)
74+
go func() {
75+
defer printerWG.Done()
76+
shellKVStaggeredPrinter(printCH, 20*time.Millisecond, 600*time.Millisecond)
77+
}()
78+
printerStop := func() {
79+
close(printCH)
80+
printerWG.Wait()
81+
}
7282
printPart := func(name string, value interface{}) {
7383
printCH <- shellKV{name, value}
7484
}
@@ -80,10 +90,12 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
8090
printPart(_partStatus, fmt.Sprintf("%#v", *flgQCmdStatus))
8191
}
8292

83-
wg := new(WaitGroupDispatcher)
84-
defer wg.Wait()
93+
defer func() {
94+
tasks.Wait()
95+
printerStop()
96+
}()
8597

86-
wg.Dispatch(func() {
98+
tasks.Dispatch(func() {
8799
homeDir := os.Getenv("HOME")
88100

89101
if wd, err := os.Getwd(); err == nil {
@@ -103,7 +115,7 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
103115
}
104116
})
105117

106-
wg.Dispatch(func() {
118+
tasks.Dispatch(func() {
107119
pidCurr := os.Getpid()
108120
var pidShell ps.Process
109121

@@ -132,25 +144,17 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
132144
printPart(_partPidParentExec, pidShellParent.Executable())
133145
})
134146

135-
//wg.Dispatch(func() {
136-
// out, err := stringExec("git", "config", "--list")
137-
// printPart("debug_o", js(out))
138-
// if err != nil {
139-
// printPart("debug_e", js(err.Error()))
140-
// }
141-
//})
142-
143-
wg.Dispatch(func() {
144-
cwg := new(WaitGroupDispatcher)
145-
defer cwg.Wait()
147+
tasks.Dispatch(func() {
148+
subTasks := new(AsyncTaskDispatcher)
149+
defer subTasks.Wait()
146150

147151
if _, err := stringExec("git", "rev-parse", "--show-toplevel"); err == nil {
148152
printPart(_partVcs, "git")
149153
} else {
150154
return
151155
}
152156

153-
cwg.Dispatch(func() {
157+
subTasks.Dispatch(func() {
154158
if branch, err := stringExec("git", "branch", "--show-current"); err == nil {
155159
branch = trim(branch)
156160
if len(branch) > 0 {
@@ -168,8 +172,8 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
168172
}
169173
})
170174

171-
cwg.Dispatch(func() {
172-
status, err := stringExec("git", "status", "--porcelain");
175+
subTasks.Dispatch(func() {
176+
status, err := stringExec("git", "status", "--porcelain")
173177
if err != nil {
174178
return
175179
}
@@ -209,7 +213,7 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
209213
printPart(_partVcsGitIdxExcluded, fOutOfIndex)
210214
})
211215

212-
cwg.Dispatch(func() {
216+
subTasks.Dispatch(func() {
213217
if status, err := stringExec("git", "rev-list", "--left-right", "--count", "HEAD...@{u}"); err == nil {
214218
parts := strings.SplitN(status, "\t", 2)
215219
if len(parts) < 2 {
@@ -218,24 +222,25 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
218222

219223
printPart(_partVcsLogAhead, parts[0])
220224
printPart(_partVcsLogBehind, parts[1])
221-
222225
}
223226
})
224227
})
225228

226-
wg.Dispatch(func() {
229+
tasks.Dispatch(func() {
227230
var err error
228231

229-
cwg := new(WaitGroupDispatcher)
230-
defer cwg.Wait()
232+
subTasks := new(AsyncTaskDispatcher)
233+
defer subTasks.Wait()
231234

232235
var stgSeriesLen string
233236
if stgSeriesLen, err = stringExec("stg", "series", "-c"); err == nil {
234237
printPart(_partVcsStg, "1")
235238
printPart(_partVcsStgQlen, stgSeriesLen)
239+
} else {
240+
return
236241
}
237242

238-
cwg.Dispatch(func() {
243+
subTasks.Dispatch(func() {
239244
if stgSeriesPos, err := stringExec("stg", "series", "-cA"); err == nil {
240245
printPart(_partVcsStgQpos, stgSeriesPos)
241246
}
@@ -248,7 +253,7 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
248253
return
249254
}
250255

251-
cwg.Dispatch(func() {
256+
subTasks.Dispatch(func() {
252257
gitSHA, _ := stringExec("stg", "id")
253258
stgSHA, _ := stringExec("stg", "id", stgPatchTop)
254259

cmd/goprompt/cmdRender.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ func cmdRenderRun(_ *cobra.Command, _ []string) error {
205205
promptLines := []string{""}
206206
if len(partsTop) > 0 {
207207
promptLines = append(promptLines, promptStatusMarker+strings.Join(partsTop, " "))
208+
} else {
209+
promptLines = append(promptLines, promptStatusMarker+strings.Repeat("-", 30))
208210
}
209211
if len(partsBottom) > 0 {
210212
promptLines = append(promptLines, promptStatusMarker+strings.Join(partsBottom, " "))

cmd/goprompt/utils.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,28 @@ package main
33
import (
44
"encoding/json"
55
"fmt"
6+
"os"
67
"strconv"
78
"strings"
89
"sync"
910
"time"
1011

11-
"EXP/pkg/shellout"
12+
"github.com/NonLogicalDev/shell.async-goprompt/pkg/shellout"
1213
)
1314

14-
type WaitGroupDispatcher struct {
15+
type AsyncTaskDispatcher struct {
1516
wg sync.WaitGroup
1617
}
1718

18-
func (d *WaitGroupDispatcher) Dispatch(fn func()) {
19+
func (d *AsyncTaskDispatcher) Dispatch(fn func()) {
1920
d.wg.Add(1)
2021
go func() {
2122
defer d.wg.Done()
2223
fn()
2324
}()
2425
}
2526

26-
func (d *WaitGroupDispatcher) Wait() {
27+
func (d *AsyncTaskDispatcher) Wait() {
2728
d.wg.Wait()
2829
}
2930

@@ -35,7 +36,11 @@ type shellKV struct {
3536
}
3637

3738
func (kv shellKV) Print() {
38-
fmt.Printf("%s\t%v\n", kv.name, kv.value)
39+
fmt.Println(kv.String())
40+
}
41+
42+
func (kv shellKV) String() string {
43+
return fmt.Sprintf("%s\t%v", kv.name, kv.value)
3944
}
4045

4146
func shellKVStaggeredPrinter(
@@ -46,15 +51,14 @@ func shellKVStaggeredPrinter(
4651
) {
4752
var parts []shellKV
4853

49-
printParts := func() {
54+
printParts := func(parts []shellKV) {
5055
for _, p := range parts {
5156
p.Print()
5257
}
5358
if len(parts) > 0 {
5459
fmt.Println()
5560
}
56-
57-
parts = nil
61+
os.Stdout.Sync()
5862
}
5963

6064
timerFirst := time.NewTimer(dFirst)
@@ -70,15 +74,18 @@ LOOP:
7074
parts = append(parts, rx)
7175

7276
case <-timerFirst.C:
73-
printParts()
77+
printParts(parts)
78+
parts = nil
7479

7580
case <-timer.C:
76-
printParts()
81+
printParts(parts)
82+
parts = nil
7783
timer.Reset(d)
7884
}
7985
}
8086

81-
printParts()
87+
printParts(parts)
88+
parts = nil
8289
}
8390

8491
//: ----------------------------------------------------------------------------
@@ -130,7 +137,7 @@ func intMin(a, b int) int {
130137
}
131138

132139
func trim(s string) string {
133-
return strings.Trim(s, "\n\t ")
140+
return strings.Trim(s, "\n")
134141
}
135142

136143
func strInt(s string) int {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module EXP
1+
module github.com/NonLogicalDev/shell.async-goprompt
22

33
go 1.18
44

misc/outtakes-t.zsh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
#
2+
# ZSH Mindfuck:
3+
#
4+
# * ${(f)EXPR} splits value of expr by Newline
5+
# * ${(s/ /)EXPR} splits value of expr by space, or any other char instead of space
6+
# * ${(j/ /)EXPR) joins value of expr by space
7+
# * ${(kv)EXPR) if EXPR is an associative array, this gives you a compacted sequence of key, value pairs.
8+
# * ${(p...)EXPR) the p here makes the following magic to recognize Print Escapes ala '\n'
9+
# * ${(@...)EXPR) in double quotes puts each array result into separate word
10+
#
11+
# ZSH Mindfuck Examples:
12+
#
13+
# > typeset -A K=(a b c d)
14+
#
15+
# > echo ${(j:.:)${(kv)K}}
16+
# a.b.c.d
17+
#
18+
# > echo ${(j:.:)${(k)K}}
19+
# a.c
20+
#
21+
# > echo ${(j:.:)${(v)K}}
22+
# b.d
23+
#
24+
# > echo "${(j:.:)${(v)K}}"
25+
# b d #
26+
# > echo "${(@j:.:)${(v)K}}"
27+
# b d
28+
#
29+
# > echo "${(@j:.:)${(@v)K}}"
30+
# b.d
31+
#
32+
# > echo "${(j:.:)${(@v)K}}"
33+
# b.d
34+
#
35+
136
__zsh_buffer_fd_reader() {
237
local D=$1
338
local FD=$2

0 commit comments

Comments
 (0)