diff --git a/README.md b/README.md index e9a564fc..640bdd54 100644 --- a/README.md +++ b/README.md @@ -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 +``` + +The indentation is not required but `;` is, as it is similar +to Haskell `>>` - that is the command monad, which imperative +programmers can ignore. :wink: + + +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 ========= diff --git a/TUTORIAL.md b/TUTORIAL.md index 05463bae..255b4a35 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -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 ---------------- diff --git a/docs/EDITORS.md b/docs/EDITORS.md deleted file mode 100644 index e7e30825..00000000 --- a/docs/EDITORS.md +++ /dev/null @@ -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. diff --git a/editors/README.md b/editors/README.md new file mode 100644 index 00000000..65fcd4e1 --- /dev/null +++ b/editors/README.md @@ -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). \ No newline at end of file diff --git a/contribs/swarm-mode.el b/editors/emacs/swarm-mode.el similarity index 100% rename from contribs/swarm-mode.el rename to editors/emacs/swarm-mode.el diff --git a/editors/vscode/.gitignore b/editors/vscode/.gitignore new file mode 100644 index 00000000..507b6fdb --- /dev/null +++ b/editors/vscode/.gitignore @@ -0,0 +1,4 @@ +node_modules +*.vsix +*-lock.json +out/ diff --git a/editors/vscode/.vscode/launch.json b/editors/vscode/.vscode/launch.json new file mode 100644 index 00000000..0e191b59 --- /dev/null +++ b/editors/vscode/.vscode/launch.json @@ -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}" + ] + } + ] +} \ No newline at end of file diff --git a/editors/vscode/.vscodeignore b/editors/vscode/.vscodeignore new file mode 100644 index 00000000..c2e45c2f --- /dev/null +++ b/editors/vscode/.vscodeignore @@ -0,0 +1,4 @@ +.vscode/** +.vscode-test/** +.gitignore +DEVELOPING.md diff --git a/editors/vscode/CHANGELOG.md b/editors/vscode/CHANGELOG.md new file mode 100644 index 00000000..7927e51b --- /dev/null +++ b/editors/vscode/CHANGELOG.md @@ -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 \ No newline at end of file diff --git a/editors/vscode/DEVELOPING.md b/editors/vscode/DEVELOPING.md new file mode 100644 index 00000000..f831a41c --- /dev/null +++ b/editors/vscode/DEVELOPING.md @@ -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. diff --git a/editors/vscode/README.md b/editors/vscode/README.md new file mode 100644 index 00000000..9f7987c8 --- /dev/null +++ b/editors/vscode/README.md @@ -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. diff --git a/editors/vscode/client/package.json b/editors/vscode/client/package.json new file mode 100644 index 00000000..d67ac15b --- /dev/null +++ b/editors/vscode/client/package.json @@ -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" + } +} \ No newline at end of file diff --git a/editors/vscode/client/src/extension.ts b/editors/vscode/client/src/extension.ts new file mode 100644 index 00000000..ada86702 --- /dev/null +++ b/editors/vscode/client/src/extension.ts @@ -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 | undefined { + if (!client) { + return undefined; + } + return client.stop(); +} diff --git a/editors/vscode/client/tsconfig.json b/editors/vscode/client/tsconfig.json new file mode 100644 index 00000000..c2e0507f --- /dev/null +++ b/editors/vscode/client/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2019", + "lib": ["ES2019"], + "outDir": "out", + "rootDir": "src", + "sourceMap": true + }, + "include": ["src"], + "exclude": ["node_modules", ".vscode-test"] +} \ No newline at end of file diff --git a/editors/vscode/images/editor_debug.png b/editors/vscode/images/editor_debug.png new file mode 100644 index 00000000..4b004860 Binary files /dev/null and b/editors/vscode/images/editor_debug.png differ diff --git a/editors/vscode/images/swarm-logo.png b/editors/vscode/images/swarm-logo.png new file mode 100644 index 00000000..4ef37afb Binary files /dev/null and b/editors/vscode/images/swarm-logo.png differ diff --git a/editors/vscode/images/swarm-logo.svg b/editors/vscode/images/swarm-logo.svg new file mode 100644 index 00000000..c09008c9 --- /dev/null +++ b/editors/vscode/images/swarm-logo.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + Ω + + + diff --git a/editors/vscode/language-configuration.json b/editors/vscode/language-configuration.json new file mode 100644 index 00000000..0c8efd1e --- /dev/null +++ b/editors/vscode/language-configuration.json @@ -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"], + ] +} \ No newline at end of file diff --git a/editors/vscode/package.json b/editors/vscode/package.json new file mode 100644 index 00000000..b672d987 --- /dev/null +++ b/editors/vscode/package.json @@ -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 ./" + } +} diff --git a/editors/vscode/syntaxes/swarm.tmLanguage.json b/editors/vscode/syntaxes/swarm.tmLanguage.json new file mode 100644 index 00000000..b6b86f24 --- /dev/null +++ b/editors/vscode/syntaxes/swarm.tmLanguage.json @@ -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" +} \ No newline at end of file diff --git a/images/editor.png b/images/editor.png new file mode 100644 index 00000000..d33f2cfa Binary files /dev/null and b/images/editor.png differ