Skip to content

About

VS Code extension for XPP/XPPAUT ODE files

Resources

Stars

3 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

XPP - VSCode Extension for XPPAUT Files

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 as 2*-3 or 2*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.

Colours typed rule by rule in .xppsettings.json: a group, a wildcard, and a description that shows on hover

What's new in 0.4.3

  • An error for a # comment after the names of a par, number, init or wiener line: XPPAUT reads its words as names (par gr=0.01 # Changed on Oct declares #, Changed, on and Oct). 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=5 stops XPP with Duplicate name X2. Reading x2 is fine.

  • One hover format: the declaration's file:line, then each description with its file:line, the reference in grey italics. {j}, array selections in .xppsettings.json and several descriptions in one ; comment are described below.

  • An .ode now reads its whole #include tree (an include inside an included file too), for hovers and for undefined names.

0.4.2

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 markov lines with the same name: the model does not load.
  • A warning for a derived parameter reading something that changes: !d=y is worked out at the run's start and stays at y's first value. A quick fix removes the !.

0.4.1

  • 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<4 is 2*(3<4)).
  • Initial values checked the way XPP reads them: init y=2*3 starts y at 2, and y(0)=a at 0.
  • @ option values checked the way XPP reads them: @ total=2*3 is 2.
  • Descriptions on hover from # comments and from .xppsettings.json.
  • .xppcolors.json becomes .xppsettings.json and xpp-ode.identifierColors becomes xpp-ode.variables; the old names still work for now (how to move).

Every release is listed in the changelog.


Diagnostics

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, solv lines with spaces around =, a sign where XPP allows none (2*-3, x^-2), operators XPP does not have (!=, &&, ||, !x), an if not written if(c)then(a)else(b), an init value that is not a plain number (init y=2*3 starts at 2), @ options XPP drops because they are not exactly name=value, numeric @ values that are not plain numbers (@ total=2*3 is 2, not 6), a two-dimensional table g @ file (not implemented in XPP), a Markov cell without its closing }, two markov lines with the same name, a # comment after the names of a par, number, init or wiener line (XPPAUT reads its words as names), and a declaration of a name that is already a member of an array (par x2=5 beside x[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 in y(0)= (y starts at 0), a division by a literal 0 (XPP silently gives 4.5e14, not an error), and a derived parameter reading something that changes (!d=y is worked out at the run's start and stays at y's first value).
  • Information, for legal surprises: 2^3^2 is (2^3)^2, -2^2 is -(2^2), and in if(c)then(a)else(b)+5 the +5 applies to the whole if.

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}.

Operator precedence

Errors, warnings and hovers for signs, comparisons, powers and @ option values

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.

1. ^ groups to the left

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

2. Unary minus binds more weakly than ^

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.

3. Comparisons bind tighter than arithmetic

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.

4. A sign is only allowed at the start, after ( or after ,

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).

5. a<b<c does not mean what it says

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.

6. & is a *, and | is a +

& 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

7. Operators XPP does not have

!= 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.)

8. if needs every part in brackets

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.

9. Division by zero is silent

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:

// .vscode/settings.json -- "warning", "information", "hint" or "off"
"xpp-ode.precedence.power": "information",   // 2^3^2 and -2^2
"xpp-ode.precedence.comparison": "warning",  // 2*3<4, 1+2<3+4, -1<0, a<b<c
"xpp-ode.precedence.logical": "warning"      // 1+1&1, a|b-c

@ option values

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.

Initial values

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.

Custom colours for variables and parameters

Colours typed rule by rule in .xppsettings.json

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.

Where to put the configuration

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).

Keys

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.

Which rule applies

Every name gets at most one entry, chosen by these rules in order:

  1. Exact name ("v"). For an array name, its members too ("u" covers u0...u9), unless a member has its own entry.
  2. Wildcard ("g_*"). When several match, the one listed last wins.
  3. 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.

Values

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.

Moving from .xppcolors.json

.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.json under "variables" in a .xppsettings.json in the same folder, then delete the old file. While both exist, .xppsettings.json wins key by key;
  • rename the setting xpp-ode.identifierColors to xpp-ode.variables; its value is unchanged. While both are set, xpp-ode.variables wins key by key.

Descriptions on hover

A description written above a declaration, the # comment after the names that XPPAUT reads as names, its quick fix, and several names sharing one comment

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.

Where to write a description

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: leak describes both names, as long as every part is name: text for 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: text and # GNA: text all 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: text above par 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, init or wiener. 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 6th declares the parameters #, Changed, on, Oct and 6th, 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.

Arrays

{j}, ranges and single members as description keys, the same keys in .xppsettings.json, layered descriptions, and the quick fix for a variable described twice

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.

Several descriptions for one name

A name can have descriptions at several levels, and the hover shows them all, most specific first:

  1. its own: x7:, or the plain comment on its own line;
  2. a selection: x[1..3]:, x[3, 5]:;
  3. 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.

Descriptions in .xppsettings.json

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.

Run ODE File

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":

  1. An X server running, such as Xming or VcXsrv. It sits in the tray once started.
  2. The DISPLAY variable telling xppaut where that server is. Starting the server does not set it; the xpp.bat shipped with xppaut sets DISPLAY=127.0.0.1:0.0 for 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"
}

Settings

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

Future work

  • Handle active comments.
  • .ani animation files: highlighting, and counting their references as uses of the .ode names.

Feedback and credits

Found a problem or want a feature? Open an issue or email me.

About

VS Code extension for XPP/XPPAUT ODE files

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages