Loading syntax highlighting themes

Ben Murphy-Baum

This is a fun snippet that will allow you to load a syntax highlighting theme from a file on disk. The file is a YAML file, which contains some key-value text to specify the colors for different code elements. Example yaml file specifying the theme:

theme: 
 name: Desert Sage
 keyword: #6288ba
 comment: #989898
 string: #cb8076
 operation: #739079
 bifunction: #b98a51
 userfunction: #b98a51
 directive: #c76060
 opkey: #4f92c5

To load it, call this from Igor and navigate to the file:

loadTheme()

When you load the theme, it will automatically set the syntax highlighting colors to those specified in the file. This should work on Igor 8, 9, and 10, but likely works on 7 as well. I attached this example theme and also Igor's 'classic' theme, which is the default.

SyntaxThemeLoader.ipf (2.81 KB) classic_theme.zip (340 bytes) desertsage_theme.zip (336 bytes)
jjweimer

Thank you for this example. I see an immediate application of the YAML format for something that I am developing.

I am curious how the in-line STRUCT definition does not throw an error. Somehow, in the far corners of my memory, I seem to recall reading that STRUCT definitions have to be on separate lines, not in-line, for function definitions. Also, the definition seems to have a mistake in spacing that does not throw even a warning???

STRUCT RGBColor &color NOT STRUCT RGBColor& color

April 26, 2025 at 09:00 am - Permalink

jjweimer

Inspired by your creation, I post below a snippet for a generic YAML loader. The mstr is the message string. The vstr in the validator string. The YAML is loaded into a text wave called YAMLwave. A return of -1 to -4 with YAMLwave having no row length means nothing was loaded for various reasons (parsed by the return value). Note that position [0, 0] in the YAMLwave text wave is the validator string, with [0, 1] as a blank.

Function load_YAML(string mstr, string vstr)
	variable refNum, slen
	string fileFilters = "(*.yml, *.yaml):.yml, .yaml;"
	string fName, line = ""
	
	// make storage text wave
	make/N=(0,2)/T/O YAMLwave
	YAMLwave = ""
	
	// open the file
	open/R/F=(fileFilters)/M=mstr refNum
	if (strlen(s_fileName) == 0)
		return -1
	endif	
	fName = s_fileName
	
	// validate the first line
	FReadLine refnum, line
	slen = strlen(line)
	close refNum
	if (slen != 0)
		if (cmpstr(line[0,slen-3],vstr) != 0)		
			return -2
		endif
	else
		return -3
	endif
	
	// load the text wave
	loadwave/A=YAML/J/K=2/M/V={":","",0,1} fName
	fName = StringFromList(0,s_wavenames)
	wave/T yw = $fName
	if (DimSize(yw,0) != 0)
		duplicate/O/T yw YAMLwave
	else
		killwaves/Z yw
		return -4
	endif
	killwaves/Z yw	
	return 0
end

I would parse the text wave as the next step.

Now a question. What would you suggest to someone who would want to operate with one YAML file that has multiple themes rather than multiple YAML files, each with their own theme? Good idea, bad idea, or ... just neutral at this point?

theme: 
 name: Desert Sage
 keyword: #6288ba
 comment: #989898
 string: #cb8076
 operation: #739079
 bifunction: #b98a51
 userfunction: #b98a51
 directive: #c76060
 opkey: #4f92c5
theme: 
 name: Monochrome
 keyword: #989898
 comment: #989898
 string: #989898
 operation: #989898
 bifunction: #989898
 userfunction: #989898
 directive: #989898
 opkey: #989898
theme:
 ...

And further, in case you wonder, I am building a way to use YAML to store graph styles that can be applied through popup control menus.

April 26, 2025 at 06:27 pm - Permalink

jjweimer

I answered my own question. The more robust approach is to keep each YAML as its own file at the OS level.

Here is a function to recast a YAML text wave into a text wave with the (theme or whatever) name. The first row after the validation string must be of the form "name: (name)"

Function recast_YAML(wave/T ywave)
	variable npts = DimSize(ywave,0)
	string wname
	
	duplicate/FREE/T ywave twave
	// remove tab characters
	twave[][] = ReplaceString("\t",twave[p][q],"")
	// validate that name exists
	if (cmpstr(twave[1][0],"name") != 0)
		return -1
	endif
	
	// clean up wave first and last blank
	DeletePoints 0,1, twave
	if (cmpstr(twave[npts-2][0],"") == 0)
		DeletePoints (npts-2),1, twave
	endif
	// create new text wave
	wname = twave[0][1]
	duplicate/O twave, $wname
	
	return 0
end

The approach is to load_YAML(...), recast_YAML(...), and process_YAML(...), with the latter written to carry out whatever coding is needed. With the YAML in a text matrix, the process_YAML code does not need to use the SplitString operation to pull off two sides around a colon.

April 27, 2025 at 08:31 am - Permalink

Ben Murphy-Baum

I am curious how the in-line STRUCT definition does not throw an error. Somehow, in the far corners of my memory, I seem to recall reading that STRUCT definitions have to be on separate lines, not in-line, for function definitions. Also, the definition seems to have a mistake in spacing that does not throw even a warning???

You're right that (maybe all) of our examples in the documentation use the old-style structure definitions that are on a separate line, but I think inline should be fine. As for the position of the ampersand, that's mostly a matter of personal preference -- both of these are acceptable to the compiler, similar to how passing references or pointers works in C++.

struct RGBColor& color
struct RGBColor &color

I find the first example easier to read, where the type can be read more clearly as 'RGBColor reference' and the name is 'color.

April 28, 2025 at 09:48 am - Permalink

Forum

Support

Gallery

Igor Pro 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More

AltStyle によって変換されたページ (->オリジナル) /