Skip to content

Commit

Permalink
code highlight beta support for javascript
Browse files Browse the repository at this point in the history
  • Loading branch information
woXrooX committed Aug 13, 2023
1 parent ea825a5 commit b4fe3e3
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 0 deletions.
75 changes: 75 additions & 0 deletions code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use strict";

import JavaScript from "./source/languages/JavaScript.js";

export default class Code extends HTMLElement{
//// Native Form Behaviour
// Identify the element as a form-associated custom element
static formAssociated = true;

static #template = document.createElement("template");

static {
Code.#template.innerHTML = `
<pre>
<code>
</code>
</pre>
`;
}

constructor(){
super();

this.shadow = this.attachShadow({mode: 'closed'});
this.RAW = this.textContent;

// If language is not defined, then exit
if(!!this.hasAttribute("lang") === false) return;

CSS: {
const style = document.createElement('style');
style.textContent = `
pre{
background-color: hsla(230, 13%, 9%, 1);
width: 100%;
height: auto;
border-radius: 5px;
box-shadow: 0px 2px 5px 1px rgba(0, 0, 0, 0.5);
& > code{
color: white;
width: 100%;
height: 100%;
padding: 5px;
line-height: 150%;
}
}
`;
this.shadow.appendChild(style);
}

// Clone And Append Template
this.shadow.appendChild(Code.#template.content.cloneNode(true));

this.codeElement = this.shadow.querySelector("pre > code");

switch(this.lang){
case "JavaScript":
this.codeElement.innerHTML = JavaScript.handle(this.RAW);
break;
case "HTML":
this.codeElement.innerHTML = this.RAW;
break;
default:
console.log("No match");
}
}

};

window.customElements.define('x-code', Code);

// Make Code Usable W/O Importing It
window.Code = Code;
32 changes: 32 additions & 0 deletions example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>X-Code</title>
<script type="module" src="code.js"></script>
<style>
*, html, body{
box-sizing: border-box;
}
</style>
</head>
<body>
<x-code lang="JavaScript">
// main.js
console.log("Hello world!");
function fun(){
return "Hello from fun!";
}

let a = 34 + 2; // asd

for(let a = 0; a < b.length; a++){

}

fun(23);

</x-code>
</body>
</html>
171 changes: 171 additions & 0 deletions source/languages/JavaScript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
export default class JavaScript{
static #tokens = null;

static handle(code){
JavaScript.#tokenize(code);
return JavaScript.#renderHighlightedCode();
}

static #tokenize(code){
const keywords = ['if', 'else', 'while', 'for', 'function', 'var', 'let', 'const', 'return'];
const operators = ['+', '-', '*', '/', '=', '==', '!=', '>', '<', '>=', '<=', '(', ')', '{', '}'];

const tokens = [];
let current = 0;

while(current < code.length){
let char = code[current];

// Whitespace
if([' ', '\s', '\t'].includes(char)){
tokens.push({ type: 'whitespace', value: ' ' });

current++;
continue;
}

// Newline
if(char == '\n'){
tokens.push({ type: 'newline', value: '\n' });

current++;
continue;
}

// Comments
if(char === '/' && code[current + 1] === '/'){
let comment = '/';

while(code[++current] !== '\n' && current < code.length) comment += code[current];

comment += '\n'

tokens.push({ type: 'comment', value: comment });

current++;
continue;
}

// Dot
if(char === '.'){
tokens.push({ type: 'dot', value: '.' });

current++;
continue;
}

// Semicolon
if(char === ';'){
tokens.push({ type: 'semicolon', value: ';' });

current++;
continue;
}

// Operators
if(operators.includes(char)){
let operator = char;

while(operators.includes(operator + code[current + 1])) operator += code[++current];

tokens.push({ type: 'operator', value: operator });

current++;
continue;
}

// Keywords and Identifiers
if(/[a-zA-Z_]/.test(char)){
let identifier = char;

while(/[a-zA-Z0-9_]/.test(code[current + 1])) identifier += code[++current];

if(keywords.includes(identifier)) tokens.push({ type: 'keyword', value: identifier });
else tokens.push({ type: 'identifier', value: identifier });

current++;
continue;
}

// Handle numbers
if(/[0-9]/.test(char)){
let number = char;

while(/[0-9.]/.test(code[current + 1])) number += code[++current];

tokens.push({ type: 'number', value: parseFloat(number) });

current++;
continue;
}

// Handle strings
if(char === '"' || char === "'"){
let string = `"`;
let quote = char;

while(code[++current] !== quote) string += code[current];

string += `"`;

tokens.push({ type: 'string', value: string });

current++;
continue;
}

// Handle unrecognized characters
tokens.push({ type: 'unknown', value: char });

current++;
}

JavaScript.#tokens = tokens;
}

static #renderHighlightedCode(){
let html = '';

for(const token of JavaScript.#tokens){
let tokenHtml = '';

switch(token.type){
case 'comment':
tokenHtml = `<span style="color:gray;">${token.value}</span>`;
break;

case 'dot':
case 'semicolon':
tokenHtml = `<span style="color: white;">${token.value}</span>`;
break;

case 'operator':
tokenHtml = `<span style="color: hsla(205, 100%, 83%, 1);">${token.value}</span>`;
break;

case 'keyword':
tokenHtml = `<span style="color: hsla(286, 38%, 58%, 1);">${token.value}</span>`;
break;

case 'identifier':
tokenHtml = `<span style="color: hsla(205, 100%, 48%, 1);">${token.value}</span>`;
break;

case 'number':
tokenHtml = `<span style="color: hsla(33, 38%, 58%, 1);">${token.value}</span>`;
break;

case 'string':
tokenHtml = `<span style="color: hsla(124, 38%, 58%, 1);">${token.value}</span>`;
break;

default:
tokenHtml = `<span>${token.value}</span>`;
}

html += tokenHtml;
}

return html;
}
};

0 comments on commit b4fe3e3

Please sign in to comment.