package syntax // bashKeywords are the words that structure a shell script, and the builtins // worth telling apart from a command on the PATH. // // The set is what sh, bash and zsh share: a script is coloured the same // whichever of them it names in its shebang, because the differences between // them are not what a highlighter can usefully show. var bashKeywords = map[string]Class{ "if": ClassKeyword, "then": ClassKeyword, "elif": ClassKeyword, "else": ClassKeyword, "fi": ClassKeyword, "for": ClassKeyword, "while": ClassKeyword, "until": ClassKeyword, "do": ClassKeyword, "done": ClassKeyword, "case": ClassKeyword, "esac": ClassKeyword, "in": ClassKeyword, "function": ClassKeyword, "select": ClassKeyword, "time": ClassKeyword, "return": ClassKeyword, "break": ClassKeyword, "continue": ClassKeyword, "exit": ClassKeyword, "true": ClassConstant, "false": ClassConstant, "echo": ClassBuiltin, "printf": ClassBuiltin, "read": ClassBuiltin, "cd": ClassBuiltin, "export": ClassBuiltin, "local": ClassBuiltin, "declare": ClassBuiltin, "readonly": ClassBuiltin, "unset": ClassBuiltin, "shift": ClassBuiltin, "set": ClassBuiltin, "test": ClassBuiltin, "source": ClassBuiltin, "eval": ClassBuiltin, "exec": ClassBuiltin, "trap": ClassBuiltin, "wait": ClassBuiltin, "command": ClassBuiltin, } // highlightBash colours a shell script, one slice of spans per line. // // Heredocs are deliberately not recognised. Following `< 0 { switch s.line[s.pos] { case open: depth++ case close: depth-- } s.pos++ } s.Emit(start, s.pos, ClassBuiltin) } // takeWord colours a bare word: a keyword, a builtin, a variable being // assigned, the command being run, or one of its arguments. func (s *bashScanner) takeWord() { start := s.pos for !s.AtEnd() && (IsWordRune(s.line[s.pos]) || s.line[s.pos] == '-') { s.pos++ } class := s.classOf(string(s.line[start:s.pos])) s.seenWord = true s.Emit(start, s.pos, class) } // classOf decides what a bare word is. func (s *bashScanner) classOf(word string) Class { if class, ok := bashKeywords[word]; ok { return class } if !s.AtEnd() && s.line[s.pos] == '=' { return ClassIdentifier // NAME=value } if s.seenWord { return ClassIdentifier // an argument } return ClassFunction // the command being run }