Skip to content

Commit 683ef7b

Browse files
committed
Add dedicated ReScript mode with improved syntax highlighting
1 parent 55e7827 commit 683ef7b

File tree

5 files changed

+160
-21
lines changed

5 files changed

+160
-21
lines changed

plugins/cm-rescript-mode.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// CodeMirror, copyright (c) by Marijn Haverbeke and others
2+
// Distributed under an MIT license: https://codemirror.net/LICENSE
3+
//
4+
// Originally derived from the CodeMirror Rust plugin:
5+
// https://github.com/codemirror/CodeMirror/blob/master/mode/rust/rust.js
6+
7+
import "codemirror/addon/mode/simple";
8+
import CodeMirror from "codemirror/lib/codemirror";
9+
10+
CodeMirror.defineSimpleMode("rescript", {
11+
start: [
12+
// string and byte string
13+
{ regex: /b?"/, token: "string", next: "string" },
14+
// raw string and raw byte string
15+
//{ regex: /b?r"/, token: "string", next: "string_raw" },
16+
//{ regex: /b?r#+"/, token: "string", next: "string_raw_hash" },
17+
// string and byte string
18+
{ regex: /b?"/, token: "string", next: "string" },
19+
{ regex: /(\:\s*)(.*)(\s*=)\s/, token: [null, "type-annotation", null]},
20+
// interpolation string
21+
{ regex: /b?`/, token: "string", next: "string_interpolation" },
22+
// character
23+
{
24+
regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/,
25+
token: "string-2"
26+
},
27+
// byte
28+
{ regex: /b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/, token: "string-2" },
29+
{
30+
regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/,
31+
token: "number"
32+
},
33+
{
34+
regex: /(let|type)(\s+rec)?(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/,
35+
token: ["keyword", "keyword2", null, "def"]
36+
},
37+
{
38+
regex: /(?:switch|module|as|else|external|for|if|in|mod|ref|type|while|open|open\!)\b/,
39+
token: "keyword"
40+
},
41+
{
42+
regex: /(?:rec)\b/,
43+
token: "keyword2"
44+
},
45+
{
46+
regex: /\b(?:char|bool|option|int|string)\b/,
47+
token: "atom"
48+
},
49+
{ regex: /\b(?:true|false)\b/, token: "builtin" },
50+
{
51+
regex: /\b(fun)(\s+)([a-zA-Z_\|][a-zA-Z0-9_]*)/,
52+
token: ["keyword", null, "def"]
53+
},
54+
{
55+
regex: /\b([A-Z][a-zA-Z0-9_]*)(\.)/,
56+
token: ["module", null]
57+
},
58+
{
59+
regex: /\b([A-Z][a-zA-Z0-9_]*)/,
60+
token: ["variant-constructor", null, null, null]
61+
},
62+
//polyvar
63+
{ regex: /#[a-zA-Z0-9_"]*/, token: "variant-constructor"},
64+
{ regex: /@.[\w\.\(\)]*/, token: "decorator" },
65+
{ regex: /#!?\[.*\]/, token: "meta" },
66+
{ regex: /\/\/.*/, token: "comment" },
67+
{ regex: /\/\*/, token: "comment", next: "comment" },
68+
{ regex: /[-+\/*=<>!\|]+/, token: "operator" },
69+
{ regex: /[a-zA-Z_]\w*!/, token: "variable-3" },
70+
{ regex: /[a-zA-Z_]\w*/, token: "variable" },
71+
{ regex: /[\{\[\(]/, indent: true },
72+
{ regex: /[\}\]\)]/, dedent: true }
73+
],
74+
variantConstructor: [
75+
76+
],
77+
string: [
78+
{ regex: /"/, token: "string", next: "start" },
79+
{ regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string" }
80+
],
81+
//string_raw: [
82+
//{ regex: /"/, token: "string", next: "start" },
83+
//{ regex: /[^"]*/, token: "string" }
84+
//],
85+
//string_raw_hash: [
86+
//{ regex: /"#+/, token: "string", next: "start" },
87+
//{ regex: /(?:[^"]|"(?!#))*/, token: "string" }
88+
//],
89+
string_interpolation: [
90+
{ regex: /`/, token: "string", next: "start" },
91+
{ regex: /[^`]*/, token: "string" }
92+
],
93+
comment: [
94+
{ regex: /.*?\*\//, token: "comment", next: "start" },
95+
{ regex: /.*/, token: "comment" }
96+
],
97+
meta: {
98+
dontIndentStates: ["comment"],
99+
electricInput: /^\s*\}$/,
100+
blockCommentStart: "/*",
101+
blockCommentEnd: "*/",
102+
lineComment: "//",
103+
fold: "brace"
104+
}
105+
});
106+
107+
CodeMirror.defineMIME("text/x-reasonsrc", "reason");
108+
CodeMirror.defineMIME("text/reason", "reason");

src/Playground.js

Lines changed: 18 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Playground.res

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ module ResultPane = {
424424
"The compiler bundle API returned a result that couldn't be interpreted. Please open an issue on our ",
425425
)}
426426
<Markdown.A
427-
target="_blank" href="https://github.com/reason-association/rescript-lang.org/issues">
427+
target="_blank" href="https://github.com/rescript-association/rescript-lang.org/issues">
428428
{React.string("issue tracker")}
429429
</Markdown.A>
430430
{React.string(".")}
@@ -1397,14 +1397,12 @@ module OutputPanel = {
13971397
}
13981398
}
13991399

1400-
let initialResContent = j`// Please note:
1401-
// ---
1402-
// The Playground is still a work in progress
1403-
// ReScript / old Reason syntax should parse just
1404-
// fine (go to the "Settings" panel for toggling syntax).
1405-
//
1406-
// Feel free to play around and compile some
1407-
// ReScript code!
1400+
let initialResContent = j`
1401+
1402+
let world = "world"
1403+
let a = \`
1404+
Hello ${"${world}"}
1405+
\`
14081406
14091407
module Button = {
14101408
@react.component
@@ -1546,6 +1544,12 @@ let default = () => {
15461544
| _ => []
15471545
}
15481546

1547+
let mode = switch compilerState {
1548+
| Ready({ targetLang: Reason }) => "reason"
1549+
| Ready({ targetLang: Res }) => "rescript"
1550+
| _ => "rescript"
1551+
}
1552+
15491553
<>
15501554
<Meta title="ReScript Playground" description="Try ReScript in the browser" />
15511555
<Next.Head>
@@ -1572,7 +1576,7 @@ let default = () => {
15721576
className="w-full py-4"
15731577
minHeight="calc(100vh - 10rem)"
15741578
maxHeight="calc(100vh - 10rem)"
1575-
mode="reason"
1579+
mode
15761580
errors=cmErrors
15771581
value={editorCode.current}
15781582
onChange={value => {

src/components/CodeMirror.js

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/CodeMirror.res

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,19 @@
88
This file is providing the core functionality and logic of our CodeMirror instances.
99
*/
1010

11-
%%raw(
12-
`
11+
%%raw(`
1312
import "codemirror/lib/codemirror.css";
1413
import "styles/cm.css";
1514
1615
if (typeof window !== "undefined" && typeof window.navigator !== "undefined") {
1716
require("codemirror/mode/javascript/javascript");
1817
require("codemirror/addon/scroll/simplescrollbars");
18+
require("plugins/cm-rescript-mode");
1919
require("plugins/cm-reason-mode");
2020
}
21-
`
22-
)
21+
`)
2322

24-
let useWindowWidth: unit => int = %raw(
25-
j` () => {
23+
let useWindowWidth: unit => int = %raw(j` () => {
2624
const isClient = typeof window === 'object';
2725
2826
function getSize() {
@@ -58,8 +56,7 @@ let useWindowWidth: unit => int = %raw(
5856
}
5957
return null;
6058
}
61-
`
62-
)
59+
`)
6360

6461
/* The module for interacting with the imperative CodeMirror API */
6562
module CM = {
@@ -90,6 +87,9 @@ module CM = {
9087
@bs.module("codemirror")
9188
external fromTextArea: (Dom.element, Options.t) => t = "fromTextArea"
9289

90+
@bs.send
91+
external setMode: (t, @bs.as("mode") _, string) => unit = "setOption"
92+
9393
@bs.send
9494
external getScrollerElement: t => Dom.element = "getScrollerElement"
9595

@@ -466,6 +466,12 @@ let make = // props relevant for the react wrapper
466466
None
467467
}, [errorsFingerprint])
468468

469+
React.useEffect1(() => {
470+
let cm = Belt.Option.getExn(cmRef.current);
471+
cm->CM.setMode(mode)
472+
None
473+
}, [mode])
474+
469475
/*
470476
Needed in case the className visually hides / shows
471477
a codemirror instance, or the window has been resized.

0 commit comments

Comments
 (0)