1package commands 2 3import ( 4 "fmt" 5 "strings" 6 7 "github.com/ambientsound/pms/api" 8 "github.com/ambientsound/pms/input/lexer" 9 "github.com/ambientsound/pms/input/parser" 10 "github.com/ambientsound/pms/options" 11) 12 13// Set manipulates a Options table by parsing input tokens from the "set" command. 14type Set struct { 15 newcommand 16 api api.API 17 tokens []parser.OptionToken 18} 19 20// NewSet returns Set. 21func NewSet(api api.API) Command { 22 return &Set{ 23 api: api, 24 tokens: make([]parser.OptionToken, 0), 25 } 26} 27 28// Parse implements Command. 29func (cmd *Set) Parse() error { 30 31 cmd.setTabCompleteVerbs("") 32 33 for { 34 // Scan the next token, which must be an identifier. 35 tok, lit := cmd.ScanIgnoreWhitespace() 36 switch tok { 37 case lexer.TokenIdentifier: 38 break 39 case lexer.TokenEnd, lexer.TokenComment: 40 return nil 41 default: 42 cmd.setTabCompleteEmpty() 43 return fmt.Errorf("Unexpected '%s', expected whitespace or END", lit) 44 } 45 46 cmd.setTabCompleteVerbs(lit) 47 48 // Parse the option statement. 49 cmd.Unscan() 50 err := cmd.ParseSet() 51 if err != nil { 52 return err 53 } 54 } 55} 56 57// ParseSet parses a single "key=val" statement. 58func (cmd *Set) ParseSet() error { 59 tokens := make([]string, 0) 60 for { 61 tok, lit := cmd.Scan() 62 if tok == lexer.TokenWhitespace || tok == lexer.TokenEnd || tok == lexer.TokenComment { 63 break 64 } 65 tokens = append(tokens, lit) 66 } 67 68 s := strings.Join(tokens, "") 69 cmd.setTabCompleteVerbs(s) 70 optionToken := parser.OptionToken{} 71 err := optionToken.Parse([]rune(s)) 72 if err != nil { 73 cmd.setTabCompleteEmpty() 74 return err 75 } 76 77 // Figure out tabcomplete 78 cmd.setTabCompleteOption(optionToken) 79 80 cmd.tokens = append(cmd.tokens, optionToken) 81 82 return nil 83} 84 85// Exec implements Command. 86func (cmd *Set) Exec() error { 87 for _, tok := range cmd.tokens { 88 opt := cmd.api.Options().Get(tok.Key) 89 90 if opt == nil { 91 return fmt.Errorf("No such option: %s", tok.Key) 92 } 93 94 // Queries print options to the statusbar. 95 if tok.Query { 96 cmd.api.Message(opt.String()) 97 continue 98 } 99 100 switch opt := opt.(type) { 101 102 case *options.BoolOption: 103 switch { 104 case !tok.Bool: 105 return fmt.Errorf("Attempting to give parameters to a boolean option (try 'set no%s' or 'set inv%s')", tok.Key, tok.Key) 106 case tok.Invert: 107 opt.SetBool(!opt.BoolValue()) 108 cmd.api.Message(opt.String()) 109 case tok.Negate: 110 opt.SetBool(false) 111 default: 112 opt.SetBool(true) 113 } 114 115 default: 116 if !tok.Bool { 117 if err := opt.Set(tok.Value); err != nil { 118 return err 119 } 120 break 121 } 122 123 // Not a boolean option, and no value. Print the value. 124 cmd.api.Message(opt.String()) 125 continue 126 } 127 128 cmd.api.OptionChanged(opt.Key()) 129 cmd.api.Message(opt.String()) 130 } 131 132 return nil 133} 134 135// setTabCompleteVerbs sets the tab complete list to the list of option keys. 136func (cmd *Set) setTabCompleteVerbs(lit string) { 137 cmd.setTabComplete(lit, cmd.api.Options().Keys()) 138} 139 140// setTabCompleteOption sets the tab complete list to an option value and a blank value. 141func (cmd *Set) setTabCompleteOption(tok parser.OptionToken) { 142 // Bool options are already handled by the verb completion. 143 if tok.Bool { 144 return 145 } 146 147 // Get the option object. If it is not found, let the verb completion handle this. 148 opt := cmd.api.Options().Get(tok.Key) 149 if opt == nil { 150 return 151 } 152 153 // Don't tab complete option values unless the value is empty. 154 if len(tok.Value) > 0 { 155 return 156 } 157 158 // Return two items: the existing value, and the typed value. 159 cmd.setTabComplete("", []string{ 160 fmt.Sprintf(`="%s"`, opt.StringValue()), 161 "=", 162 }) 163} 164