github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/cmds/elvish/edit/completion/complete_arg.go (about) 1 package completion 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/u-root/u-root/cmds/elvish/edit/ui" 11 "github.com/u-root/u-root/cmds/elvish/parse" 12 ) 13 14 type argComplContext struct { 15 complContextCommon 16 words []string 17 } 18 19 func (*argComplContext) name() string { return "argument" } 20 21 func findArgComplContext(n parse.Node, ev pureEvaler) complContext { 22 if sep, ok := n.(*parse.Sep); ok { 23 if form, ok := sep.Parent().(*parse.Form); ok && form.Head != nil { 24 return &argComplContext{ 25 complContextCommon{ 26 "", quotingForEmptySeed, n.End(), n.End()}, 27 evalFormPure(form, "", n.End(), ev), 28 } 29 } 30 } 31 if primary, ok := n.(*parse.Primary); ok { 32 if compound, seed := primaryInSimpleCompound(primary, ev); compound != nil { 33 if form, ok := compound.Parent().(*parse.Form); ok { 34 if form.Head != nil && form.Head != compound { 35 return &argComplContext{ 36 complContextCommon{ 37 seed, primary.Type, compound.Begin(), compound.End()}, 38 evalFormPure(form, seed, compound.Begin(), ev), 39 } 40 } 41 } 42 } 43 } 44 return nil 45 } 46 47 func evalFormPure(form *parse.Form, seed string, seedBegin int, ev pureEvaler) []string { 48 // Find out head of the form and preceding arguments. 49 // If form.Head is not a simple compound, head will be "", just what we want. 50 head, _ := ev.PurelyEvalPartialCompound(form.Head, nil) 51 words := []string{head} 52 for _, compound := range form.Args { 53 if compound.Begin() >= seedBegin { 54 break 55 } 56 if arg, err := ev.PurelyEvalCompound(compound); err == nil { 57 // XXX Arguments that are not simple compounds are simply ignored. 58 words = append(words, arg) 59 } 60 } 61 62 words = append(words, seed) 63 return words 64 } 65 66 // To complete an argument, delegate the actual completion work to a suitable 67 // complContext. 68 func (ctx *argComplContext) generate(env *complEnv, ch chan<- rawCandidate) error { 69 return completeArg(ctx.words, env.evaler, env.argCompleter, ch) 70 } 71 72 // TODO: getStyle does redundant stats. 73 func complFilenameInner(head string, executableOnly bool, rawCands chan<- rawCandidate) error { 74 dir, fileprefix := filepath.Split(head) 75 dirToRead := dir 76 if dirToRead == "" { 77 dirToRead = "." 78 } 79 80 infos, err := ioutil.ReadDir(dirToRead) 81 if err != nil { 82 return fmt.Errorf("cannot list directory %s: %v", dirToRead, err) 83 } 84 85 // Make candidates out of elements that match the file component. 86 for _, info := range infos { 87 name := info.Name() 88 // Show dot files iff file part of pattern starts with dot, and vice 89 // versa. 90 if dotfile(fileprefix) != dotfile(name) { 91 continue 92 } 93 // Only accept searchable directories and executable files if 94 // executableOnly is true. 95 if executableOnly && !(info.IsDir() || (info.Mode()&0111) != 0) { 96 continue 97 } 98 99 // Full filename for source and getStyle. 100 full := dir + name 101 102 suffix := " " 103 if info.IsDir() { 104 suffix = string(filepath.Separator) 105 } else if info.Mode()&os.ModeSymlink != 0 { 106 stat, err := os.Stat(full) 107 if err == nil && stat.IsDir() { 108 // Symlink to directory. 109 suffix = string(filepath.Separator) 110 } 111 } 112 113 rawCands <- &complexCandidate{ 114 stem: full, codeSuffix: suffix, 115 style: ui.StylesFromString(""), 116 } 117 } 118 119 return nil 120 } 121 122 func dotfile(fname string) bool { 123 return strings.HasPrefix(fname, ".") 124 }