Skip to content
creating a code editor with prism
Profile icon
conspicous

just to note, this was created with my editor PIE.

warning, this tutorial creates a bare-bone editor, you will need to style and add more elements.

Setting up...

So you came here to learn to build an editor, no listen to me talk nonsensically, so here we go...

you will need to set up a HTML5, CSS3 and JS repl and create the following layout

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <style href="style.css"></style> <title>Title</title> </head> <body> <script src="script.js"> </body> </html>

Making the editor UI...

Making a UI is essential to having a good user experience... otherwise you can't use it.

in the header add the following scripts in this exact order.

<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/prism.min.js" integrity="sha512-WkVkkoB31AoI9DAk6SEEEyacH9etQXKUov4JRRuM1Y681VsTq7jYgrRw06cbP6Io7kPsKx+tLFpH/HXZSZ2YEQ==" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha512-ROhjG07IRaPZsryG77+MVyx3ZT5q3sGEGENoGItwc9xgvx+dl+s3D8Ob1zPdbl/iKklMKp7uFemLJFDRw0bvig==" crossorigin="anonymous"></script>

it will load prism.js and marked.js which are required in syntax highlighting and rendering

We then want to make the editor and no it is not an average input, it is a code block as it allows custom styles (this is probably where people go wrong).
So to create the editable code block, we add to the body the following:

<center> <div class="container"> <div id="editor"><code class="language-markdown" contenteditable="plaintext-only"># Just a simple test</code></div> <div><iframe id="preview"></iframe></div> </div> </center>

note that if you add a new line, it must start at column 0 of that newline.

in style.css add the following to style the page:

*{ outline: none; } .container{ display: grid; grid-template-columns: auto auto; min-height: 100vh; } #editor, #preview{ width: calc(50vw - 24px); height: calc(100vh - 24px); background-color: #eee; padding: 10px; border: none; text-align: left; } html, body{ margin: 0; padding: 0; }

this will prevent the focus outline in browsers appearing as you edit as well as style the editor.

ok, so we have the editor, but what about syntax highlighting, which makes up around 60%-70% of the JS code.

The syntax highlighter...

YAY, time for PRISM.JS and rendering. So, we now move on to script.js and add these:

this is for getting the preview and editing element

The syntax highlighter is important so programmer do not have to "Strain their eyes in misery" as the replit offline project site described it.

var editor = document.getElementById("editor") var preview = document.getElementById("preview")

syntax highlighter and fix to prevent the cursor going back to beginning for every rehighlight.

var editor = document.getElementById("editor") var preview = document.getElementById("preview") // prevents the cursor from going to start. function saveCaretPosition(context){ var selection = window.getSelection(); var range = selection.getRangeAt(0); range.setStart( context, 0 ); var len = range.toString().length; return function restore(){ var pos = getTextNodeAtPosition(context, len); selection.removeAllRanges(); var range = new Range(); range.setStart(pos.node ,pos.position); selection.addRange(range); } } function getTextNodeAtPosition(root, index){ const NODE_TYPE = NodeFilter.SHOW_TEXT; var treeWalker = document.createTreeWalker(root, NODE_TYPE, function next(elem) { if(index > elem.textContent.length){ index -= elem.textContent.length; return NodeFilter.FILTER_REJECT } return NodeFilter.FILTER_ACCEPT; }); var c = treeWalker.nextNode(); return { node: c? c: root, position: index }; } // the syntax highlighter // you can move the render function to a button like this <button onclick="render()">Run</button> var code = document.getElementsByTagName('code')[0]; code.addEventListener('input',function () { var restore = saveCaretPosition(this); Prism.highlightElement(this); restore(); render(); })

renderer to render the markdown in an iframe.

function render(){ var markdn = editor.innerText content = marked(markdn) preview.contentWindow.document.body.innerHTML = content; }

then we return to the stylesheet and add the following

/* prism */ code[class*="language-"], pre[class*="language-"] { color: #393A34; font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; direction: ltr; text-align: left; font-size: .9em; line-height: 1.2em; -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; -webkit-hyphens: none; -moz-hyphens: none; -ms-hyphens: none; hyphens: none; border: none; } pre > code[class*="language-"] { font-size: 1em; } pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { background: #C1DEF1; } pre[class*="language-"]::selection, pre[class*="language-"] ::selection, code[class*="language-"]::selection, code[class*="language-"] ::selection { background: #C1DEF1; } /* Code blocks */ pre[class*="language-"] { padding: 1em; margin: .5em 0; overflow: auto; border: 1px solid #dddddd; background-color: white; } /* Inline code */ :not(pre) > code[class*="language-"] { padding: .2em; padding-top: 1px; padding-bottom: 1px; } .token.comment, .token.prolog, .token.doctype, .token.cdata { color: #008000; font-style: italic; } .token.namespace { opacity: .7; } .token.string { color: #A31255; } .token.punctuation, .token.operator { color: #6121ff; /* no highlight */ } .token.url, .token.symbol, .token.number, .token.boolean, .token.variable, .token.constant, .token.inserted { color: #36acaa; } .token.atrule, .token.keyword, .token.attr-value, .language-autohotkey .token.selector, .language-json .token.boolean, .language-json .token.number, code[class*="language-css"] { color: #0000ff; } .token.function { color: #393A34; } .token.deleted, .language-autohotkey .token.tag { color: #9a050f; } .token.selector, .language-autohotkey .token.keyword { color: #00009f; } .token.important, .token.bold { font-weight: bold; } .token.italic { font-style: italic; } .token.class-name, .language-json .token.property { color: #2B91AF; } .token.tag, .token.selector { color: #800000; } .token.attr-name, .token.property, .token.regex, .token.entity { color: #ff0000; } .token.directive.tag .tag { background: #ffff00; color: #393A34; } /* overrides color-values for the Line Numbers plugin * http://prismjs.com/plugins/line-numbers/ */ .line-numbers .line-numbers-rows { border-right-color: #a5a5a5; } .line-numbers-rows > span:before { color: #2B91AF; } /* overrides color-values for the Line Highlight plugin * http://prismjs.com/plugins/line-highlight/ */ .line-highlight { background: rgba(193, 222, 241, 0.2); background: -webkit-linear-gradient(left, rgba(193, 222, 241, 0.2) 70%, rgba(221, 222, 241, 0)); background: linear-gradient(to right, rgba(193, 222, 241, 0.2) 70%, rgba(221, 222, 241, 0)); } /* end */

the stylesheet above adds syntax highlighting for prismjs that you can change the color and font for.

and there you go... your very own editor, which you can stylise however you want.
and it is really that simple, except you do not have line numbers, but it is a starting point. ¯_(ツ)_/¯

I would like to point out that anything you load from https://cdnjs.com/libraries/prism must be loaded after prism.js, this also includes any distribution of it.

challenge - create a fully loaded IDE.

you can preview the resulting code below in the repl.

You are viewing a single comment. View All
Profile icon
conspicous

@aguy11
thanks for upvoting. For the 100k special, I am creating a language called "slice" to go with the PIE editor. haha cooking references.

And it is using your python lexer tutorial.

Profile icon
aguy11

@Lethdev2019
Yay, thanks! I don't know how you found that tutorial though... The problem is, I'm not sure how to make a recipe made of code..

Profile icon
conspicous

searched up how to do it a long time ago and bookmarked it.

@aguy11