aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Nicolas Veigel <art.jnveigel@gmail.com>2024-03-16 04:16:27 +0100
committerLouis Pilfold <louis@lpil.uk>2024-03-26 10:31:25 +0000
commitb17fbfc163dfef74d66a2fceaf00f4c621462ade (patch)
tree8ce160ab0c36f8a2fb2d659fa220aaaf1ebac7da
parent2caba36a9adf0ee24417a09416e81610352e67f6 (diff)
downloadtour-b17fbfc163dfef74d66a2fceaf00f4c621462ade.tar.gz
tour-b17fbfc163dfef74d66a2fceaf00f4c621462ade.zip
feat: highlight js gleam lang definition
-rw-r--r--static/highlight/highlight-gleam.js119
-rw-r--r--static/js/highlight/highlight-gleam.js269
-rw-r--r--static/js/highlight/regexes.js2
3 files changed, 270 insertions, 120 deletions
diff --git a/static/highlight/highlight-gleam.js b/static/highlight/highlight-gleam.js
deleted file mode 100644
index 807d9ec..0000000
--- a/static/highlight/highlight-gleam.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/**
- * Registers gleam as a language
- *
- * Largely based off https://github.com/gleam-lang/website/blob/main/javascript/highlightjs-gleam.js
- * Edited to work with minified hightlightjs core v11 (module)
- */
-
-import hljs from "./highlight.core.min.js";
-
-hljs.registerLanguage("gleam", function (hljs) {
- const KEYWORDS = {
- className: "keyword",
- beginKeywords:
- "as assert auto case const delegate derive echo else fn if " +
- "implement import let macro opaque panic pub test todo type use",
- };
- const STRING = {
- className: "string",
- variants: [{ begin: /"/, end: /"/ }],
- contains: [hljs.BACKSLASH_ESCAPE],
- relevance: 0,
- };
- const NAME = {
- className: "variable",
- begin: "\\b[a-z][a-z0-9_]*\\b",
- relevance: 0,
- };
- const DISCARD_NAME = {
- className: "comment",
- begin: "\\b_[a-z][a-z0-9_]*\\b",
- relevance: 0,
- };
- const NUMBER = {
- className: "number",
- variants: [
- {
- // binary
- begin: "\\b0[bB](?:_?[01]+)+",
- },
- {
- // octal
- begin: "\\b0[oO](?:_?[0-7]+)+",
- },
- {
- // hex
- begin: "\\b0[xX](?:_?[0-9a-fA-F]+)+",
- },
- {
- // dec, float
- begin: "\\b\\d(?:_?\\d+)*(?:\\.(?:\\d(?:_?\\d+)*)*)?",
- },
- ],
- relevance: 0,
- };
-
- return {
- name: "Gleam",
- aliases: ["gleam"],
- contains: [
- hljs.C_LINE_COMMENT_MODE,
- STRING,
- {
- // bit array
- begin: "<<",
- end: ">>",
- contains: [
- {
- className: "keyword",
- beginKeywords:
- "binary bits bytes int float bit_string bit_array bits utf8 utf16 " +
- "utf32 utf8_codepoint utf16_codepoint utf32_codepoint signed " +
- "unsigned big little native unit size",
- },
- KEYWORDS,
- STRING,
- NAME,
- DISCARD_NAME,
- NUMBER,
- ],
- relevance: 10,
- },
- {
- className: "function",
- beginKeywords: "fn",
- end: "\\(",
- excludeEnd: true,
- contains: [
- {
- className: "title",
- begin: "[a-z][a-z0-9_]*\\w*",
- relevance: 0,
- },
- ],
- },
- {
- className: "attribute",
- begin: "@",
- end: "\\(",
- excludeEnd: true,
- },
- KEYWORDS,
- {
- // Type names and constructors
- className: "title",
- begin: "\\b[A-Z][A-Za-z0-9]*\\b",
- relevance: 0,
- },
- {
- className: "operator",
- begin: "[+\\-*/%!=<>&|.]+",
- relevance: 0,
- },
- NAME,
- DISCARD_NAME,
- NUMBER,
- ],
- };
-});
-hljs.highlightAll(); \ No newline at end of file
diff --git a/static/js/highlight/highlight-gleam.js b/static/js/highlight/highlight-gleam.js
new file mode 100644
index 0000000..47362a4
--- /dev/null
+++ b/static/js/highlight/highlight-gleam.js
@@ -0,0 +1,269 @@
+/**
+ * Registers gleam as a language
+ *
+ * Based off https://github.com/gleam-lang/website/blob/main/javascript/highlightjs-gleam.js
+ * Edited to work with minified hightlightjs core v11 (module) & match more of the syntax
+ */
+
+import hljs from "./highlight.core.min.js";
+import * as regexes from "./regexes.js";
+
+/**
+ * Copies an object to prevent prototype pollution
+ * @template {object} TObject - the object's structure
+ * @param {TObject} obj - The source object to copy
+ * @returns {TObject} - A shallow copy of the source object
+ */
+const cp = (obj) => ({ ...obj });
+
+
+// Define operators and keywords to highlight gleam code without spawning an editor
+const GLEAM_OPERATORS = [
+ "<<", ">>", "<-", "->", "|>", "<>", "..",
+ "<=", "<=.", ">=", ">=.", "==", "==.", "%", "%.",
+ "!=", "!=.", '<', "<.", ">", ">.", "&&", "||",
+ "+", "+.", "-", "-.", "/", "/.", "*", "*.", "=",
+]
+const GLEAM_KEYWORDS = [
+ "as",
+ "assert",
+ "auto",
+ "case",
+ "const",
+ "delegate",
+ "derive",
+ "echo",
+ "else",
+ "fn",
+ "if",
+ "implement",
+ "import",
+ "let",
+ "macro",
+ "opaque",
+ "panic",
+ "pub",
+ "test",
+ "todo",
+ "type",
+ "use",
+];
+
+/**
+ * HLJS modes
+ * Glorified regular expressions used to target & highlight code snippets
+ *
+ * Ordered by `relevance` -> more or less translates to parsing order / priority
+ * https://highlightjs.readthedocs.io/en/stable/language-guide.html#relevance
+ *
+ * Their `scope` maps to 1 or more css class
+ * https://highlightjs.readthedocs.io/en/stable/css-classes-reference.html
+ */
+
+// Relevance 0
+
+const PUNCTUATION = {
+ name: "punctuation",
+ scope: "punctuation",
+ match: regexes.punctuation,
+ relevance: 0,
+};
+
+const VARIABLES = {
+ scope: "variable",
+ match: regexes.snakeCase,
+ relevance: 0,
+};
+
+/**
+ * TODO: fix regex to not break other selectors
+ */
+const FUNCTION_PARAM = {
+ scope: "function-param",
+ match: regexes.functionParam,
+ relevance: 0,
+}
+
+const DISCARD_NAMES = {
+ scope: "attribute",
+ begin: regexes.discardName,
+ relevance: 0,
+};
+
+const MODULES = {
+ scope: "module",
+ match: regexes.importModule,
+ relevance: 0,
+}
+
+// Relevance 1
+
+const OPERATORS = {
+ scope: "operator",
+ begin: regexes.operator,
+ keywords: {
+ operator: GLEAM_OPERATORS.join(" "),
+ $pattern: /\b\S+\b/g,
+ },
+ relevance: 1,
+}
+
+const KEYWORDS = {
+ name: 'Gleam keywords',
+ scope: "keyword",
+ keywords: {
+ keyword: GLEAM_KEYWORDS.join(" "),
+ operator: GLEAM_OPERATORS.join(" "),
+ },
+ relevance: 1,
+};
+
+// Relevance 2
+
+const LITERALS = {
+ name: "Booleans or Nil",
+ scope: "literal",
+ match: regexes.literal,
+ relevance: 2,
+};
+
+const NUMBERS = {
+ name: "Number",
+ scope: "number",
+ variants: [
+ {
+ begin: regexes.number.binary
+ },
+ {
+ begin: regexes.number.octal
+ },
+ {
+ begin: regexes.number.hex
+ },
+ {
+ begin: regexes.number.decOrFloat
+ },
+ {
+ match: regexes.number.scientific
+ }
+ ],
+ relevance: 2,
+};
+
+// Relevance 3
+
+const TYPES = {
+ name: "Types & Aliases",
+ scope: "type",
+ match: regexes.type,
+ relevance: 3,
+}
+
+// Relevance 4
+
+const FUNCTION_CALL = {
+ name: "Function calls",
+ scope: "function function-name function-call",
+ match: regexes.functionCall,
+ relevance: 4,
+};
+
+const FUNCTION_DECLARATION = {
+ name: "function declaration",
+ scope: "function function-name",
+ beginKeywords: "fn",
+ end: regexes.endParenthesis,
+ returnEnd: true,
+ relevance: 4,
+};
+
+// Relevance 5
+
+// Relevance 6
+
+const ATTRIBUTES = {
+ name: "Attributes",
+ scope: "attribute",
+ match: regexes.attribute,
+ relevance: 6,
+}
+
+// Relevance 7
+
+const STRINGS = {
+ name: "Strings",
+ scope: "string",
+ variants: [{ begin: /"/, end: /"/ }],
+ contains: [hljs.BACKSLASH_ESCAPE],
+ relevance: 7,
+};
+
+// Relevance 8
+
+const BIT_ARRAYS = {
+ // bit array
+ begin: "<<",
+ end: ">>",
+ scope: "operator",
+ contains: [
+ {
+ scope: "keyword",
+ beginKeywords:
+ "binary bits bytes int float bit_string bit_array bits utf8 utf16 " +
+ "utf32 utf8_codepoint utf16_codepoint utf32_codepoint signed " +
+ "unsigned big little native unit size",
+ },
+ cp(KEYWORDS),
+ cp(STRINGS),
+ cp(VARIABLES),
+ cp(DISCARD_NAMES),
+ cp(NUMBERS),
+ cp(PUNCTUATION),
+ ],
+ relevance: 8,
+};
+
+// Relevance 10
+
+const COMMENTS = {
+ name: "Comments",
+ scope: "comment",
+ match: regexes.comment,
+ relevance: 10,
+}
+
+/**
+ * Register the Gleam lang to HLJS global exported from `./highlight.core.min.js`
+ */
+hljs.registerLanguage("gleam", function(hljs) {
+ return {
+ name: "Gleam",
+ aliases: ["gleam"],
+ keywords: {
+ keyword: KEYWORDS.keywords.keyword,
+ operator: OPERATORS.keywords.operator,
+ },
+ contains: [
+ hljs.C_LINE_COMMENT_MODE,
+ cp(PUNCTUATION),
+ cp(MODULES),
+ cp(DISCARD_NAMES),
+ cp(OPERATORS),
+ cp(LITERALS),
+ cp(NUMBERS),
+ cp(TYPES),
+ cp(FUNCTION_DECLARATION),
+ cp(FUNCTION_CALL),
+ cp(ATTRIBUTES),
+ cp(STRINGS),
+ cp(COMMENTS),
+ ],
+ };
+});
+
+/**
+ * Wait until other scripts & css are loaded before highlighting
+ */
+addEventListener("DOMContentLoaded", () => {
+ hljs.highlightAll();
+})
diff --git a/static/js/highlight/regexes.js b/static/js/highlight/regexes.js
index c59c5c8..4477a98 100644
--- a/static/js/highlight/regexes.js
+++ b/static/js/highlight/regexes.js
@@ -7,7 +7,7 @@ export const functionCall = /\b(?:_)*[a-z_]+[a-z\d_]+\b(?=\()/g;
export const functionParam = /(?<=\b(?:_)*[a-z_]+[a-z\d_]+\b\()(.|\s|\n)*(?=\))/g;
export const operator = /(<<|>>|<-|->|\|>|<>|\.\.|<=\.?|>=\.?|==\.?|!=\.?|<\.?|>\.?|&&|\|\||\+\.?|-\.?|\/\.?|\*\.?|%\.?|=)/g;
export const type = /\b[A-Z]{1}(?:[a-z]+[A-Z]{0,1})*\b/g;
-export const booleanOrNil = /\b(True|False|Nil)\b/g;
+export const literal = /\b(True|False|Nil)\b/g;
export const comment = /\/\/.*/g;
export const attribute = /@[a-zA-Z0-9]+\b(?=\()/g;
export const discardName = /\b_[a-z][a-z0-9_]*\b/g;