Loading syntax highlighting themes

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)

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

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.

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.

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.

Forum

Support

Gallery

Igor Pro 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More