Initial commit - UCXL VS Code extension
- Added UCXL VS Code extension with syntax highlighting - Implemented language configuration and grammar definitions - Created extension package with examples and documentation - Added syntax highlighting for UCXL code structures 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
5
LICENSE.md
Normal file
5
LICENSE.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Anthony Rawlins
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy...
|
||||||
27
README.md
Normal file
27
README.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# UCXL Syntax + Linter
|
||||||
|
|
||||||
|
Highlights and lints UCXL addresses anywhere in your files.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Syntax colouring for UCXL scheme, agent, topic, project, context, temporal segments, and path
|
||||||
|
- Inline linting for invalid UCXL syntax with red squiggly underlines
|
||||||
|
- Hover tooltips showing parsed components
|
||||||
|
|
||||||
|
## Install
|
||||||
|
1. Clone this repo
|
||||||
|
2. Install `vsce` if you don’t have it:
|
||||||
|
```bash
|
||||||
|
npm install -g vsce
|
||||||
|
|
||||||
|
|
||||||
|
Then you can see the syntax-colouring in action right here by reading this file and hovering over:
|
||||||
|
|
||||||
|
|
||||||
|
ucxl://AB001:dev@project:task/~~/policies/linting/details.md
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
ucxl://F0131:finance@project:any/~~/policies/linting/details.md
|
||||||
|
|
||||||
|
```
|
||||||
17
examples/ucxl_example.py
Normal file
17
examples/ucxl_example.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import posixpath
|
||||||
|
|
||||||
|
def ucxl_path_join(*args):
|
||||||
|
"""
|
||||||
|
Join multiple path components into a single UCXL path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
*args: Path components to join.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A single UCXL path.
|
||||||
|
"""
|
||||||
|
ucxl = "ucxl://AB001:dev@project:task/~~/policies/linting/details.md"
|
||||||
|
|
||||||
|
return posixpath.join(*args)
|
||||||
|
|
||||||
|
|
||||||
5
examples/ucxl_example.rs
Normal file
5
examples/ucxl_example.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let context = ucxl::Context::from(ucxl://any:developer@chorus:website-redesign/#/db/connection/credentials).expect("Failed to create UCXL context");
|
||||||
|
}
|
||||||
261
extension.js
Normal file
261
extension.js
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
const vscode = require("vscode");
|
||||||
|
|
||||||
|
/* const UCXL_REGEX = /^ucxl:\/\/([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)@([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)((\/(\^\^|~~))?\/[^\\s]*)?$/; */
|
||||||
|
// Updated regex to match the new UCXL address format with optional temporal and context path
|
||||||
|
// The regex captures:
|
||||||
|
// - Agent and Role: alphanumeric with underscores and hyphens
|
||||||
|
// - Project and Task: alphanumeric with underscores and hyphens
|
||||||
|
// - Temporal: optional, can be `#`, `~*`, `^*`, `~~`, `^^`, `~<number>`, or `^<number>`
|
||||||
|
// - Context Path: optional, can be any string after the temporal part
|
||||||
|
// The regex ensures that the address starts with `ucxl://` and is followed by the correct structure of components.
|
||||||
|
// The regex is designed to be flexible while ensuring that the components are valid according to the UCXL specification.
|
||||||
|
// The regex also allows for the context path to be any valid string, ensuring it captures the full address correctly.
|
||||||
|
// The regex is designed to be used in a VSCode extension for validating and providing hover information
|
||||||
|
// about UCXL addresses in text documents.
|
||||||
|
//const UCXL_REGEX = /^ucxl:\/\/([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)@([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)\/(#|~\*|\^\*|~~|\^\^|~\d+|\^\d+)\/(.+)$/;
|
||||||
|
|
||||||
|
// Agent ID regex for UCXL addresses
|
||||||
|
const UCXL_REGEX = /^ucxl:\/\/([A-Z0-9]{3,5}|any|none):([a-zA-Z0-9_-]+)@([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)\/(#|~\*|\^\*|~~|\^\^|~\d+|\^\d+)\/(.+)$/;
|
||||||
|
|
||||||
|
function activate(context) {
|
||||||
|
const diagnosticCollection = vscode.languages.createDiagnosticCollection("ucxl");
|
||||||
|
context.subscriptions.push(diagnosticCollection);
|
||||||
|
|
||||||
|
function updateDiagnostics(doc) {
|
||||||
|
if (!doc) return;
|
||||||
|
const diagnostics = [];
|
||||||
|
const text = doc.getText();
|
||||||
|
const lines = text.split(/\r?\n/);
|
||||||
|
|
||||||
|
lines.forEach((line, idx) => {
|
||||||
|
const matches = line.matchAll(/ucxl:\/\/[^\s]+/g);
|
||||||
|
for (const match of matches) {
|
||||||
|
const addr = match[0];
|
||||||
|
if (!UCXL_REGEX.test(addr)) {
|
||||||
|
diagnostics.push(
|
||||||
|
new vscode.Diagnostic(
|
||||||
|
new vscode.Range(idx, match.index, idx, match.index + addr.length),
|
||||||
|
"Invalid UCXL address syntax",
|
||||||
|
vscode.DiagnosticSeverity.Error
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
diagnosticCollection.set(doc.uri, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
vscode.workspace.onDidOpenTextDocument(updateDiagnostics);
|
||||||
|
vscode.workspace.onDidChangeTextDocument(e => updateDiagnostics(e.document));
|
||||||
|
vscode.workspace.onDidCloseTextDocument(doc => diagnosticCollection.delete(doc.uri));
|
||||||
|
|
||||||
|
// Hover Provider
|
||||||
|
const hoverProvider = vscode.languages.registerHoverProvider(
|
||||||
|
{ scheme: "file", language: "*" },
|
||||||
|
{
|
||||||
|
provideHover(document, position) {
|
||||||
|
const range = document.getWordRangeAtPosition(position, /ucxl:\/\/[^\s]+/);
|
||||||
|
if (!range) return;
|
||||||
|
const address = document.getText(range);
|
||||||
|
|
||||||
|
const match = address.match(UCXL_REGEX);
|
||||||
|
if (!match) return new vscode.Hover(`$(error) **Invalid UCXL address**`);
|
||||||
|
|
||||||
|
const [, agent, role, project, task, , temporal, , context_path] = match;
|
||||||
|
const md = new vscode.MarkdownString();
|
||||||
|
md.isTrusted = true;
|
||||||
|
md.appendMarkdown(`### UCXL Address Components\n`);
|
||||||
|
md.appendMarkdown(`- **Agent**: \`${agent}\`\n`);
|
||||||
|
md.appendMarkdown(`- **Role**: \`${role}\`\n`);
|
||||||
|
md.appendMarkdown(`- **Project**: \`${project}\`\n`);
|
||||||
|
md.appendMarkdown(`- **Task**: \`${task}\`\n`);
|
||||||
|
if (temporal) md.appendMarkdown(`- **Temporal**: \`${temporal}\`\n`);
|
||||||
|
if (context_path) md.appendMarkdown(`- **Context Path**: \`${context_path}\`\n`);
|
||||||
|
|
||||||
|
return new vscode.Hover(md, range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
context.subscriptions.push(hoverProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lintUcxlLine(lineText, lineNum, diagnostics) {
|
||||||
|
const match = lineText.match(UCXL_REGEX);
|
||||||
|
if (!match) return;
|
||||||
|
|
||||||
|
const fullAddress = match[0];
|
||||||
|
const agentToken = match[1];
|
||||||
|
|
||||||
|
if (!isValidAgent(agentToken)) {
|
||||||
|
const agentStart = lineText.indexOf(agentToken);
|
||||||
|
const agentEnd = agentStart + agentToken.length;
|
||||||
|
diagnostics.push(
|
||||||
|
new vscode.Diagnostic(
|
||||||
|
new vscode.Range(lineNum, agentStart, lineNum, agentEnd),
|
||||||
|
"Invalid agent token (checksum or field error)",
|
||||||
|
vscode.DiagnosticSeverity.Error
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDiagnostics(doc, diagnosticCollection) {
|
||||||
|
const diagnostics = [];
|
||||||
|
for (let i = 0; i < doc.lineCount; i++) {
|
||||||
|
const lineText = doc.lineAt(i).text;
|
||||||
|
lintUcxlLine(lineText, i, diagnostics);
|
||||||
|
}
|
||||||
|
diagnosticCollection.set(doc.uri, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vscode.languages.registerHoverProvider("ucxl", {
|
||||||
|
provideHover(document, position) {
|
||||||
|
const wordRange = document.getWordRangeAtPosition(position, /[A-Z0-9]{5}/);
|
||||||
|
if (!wordRange) return null;
|
||||||
|
|
||||||
|
const token = document.getText(wordRange);
|
||||||
|
if (!isValidAgent(token)) return null;
|
||||||
|
|
||||||
|
const agentId = decodeToken(token);
|
||||||
|
return new vscode.Hover(`**AgentID**: Version=${agentId.version}, Host=${agentId.hostId}, GPU=${agentId.gpuSlot}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
|
const CROCKFORD_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
||||||
|
const VERSION_BITS = 3;
|
||||||
|
const HOST_ID_BITS = 10;
|
||||||
|
const GPU_SLOT_BITS = 4;
|
||||||
|
const RESERVED_BITS = 2;
|
||||||
|
const CHECKSUM_BITS = 6;
|
||||||
|
|
||||||
|
const PREFIX_BITS = VERSION_BITS + HOST_ID_BITS + GPU_SLOT_BITS + RESERVED_BITS; // 19
|
||||||
|
const TOTAL_BITS = PREFIX_BITS + CHECKSUM_BITS; // 25
|
||||||
|
|
||||||
|
const MAX_HOST_ID = (1 << HOST_ID_BITS) - 1;
|
||||||
|
const MAX_GPU_SLOT = (1 << GPU_SLOT_BITS) - 1;
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
// Base32 helpers
|
||||||
|
// -------------------
|
||||||
|
function intToBase32(n, length) {
|
||||||
|
let chars = Array(length).fill('0');
|
||||||
|
for (let i = length - 1; i >= 0; i--) {
|
||||||
|
chars[i] = CROCKFORD_ALPHABET[n & 0x1F];
|
||||||
|
n >>= 5;
|
||||||
|
}
|
||||||
|
return chars.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function base32ToInt(s) {
|
||||||
|
const charMap = {};
|
||||||
|
for (let i = 0; i < CROCKFORD_ALPHABET.length; i++) {
|
||||||
|
charMap[CROCKFORD_ALPHABET[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.length !== 5) throw new Error(`token length must be 5, got ${s.length}`);
|
||||||
|
|
||||||
|
let n = 0;
|
||||||
|
for (let ch of s.toUpperCase()) {
|
||||||
|
if ("ILOU".includes(ch)) throw new Error(`invalid character ${ch}`);
|
||||||
|
const val = charMap[ch];
|
||||||
|
if (val === undefined) throw new Error(`invalid character ${ch}`);
|
||||||
|
n = (n << 5) | val;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
// SHA256 first bits
|
||||||
|
// -------------------
|
||||||
|
function sha256FirstBits(value, bits) {
|
||||||
|
const bytes = Buffer.from([(value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]);
|
||||||
|
const hash = crypto.createHash('sha256').update(bytes).digest();
|
||||||
|
return hash[0] >> (8 - bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
// Pack/Unpack
|
||||||
|
// -------------------
|
||||||
|
function packFields(version, hostId, gpuSlot, reserved) {
|
||||||
|
if (version >= (1 << VERSION_BITS)) throw new Error("version out of range");
|
||||||
|
if (hostId > MAX_HOST_ID) throw new Error("host_id out of range");
|
||||||
|
if (gpuSlot > MAX_GPU_SLOT) throw new Error("gpu_slot out of range");
|
||||||
|
if (reserved >= (1 << RESERVED_BITS)) throw new Error("reserved out of range");
|
||||||
|
|
||||||
|
let bits = 0;
|
||||||
|
bits = (bits << VERSION_BITS) | version;
|
||||||
|
bits = (bits << HOST_ID_BITS) | hostId;
|
||||||
|
bits = (bits << GPU_SLOT_BITS) | gpuSlot;
|
||||||
|
bits = (bits << RESERVED_BITS) | reserved;
|
||||||
|
|
||||||
|
const checksum = sha256FirstBits(bits, CHECKSUM_BITS);
|
||||||
|
bits = (bits << CHECKSUM_BITS) | checksum;
|
||||||
|
|
||||||
|
if (bits >= (1 << TOTAL_BITS)) throw new Error("packed value exceeds allowed bit length");
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpackFields(packed) {
|
||||||
|
if (packed >= (1 << TOTAL_BITS)) throw new Error("packed value exceeds allowed bit length");
|
||||||
|
|
||||||
|
const checksum = packed & ((1 << CHECKSUM_BITS) - 1);
|
||||||
|
const prefix = packed >> CHECKSUM_BITS;
|
||||||
|
|
||||||
|
let tmp = prefix;
|
||||||
|
const reserved = tmp & ((1 << RESERVED_BITS) - 1); tmp >>= RESERVED_BITS;
|
||||||
|
const gpuSlot = tmp & ((1 << GPU_SLOT_BITS) - 1); tmp >>= GPU_SLOT_BITS;
|
||||||
|
const hostId = tmp & ((1 << HOST_ID_BITS) - 1); tmp >>= HOST_ID_BITS;
|
||||||
|
const version = tmp & ((1 << VERSION_BITS) - 1);
|
||||||
|
|
||||||
|
const expected = sha256FirstBits(prefix, CHECKSUM_BITS);
|
||||||
|
if (expected !== checksum) throw new Error("checksum mismatch");
|
||||||
|
|
||||||
|
return { version, hostId, gpuSlot, reserved, checksum };
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
// Encode / Decode Token
|
||||||
|
// -------------------
|
||||||
|
function encodeToken(version, hostId, gpuSlot, reserved) {
|
||||||
|
const packed = packFields(version, hostId, gpuSlot, reserved);
|
||||||
|
return intToBase32(packed, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeToken(token) {
|
||||||
|
const packed = base32ToInt(token);
|
||||||
|
return unpackFields(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
// AgentID validator
|
||||||
|
// -------------------
|
||||||
|
function isValidAgent(agent) {
|
||||||
|
try {
|
||||||
|
decodeToken(agent);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage:
|
||||||
|
// console.log(isValidAgent(encodeToken(1, 42, 3, 0))); // true
|
||||||
|
// console.log(isValidAgent("ABCDE")); // false if checksum fails
|
||||||
|
|
||||||
|
function isValidUCXLAddress(address) {
|
||||||
|
return UCXL_REGEX.test(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function deactivate() {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { activate, deactivate };
|
||||||
18
language-configuration.json
Normal file
18
language-configuration.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"comments": {
|
||||||
|
"lineComment": "#",
|
||||||
|
"blockComment": ["/*", "*/"]
|
||||||
|
},
|
||||||
|
"brackets": [
|
||||||
|
["{", "}"],
|
||||||
|
["[", "]"],
|
||||||
|
["(", ")"]
|
||||||
|
],
|
||||||
|
"autoClosingPairs": [
|
||||||
|
{ "open": "{", "close": "}" },
|
||||||
|
{ "open": "[", "close": "]" },
|
||||||
|
{ "open": "(", "close": ")" },
|
||||||
|
{ "open": "\"", "close": "\"" },
|
||||||
|
{ "open": "'", "close": "'" }
|
||||||
|
]
|
||||||
|
}
|
||||||
84
package.json
Normal file
84
package.json
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"name": "ucxl-syntax-linter",
|
||||||
|
"displayName": "UCXL Syntax + Linter",
|
||||||
|
"description": "Syntax highlighting and linting for UCXL addresses inline in any file.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"publisher": "Anthony Rawlins - CHORUS.services",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/anthonyrawlins/ucxl-vscode-linter.git"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.70.0"
|
||||||
|
},
|
||||||
|
"categories": [
|
||||||
|
"Linters",
|
||||||
|
"Programming Languages"
|
||||||
|
],
|
||||||
|
"activationEvents": [
|
||||||
|
"onLanguage:ucxl",
|
||||||
|
"onLanguage:go",
|
||||||
|
"onLanguage:rust",
|
||||||
|
"onLanguage:javascript",
|
||||||
|
"onLanguage:python",
|
||||||
|
"onLanguage:markdown"
|
||||||
|
],
|
||||||
|
"main": "./extension.js",
|
||||||
|
"contributes": {
|
||||||
|
"languages": [
|
||||||
|
{
|
||||||
|
"id": "ucxl",
|
||||||
|
"aliases": ["UCXL"],
|
||||||
|
"extensions": [".ucxl"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"grammars": [
|
||||||
|
{
|
||||||
|
"language": "ucxl",
|
||||||
|
"scopeName": "source.ucxl",
|
||||||
|
"path": "./syntaxes/ucxl.tmLanguage.json",
|
||||||
|
"injectTo": [
|
||||||
|
"source",
|
||||||
|
"text",
|
||||||
|
"source.go",
|
||||||
|
"source.rust",
|
||||||
|
"source.js",
|
||||||
|
"source.py"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "go",
|
||||||
|
"scopeName": "source.go.ucxl",
|
||||||
|
"path": "./syntaxes/ucxl.tmLanguage.json",
|
||||||
|
"injectTo": ["source.go"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "rust",
|
||||||
|
"scopeName": "source.rust.ucxl",
|
||||||
|
"path": "./syntaxes/ucxl.tmLanguage.json",
|
||||||
|
"injectTo": ["source.rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "javascript",
|
||||||
|
"scopeName": "source.js.ucxl",
|
||||||
|
"path": "./syntaxes/ucxl.tmLanguage.json",
|
||||||
|
"injectTo": ["source.js"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "python",
|
||||||
|
"scopeName": "source.python.ucxl",
|
||||||
|
"path": "./syntaxes/ucxl.tmLanguage.json",
|
||||||
|
"injectTo": ["source.py"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "markdown",
|
||||||
|
"scopeName": "source.markdown.ucxl",
|
||||||
|
"path": "./syntaxes/ucxl.tmLanguage.json",
|
||||||
|
"injectTo": ["source.md"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"package": "vsce package"
|
||||||
|
}
|
||||||
|
}
|
||||||
33
syntaxes/ucxl.tmLanguage.backup.json
Normal file
33
syntaxes/ucxl.tmLanguage.backup.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"scopeName": "source.ucxl",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "keyword.scheme.ucxl",
|
||||||
|
"match": "\\bucxl://"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.name.agent.ucxl",
|
||||||
|
"match": "(?<=ucxl://)[a-zA-Z0-9_-]+(?=:)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "support.class.topic.ucxl",
|
||||||
|
"match": "(?<=:)[a-zA-Z0-9_-]+(?=@)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.name.project.ucxl",
|
||||||
|
"match": "(?<=@)[a-zA-Z0-9_-]+(?=:)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "variable.parameter.context.ucxl",
|
||||||
|
"match": "(?<=:)[a-zA-Z0-9_-]+(?=/)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "constant.language.temporal.ucxl",
|
||||||
|
"match": "(?<=/)(#|~\\*|\\^\\*|~~|\\^\\^|~\\d+|\\^\\d+)(?=/)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string.path.ucxl",
|
||||||
|
"match": "(?<=/)[^\\s]+"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
36
syntaxes/ucxl.tmLanguage.json
Normal file
36
syntaxes/ucxl.tmLanguage.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"scopeName": "source.ucxl",
|
||||||
|
"name": "UCXL",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "meta.ucxl",
|
||||||
|
"match": "^ucxl://([A-Z0-9]{5})" ,
|
||||||
|
"captures": {
|
||||||
|
"1": { "name": "constant.language.agent.ucxl" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": "^ucxl://[A-Z0-9]{5}:([a-zA-Z0-9_-]+)",
|
||||||
|
"captures": {
|
||||||
|
"1": { "name": "entity.name.role.ucxl" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": "@([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)",
|
||||||
|
"captures": {
|
||||||
|
"1": { "name": "entity.name.project.ucxl" },
|
||||||
|
"2": { "name": "entity.name.task.ucxl" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": "\\/(#|~\\*|\\^\\*|~~|\\^\\^|~\\d+|\\^\\d+)\\/",
|
||||||
|
"captures": {
|
||||||
|
"1": { "name": "constant.language.temporal.ucxl" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": "\\/([^\\s]+)$",
|
||||||
|
"name": "string.path.ucxl"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
ucxl-syntax-linter-0.0.1.vsix
Normal file
BIN
ucxl-syntax-linter-0.0.1.vsix
Normal file
Binary file not shown.
Reference in New Issue
Block a user