Repository navigation
Expand file tree
/
Copy pathcompletion_bash_input.go
More file actions
89 lines (86 loc) · 2.34 KB
/
Copy pathcompletion_bash_input.go
File metadata and controls
89 lines (86 loc) · 2.34 KB
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
package cli
import (
"fmt"
"strings"
)
// Readline may retain balanced quotes in CURRENT even though the CLI scanner
// has removed them. Reuse the non-evaluating lexer before aligning word breaks.
func bashCompletionReplacementPrefix(prefix, current string) string {
if !strings.HasSuffix(prefix, current) {
line := "_ " + current
if request, err := bashCompletionRequest(line, len(line)); err == nil && len(request.Words) == 1 {
current = request.Words[0]
}
}
if strings.HasSuffix(prefix, current) {
return prefix[:len(prefix)-len(current)]
}
return ""
}
// Bash owns expansion and execution. This lexer only removes simple quoting and
// identifies the simple command surrounding the cursor; it never evaluates code.
func bashCompletionRequest(line string, point int) (CompletionRequest, error) {
if point < 0 || point > len(line) {
return CompletionRequest{}, fmt.Errorf("invalid Bash completion cursor")
}
var words []string
current, offset := -1, 0
for i := 0; i < len(line); {
if strings.ContainsRune(";|&\n", rune(line[i])) {
if i >= point {
break
}
words, current = nil, -1
i++
continue
}
if line[i] == ' ' || line[i] == '\t' || line[i] == '\r' {
if i >= point && current < 0 {
current, offset = len(words), 0
words = append(words, "")
}
i++
continue
}
start := i
var word strings.Builder
var quote byte
prefix := 0
for i < len(line) {
char := line[i]
if quote == 0 && strings.ContainsRune(" \t\r\n;|&", rune(char)) {
break
}
if char == '\\' && quote != '\'' && i+1 < len(line) && (quote == 0 || strings.ContainsRune("\\\"$`\n", rune(line[i+1]))) {
i++
if line[i] != '\n' {
word.WriteByte(line[i])
}
} else if (char == '\'' || char == '"') && (quote == 0 || quote == char) {
if quote == 0 {
quote = char
} else {
quote = 0
}
} else {
word.WriteByte(char)
}
i++
if i <= point {
prefix = word.Len()
}
}
if point >= start && point <= i {
current, offset = len(words), prefix
}
words = append(words, word.String())
}
if current < 0 {
current, offset = len(words), 0
words = append(words, "")
}
if current == 0 || len(words) == 0 {
return CompletionRequest{}, fmt.Errorf("bash completion has no argument target")
}
return CompletionRequest{Words: words[1:], Word: current - 1, Offset: offset}, nil
}