Skip to content

Instantly share code, notes, and snippets.

@KnIfER
Last active April 3, 2026 04:02
Show Gist options
  • Select an option

  • Save KnIfER/ea569f0be547fb5e59ed86fdf98805b8 to your computer and use it in GitHub Desktop.

Select an option

Save KnIfER/ea569f0be547fb5e59ed86fdf98805b8 to your computer and use it in GitHub Desktop.
vscode-highlight-issue

Add text overlay to simplify code reading in visual studio code

scenario

While coding compute shaders in Godot with lots of vec4[] buffer , because of the maximum channel is 4 , I have to define more and more buffers, like :

layout(set = 0, binding = 3, std430) restrict buffer b_biomes_define { vec4 biomes_define[]; };
layout(set = 0, binding = 4, std430) restrict buffer b_details_define { vec4 details_define[]; };
layout(set = 0, binding = 5, std430) restrict buffer b_flags_int { ivec4 flags_int[]; 
// ……

As a result , the shader code become more and more messy: fabiospampinato/vscode-highlight#172

And I tend to forget the real parameter name.

Need...

I need to display text overlay as a decoration like : fabiospampinato/vscode-highlight#172

Notice how the original text is covered with "hello" as a hint.

css (by grok) :


span.ced-1-TextEditorDecorationType702-1::after {
    content: "hello";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    pointer-events: none;
    white-space: nowrap;
    font-size: inherit;
    font-weight: inherit;
    color: rgb(255 171 0);
    text-shadow: 0 0 20px rgb(0 0 0 / 80%);
    z-index: 10;
    opacity: 1;
}

span.ced-1-TextEditorDecorationType702-1 {
    position: relative;
    opacity: 1;
    color: #9d0000!important;
}

More Problem

the class of decorated text changes every time, and there is no obvious pattern , make it hard to customize using the Custom Css plugin

Workaround

  1. using the before option to insert unique identifier
  2. use identifier to modify style sheet

fabiospampinato/vscode-highlight#173

custom.js

loaded by the custom css/js plugin, it checks changes in decoration style continuously.

terrain_cs.css

fetched and expanded by custom.js, lt's basicly a style template .

todo

add per-workspace config

demo

image

	
	"(biomes_define\\[.{1,15}\\]\\.x)":  {	
		"filterFileRegex": ".*\\.glsl|.*\\.gdshaderinc|.*\\.gdshader",
		"regexFlags": "g", "decorations": [ {
				"overviewRulerColor": "#b39211", "backgroundColor": "#cca71400", "color": "#1f1f1f", "fontWeight": "bold", "contentText":"hello"
				, "before" : {"contentText" : "旋转", "color": "gray"} 
			} ]
	},
	"(biomes_define\\[.{1,15}\\]\\.y)":  {	
		"filterFileRegex": ".*\\.glsl|.*\\.gdshaderinc|.*\\.gdshader",
		"regexFlags": "g", "decorations": [ {
				"overviewRulerColor": "#b39211", "backgroundColor": "#cca71400", "color": "#1f1f1f", "fontWeight": "bold", "contentText":"hello"
				, "before" : {"contentText" : "elevation", "color": "gray"} 
			} ]
	},
	"(biomes_define\\[.{1,15}\\]\\.z)":  {	
		"filterFileRegex": ".*\\.glsl|.*\\.gdshaderinc|.*\\.gdshader",
		"regexFlags": "g", "decorations": [ {
				"overviewRulerColor": "#b39211", "backgroundColor": "#cca71400", "color": "#1f1f1f", "fontWeight": "bold", "contentText":"hello"
				, "before" : {"contentText" : "缩放_Z", "color": "gray"} 
			} ]
	},
	"(biomes_define\\[.{1,15}\\]\\.w)":  {	
		"filterFileRegex": ".*\\.glsl|.*\\.gdshaderinc|.*\\.gdshader",
		"regexFlags": "g", "decorations": [ {
				"overviewRulerColor": "#b39211", "backgroundColor": "#cca71400", "color": "#1f1f1f", "fontWeight": "bold", "contentText":"hello"
				, "before" : {"contentText" : "淡出距离", "color": "gray"} 
			} ]
	},
function addStyle(text, id, h){
var el = 0;
if(id) el = document.getElementById(id);
if(!el) {
el = document.createElement('STYLE');
if(id) el.id = id;
(h||document.head).append(el);
}
else if(h && h!=el.parentNode) {
h.append(el);
}
// el.innerHTML = html(text);
el.textContent = text;
};
function findTextEditorDecorationRule() {
var styleTags = document.head.getElementsByTagName('STYLE');
// for (var i = styleTags.length-1; i >= 0; i--) {
for (var i = 0; i <styleTags.length; i++) {
var style = styleTags[i];
try {
var cssRules = style.sheet?.cssRules;
if (cssRules)
for (var rule of cssRules) {
if (rule.type !== CSSRule.STYLE_RULE) continue;
var selectorText = rule.selectorText || '';
if (selectorText.includes('TextEditorDecorationType')) {
// console.log('i::', i, styleTags.length);
return style;
}
}
} catch (err) {
}
}
return null;
}
function extractDecorationSelectors(t) {
var ret = []
if(t && t.sheet?.cssRules?.length) {
var cssRules = t.sheet.cssRules;
var text = '';
for (var i = 0; i < cssRules.length; i++) {
var rule = cssRules[i];
var selector = rule.selectorText || '';
if(selector.endsWith("::before"))
{
// if (!selector.includes('TextEditorDecorationType')) continue;
var cssText = rule.cssText || '';
console.log('cssText::', cssText);
var spec = 'content:';
var idx = cssText.indexOf(spec);
if (idx>0) {
var ed = cssText.indexOf(';', idx);
// .replace('.monaco-editor ','')
if(cssText[ed-1]=='"')
ret.push({name:selector, id:cssText.slice(idx+spec.length+2, ed-1)});
// ret.push(cssText);
}
}
}
return ret;
}
}
async function loadAndReplaceCSS(selectors) {
const CUSTOM_CSS_URL = 'http://127.0.0.1:8080/base/photos/styles/terrain_cs.css'; // ← 修改成你的 css 文件路径
try {
var response = await fetch(CUSTOM_CSS_URL + '?t=' + Date.now());
let cssText = await response.text();
var back0 = cssText
function expandRepeatBlocks(css) {
const repeatBlockRegex = /\/\*repeat\[(.*?)\]\s*\*\/([\s\S]*?)\/\*repeated\*\//g;
return css.replace(repeatBlockRegex, (match, arrayStr, block) => {
var members = arrayStr.split(',').map(m => m.trim().replace(/['"]/g, ''));
var ret = ''; // 解析数组成员
for (var x = 0; x < members.length; x++) {
var rep = block, key=members[x]; // repeat for each member
var arr = key.split('/');
for (var i = 1; i < arr.length; i++) {
rep = rep.replaceAll("THIS_"+i, arr[i]);
}
rep = rep.replaceAll(/THIS(_[0-9]+)?/g, arr[0]);
// console.log('arr', key, arr, ret)
ret += rep;
ret += "\n";
}
return ret;
});
}
cssText = expandRepeatBlocks(cssText)
// console.log('cssText::', cssText);
var expand_def = []
for (var i = 0; i < selectors.length; i++) {
var sel = selectors[i];
// console.log('sel::', sel);
var id = sel.id;
var selector = sel.name;
var back = cssText
// hide inserted identifier helper
cssText = cssText.replaceAll(id+'_hide', selector);
// hack : inserted text is xxx-3, wrapped text is xxx-1
cssText = cssText.replaceAll(RegExp('(?<!")'+id, 'g'), selector.replace('-3::before', '-1'));
if(back.length==cssText.length) { //; not changed
expand_def.push(sel);
}
}
if(expand_def.length) {
var st = back0.indexOf('/*repeat['), ed = back0.indexOf('/*repeated*/', st+15);
if(ed>st && st>=0) {
st = back0.indexOf('\n', st+7);
var defBlock = back0.slice(st+1, ed);
for (var i = 0; i < expand_def.length; i++) {
var sel = expand_def[i];
console.log('expand_def::', sel);
var id = sel.id;
var selector = sel.name;
var newTmp = defBlock.replaceAll(/THIS(_[0-9]+)?/g, id);
newTmp = newTmp.replaceAll(id+'_hide', selector);
// hack : inserted text is xxx-3, wrapped text is xxx-1
newTmp = newTmp.replaceAll(RegExp('(?<!")'+id, 'g'), selector.replace('-3::before', '-1'));
cssText += '\n';
cssText += newTmp;
}
}
}
console.log('cssText::', cssText);
addStyle(cssText, 'text-decoration-overlay');
} catch (err) {
console.error(err);
}
}
function extractStyle(t) {
if(t && t.sheet?.cssRules?.length) {
var cssRules = t.sheet.cssRules;
var text = '';
for (var i = 0; i < cssRules.length; i++) {
var rule = cssRules[i];
text += rule.cssText;
text += '\n';
}
return text;
}
}
var lastStyles = ''; // todo opt
function fix_decorations() {
var t = findTextEditorDecorationRule();
// console.log('findTextEditorDecorationRule::', t);
if(t) {
var text = extractStyle(t);
if(lastStyles!=text) {
console.log('changed::');
var sels = extractDecorationSelectors(t)
loadAndReplaceCSS(sels)
lastStyles = text;
}
}
}
console.log('checkLights::', 123);
var checkLights = setInterval(()=>{
fix_decorations()
}, 350);
/*repeat[template, identifier1, identifier2]*/
THIS_hide {
content: "";
}
THIS::after {
content: "THIS";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
white-space: nowrap;
font-size: inherit;
font-weight: inherit;
color: rgb(255 171 0);
/* text-shadow: rgb(255 0 0 / 80%) 0px 0px 20px; */
z-index: 10;
opacity: 1;
color: gray;
background: #260022; /* 底色 */
}
THIS.mtk4::after {
color: #a074c4;
/* color: #319331; */
}
THIS {
position: relative;
opacity: 1;
/* color: #9d0000!important; */
filter:invert(1);
background: #260022;
color: #260022; /* 底色 */
}
THIS:hover {
color: #9d0000!important;
}
/*repeated*/
@KnIfER

KnIfER commented Apr 3, 2026

Copy link
Copy Markdown
Author

warning : it's for vscode Version: 1.64.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment