nix.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /*
  2. Language: Nix
  3. Author: Domen Kožar <domen@dev.si>
  4. Description: Nix functional language
  5. Website: http://nixos.org/nix
  6. Category: system
  7. */
  8. /** @type LanguageFn */
  9. function nix(hljs) {
  10. const regex = hljs.regex;
  11. const KEYWORDS = {
  12. keyword: [
  13. "assert",
  14. "else",
  15. "if",
  16. "in",
  17. "inherit",
  18. "let",
  19. "or",
  20. "rec",
  21. "then",
  22. "with",
  23. ],
  24. literal: [
  25. "true",
  26. "false",
  27. "null",
  28. ],
  29. built_in: [
  30. // toplevel builtins
  31. "abort",
  32. "baseNameOf",
  33. "builtins",
  34. "derivation",
  35. "derivationStrict",
  36. "dirOf",
  37. "fetchGit",
  38. "fetchMercurial",
  39. "fetchTarball",
  40. "fetchTree",
  41. "fromTOML",
  42. "import",
  43. "isNull",
  44. "map",
  45. "placeholder",
  46. "removeAttrs",
  47. "scopedImport",
  48. "throw",
  49. "toString",
  50. ],
  51. };
  52. const BUILTINS = {
  53. scope: 'built_in',
  54. match: regex.either(...[
  55. "abort",
  56. "add",
  57. "addDrvOutputDependencies",
  58. "addErrorContext",
  59. "all",
  60. "any",
  61. "appendContext",
  62. "attrNames",
  63. "attrValues",
  64. "baseNameOf",
  65. "bitAnd",
  66. "bitOr",
  67. "bitXor",
  68. "break",
  69. "builtins",
  70. "catAttrs",
  71. "ceil",
  72. "compareVersions",
  73. "concatLists",
  74. "concatMap",
  75. "concatStringsSep",
  76. "convertHash",
  77. "currentSystem",
  78. "currentTime",
  79. "deepSeq",
  80. "derivation",
  81. "derivationStrict",
  82. "dirOf",
  83. "div",
  84. "elem",
  85. "elemAt",
  86. "false",
  87. "fetchGit",
  88. "fetchMercurial",
  89. "fetchTarball",
  90. "fetchTree",
  91. "fetchurl",
  92. "filter",
  93. "filterSource",
  94. "findFile",
  95. "flakeRefToString",
  96. "floor",
  97. "foldl'",
  98. "fromJSON",
  99. "fromTOML",
  100. "functionArgs",
  101. "genList",
  102. "genericClosure",
  103. "getAttr",
  104. "getContext",
  105. "getEnv",
  106. "getFlake",
  107. "groupBy",
  108. "hasAttr",
  109. "hasContext",
  110. "hashFile",
  111. "hashString",
  112. "head",
  113. "import",
  114. "intersectAttrs",
  115. "isAttrs",
  116. "isBool",
  117. "isFloat",
  118. "isFunction",
  119. "isInt",
  120. "isList",
  121. "isNull",
  122. "isPath",
  123. "isString",
  124. "langVersion",
  125. "length",
  126. "lessThan",
  127. "listToAttrs",
  128. "map",
  129. "mapAttrs",
  130. "match",
  131. "mul",
  132. "nixPath",
  133. "nixVersion",
  134. "null",
  135. "parseDrvName",
  136. "parseFlakeRef",
  137. "partition",
  138. "path",
  139. "pathExists",
  140. "placeholder",
  141. "readDir",
  142. "readFile",
  143. "readFileType",
  144. "removeAttrs",
  145. "replaceStrings",
  146. "scopedImport",
  147. "seq",
  148. "sort",
  149. "split",
  150. "splitVersion",
  151. "storeDir",
  152. "storePath",
  153. "stringLength",
  154. "sub",
  155. "substring",
  156. "tail",
  157. "throw",
  158. "toFile",
  159. "toJSON",
  160. "toPath",
  161. "toString",
  162. "toXML",
  163. "trace",
  164. "traceVerbose",
  165. "true",
  166. "tryEval",
  167. "typeOf",
  168. "unsafeDiscardOutputDependency",
  169. "unsafeDiscardStringContext",
  170. "unsafeGetAttrPos",
  171. "warn",
  172. "zipAttrsWith",
  173. ].map(b => `builtins\\.${b}`)),
  174. relevance: 10,
  175. };
  176. const IDENTIFIER_REGEX = '[A-Za-z_][A-Za-z0-9_\'-]*';
  177. const LOOKUP_PATH = {
  178. scope: 'symbol',
  179. match: new RegExp(`<${IDENTIFIER_REGEX}(/${IDENTIFIER_REGEX})*>`),
  180. };
  181. const PATH_PIECE = "[A-Za-z0-9_\\+\\.-]+";
  182. const PATH = {
  183. scope: 'symbol',
  184. match: new RegExp(`(\\.\\.|\\.|~)?/(${PATH_PIECE})?(/${PATH_PIECE})*(?=[\\s;])`),
  185. };
  186. const OPERATOR_WITHOUT_MINUS_REGEX = regex.either(...[
  187. '==',
  188. '=',
  189. '\\+\\+',
  190. '\\+',
  191. '<=',
  192. '<\\|',
  193. '<',
  194. '>=',
  195. '>',
  196. '->',
  197. '//',
  198. '/',
  199. '!=',
  200. '!',
  201. '\\|\\|',
  202. '\\|>',
  203. '\\?',
  204. '\\*',
  205. '&&',
  206. ]);
  207. const OPERATOR = {
  208. scope: 'operator',
  209. match: regex.concat(OPERATOR_WITHOUT_MINUS_REGEX, /(?!-)/),
  210. relevance: 0,
  211. };
  212. // '-' is being handled by itself to ensure we are able to tell the difference
  213. // between a dash in an identifier and a minus operator
  214. const NUMBER = {
  215. scope: 'number',
  216. match: new RegExp(`${hljs.NUMBER_RE}(?!-)`),
  217. relevance: 0,
  218. };
  219. const MINUS_OPERATOR = {
  220. variants: [
  221. {
  222. scope: 'operator',
  223. beforeMatch: /\s/,
  224. // The (?!>) is used to ensure this doesn't collide with the '->' operator
  225. begin: /-(?!>)/,
  226. },
  227. {
  228. begin: [
  229. new RegExp(`${hljs.NUMBER_RE}`),
  230. /-/,
  231. /(?!>)/,
  232. ],
  233. beginScope: {
  234. 1: 'number',
  235. 2: 'operator'
  236. },
  237. },
  238. {
  239. begin: [
  240. OPERATOR_WITHOUT_MINUS_REGEX,
  241. /-/,
  242. /(?!>)/,
  243. ],
  244. beginScope: {
  245. 1: 'operator',
  246. 2: 'operator'
  247. },
  248. },
  249. ],
  250. relevance: 0,
  251. };
  252. const ATTRS = {
  253. beforeMatch: /(^|\{|;)\s*/,
  254. begin: new RegExp(`${IDENTIFIER_REGEX}(\\.${IDENTIFIER_REGEX})*\\s*=(?!=)`),
  255. returnBegin: true,
  256. relevance: 0,
  257. contains: [
  258. {
  259. scope: 'attr',
  260. match: new RegExp(`${IDENTIFIER_REGEX}(\\.${IDENTIFIER_REGEX})*(?=\\s*=)`),
  261. relevance: 0.2,
  262. }
  263. ],
  264. };
  265. const NORMAL_ESCAPED_DOLLAR = {
  266. scope: 'char.escape',
  267. match: /\\\$/,
  268. };
  269. const INDENTED_ESCAPED_DOLLAR = {
  270. scope: 'char.escape',
  271. match: /''\$/,
  272. };
  273. const ANTIQUOTE = {
  274. scope: 'subst',
  275. begin: /\$\{/,
  276. end: /\}/,
  277. keywords: KEYWORDS,
  278. };
  279. const ESCAPED_DOUBLEQUOTE = {
  280. scope: 'char.escape',
  281. match: /'''/,
  282. };
  283. const ESCAPED_LITERAL = {
  284. scope: 'char.escape',
  285. match: /\\(?!\$)./,
  286. };
  287. const STRING = {
  288. scope: 'string',
  289. variants: [
  290. {
  291. begin: "''",
  292. end: "''",
  293. contains: [
  294. INDENTED_ESCAPED_DOLLAR,
  295. ANTIQUOTE,
  296. ESCAPED_DOUBLEQUOTE,
  297. ESCAPED_LITERAL,
  298. ],
  299. },
  300. {
  301. begin: '"',
  302. end: '"',
  303. contains: [
  304. NORMAL_ESCAPED_DOLLAR,
  305. ANTIQUOTE,
  306. ESCAPED_LITERAL,
  307. ],
  308. },
  309. ],
  310. };
  311. const FUNCTION_PARAMS = {
  312. scope: 'params',
  313. match: new RegExp(`${IDENTIFIER_REGEX}\\s*:(?=\\s)`),
  314. };
  315. const EXPRESSIONS = [
  316. NUMBER,
  317. hljs.HASH_COMMENT_MODE,
  318. hljs.C_BLOCK_COMMENT_MODE,
  319. hljs.COMMENT(
  320. /\/\*\*(?!\/)/,
  321. /\*\//,
  322. {
  323. subLanguage: 'markdown',
  324. relevance: 0
  325. }
  326. ),
  327. BUILTINS,
  328. STRING,
  329. LOOKUP_PATH,
  330. PATH,
  331. FUNCTION_PARAMS,
  332. ATTRS,
  333. MINUS_OPERATOR,
  334. OPERATOR,
  335. ];
  336. ANTIQUOTE.contains = EXPRESSIONS;
  337. const REPL = [
  338. {
  339. scope: 'meta.prompt',
  340. match: /^nix-repl>(?=\s)/,
  341. relevance: 10,
  342. },
  343. {
  344. scope: 'meta',
  345. beforeMatch: /\s+/,
  346. begin: /:([a-z]+|\?)/,
  347. },
  348. ];
  349. return {
  350. name: 'Nix',
  351. aliases: [ "nixos" ],
  352. keywords: KEYWORDS,
  353. contains: EXPRESSIONS.concat(REPL),
  354. };
  355. }
  356. export { nix as default };