The editor for web pages

@stxt-lang/editor turns a <textarea> into an STXT editor, with highlighting, errors while editing, and grammar-driven completion and hover.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor.css">
<script src="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor.js"></script>

<textarea id="doc" name="doc">
Recipe (com.example.cooking): Pancakes
	Serves: four
	Difficulty: Easy
</textarea>

<script>
    const template = `
Template (@stxt.template): com.example.cooking
	Structure >>
		Recipe (com.example.cooking):
			Serves: (1) NATURAL
			Difficulty: (?) ENUM [Easy, Medium, Hard]
`;

    const editor = StxtEditor.fromTextArea(document.getElementById('doc'), {
        grammars: [template],
        onChange: (text, diagnostics) => console.log(diagnostics),
    });
</script>

In this example we have:

  • The document: the text of the <textarea>.
  • The grammar: a template, passed as text in grammars.
  • An error: Serves: four is underlined, because the template asks for a NATURAL.

It is the editor of the playground, published as a library. It uses the TypeScript library @stxt-lang/core and carries no grammar of the language of its own: the errors and their codes are the same as in the command line, the VS Code extension and the libraries.

What it does

  • Highlighting: it comes from parsing the document. Blocks the grammar declares as MARKDOWN are highlighted as Markdown.
  • Errors while editing: syntax errors, and validation errors against the grammars.
  • Completion: while typing or with Ctrl+Space. It offers the nodes the grammar allows at that point and the values of an ENUM.
  • Hover: over a node, what the parser and its grammar say about it.
  • Tab key: it inserts a tab or four spaces. Switching from one to the other re-indents the document.
  • Forms: the <textarea> stays hidden and in sync, and its form still submits the text.

Installation

The current version is 0.1.0. The API may change before 1.0.

With a <script> there is nothing to install. The package carries its files ready for a page, minified, and any CDN of npm packages serves them. They are under https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/:

File Global Size (gzip) Contents
dist/stxt-editor.js StxtEditor 421 kB (133 kB) The editor and the static highlighting, with CodeMirror and @stxt-lang/core inside
dist/stxt-highlight.js StxtHighlight 76 kB (21 kB) Only the static highlighting, without CodeMirror
dist/stxt-editor.css 6 kB (2 kB) The styles of both

With a bundler:

npm install @stxt-lang/editor
import { fromTextArea } from '@stxt-lang/editor';
import '@stxt-lang/editor/stxt-editor.css';

The package is an ES module with type declarations. CodeMirror 6 and @stxt-lang/core are dependencies and are not bundled: a page that already uses CodeMirror loads a single copy.

Creating an editor

Function What it does
fromTextArea(textarea, options) Replaces a <textarea> with an editor. The <textarea> is hidden and keeps the text
mount(element, options) Creates an editor inside an element

Both return an Editor. No option is required:

Option Default Description
value The value of the <textarea>, or "" The initial text
grammars [] Texts of @stxt.schema or @stxt.template documents
validation true Validates against the grammars. Syntax errors are always shown
indent "tabs" What the Tab key inserts: "tabs" or "spaces"
readOnly false The text can be read and not edited
onChange (text, diagnostics) => void. Called on creation and after every change

A grammar written inside the document itself works too.

The editor

With the document and the template of the beginning:

import { mount } from '@stxt-lang/editor';

const editor = mount(document.getElementById('holder'), {
    value: text,
    grammars: [template],
});

editor.getDiagnostics();
// [{ line: 1, code: "INVALID_VALUE", message: "Serves: Invalid natural (four)",
//    severity: "warning", source: "validation" }]

editor.setValidation(false);
editor.getDiagnostics();      // []

editor.setIndent('spaces');   // re-indents the document with four spaces
editor.getValue();            // "Recipe (com.example.cooking): Pancakes\n    Serves: four\n..."
Method Description
getValue() / setValue(text) The text. setValue is an edit: it can be undone
setGrammars(texts) Replaces the grammars and validates again
setValidation(enabled) Turns validation on or off
setIndent(mode) Changes what the Tab key inserts and re-indents the document
setReadOnly(readOnly) Read-only or editable
getDiagnostics() The problems of the document
getGrammarDiagnostics() The problems of each grammar, in the order of grammars
focus() Gives the focus to the editor
destroy() Removes the editor and shows the <textarea> again
view The CodeMirror EditorView

A diagnostic has five fields:

Field Value
line The line, from 0. The ParseExceptions of the library count from 1
code The error code: INDENTATION_MIXED, INVALID_VALUE, SCHEMA_NOT_FOUND...
message The message, in English
severity "error" for syntax and grammar errors, "warning" for validation errors
source "syntax" (the document), "grammar" (a grammar that does not load) or "validation"

For more than one document per view, or for another layout, the package also exports the pieces of the editor:

  • Analyzer: parses and validates a set of documents. It uses neither the DOM nor CodeMirror.
  • createStxtEditor, createStxtExtensions: the CodeMirror extensions.
  • setTokensEffect, toCmDiagnostics: they take an analysis to a view.

Static highlighting

For pages that show STXT and do not edit it there is a highlighter without CodeMirror:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor.css">
<script src="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-highlight.js"></script>

<pre><code class="language-stxt">Recipe (com.example.cooking): Pancakes
	Serves: 4</code></pre>

<script>
    StxtHighlight.highlightAll();
</script>
Function What it does
highlightAll(selector, options) Highlights the elements of the selector. By default, pre code.language-stxt
highlight(element, options) Highlights one element
highlightText(text, options) Returns the HTML without touching the DOM. It also works in Node

The only option is grammars: with them, MARKDOWN blocks are highlighted as Markdown. With a bundler it is imported from @stxt-lang/editor/highlight:

import { highlightText } from '@stxt-lang/editor/highlight';

highlightText('Recipe: Pancakes');
// <span class="stxt-tok-property">Recipe</span><span class="stxt-tok-property">:</span><span class="stxt-tok-string"> Pancakes</span>

Themes

The colours are CSS properties of .stxt-editor (the editor) and of .stxt-highlight (the static highlighting):

.stxt-editor,
.stxt-highlight {
    --stxt-node: #0d5cb6;
    --stxt-value: #d05a80;
    --stxt-comment: #008000;
    --stxt-background: #fff;
}

.stxt-editor {
    height: 22rem;
}

Without a height, the editor grows with the text. With one, it scrolls.

Property What it colours
--stxt-node The names of the nodes
--stxt-block The >> of a text block
--stxt-namespace The namespaces
--stxt-value The values
--stxt-comment The comments
--stxt-muted Punctuation, line numbers and secondary text
--stxt-background, --stxt-text, --stxt-border The box of the editor
--stxt-gutter-background, --stxt-active-line, --stxt-active-gutter The line numbers and the current line
--stxt-selected, --stxt-code-background, --stxt-separator The selected completion, Markdown code and the rule of the hover
--stxt-font-mono, --stxt-font-body, --stxt-font-size The fonts

The defaults are the palette of this site. The code and the changes of each version are in the repository.