-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
completions.go
114 lines (104 loc) · 2.53 KB
/
completions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package hush
import (
"log"
"os"
"path/filepath"
"strings"
"mvdan.cc/sh/v3/syntax"
)
type completion struct {
Completion string
Start, End int
}
func getCompletions(line string, cursor int) []completion {
completions, err := getCompletionsErr(line, cursor)
if err != nil {
log.Print("Failed completions:", err, "\r\n")
return nil
}
return completions
}
func getCompletionsErr(line string, cursor int) ([]completion, error) {
parser := syntax.NewParser()
var stmts []*syntax.Stmt
err := parser.Stmts(strings.NewReader(line), func(stmt *syntax.Stmt) bool {
if int(stmt.Pos().Offset()) <= cursor && int(stmt.End().Offset()) >= cursor {
stmts = append(stmts, stmt)
}
return true
})
if err != nil || len(stmts) == 0 {
return nil, err
}
cursorStmt := stmts[0]
cursorStmtStr := formatStmt(line, cursorStmt)
cursorStmtOffset := int(cursorStmt.Pos().Offset())
cursor -= cursorStmtOffset
var commandWord, cursorWord *syntax.Word
err = parser.Words(strings.NewReader(cursorStmtStr), func(word *syntax.Word) bool {
if commandWord == nil {
commandWord = word
}
if int(word.Pos().Offset()) <= cursor && int(word.End().Offset()) >= cursor {
cursorWord = word
}
return true
})
if err != nil || cursorWord == nil {
return nil, err
}
commandWordStr, err := evalWord(commandWord.Parts)
if err != nil {
return nil, err
}
cursorWordStr, err := evalWord(cursorWord.Parts)
if err != nil {
return nil, err
}
return getStatementCompletions(
commandWordStr,
cursorWordStr,
cursorStmtOffset+int(cursorWord.Pos().Offset()),
cursorStmtOffset+int(cursorWord.End().Offset()))
}
func getStatementCompletions(commandName string, word string, start, end int) ([]completion, error) {
switch {
case strings.Contains(word, "/"):
dir := word
filter := false
info, err := os.Stat(dir)
if err != nil || !info.IsDir() {
dir = filepath.Dir(dir)
filter = true
}
dirEntries, err := os.ReadDir(dir)
if err != nil {
return nil, nil
}
var completions []completion
for _, d := range dirEntries {
base := filepath.Base(word)
name := d.Name()
if !filter || strings.HasPrefix(name, base) {
file := fileJoin(dir, name)
if d.IsDir() {
file += string(filepath.Separator)
}
completions = append(completions, completion{
Completion: file,
Start: start,
End: end,
})
}
}
return completions, nil
default:
return nil, nil
}
}
func fileJoin(a, b string) string {
if a == "." {
return "." + string(filepath.Separator) + b
}
return filepath.Join(a, b)
}