From 12bc0cb385d7f13243cac90260c3259f653c44a2 Mon Sep 17 00:00:00 2001 From: dylan <> Date: Sat, 6 May 2023 10:00:41 -0700 Subject: [PATCH] Syntax highlighting --- codetab.ts | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++- colors.ts | 6 ++ deno.lock | 11 ++++ deps.ts | 26 +++++++- 4 files changed, 210 insertions(+), 4 deletions(-) diff --git a/codetab.ts b/codetab.ts index fc5a895..6eb34fd 100644 --- a/codetab.ts +++ b/codetab.ts @@ -4,6 +4,7 @@ import { drawText } from "./builtins.ts"; import { COLOR } from "./colors.ts"; import {getSheet, setSheet} from "./sheet.ts"; import { K, getKeyboardString, keyPressed, shiftKeyDown } from "./keyboard.ts"; +import { tokenize } from "./deps.ts"; const state = { scrollX: 0, @@ -138,6 +139,145 @@ const gridToIndex = (str: string, x: number, y: number) => { return lines.slice(0, y).join("\n").length+Math.min(x, lines[y].length)+1; } +const keywords = [ + "break", + "case", + "catch", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "else", + "export", + "extends", + "finally", + "for", + "function", + "if", + "import", + "in", + "instanceof", + "new", + "return", + "super", + "switch", + "this", + "throw", + "try", + "typeof", + "var", + "void", + "while", + "with", + "let", + "static", + "yield", + "await", + "enum", + "implements", + "interface", + "package", + "private", + "protected", + "public", + "=>", +]; +const values = [ + "false", + "null", + "true", + "undefined", +]; +const operator = [ + "&&", + "||", + "??", + "--", + "++", + ".", + "?.", + "<", + "<=", + ">", + ">=", + "!=", + "!==", + "==", + "===", + "+", + "-", + "%", + "&", + "|", + "^", + "/", + "*", + "**", + "<<", + ">>", + ">>>", + "=", + "+=", + "-=", + "%=", + "&=", + "|=", + "^=", + "/=", + "*=", + "**=", + "<<=", + ">>=", + ">>>=", + "!", + "?", + "~", + "...", +]; +const punctuation = [ + "(", + ")", + "[", + "]", + "{", + "}", + ".", + ":", + ";", + ",", +]; + +const keywordColor = COLOR.PURPLE; +const operatorColor = COLOR.CYAN; +const valueColor = COLOR.ORANGE; +const stringColor = COLOR.GREEN; +const regexColor = stringColor; +const punctuationColor = COLOR.WHITE; +const commentColor = COLOR.GRAY; +const identifierColor = COLOR.LIGHTGRAY; +const invalidColor = COLOR.RED; + +const tokenColors = { + "StringLiteral": stringColor, + "NoSubstitutionTemplate": stringColor, + "TemplateHead": stringColor, + "TemplateMiddle": stringColor, + "TemplateTail": stringColor, + "RegularExpressionLiteral": regexColor, + "MultiLineComment": commentColor, + "SingleLineComment": commentColor, + "IdentifierName": identifierColor, + "PrivateIdentifier": identifierColor, + "NumericLiteral": valueColor, + "Punctuator": punctuationColor, + "WhiteSpace": punctuationColor, + "LineTerminatorSequence": punctuationColor, + "Invalid": invalidColor, +} + const drawCodeField = (code: string, x: number, y: number, w: number, h: number) => { const { scrollX, @@ -162,9 +302,34 @@ const drawCodeField = (code: string, x: number, y: number, w: number, h: number) fillRect(x+focusX*fontWidth-scrollX, y+focusY*(fontHeight+1)-scrollY, fontWidth+1, fontHeight+1, COLOR.YELLOW); } // TODO: Add syntax highlighting use "npm:js-tokens" maybe? - code.split("\n").forEach((line, i) => { - drawText(x-scrollX, 1+y+i*(fontHeight+1)-scrollY, line); - }); + const tokens = [...tokenize(code)]; + let cx = 0; + let cy = 0; + tokens.forEach((token) => { + const lines = token.value.split("\n"); + lines.forEach((line, i) => { + let color = tokenColors[token.type]; + if (keywords.includes(token.value)) { + color = keywordColor; + } + if (values.includes(token.value)) { + color = valueColor; + } + if (operator.includes(token.value)) { + color = operatorColor; + } + if (punctuation.includes(token.value)) { + color = punctuationColor; + } + drawText(x+cx-scrollX, 1+y+cy-scrollY, line, color); + if (i === lines.length-1) { + cx += fontWidth*line.length; + } else { + cx=0; + cy+=fontHeight+1; + } + }); + }) } const update = () => { diff --git a/colors.ts b/colors.ts index 9e781b1..11f7905 100644 --- a/colors.ts +++ b/colors.ts @@ -8,6 +8,12 @@ const colors = { BLUE: [0, 0, 1], DARKBLUE: [0.1, 0.05, 0.4], BROWN: [0.6, 0.5, 0.4], + GRAY: [0.5, 0.5, 0.5], + PURPLE: [0.7, 0.1, 0.85], + ORANGE: [0.95, 0.75, 0.25], + CYAN: [0, 0.9, 0.9], + LIGHTGRAY: [0.75, 0.75, 0.75], + REDDISH: [0.7, 1, 0.5], } as const; export const palette: Array<[number, number, number, number]> = Object.values(colors).map(val => [...val, 1]); diff --git a/deno.lock b/deno.lock index 8c12f9c..3747ccf 100644 --- a/deno.lock +++ b/deno.lock @@ -42,5 +42,16 @@ "https://glfw-binaries.deno.dev/3.4.0-patch2/glfw3_darwin_aarch64.js": "ae4d795d93830b8a27714ab6c20b69b67f3d4ad3544c50e344558756cf2e92f3", "https://glfw-binaries.deno.dev/3.4.0-patch2/glfw3_linux.js": "b064aedb175fee1a977937f07584238f313a1958f9869273e7e672c42f09932d", "https://glfw-binaries.deno.dev/3.4.0-patch2/glfw3_windows.js": "6ac603e03520c8c333e1475cb00f982adb1f8a99de7f4bb0b8953da66f210159" + }, + "npm": { + "specifiers": { + "js-tokens": "js-tokens@8.0.1" + }, + "packages": { + "js-tokens@8.0.1": { + "integrity": "sha512-3AGrZT6tuMm1ZWWn9mLXh7XMfi2YtiLNPALCVxBCiUVq0LD1OQMxV/AdS/s7rLJU5o9i/jBZw/N4vXXL5dm29A==", + "dependencies": {} + } + } } } diff --git a/deps.ts b/deps.ts index 432703c..34a711e 100644 --- a/deps.ts +++ b/deps.ts @@ -1,6 +1,30 @@ +// dwm export { createWindow, getProcAddress, mainloop, } from "https://deno.land/x/dwm@0.3.3/mod.ts"; -export * as gl from "https://deno.land/x/gluten@0.1.6/api/gles23.2.ts"; \ No newline at end of file +export * as gl from "https://deno.land/x/gluten@0.1.6/api/gles23.2.ts"; + +// jsTokens +import jsTokens from "npm:js-tokens"; +export function tokenize(input: string): Iterable { + // deno-lint-ignore no-explicit-any + return (jsTokens as any)(input); +}; +type Token = + | { type: "StringLiteral"; value: string; closed: boolean } + | { type: "NoSubstitutionTemplate"; value: string; closed: boolean } + | { type: "TemplateHead"; value: string } + | { type: "TemplateMiddle"; value: string } + | { type: "TemplateTail"; value: string; closed: boolean } + | { type: "RegularExpressionLiteral"; value: string; closed: boolean } + | { type: "MultiLineComment"; value: string; closed: boolean } + | { type: "SingleLineComment"; value: string } + | { type: "IdentifierName"; value: string } + | { type: "PrivateIdentifier"; value: string } + | { type: "NumericLiteral"; value: string } + | { type: "Punctuator"; value: string } + | { type: "WhiteSpace"; value: string } + | { type: "LineTerminatorSequence"; value: string } + | { type: "Invalid"; value: string }; \ No newline at end of file