src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/edit/complete_getopt.go (about) 1 package edit 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 "unicode/utf8" 8 9 "src.elv.sh/pkg/eval" 10 "src.elv.sh/pkg/eval/vals" 11 "src.elv.sh/pkg/getopt" 12 "src.elv.sh/pkg/parse" 13 "src.elv.sh/pkg/ui" 14 ) 15 16 func completeGetopt(fm *eval.Frame, vArgs, vOpts, vArgHandlers any) error { 17 args, err := parseGetoptArgs(vArgs) 18 if err != nil { 19 return err 20 } 21 opts, err := parseGetoptOptSpecs(vOpts) 22 if err != nil { 23 return err 24 } 25 argHandlers, variadic, err := parseGetoptArgHandlers(vArgHandlers) 26 if err != nil { 27 return err 28 } 29 30 // TODO: Make the Config field configurable 31 _, parsedArgs, ctx := getopt.Complete(args, opts.opts, getopt.GNU) 32 33 out := fm.ValueOutput() 34 putShortOpt := func(opt *getopt.OptionSpec) error { 35 c := complexItem{Stem: "-" + string(opt.Short)} 36 if d, ok := opts.desc[opt]; ok { 37 if e, ok := opts.argDesc[opt]; ok { 38 c.Display = ui.T(c.Stem + " " + e + " (" + d + ")") 39 } else { 40 c.Display = ui.T(c.Stem + " (" + d + ")") 41 } 42 } 43 return out.Put(c) 44 } 45 putLongOpt := func(opt *getopt.OptionSpec) error { 46 c := complexItem{Stem: "--" + opt.Long} 47 if d, ok := opts.desc[opt]; ok { 48 if e, ok := opts.argDesc[opt]; ok { 49 c.Display = ui.T(c.Stem + " " + e + " (" + d + ")") 50 } else { 51 c.Display = ui.T(c.Stem + " (" + d + ")") 52 } 53 } 54 return out.Put(c) 55 } 56 call := func(fn eval.Callable, args ...any) error { 57 return fn.Call(fm, args, eval.NoOpts) 58 } 59 60 switch ctx.Type { 61 case getopt.OptionOrArgument, getopt.Argument: 62 // Find argument handler. 63 var argHandler eval.Callable 64 if len(parsedArgs) < len(argHandlers) { 65 argHandler = argHandlers[len(parsedArgs)] 66 } else if variadic { 67 argHandler = argHandlers[len(argHandlers)-1] 68 } 69 if argHandler != nil { 70 return call(argHandler, ctx.Text) 71 } 72 // TODO(xiaq): Notify that there is no suitable argument completer. 73 case getopt.AnyOption: 74 for _, opt := range opts.opts { 75 if opt.Short != 0 { 76 err := putShortOpt(opt) 77 if err != nil { 78 return err 79 } 80 } 81 if opt.Long != "" { 82 err := putLongOpt(opt) 83 if err != nil { 84 return err 85 } 86 } 87 } 88 case getopt.LongOption: 89 for _, opt := range opts.opts { 90 if opt.Long != "" && strings.HasPrefix(opt.Long, ctx.Text) { 91 err := putLongOpt(opt) 92 if err != nil { 93 return err 94 } 95 } 96 } 97 case getopt.ChainShortOption: 98 for _, opt := range opts.opts { 99 if opt.Short != 0 { 100 // TODO(xiaq): Loses chained options. 101 err := putShortOpt(opt) 102 if err != nil { 103 return err 104 } 105 } 106 } 107 case getopt.OptionArgument: 108 gen := opts.argGenerator[ctx.Option.Spec] 109 if gen != nil { 110 return call(gen, ctx.Option.Argument) 111 } 112 } 113 return nil 114 } 115 116 // TODO(xiaq): Simplify most of the parsing below with reflection. 117 118 func parseGetoptArgs(v any) ([]string, error) { 119 var args []string 120 var err error 121 errIterate := vals.Iterate(v, func(v any) bool { 122 arg, ok := v.(string) 123 if !ok { 124 err = fmt.Errorf("arg should be string, got %s", vals.Kind(v)) 125 return false 126 } 127 args = append(args, arg) 128 return true 129 }) 130 if errIterate != nil { 131 err = errIterate 132 } 133 return args, err 134 } 135 136 type parsedOptSpecs struct { 137 opts []*getopt.OptionSpec 138 desc map[*getopt.OptionSpec]string 139 argDesc map[*getopt.OptionSpec]string 140 argGenerator map[*getopt.OptionSpec]eval.Callable 141 } 142 143 func parseGetoptOptSpecs(v any) (parsedOptSpecs, error) { 144 result := parsedOptSpecs{ 145 nil, map[*getopt.OptionSpec]string{}, 146 map[*getopt.OptionSpec]string{}, map[*getopt.OptionSpec]eval.Callable{}} 147 148 var err error 149 errIterate := vals.Iterate(v, func(v any) bool { 150 m, ok := v.(vals.Map) 151 if !ok { 152 err = fmt.Errorf("opt should be map, got %s", vals.Kind(v)) 153 return false 154 } 155 156 opt := &getopt.OptionSpec{} 157 158 getStringField := func(k string) (string, bool, error) { 159 v, ok := m.Index(k) 160 if !ok { 161 return "", false, nil 162 } 163 if vs, ok := v.(string); ok { 164 return vs, true, nil 165 } 166 return "", false, 167 fmt.Errorf("%s should be string, got %s", k, vals.Kind(v)) 168 } 169 getCallableField := func(k string) (eval.Callable, bool, error) { 170 v, ok := m.Index(k) 171 if !ok { 172 return nil, false, nil 173 } 174 if vb, ok := v.(eval.Callable); ok { 175 return vb, true, nil 176 } 177 return nil, false, 178 fmt.Errorf("%s should be fn, got %s", k, vals.Kind(v)) 179 } 180 getBoolField := func(k string) (bool, bool, error) { 181 v, ok := m.Index(k) 182 if !ok { 183 return false, false, nil 184 } 185 if vb, ok := v.(bool); ok { 186 return vb, true, nil 187 } 188 return false, false, 189 fmt.Errorf("%s should be bool, got %s", k, vals.Kind(v)) 190 } 191 192 if s, ok, errGet := getStringField("short"); ok { 193 r, size := utf8.DecodeRuneInString(s) 194 if r == utf8.RuneError || size != len(s) { 195 err = fmt.Errorf( 196 "short should be exactly one rune, got %v", parse.Quote(s)) 197 return false 198 } 199 opt.Short = r 200 } else if errGet != nil { 201 err = errGet 202 return false 203 } 204 if s, ok, errGet := getStringField("long"); ok { 205 opt.Long = s 206 } else if errGet != nil { 207 err = errGet 208 return false 209 } 210 if opt.Short == 0 && opt.Long == "" { 211 err = errors.New( 212 "opt should have at least one of short and long forms") 213 return false 214 } 215 216 argRequired, _, errGet := getBoolField("arg-required") 217 if errGet != nil { 218 err = errGet 219 return false 220 } 221 argOptional, _, errGet := getBoolField("arg-optional") 222 if errGet != nil { 223 err = errGet 224 return false 225 } 226 switch { 227 case argRequired && argOptional: 228 err = errors.New( 229 "opt cannot have both arg-required and arg-optional") 230 return false 231 case argRequired: 232 opt.Arity = getopt.RequiredArgument 233 case argOptional: 234 opt.Arity = getopt.OptionalArgument 235 } 236 237 if s, ok, errGet := getStringField("desc"); ok { 238 result.desc[opt] = s 239 } else if errGet != nil { 240 err = errGet 241 return false 242 } 243 if s, ok, errGet := getStringField("arg-desc"); ok { 244 result.argDesc[opt] = s 245 } else if errGet != nil { 246 err = errGet 247 return false 248 } 249 if f, ok, errGet := getCallableField("completer"); ok { 250 result.argGenerator[opt] = f 251 } else if errGet != nil { 252 err = errGet 253 return false 254 } 255 256 result.opts = append(result.opts, opt) 257 return true 258 }) 259 if errIterate != nil { 260 err = errIterate 261 } 262 return result, err 263 } 264 265 func parseGetoptArgHandlers(v any) ([]eval.Callable, bool, error) { 266 var argHandlers []eval.Callable 267 var variadic bool 268 var err error 269 errIterate := vals.Iterate(v, func(v any) bool { 270 sv, ok := v.(string) 271 if ok { 272 if sv == "..." { 273 variadic = true 274 return true 275 } 276 err = fmt.Errorf( 277 "string except for ... not allowed as argument handler, got %s", 278 parse.Quote(sv)) 279 return false 280 } 281 argHandler, ok := v.(eval.Callable) 282 if !ok { 283 err = fmt.Errorf( 284 "argument handler should be fn, got %s", vals.Kind(v)) 285 } 286 argHandlers = append(argHandlers, argHandler) 287 return true 288 }) 289 if errIterate != nil { 290 err = errIterate 291 } 292 return argHandlers, variadic, err 293 }