NetStacksNetStacks

Network CLI (netcli) Highlighting

How NetStacks highlights Cisco and Junos config backups in Monaco with the custom netcli grammar, plus the YANG, XML, and JSON format providers.

Overview

When you open a saved device configuration in the NetStacks Workspace editor (Monaco), CLI backups are highlighted with a custom language called netcli. It is a single, vendor-agnostic grammar that covers both major config families:

  • Cisco-style — IOS, IOS-XE, IOS-XR, and NX-OS, with ! comments and indentation-scoped stanzas.
  • Junos-style{ } braces, set/delete statements, # comments, and [ ... ] value lists.

Rather than ship two grammars plus a vendor detector, netcli tokenizes the lexical features the two families share — comments, stanza keywords, IP/CIDR/MAC literals, numbers, strings, and brackets. The result is readable highlighting for any backup without having to tell the editor which vendor wrote it.

Always available, fully offline

netcli is a client-side Monaco language. It needs no download, no Agent, and no network access, and it works in every build — the standalone Terminal and the Controller-served web client alike. The same is true of the YANG, XML, and JSON support described below.

How It Works

NetStacks registers its language features on the Monaco instance once at app startup, after the Monaco environment is configured but before any editor is created. A single bootstrap call wires up YANG, the XML format provider, and netcli. JSON is intentionally left to Monaco's own bundled worker.

Language Registration

At bootstrap the app calls a single registration function that adds every NetStacks-specific language feature to Monaco. For netcli it registers the language id, attaches a Monarch tokenizer, and sets a language configuration (brackets, comments, auto-closing pairs):

languages/index.tstypescript
// Network CLI — Cisco/Junos config backups
// (no built-in Monaco language, so NetStacks adds one).
monaco.languages.register({
  id: 'netcli',
  aliases: ['Network CLI', 'netcli'],
});
monaco.languages.setMonarchTokensProvider('netcli', netcliLanguage);
monaco.languages.setLanguageConfiguration('netcli', netcliLanguageConfig);

Unlike YANG (which is registered with a .yang file extension), netcli is registered with aliases only. Backups are not files on disk with a recognizable extension, so the editor selects the language by the backup's stored format rather than by filename — see the next section.

One grammar, case-insensitive

The netcli Monarch grammar runs case-insensitively (ignoreCase), so INTERFACE, Interface, and interface are all recognized as the same keyword. YANG, by contrast, is case-sensitive because YANG statement names are.

How Backups Pick a Language

Every stored device config carries a config_format string (for example cli, json, xml). When the Backup History view opens a version, it maps that format to a Monaco language id. CLI backups resolve to netcli:

components/BackupHistoryTab.tsxtypescript
// Map a backup's config_format to a Monaco language id.
// CLI configs use the custom 'netcli' grammar (Cisco/Junos).
function backupLanguage(format: string): string {
  switch (format.toLowerCase()) {
    case 'json':
      return 'json';
    case 'xml':
    case 'netconf-xml':
      return 'xml';
    case 'yaml':
    case 'yml':
      return 'yaml';
    case 'cli':
      return 'netcli';
    default:
      return 'plaintext';
  }
}

So a backup with config_format: "cli" is highlighted as netcli; xml and netconf-xml both render as XML; structured JSON dumps render as JSON; and anything unrecognized falls back to plaintext rather than guessing wrong.

CLI vs structured backups

In Backup History, a backup is considered "structured" when its format is anything other than cli (JSON, XML, YAML, gNMI, and so on). CLI backups are the ones that get netcli highlighting.

What netcli Highlights

The tokenizer recognizes a small, deliberate set of lexical categories that make a raw config easier to scan. The following sub-sections walk through each one, grounded in the actual grammar.

Stanza & Statement Keywords

A curated list of config keywords is highlighted as keywords wherever they appear as an identifier-leading word. The list mixes Junos operative verbs, Cisco operative verbs, and common top-level stanzas from both vendors. The exact set the grammar ships with is:

netcli keywordstext
// Junos operative verbs
set delete deactivate activate rename insert replace

