Add vscode editor integration (#255)

- adds `editors` directory
- migrates [xsebek/swarm-vscode](https://github.com/xsebek/swarm-vscode) repo to `editors/`
- updates the highlighting (drill, sum types,...)
- adds some notes how to test and build the extension
- part of #100 
- closes #123
This commit is contained in:
Ondřej Šebek 2021-11-11 17:15:17 +01:00 committed by GitHub
parent e5166e31c9
commit a0e65d9e66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 610 additions and 21 deletions

View File

@ -79,6 +79,38 @@ The recommended way to install Swarm at the moment is as follows:
tutorial](TUTORIAL.md) to help get you started. Eventually there
will be an in-game tutorial.
Programming swarm
=================
Your base has a dictionary to store definitions, like this one:
```
def moveUntil : cmd bool -> cmd () = \predicate.
res <- predicate;
if res {
noop
} {
moveUntil predicate
}
end
```
<sup>The indentation is not required but `;` is, as it is similar
to Haskell `>>` - that is the command monad, which imperative
programmers can ignore. :wink:
</sup>
This allows you to program robots to perform complicated tasks.
While you can write commands and definitions like the one above
in the REPL, swarm also has a editor support with highlighting
and LSP integration:
![Editor with problem popup](images/editor.png)
See the `editors` folder for details on how to configure your editor.
Community
=========

View File

@ -393,7 +393,7 @@ than nothing for now.
There is some rudimentary [Language Server-based editor
support](https://github.com/byorgey/swarm/blob/main/docs/EDITORS.md)
giving syntax and error highlighting for `.sw` files; at the moment
Emacs is supported, and VSCode will be added shortly.
Emacs is supported along with VSCode.
World generation
----------------

View File

@ -1,20 +0,0 @@
# Text Editor Configuration
Validate a swarm-lang file using: `swarm format ./file.sw`
Swarm comes with a language server protocol (LSP) server.
Make sure the `swarm` program is present in your PATH, then
configure your editor:
* [Emacs](#EMACS)
The current LSP implementation features the following extensions:
* error diagnostic on load/save
## EMACS
Using [lsp-mode](https://github.com/emacs-lsp/lsp-mode):
Load the [swarm-mode.el](../contribs/swarm-mode.el) and start
the `M-x lsp` service in a *swarm-mode* buffer.

35
editors/README.md Normal file
View File

@ -0,0 +1,35 @@
# Text Editor Configuration
Validate a swarm-lang file using: `swarm format ./file.sw`
Swarm comes with a language server protocol (LSP) server.
Make sure the `swarm` program is present in your PATH, then
configure your editor.
## EMACS
The current LSP implementation features the following extensions:
* error diagnostic on load/save
Using [lsp-mode](https://github.com/emacs-lsp/lsp-mode):
Load the [swarm-mode.el](../contribs/swarm-mode.el) and start
the `M-x lsp` service in a *swarm-mode* buffer.
## VS Code and VS Codium
The [swarm-lang](./vscode) extension provides highlighting and an
LSP client. That is if you have `swarm` executable in PATH, then
the executable will be used as LSP server to show errors as you type.
You can get it by:
- installing from MS marketplace ([link](https://marketplace.visualstudio.com/items?itemName=xsebek.swarm-language))
- building from source in the [vscode folder](./vscode/DEVELOPING.md)
- **TBD** get the VSIX from GitHub releases
- **TBD** installing from the VS codium free marketplace
## Vim and Neovim
Currently there is neither highlighting nor LSP support for Vim,
but we would be happy to [accept a contribution](../CONTRIBUTING.md).

4
editors/vscode/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
*.vsix
*-lock.json
out/

17
editors/vscode/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
// A launch configuration that launches the extension inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
]
}
]
}

View File

@ -0,0 +1,4 @@
.vscode/**
.vscode-test/**
.gitignore
DEVELOPING.md

View File

@ -0,0 +1,19 @@
# Change Log
All notable changes to the "swarm-language" extension will be documented in this file.
## version 0.0.3
- [Highlighter] Update reserved word list (include `drill`, `has`, etc.)
- [Highlighter] Recognize sum types (`a + b`)
- [Highlighter] Recognize explicit `forall a b.`
- moved the package to `byorgey/swarm` repository
- added a logo (featuring base and four robots)
## version 0.0.2
- Add LSP integration (requires `swarm` executable).
## version 0.0.1
- Initial release with support for higlighting

View File

@ -0,0 +1,72 @@
## Developing
> The extension was created using [`yo code`](https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide#developing-a-new-grammar-extension)
> and further extended to suport LSP by including modified
> [Microsoft lsp-sample](https://github.com/microsoft/vscode-extension-samples/tree/main/lsp-sample).
>
> As a note, this is deliberately a separate file from `README.md` which shows in the VSCode extension view.
### What's in the folder
* `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension.
* `syntaxes/swarm.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization.
* `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets.
* `client/src/extension.ts` - this is the LSP client, that will connect to `swarm` executable which can serve as LSP server.
### Get up and running straight away
* Make sure the language configuration settings in `language-configuration.json` are accurate.
* Press `F5` to open a new window with your extension loaded.
* Create a new file with a file name suffix matching your language.
* Verify that syntax highlighting works and that the language configuration settings are working.
### Make changes
* You can relaunch the extension from the debug toolbar after making changes to the files listed above.
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
### Updating the syntax highlighting
Whenever swarm language adds new features, the highlighing needs to be updated.
To save some time, get the current reserved words by running `cabal repl`:
```haskell
import Swarm.Language.Syntax
import qualified Data.Text as T
:set -XOverloadedStrings
-- get basic functions/commands
T.intercalate "|" $ map (syntax . constInfo) (filter isUserFunc allConst)
-- get list of directions
T.intercalate "|" $ map (dirSyntax . dirInfo) allDirs
```
You still have to add for example types manually.
### Add more language features
To add features such as intellisense, hovers and validators check out the VS Code extenders documentation
at [VSCode docs](https://code.visualstudio.com/docs).
## Install your extension
First install the dependencies:
```bash
npm update
npm install
```
If you want to include the LSP client then after the previous install do:
```sh
cd client
tsc --build
```
To build the VSIX package do:
```sh
vsce package --baseImagesUrl "https://raw.githubusercontent.com/byorgey/swarm/editors/vscode"
```
To share this extension with the world, read on https://code.visualstudio.com/docs about publishing an extension or ask @xsebek to do it.

17
editors/vscode/README.md Normal file
View File

@ -0,0 +1,17 @@
# swarm-language README
This VSCode extension provides a basic highlighting and LSP client for the swarm programming language.
![VSCode screenshot](images/editor_debug.png)
## Extension Settings
There are no customizations yet, sorry.
## Known Issues
- The highlighter expects type and `=` sign on the same line as `def`/`let`, i.e.
```haskell
def missionImpossible : cmd a -> a = ...
```
This regexp limitation is unlikely to impact many users, but is still a departure from real swarm.

View File

@ -0,0 +1,23 @@
{
"name": "swarm-lsp-client",
"description": "VSCode part of the swarm language server",
"author": "Ondřej Šebek",
"license": "MIT",
"version": "0.0.3",
"publisher": "vscode",
"repository": {
"type": "git",
"url": "https://github.com/byorgey/swarm"
},
"engines": {
"vscode": "^1.52.0"
},
"dependencies": {
"vscode-languageclient": "^7.0.0"
},
"devDependencies": {
"@types/vscode": "^1.52.0",
"@vscode/test-electron": "^1.6.1"
}
}

View File

@ -0,0 +1,51 @@
import * as path from 'path';
import * as process from 'process';
import { workspace, ExtensionContext, DebugAdapterExecutable } from 'vscode';
import {
Executable,
LanguageClient,
LanguageClientOptions,
ServerOptions,
TextDocumentSyncKind,
TransportKind
} from 'vscode-languageclient/node';
let client: LanguageClient;
export function activate(context: ExtensionContext) {
// The server is implemented in node
const serverModule : Executable = {
command: "swarm",
args: ["lsp"],
options: {detached:true, shell:true},
}
var serverOptions: ServerOptions = serverModule;
TextDocumentSyncKind
// Options to control the language client
const clientOptions: LanguageClientOptions = {
// Register the server for swarm files
documentSelector: [{ scheme: 'file', language: 'swarm' }]
};
// Create the language client and start the client.
client = new LanguageClient(
'languageServerSwarm',
'Language Server Swarm',
serverOptions,
clientOptions
);
// Start the client. This will also launch the server
client.start();
}
export function deactivate(): Thenable<void> | undefined {
if (!client) {
return undefined;
}
return client.stop();
}

View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2019",
"lib": ["ES2019"],
"outDir": "out",
"rootDir": "src",
"sourceMap": true
},
"include": ["src"],
"exclude": ["node_modules", ".vscode-test"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:export-filename="/home/sebeko/Pictures/swarm-logo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
sodipodi:docname="swarm-logo.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.69130191"
inkscape:cx="611.16568"
inkscape:cy="549.68747"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" />
<defs
id="defs2" />
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2">
<circle
style="fill:#000000;stroke-width:0.26543;fill-opacity:1"
id="path1259"
cx="104.08221"
cy="99.740601"
r="80" />
<rect
style="fill:#d4aa00;stroke-width:0.21937"
id="rect8750"
width="96"
height="96"
x="96.124489"
y="-51.069984"
transform="rotate(45)" />
<rect
style="fill:#000000;stroke-width:0.187561"
id="rect8750-4"
width="82.080002"
height="82.080002"
x="103.0845"
y="-44.109989"
transform="rotate(45)" />
<rect
style="fill:#000000;stroke-width:0.235961"
id="rect8899"
width="11.058861"
height="151.99393"
x="139.36646"
y="-77.826645"
transform="rotate(45)" />
<rect
style="fill:#000000;stroke-width:0.235961"
id="rect8899-3"
width="11.058861"
height="151.99393"
x="-7.3591065"
y="-220.89285"
transform="rotate(135)" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="omega">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:75.9353px;line-height:1.25;font-family:sans-serif;fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:1.89839"
x="74.614418"
y="127.18298"
id="text5375"
transform="scale(1.0203302,0.98007488)"><tspan
sodipodi:role="line"
id="tspan5373"
style="fill:#e6e6e6;stroke-width:1.89839"
x="74.614418"
y="127.18298">Ω</tspan></text>
</g>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,27 @@
{
"comments": {
// symbol used for single line comment. Remove this entry if your language does not support line comments
"lineComment": "//",
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
"blockComment": [ "/*", "*/" ]
},
// symbols used as brackets
"brackets": [
["{", "}"],
["(", ")"]
],
// symbols that are auto closed when typing
"autoClosingPairs": [
["{", "}"],
["(", ")"],
["\"", "\""],
],
// symbols that can be used to surround a selection
"surroundingPairs": [
["{", "}"],
["(", ")"],
["\"", "\""],
["def", "end"],
["let", "end"],
]
}

View File

@ -0,0 +1,83 @@
{
"name": "swarm-language",
"displayName": "swarm-language",
"description": "VSCode support for swarm (the game) programming language.",
"version": "0.0.3",
"icon": "images/swarm-logo.png",
"publisher": "xsebek",
"repository": {
"url": "https://github.com/byorgey/swarm"
},
"engines": {
"vscode": "^1.61.0"
},
"categories": [
"Programming Languages"
],
"activationEvents": [
"onLanguage:swarm"
],
"main": "./client/out/extension",
"contributes": {
"languages": [
{
"id": "swarm",
"aliases": [
"swarm",
"swarm"
],
"extensions": [
".sw"
],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "swarm",
"scopeName": "source.swarm",
"path": "./syntaxes/swarm.tmLanguage.json"
}
],
"configuration": {
"type": "object",
"title": "Swarm language configuration",
"properties": {
"languageServerExample.maxNumberOfProblems": {
"scope": "resource",
"type": "number",
"default": 100,
"description": "Controls the maximum number of problems produced by the server."
},
"languageServerExample.trace.server": {
"scope": "window",
"type": "string",
"enum": [
"off",
"messages",
"verbose"
],
"default": "off",
"description": "Traces the communication between VS Code and the language server."
}
}
}
},
"devDependencies": {
"@types/mocha": "^8.2.2",
"@types/node": "^12.12.0",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"esbuild": "^0.13.13",
"eslint": "^7.26.0",
"mocha": "^8.3.2",
"typescript": "^4.4.3"
},
"scripts": {
"vscode:prepublish": "npm run esbuild-base -- --minify",
"esbuild-base": "esbuild ./client/src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node",
"esbuild": "npm run esbuild-base -- --sourcemap",
"esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
"test-compile": "tsc -p ./"
}
}

View File

@ -0,0 +1,109 @@
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "swarm",
"patterns": [
{"include": "#keywords"},
{"include": "#comments"},
{"include": "#strings"},
{"include": "#variables"},
{"include": "#constants"}
],
"repository": {
"keywords": {
"patterns": [
{
"name": "keyword.control.dictionary.def",
"begin": "def\\s+(\\w+)\\s*(:((\\s*(cmd|dir|string|int|\\(|\\)|\\{|\\}|(\\*|\\+|->)|[a-z]\\w*|forall ([a-z]\\w*\\s*)+.)\\s*)+))?=",
"end": "end",
"beginCaptures": {
"1": {"name": "variable.other"},
"3": {"name": "storage.type"},
"5": {"name": "storage.modifier"}
},
"patterns": [
{"include": "#keywords"},
{"include": "#comments"},
{"include": "#strings"},
{"include": "#variables"},
{"include": "#constants"}
]
},
{
"name": "keyword.control.dictionary.let",
"begin": "let\\s+(\\w+)\\s*(:((\\s*(cmd|dir|string|int|\\(|\\)|(\\*|\\+|->)|[a-z]\\w*|forall ([a-z]\\w*\\s*)+.)\\s*)+))?=",
"end": "in",
"beginCaptures": {
"1": {"name": "variable.other"},
"3": {"name": "storage.type"},
"5": {"name": "storage.modifier"}
},
"patterns": [
{"include": "#keywords"},
{"include": "#comments"},
{"include": "#strings"},
{"include": "#variables"},
{"include": "#constants"}
]
},
{
"name": "keyword.operator",
"match": "(<|>|==|<=|>=|!=|=|;|<-|-|\\+|\\*|\\^|\\$|/(?![/|*]))"
},
{
"name": "keyword.operator.lambda",
"match": "\\\\(\\w+)\\.",
"captures": { "1": {"name": "variable.other"} }
},
{
"name": "keyword.other",
"match": "\\b(?i)(noop|wait|selfdestruct|move|turn|grab|place|give|install|make|has|count|drill|build|salvage|reprogram|say|log|view|appear|create|whereami|blocked|scan|upload|ishere|whoami|random|run|if|inl|inr|case|fst|snd|force|return|try|raise|not)\\b"
}
]
},
"comments": {
"patterns": [
{
"name": "comment.line.double-slash",
"begin": "//",
"end": "\n"
},
{
"name":"comment.block",
"begin": "\/[*]",
"end": "[*](\/)"
}
]
},
"strings":{
"patterns": [
{
"name":"string.quoted.double",
"begin": "\"",
"end": "\""
}
]
},
"variables":{
"patterns": [
{
"name": "variable.language.dir",
"match": "\\b(?i)(left|right|back|forward|north|south|east|west|down)\\b"
},
{
"name": "variable.other" ,
"match": "\\b(?i)([a-z]\\w*)\\b"
}
]
},
"constants": {
"patterns": [
{
"name": "constant.numeric",
"match": "[0-9]"
}
]
}
},
"scopeName": "source.swarm"
}

BIN
images/editor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB