Skip to content

Commit c2b0b02

Browse files
committed
Refactor some files into an internal directory
1 parent 161b9a1 commit c2b0b02

File tree

12 files changed

+643
-553
lines changed

12 files changed

+643
-553
lines changed

command.go

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package console
22

33
import (
44
"github.com/spf13/cobra"
5-
"github.com/spf13/pflag"
65
)
76

87
const (
@@ -74,39 +73,3 @@ next:
7473

7574
c.filters = updated
7675
}
77-
78-
// resetFlagsDefaults resets all flags to their default values.
79-
//
80-
// Slice flags accumulate per execution (and do not reset),
81-
//
82-
// so we must reset them manually.
83-
//
84-
// Example:
85-
//
86-
// Given cmd.Flags().StringSlice("comment", nil, "")
87-
// If you run a command with --comment "a" --comment "b" you will get
88-
// the expected [a, b] slice.
89-
//
90-
// If you run a command again with no --comment flags, you will get
91-
// [a, b] again instead of an empty slice.
92-
//
93-
// If you run the command again with --comment "c" --comment "d" flags,
94-
// you will get [a, b, c, d] instead of just [c, d].
95-
func resetFlagsDefaults(target *cobra.Command) {
96-
target.Flags().VisitAll(func(flag *pflag.Flag) {
97-
flag.Changed = false
98-
switch value := flag.Value.(type) {
99-
case pflag.SliceValue:
100-
var res []string
101-
102-
if len(flag.DefValue) > 0 && flag.DefValue != "[]" {
103-
res = append(res, flag.DefValue)
104-
}
105-
106-
value.Replace(res)
107-
108-
default:
109-
flag.Value.Set(flag.DefValue)
110-
}
111-
})
112-
}

completer.go

Lines changed: 25 additions & 279 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
package console
22

33
import (
4-
"bytes"
5-
"errors"
6-
"fmt"
7-
"os"
8-
"regexp"
94
"strings"
10-
"unicode"
11-
"unicode/utf8"
125

6+
"github.com/reeflective/readline"
137
"github.com/rsteube/carapace"
148
"github.com/rsteube/carapace/pkg/style"
159
completer "github.com/rsteube/carapace/pkg/x"
16-
"github.com/rsteube/carapace/pkg/xdg"
1710

18-
"github.com/reeflective/readline"
11+
"github.com/reeflective/console/internal/completion"
12+
"github.com/reeflective/console/internal/line"
1913
)
2014