// Cisco operative verbs
no shutdown description enable disable

// common top-level stanzas (both vendors)
interface interfaces router routing-options protocols
policy policy-options policy-map class-map route-map
prefix-list access-list ip ipv6 mpls bgp ospf ospf3
isis rip ldp rsvp vlan vlans vrf bridge-domain
system security firewall snmp snmp-server logging ntp
aaa tacacs radius username user group groups
chassis forwarding-options class-of-service event-options
address-family neighbor network redistribute permit deny
match then from unit family inet inet6 vlan-id
apply-groups import export community as-path route
static next-hop metric preference local-preference
hostname switchport channel-group spanning-tree mtu

Any other identifier — an interface name like GigabitEthernet0/1, a policy name, a hostname value — is tokenized as a plain identifier, not a keyword. That keeps structural keywords visually distinct from the values you actually configured.

Why a fixed keyword list?

netcli is a readability aid, not a vendor parser. A fixed, shared keyword list highlights the words that anchor a config stanza across vendors without trying to model every command tree. Words outside the list simply render as identifiers — nothing breaks, they just are not colored as keywords.

Comments

Both vendors' comment conventions are recognized, including Junos inline block annotations:

  • Cisco! to end of line.
  • Junos / shell# to end of line.
  • Junos inline annotations /* ... */ block comments, which may span multiple lines.
comment-styles.cfgtext
! Cisco-style banner divider — highlighted as a comment
interface GigabitEthernet0/1
 description uplink   ! trailing comment to end of line

# Junos / shell-style comment
set system host-name edge1   # also a comment

/* Junos inline annotation that
   spans more than one line */
interfaces {
    ge-0/0/0 {            /* per-stanza note */
        description core;
    }
}

IP, IPv6, MAC & Number Literals

Address and numeric literals are highlighted as numbers so they jump out against keywords and identifiers. The grammar recognizes, in order:

  • IPv4, IPv4 + mask, and CIDR — a dotted quad, optionally followed by a /prefix (for example 10.0.0.1, 10.0.0.0/24). A bare mask like 255.255.255.0 is itself a dotted quad, so it is highlighted too.
  • IPv6 (loose) — runs of hex groups separated by colons (for example 2001:db8::1).
  • MAC addresses — hex pairs separated by : or . (for example 00:1a:2b:3c:4d:5e or 001a.2b3c.4d5e).
  • Plain integers — AS numbers, VLAN ids, MTU values, metrics, and so on.
literals.cfgtext
ip address 10.0.0.1 255.255.255.0      ! IPv4 + mask
ip route 0.0.0.0/0 10.0.0.254          ! CIDR + next-hop
ipv6 address 2001:db8:abcd::1/64       ! IPv6 + prefix
mac-address 00:1a:2b:3c:4d:5e          ! colon MAC
mac address 001a.2b3c.4d5e             ! dotted MAC
router bgp 65001                       ! plain integer (AS)
 vlan 100                              ! plain integer (VLAN id)
 mtu 9000
Loose by design

The IPv6 and MAC patterns are intentionally loose — they match the common shapes seen in real backups rather than validating every edge case. The goal is to color the address so a human can scan it, not to reject malformed input.

Braces, Brackets & Strings

Structural punctuation common to Junos configs is tokenized so Monaco can bracket-match and so value lists read clearly:

  • { } — Junos stanza braces, matched as bracket pairs.
  • [ ] — Junos value lists, also matched as bracket pairs.
  • ; — the Junos statement terminator, tokenized as a delimiter.
  • Double-quoted strings — with escape handling inside the string; an unterminated quote is flagged as an invalid string.
junos-structure.conftext
policy-options {
    policy-statement export-statics {
        term static {
            from protocol [ static aggregate ];   /* value list in [ ] */
            then accept;                          /* ; terminates statement */
        }
    }
}
system {
    login {
        message "Authorized access only";        /* quoted string */
    }
}

Worked Examples

The same netcli grammar highlights both vendor families. These are representative backups you can paste into the Workspace editor (or view from Backup History) to see the highlighting.

