UnoDOS pc64

UnoCode: the VS Code-class editor

A full code workbench that runs on the machine itself, in the shape of Visual Studio Code: an activity bar and side bar, tabbed editors with a minimap, a command palette, an integrated terminal - and extensions. Colour themes, languages, syntax grammars and snippets are the same file formats Visual Studio Code uses, so a theme or a snippet file written for VS Code works here unchanged.

UnoCode on first run. The activity bar down the left switches the side bar between Explorer, Search, Source Control, Run and Extensions; the editor has a line-number gutter, a change-marked left edge and a minimap; the status bar shows the folder, the problem counts, the cursor position, the indentation, the encoding, the line ending and the language.
Looking for how to use it?This page is for people extending UnoCode - the file formats, the extension API and the internals. If you just want to use the editor, the UnoCode page is the one you want.
Two editors, and whyUnoCode and Studio are both here on purpose. Studio is the small one - an editor, the built-in compiler and an AI assistant, three keys from source to a running app. UnoCode is the big one - more editor, and extensible. Neither replaces the other, and a distro can ship either, both or neither.

The five keys worth learning

Everything in UnoCode is a command, and every command is in one searchable list. If you remember nothing else, remember the first line:

KeyWhat it does
Ctrl+Shift+PThe command palette - every command, searchable, with its keyboard shortcut beside it
Ctrl+PGo to a file in the open folder
Ctrl+BShow or hide the side bar
Ctrl+`The integrated terminal (type help)
Ctrl+,Open settings.json
The command palette, filtered to theme. Each row shows the command's title, its id in grey, and its keyboard shortcut on the right. The second row is contributed by an extension - the palette does not distinguish between a built-in command and one an extension registered, because nothing else in UnoCode does either.

If the palette can find it, a key can be bound to it and an extension can call it. That is the whole design: nothing is wired to a key directly, so anything can be rebound.

The editor

Open a file from the Explorer, with Ctrl+P, or by typing open SDK\SAMPLE.C in the terminal. Each file gets a tab; the breadcrumb bar above the text shows where it came from.

A UnoC source file open beside the welcome document. Comments, preprocessor lines, types, numbers and strings are each coloured by the language's grammar; the minimap on the right is the whole file in miniature with the visible region marked; the bar down the left edge of the gutter marks lines changed since the file was opened.

The editing keys are the ones you already know - arrows and Home/End/PgUp/PgDn to move, Shift+movement to select, Ctrl+X/C/V/A, Ctrl+Z to undo - plus the ones that make an editor worth using:

Multiple cursors

Ctrl+Alt+Down adds a cursor on the next line, Ctrl+D adds one at the next match of what is selected, and everything you type then happens at all of them at once. Esc collapses back to one.

Find and replace

Ctrl+F finds, Ctrl+H replaces, and the three small toggles switch on case sensitivity, whole-word matching and regular expressions. The match count is live.

Whole lines

Alt+Up/Down moves the current line, Shift+Alt+Down copies it down, Ctrl+Shift+K deletes it, and Ctrl+/ comments or uncomments the selection in the language's own comment syntax.

It closes what you open

Type ( and you get () with the cursor between them; type the closing one and the cursor steps over it rather than doubling it. Enter keeps the indentation, and opens a block out onto its own lines.

Find, with the live match count (0 of 11) and the case / whole-word / regular-expression toggles. Every match in the file is highlighted as you type, not just the next one.

Suggestions

Ctrl+Space asks for suggestions, and they appear as you type. They come from four places at once and are ranked together: the language's keywords, every distinct word already in the file (which is what makes completion useful in a language nothing knows about), snippets, and anything an extension offers.

The suggestion list in a C file. The rows marked S are snippets contributed by an installed extension - unoapp expands to a skeleton app - and the rows marked k are the language's own keywords. Accept with Tab or Enter.

The integrated terminal

Ctrl+` opens a panel at the bottom with Problems, Output and Terminal tabs. The terminal is UnoCode's own small shell - UnoDOS has no shell process for it to host - so help lists exactly what it has, and a word it does not know is an error rather than a silent nothing.

