import {parser as cshParser} from "./csh-parser.js" import { LRLanguage, indentNodeProp, foldNodeProp, foldInside, delimitedIndent, continuedIndent } from "@codemirror/language" import {styleTags, tags as t} from "@lezer/highlight" import {completeFromList, CompletionSource} from "@codemirror/autocomplete" /// A language provider for the C shell, based on the Lezer grammar in /// `csh.grammar`. export const cshLanguage = LRLanguage.define({ name: "csh", parser: cshParser.configure({ props: [ indentNodeProp.add({ Group: delimitedIndent({closing: "}"}), Subshell: delimitedIndent({closing: ")"}), WhileStatement: delimitedIndent({closing: "end"}), ForeachStatement: delimitedIndent({closing: "end"}), SwitchStatement: delimitedIndent({closing: "endsw"}), // `then` sits on the same line as the `if` it belongs to, so what is // indented is everything after that line. IfStatement: continuedIndent({except: /^\s*(endif|else)\b/}), CaseClause: continuedIndent({except: /^\s*(case|default|endsw)\b/}) }), foldNodeProp.add({ Group: foldInside, Subshell: foldInside, WhileStatement: foldInside, ForeachStatement: foldInside, SwitchStatement: foldInside }), styleTags({ "if then else endif": t.controlKeyword, "while foreach end repeat": t.controlKeyword, "switch case default endsw": t.controlKeyword, "set setenv unset alias": t.definitionKeyword, Label: t.labelName, Comment: t.lineComment, String: t.string, RawString: t.string, HeredocBody: t.special(t.string), Escape: t.escape, LineContinuation: t.escape, Glob: t.special(t.string), "SimpleCommand/CommandName ShortCommand/CommandName": t.function(t.variableName), VariableName: t.definition(t.variableName), SimpleExpansion: t.variableName, ParamName: t.variableName, ParamOp: t.operator, Dollar: t.variableName, // `:q` and its relatives say what to do with the word before them, // which is what an operator is. Modifier: t.operator, Number: t.number, RedirectOp: t.controlOperator, "PipeOp Background": t.controlOperator, "AndOp OrOp NotOp": t.logicOperator, "AddOp MulOp": t.arithmeticOperator, "ShiftOp BitAndOp BitXorOp BitOrOp BitNotOp": t.bitwiseOperator, "RelateOp EqualOp MatchOp": t.compareOperator, FileTestOp: t.operatorKeyword, AssignOp: t.definitionOperator, IncDecOp: t.updateOperator, // `@` is a command rather than a symbol, and reads as the arithmetic // it introduces. "@": t.definitionKeyword, '"\\""': t.string, '"${" "}" "{"': t.brace, '"(" ")"': t.paren, '"[" "]"': t.squareBracket, '";"': t.separator, Terminator: t.separator }) ] }), languageData: { commentTokens: {line: "#"}, closeBrackets: {brackets: ["(", "[", "{", "'", '"', "`"]}, indentOnInput: /^\s*(endif|end|endsw|else|breaksw|\}|\))$/ } }) // csh has no functions, so its builtins are the whole of what it can be told // to do on its own, and there are few enough to name. const builtins = [ "alias", "bg", "break", "breaksw", "case", "cd", "chdir", "continue", "default", "dirs", "echo", "else", "end", "endif", "endsw", "eval", "exec", "exit", "fg", "foreach", "glob", "goto", "hashstat", "history", "if", "jobs", "kill", "limit", "login", "logout", "nice", "nohup", "notify", "onintr", "popd", "pushd", "rehash", "repeat", "set", "setenv", "shift", "source", "stop", "suspend", "switch", "then", "time", "umask", "unalias", "unhash", "unlimit", "unset", "unsetenv", "wait", "while", "@" ].map(label => ({label, type: "keyword"})) // The variables csh sets for you. `status` is where the last command's exit // code lands, which is `$?` everywhere else. const specials = [ "argv", "cdpath", "cwd", "echo", "histchars", "history", "home", "ignoreeof", "mail", "noclobber", "noglob", "nonomatch", "notify", "path", "prompt", "savehist", "shell", "status", "term", "time", "user", "verbose" ].map(label => ({label, type: "constant"})) /// Autocompletion for the C shell's builtins and the variables it sets. export const cshCompletion: CompletionSource = completeFromList([...builtins, ...specials]) export {cshParser}