21-
func (c *Console) complete(line []rune, pos int) readline.Completions {
15+
func (c *Console) complete(input []rune, pos int) readline.Completions {
2216
menu := c.activeMenu()
2317

2418
// Ensure the carapace library is called so that the function
@@ -27,7 +21,7 @@ func (c *Console) complete(line []rune, pos int) readline.Completions {
2721

2822
// Split the line as shell words, only using
2923
// what the right buffer (up to the cursor)
30-
args, prefixComp, prefixLine := splitArgs(line, pos)
24+
args, prefixComp, prefixLine := completion.SplitArgs(input, pos)
3125

3226
// Prepare arguments for the carapace completer
3327
// (we currently need those two dummies for avoiding a panic).
@@ -42,7 +36,7 @@ func (c *Console) complete(line []rune, pos int) readline.Completions {
4236

4337
for idx, val := range completions.Values {
4438
raw[idx] = readline.Completion{
45-
Value: unescapeValue(prefixComp, prefixLine, val.Value),
39+
Value: line.UnescapeValue(prefixComp, prefixLine, val.Value),
4640
Display: val.Display,
4741
Description: val.Description,
4842
Style: style.SGR(val.Style),
@@ -112,280 +106,32 @@ func (c *Console) justifyCommandComps(comps readline.Completions) readline.Compl
112106
return comps
113107
}
114108

115-
func (c *Console) defaultStyleConfig() {
116-
// If carapace config file is found, just return.
117-
if dir, err := xdg.UserConfigDir(); err == nil {
118-
_, err := os.Stat(fmt.Sprintf("%v/carapace/styles.json", dir))
119-
if err == nil {
120-
return
121-
}
122-
}
123-
124-
// Overwrite all default styles for color
125-
for i := 1; i < 13; i++ {
126-
styleStr := fmt.Sprintf("carapace.Highlight%d", i)
127-
style.Set(styleStr, "bright-white")
128-
}
129-
130-
// Overwrite all default styles for flags
131-
style.Set("carapace.FlagArg", "bright-white")
132-
style.Set("carapace.FlagMultiArg", "bright-white")
133-
style.Set("carapace.FlagNoArg", "bright-white")
134-
style.Set("carapace.FlagOptArg", "bright-white")
135-
}
136-
137-
// splitArgs splits the line in valid words, prepares them in various ways before calling
138-
// the completer with them, and also determines which parts of them should be used as
139-
// prefixes, in the completions and/or in the line.
140-
func splitArgs(line []rune, pos int) (args []string, prefixComp, prefixLine string) {
141-
line = line[:pos]
142-
143-
// Remove all colors from the string
144-
line = []rune(strip(string(line)))
145-
146-
// Split the line as shellwords, return them if all went fine.
147-
args, remain, err := splitCompWords(string(line))
148-
149-
// We might have either no error and args, or no error and
150-
// the cursor ready to complete a new word (last character
151-
// in line is a space).
152-
// In some of those cases we append a single dummy argument
153-
// for the completer to understand we want a new word comp.
154-
mustComplete, args, remain := mustComplete(line, args, remain, err)
155-
if mustComplete {
156-
return sanitizeArgs(args), "", remain
157-
}
158-
159-
// But the completion candidates themselves might need slightly
160-
// different prefixes, for an optimal completion experience.
161-
arg, prefixComp, prefixLine := adjustQuotedPrefix(remain, err)
162-
163-
// The remainder is everything following the open charater.
164-
// Pass it as is to the carapace completion engine.
165-
args = append(args, arg)
166-
167-
return sanitizeArgs(args), prefixComp, prefixLine
168-
}
169-
170-
func mustComplete(line []rune, args []string, remain string, err error) (bool, []string, string) {
171-
dummyArg := ""
172-
173-
// Empty command line, complete the root command.
174-
if len(args) == 0 || len(line) == 0 {
175-
return true, append(args, dummyArg), remain
176-
}
177-
178-
// If we have an error, we must handle it later.
109+
// highlightSyntax - Entrypoint to all input syntax highlighting in the Wiregost console.
110+
func (c *Console) highlightSyntax(input []rune) string {
111+
// Split the line as shellwords
112+
args, unprocessed, err := line.Split(string(input), true)
179113
if err != nil {
180-
return false, args, remain
181-
}
182-
183-
lastChar := line[len(line)-1]
184-
185-
// No remain and a trailing space means we want to complete
186-
// for the next word, except when this last space was escaped.
187-
if remain == "" && unicode.IsSpace(lastChar) {
188-
if strings.HasSuffix(string(line), "\\ ") {
189-
return true, args, args[len(args)-1]
190-
}
191-
192-
return true, append(args, dummyArg), remain
193-
}
194-
195-
// Else there is a character under the cursor, which means we are
196-
// in the middle/at the end of a posentially completed word.
197-
return true, args, remain
198-
}
199-
200-
func adjustQuotedPrefix(remain string, err error) (arg, comp, line string) {
201-
arg = remain
202-
203-
switch {
204-
case errors.Is(err, errUnterminatedDoubleQuote):
205-
comp = "\""
206-
line = comp + arg
207-
case errors.Is(err, errUnterminatedSingleQuote):
208-
comp = "'"
209-
line = comp + arg
210-
case errors.Is(err, errUnterminatedEscape):
211-
arg = strings.ReplaceAll(arg, "\\", "")
212-
}
213-
214-
return arg, comp, line
215-
}
216-
217-
// sanitizeArg unescapes a restrained set of characters.
218-
func sanitizeArgs(args []string) (sanitized []string) {
219-
for _, arg := range args {
220-
arg = replacer.Replace(arg)
221-
sanitized = append(sanitized, arg)
222-
}
223-
224-
return sanitized
225-
}
226-
227-
// when the completer has returned us some completions, we sometimes
228-
// needed to post-process them a little before passing them to our shell.
229-
func unescapeValue(prefixComp, prefixLine, val string) string {
230-
quoted := strings.HasPrefix(prefixLine, "\"") ||
231-
strings.HasPrefix(prefixLine, "'")
232-
233-
if quoted {
234-
val = strings.ReplaceAll(val, "\\ ", " ")
235-
}
236-
237-
return val
238-
}
239-
240-
// split has been copied from go-shellquote and slightly modified so as to also
241-
// return the remainder when the parsing failed because of an unterminated quote.
242-
func splitCompWords(input string) (words []string, remainder string, err error) {
243-
var buf bytes.Buffer
244-
words = make([]string, 0)
245-
246-
for len(input) > 0 {
247-
// skip any splitChars at the start
248-
char, read := utf8.DecodeRuneInString(input)
249-
if strings.ContainsRune(splitChars, char) {
250-
input = input[read:]
251-
continue
252-
} else if char == escapeChar {
253-
// Look ahead for escaped newline so we can skip over it
254-
next := input[read:]
255-
if len(next) == 0 {
256-
remainder = string(escapeChar)
257-
err = errUnterminatedEscape
258-
259-
return words, remainder, err
260-
}
261-
262-
c2, l2 := utf8.DecodeRuneInString(next)
263-
if c2 == '\n' {
264-
input = next[l2:]
265-
continue
266-
}
267-
}
268-
269-
var word string
270-
271-
word, input, err = splitCompWord(input, &buf)
272-
if err != nil {
273-
return words, word + input, err
274-
}
275-
276-
words = append(words, word)
277-
}
278-
279-
return words, remainder, nil
280-
}
281-
282-
// splitWord has been modified to return the remainder of the input (the part that has not been
283-
// added to the buffer) even when an error is returned.
284-
func splitCompWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
285-
buf.Reset()
286-
287-
raw:
288-
{
289-
cur := input
290-
for len(cur) > 0 {
291-
char, read := utf8.DecodeRuneInString(cur)
292-
cur = cur[read:]
293-
switch {
294-
case char == singleChar:
295-
buf.WriteString(input[0 : len(input)-len(cur)-read])
296-
input = cur
297-
goto single
298-
case char == doubleChar:
299-
buf.WriteString(input[0 : len(input)-len(cur)-read])
300-
input = cur
301-
goto double
302-
case char == escapeChar:
303-
buf.WriteString(input[0 : len(input)-len(cur)-read])
304-
buf.WriteRune(char)
305-
input = cur
306-
goto escape
307-
case strings.ContainsRune(splitChars, char):
308-
buf.WriteString(input[0 : len(input)-len(cur)-read])
309-
return buf.String(), cur, nil
310-
}
311-
}
312-
if len(input) > 0 {
313-
buf.WriteString(input)
314-
input = ""
315-
}
316-
goto done
317-
}
318-
319-
escape:
320-
{
321-
if len(input) == 0 {
322-
input = buf.String() + input
323-
return "", input, errUnterminatedEscape
324-
}
325-
c, l := utf8.DecodeRuneInString(input)
326-
if c != '\n' {
327-
buf.WriteString(input[:l])
328-
}
329-
input = input[l:]
114+
args = append(args, unprocessed)
330115
}
331116

332-
goto raw
117+
done := make([]string, 0) // List of processed words, append to
118+
remain := args // List of words to process, draw from
119+
trimmed := line.TrimSpaces(remain) // Match stuff against trimmed words
333120

334-
single:
335-
{
336-
i := strings.IndexRune(input, singleChar)
337-
if i == -1 {
338-
return "", input, errUnterminatedSingleQuote
339-
}
340-
buf.WriteString(input[0:i])
341-
input = input[i+1:]
342-
goto raw
121+
// Highlight the root command when found.
122+
cmd, _, _ := c.activeMenu().Find(trimmed)
123+
if cmd != nil {
124+
done, remain = line.HighlightCommand(done, args, c.activeMenu().Command, c.cmdHighlight)
343125
}
344126

345-
double:
346-
{
347-
cur := input
348-
for len(cur) > 0 {
349-
c, read := utf8.DecodeRuneInString(cur)
350-
cur = cur[read:]
351-
switch c {
352-
case doubleChar:
353-
buf.WriteString(input[0 : len(input)-len(cur)-read])
354-
input = cur
355-
goto raw
356-
case escapeChar:
357-
// bash only supports certain escapes in double-quoted strings
358-
char2, l2 := utf8.DecodeRuneInString(cur)
359-
cur = cur[l2:]
360-
if strings.ContainsRune(doubleEscapeChars, char2) {
361-
buf.WriteString(input[0 : len(input)-len(cur)-read-l2])
127+
// Highlight command flags
128+
done, remain = line.HighlightCommandFlags(done, remain, c.flagHighlight)
362129

363-
if char2 != '\n' {
364-
buf.WriteRune(char2)
365-
}
366-
input = cur
367-
}
368-
}
369-
}
130+
// Done with everything, add remainind, non-processed words
131+
done = append(done, remain...)
370132

371-
return "", input, errUnterminatedDoubleQuote
372-
}
133+
// Join all words.
134+
highlighted := strings.Join(done, "")
373135

374-
done:
375-
return buf.String(), input, nil
136+
return highlighted
376137
}
377-
378-
const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
379-
380-
var re = regexp.MustCompile(ansi)
381-
382-
// strip removes all ANSI escaped color sequences in a string.
383-
func strip(str string) string {
384-
return re.ReplaceAllString(str, "")
385-
}
386-
387-
var replacer = strings.NewReplacer(
388-
"\n", ` `,
389-
"\t", ` `,
390-
"\\ ", " ", // User-escaped spaces in words.
391-
)

0 commit comments

Comments
 (0)