{"_id":"@codincod/codemirror-lang-ocaml","_rev":"3-20030a5edd5bb7bd0aecaf7224e76290","name":"@codincod/codemirror-lang-ocaml","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@codincod/codemirror-lang-ocaml","version":"0.1.0","keywords":["codemirror","code","editor","ml","ocaml"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-ocaml@0.1.0","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"e73a72de624696d98600d1f99db1e9d9e55c37cc","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-ocaml/-/codemirror-lang-ocaml-0.1.0.tgz","fileCount":14,"integrity":"sha512-Is15Mj6aAD0F25sIB4wQzEwvmct3Px4u3mXHqX3t8aHu1QmTVJaM7BnMa0logjwjTMXGNx6wwugDjediAbcUXg==","signatures":[{"sig":"MEYCIQCsSBV2m2O4CYlgkgzgxe0sq6AK3LrTyGW3YVtUwth4RgIhAL8OxLz0Jkg2DYMIRmSj+oYxSwbYyxa+m5D5jGHp2tFU","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":475477},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"gitHead":"7fd262c2297f8a9f07d40a0e2f1b96466a67e558","scripts":{"test":"npm run build && node --test \"test/*.test.ts\"","build":"rm -rf dist && lezer-generator src/ocaml.grammar -o src/parser.js && cm-buildhelper src/ocaml.ts","corpus":"npm run build && node test/corpus.ts","prepare":"npm run build","build-debug":"lezer-generator src/ocaml.grammar --names -o src/parser.js && cm-buildhelper src/ocaml.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"git+https://codeberg.org/reeven/codemirror-lang-ocaml.git","type":"git"},"_npmVersion":"11.6.2","description":"OCaml language support for the CodeMirror code editor","directories":{},"sideEffects":false,"_nodeVersion":"25.0.0","dependencies":{"@lezer/lr":"^1.0.0","@lezer/common":"^1.0.0","@lezer/highlight":"^1.0.0","@codemirror/language":"^6.0.0","@codemirror/autocomplete":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0","@lezer/generator":"^1.7.0","@codemirror/state":"^6.7.1","@codemirror/buildhelper":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/codemirror-lang-ocaml_0.1.0_1785950188276_0.15654616974924762","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@codincod/codemirror-lang-ocaml","version":"0.1.1","keywords":["codemirror","code","editor","ml","ocaml"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-ocaml@0.1.1","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"8ca2f810f4ede25225616cbc0780905709003c58","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-ocaml/-/codemirror-lang-ocaml-0.1.1.tgz","fileCount":14,"integrity":"sha512-208eFMwcYmfKepdKsS+bGeK290YPwf5ZSh8wrtinmk73TDmVflLcqPMRyiodID4rhDolsW0WmCpVpHVxpHHZAw==","signatures":[{"sig":"MEQCIBqpbcWsVsQAQlPvLGL2AGAyenQMDXFwoQiDVyyOB1rVAiBa/n3vM5GMdfGTCpLxMAt91xWf+N0LLXr0HDvlU1wJ9w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":475600},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"gitHead":"d2a99eef4366a5d0ed0653024ed94e368b305f1b","scripts":{"gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group top --follow","test":"npm run build && node --test \"test/*.test.ts\"","build":"rm -rf dist && lezer-generator src/ocaml.grammar -o src/parser.js && cm-buildhelper src/ocaml.ts","corpus":"npm run build && node ../lezer-survey/src/cli.ts measure --group top --follow","prepare":"npm run build","build-debug":"lezer-generator src/ocaml.grammar --names -o src/parser.js && cm-buildhelper src/ocaml.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"https://codeberg.org/reeven/codemirror-lang-ocaml","type":"git"},"_npmVersion":"11.6.2","description":"OCaml language support for the CodeMirror code editor","directories":{},"sideEffects":false,"_nodeVersion":"25.0.0","dependencies":{"@lezer/lr":"^1.0.0","@lezer/common":"^1.0.0","@lezer/highlight":"^1.0.0","@codemirror/language":"^6.0.0","@codemirror/autocomplete":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0","@lezer/generator":"^1.7.0","@codemirror/state":"^6.7.1","@codemirror/buildhelper":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/codemirror-lang-ocaml_0.1.1_1785970686253_0.6283044492115202","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@codincod/codemirror-lang-ocaml","version":"0.1.2","description":"OCaml language support for the CodeMirror code editor","keywords":["codemirror","code","editor","ml","ocaml"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","type":"module","main":"dist/index.cjs","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"module":"dist/index.js","types":"dist/index.d.ts","sideEffects":false,"scripts":{"build":"rm -rf dist && lezer-generator src/ocaml.grammar -o src/parser.js && cm-buildhelper src/ocaml.ts","build-debug":"lezer-generator src/ocaml.grammar --names -o src/parser.js && cm-buildhelper src/ocaml.ts","prepare":"npm run build","test":"npm run build && node --test \"test/*.test.ts\"","measure":"npm run build && node ../lezer-survey/src/cli.ts measure --group top --follow","fuzz":"npm run build && node ../lezer-survey/src/cli.ts fuzz","gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group top --follow"},"dependencies":{"@codemirror/autocomplete":"^6.0.0","@codemirror/language":"^6.0.0","@lezer/highlight":"^1.0.0","@lezer/lr":"^1.0.0"},"devDependencies":{"@codemirror/buildhelper":"^1.0.2","@codemirror/state":"^6.7.1","@lezer/generator":"^1.7.0","typescript":"^5.6.0"},"homepage":"https://codincod.com/","repository":{"type":"git","url":"https://codeberg.org/reeven/codemirror-lang-ocaml"},"gitHead":"4b6742a51d5694d1cf6aae15f417e6d5c6fee50b","_id":"@codincod/codemirror-lang-ocaml@0.1.2","_nodeVersion":"25.0.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-RoCdnYcTkSUJv1oA6KLLERSp5Vdpw2sPZyTg0wG3Uab+ZSYEpFwd88KFcGwNtV6UdgfEP16VGO9WFHpKloA/oQ==","shasum":"656384a1a02dda73f0e2fb8149b3e593d7eecec9","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-ocaml/-/codemirror-lang-ocaml-0.1.2.tgz","fileCount":14,"unpackedSize":481112,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCmaTyeGGX4p095dBV8BKNU8B77F13ghIK1jlnQh/ZOxgIhAL3KGWnXHN33qPDTS1WiUNGHwf0xN+wi++ej5tM8ZGN1"}]},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"directories":{},"maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/codemirror-lang-ocaml_0.1.2_1786760102725_0.716517355147561"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T17:16:28.068Z","modified":"2026-08-15T02:15:03.108Z","0.1.0":"2026-08-05T17:16:28.495Z","0.1.1":"2026-08-05T22:58:06.435Z","0.1.2":"2026-08-15T02:15:02.880Z"},"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","homepage":"https://codincod.com/","keywords":["codemirror","code","editor","ml","ocaml"],"repository":{"type":"git","url":"https://codeberg.org/reeven/codemirror-lang-ocaml"},"description":"OCaml language support for the CodeMirror code editor","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"readme":"# @codincod/codemirror-lang-ocaml\n\nThis package implements [OCaml](https://ocaml.org/) language support for the\n[CodeMirror](https://codemirror.net/) code editor: a\n[Lezer](https://lezer.codemirror.net/) grammar, highlighting, indentation,\nfolding and completion for the standard library.\n\nThe grammar is written from scratch. OCaml has never had a Lezer grammar, and\nwhat a CodeMirror 6 editor could reach for until now was the `mllike` stream\nmode in `@codemirror/legacy-modes`, which reads one keyword list for OCaml,\nF# and Standard ML and has no idea where a definition ends.\n\nWritten in part for [CodinCod](https://codincod.com/), a competitive coding\nplatform, where it colours the editor people solve puzzles in.\n\nThis code is released under an\n[MIT license](https://codeberg.org/reeven/codemirror-lang-ocaml/src/branch/master/LICENSE).\n\n## Usage\n\n```javascript\nimport {EditorView, basicSetup} from \"codemirror\"\nimport {ocaml} from \"@codincod/codemirror-lang-ocaml\"\n\nconst view = new EditorView({\n  parent: document.body,\n  doc: `type shape =\n  | Circle of float\n  | Rect of {width : float; height : float}\n\nlet area = function\n  | Circle r -> Float.pi *. r *. r\n  | Rect {width; height} -> width *. height\n\nlet () =\n  [Circle 1.0; Rect {width = 2.0; height = 3.0}]\n  |> List.map area\n  |> List.iter (Printf.printf \"%.2f\\\\n\")\n`,\n  extensions: [basicSetup, ocaml()]\n})\n```\n\n## What it reads\n\nMeasured against the stream mode it replaces, over 1011 files of OCaml from\nCodinCod's language-guessing pool: the stream mode carries a highlighting tag on\n**90.34%** of the text in 8 colours, with 54.99% of it in the busiest one. This\npackage tags **97.76%** in 15 colours, with 37.22% in the busiest. Over the same\n1011 files, **88.03%** parse with no error node.\n\nOver the OCaml compiler and standard library, Jane Street's `core` and the dune\nbuild system, 2996 files and 17.98 MiB, **99.00%** parse with no error node.\n\nOver 20000 files of published OCaml, 144 MiB out of a thousand repositories,\n**98.49%**. The denominator there is the compiler's: `ocamlc -stop-after\nparsing` turns down 1137 of those files, most of them HOL Light and camlp4\nwriting terms between backticks, which is a preprocessor's syntax rather than\nthe language's. Of the 18863 files it does parse, 285 still hold an error node,\nand every one of those is a thing to fix.\n\nFour things took most of the work.\n\n* **A comment holds a language.** `(* (* *) *)` nests, so the depth has to be\n  counted rather than matched. A string inside a comment is read as a string\n  and a character literal as a character, which is OCaml's own rule and not a\n  nicety: `(* the \"*)\" case *)` and `(* '\"' *)` both end where their author\n  meant them to, and a tokenizer that stopped at the first star-paren would\n  colour the rest of the file as a comment.\n* **An apostrophe is three things.** `'a` is a type variable, `'a'` is a\n  character, and `x'` is an ordinary name. The name is read first so the quote\n  in it never reaches the tokenizer that would open a literal, the character is\n  tried next because it is the only one with a closing quote, and what is left\n  is a type variable. OCaml lexes the quote and the name as two tokens, so\n  `' a` is one variable and is read as one.\n* **The semicolon that ends a sequence.** `begin f (); g (); end` may dangle\n  its last separator, and so may a record, a list and the arm of a clause. The\n  grammar cannot see that the statement after the separator is missing until it\n  has read the token that closes the block, so the tokenizer decides: a\n  semicolon standing in front of a closer, or in front of a word that cannot\n  open an expression, is a different token from the one that separates. Spelling\n  it in the grammar instead cost two and a half points of the corpus, because\n  the parser weighed an empty statement against the bar that ends a clause and\n  got the bar wrong in every file that wrote one.\n* **A capitalised word is a module or a constructor.** OCaml spells the two the\n  same, and the dot that follows one of them is the whole of the difference.\n  That dot arrives too late for the grammar, so the tokenizer reads it: a\n  capitalised word with a dot against it is a module segment and can be nothing\n  else. `Some x` is a constructor, `Set.empty` is a module, and a theme can\n  colour them apart.\n\nSmaller things worth naming.\n\n* **Operators are not declared.** `>>=`, `|>` and `+.` are ordinary names in\n  OCaml and a library may write its own, so they are classified by their first\n  character rather than listed. A leading `!`, `~` or `?` binds tighter than\n  application, which is what hands `f !r` what the reference holds rather than\n  applying `f` to an exclamation mark.\n* **Binding operators are one word.** `let*` and `and+` are a keyword with an\n  operator against it, and reading the two apart would leave the grammar with a\n  `let` whose name was an operator. A percent is deliberately not one of the\n  characters they may be spelled with: `let%lwt` hands the binding to a\n  preprocessor and is a `let` with an extension after it.\n* **Attributes and extensions are read whole.** `[@inline]`, `[@@deriving]`,\n  `[@@@warning]`, `[%blob]` and `[%%if]` reach the tree with their name apart\n  from their payload, and the payload keeps whatever colour the code inside it\n  earned. The name may be a reserved word, because the name comes first and\n  nothing else can stand there.\n* **Quoted strings take no escapes.** `{|raw|}` and `{sql|select 1|sql}` end at\n  the delimiter they opened with and at nothing else.\n\n## What it does not read\n\n* **Labelled tuples.** `(~x, y)` as a pattern and `(x:int * y:string)` as a\n  type are OCaml 5.4 syntax that the compiler's own sources have started using.\n  The parenthesised form collides with the label a function type already puts in\n  the same place, and it is most of the gap in the 99.00% above.\n* **An arm mixing a value pattern and an exception pattern.** `| None\n  | exception _ ->` is legal and rare. Either kind alone works, and so does\n  `| exception (A | B) ->`.\n* **Indexing operators.** `let (.%()) map e = ...` defines one, and the name is\n  read as an operator in brackets rather than as the whole of `.%()`.\n* **A record path that starts lowercase and continues capitalised.**\n  `{flag.Flag.Internal.count with ...}` opens a module halfway along a field\n  path.\n* **`M.[...]` in a pattern.** A module opened for the length of a pattern works\n  with parentheses, and a record works with braces; the list form is the one\n  shape that would make every list pattern ambiguous with a constructor applied\n  to one.\n* **A tree that groups `a :: b as x` and `a | b, c` the way the manual does.**\n  Both parse. OCaml binds `as` loosest and `|` looser than `,`; this grammar\n  binds the tuple loosest, which changes how the pattern nests and nothing else.\n\n## Measuring it yourself\n\n```bash\nnpm run measure -- path/to/ocaml/files\nnpm run gaps -- path/to/ocaml/files\n```\n\nNo corpus ships with the package, because none of that code is ours to\nredistribute. Point it at your own; it counts files with no error node and\ngroups them by directory, so a project that drags the total down names itself.\n\nRun the compiler over the same directory before believing the number.\n`ocamlc -stop-after parsing` is the honest denominator: a file it turns down is\na file this grammar cannot be charged for, and a directory of OCaml gathered by\nextension holds more of those than anybody expects.\n","readmeFilename":"README.md"}