The terminal, after help and ext. It can list and change directories, read and copy files, search the folder, open and run files, read and write settings, switch the theme, run any UnoCode command by id, and evaluate a JavaScript expression in the same interpreter the extensions run in.

Two of its commands are worth knowing about:

  • run file runs a .UNO app, or wraps a .PY file for the Python runtime and runs that - the same thing F5 does to the file you are editing.
  • js expression evaluates JavaScript in the extension host itself, which is how you find out what an extension is actually doing.

Colour themes

Six themes are built in - Dark+, Light+, Monokai, Solarized Dark, High Contrast and UnoDOS Blue - and more arrive as extensions. Ctrl+K then Ctrl+T, or Preferences: Color Theme in the palette, switches between them; your choice is saved.

The same file under Nord, a theme that arrives as an extension containing one JSON file and no code at all. The whole workbench recolours - activity bar, side bar, tabs, editor, status bar - because a theme sets semantic colours rather than painting anything itself.

A theme file is a Visual Studio Code theme file, unchanged:

{
    "name": "Nord",
    "type": "dark",
    "colors": {
        "editor.background": "#2E3440",
        "editor.foreground": "#D8DEE9",
        "activityBar.background": "#3B4252",
        "statusBar.background": "#3B4252"
    },
    "tokenColors": [
        { "scope": "comment",
          "settings": { "foreground": "#616E88", "fontStyle": "italic" } },
        { "scope": ["keyword", "storage"],
          "settings": { "foreground": "#81A1C1" } }
    ]
}

Two rules make writing one by hand reasonable. A colour key UnoCode does not know is ignored, so a theme written for a newer editor still loads; and a key you leave out is worked out from the ones you set, so a theme with three colours in it still renders a complete, coherent workbench.

Extensions

An extension is a folder. Copy it onto the disk under EXT and UnoCode finds it at startup - on any drive, so an extension on a USB stick needs no installing.

The Extensions view. Each row shows the name, version and description from the extension's manifest. Nord and UnoDOS Snippets contain no code at all; Hello UnoCode does, and once it has run its row shows how long its activation took. Enter enables or disables one, Ctrl+R reloads them all.

Three of the four kinds of extension need no programming whatever - they are JSON files that describe something:

A theme

One colour-theme file and a manifest naming it. No code runs, ever.

A language

An id, the file extensions it claims and its comment syntax - and UnoCode knows a new language.

A grammar

A TextMate-style file of patterns that colours that language. Same shape as VS Code's.

Snippets

A map of short prefixes to the text they expand to, offered in the suggestion list.

The manifest is package.json, with VS Code's keys:

{
    "name": "nord",
    "displayName": "Nord Theme",
    "description": "An arctic, north-bluish colour theme.",
    "version": "1.0.0",
    "publisher": "you",

    // no "main", so no code ever runs for this extension
    "contributes": {
        "themes": [
            { "label": "Nord", "uiTheme": "vs-dark", "path": "THEMES/NORD.JSN" }
        ]
    }
}

The fourth kind runs JavaScript. An extension with a main gets a real programming interface - commands, messages, quick picks, the active editor and its text, settings, files, and events when a document is opened, changed or saved:

var vscode = require('vscode');

function activate(context) {
    vscode.commands.registerCommand('hello.sayHello', function () {
        var ed = vscode.window.activeTextEditor;
        vscode.window.showInformationMessage(
            'Hello from an extension - ' + (ed ? ed.document.fileName : 'no editor'));
    });

    // completions, offered only in C files
    vscode.languages.registerCompletionItemProvider('c', {
        provideCompletionItems: function (document, position) {
            return [{ label: 'uno_fs_read', detail: 'UnoDOS',
                      insertText: 'uno_fs_read(vol, name, buf, max)' }];
        }
    });

    vscode.workspace.onDidSaveTextDocument(function (doc) {
        console.log('saved: ' + doc.fileName);
    });
}