Cisco IOS / IOS-XE / NX-OS

Cisco configs use ! comments and indentation-scoped stanzas. Keywords like interface, router, bgp, no, and shutdown are colored as keywords; addresses and AS numbers as literals; everything else (interface and neighbor descriptions, process names) as identifiers.

edge1-running.cfgtext
! edge1 — running config backup (config_format: cli)
hostname edge1
!
interface GigabitEthernet0/1
 description uplink-to-core
 ip address 10.0.0.1 255.255.255.0
 ipv6 address 2001:db8::1/64
 no shutdown
!
interface Vlan100
 ip address 192.168.100.1 255.255.255.0
!
router bgp 65001
 neighbor 10.0.0.2 remote-as 65002
 address-family ipv4
  network 192.168.100.0 mask 255.255.255.0
  redistribute static
!
ip route 0.0.0.0/0 10.0.0.254
access-list 10 permit 10.0.0.0 0.0.0.255

Junos (brace and set styles)

Junos backups come in two shapes — the nested brace format and the flat set format. netcli handles both. In the brace format, { } match as brackets and ; reads as a delimiter; in the set format, each line begins with an operative verb keyword.

Brace format

srx1-show.conftext
# srx1 — show configuration (config_format: cli)
interfaces {
    ge-0/0/0 {
        unit 0 {
            family inet {
                address 10.0.0.1/24;
            }
            family inet6 {
                address 2001:db8::1/64;
            }
        }
    }
}
protocols {
    bgp {
        group core {
            neighbor 10.0.0.2 {
                peer-as 65002;
            }
        }
    }
}
routing-options {
    static {
        route 0.0.0.0/0 next-hop 10.0.0.254;
    }
}

Set format

srx1-set.conftext
# srx1 — show configuration | display set
set system host-name srx1
set interfaces ge-0/0/0 unit 0 family inet address 10.0.0.1/24
set protocols bgp group core neighbor 10.0.0.2 peer-as 65002
set routing-options static route 0.0.0.0/0 next-hop 10.0.0.254
delete interfaces ge-0/0/1 disable
deactivate protocols ospf

YANG Highlighting & Formatter

Alongside netcli, NetStacks registers a YANG language (id yang, extension .yang) with its own Monarch tokenizer and a document format provider. The tokenizer highlights YANG statement keywords (module, container, leaf, list, rpc, and many more), and gives the argument of a type statement special treatment so built-in types (string, uint32, boolean, enumeration, and the rest) are tokenized as types.

The YANG formatter is an indentation-based pretty-printer: it walks the document line by line, tracks nesting depth from { and }, and re-indents with two spaces per level. Quoted strings and block comments are passed through verbatim so their contents are never reformatted, and it does not try to expand one-line stanzas onto multiple lines. Run Format Document to normalize nesting.

example-system.yangtext
module example-system {
  namespace "urn:example:system";
  prefix sys;

  container system {
    leaf hostname {
      type string;
    }
    leaf-list name-server {
      type inet:ipv4-address;
    }
    list user {
      key name;
      leaf name { type string; }
      leaf uid  { type uint32; }
    }
  }
}
Format Document on YANG and XML

YANG and XML both register a document formatting provider, so the editor's Format Document action works on them. The YANG formatter is the custom indent printer described here; XML uses the provider described next.

XML Format Provider

XML highlighting comes from Monaco's built-in XML language; NetStacks adds a document format provider on top of it so you can pretty-print NETCONF payloads and config exports. The provider runs an XML formatter configured with two-space indentation, content collapsing, and newline line separators. If the input is not well-formed, the formatter returns it unchanged rather than throwing — so a malformed payload is left as-is instead of corrupting it.

before-format.xmlxml
<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><interfaces><interface><name>eth0</name></interface></interfaces></config>

After running Format Document, the same payload is indented two spaces per level:

after-format.xmlxml
<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces>
    <interface>
      <name>eth0</name>
    </interface>
  </interfaces>
</config>
netconf-xml backups

