Skip to content

Instantly share code, notes, and snippets.

@jonasfrey
Last active April 2, 2023 20:26
Show Gist options
  • Save jonasfrey/dab4d236bc505df6db8a9d5005b4ad0b to your computer and use it in GitHub Desktop.
Save jonasfrey/dab4d236bc505df6db8a9d5005b4ad0b to your computer and use it in GitHub Desktop.
monaco /vscode editor for shadertoy.com
var f_display_editor = null;
class O_error{
constructor(
n_idx_line,
s_text
){
this.n_idx_line = n_idx_line
this.s_text = s_text
}
}
var f_implement_monaco_editor = function(){
f_display_editor = function(s_selector){
var a_o = Array.prototype.slice.apply(document.querySelectorAll('#editorManager .tab'));
for(var o of a_o){
o.classList.remove("selected");
}
window.event.target.classList.add("selected");
var a_s_selector = [
`#editor .CodeMirror`,
'#editor .monaco-editor'
];
for(let s_selector of a_s_selector){
document.querySelector(s_selector).style.display = "none";
}
document.querySelector(s_selector).style.display = "block";
}
var s_html_editor_choice = `
<div id="editorManager">
<div id="tab0" style='width: 45%;' onclick="f_display_editor('#editor .CodeMirror')" class="tab"><label>Shadertoy Editor</label></div>
<div id="tab1" style='width: 45%;' onclick="f_display_editor('#editor .monaco-editor')" class="tab"><label>VScode (Monaco) Editor</label></div>
</div>
`
var o_div_html_editor_choice = document.createElement("div");
o_div_html_editor_choice.innerHTML = s_html_editor_choice;
document.querySelector(".block1").insertBefore(
o_div_html_editor_choice,
document.querySelector(".block1").firstChild,
);
var o_script = document.createElement('script');
o_script.type = 'text/javascript';
o_script.src = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/loader.min.js';
document.head.appendChild(o_script);
window.o_editor_monaco = null;
o_script.onload = function(){
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs' }});
require(["vs/editor/editor.main"], () => {
f_add_glsl_language_to_monaco_editor(monaco);
o_editor_monaco = monaco.editor.create(document.getElementById('editor'), {
value: gShaderToy.mPass[gShaderToy.mActiveDoc].mDocs.getValue(),
language: 'glsl',
theme: 'vs-dark',
// automaticLayout: true
});
o_editor_monaco.a_n_view_zone_id = []
o_editor_monaco.n_ms_checkerrors = 4200;
o_editor_monaco.n_id_timeout_checkerrors = 0;
window.onresize = function (){
o_editor_monaco.layout();
};
o_editor_monaco.getModel().onDidChangeContent((event) => {
var s_code = o_editor_monaco.getModel().getValue();
gShaderToy.mPass[gShaderToy.mActiveDoc].mDocs.setValue(s_code);
document.querySelector("#compileButton").click()
window.clearTimeout(o_editor_monaco.n_id_timeout_checkerrors);
o_editor_monaco.n_id_timeout_checkerrors = window.setTimeout(function(){
var a_o_div_error = Array.prototype.slice.call(document.querySelectorAll(`.errorMessage`));
var a_o_error = [];
for(var o_div_error of a_o_div_error){
var o_error = new O_error(
Array.prototype.slice.call(document.querySelectorAll(".CodeMirror-code > div")).indexOf((o_div_error.parentElement.parentElement)),
o_div_error.innerText
);
a_o_error.push(o_error)
}
a_o_error = a_o_error.filter(o=>o.s_text.trim() != '');
// remvoe all errors
o_editor_monaco.changeViewZones(function (changeAccessor) {
for(var n of o_editor_monaco.a_n_view_zone_id){
changeAccessor.removeZone(n)
}
});
console.log(a_o_error)
// add new errors if existing
for(var o_error of a_o_error){
// Add a zone to make hit testing more interesting
// var o_error = {n_idx_line: 2, s_text: "test error"}
var a_n_view_zone_id = [];
o_editor_monaco.changeViewZones(function (changeAccessor) {
var o_div_error = document.createElement('div');
o_div_error.style.background = '#FEE';
o_div_error.style.border = '1px solid #EDD';
o_div_error.style.color = '#A66';
o_div_error.innerText = o_error.s_text;
o_editor_monaco.a_n_view_zone_id.push(changeAccessor.addZone({
afterLineNumber: o_error.n_idx_line,
heightInLines: 'auto',
domNode: o_div_error,
}));
});
}
},o_editor_monaco.n_ms_checkerrors)
});
document.querySelector(".monaco-editor").addEventListener("keydown", function (e) {
if (e.key === 's' && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
e.preventDefault();
// o_self.f_run_o_program();
document.querySelector("#saveButton").click();
}
}, false);
});
}
var f_ChangePass_old = ShaderToy.prototype.ChangePass;
ShaderToy.prototype.ChangePass = function(n_id){
console.log("change Pass")
f_ChangePass_old.call(this, n_id)
var s_code = gShaderToy.mPass[gShaderToy.mActiveDoc].mDocs.getValue();
o_editor_monaco.getModel().setValue(s_code);
}
var f_add_glsl_language_to_monaco_editor = function(monaco){
monaco.languages.register({ id: 'glsl' });
monaco.languages.setLanguageConfiguration('glsl', {
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
lineComment: '//',
blockComment: ['/*', '*/']
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
],
autoClosingPairs: [
{ open: '[', close: ']' },
{ open: '{', close: '}' },
{ open: '(', close: ')' },
{ open: "'", close: "'", notIn: ['string', 'comment'] },
{ open: '"', close: '"', notIn: ['string'] }
]
});
monaco.languages.setMonarchTokensProvider('glsl', {
defaultToken: '',
tokenPostfix: '.glsl',
types: [ 'bool', 'bvec2', 'bvec3', 'bvec4', 'float', 'int', 'ivec2', 'ivec3', 'ivec4', 'mat2', 'mat2x2', 'mat2x3', 'mat2x4', 'mat3', 'mat3x2', 'mat3x3', 'mat3x4', 'mat4', 'mat4x2', 'mat4x3', 'mat4x4', 'uint', 'uvec2', 'uvec3', 'uvec4', 'vec2', 'vec3', 'vec4', 'void' ],
keywords: [ 'attribute', 'break', 'case', 'centroid', 'const', 'continue', 'default', 'discard', 'do', 'else', 'false', 'flat', 'for', 'highp', 'if', 'in', 'inout', 'invariant', 'isampler2D', 'isampler2DArray', 'isampler3D', 'isamplerCube', 'layout', 'lowp', 'mediump', 'out', 'precision', 'return', 'sampler2D', 'sampler2DArray', 'sampler2DArrayShadow', 'sampler2DShadow', 'sampler3D', 'samplerCube', 'samplerCubeShadow', 'smooth', 'struct', 'switch', 'true', 'uniform', 'usampler2D', 'usampler2DArray', 'usampler3D', 'usamplerCube', 'varying', 'while' ],
functions: [ 'radians', 'degrees', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh', 'pow', 'exp', 'log', 'exp2', 'log2', 'sqrt', 'inversesqrt', 'abs', 'sign', 'floor', 'trunc', 'round', 'roundEven', 'ceil', 'fract', 'mod', 'modf', 'min', 'max', 'clamp', 'mix', 'step', 'smoothstep', 'isnan', 'isinf', 'floatBitsToInt', 'floatBitsToUint', 'intBitsToFloat', 'uintBitsToFloat', 'packSnorm2x16', 'unpackSnorm2x16', 'packUnorm2x16', 'unpackUnorm2x16', 'packHalf2x16', 'unpackHalf2x16', 'length', 'distance', 'dot', 'cross', 'normalize', 'faceforward', 'reflect', 'refract', 'matrixCompMult', 'outerProduct', 'transpose', 'determinant', 'inverse', 'lessThan', 'lessThanEqual', 'greaterThan', 'greaterThanEqual', 'equal', 'notEqual', 'any', 'all', 'not', 'textureSize', 'texture', 'texture2D', 'textureCube', 'texture2DProj', 'texture2DLodEXT', 'texture2DProjLodEXT', 'textureCubeLodEXT', 'texture2DGradEXT', 'texture2DProjGradEXT', 'textureCubeGradEXT', 'textureProj', 'textureLod', 'textureOffset', 'texelFetch', 'texelFetchOffset', 'textureProjOffset', 'textureLodOffset', 'textureProjLod', 'textureProjLodOffset', 'textureGrad', 'textureGradOffset', 'textureProjGrad', 'textureProjGradOffset', 'dFdx', 'dFdy', 'fwidth' ],
operators: [ '++', '--', '+', '-', '~', '!', '*', '/', '%', '<<', '>>', '<', '>', '<=', '>=', '==', '!=', '&', '^', '|', '&&', '^^', '||', '?', ':', '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '&=', '^=', '|=', ',' ],
brackets: [
{ token: 'delimiter.curly', open: '{', close: '}' },
{ token: 'delimiter.parenthesis', open: '(', close: ')' },
{ token: 'delimiter.square', open: '[', close: ']' },
{ token: 'delimiter.angle', open: '<', close: '>' }
],
symbols: /[=><!~?:&|+\-*\/\^%]+/,
integersuffix: /[uU]?/,
floatsuffix: /[fF]?/,
func: /[a-zA-Z_0-9]+/,
tokenizer: {
root: [
[/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/, 'number.float'],
[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/, 'number.float'],
[/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/, 'number.hex'],
[/([+-]?)\d+(@integersuffix)/, 'number.integer'],
[/#(version|define|undef|ifdef|ifndef|else|elsif|endif)/, 'keyword.directive'],
[/\$[a-zA-Z0-9]*/, 'regexp'],
[/\s[A-Z_][A-Z_0-9]*/, 'constant'],
[/gl_[a-zA-Z_0-9]+/, 'keyword.gl'],
[
/([a-zA-Z_][a-zA-Z_0-9]*)/,
{
cases: {
'@types': { token: 'keyword.$0' },
'@keywords': { token: 'keyword.$0' },
'@functions': { token: 'keyword.builtins.$0' },
'@default': 'identifier'
}
}
],
[/(\d+(\.\d+))/, 'number.float'],
[/\d+/, 'keyword'],
[/\/\/.+/, 'comment'],
[/\/\*.+?(\*\/)/, 'comment'],
[/[{}()\[\]]/, '@brackets'],
[
/@symbols/,
{
cases: {
'@operators': 'delimiter',
'@default': ''
}
}
],
[/[;,.]/, 'delimiter'],
],
}
});
}
}
var f_b_shadertoy_loaded = async function(){
window.setTimeout(function(){
return window.gShaderToy != null;
},100)
}
if(window.location.href.includes("/view/")){
var n_id = 0;
n_id = window.setInterval(function(){
if(window.gShaderToy != null){
window.clearInterval(n_id);
window.setTimeout(function(){
f_implement_monaco_editor();
},1000)
}
},100);
// var b_shadertoy_loaded = await f_b_shadertoy_loaded();
// while(!b_shadertoy_loaded){
// b_shadertoy_loaded = await f_b_shadertoy_loaded();
// }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment