Write XPPAUT models (.ode, .inc) with highlighting, refactoring and checks that catch the mistakes XPP itself accepts silently.
- Diagnostics: missing
done, unbalanced brackets, reserved or duplicate names, undefined and unused names, options XPP would silently ignore or misread, and operator-precedence traps such as2*-3or2*3<4. - Custom colours per variable, parameter or category, shared by every model in a folder.
- Descriptions on hover: document a name in a
#comment, like a docstring, and hovering it shows the text. - Rename (
F2) and highlight-all-occurrences for variables, parameters and functions, across#included files; "Extract to Variable" from the context menu. - Syntax highlighting, bracket matching, and commenting with
Ctrl+/. - A "Run ODE File" button that starts xppaut on the current file, with setup notes for Linux, macOS and Windows.
-
An error for a
#comment after the names of apar,number,initorwienerline: XPPAUT reads its words as names (par gr=0.01 # Changed on Octdeclares#,Changed,onandOct). The error links to the XPPAUT issue, and a quick fix moves the comment above the line as# gr: Changed on Oct, where it describes the name on hover. See where to write a description. -
An error for a name that is already a member of an array: with
x[1..3]'=-x[j],par x2=5stops XPP withDuplicate name X2. Readingx2is fine. -
One hover format: the declaration's
file:line, then each description with itsfile:line, the reference in grey italics.{j}, array selections in.xppsettings.jsonand several descriptions in one;comment are described below. -
An
.odenow reads its whole#includetree (an include inside an included file too), for hovers and for undefined names.
More checks for what XPP reads differently from how it looks, each measured on xppautX:
- An error for
table g @ file: XPP has no two-dimensional tables and stops loading. - An error for a Markov cell without its closing
}({0} {a): XPP reads on past the end of the line. - An error for two
markovlines with the same name: the model does not load. - A warning for a derived parameter reading something that changes:
!d=yis worked out at the run's start and stays aty's first value. A quick fix removes the!.
- Operator-precedence checks: an error for a sign XPP rejects (
2*-3) and for operators XPP does not have (!=,&&), with quick fixes; a warning where a comparison,&or|silently regroups arithmetic (2*3<4is2*(3<4)). - Initial values checked the way XPP reads them:
init y=2*3startsyat 2, andy(0)=aat 0. @option values checked the way XPP reads them:@ total=2*3is 2.- Descriptions on hover from
#comments and from.xppsettings.json. .xppcolors.jsonbecomes.xppsettings.jsonandxpp-ode.identifierColorsbecomesxpp-ode.variables; the old names still work for now (how to move).
Every release is listed in the changelog.
The extension checks each .ode/.inc file as you type and reports:
- Errors, for what stops XPP loading the file or makes it ignore a line: missing
done, unbalanced brackets, reserved words used as names, duplicate or conflicting names,solvlines with spaces around=, a sign where XPP allows none (2*-3,x^-2), operators XPP does not have (!=,&&,||,!x), anifnot writtenif(c)then(a)else(b), aninitvalue that is not a plain number (init y=2*3starts at 2),@options XPP drops because they are not exactlyname=value, numeric@values that are not plain numbers (@ total=2*3is 2, not 6), a two-dimensionaltable g @ file(not implemented in XPP), a Markov cell without its closing}, twomarkovlines with the same name, a#comment after the names of apar,number,initorwienerline (XPPAUT reads its words as names), and a declaration of a name that is already a member of an array (par x2=5besidex[1..3]:Duplicate name X2). - Warnings, for what loads but probably does not mean what you wrote: text after
done(shown dimmed; XPP stops reading there), undefined names, unused parameters, fixed variables and functions (shown faded), initial conditions for names that are not state variables, lines XPP silently skips, unknown@option names, fixed variables named like a keyword (p=1), a comparison,&or|next to arithmetic (2*3<4,-1<0,a<b<c,a|b-c), a formula iny(0)=(ystarts at 0), a division by a literal0(XPP silently gives 4.5e14, not an error), and a derived parameter reading something that changes (!d=yis worked out at the run's start and stays aty's first value). - Information, for legal surprises:
2^3^2is(2^3)^2,-2^2is-(2^2), and inif(c)then(a)else(b)+5the+5applies to the wholeif.
Names defined in #included files count as defined, also in files included by those files. An .ode reads its whole include tree; an .inc does not read the .ode that includes it, because it cannot know which one that is. So inside an .inc file the undefined-name check is off, and hovering a name that only the .ode declares shows nothing. If your project uses .inc files, give the names they share a look in .xppsettings.json by name ("gk": "#c00"): a name key colours the name wherever it is written, declared in that file or not. The @states-style categories and descriptions need the name declared in the file (or in a file it includes).
The XPP syntax rules behind these checks
All of these were verified by running files through xppaut 8.0.
How XPP decides what a line is
| Line looks like | XPP reads it as |
|---|---|
word name ... (a word, a space, then a name) |
a declaration chosen by the first letters of word |
word=... or word = ... |
a fixed variable called word, whatever word is |
x'=, dx/dt=, x(t+1)=, x(t)= |
a state variable |
x(0)= |
an initial condition |
f(a,b)= |
a function |
!a= |
a derived parameter |
0= |
an algebraic condition |
@ ... |
options |
#... or "... |
a comment |
a first word starting with d that is not followed by =, ', (, [ or /dt (done, d, done # notes, even done x=1) |
end of file, everything after it is ignored |
| anything else | silently ignored |
Keyword prefixes. Only the first letters of the keyword matter: p, par, param, params and parameter all declare parameters. Single letters work for p(ar), i(nit), w(iener), n(umber), g(lobal), b(dry), v(olt), o(ptions) and d(one); two letters are needed for au(x), ma(rkov), ta(ble), se(t), so(lv), sp(ecial), ex(port), im(port) and on(ly). The separator after the keyword must be a space; a tab makes XPP read init<tab>x=5 as a fixed variable named initx.
Keywords are not reserved names. p=1, par=1, done=1 and dt=1 are all legal fixed variables, because the = directly after the word wins. The extension only warns about them because they are easy to misread. The names XPP really rejects are the builtin functions (sin, heav, delay, ...), if/then/else, arg1..arg9, t, pi and set.
Where spaces around = matter.
| Form | Spaces around = |
|---|---|
@ dt=0.1,total=100 |
not allowed: @ dt = 0.1 is silently ignored and the default is used |
solv y=-.5 |
not allowed: XPP fails to load the file |
par a = 1, init x = 0, aux z = x, x' = -x, f(x) = 2*x, x(0) = 1, !a = b*2, 0 = y-x, global 1 x-1 {x = 0} |
allowed |
Lists. par and init items may be separated by commas or by spaces (par a=1 b=2), and a parameter may be listed without a value (par ind, which gives it 0).
Comments and #. # starts a comment except inside braces, where it is the Volterra convolution operator: y(t)=int{exp(-t)#x}.
XPP's expression parser groups some expressions differently from ordinary maths, and refuses others outright. This is long-standing XPPAUT behaviour that existing models rely on; the extension only makes it visible. Hover over ^, ** or a comparison for the rule, and use the quick fix (Ctrl+.) on a rejected sign.
| you write | XPP reads it as | value | reported as |
|---|---|---|---|
2*-3 |
— | the file does not load; quick fix: 2*(-3) |
error |
2*3<4 |
2*(3<4) |
2 (not 0) | warning |
1+2<3+4 |
1+(2<3)+4 |
6 (not 1) | warning |
-1<0 |
-(1<0) |
-0, i.e. false (not true) | warning |
a<b<c |
(a<b)<c |
3<2<1 is true |
warning |
a|b-c |
(a|b)-c |
2|0-1 is 0 (not 1) |
warning |
1+1&1 |
1+(1&1) |
2 (not 1) | warning |
a!=b, a&&b, a||b, !a |
— | the file does not load; quick fix: not(a==b), a&b, a|b, not(a) |
error |
if 1>0 then 10 else 20 |
— | the file does not load: only if(c)then(a)else(b) does |
error |
2^3^2 |
(2^3)^2 |
64 (ordinary notation means 512) | information |
-2^2 |
-(2^2) |
-4 (not 4) | information |
It all follows from one priority table: comparisons bind tighter than all arithmetic, the opposite of nearly every other language, and unary minus binds more weakly than both ^ and the comparisons.
| priority | operators |
|---|---|
| 7 | ^, **, and every comparison: < > <= >= == != |
| 6 | *, /, &, and unary minus (a separate operator, ~) |
| 4 | binary +, -, | |
Each rule, with examples
Everything below was confirmed by compiling the expression with add_expr() and running it through evaluate() in xppautX, and the "will not load" cases by running the file through xppautX -silent.
2^3^2^2 is ((2^3)^2)^2 = 4096. ** is the same operator, so 2**3**2 is 64 too.
x'=-a*x^2^3 # XPP: (x^2)^3, i.e. x^6
x'=-a*(x^2)^3 # the same thing, written so it cannot be misread
x'=-a*x^(2^3) # what ordinary notation would have meant: x^8
This is usually what you wanted anyway, which is why it is only an information message:
aux e=-x^2 # -(x^2): negative for every x -- and normally what you meant
aux e=(-x)^2 # x^2: positive for every x
It applies to scientific notation as well: -3E-5^2 is -(3E-5^2) = -9e-10. The minus inside 1e-3 belongs to the number, not to an operator, so 1e-3^2 is (1e-3)^2 = 1e-6.
This is the one that quietly ruins results, so it is a warning. XPP evaluates the comparison first and then does the arithmetic on its 0 or 1:
2*3<4 # XPP: 2*(3<4) = 2 -- not (2*3)<4 = 0
3-1<2 # XPP: 3-(1<2) = 2 -- not (3-1)<2 = 0
1+2<3+4 # XPP: 1+(2<3)+4 = 6 -- not (1+2)<(3+4) = 1
1/2<1 # XPP: 1/(2<1) -- a division by zero
(2*3)<4 # bracket what you want compared
A leading sign is the same rule, and it flips if branches silently:
f(t)=if(-t<0)then(a)else(b) # XPP: if(-(t<0)) -- always -0, i.e. always the else branch
f(t)=if((-t)<0)then(a)else(b) # what you meant
-1<0 is -(1<0) = -0, which is false even though -1 really is less than 0; and -1>=0 is -(1>=0) = -1, which is true (any non-zero value is). Operators weaker than a comparison need no brackets: 1<2&3<4 is (1<2)&(3<4), and 2^2<3 is (2^2)<3, both as expected.
Anywhere else XPP rejects the expression and the file does not load at all (ERROR compiling X'), so the extension reports it as an error:
x'=2*-3 # rejected -- write 2*(-3)
x'=x^-2 # rejected -- write x^(-2)
x'=a+-b # rejected -- write a+(-b)
f(t)=if(t<-1.3)... # rejected -- write if(t<(-1.3))
x'=+2*a # rejected -- XPP has no unary "+" at all, not even as "(+2)"
x'=-3E-5*a # fine: the sign starts the expression
A par value is a plain number, not an expression, so par a=-3E-5 is fine. The quick fix writes the working form for you: it brackets a minus (2*-3 becomes 2*(-3)) and drops a plus (2*+3 becomes 2*3).
Comparisons group to the left, so a<b<c is (a<b)<c: the second comparison tests the first one's 0 or 1 against c:
0<x<1 # XPP: (0<x)<1 -- true only when x is NOT above 0
(0<x)&(x<1) # what you meant
3<2<1 is (3<2)<1 = 0<1 = 1, true, although neither half holds.
& has the priority of * and /, and | that of + and -, so they group with arithmetic from left to right instead of after it:
1+1&1 # XPP: 1+(1&1) = 2 -- not (1+1)&1 = 1
a|b-c # XPP: (a|b)-c -- not a|(b-c)
a*b&c # (a*b)&c, as expected: no warning
(a+b)&c # bracket what you want combined
!= is in XPP's operator table but its reader stops at the !, so there is no working not-equal. &&, || and a ! in front of a value are not XPP operators either. All of them stop the file loading; the quick fix writes not(a==b), &, | or not(a). (!name=... at the start of a line is a derived parameter and is fine.)
Only if(c)then(a)else(b) loads. XPP removes spaces before reading, so if(c)then 10 else 20 becomes then10else20, and the e of else is read as the exponent of 10 (illegal expression: 10EL). An if without else does not load either. An operator right after the else part applies to the whole if: if(1>0)then(10)else(20)+5 is 15.
XPP replaces a zero divisor with about 2.2e-15, so 1/0 is 4.5e14 and 0/0 is 0, with no error. A divisor that is literally 0 is a warning.
The error is always reported, like everything else XPP rejects. The advisory findings have one setting each:
An @ line is not made of expressions. XPP splits it on commas and spaces (@ dt=.05 meth=cvode total=100 sets all three), and every piece must be exactly name=value. Numeric values are read with atof(), which stops at the first character that cannot be part of a number and never reports an error:
| you write | XPP uses | |
|---|---|---|
@ total=2*3 |
2 |
not 6: @ values are not expressions |
@ total=4abc |
4 |
|
@ total=(4) |
0 |
atof finds no number to start with |
@ total= |
default | the option is dropped entirely |
@ total= 4 |
default | the value was split off, so the piece is not name=value |
@ total = 4 |
default | reads as the three unrelated words total, =, 4 |
@ t0=-5 |
-5 |
negative values are fine |
@ total=1e1 |
10 |
as is scientific notation |
Work the value out yourself, or put it in a par and use that in your equations. Options that take a name, a file or a keyword (meth=cvode, xp=x, output=out.dat) are left alone.
Like @ values, init values are plain numbers read with atof(), and an initial condition y(0)= is a number too:
| you write | y starts at |
reported as |
|---|---|---|
init y=2*3 |
2 | error |
init y=a (with par a=2) |
0 | error |
y(0)=a |
0: the formula is only kept as y's history for delay equations |
warning (information in a model that uses delay) |
y(0)=2*3 |
2 | warning |
x[1..5](0)=a*[j] |
a, 2a, ... as written: array conditions are evaluated |
— |
init y=-1.5e-3, y(0)=.5 |
as written | — |
Work the value out yourself, or, for an array, use the x[1..n](0)= form.
Give any name in your models its own look, independent of the theme: the membrane voltage always red, every parameter bold green, builtins italic, one variable in a box. The colours follow the parser, so a parameter is coloured everywhere it is used, not only on its par line.
Put a .xppsettings.json in a folder, and it applies to every .ode/.inc file in that folder and its subfolders:
{
"variables": {
"@states": "#ff7b72",
"@parameters": { "color": "#7ee787", "fontWeight": "bold" },
"@fixed": { "color": "#d2a8ff" },
"@builtins": { "fontStyle": "italic" },
"@options": { "opacity": 0.6 },
"v": { "color": "#ffffff", "borderColor": "#ff7b72", "borderRadius": "3px" },
"g_*": { "textDecoration": "underline" },
"iapp": { "light": { "color": "#a00000" }, "dark": { "color": "#ff8888" } }
}
}Reading it: state variables are salmon and parameters bold green everywhere they appear. v is white in a salmon box, because an exact name wins over its group. Every name starting with g_ (g_na, g_k, g_l) is underlined in the theme's colour, because a wildcard wins over its group and replaces it entirely. iapp is dark red on light themes and pale red on dark ones. Option names on @ lines are faded.
A function's arguments are local to its line: in iion(v,w)=... the w is not the state array w, so it is not coloured like it.
Hex colours show a swatch you can click for the colour picker. Completion (Ctrl+Space, or typing " or @) offers the groups, every name declared in the folder's models with its kind, and the style properties with their allowed values; misspelled properties and invalid values are underlined. Changes apply immediately.
| Place | Applies to | How |
|---|---|---|
"variables" in a .xppsettings.json in a folder |
every .ode/.inc file in that folder and its subfolders |
create the file; a file in a subfolder overrides one in a parent folder, key by key |
xpp-ode.variables in .vscode/settings.json |
the whole workspace | Settings > search "XPP-ODE" > edit in settings.json |
xpp-ode.variables in user settings |
every workspace | same |
Both places take the same object: the setting holds it directly, the file under its "variables" key. Files override the setting, key by key (see Which rule applies).
| Key | Meaning | Example |
|---|---|---|
| a name | that identifier, case-insensitive; an array name also covers its members | "v", "gsyn", "u" (covers u[j], u[0..9], u0...u9) |
a name with * |
every identifier matching the pattern; * stands for any letters, digits or _ (also none), at the end, the start or in the middle |
"v_*", "*_syn", "u*x" |
| an array with members | only those members, as in descriptions: a list, a range, or both | "vm[2, 4]", "x[1..3, 7]" |
@group |
a whole category, resolved by the parser | "@states", "@parameters" |
| Group | Contains |
|---|---|
@states |
state variables: x'=, dx/dt=, x(t+1)=, x(t)=, solv |
@parameters |
par and number parameters, !name= derived parameters |
@fixed |
fixed variables name=expression |
@functions |
user functions f(x)= |
@aux |
aux quantities |
@wiener |
wiener variables |
@markov |
markov variables |
@tables |
table and special names |
@options |
option names on @ lines (dt, total, xp, ...) |
@builtins |
builtin functions and constants (sin, heav, t, pi, ...) |
@keywords |
declaration keywords (par, init, aux, done, ...) |
Comments and text after done are never coloured.
Every name gets at most one entry, chosen by these rules in order:
- Exact name (
"v"). For an array name, its members too ("u"coversu0...u9), unless a member has its own entry. - Wildcard (
"g_*"). When several match, the one listed last wins. - Group (
"@parameters"). A name belongs to one group only, so groups never compete.
The chosen entry replaces the others; properties are not combined. With "@parameters": { "fontWeight": "bold" } and "gna": "#ff0000", gna is red and not bold. Whatever the entry leaves out comes from the theme, not from a lower rule; to keep the bold, write it again: "gna": { "color": "#ff0000", "fontWeight": "bold" }.
When the same key appears in several places, the one closest to the file wins: the setting, then each .xppsettings.json from the workspace root down to the file's own folder, each overriding the one before for that key. The look and the description are overridden separately, so an entry that only adds a description keeps the colours given further up. The order above is applied afterwards, whatever the source: an exact name in your user settings still beats a wildcard in the folder's .xppsettings.json. Wildcards from all sources form one list, with the closer files' wildcards after (and so above) the setting's.
Inside a style, light/dark properties override the base properties for that kind of theme.
A value is either a hex colour string or a style object.
"v": "#ff7b72"
"v": { "color": "#ff7b72", "fontWeight": "bold" }| Property | Allowed values | Notes |
|---|---|---|
color |
#rgb, #rrggbb, #rrggbbaa |
text colour |
backgroundColor |
hex colour | use aa for a translucent highlight, e.g. #ffff0040 |
fontWeight |
bold, normal |
|
fontStyle |
italic, normal |
|
textDecoration |
underline, line-through, overline, underline wavy, underline dotted, underline dashed |
|
opacity |
0 to 1 |
0.5 fades the name |
borderColor |
hex colour | alone it draws a 1px solid box |
borderStyle |
solid, dashed, dotted, double |
|
borderWidth |
length: 1px, 0.1em |
|
borderRadius |
length: 3px |
rounded box corners |
light |
object with the properties above | applied only in light themes |
dark |
object with the properties above | applied only in dark themes |
description |
text | shown when hovering the name, see Descriptions on hover |
Every VS Code theme declares itself as light, dark or high-contrast; light/dark entries are layered on top of the base properties for that kind of theme. Entries the extension cannot use (unknown group, bad colour, unknown top-level key, ...) are skipped and reported once as a warning; the rest still apply.
.xppcolors.json and the xpp-ode.identifierColors setting are deprecated. They still work in this release, with a warning, but will be removed:
- move the content of a
.xppcolors.jsonunder"variables"in a.xppsettings.jsonin the same folder, then delete the old file. While both exist,.xppsettings.jsonwins key by key; - rename the setting
xpp-ode.identifierColorstoxpp-ode.variables; its value is unchanged. While both are set,xpp-ode.variableswins key by key.
Write what a name means in a # comment, like a docstring, and hovering the name anywhere in the code shows it with the name's kind:
# gna: Maximal sodium conductance (mS/cm^2)
par gna=120
gna — parameter (hh.ode:2)
Maximal sodium conductance (mS/cm^2) (hh.ode:1)
Every hover reads the same way: the name, its kind and the file:line where it is declared, then each description with the file:line it was written at, in grey italics, then the descriptions from .xppsettings.json with their key. The file is shown for every line, also for names declared in #included files.
Names declared in #included files show their descriptions too, and so do the files those include in turn.
Above the declaration, one # name: text line per name:
# ---- sodium current ----
# gna: maximal sodium conductance (mS/cm^2)
# ena: sodium reversal potential (mV)
par gna=120, ena=50
The rules:
- One line per name, or several with
;.# gk: maximal K; gl: leakdescribes both names, as long as every part isname: textfor a name on the declaration line; otherwise the;is ordinary text and the whole comment goes to the first name. - Directly above. The comment lines must touch the declaration. A blank line, or a line holding only spaces, between them breaks the link; a comment below the declaration, or further up, describes nothing. Comment lines without a key, such as the
# ---- sodium current ----header, are ignored and do not break it. - Several declarations, several blocks. Each declaration line takes the block of comments right above it.
- Spaces and case are free.
#gna:text,# gna : text,# gna: textand# GNA: textall work, and so do tabs. The key needs its:and some text after it. - The key must be a name on that line. A key that is not (
# zzz: textabovepar gna=120) is ignored; for an array member the array does not have, it is a warning.
After the code, on an equation line (or on any line of a line continued with \): the whole comment describes the one name on the line.
x'=-x/tau # membrane voltage of the cell (mV)
Not after
par,number,initorwiener. XPPAUT reads a#comment only at the start of a line. After the names on these lines it makes a name of#and of every word after it:par gr=0.01 # Changed on Oct 6thdeclares the parameters#,Changed,on,Octand6th, and a second such line stops the load because#is declared twice (XPPAUT issue 11). The extension marks it as an error with a link to that issue, still shows the text on hover, and its quick fix (Ctrl+.) moves the comment to its own line above.
An array such as x[1..10] is one name for a plain comment, and keys can pick its members:
| Key | Describes |
|---|---|
x: text |
the array and every member |
x[3..5]: text |
members x3..x5 |
x[3, 5]: text |
members x3 and x5; lists and ranges mix: x[1..3, 7, 9] |
x7: text or x[7]: text |
only x7; the two spellings mean the same, so the one written later comes first in the hover |
# x: membrane voltage of cell j (mV)
# x[1..3]: excitatory cells
# x7: the pacemaker cell
x[1..10]'=-x[j]+i_syn[j]
A key for a member the array does not have (x11: or x[9..12]: above) is a warning.
{j} is the member's number. Write {j} in a description, in a comment or in .xppsettings.json, and hovering x7 shows 7 in its place:
# x: membrane voltage of cell {j} (mV)
# x[1..3]: excitatory cell {j}
x[1..10]'=-x[j]+i_syn[j]
x2 — state variable (array x[1..10]) (m.ode:3)
excitatory cell 2 (m.ode:2 — from x[1..3])
membrane voltage of cell 2 (mV) (m.ode:1 — from x)
Hovering the array x itself, or a name that is not an array member, leaves {j} as written.
A name can have descriptions at several levels, and the hover shows them all, most specific first:
- its own:
x7:, or the plain comment on its own line; - a selection:
x[1..3]:,x[3, 5]:; - inherited:
x:seen from a member, or a comment shared by the names of a line.
x7 — state variable (array x[1..10]) (m.ode:4)
the pacemaker cell (m.ode:3)
membrane voltage of cell 7 (mV) (m.ode:1 — from x)
Two descriptions of one variable are a mistake: gna described both above its line and after it, or twice above. The later one wins (a comment after the code counts as later than the lines above), and the other is marked with a warning "Description of "gna" is overridden by line 12" that links to the winner. Its quick fix (Ctrl+.) removes the overridden description, or only that part when it shares a comment line with others. Array members are exempt: x3 may be described by x:, x[1..3]:, x[3, 5]: and x3: all at once, and the hover shows them all, the later one first.
For names in files you cannot edit, or shared by a folder of models, add a description to the entry in .xppsettings.json or xpp-ode.variables. An entry may hold only a description, which leaves the colours alone:
{
"variables": {
"gna": { "color": "#7ee787", "description": "Maximal sodium conductance (mS/cm^2)" },
"v": { "description": "Membrane potential (mV)" },
"g_*": { "description": "A conductance" },
"@parameters": { "description": "Units: mS/cm^2 unless stated" }
}
}These are shown after the comments, each with its key. Unlike colours, the levels do not hide each other: a name gets the description of its own entry, of every matching wildcard (last listed first) and of its group. An array member also gets its array's ("x" for x7). Keys that pick members ("x[1..3]") work here too ("x[7]" is the same entry as "x7": the one listed last wins), and apply to those members after their own entry and before the whole array's (the last one listed wins when several pick the same member). Colours follow the same order: a member's own entry, then the selections, then the array.
The "Run ODE File" button (editor title bar of any .ode file) saves the file, opens an integrated terminal in the file's folder, and runs:
<xpp-ode.runCommand> "<file name>.ode"
It runs in the file's folder because xppaut looks for #included files, table files and dll_lib libraries relative to the directory it is started from, and writes its output files there too.
The default command is xppaut, which works when xppaut is on your PATH. To change it: Settings (Ctrl + , / Cmd + ,) > search "XPP-ODE" > Run Command. Typical values:
| Setup | Run Command |
|---|---|
Linux, xppaut installed from the package manager or make install |
xppaut |
| macOS with XQuartz, xppaut not on the PATH | /usr/local/bin/xppaut (or wherever you installed it) |
| Windows, xppaut installed inside WSL (with WSLg or an X server) | wsl xppaut |
Windows, the Cygwin build from xppwin.zip |
C:\xppall\xppaut.exe (see below) |
| Extra options for every run | xppaut -xorfix, xppaut -silent, ... |
Since only the file name is passed, wsl xppaut works without translating Windows paths: WSL starts in the same folder. The setting can be set per workspace (.vscode/settings.json), so a project can carry its own command.
Windows with the Cygwin build
The Windows xppaut.exe is an X11 program. It needs two things, or it exits with "Failed to open X-Display":
- An X server running, such as Xming or VcXsrv. It sits in the tray once started.
- The
DISPLAYvariable telling xppaut where that server is. Starting the server does not set it; thexpp.batshipped with xppaut setsDISPLAY=127.0.0.1:0.0for this reason.
The extension handles both with xpp-ode.display and xpp-ode.xServer (see Settings). Before each run it checks whether a server is listening on the display. If none is and xpp-ode.xServer is set, it starts the server and waits for it; otherwise it shows a warning naming the address and the fix, and still runs xppaut so you see its own message too.
Do not chain the server into the run command (xming && xppaut): && waits for the first program to exit, and an X server never exits.
A complete Windows .vscode/settings.json:
{
"xpp-ode.runCommand": "C:\\xppall\\xppaut.exe",
"xpp-ode.xServer": "\"C:\\Program Files (x86)\\Xming\\Xming.exe\" :0 -multiwindow -clipboard"
}| Setting | Default | Effect |
|---|---|---|
xpp-ode.variables |
{} |
colours, styles and descriptions per name, wildcard or group |
xpp-ode.precedence.comparison |
warning |
how to report a comparison next to arithmetic: warning, information, hint or off |
xpp-ode.precedence.logical |
warning |
how to report & or | next to arithmetic, same values |
xpp-ode.precedence.power |
information |
how to report 2^3^2 and -2^2, same values |
xpp-ode.debounceDelay |
300 |
milliseconds to wait after typing before the file is checked again |
xpp-ode.runCommand |
xppaut |
the command the Run ODE File button runs; the file name is appended |
xpp-ode.display |
127.0.0.1:0.0 |
Windows only: DISPLAY given to xppaut when not already set |
xpp-ode.xServer |
empty | Windows only: X server command line to start when none is running, e.g. "C:\Program Files (x86)\Xming\Xming.exe" :0 -multiwindow -clipboard |
- Handle active comments.
.anianimation files: highlighting, and counting their references as uses of the.odenames.
Found a problem or want a feature? Open an issue or email me.
- This extension is a fork of Joe-McCann's XPP-ODE-Extension, which added the first highlighting of reserved functions, derivatives and comments, for any theme.
- Logo designed by Manar Moustafa.
- Thanks to Nianqi Deng for suggesting the Run ODE File button, and to Leqi (Sammy) Wang for suggesting custom colours.