exports.activate = activate;
That extension's command, run from the palette. It was not loaded until the moment the command was chosen: the manifest declared it, so it was in the palette from startup, and choosing it read the file, ran it and called the handler it registered. The message on the right is the extension talking.
A runaway extension is stopped, not fatalAn extension that misbehaves cannot take the machine with it. Every call into an extension runs on a step budget; one that does not finish is stopped, reported and switched off. On a system with no preemption that is not a nicety - it is the reason it is safe to run code somebody else wrote at all.

One thing about the interface is deliberately different from Visual Studio Code, and it is stated rather than hidden: require() resolves 'vscode' and nothing else, because there is no package manager to resolve anything else against.

Promises are real nowThe asynchronous calls in this API return real Promises, and async/await work. That was not true at first - the interpreter had no microtask queue, so the calls returned an object with a .then() on it and the manual said so. It has one now, and await genuinely suspends, so an extension written the way a VS Code extension is written behaves the way it expects.

Language servers

UnoCode contains a Language Server Protocol client: a server per language, spoken to in JSON-RPC over pipes, feeding diagnostics, completions, hover, go-to-definition, find-references, rename and format back into the editor. It is configured from settings, one command line per language, and an empty command switches one off.

Not on pc64, and whyNone of that runs on pc64. A language server is a separate program and pc64 has no processes to start one in, so the client asks the platform, is told there is nothing, and never starts anything - at no cost, and with the editor's own grammar-derived completions untouched. This section describes a capability that is real in the shared UnoCode codebase and inert on this platform. It becomes live here if and when UnoDOS grows a way to run a child program.

Two decisions in the client are worth knowing if you are reading the source. It syncs the whole document after a pause in typing rather than sending incremental edits, because incremental sync means client and server each keep a copy and one mis-ranged edit makes them diverge silently. And a server that dies is restarted with a widening delay and has its documents re-opened on the replacement, because a language server exiting is ordinary rather than exceptional.

Settings and keyboard shortcuts

Both are files, both are yours to edit, and both are the formats Visual Studio Code uses - including its habit of allowing comments and trailing commas in them.

FileWhat it is
UNOCODE\SETTINGS.JSNSettings, as a flat map of dotted keys. Ctrl+, opens it.
UNOCODE\KEYBIND.JSNKeyboard shortcuts. Preferences: Open Keyboard Shortcuts (JSON) writes the whole shipped keymap into it the first time, so changing one binding does not start with guessing what a key is called.
// UnoCode settings.  Comments and trailing commas are allowed.
{
    "editor.fontSize": 15,
    "editor.tabSize": 4,
    "editor.insertSpaces": true,
    "editor.minimap.enabled": true,
    "editor.renderWhitespace": "boundary",
    "workbench.colorTheme": "Nord",
    "files.trimTrailingWhitespace": true,
}

A keybinding is { "key", "command", "when" }. when is a real condition - editorTextFocus, editorHasSelection, terminalFocus and the rest, combined with !, && and || - so the same key can do different things in different places. A command written with a leading - removes a shipped binding. Yours win over an extension's, which win over the defaults.

Why the names look truncatedThe file names are short because the disk is FAT, which allows eight characters and a three-letter extension. The CONTENTS are not shortened: SETTINGS.JSN is settings.json, key for key.

What it does not do

Honestly, so you are not looking for them:

  • No word wrap. Long lines scroll sideways.
  • No language server runs on this machine, so there is no hover documentation, no go-to-definition and no rename-across-a-project here. UnoCode has a language-server client - see Language servers below - but a server is a separate program, and pc64 has no way to start one. Completions come from the language's keywords, the words already in the file and any snippets, which is what you get.
  • Source Control tracks your unsaved edits against the file as it was opened, and marks changed lines in the gutter. There is no repository on this machine and it says so rather than pretending.
  • Function keys depend on the keyboard. PS/2 keyboards deliver them; USB keyboards do not, in this build. Every default shortcut is a Ctrl chord for that reason, and nothing needs an F-key.

For extension authors

The complete file formats - the manifest keys, the theme colour keys, the grammar model and its one documented difference from TextMate, the settings table and the whole JavaScript interface - are in pc64/unocode/UNOCODE.md in the source tree, beside the three sample extensions the disk ships: a theme, a language with a grammar and snippets, and one with code in it.