Backups stored with the format netconf-xml (as well as plain xml) are highlighted as XML, so NETCONF configuration responses get the same highlighting and Format Document support as any other XML document.

JSON (Monaco built-in)

JSON is intentionally not given a NetStacks provider. Monaco's bundled json.worker already supplies syntax highlighting, schema validation, and Format Document, so NetStacks leaves JSON to the worker rather than duplicating it. Structured backups stored as json (for example gNMI or REST dumps) are highlighted by that worker.

device-state.jsonjson
{
  "interfaces": {
    "interface": [
      {
        "name": "GigabitEthernet0/1",
        "config": { "enabled": true, "mtu": 9000 },
        "ipv4": { "address": "10.0.0.1", "prefix-length": 24 }
      }
    ]
  }
}

Editor Behavior (Brackets, Comments)

Beyond coloring, netcli ships a language configuration that drives editor conveniences. Because the grammar runs inside Monaco, these behaviors come for free:

  • Line comment — toggling a line comment uses ! (the Cisco convention); the block comment pair is /* */.
  • Bracket matching{ } and [ ] are registered as bracket pairs, so Monaco highlights the matching brace and lets you jump between them.
  • Auto-closing & surrounding pairs — typing an opening {, [, or " auto-inserts its closer, and selecting text then typing one of those wraps the selection.
Comment toggle differs by language

In a netcli buffer the comment-toggle shortcut inserts !. In a YANG buffer it inserts //, because YANG's line comment is // (with /* */ for blocks). Each language sets its own configuration.

What It Is Not

It is worth being precise about scope so expectations are right:

  • Not a parser or validator. netcli is a lexical highlighter. It does not build a command tree, does not know whether a command is valid for a given platform, and reports no diagnostics or errors.
  • Not vendor detection. There is one grammar for both families; the editor does not try to decide "this is IOS" vs "this is Junos." The shared lexical features are highlighted either way.
  • Not exhaustive keyword coverage. Only the curated keyword list is colored as keywords. Commands outside that list render as identifiers — readable, just not keyword-colored.
  • No completion or hover. Those are LSP features. netcli is client-side highlighting only; for diagnostics and completion in other languages, see the LSP system.
Highlighting, not change control

Editing a backup buffer in the Workspace is for exploration and readability. Highlighting a config does not push it to a device. Config change control and push-to-device are handled separately — see Config Snapshots and Diff & History.

Q&A

Q: What is the netcli language?
A: A custom, vendor-agnostic Monaco grammar that highlights Cisco and Junos config backups. It colors comments, a curated set of stanza/statement keywords, IP/CIDR/MAC and number literals, strings, and Junos braces/brackets.
Q: Does it work for both Cisco and Junos?
A: Yes. There is one grammar covering both families — Cisco-style (! comments, indentation stanzas) and Junos-style ({ } braces, set/delete verbs, # comments, [ ... ] value lists). No vendor detection is needed.
Q: How does a backup get netcli highlighting?
A: Backups carry a config_format. When the value is cli, the editor maps it to the netcli language. json maps to JSON, xml and netconf-xml to XML, yaml/yml to YAML, and anything else to plain text.
Q: Does netcli validate my config?
A: No. It is a lexical readability aid, not a parser or validator. It does not check command validity or produce diagnostics.
Q: Is it available in every build?
A: Yes. netcli, YANG highlighting/formatting, the XML format provider, and Monaco's JSON support are all client-side and run with no download or network access — in the standalone Terminal and the Controller-served web client.
Q: Why does NetStacks not add its own JSON support?
A: Monaco's bundled json.worker already provides JSON highlighting, schema validation, and Format Document, so there is nothing to duplicate.
Q: How do I pretty-print a YANG model or NETCONF XML?
A: Run Format Document. YANG uses a custom indent-based pretty-printer (two spaces per level, strings and block comments untouched); XML uses a format provider that indents two spaces and safely returns malformed input unchanged.
Q: Can I add highlighting for another vendor's syntax?
A: netcli itself is a fixed grammar, but the editor supports a pluggable LSP system for richer per-language features. See Language Support (LSP) for adding your own language server.