Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# client executables files
node_modules/
out/
.idea/

.pnpm-debug.log

Expand Down
1 change: 1 addition & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ package-lock.json
eslint.config.ts
*.ts
dist/**/*.map
assets
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
# SimplicityHL extension for VSCode

VSCode extension that provides syntax highlighting for the [SimplicityHL](https://github.com/BlockstreamResearch/SimplicityHL) programming language.
VSCode extension that provides syntax highlighting for the [SimplicityHL][1] programming language.

[Simplicity](https://github.com/BlockstreamResearch/simplicity) is a typed, combinator-based, functional language without loops or recursion, developed to be an alternative to Bitcoin script that is formally specified, and can be statically analyzed with upper bounds on computation resources prior to execution.
[Simplicity][2] is a typed, combinator-based, functional language without loops or recursion, developed to be an alternative to Bitcoin script that is formally specified, and can be statically analyzed with upper bounds on computation resources prior to execution.

SimplicityHL is a high-level language for writing Simplicity smart contracts. SimplicityHL looks and feels like [Rust](https://www.rust-lang.org), but is compiled to Simplicity bytecode. Developers write SimplicityHL transactions, which Bitcoin/Liquid nodes verify with the Simplicity script interpreter.
SimplicityHL is a high-level language for writing Simplicity smart contracts. SimplicityHL looks and feels like [Rust][3], but is compiled to Simplicity bytecode. Developers write SimplicityHL transactions, which Bitcoin/Liquid nodes verify with the Simplicity script interpreter.

## Features

- Syntax highlighting and snippets for `.simf` and `.wit` files
- Compiler diagnostics, completion, hover, signature help, symbols, references, and go to definition
- Opt-in imports and enums support in the language server and direct compiler commands
- Formatting of SimplicityHL code using default VSCode bindings

The extension installs or connects to the [SimplicityHL language server](https://github.com/BlockstreamResearch/simplicityhl-lsp), which provides language intelligence:
The extension installs or connects to the [SimplicityHL language server][4], which provides language intelligence.
To provide concise and clear code you can also use the [simfmt][5] formatter.

- Error diagnostics
![diagnostics](https://github.com/user-attachments/assets/54315645-464b-40c3-bb72-c6e8c4bc0ad5)
![diagnostics][6]

- Completion of user-defined functions, imported items, built-ins, and jets
![completion](https://github.com/user-attachments/assets/bbc2b9de-c286-4d31-b47e-ac95885f8916)
![completion][7]

- Formatting of SimplicityHL code
![formatting][8]

## Experimental features

Expand All @@ -34,4 +39,14 @@ For local extension development, `simplicityhl.server.path` can point to a local

### Development

To install the extension manually or hack on the source code see [development.md](docs/development.md)
To install the extension manually or hack on the source code see [development.md][9]

[1]: https://github.com/BlockstreamResearch/SimplicityHL
[2]: https://github.com/BlockstreamResearch/simplicity
[3]: https://www.rust-lang.org
[4]: https://github.com/BlockstreamResearch/simplicityhl-lsp
[5]: https://github.com/BlockstreamResearch/simfmt
[6]: https://raw.githubusercontent.com/BlockstreamResearch/simplicityhl-vscode/master/assets/error_diagnostic.gif
[7]: https://raw.githubusercontent.com/BlockstreamResearch/simplicityhl-vscode/master/assets/completion.gif
[8]: https://raw.githubusercontent.com/BlockstreamResearch/simplicityhl-vscode/master/assets/formatting.gif
[9]: https://raw.githubusercontent.com/BlockstreamResearch/simplicityhl-vscode/master/docs/development.md
Binary file added assets/completion.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/error_diagnostic.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/formatting.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 37 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "simplicityhl",
"displayName": "SimplicityHL Language Support",
"description": "Syntax highlighting and autocompletion for SimplicityHL (Simfony) language",
"version": "0.4.0",
"version": "0.4.1",
"publisher": "Blockstream",
"repository": {
"type": "git",
Expand Down Expand Up @@ -95,6 +95,22 @@
"default": "",
"description": "Path to the simc compiler. If empty, searches PATH and common locations."
},
"simplicityhl.formatter.path": {
"type": "string",
"default": "",
"scope": "machine-overridable",
"description": "Path to the simfmt formatter. If empty, searches PATH and common locations."
},
"simplicityhl.formatter.autoSaveBeforeFormat": {
"type": "boolean",
"default": true,
"description": "Automatically save the file before formatting."
},
"simplicityhl.formatter.disableAutoupdate": {
"type": "boolean",
"default": false,
"description": "Do not autoupdate simfmt formatter."
},
"simplicityhl.build.autoSaveBeforeCompile": {
"type": "boolean",
"default": true,
Expand All @@ -118,6 +134,11 @@
}
}
],
"configurationDefaults": {
"[simplicityhl]": {
"editor.defaultFormatter": "Blockstream.simplicityhl"
}
},
"commands": [
{
"command": "simplicityhl.restartServer",
Expand All @@ -144,6 +165,11 @@
"command": "simplicityhl.compileJson",
"title": "Compile to JSON",
"category": "SimplicityHL"
},
{
"command": "simplicityhl.formatFile",
"title": "Format Current File",
"category": "SimplicityHL"
}
],
"menus": {
Expand All @@ -164,6 +190,11 @@
"when": "resourceLangId == simplicityhl",
"command": "simplicityhl.compileWithWitness",
"group": "1_modification"
},
{
"when": "resourceLangId == simplicityhl",
"command": "simplicityhl.formatFile",
"group": "1_modification"
}
],
"commandPalette": [
Expand All @@ -182,6 +213,10 @@
{
"command": "simplicityhl.compileJson",
"when": "resourceLangId == simplicityhl"
},
{
"command": "simplicityhl.formatFile",
"when": "resourceLangId == simplicityhl"
}
]
},
Expand Down Expand Up @@ -240,7 +275,7 @@
"compile": "npm run check-types && node esbuild.mjs --production",
"check": "npm test && npm run eslint-check && npm run compile",
"check-types": "tsc --noEmit",
"test": "node --test -r ts-node/register src/contracts.test.ts src/find_executable.test.ts",
"test": "node --test -r ts-node/register src/contracts.test.ts src/find_executable.test.ts src/update_cache.test.ts src/find_server.test.ts src/format/output.test.ts",
"watch": "npm-run-all -p watch:*",
"watch:esbuild": "node esbuild.mjs --watch",
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
Expand Down
33 changes: 7 additions & 26 deletions src/commands/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,28 @@ import type { CompileOptions } from "../compiler/args";
import {
COMMAND_IDS,
CONFIGURATION_SECTION,
LANGUAGE_IDS,
SETTINGS,
} from "../contracts";
import { getActiveSimplicityHLDocument } from "../document";

function showCompilationFailed(): void {
vscode.window.showErrorMessage(
"Compilation failed. See the SimplicityHL Compiler output for details."
);
}

// Validates that the active editor contains a SimplicityHL file
// Gets the active SimplicityHL file and auto-saves it when configured.
async function getSimplicityHLDocument(): Promise<vscode.TextDocument | undefined> {
const editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showWarningMessage("No active file to compile");
return undefined;
}

const document = editor.document;
if (document.languageId !== LANGUAGE_IDS.source) {
vscode.window.showWarningMessage("Current file is not a SimplicityHL file (.simf)");
return undefined;
}

// Auto-save before compile if enabled
const config = vscode.workspace.getConfiguration(CONFIGURATION_SECTION);
const autoSave = config.get<boolean>(
SETTINGS.autoSaveBeforeCompile.key,
SETTINGS.autoSaveBeforeCompile.default,
);
if (autoSave && document.isDirty) {
const saved = await document.save();
if (!saved) {
void vscode.window.showWarningMessage(
"SimplicityHL compilation canceled because the file could not be saved.",
);
return undefined;
}
}

return document;
return getActiveSimplicityHLDocument({
action: "compile",
saveBeforeAction: autoSave,
failIfSaveFails: true,
});
}

async function compileActiveDocument(
Expand Down
33 changes: 33 additions & 0 deletions src/commands/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Command and native provider registration for SimplicityHL formatting.

import * as vscode from "vscode";
import { COMMAND_IDS, LANGUAGE_IDS } from "../contracts";
import { getActiveSimplicityHLDocument } from "../document";
import type { SimplicityHLFormatter } from "../format";

export function registerFormattingCommands(
context: vscode.ExtensionContext,
formatter: () => SimplicityHLFormatter,
): void {
const formatFileCommand = vscode.commands.registerCommand(
COMMAND_IDS.formatFile,
async () => {
const document = await getActiveSimplicityHLDocument({ action: "format" });
if (!document) return;

await formatter().formatDocument(document);
},
);

const provider = vscode.languages.registerDocumentFormattingEditProvider(
{ language: LANGUAGE_IDS.source },
{
provideDocumentFormattingEdits(document, options, token) {
if (token.isCancellationRequested) return undefined;
return formatter().provideDocumentFormattingEdits(document, options, token);
},
},
);

context.subscriptions.push(formatFileCommand, provider);
}
15 changes: 13 additions & 2 deletions src/contracts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as fs from "node:fs";
import * as path from "node:path";
import { test } from "node:test";

import { SETTINGS, languageClientOptions } from "./contracts";
import { COMMAND_IDS, SETTINGS, languageClientOptions } from "./contracts";

void test("client languages and consumed settings match package contributions", () => {
const manifest = JSON.parse(
Expand All @@ -24,12 +24,23 @@ void test("client languages and consumed settings match package contributions",
),
) as Record<string, { default?: unknown }>;

for (const setting of [SETTINGS.serverPath, SETTINGS.imports, SETTINGS.enums]) {
for (const setting of [
SETTINGS.serverPath,
SETTINGS.imports,
SETTINGS.enums,
SETTINGS.formatterPath,
SETTINGS.autoSaveBeforeFormat,
SETTINGS.formatterDisableAutoupdate,
]) {
const contribution =
contributedSettings[
`${clientOptions.synchronize.configurationSection}.${setting.key}`
];
assert.ok(contribution, `Missing package contribution for ${setting.key}`);
assert.equal(contribution.default, setting.default);
}

assert.ok(contributions.commands.some(
({ command }: { command: string }) => command === COMMAND_IDS.formatFile,
));
});
15 changes: 15 additions & 0 deletions src/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export const CONFIGURATION_SECTION = "simplicityhl";
export const LANGUAGE_CLIENT_ID = "simplicityhlLspClient";
export const LANGUAGE_CLIENT_NAME = "SimplicityHL LSP";
export const SERVER_BINARY = "simplicityhl-lsp";
export const FORMATTER_BINARY = "simfmt";
export const FORMATTER_UPDATE_CACHE_KEY = "simfmt.lastUpdateAttemptAt";
export const TASK_TYPE = "simplicityhl";

export const LANGUAGE_IDS = {
Expand All @@ -15,6 +17,7 @@ export const COMMAND_IDS = {
compileFileDebug: "simplicityhl.compileFileDebug",
compileWithWitness: "simplicityhl.compileWithWitness",
compileJson: "simplicityhl.compileJson",
formatFile: "simplicityhl.formatFile",
} as const;

export const TASK_COMMANDS = [
Expand All @@ -40,6 +43,18 @@ export const SETTINGS = {
key: "compiler.path",
default: "",
},
formatterPath: {
key: "formatter.path",
default: "",
},
autoSaveBeforeFormat: {
key: "formatter.autoSaveBeforeFormat",
default: true,
},
formatterDisableAutoupdate: {
key: "formatter.disableAutoupdate",
default: false,
},
autoSaveBeforeCompile: {
key: "build.autoSaveBeforeCompile",
default: true,
Expand Down
Loading