├── snippets └── sway.json ├── .prettierignore ├── images ├── logo.png ├── dark │ ├── sway.png │ ├── play.svg │ ├── edit.svg │ └── refresh.svg ├── light │ ├── sway.png │ ├── play.svg │ ├── edit.svg │ └── refresh.svg └── fuel.svg ├── .gitattributes ├── client ├── test │ └── syntaxes │ │ ├── basic_predicate.sw │ │ ├── const_decl.sw │ │ ├── storage_declaration.sw │ │ ├── smo_opcode.sw │ │ ├── match_expressions_mismatched.sw │ │ ├── chained_if_let.sw │ │ ├── particle.sw │ │ ├── basic_predicate.sw.snap │ │ ├── const_decl.sw.snap │ │ ├── storage_declaration.sw.snap │ │ ├── smo_opcode.sw.snap │ │ ├── match_expressions_mismatched.sw.snap │ │ ├── chained_if_let.sw.snap │ │ └── particle.sw.snap ├── src │ ├── commands │ │ ├── forcBuild.ts │ │ ├── forcRun.ts │ │ ├── installServer.ts │ │ ├── startFuelCore.ts │ │ ├── forcTest.ts │ │ ├── goToLocation.ts │ │ ├── stopFuelCore.ts │ │ ├── peekLocations.ts │ │ ├── openAstFile.ts │ │ └── openDotGraph.ts │ ├── client.ts │ ├── interface │ │ ├── visualize.ts │ │ ├── showAst.ts │ │ └── onEnter.ts │ ├── util │ │ ├── convert.ts │ │ └── util.ts │ ├── status_bar │ │ └── fuelCoreStatus.ts │ ├── config.ts │ ├── palettes.ts │ ├── program.ts │ └── main.ts └── tsconfig.json ├── .vscodeignore ├── .prettierrc.json ├── scripts └── e2e.sh ├── tsconfig.json ├── .github └── workflows │ ├── conventional-commits.yml │ ├── marketplace-publish.yml │ ├── changelog.yml │ └── ci.yml ├── .vscode ├── tasks.json └── launch.json ├── language-configuration.json ├── README.md ├── docs └── testing.md ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── package.json └── syntaxes └── sway.tmLanguage.json /snippets/sway.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | build 3 | coverage 4 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FuelLabs/sway-vscode-plugin/HEAD/images/logo.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto 3 | 4 | -------------------------------------------------------------------------------- /client/test/syntaxes/basic_predicate.sw: -------------------------------------------------------------------------------- 1 | predicate; 2 | 3 | fn main() -> bool { 4 | true 5 | } 6 | -------------------------------------------------------------------------------- /images/dark/sway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FuelLabs/sway-vscode-plugin/HEAD/images/dark/sway.png -------------------------------------------------------------------------------- /images/light/sway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FuelLabs/sway-vscode-plugin/HEAD/images/light/sway.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | .gitignore 4 | vsc-extension-quickstart.md 5 | node_modules 6 | **/*.ts 7 | **/tsconfig.json 8 | -------------------------------------------------------------------------------- /client/test/syntaxes/const_decl.sw: -------------------------------------------------------------------------------- 1 | script; 2 | 3 | const GLOBAL_VAL: u64 = 99; 4 | 5 | fn main() -> u64 { 6 | const LOCAL_VAL = 1; 7 | GLOBAL_VAL + LOCAL_VAL 8 | } 9 | -------------------------------------------------------------------------------- /client/test/syntaxes/storage_declaration.sw: -------------------------------------------------------------------------------- 1 | contract; 2 | storage { 3 | supply: u64 = 0, 4 | demand: u64 = 0, 5 | name: str[4] = "test", 6 | } 7 | fn main() { 8 | } 9 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "semi": true, 4 | "singleQuote": true, 5 | "bracketSpacing": true, 6 | "trailingComma": "es5", 7 | "arrowParens": "avoid" 8 | } -------------------------------------------------------------------------------- /scripts/e2e.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export CODE_TESTS_PATH="$(pwd)/client/out/test" 4 | export CODE_TESTS_WORKSPACE="$(pwd)/client/testFixture" 5 | 6 | node "$(pwd)/client/out/test/runTest" -------------------------------------------------------------------------------- /client/src/commands/forcBuild.ts: -------------------------------------------------------------------------------- 1 | import { Terminal } from '../util/util'; 2 | 3 | export default function forcBuild(forcDir: string) { 4 | Terminal.Sway.execute(`cd ${forcDir} && forc build`); 5 | } 6 | -------------------------------------------------------------------------------- /client/src/commands/forcRun.ts: -------------------------------------------------------------------------------- 1 | import { Terminal } from '../util/util'; 2 | 3 | export default function forcRun(forcDir: string) { 4 | Terminal.Sway.execute(`cd ${forcDir} && forc run --unsigned`); 5 | } 6 | -------------------------------------------------------------------------------- /client/src/commands/installServer.ts: -------------------------------------------------------------------------------- 1 | import { Terminal } from '../util/util'; 2 | 3 | export default function installServer() { 4 | Terminal.Sway.execute( 5 | `curl --proto '=https' --tlsv1.2 -sSf https://install.fuel.network/fuelup-init.sh | sh` 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /client/test/syntaxes/smo_opcode.sw: -------------------------------------------------------------------------------- 1 | script; 2 | 3 | use std::constants::ZERO_B256; 4 | 5 | fn main() -> bool { 6 | asm(recipient: ZERO_B256, msg_len: 0, output: 0, coins: 0) { 7 | smo recipient msg_len coins output; 8 | } 9 | true 10 | } 11 | -------------------------------------------------------------------------------- /client/src/commands/startFuelCore.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode'; 2 | import { Terminal } from '../util/util'; 3 | 4 | export default function startFuelCore() { 5 | Terminal.FuelCore.execute(`fuel-core run --db-type in-memory`); 6 | window.showInformationMessage(`Started fuel-core`); 7 | } 8 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2019", 5 | "lib": ["ES2019"], 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "sourceMap": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules", ".vscode-test"] 12 | } 13 | -------------------------------------------------------------------------------- /client/src/commands/forcTest.ts: -------------------------------------------------------------------------------- 1 | import { Terminal } from '../util/util'; 2 | 3 | export default function forcTest(forcDir: string, _testName?: string) { 4 | // TODO: add support for running specific tests when 5 | // https://github.com/FuelLabs/sway/issues/3268 is resolved 6 | Terminal.Sway.execute(`cd ${forcDir} && forc test`); 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2019", 5 | "lib": ["ES2019"], 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "sourceMap": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules", ".vscode-test"], 12 | "references": [{ "path": "./client" }] 13 | } 14 | -------------------------------------------------------------------------------- /client/src/commands/goToLocation.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as lc from 'vscode-languageclient'; 3 | import { toVSCodeRange, toVSCodeUri } from '../util/convert'; 4 | 5 | export default async function goToLocation({ uri, range }: lc.Location) { 6 | await vscode.window.showTextDocument(toVSCodeUri(uri), { 7 | selection: toVSCodeRange(range), 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /images/light/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/dark/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/commands/stopFuelCore.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process'; 2 | import { window } from 'vscode'; 3 | import updateFuelCoreStatus from '../status_bar/fuelCoreStatus'; 4 | 5 | export default function stopFuelCore() { 6 | // Using exec instead of the Terminal utility here because we don't need the user to see the 7 | // command output. 8 | exec(`pkill -15 fuel-core`, () => { 9 | window.showInformationMessage(`Stopped fuel-core`); 10 | updateFuelCoreStatus(); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /client/test/syntaxes/match_expressions_mismatched.sw: -------------------------------------------------------------------------------- 1 | script; 2 | 3 | struct MyStruct { 4 | a: u64, 5 | b: u64, 6 | } 7 | 8 | enum MyEnum { 9 | Variant1: (), 10 | Variant2: u64, 11 | Variant3: MyStruct, 12 | } 13 | 14 | fn main() -> u64 { 15 | let x = MyEnum::Variant1; 16 | let y = MyEnum::Variant2(5); 17 | let z = MyEnum::Variant3(MyStruct { 18 | a: 0, b: 1 19 | }); 20 | 21 | match y { 22 | MyEnum::Variant2(y) => y, _ => 10, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/test/syntaxes/chained_if_let.sw: -------------------------------------------------------------------------------- 1 | script; 2 | 3 | enum Result { 4 | Ok: T, 5 | Err: E, 6 | } 7 | 8 | // should return 5 9 | fn main() -> u64 { 10 | let result_a = Result::Ok::(5u64); 11 | let result_b = Result::Err::(false); 12 | 13 | if let Result::Err(a) = result_a { 14 | 6 15 | } else if let Result::Ok(num) = result_b { 16 | 10 17 | } else if let Result::Ok(num) = result_a { 18 | num 19 | } else { 20 | 42 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /images/dark/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/light/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/workflows/conventional-commits.yml: -------------------------------------------------------------------------------- 1 | name: PR Conventional Commit Validation 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened, edited] 6 | 7 | jobs: 8 | validate-pr-title: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | pull-requests: read 12 | steps: 13 | - name: PR Conventional Commit Validation 14 | uses: ytanikin/PRConventionalCommits@1.1.0 15 | with: 16 | task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]' 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | add_label: 'false' 19 | -------------------------------------------------------------------------------- /.github/workflows/marketplace-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Extension 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | environment: vscode-deploy 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: 16 17 | 18 | - run: npm ci 19 | 20 | - name: Publish to Visual Studio Marketplace 21 | uses: HaaLeo/publish-vscode-extension@v1 22 | with: 23 | pat: ${{ secrets.VS_MARKETPLACE_TOKEN }} 24 | registryUrl: https://marketplace.visualstudio.com 25 | -------------------------------------------------------------------------------- /client/src/commands/peekLocations.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as lc from 'vscode-languageclient'; 3 | import { toVSCodeLocation } from '../util/convert'; 4 | 5 | export interface PeekLocationsParams { 6 | locations: lc.Location[]; 7 | } 8 | 9 | export default async function peekLocations({ 10 | locations, 11 | }: PeekLocationsParams) { 12 | const uri = vscode.window.activeTextEditor.document.uri; 13 | const position = vscode.window.activeTextEditor.selection.active; 14 | vscode.commands.executeCommand( 15 | 'editor.action.peekLocations', 16 | uri, 17 | position, 18 | locations.map(toVSCodeLocation), 19 | 'peek' 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "compile", 7 | "group": "build", 8 | "presentation": { 9 | "panel": "dedicated", 10 | "reveal": "never" 11 | }, 12 | "problemMatcher": ["$tsc"] 13 | }, 14 | { 15 | "type": "npm", 16 | "script": "watch", 17 | "isBackground": true, 18 | "group": { 19 | "kind": "build", 20 | "isDefault": true 21 | }, 22 | "presentation": { 23 | "panel": "dedicated", 24 | "reveal": "never" 25 | }, 26 | "problemMatcher": ["$esbuild-watch"] 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Update Changelog 2 | on: 3 | # Manually triggered 4 | workflow_dispatch: 5 | 6 | jobs: 7 | update-changelog: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | fetch-depth: 0 13 | - name: Generate changelog and push 14 | uses: BobAnkh/auto-generate-changelog@v1.2.5 15 | with: 16 | ACCESS_TOKEN: ${{secrets.REPO_TOKEN}} 17 | COMMIT_MESSAGE: 'docs(CHANGELOG): Update release notes' 18 | PATH: 'CHANGELOG.md' 19 | TYPE: 'feat:Feature,fix:Bug Fixes,docs:Documentation,refactor:Refactor,perf:Performance Improvements,chore:Chores' 20 | -------------------------------------------------------------------------------- /client/src/client.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LanguageClient, 3 | LanguageClientOptions, 4 | ServerOptions, 5 | } from 'vscode-languageclient/node'; 6 | import { EXTENSION_ROOT } from './config'; 7 | 8 | let client: LanguageClient | null; 9 | 10 | export const createClient = ( 11 | clientOptions: LanguageClientOptions, 12 | serverOptions: ServerOptions 13 | ): LanguageClient => { 14 | if (client) { 15 | throw new Error('Client already exists!'); 16 | } 17 | client = new LanguageClient( 18 | EXTENSION_ROOT, 19 | 'Sway Language Server', 20 | serverOptions, 21 | clientOptions 22 | ); 23 | return client; 24 | }; 25 | 26 | export const getClient = (): LanguageClient => client; 27 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Sway Server", 8 | "program": "forc", 9 | "args": ["lsp"] 10 | }, 11 | { 12 | "type": "extensionHost", 13 | "request": "launch", 14 | "name": "Launch Client", 15 | "runtimeExecutable": "${execPath}", 16 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"], 17 | "outFiles": ["${workspaceRoot}/client/out/**/*.js"], 18 | "preLaunchTask": { 19 | "type": "npm", 20 | "script": "esbuild" 21 | } 22 | } 23 | ], 24 | "compounds": [ 25 | { 26 | "name": "Client + Server", 27 | "configurations": ["Launch Client", "Sway Server"] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /client/src/interface/visualize.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RequestType, 3 | TextDocumentIdentifier, 4 | } from 'vscode-languageclient/node'; 5 | import { getClient } from '../client'; 6 | 7 | export type GraphKind = 'build_plan'; 8 | 9 | interface VisualizeParams { 10 | textDocument: TextDocumentIdentifier; 11 | graphKind: GraphKind; 12 | } 13 | 14 | const request = new RequestType( 15 | 'sway/visualize' 16 | ); 17 | 18 | export const visualize = async ( 19 | filePath: string, 20 | graphKind: GraphKind 21 | ): Promise => { 22 | const client = getClient(); 23 | const params: VisualizeParams = { 24 | textDocument: { 25 | uri: filePath, 26 | }, 27 | graphKind, 28 | }; 29 | return await client.sendRequest(request, params); 30 | }; 31 | -------------------------------------------------------------------------------- /client/test/syntaxes/particle.sw: -------------------------------------------------------------------------------- 1 | script; 2 | 3 | /// A simple Particle struct 4 | pub struct Particle { 5 | position: [u64; 3], 6 | velocity: [u64; 3], 7 | acceleration: [u64; 3], 8 | mass: u64, 9 | } 10 | 11 | impl Particle { 12 | /// Creates a new Particle with the given position, velocity, acceleration, and mass 13 | fn new(position: [u64; 3], velocity: [u64; 3], acceleration: [u64; 3], mass: u64) -> Particle { 14 | Particle { 15 | position: position, 16 | velocity: velocity, 17 | acceleration: acceleration, 18 | mass: mass, 19 | } 20 | } 21 | } 22 | 23 | fn main() { 24 | let position = [0, 0, 0]; 25 | let velocity = [0, 1, 0]; 26 | let acceleration = [1, 1, position[1]]; 27 | let mass = 10; 28 | let p = ~Particle::new(position, velocity, acceleration, mass); 29 | } 30 | -------------------------------------------------------------------------------- /language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "//", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": ["/*", "*/"] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | ["{", "}"], 17 | ["[", "]"], 18 | ["(", ")"], 19 | ["\"", "\""], 20 | ["'", "'"] 21 | ], 22 | // symbols that can be used to surround a selection 23 | "surroundingPairs": [ 24 | ["{", "}"], 25 | ["[", "]"], 26 | ["(", ")"], 27 | ["\"", "\""], 28 | ["'", "'"] 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /client/src/interface/showAst.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DocumentUri, 3 | RequestType, 4 | TextDocumentIdentifier, 5 | } from 'vscode-languageclient/node'; 6 | import { getClient } from '../client'; 7 | 8 | export type AstKind = 'lexed' | 'parsed' | 'typed'; 9 | 10 | interface ShowAstParams { 11 | textDocument: TextDocumentIdentifier; 12 | astKind: AstKind; 13 | savePath: DocumentUri; 14 | } 15 | 16 | const request = new RequestType< 17 | ShowAstParams, 18 | TextDocumentIdentifier | null, 19 | void 20 | >('sway/show_ast'); 21 | 22 | export const showAst = async ( 23 | filePath: string, 24 | astKind: AstKind, 25 | savePath: string 26 | ): Promise => { 27 | const client = getClient(); 28 | const params: ShowAstParams = { 29 | textDocument: { 30 | uri: filePath, 31 | }, 32 | astKind, 33 | savePath, 34 | }; 35 | return await client.sendRequest(request, params); 36 | }; 37 | -------------------------------------------------------------------------------- /client/src/commands/openAstFile.ts: -------------------------------------------------------------------------------- 1 | import { commands, Uri } from 'vscode'; 2 | import { AstKind, showAst } from '../interface/showAst'; 3 | import { addFilePrefix, log } from '../util/util'; 4 | 5 | const SAVE_PATH = '/tmp/'; 6 | 7 | export default async function openAstFile(filePath: string, astKind: AstKind) { 8 | try { 9 | const astDocument = await showAst( 10 | addFilePrefix(filePath), 11 | astKind, 12 | addFilePrefix(SAVE_PATH) 13 | ); 14 | if (astDocument) { 15 | const openPath = Uri.parse(astDocument.uri); 16 | await commands.executeCommand('vscode.openFolder', openPath, { 17 | forceNewWindow: true, 18 | }); 19 | log.info(`Successfully opened ${astKind} AST file ${filePath}`); 20 | } else { 21 | log.error(`No ${astKind} AST file found for ${filePath}`); 22 | } 23 | } catch (error) { 24 | log.error(`Failed to open ${astKind} AST file for ${filePath}`, error); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /images/dark/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/light/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [master] 7 | 8 | jobs: 9 | format: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 16 16 | - run: npm ci 17 | - run: npm run format:check 18 | 19 | build: 20 | needs: [format] 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: actions/setup-node@v1 25 | with: 26 | node-version: 16 27 | 28 | - run: npm ci 29 | - name: Dry run publish 30 | uses: HaaLeo/publish-vscode-extension@v1 31 | with: 32 | pat: 'quickbrownfox' 33 | dryRun: true 34 | syntaxes: 35 | runs-on: ubuntu-latest 36 | 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions/setup-node@v1 40 | with: 41 | node-version: 16 42 | - run: npm ci 43 | - run: npm run test-syntaxes 44 | -------------------------------------------------------------------------------- /client/test/syntaxes/basic_predicate.sw.snap: -------------------------------------------------------------------------------- 1 | >predicate; 2 | #^^^^^^^^^ source.sway source.sway meta.attribute.sway 3 | # ^ source.sway 4 | > 5 | >fn main() -> bool { 6 | #^^ source.sway meta.function.definition.sway keyword.other.fn.sway 7 | # ^ source.sway meta.function.definition.sway 8 | # ^^^^ source.sway meta.function.definition.sway entity.name.function.sway 9 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 10 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 11 | # ^ source.sway meta.function.definition.sway 12 | # ^^ source.sway meta.function.definition.sway keyword.operator.arrow.skinny.sway 13 | # ^ source.sway meta.function.definition.sway 14 | # ^^^^ source.sway meta.function.definition.sway entity.name.type.primitive.sway 15 | # ^ source.sway meta.function.definition.sway 16 | # ^ source.sway meta.function.definition.sway punctuation.brackets.curly.sway 17 | > true 18 | #^^^^ source.sway 19 | # ^^^^ source.sway constant.language.bool.sway 20 | >} 21 | #^ source.sway punctuation.brackets.curly.sway 22 | > -------------------------------------------------------------------------------- /client/src/util/convert.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as lc from 'vscode-languageclient'; 3 | 4 | export function toVSCodePosition(position: lc.Position): vscode.Position { 5 | return new vscode.Position(position.line, position.character); 6 | } 7 | 8 | export function toVSCodeRange(range: lc.Range): vscode.Range { 9 | return new vscode.Range( 10 | toVSCodePosition(range.start), 11 | toVSCodePosition(range.end) 12 | ); 13 | } 14 | 15 | export function toVSCodeUri(uri: lc.DocumentUri): vscode.Uri { 16 | return vscode.Uri.parse(uri); 17 | } 18 | 19 | export function toVSCodeLocation(location: lc.Location): vscode.Location { 20 | return new vscode.Location( 21 | toVSCodeUri(location.uri), 22 | toVSCodeRange(location.range) 23 | ); 24 | } 25 | 26 | export function toLSPPosition(position: vscode.Position): lc.Position { 27 | return lc.Position.create(position.line, position.character); 28 | } 29 | 30 | export function toLSPRange(range: vscode.Range): lc.Range { 31 | return lc.Range.create(toLSPPosition(range.start), toLSPPosition(range.end)); 32 | } 33 | 34 | export function toLSPUri(uri: vscode.Uri): lc.DocumentUri { 35 | return uri.toString(); 36 | } 37 | 38 | export function toLSPLocation(location: vscode.Location): lc.Location { 39 | return lc.Location.create(toLSPUri(location.uri), toLSPRange(location.range)); 40 | } 41 | -------------------------------------------------------------------------------- /client/src/status_bar/fuelCoreStatus.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process'; 2 | import { StatusBarAlignment, StatusBarItem, ThemeColor, window } from 'vscode'; 3 | 4 | let fuelCoreStatus: StatusBarItem; 5 | let isFuelCoreRunning: boolean; 6 | 7 | export const getFuelCoreStatus = () => { 8 | if (!fuelCoreStatus) { 9 | fuelCoreStatus = window.createStatusBarItem(StatusBarAlignment.Left, 100); 10 | } 11 | return fuelCoreStatus; 12 | }; 13 | 14 | export default function updateFuelCoreStatus() { 15 | const initializedItem = getFuelCoreStatus(); 16 | exec(`ps aux | grep -i fuel-cor`, (_error, stdout, _stderr) => { 17 | isFuelCoreRunning = stdout.includes('fuel-core'); 18 | }); 19 | 20 | if (isFuelCoreRunning) { 21 | initializedItem.text = '$(symbol-event) running'; 22 | initializedItem.command = 'sway.stopFuelCore'; 23 | initializedItem.tooltip = 'Stop the locally running fuel-core server'; 24 | // Using any string other than the approved theme colors will reset the background to default. 25 | initializedItem.backgroundColor = new ThemeColor('reset'); 26 | initializedItem.show(); 27 | } else { 28 | initializedItem.text = '$(symbol-event) stopped'; 29 | initializedItem.command = 'sway.startFuelCore'; 30 | initializedItem.tooltip = 'Start fuel-core server at 127.0.0.1:4000'; 31 | initializedItem.backgroundColor = new ThemeColor( 32 | 'statusBarItem.warningBackground' 33 | ); 34 | initializedItem.show(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sway VSCode Plugin 5 | 6 | 7 | [![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/FuelLabs.sway-vscode-plugin)](https://marketplace.visualstudio.com/items?itemName=FuelLabs.sway-vscode-plugin) 8 | [![discord](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 9 | 10 | This extension provides LSP support for the Sway smart contract programming language. 11 | 12 | ## Features 13 | 14 | - goto type definition 15 | - find all references 16 | - types and documentation on hover 17 | - inlay hints for types and parameter names 18 | - semantic syntax highlighting 19 | - symbol renaming 20 | - code actions 21 | - imports insertion 22 | 23 | _Coming Soon_ 24 | 25 | - code completion 26 | - apply suggestions from errors 27 | - workspace symbol search 28 | - ... and many more 29 | 30 | 31 | ## Quick start 32 | 33 | 1. Install the [Fuel toolchain](https://fuellabs.github.io/fuelup/master/installation/index.html). 34 | 1. Ensure `forc-lsp` is installed correctly by entering `forc-lsp --version` into your terminal. 35 | 1. Install the [Sway VSCode plugin](https://marketplace.visualstudio.com/items?itemName=FuelLabs.sway-vscode-plugin). 36 | 37 | ## Configuration 38 | 39 | This extension provides configurations through VSCode's configuration settings. All configurations are under `sway-lsp.*`. 40 | -------------------------------------------------------------------------------- /client/src/interface/onEnter.ts: -------------------------------------------------------------------------------- 1 | import { TextDocumentChangeEvent, window } from 'vscode'; 2 | import { 3 | RequestType, 4 | TextDocumentContentChangeEvent, 5 | TextDocumentEdit, 6 | TextDocumentIdentifier, 7 | WorkspaceEdit, 8 | } from 'vscode-languageclient/node'; 9 | import { getClient } from '../client'; 10 | import { toVSCodeRange } from '../util/convert'; 11 | import { addFilePrefix } from '../util/util'; 12 | 13 | interface OnEnterParams { 14 | textDocument: TextDocumentIdentifier; 15 | contentChanges: TextDocumentContentChangeEvent[]; 16 | } 17 | 18 | const request = new RequestType( 19 | 'sway/on_enter' 20 | ); 21 | 22 | export const onEnter = async (changeEvent: TextDocumentChangeEvent) => { 23 | if ( 24 | changeEvent.document.uri.scheme === 'file' && 25 | changeEvent.document.languageId === 'sway' && 26 | changeEvent.contentChanges.length === 1 && 27 | changeEvent.contentChanges[0].text.includes('\n') 28 | ) { 29 | const client = getClient(); 30 | const params: OnEnterParams = { 31 | textDocument: { 32 | uri: addFilePrefix(changeEvent.document.uri.fsPath), 33 | }, 34 | contentChanges: [...changeEvent.contentChanges], 35 | }; 36 | const response = await client.sendRequest(request, params); 37 | if (!!response) { 38 | window.activeTextEditor.edit(editBuilder => { 39 | response.documentChanges?.forEach((change: TextDocumentEdit) => { 40 | change.edits.forEach(edit => { 41 | editBuilder.replace(toVSCodeRange(edit.range), edit.newText); 42 | }); 43 | }); 44 | }); 45 | } 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /docs/testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | Prior to testing make sure you have [the Fuel toolchain](https://fuellabs.github.io/sway/latest/introduction/installation.html) installed as well. 4 | 5 | On macOS, ensure you have the `code` CLI tool installed by running `code --version`. If not, open the VSCode editor -> cmd + shift + p -> search `>Shell Command install` -> install. Additional information: . 6 | 7 | ## Testing as a real installed extension 8 | 9 | ```sh 10 | git clone git@github.com:FuelLabs/sway-vscode-plugin.git 11 | cd sway-vscode-plugin 12 | npm i 13 | npm run install-extension 14 | ``` 15 | 16 | ## Test your changes on your local install of VS Code 17 | 18 | Firstly, uninstall the official release of the extension and reload VS Code if you have it installed. You will need to do this step each time you want to re-install and test your local version. 19 | 20 | ```sh 21 | cd sway-vscode-plugin 22 | npm i 23 | git checkout 24 | npm run install-extension 25 | ``` 26 | 27 | ## Testing in Debug mode 28 | 29 | - In order to start the Debug mode, open `vscode-plugin` in Visual Studio Code, make sure that it is opened as root/main workspace - in order to avoid any problems. 30 | - Make sure that in `Run and Debug` Tab that "Launch Client" is selected - press F5 and new Visual Studio Code Debug Window will be opened. 31 | - Within that Window open a `.sw` file like `main.sw` - which will activate `forc lsp`. 32 | 33 | ## Testing in Debug mode with the attached Server _(This is only needed if you are developing the Server.)_ 34 | 35 | - Install this extension -> [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) 36 | - Repeat the steps outlined in "Testing in Debug mode", then go back `Run and Debug` Tab, from the dropdown menu choose "Sway Server" which will attach the server in the debug mode as well. 37 | 38 | ## When does the plugin get activated? 39 | 40 | Currently it gets activated once you open a file with `.sw` extension. 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Ignore Typescript files 107 | out 108 | .vscode-test 109 | sway-vscode-plugin.vsix 110 | 111 | # .vsix 112 | *.vsix 113 | -------------------------------------------------------------------------------- /client/src/util/util.ts: -------------------------------------------------------------------------------- 1 | import { inspect } from 'util'; 2 | import * as vscode from 'vscode'; 3 | 4 | export const addFilePrefix = (path: string) => `file://${path}`; 5 | 6 | export const log = new (class { 7 | private enabled = true; 8 | private readonly output = 9 | vscode.window.createOutputChannel('Sway LSP Client'); 10 | 11 | setEnabled(yes: boolean): void { 12 | log.enabled = yes; 13 | } 14 | 15 | // Hint: the type [T, ...T[]] means a non-empty array 16 | debug(...msg: [unknown, ...unknown[]]): void { 17 | if (!log.enabled) return; 18 | log.write('DEBUG', ...msg); 19 | } 20 | 21 | info(...msg: [unknown, ...unknown[]]): void { 22 | log.write('INFO', ...msg); 23 | } 24 | 25 | warn(...msg: [unknown, ...unknown[]]): void { 26 | log.write('WARN', ...msg); 27 | } 28 | 29 | error(...msg: [unknown, ...unknown[]]): void { 30 | log.write('ERROR', ...msg); 31 | log.output.show(true); 32 | } 33 | 34 | private write(label: string, ...messageParts: unknown[]): void { 35 | const message = messageParts.map(log.stringify).join(' '); 36 | const dateTime = new Date().toLocaleString(); 37 | log.output.appendLine(`${label} [${dateTime}]: ${message}`); 38 | } 39 | 40 | private stringify(val: unknown): string { 41 | if (typeof val === 'string') return val; 42 | return inspect(val, { 43 | colors: false, 44 | depth: 6, // heuristic 45 | }); 46 | } 47 | })(); 48 | 49 | // Utilities for interacting with VSCode terminals. 50 | export namespace Terminal { 51 | type Names = 'sway' | 'fuel-core'; 52 | class NamedTerminal { 53 | static type: Names; 54 | static execute(cmd: string): void { 55 | const terminal = this.get(); 56 | terminal.sendText(cmd); 57 | terminal.show(true); 58 | } 59 | 60 | private static get(): vscode.Terminal { 61 | const existing = vscode.window.terminals.find(t => t.name === this.type); 62 | return existing ?? vscode.window.createTerminal(this.type); 63 | } 64 | } 65 | // Used to execute any commands that are not long-running. 66 | export class Sway extends NamedTerminal { 67 | static type: Names = 'sway'; 68 | } 69 | // Only used for starting fuel-core, which is a long-running process. 70 | export class FuelCore extends NamedTerminal { 71 | static type: Names = 'fuel-core'; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /client/test/syntaxes/const_decl.sw.snap: -------------------------------------------------------------------------------- 1 | >script; 2 | #^^^^^^ source.sway source.sway meta.attribute.sway 3 | # ^ source.sway 4 | > 5 | >const GLOBAL_VAL: u64 = 99; 6 | #^^^^^ source.sway storage.type.sway 7 | # ^ source.sway 8 | # ^^^^^^^^^^ source.sway constant.other.caps.sway 9 | # ^ source.sway keyword.operator.key-value.sway 10 | # ^ source.sway 11 | # ^^^ source.sway entity.name.type.numeric.sway 12 | # ^ source.sway 13 | # ^ source.sway keyword.operator.assignment.equal.sway 14 | # ^ source.sway 15 | # ^^ source.sway constant.numeric.decimal.sway 16 | # ^ source.sway punctuation.semi.sway 17 | > 18 | >fn main() -> u64 { 19 | #^^ source.sway meta.function.definition.sway keyword.other.fn.sway 20 | # ^ source.sway meta.function.definition.sway 21 | # ^^^^ source.sway meta.function.definition.sway entity.name.function.sway 22 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 23 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 24 | # ^ source.sway meta.function.definition.sway 25 | # ^^ source.sway meta.function.definition.sway keyword.operator.arrow.skinny.sway 26 | # ^ source.sway meta.function.definition.sway 27 | # ^^^ source.sway meta.function.definition.sway entity.name.type.numeric.sway 28 | # ^ source.sway meta.function.definition.sway 29 | # ^ source.sway meta.function.definition.sway punctuation.brackets.curly.sway 30 | > const LOCAL_VAL = 1; 31 | #^^^^ source.sway 32 | # ^^^^^ source.sway storage.type.sway 33 | # ^ source.sway 34 | # ^^^^^^^^^ source.sway constant.other.caps.sway 35 | # ^ source.sway 36 | # ^ source.sway keyword.operator.assignment.equal.sway 37 | # ^ source.sway 38 | # ^ source.sway constant.numeric.decimal.sway 39 | # ^ source.sway punctuation.semi.sway 40 | > GLOBAL_VAL + LOCAL_VAL 41 | #^^^^ source.sway 42 | # ^^^^^^^^^^ source.sway constant.other.caps.sway 43 | # ^ source.sway 44 | # ^ source.sway keyword.operator.math.sway 45 | # ^ source.sway 46 | # ^^^^^^^^^ source.sway constant.other.caps.sway 47 | >} 48 | #^ source.sway punctuation.brackets.curly.sway 49 | > -------------------------------------------------------------------------------- /images/fuel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/test/syntaxes/storage_declaration.sw.snap: -------------------------------------------------------------------------------- 1 | >contract; 2 | #^^^^^^^^ source.sway source.sway meta.attribute.sway 3 | # ^ source.sway 4 | >storage { 5 | #^^^^^^^ source.sway variable.other.sway 6 | # ^ source.sway 7 | # ^ source.sway punctuation.brackets.curly.sway 8 | > supply: u64 = 0, 9 | #^^^^ source.sway 10 | # ^^^^^^ source.sway variable.other.sway 11 | # ^ source.sway keyword.operator.key-value.sway 12 | # ^ source.sway 13 | # ^^^ source.sway entity.name.type.numeric.sway 14 | # ^ source.sway 15 | # ^ source.sway keyword.operator.assignment.equal.sway 16 | # ^ source.sway 17 | # ^ source.sway constant.numeric.decimal.sway 18 | # ^ source.sway punctuation.comma.sway 19 | > demand: u64 = 0, 20 | #^^^^ source.sway 21 | # ^^^^^^ source.sway variable.other.sway 22 | # ^ source.sway keyword.operator.key-value.sway 23 | # ^ source.sway 24 | # ^^^ source.sway entity.name.type.numeric.sway 25 | # ^ source.sway 26 | # ^ source.sway keyword.operator.assignment.equal.sway 27 | # ^ source.sway 28 | # ^ source.sway constant.numeric.decimal.sway 29 | # ^ source.sway punctuation.comma.sway 30 | > name: str[4] = "test", 31 | #^^^^ source.sway 32 | # ^^^^ source.sway variable.other.sway 33 | # ^ source.sway keyword.operator.key-value.sway 34 | # ^ source.sway 35 | # ^^^ source.sway entity.name.type.primitive.sway 36 | # ^ source.sway punctuation.brackets.square.sway 37 | # ^ source.sway constant.numeric.decimal.sway 38 | # ^ source.sway punctuation.brackets.square.sway 39 | # ^ source.sway 40 | # ^ source.sway keyword.operator.assignment.equal.sway 41 | # ^ source.sway 42 | # ^ source.sway string.quoted.double.sway punctuation.definition.string.sway 43 | # ^^^^ source.sway string.quoted.double.sway 44 | # ^ source.sway string.quoted.double.sway punctuation.definition.string.sway 45 | # ^ source.sway punctuation.comma.sway 46 | >} 47 | #^ source.sway punctuation.brackets.curly.sway 48 | >fn main() { 49 | #^^ source.sway meta.function.definition.sway keyword.other.fn.sway 50 | # ^ source.sway meta.function.definition.sway 51 | # ^^^^ source.sway meta.function.definition.sway entity.name.function.sway 52 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 53 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 54 | # ^ source.sway meta.function.definition.sway 55 | # ^ source.sway meta.function.definition.sway punctuation.brackets.curly.sway 56 | >} 57 | #^ source.sway punctuation.brackets.curly.sway 58 | > -------------------------------------------------------------------------------- /client/src/commands/openDotGraph.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as vscode from 'vscode'; 3 | import { EXTENSION_ROOT, getExtensionPath } from '../config'; 4 | import { GraphKind, visualize } from '../interface/visualize'; 5 | import { addFilePrefix, log } from '../util/util'; 6 | 7 | export default async function openDotGraph( 8 | filePath: string, 9 | graphKind: GraphKind 10 | ) { 11 | try { 12 | const dotContents = await visualize(addFilePrefix(filePath), graphKind); 13 | if (dotContents) { 14 | const nodeModulesPath = vscode.Uri.file( 15 | path.join(getExtensionPath(), 'node_modules') 16 | ); 17 | 18 | const panel = vscode.window.createWebviewPanel( 19 | `${EXTENSION_ROOT}.crate-graph`, 20 | `${EXTENSION_ROOT} build plan graph`, 21 | vscode.ViewColumn.Two, 22 | { 23 | enableScripts: true, 24 | retainContextWhenHidden: true, 25 | localResourceRoots: [nodeModulesPath], 26 | } 27 | ); 28 | const uri = panel.webview.asWebviewUri(nodeModulesPath); 29 | 30 | const html = ` 31 | 32 | 33 | 34 | 46 | 47 | 48 | 49 | 50 | 51 |
52 | 70 | 71 | `; 72 | 73 | panel.webview.html = html; 74 | } else { 75 | log.error(`No ${graphKind} graph found for ${filePath}`); 76 | } 77 | } catch (error) { 78 | log.error(`Failed to open ${graphKind} graph for ${filePath}`, error); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /client/src/config.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { log } from './util/util'; 3 | 4 | export const EXTENSION_ID = 'fuellabs.sway-vscode-plugin'; 5 | export const EXTENSION_ROOT = 'sway-lsp'; 6 | 7 | export const getExtension = () => vscode.extensions.getExtension(EXTENSION_ID); 8 | 9 | export const getExtensionPath = () => getExtension()!.extensionPath; 10 | 11 | export const getExtensionManifest = () => getExtension()!.packageJSON; 12 | 13 | export class Config { 14 | private readonly requiresReloadOpts = ['debug', 'diagnostic'].map( 15 | opt => `${EXTENSION_ROOT}.${opt}` 16 | ); 17 | 18 | readonly package: { 19 | version: string; 20 | } = getExtensionManifest(); 21 | 22 | readonly globalStorageUri: vscode.Uri; 23 | 24 | constructor(ctx: vscode.ExtensionContext) { 25 | this.globalStorageUri = ctx.globalStorageUri; 26 | vscode.workspace.onDidChangeConfiguration( 27 | this.onDidChangeConfiguration, 28 | this, 29 | ctx.subscriptions 30 | ); 31 | this.refreshLogging(); 32 | } 33 | 34 | private refreshLogging() { 35 | log.setEnabled(this.traceExtension); 36 | log.info('Starting the Sway Language Client and Server'); 37 | log.info('Extension version:', this.package.version); 38 | 39 | const cfg = Object.entries(this.cfg).filter( 40 | ([_, val]) => !(val instanceof Function) 41 | ); 42 | log.info('Using configuration', Object.fromEntries(cfg)); 43 | } 44 | 45 | private async onDidChangeConfiguration( 46 | event: vscode.ConfigurationChangeEvent 47 | ) { 48 | this.refreshLogging(); 49 | 50 | const requiresReloadOpt = this.requiresReloadOpts.find(opt => 51 | event.affectsConfiguration(opt) 52 | ); 53 | 54 | if (!requiresReloadOpt) return; 55 | 56 | const userResponse = await vscode.window.showInformationMessage( 57 | `Changing "${requiresReloadOpt}" requires a reload`, 58 | 'Reload now' 59 | ); 60 | 61 | if (userResponse === 'Reload now') { 62 | await vscode.commands.executeCommand('workbench.action.reloadWindow'); 63 | } 64 | } 65 | 66 | // We don't do runtime config validation here for simplicity. More on stackoverflow: 67 | // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension 68 | 69 | private get cfg(): vscode.WorkspaceConfiguration { 70 | return vscode.workspace.getConfiguration(EXTENSION_ROOT); 71 | } 72 | 73 | /** 74 | * Beware that postfix `!` operator erases both `null` and `undefined`. 75 | * This is why the following doesn't work as expected: 76 | * 77 | * ```ts 78 | * const nullableNum = vscode 79 | * .workspace 80 | * .getConfiguration 81 | * .getConfiguration("sway-lsp") 82 | * .get(path)!; 83 | * 84 | * // What happens is that type of `nullableNum` is `number` but not `null | number`: 85 | * const fullFledgedNum: number = nullableNum; 86 | * ``` 87 | * So this getter handles this quirk by not requiring the caller to use postfix `!` 88 | */ 89 | private get(path: string): T { 90 | return this.cfg.get(path)!; 91 | } 92 | 93 | get binPath() { 94 | return this.get('diagnostic.binPath'); 95 | } 96 | 97 | get disableLsp() { 98 | return this.get('diagnostic.disableLsp'); 99 | } 100 | 101 | get traceExtension() { 102 | return this.get('trace.extension'); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /client/src/palettes.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as vscode from 'vscode'; 3 | import forcBuild from './commands/forcBuild'; 4 | import forcRun from './commands/forcRun'; 5 | import forcTest from './commands/forcTest'; 6 | import goToLocation from './commands/goToLocation'; 7 | import installServer from './commands/installServer'; 8 | import openAstFile from './commands/openAstFile'; 9 | import openDotGraph from './commands/openDotGraph'; 10 | import peekLocations from './commands/peekLocations'; 11 | import startFuelCore from './commands/startFuelCore'; 12 | import stopFuelCore from './commands/stopFuelCore'; 13 | import { Config } from './config'; 14 | 15 | interface CommandPalette { 16 | command: string; 17 | callback: (args?: any) => Promise; 18 | } 19 | 20 | export class CommandPalettes { 21 | constructor(readonly config: Config) {} 22 | 23 | get(): CommandPalette[] { 24 | return [ 25 | { 26 | command: 'sway.runScript', 27 | callback: async () => { 28 | const currentTabDirectory = path.dirname( 29 | vscode.window.activeTextEditor.document.fileName 30 | ); 31 | forcRun(currentTabDirectory); 32 | }, 33 | }, 34 | { 35 | command: 'sway.runTests', 36 | callback: async () => { 37 | const currentTabDirectory = path.dirname( 38 | vscode.window.activeTextEditor.document.fileName 39 | ); 40 | forcTest(currentTabDirectory); 41 | }, 42 | }, 43 | { 44 | command: 'sway.forcBuild', 45 | callback: async () => { 46 | const currentTabDirectory = path.dirname( 47 | vscode.window.activeTextEditor.document.fileName 48 | ); 49 | forcBuild(currentTabDirectory); 50 | }, 51 | }, 52 | { 53 | command: 'sway.startFuelCore', 54 | callback: async () => startFuelCore(), 55 | }, 56 | { 57 | command: 'sway.stopFuelCore', 58 | callback: async () => stopFuelCore(), 59 | }, 60 | { 61 | command: 'sway.showLexedAst', 62 | callback: async () => { 63 | const currentFile = vscode.window.activeTextEditor.document.fileName; 64 | await openAstFile(currentFile, 'lexed'); 65 | }, 66 | }, 67 | { 68 | command: 'sway.showParsedAst', 69 | callback: async () => { 70 | const currentFile = vscode.window.activeTextEditor.document.fileName; 71 | await openAstFile(currentFile, 'parsed'); 72 | }, 73 | }, 74 | { 75 | command: 'sway.showTypedAst', 76 | callback: async () => { 77 | const currentFile = vscode.window.activeTextEditor.document.fileName; 78 | await openAstFile(currentFile, 'typed'); 79 | }, 80 | }, 81 | { 82 | command: 'sway.viewBuildPlan', 83 | callback: async () => { 84 | const currentFile = vscode.window.activeTextEditor.document.fileName; 85 | await openDotGraph(currentFile, 'build_plan'); 86 | }, 87 | }, 88 | { 89 | command: 'sway.installServer', 90 | callback: async () => installServer(), 91 | }, 92 | { 93 | command: 'sway.goToLocation', 94 | callback: goToLocation, 95 | }, 96 | { 97 | command: 'sway.peekLocations', 98 | callback: peekLocations, 99 | }, 100 | ]; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## [0.3.5](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.3.5) - 2024-06-25 05:32:50 4 | 5 | ## What's Changed 6 | * chore: send client name with initialization request by @sdankel in https://github.com/FuelLabs/sway-vscode-plugin/pull/187 7 | 8 | 9 | **Full Changelog**: https://github.com/FuelLabs/sway-vscode-plugin/compare/0.3.4...0.3.5 10 | 11 | ### Chores 12 | 13 | - general: 14 | - send client name with initialization request (#187) ([b4b622d](https://github.com/FuelLabs/sway-vscode-plugin/commit/b4b622d0daa0a54edd5e5c46e6c6d91cf5d9b97a)) ([#187](https://github.com/FuelLabs/sway-vscode-plugin/pull/187)) 15 | 16 | ## [0.3.4](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.3.4) - 2024-05-26 23:46:40 17 | 18 | ## What's Changed 19 | * ci: Use fuel service user token by @sdankel in https://github.com/FuelLabs/sway-vscode-plugin/pull/184 20 | * fix: Only send OnEnter for sway files by @sdankel in https://github.com/FuelLabs/sway-vscode-plugin/pull/183 21 | * chore: bump to 0.3.4 by @sdankel in https://github.com/FuelLabs/sway-vscode-plugin/pull/185 22 | 23 | 24 | **Full Changelog**: https://github.com/FuelLabs/sway-vscode-plugin/compare/0.3.3...0.3.4 25 | 26 | ### Bug Fixes 27 | 28 | - general: 29 | - Only send OnEnter for sway files (#183) ([983aee6](https://github.com/FuelLabs/sway-vscode-plugin/commit/983aee6d83ac1a5e4701ff312b8e0b5b8446ec33)) ([#183](https://github.com/FuelLabs/sway-vscode-plugin/pull/183)) 30 | 31 | ### Chores 32 | 33 | - general: 34 | - bump to 0.3.4 (#185) ([6de0cd6](https://github.com/FuelLabs/sway-vscode-plugin/commit/6de0cd6a8df1b103276c8caf1ceffaf860f8c88b)) ([#185](https://github.com/FuelLabs/sway-vscode-plugin/pull/185)) 35 | 36 | ## [0.3.3](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.3.3) - 2024-04-18 23:43:25 37 | 38 | ## [0.3.2](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.3.2) - 2024-04-17 20:04:11 39 | 40 | ## [0.3.1](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.3.1) - 2024-03-01 20:38:57 41 | 42 | ## [0.3.0](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.3.0) - 2024-02-27 23:35:46 43 | 44 | ## [0.2.16](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.16) - 2023-11-28 21:57:20 45 | 46 | ## [0.2.15](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.15) - 2023-08-01 03:58:38 47 | 48 | ## [0.2.14](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.14) - 2023-05-04 20:01:53 49 | 50 | ## [0.2.13](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.13) - 2023-03-22 04:17:52 51 | 52 | ## [0.2.12](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.12) - 2023-03-21 00:02:00 53 | 54 | ## [0.2.11](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.11) - 2023-03-20 23:15:58 55 | 56 | ## [0.2.9](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.9) - 2023-01-25 02:19:45 57 | 58 | ## [0.2.8](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.8) - 2023-01-11 22:57:29 59 | 60 | ## [0.2.7](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.7) - 2022-12-06 07:11:44 61 | 62 | ## [0.2.6](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.6) - 2022-12-06 06:14:38 63 | 64 | ## [0.2.5](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.5) - 2022-10-31 00:31:49 65 | 66 | ## [0.2.4](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.4) - 2022-08-25 23:36:41 67 | 68 | ## [0.2.3](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.3) - 2022-06-16 20:57:27 69 | 70 | ## [0.2.2](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.2) - 2022-04-10 11:26:49 71 | 72 | ## [0.2.1](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.1) - 2022-03-04 02:02:54 73 | 74 | ## [0.2.0](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.2.0) - 2022-02-27 22:46:34 75 | 76 | ## [0.1.0](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.1.0) - 2021-12-25 14:17:08 77 | 78 | ## [0.0.1](https://github.com/FuelLabs/sway-vscode-plugin/releases/tag/0.0.1) - 2021-12-22 13:51:35 79 | 80 | Initial release. 81 | 82 | \* *This CHANGELOG was automatically generated by [auto-generate-changelog](https://github.com/BobAnkh/auto-generate-changelog)* 83 | -------------------------------------------------------------------------------- /client/src/program.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | // import * as fuels from 'typechain-target-fuels'; 4 | import { 5 | Event, 6 | EventEmitter, 7 | ProviderResult, 8 | TreeDataProvider, 9 | TreeItem, 10 | TreeItemCollapsibleState, 11 | window, 12 | } from 'vscode'; 13 | 14 | const ABI_FILE_SUFFIX = '-abi.json'; 15 | 16 | export class ProgramProvider implements TreeDataProvider { 17 | private _onDidChangeTreeData: EventEmitter = 18 | new EventEmitter(); 19 | readonly onDidChangeTreeData: Event = 20 | this._onDidChangeTreeData.event; 21 | 22 | constructor( 23 | private workspaceRoot: string | undefined, 24 | readonly type: ProgramType 25 | ) {} 26 | 27 | refresh(): void { 28 | this._onDidChangeTreeData.fire(); 29 | } 30 | 31 | getTreeItem(element: TreeItem): TreeItem { 32 | return element; 33 | } 34 | 35 | getChildren(contract?: Function | Program): ProviderResult { 36 | if (!this.workspaceRoot) { 37 | window.showInformationMessage('No contract in empty workspace'); 38 | return Promise.resolve([]); 39 | } 40 | 41 | return contract 42 | ? Promise.resolve(contract['children']) 43 | : this.getPrograms(); 44 | } 45 | 46 | /** 47 | * Reads contracts from the ABIs. 48 | * @returns array of contracts 49 | */ 50 | private async getPrograms(): Promise { 51 | const allFiles = getAllFiles(this.workspaceRoot, []); 52 | const swayFilePaths = allFiles.filter(file => file.endsWith('.sw')); 53 | const forcTomlFilePaths = allFiles.filter(file => 54 | file.endsWith('Forc.toml') 55 | ); 56 | const abiFilePaths = allFiles.filter(file => 57 | file.endsWith(ABI_FILE_SUFFIX) 58 | ); 59 | const programs = abiFilePaths.map(filepath => { 60 | const contractName = path 61 | .parse(filepath) 62 | .base.replace(ABI_FILE_SUFFIX, ''); 63 | 64 | // This is assuming that there is only one sway contract in the same directory as a Forc.toml 65 | const forcFilePath = forcTomlFilePaths.find(forcFilePath => 66 | fs.readFileSync(forcFilePath).toString().includes(contractName) 67 | ); 68 | 69 | // Find out the program type of the sway file 70 | const swayFilePath = swayFilePaths.find(swayFilePath => { 71 | return swayFilePath.startsWith(path.parse(forcFilePath).dir); 72 | }); 73 | const swayFileMatchesType = fs 74 | .readFileSync(swayFilePath) 75 | .toString() 76 | .startsWith(this.type); 77 | if (!swayFileMatchesType) return undefined; 78 | 79 | // Attach the source file path to each function node in addition to the contract node 80 | const buffer = fs.readFileSync(filepath); 81 | const abi: Object[] = JSON.parse(buffer.toString()); 82 | const functions = abi 83 | .filter(obj => obj['type'] === 'function') 84 | .map(func => new Function(func['name'], swayFilePath)); 85 | 86 | return new Program(contractName, swayFilePath, functions, this.type); 87 | }); 88 | 89 | // Filter out programs that do not match the given type 90 | return programs.filter(program => !!program); 91 | } 92 | } 93 | 94 | /** 95 | * Recursively finds all ABI files in the workspace 96 | * @param dirPath path to current directory 97 | * @param arrayOfFiles array of files to search 98 | * @returns list of all ABI file paths 99 | */ 100 | const getAllFiles = (dirPath: string, arrayOfFiles: string[]): string[] => { 101 | let files = fs.readdirSync(dirPath); 102 | 103 | arrayOfFiles = arrayOfFiles || []; 104 | 105 | files.forEach(function (file) { 106 | if (fs.statSync(dirPath + '/' + file).isDirectory()) { 107 | arrayOfFiles = getAllFiles(dirPath + '/' + file, arrayOfFiles); 108 | } else { 109 | arrayOfFiles.push(path.join(dirPath, '/', file)); 110 | } 111 | }); 112 | 113 | return arrayOfFiles; 114 | }; 115 | 116 | export type ProgramType = 'contract' | 'script' | 'predicate'; 117 | 118 | export class Program extends TreeItem { 119 | constructor( 120 | public readonly name: string, 121 | public readonly sourceFilePath: string, 122 | public readonly children: Function[], 123 | public readonly type?: ProgramType 124 | ) { 125 | super( 126 | name, 127 | children 128 | ? TreeItemCollapsibleState.Expanded 129 | : TreeItemCollapsibleState.None 130 | ); 131 | 132 | this.label = name; 133 | this.tooltip = name; 134 | } 135 | 136 | contextValue = this.type ?? 'program'; 137 | } 138 | 139 | export class Function extends TreeItem { 140 | constructor( 141 | public readonly label: string, 142 | public readonly sourceFilePath: string 143 | ) { 144 | super(label, TreeItemCollapsibleState.None); 145 | 146 | this.tooltip = this.label; 147 | } 148 | 149 | contextValue = 'function'; 150 | } 151 | -------------------------------------------------------------------------------- /client/test/syntaxes/smo_opcode.sw.snap: -------------------------------------------------------------------------------- 1 | >script; 2 | #^^^^^^ source.sway source.sway meta.attribute.sway 3 | # ^ source.sway 4 | > 5 | >use std::constants::ZERO_B256; 6 | #^^^ source.sway meta.use.sway keyword.other.sway 7 | # ^ source.sway meta.use.sway 8 | # ^^^ source.sway meta.use.sway entity.name.namespace.sway 9 | # ^^ source.sway meta.use.sway keyword.operator.namespace.sway 10 | # ^^^^^^^^^ source.sway meta.use.sway entity.name.namespace.sway 11 | # ^^ source.sway meta.use.sway keyword.operator.namespace.sway 12 | # ^^^^^^^^^ source.sway meta.use.sway 13 | # ^ source.sway meta.use.sway punctuation.semi.sway 14 | > 15 | >fn main() -> bool { 16 | #^^ source.sway meta.function.definition.sway keyword.other.fn.sway 17 | # ^ source.sway meta.function.definition.sway 18 | # ^^^^ source.sway meta.function.definition.sway entity.name.function.sway 19 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 20 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 21 | # ^ source.sway meta.function.definition.sway 22 | # ^^ source.sway meta.function.definition.sway keyword.operator.arrow.skinny.sway 23 | # ^ source.sway meta.function.definition.sway 24 | # ^^^^ source.sway meta.function.definition.sway entity.name.type.primitive.sway 25 | # ^ source.sway meta.function.definition.sway 26 | # ^ source.sway meta.function.definition.sway punctuation.brackets.curly.sway 27 | > asm(recipient: ZERO_B256, msg_len: 0, output: 0, coins: 0) { 28 | #^^^^ source.sway 29 | # ^^^ source.sway meta.asm.definition.sway meta.attribute.asm.sway 30 | # ^ source.sway meta.asm.definition.sway punctuation.brackets.round.sway 31 | # ^^^^^^^^^ source.sway meta.asm.definition.sway variable.other.sway 32 | # ^ source.sway meta.asm.definition.sway keyword.operator.key-value.sway 33 | # ^ source.sway meta.asm.definition.sway 34 | # ^^^^^^^^^ source.sway meta.asm.definition.sway constant.other.caps.sway 35 | # ^ source.sway meta.asm.definition.sway punctuation.comma.sway 36 | # ^ source.sway meta.asm.definition.sway 37 | # ^^^^^^^ source.sway meta.asm.definition.sway variable.other.sway 38 | # ^ source.sway meta.asm.definition.sway keyword.operator.key-value.sway 39 | # ^ source.sway meta.asm.definition.sway 40 | # ^ source.sway meta.asm.definition.sway constant.numeric.decimal.sway 41 | # ^ source.sway meta.asm.definition.sway punctuation.comma.sway 42 | # ^ source.sway meta.asm.definition.sway 43 | # ^^^^^^ source.sway meta.asm.definition.sway variable.other.sway 44 | # ^ source.sway meta.asm.definition.sway keyword.operator.key-value.sway 45 | # ^ source.sway meta.asm.definition.sway 46 | # ^ source.sway meta.asm.definition.sway constant.numeric.decimal.sway 47 | # ^ source.sway meta.asm.definition.sway punctuation.comma.sway 48 | # ^ source.sway meta.asm.definition.sway 49 | # ^^^^^ source.sway meta.asm.definition.sway variable.other.sway 50 | # ^ source.sway meta.asm.definition.sway keyword.operator.key-value.sway 51 | # ^ source.sway meta.asm.definition.sway 52 | # ^ source.sway meta.asm.definition.sway constant.numeric.decimal.sway 53 | # ^ source.sway meta.asm.definition.sway punctuation.brackets.round.sway 54 | # ^ source.sway meta.asm.definition.sway 55 | # ^ source.sway meta.asm.definition.sway punctuation.brackets.curly.sway 56 | > smo recipient msg_len coins output; 57 | #^^^^^^^^ source.sway 58 | # ^^^ source.sway variable.other.sway 59 | # ^ source.sway 60 | # ^^^^^^^^^ source.sway variable.other.sway 61 | # ^ source.sway 62 | # ^^^^^^^ source.sway variable.other.sway 63 | # ^ source.sway 64 | # ^^^^^ source.sway variable.other.sway 65 | # ^ source.sway 66 | # ^^^^^^ source.sway variable.other.sway 67 | # ^ source.sway punctuation.semi.sway 68 | > } 69 | #^^^^ source.sway 70 | # ^ source.sway punctuation.brackets.curly.sway 71 | > true 72 | #^^^^ source.sway 73 | # ^^^^ source.sway constant.language.bool.sway 74 | >} 75 | #^ source.sway punctuation.brackets.curly.sway 76 | > -------------------------------------------------------------------------------- /client/src/main.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process'; 2 | import { lt } from 'semver'; 3 | import { promisify } from 'util'; 4 | import { commands, ExtensionContext, window, workspace } from 'vscode'; 5 | import * as lc from 'vscode-languageclient/node'; 6 | import { createClient, getClient } from './client'; 7 | import { 8 | Config, 9 | EXTENSION_ROOT as EXTENSION_ROOT, 10 | getExtensionManifest, 11 | } from './config'; 12 | import { onEnter } from './interface/onEnter'; 13 | import { CommandPalettes } from './palettes'; 14 | import updateFuelCoreStatus from './status_bar/fuelCoreStatus'; 15 | import { log, Terminal } from './util/util'; 16 | 17 | const LSP_EXECUTABLE_NAME = 'forc-lsp'; 18 | 19 | export async function activate(context: ExtensionContext) { 20 | const config = new Config(context); 21 | 22 | // Register all command palettes 23 | const commandPalettes = new CommandPalettes(config).get(); 24 | context.subscriptions.push( 25 | ...commandPalettes.map(({ command, callback }) => 26 | commands.registerCommand(command, callback) 27 | ) 28 | ); 29 | 30 | // Start a recurring task to keep fuel-core status updated 31 | setInterval(updateFuelCoreStatus, 1000); 32 | 33 | if (config.disableLsp) { 34 | log.info('Sway Language Server is disabled. Exiting...'); 35 | return; 36 | } 37 | 38 | // Listen for did_change events for on_enter capabilities. 39 | workspace.onDidChangeTextDocument( 40 | async changeEvent => await onEnter(changeEvent) 41 | ); 42 | 43 | try { 44 | const client = createClient( 45 | getClientOptions(), 46 | await getServerOptions(config) 47 | ); 48 | 49 | // Start the client. This will also launch the server 50 | await client.start(); 51 | 52 | log.info('Client has connected to the Sway Language Server Successfully!'); 53 | } catch (error) { 54 | log.error(error); 55 | log.error( 56 | 'Unable to start the Sway Language Server. Please check the logs for more information.' 57 | ); 58 | } 59 | } 60 | 61 | export function deactivate(): Thenable | undefined { 62 | const client = getClient(); 63 | if (!client) { 64 | return undefined; 65 | } 66 | return client.stop(); 67 | } 68 | 69 | async function getServerOptions(config: Config): Promise { 70 | // Look for the default executable in FUELUP_HOME if it exists, otherwise look for it in the PATH. 71 | const defaultExecutable = process.env.FUELUP_HOME 72 | ? `${process.env.FUELUP_HOME}/bin/${LSP_EXECUTABLE_NAME}` 73 | : LSP_EXECUTABLE_NAME; 74 | 75 | // Use the settings override path if provided, otherwise use the default executable. 76 | const settingsExecutable = config.binPath; 77 | const executable = settingsExecutable || defaultExecutable; 78 | 79 | // Check if the executable exists. 80 | try { 81 | let versionRes = await promisify(exec)(`${executable} --version`); 82 | let versionStr = versionRes.stdout.trim(); 83 | log.info(`Server executable version: ${versionStr}`); 84 | 85 | let version = versionStr.split(' ')[1]; 86 | let latestForcVersion = getExtensionManifest().latestForcVersion; 87 | log.info(`Latest forc version: ${latestForcVersion}`); 88 | 89 | if (lt(version, latestForcVersion)) { 90 | window 91 | .showInformationMessage( 92 | 'A new version of the Sway Language Server is available.', 93 | 'Update', 94 | 'Later' 95 | ) 96 | .then(async selection => { 97 | if (selection === 'Update') { 98 | Terminal.Sway.execute('fuelup update'); 99 | } 100 | }); 101 | } 102 | } catch (error) { 103 | if (!!settingsExecutable) { 104 | const updateMessage = 105 | 'Update the setting "sway-lsp.diagnostic.binPath" either to a valid path to a forc-lsp executable, or leave it empty to use the executable to which your $PATH resolves.'; 106 | window 107 | .showErrorMessage( 108 | 'The Sway Language Server is not installed at the path defined in your Extension Settings.', 109 | 'Edit Setting', 110 | 'Later' 111 | ) 112 | .then(async selection => { 113 | if (selection === 'Edit Setting') { 114 | window.showInformationMessage(updateMessage); 115 | commands.executeCommand( 116 | 'workbench.action.openWorkspaceSettings', 117 | 'sway-lsp.diagnostic.binPath' 118 | ); 119 | } 120 | }); 121 | throw Error(`Missing executable: ${LSP_EXECUTABLE_NAME}\ 122 | \n\nThe VSCode setting "sway-lsp.diagnostic.binPath" is set to an invalid path: "${settingsExecutable}" 123 | \n${updateMessage}`); 124 | } else { 125 | window 126 | .showErrorMessage( 127 | 'The Sway Language Server is not installed. Would you like to install it?', 128 | 'Yes', 129 | 'No' 130 | ) 131 | .then(async selection => { 132 | if (selection === 'Yes') { 133 | window.showInformationMessage( 134 | 'Follow the instructions in the terminal to install the Sway Language Server, then reload the window.' 135 | ); 136 | commands.executeCommand('sway.installServer'); 137 | } 138 | }); 139 | throw Error(`Missing executable: ${LSP_EXECUTABLE_NAME}\ 140 | \n\nYou may need to install the fuel toolchain or add ${process.env.HOME}/.fuelup/bin your path. Try running:\ 141 | \n\ncurl --proto '=https' --tlsv1.2 -sSf https://install.fuel.network/fuelup-init.sh | sh\ 142 | \n\nOr read about fuelup for more information: https://github.com/FuelLabs/fuelup\n`); 143 | } 144 | } 145 | 146 | // Get the full path to the server executable. 147 | const { stdout: executablePath } = await promisify(exec)( 148 | `which ${executable}` 149 | ); 150 | const command = executablePath.trim(); 151 | log.info(`Using server executable: ${command}`); 152 | 153 | const serverExecutable: lc.Executable = { 154 | command: `RUST_BACKTRACE=FULL ${command}`, 155 | options: { 156 | shell: true, 157 | }, 158 | }; 159 | 160 | return { 161 | run: serverExecutable, 162 | debug: serverExecutable, 163 | transport: lc.TransportKind.stdio, 164 | }; 165 | } 166 | 167 | function getClientOptions(): lc.LanguageClientOptions { 168 | // Options to control the language client 169 | const clientOptions: lc.LanguageClientOptions = { 170 | // Register the server for plain text documents 171 | documentSelector: [ 172 | { scheme: 'file', language: 'sway' }, 173 | { scheme: 'untitled', language: 'sway' }, 174 | ], 175 | synchronize: { 176 | // Notify the server about file changes to *.sw files contained in the workspace 177 | fileEvents: [ 178 | workspace.createFileSystemWatcher('**/.sw'), 179 | workspace.createFileSystemWatcher('**/*.sw'), 180 | ], 181 | }, 182 | initializationOptions: { 183 | ...workspace.getConfiguration(EXTENSION_ROOT), 184 | client: 'vscode', 185 | }, 186 | markdown: { 187 | isTrusted: true, 188 | supportHtml: true, 189 | }, 190 | }; 191 | 192 | return clientOptions; 193 | } 194 | -------------------------------------------------------------------------------- /client/test/syntaxes/match_expressions_mismatched.sw.snap: -------------------------------------------------------------------------------- 1 | >script; 2 | #^^^^^^ source.sway source.sway meta.attribute.sway 3 | # ^ source.sway 4 | > 5 | >struct MyStruct { 6 | #^^^^^^ source.sway keyword.declaration.struct.sway 7 | # ^ source.sway 8 | # ^^^^^^^^ source.sway entity.name.type.struct.sway 9 | # ^ source.sway 10 | # ^ source.sway punctuation.brackets.curly.sway 11 | > a: u64, 12 | #^^^^ source.sway 13 | # ^ source.sway variable.other.sway 14 | # ^ source.sway keyword.operator.key-value.sway 15 | # ^ source.sway 16 | # ^^^ source.sway entity.name.type.numeric.sway 17 | # ^ source.sway punctuation.comma.sway 18 | > b: u64, 19 | #^^^^ source.sway 20 | # ^ source.sway variable.other.sway 21 | # ^ source.sway keyword.operator.key-value.sway 22 | # ^ source.sway 23 | # ^^^ source.sway entity.name.type.numeric.sway 24 | # ^ source.sway punctuation.comma.sway 25 | >} 26 | #^ source.sway punctuation.brackets.curly.sway 27 | > 28 | >enum MyEnum { 29 | #^^^^ source.sway keyword.declaration.enum.sway 30 | # ^ source.sway 31 | # ^^^^^^ source.sway entity.name.type.enum.sway 32 | # ^ source.sway 33 | # ^ source.sway punctuation.brackets.curly.sway 34 | > Variant1: (), 35 | #^^^^ source.sway 36 | # ^^^^^^^^ source.sway entity.name.type.sway 37 | # ^ source.sway keyword.operator.key-value.sway 38 | # ^ source.sway 39 | # ^ source.sway punctuation.brackets.round.sway 40 | # ^ source.sway punctuation.brackets.round.sway 41 | # ^ source.sway punctuation.comma.sway 42 | > Variant2: u64, 43 | #^^^^ source.sway 44 | # ^^^^^^^^ source.sway entity.name.type.sway 45 | # ^ source.sway keyword.operator.key-value.sway 46 | # ^ source.sway 47 | # ^^^ source.sway entity.name.type.numeric.sway 48 | # ^ source.sway punctuation.comma.sway 49 | > Variant3: MyStruct, 50 | #^^^^ source.sway 51 | # ^^^^^^^^ source.sway entity.name.type.sway 52 | # ^ source.sway keyword.operator.key-value.sway 53 | # ^ source.sway 54 | # ^^^^^^^^ source.sway entity.name.type.sway 55 | # ^ source.sway punctuation.comma.sway 56 | >} 57 | #^ source.sway punctuation.brackets.curly.sway 58 | > 59 | >fn main() -> u64 { 60 | #^^ source.sway meta.function.definition.sway keyword.other.fn.sway 61 | # ^ source.sway meta.function.definition.sway 62 | # ^^^^ source.sway meta.function.definition.sway entity.name.function.sway 63 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 64 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 65 | # ^ source.sway meta.function.definition.sway 66 | # ^^ source.sway meta.function.definition.sway keyword.operator.arrow.skinny.sway 67 | # ^ source.sway meta.function.definition.sway 68 | # ^^^ source.sway meta.function.definition.sway entity.name.type.numeric.sway 69 | # ^ source.sway meta.function.definition.sway 70 | # ^ source.sway meta.function.definition.sway punctuation.brackets.curly.sway 71 | > let x = MyEnum::Variant1; 72 | #^^^^ source.sway 73 | # ^^^ source.sway storage.modifier.sway 74 | # ^ source.sway 75 | # ^ source.sway variable.other.sway 76 | # ^ source.sway 77 | # ^ source.sway keyword.operator.assignment.equal.sway 78 | # ^ source.sway 79 | # ^^^^^^ source.sway entity.name.type.sway 80 | # ^^ source.sway keyword.operator.namespace.sway 81 | # ^^^^^^^^ source.sway entity.name.type.sway 82 | # ^ source.sway punctuation.semi.sway 83 | > let y = MyEnum::Variant2(5); 84 | #^^^^ source.sway 85 | # ^^^ source.sway storage.modifier.sway 86 | # ^ source.sway 87 | # ^ source.sway variable.other.sway 88 | # ^ source.sway 89 | # ^ source.sway keyword.operator.assignment.equal.sway 90 | # ^ source.sway 91 | # ^^^^^^ source.sway entity.name.type.sway 92 | # ^^ source.sway keyword.operator.namespace.sway 93 | # ^^^^^^^^ source.sway meta.function.call.sway entity.name.function.sway 94 | # ^ source.sway meta.function.call.sway punctuation.brackets.round.sway 95 | # ^ source.sway meta.function.call.sway constant.numeric.decimal.sway 96 | # ^ source.sway meta.function.call.sway punctuation.brackets.round.sway 97 | # ^ source.sway punctuation.semi.sway 98 | > let z = MyEnum::Variant3(MyStruct { 99 | #^^^^ source.sway 100 | # ^^^ source.sway storage.modifier.sway 101 | # ^ source.sway 102 | # ^ source.sway variable.other.sway 103 | # ^ source.sway 104 | # ^ source.sway keyword.operator.assignment.equal.sway 105 | # ^ source.sway 106 | # ^^^^^^ source.sway entity.name.type.sway 107 | # ^^ source.sway keyword.operator.namespace.sway 108 | # ^^^^^^^^ source.sway meta.function.call.sway entity.name.function.sway 109 | # ^ source.sway meta.function.call.sway punctuation.brackets.round.sway 110 | # ^^^^^^^^ source.sway meta.function.call.sway entity.name.type.sway 111 | # ^ source.sway meta.function.call.sway 112 | # ^ source.sway meta.function.call.sway punctuation.brackets.curly.sway 113 | > a: 0, b: 1 114 | #^^^^^^^^ source.sway meta.function.call.sway 115 | # ^ source.sway meta.function.call.sway variable.other.sway 116 | # ^ source.sway meta.function.call.sway keyword.operator.key-value.sway 117 | # ^ source.sway meta.function.call.sway 118 | # ^ source.sway meta.function.call.sway constant.numeric.decimal.sway 119 | # ^ source.sway meta.function.call.sway punctuation.comma.sway 120 | # ^ source.sway meta.function.call.sway 121 | # ^ source.sway meta.function.call.sway variable.other.sway 122 | # ^ source.sway meta.function.call.sway keyword.operator.key-value.sway 123 | # ^ source.sway meta.function.call.sway 124 | # ^ source.sway meta.function.call.sway constant.numeric.decimal.sway 125 | > }); 126 | #^^^^ source.sway meta.function.call.sway 127 | # ^ source.sway meta.function.call.sway punctuation.brackets.curly.sway 128 | # ^ source.sway meta.function.call.sway punctuation.brackets.round.sway 129 | # ^ source.sway punctuation.semi.sway 130 | > 131 | > match y { 132 | #^^^^ source.sway 133 | # ^^^^^ source.sway keyword.control.sway 134 | # ^ source.sway 135 | # ^ source.sway variable.other.sway 136 | # ^ source.sway 137 | # ^ source.sway punctuation.brackets.curly.sway 138 | > MyEnum::Variant2(y) => y, _ => 10, 139 | #^^^^^^^^ source.sway 140 | # ^^^^^^ source.sway entity.name.type.sway 141 | # ^^ source.sway keyword.operator.namespace.sway 142 | # ^^^^^^^^ source.sway meta.function.call.sway entity.name.function.sway 143 | # ^ source.sway meta.function.call.sway punctuation.brackets.round.sway 144 | # ^ source.sway meta.function.call.sway variable.other.sway 145 | # ^ source.sway meta.function.call.sway punctuation.brackets.round.sway 146 | # ^ source.sway 147 | # ^^ source.sway keyword.operator.arrow.fat.sway 148 | # ^ source.sway 149 | # ^ source.sway variable.other.sway 150 | # ^ source.sway punctuation.comma.sway 151 | # ^ source.sway 152 | # ^ source.sway variable.other.sway 153 | # ^ source.sway 154 | # ^^ source.sway keyword.operator.arrow.fat.sway 155 | # ^ source.sway 156 | # ^^ source.sway constant.numeric.decimal.sway 157 | # ^ source.sway punctuation.comma.sway 158 | # ^^ source.sway 159 | > } 160 | #^^^^ source.sway 161 | # ^ source.sway punctuation.brackets.curly.sway 162 | >} 163 | #^ source.sway punctuation.brackets.curly.sway 164 | > -------------------------------------------------------------------------------- /client/test/syntaxes/chained_if_let.sw.snap: -------------------------------------------------------------------------------- 1 | >script; 2 | #^^^^^^ source.sway source.sway meta.attribute.sway 3 | # ^ source.sway 4 | > 5 | >enum Result { 6 | #^^^^ source.sway keyword.declaration.enum.sway 7 | # ^ source.sway 8 | # ^^^^^^ source.sway entity.name.type.enum.sway 9 | # ^ source.sway punctuation.brackets.angle.sway 10 | # ^ source.sway entity.name.type.sway 11 | # ^ source.sway punctuation.comma.sway 12 | # ^ source.sway 13 | # ^ source.sway entity.name.type.sway 14 | # ^ source.sway punctuation.brackets.angle.sway 15 | # ^ source.sway 16 | # ^ source.sway punctuation.brackets.curly.sway 17 | > Ok: T, 18 | #^^^^ source.sway 19 | # ^^ source.sway entity.name.type.result.sway 20 | # ^ source.sway keyword.operator.key-value.sway 21 | # ^ source.sway 22 | # ^ source.sway entity.name.type.sway 23 | # ^ source.sway punctuation.comma.sway 24 | > Err: E, 25 | #^^^^ source.sway 26 | # ^^^ source.sway entity.name.type.result.sway 27 | # ^ source.sway keyword.operator.key-value.sway 28 | # ^ source.sway 29 | # ^ source.sway entity.name.type.sway 30 | # ^ source.sway punctuation.comma.sway 31 | >} 32 | #^ source.sway punctuation.brackets.curly.sway 33 | > 34 | >// should return 5 35 | #^^^^^^^^^^^^^^^^^^ source.sway comment.line.double-slash.sway 36 | >fn main() -> u64 { 37 | #^^ source.sway meta.function.definition.sway keyword.other.fn.sway 38 | # ^ source.sway meta.function.definition.sway 39 | # ^^^^ source.sway meta.function.definition.sway entity.name.function.sway 40 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 41 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 42 | # ^ source.sway meta.function.definition.sway 43 | # ^^ source.sway meta.function.definition.sway keyword.operator.arrow.skinny.sway 44 | # ^ source.sway meta.function.definition.sway 45 | # ^^^ source.sway meta.function.definition.sway entity.name.type.numeric.sway 46 | # ^ source.sway meta.function.definition.sway 47 | # ^ source.sway meta.function.definition.sway punctuation.brackets.curly.sway 48 | > let result_a = Result::Ok::(5u64); 49 | #^^^^ source.sway 50 | # ^^^ source.sway storage.modifier.sway 51 | # ^ source.sway 52 | # ^^^^^^^^ source.sway variable.other.sway 53 | # ^ source.sway 54 | # ^ source.sway keyword.operator.assignment.equal.sway 55 | # ^ source.sway 56 | # ^^^^^^ source.sway entity.name.type.sway 57 | # ^^ source.sway keyword.operator.namespace.sway 58 | # ^^ source.sway entity.name.type.result.sway 59 | # ^^ source.sway keyword.operator.namespace.sway 60 | # ^ source.sway punctuation.brackets.angle.sway 61 | # ^^^ source.sway entity.name.type.numeric.sway 62 | # ^ source.sway punctuation.comma.sway 63 | # ^ source.sway 64 | # ^^^^ source.sway entity.name.type.primitive.sway 65 | # ^ source.sway punctuation.brackets.angle.sway 66 | # ^ source.sway punctuation.brackets.round.sway 67 | # ^ source.sway constant.numeric.decimal.sway 68 | # ^^^ source.sway constant.numeric.decimal.sway entity.name.type.numeric.sway 69 | # ^ source.sway punctuation.brackets.round.sway 70 | # ^ source.sway punctuation.semi.sway 71 | > let result_b = Result::Err::(false); 72 | #^^^^ source.sway 73 | # ^^^ source.sway storage.modifier.sway 74 | # ^ source.sway 75 | # ^^^^^^^^ source.sway variable.other.sway 76 | # ^ source.sway 77 | # ^ source.sway keyword.operator.assignment.equal.sway 78 | # ^ source.sway 79 | # ^^^^^^ source.sway entity.name.type.sway 80 | # ^^ source.sway keyword.operator.namespace.sway 81 | # ^^^ source.sway entity.name.type.result.sway 82 | # ^^ source.sway keyword.operator.namespace.sway 83 | # ^ source.sway punctuation.brackets.angle.sway 84 | # ^^^ source.sway entity.name.type.numeric.sway 85 | # ^ source.sway punctuation.comma.sway 86 | # ^ source.sway 87 | # ^^^^ source.sway entity.name.type.primitive.sway 88 | # ^ source.sway punctuation.brackets.angle.sway 89 | # ^ source.sway punctuation.brackets.round.sway 90 | # ^^^^^ source.sway constant.language.bool.sway 91 | # ^ source.sway punctuation.brackets.round.sway 92 | # ^ source.sway punctuation.semi.sway 93 | > 94 | > if let Result::Err(a) = result_a { 95 | #^^^^ source.sway 96 | # ^^ source.sway keyword.control.sway 97 | # ^ source.sway 98 | # ^^^ source.sway storage.modifier.sway 99 | # ^ source.sway 100 | # ^^^^^^ source.sway entity.name.type.sway 101 | # ^^ source.sway keyword.operator.namespace.sway 102 | # ^^^ source.sway entity.name.type.result.sway 103 | # ^ source.sway punctuation.brackets.round.sway 104 | # ^ source.sway variable.other.sway 105 | # ^ source.sway punctuation.brackets.round.sway 106 | # ^ source.sway 107 | # ^ source.sway keyword.operator.assignment.equal.sway 108 | # ^ source.sway 109 | # ^^^^^^^^ source.sway variable.other.sway 110 | # ^ source.sway 111 | # ^ source.sway punctuation.brackets.curly.sway 112 | > 6 113 | #^^^^^^^^ source.sway 114 | # ^ source.sway constant.numeric.decimal.sway 115 | > } else if let Result::Ok(num) = result_b { 116 | #^^^^ source.sway 117 | # ^ source.sway punctuation.brackets.curly.sway 118 | # ^ source.sway 119 | # ^^^^ source.sway keyword.control.sway 120 | # ^ source.sway 121 | # ^^ source.sway keyword.control.sway 122 | # ^ source.sway 123 | # ^^^ source.sway storage.modifier.sway 124 | # ^ source.sway 125 | # ^^^^^^ source.sway entity.name.type.sway 126 | # ^^ source.sway keyword.operator.namespace.sway 127 | # ^^ source.sway entity.name.type.result.sway 128 | # ^ source.sway punctuation.brackets.round.sway 129 | # ^^^ source.sway variable.other.sway 130 | # ^ source.sway punctuation.brackets.round.sway 131 | # ^ source.sway 132 | # ^ source.sway keyword.operator.assignment.equal.sway 133 | # ^ source.sway 134 | # ^^^^^^^^ source.sway variable.other.sway 135 | # ^ source.sway 136 | # ^ source.sway punctuation.brackets.curly.sway 137 | > 10 138 | #^^^^^^^^ source.sway 139 | # ^^ source.sway constant.numeric.decimal.sway 140 | > } else if let Result::Ok(num) = result_a { 141 | #^^^^ source.sway 142 | # ^ source.sway punctuation.brackets.curly.sway 143 | # ^ source.sway 144 | # ^^^^ source.sway keyword.control.sway 145 | # ^ source.sway 146 | # ^^ source.sway keyword.control.sway 147 | # ^ source.sway 148 | # ^^^ source.sway storage.modifier.sway 149 | # ^ source.sway 150 | # ^^^^^^ source.sway entity.name.type.sway 151 | # ^^ source.sway keyword.operator.namespace.sway 152 | # ^^ source.sway entity.name.type.result.sway 153 | # ^ source.sway punctuation.brackets.round.sway 154 | # ^^^ source.sway variable.other.sway 155 | # ^ source.sway punctuation.brackets.round.sway 156 | # ^ source.sway 157 | # ^ source.sway keyword.operator.assignment.equal.sway 158 | # ^ source.sway 159 | # ^^^^^^^^ source.sway variable.other.sway 160 | # ^ source.sway 161 | # ^ source.sway punctuation.brackets.curly.sway 162 | > num 163 | #^^^^^^^^ source.sway 164 | # ^^^ source.sway variable.other.sway 165 | > } else { 166 | #^^^^ source.sway 167 | # ^ source.sway punctuation.brackets.curly.sway 168 | # ^ source.sway 169 | # ^^^^ source.sway keyword.control.sway 170 | # ^ source.sway 171 | # ^ source.sway punctuation.brackets.curly.sway 172 | > 42 173 | #^^^^^^^^ source.sway 174 | # ^^ source.sway constant.numeric.decimal.sway 175 | > } 176 | #^^^^ source.sway 177 | # ^ source.sway punctuation.brackets.curly.sway 178 | >} 179 | #^ source.sway punctuation.brackets.curly.sway 180 | > -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sway-vscode-plugin", 3 | "displayName": "Sway", 4 | "description": "Sway language extension for Visual Studio Code", 5 | "icon": "images/logo.png", 6 | "version": "0.3.6", 7 | "latestForcVersion": "0.63.5", 8 | "publisher": "FuelLabs", 9 | "license": "Apache-2.0", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/FuelLabs/sway-vscode-plugin.git" 13 | }, 14 | "scripts": { 15 | "vscode:prepublish": "npm run esbuild-base -- --minify", 16 | "esbuild-base": "esbuild ./client/src/main.ts --bundle --outfile=client/out/main.js --external:vscode --format=cjs --platform=node", 17 | "esbuild": "npm run esbuild-base -- --sourcemap", 18 | "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch=forever &", 19 | "pre:install-extension": "rm sway-vscode-plugin.vsix", 20 | "install-extension": "npm run package && code --install-extension sway-vscode-plugin.vsix --force", 21 | "format:check": "prettier --check 'client/src' 'snippets' 'syntaxes'", 22 | "format:fix": "prettier --write 'client/src' 'snippets' 'syntaxes'", 23 | "fix-syntaxes": "vscode-tmgrammar-snap --updateSnapshot client/test/syntaxes/*sw", 24 | "test-syntaxes": "vscode-tmgrammar-snap client/test/syntaxes/*sw", 25 | "package": "vsce package -o sway-vscode-plugin.vsix" 26 | }, 27 | "engines": { 28 | "vscode": "^1.66.0" 29 | }, 30 | "categories": [ 31 | "Programming Languages", 32 | "Extension Packs", 33 | "Snippets", 34 | "Themes", 35 | "Debuggers" 36 | ], 37 | "main": "./client/out/main", 38 | "activationEvents": [ 39 | "onDebug", 40 | "onLanguage:sway", 41 | "onView:sway", 42 | "onCommand:sway.runScript", 43 | "onCommand:sway.runTests", 44 | "onCommand:sway.forcBuild", 45 | "onCommand:sway.startFuelCore", 46 | "onCommand:sway.stopFuelCore", 47 | "onCommand:sway.showLexedAst", 48 | "onCommand:sway.showParsedAst", 49 | "onCommand:sway.showTypedAst", 50 | "onCommand:sway.viewBuildPlan", 51 | "onCommand:sway.installServer", 52 | "onCommand:sway.goToLocation", 53 | "onCommand:sway.peekLocations" 54 | ], 55 | "contributes": { 56 | "configuration": { 57 | "type": "object", 58 | "title": "sway-lsp", 59 | "properties": { 60 | "sway-lsp.trace.server": { 61 | "scope": "window", 62 | "type": "string", 63 | "description": "Traces the communication between VS Code and the Sway language server.", 64 | "enum": [ 65 | "off", 66 | "messages", 67 | "verbose" 68 | ], 69 | "enumDescriptions": [ 70 | "No traces", 71 | "Error only", 72 | "Full log" 73 | ], 74 | "default": "off" 75 | }, 76 | "sway-lsp.trace.extension": { 77 | "description": "Enable logging of the Sway VS Code extension itself.", 78 | "type": "boolean", 79 | "default": false 80 | }, 81 | "sway-lsp.debug.showCollectedTokensAsWarnings": { 82 | "scope": "window", 83 | "type": "string", 84 | "description": "Show either successfully parsed or typed tokens by the sway-lsp server as warnings. If set to off, sway-lsp will revert to only showing warnings and errors reported by the compiler.", 85 | "enum": [ 86 | "off", 87 | "parsed", 88 | "typed" 89 | ], 90 | "enumDescriptions": [ 91 | "Debugging off", 92 | "Show parsed tokens", 93 | "Show typed tokens" 94 | ], 95 | "default": "off" 96 | }, 97 | "sway-lsp.diagnostic.showWarnings": { 98 | "description": "Show compiler warnings", 99 | "type": "boolean", 100 | "default": true 101 | }, 102 | "sway-lsp.diagnostic.showErrors": { 103 | "description": "Show compiler errors", 104 | "type": "boolean", 105 | "default": true 106 | }, 107 | "sway-lsp.diagnostic.binPath": { 108 | "description": "Optionally override the path to the Sway language server executable. If empty, the extension will use the forc-lsp executable to which your $PATH resolves (recommended for most users).", 109 | "type": "string", 110 | "default": "", 111 | "pattern": "(^/(.+/)*forc-lsp$)|^$", 112 | "patternErrorMessage": "Must be an absolute path to the `forc-lsp` executable, or left empty." 113 | }, 114 | "sway-lsp.diagnostic.disableLsp": { 115 | "description": "Disable the LSP server. This will disable all language features except for basic syntax highlighting.", 116 | "type": "boolean", 117 | "default": false 118 | }, 119 | "sway-lsp.logging.level": { 120 | "scope": "window", 121 | "type": "string", 122 | "description": "Set the log level for LSP server logs.", 123 | "enum": [ 124 | "off", 125 | "error", 126 | "warn", 127 | "info", 128 | "debug", 129 | "trace" 130 | ], 131 | "default": "error" 132 | }, 133 | "sway-lsp.inlayHints.renderColons": { 134 | "markdownDescription": "Whether to render leading colons for type hints, and trailing colons for parameter hints.", 135 | "default": true, 136 | "type": "boolean" 137 | }, 138 | "sway-lsp.inlayHints.typeHints": { 139 | "markdownDescription": "Whether to show inlay type hints for variables.", 140 | "default": true, 141 | "type": "boolean" 142 | }, 143 | "sway-lsp.inlayHints.maxLength": { 144 | "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.", 145 | "default": 25, 146 | "type": [ 147 | "null", 148 | "integer" 149 | ], 150 | "minimum": 0 151 | }, 152 | "sway-lsp.onEnter.continueDocComments": { 153 | "markdownDescription": "Whether to continue /// comments when enter is pressed.", 154 | "default": false, 155 | "type": "boolean" 156 | }, 157 | "sway-lsp.onEnter.continueComments": { 158 | "markdownDescription": "Whether to continue // comments when enter is pressed.", 159 | "default": false, 160 | "type": "boolean" 161 | } 162 | }, 163 | "commands": [ 164 | { 165 | "command": "sway.runScript", 166 | "title": "Sway: Run script" 167 | }, 168 | { 169 | "command": "sway.runTests", 170 | "title": "Sway: Run tests" 171 | }, 172 | { 173 | "command": "sway.forcBuild", 174 | "title": "Sway: Build" 175 | }, 176 | { 177 | "command": "sway.startFuelCore", 178 | "title": "Sway: Start Fuel Core" 179 | }, 180 | { 181 | "command": "sway.stopFuelCore", 182 | "title": "Sway: Stop Fuel Core" 183 | }, 184 | { 185 | "command": "sway.showLexedAst", 186 | "title": "Sway: Show Lexed AST" 187 | }, 188 | { 189 | "command": "sway.showParsedAst", 190 | "title": "Sway: Show Parsed AST" 191 | }, 192 | { 193 | "command": "sway.showTypedAst", 194 | "title": "Sway: Show Typed AST" 195 | }, 196 | { 197 | "command": "sway.viewBuildPlan", 198 | "title": "Sway: View Build Plan" 199 | }, 200 | { 201 | "command": "sway.installServer", 202 | "title": "Sway: Install Server" 203 | }, 204 | { 205 | "command": "sway.goToLocation", 206 | "title": "Sway: Go To Definition" 207 | }, 208 | { 209 | "command": "sway.peekLocations", 210 | "title": "Sway: Peek Locations" 211 | } 212 | ] 213 | }, 214 | "languages": [ 215 | { 216 | "id": "sway", 217 | "aliases": [ 218 | "Sway", 219 | "sway" 220 | ], 221 | "extensions": [ 222 | ".sw" 223 | ], 224 | "configuration": "./language-configuration.json" 225 | } 226 | ], 227 | "commands": [ 228 | { 229 | "command": "programs.refreshEntry", 230 | "title": "Refresh", 231 | "icon": { 232 | "light": "images/light/refresh.svg", 233 | "dark": "images/dark/refresh.svg" 234 | } 235 | }, 236 | { 237 | "command": "programs.editEntry", 238 | "title": "Edit", 239 | "icon": { 240 | "light": "images/light/edit.svg", 241 | "dark": "images/dark/edit.svg" 242 | } 243 | }, 244 | { 245 | "command": "programs.run", 246 | "title": "Run", 247 | "icon": { 248 | "light": "images/light/play.svg", 249 | "dark": "images/dark/play.svg" 250 | } 251 | }, 252 | { 253 | "command": "sway.runScript", 254 | "title": "Sway: Run script" 255 | }, 256 | { 257 | "command": "sway.runTests", 258 | "title": "Sway: Run tests" 259 | }, 260 | { 261 | "command": "sway.forcBuild", 262 | "title": "Sway: Build" 263 | }, 264 | { 265 | "command": "sway.startFuelCore", 266 | "title": "Sway: Start Fuel Core" 267 | }, 268 | { 269 | "command": "sway.stopFuelCore", 270 | "title": "Sway: Stop Fuel Core" 271 | }, 272 | { 273 | "command": "sway.showLexedAst", 274 | "title": "Sway: Show Lexed AST" 275 | }, 276 | { 277 | "command": "sway.showParsedAst", 278 | "title": "Sway: Show Parsed AST" 279 | }, 280 | { 281 | "command": "sway.showTypedAst", 282 | "title": "Sway: Show Typed AST" 283 | }, 284 | { 285 | "command": "sway.viewBuildPlan", 286 | "title": "Sway: View Build Plan" 287 | }, 288 | { 289 | "command": "sway.installServer", 290 | "title": "Sway: Install Server" 291 | }, 292 | { 293 | "command": "sway.goToLocation", 294 | "title": "Sway: Go To Definition", 295 | "args": [ 296 | { 297 | "name": "uri", 298 | "description": "URI of the document to open.", 299 | "schema": { 300 | "type": "string" 301 | } 302 | }, 303 | { 304 | "name": "range", 305 | "description": "Range of selected text in the document.", 306 | "schema": { 307 | "$ref": "#/definitions/Range" 308 | } 309 | } 310 | ] 311 | }, 312 | { 313 | "command": "sway.peekLocations", 314 | "title": "Sway: Peek Locations", 315 | "args": [ 316 | { 317 | "name": "locations", 318 | "description": "Array of locations.", 319 | "schema": { 320 | "type": "array", 321 | "items": [ 322 | { 323 | "$ref": "#/definitions/Location" 324 | } 325 | ] 326 | } 327 | } 328 | ] 329 | } 330 | ], 331 | "snippets": [ 332 | { 333 | "language": "sway", 334 | "path": "./snippets/sway.json" 335 | } 336 | ], 337 | "grammars": [ 338 | { 339 | "language": "sway", 340 | "scopeName": "source.sway", 341 | "path": "./syntaxes/sway.tmLanguage.json" 342 | } 343 | ], 344 | "breakpoints": [ 345 | { 346 | "language": "sway" 347 | } 348 | ], 349 | "debuggers": [ 350 | { 351 | "type": "sway", 352 | "label": "Sway Debugger", 353 | "program": "/usr/bin/env", 354 | "args": [ 355 | "forc-debug", 356 | "--serve" 357 | ], 358 | "languages": [ 359 | "sway" 360 | ], 361 | "configurationAttributes": { 362 | "launch": { 363 | "required": [ 364 | "program" 365 | ], 366 | "properties": { 367 | "program": { 368 | "type": "string", 369 | "description": "Absolute path to a text file.", 370 | "default": "${file}" 371 | } 372 | } 373 | }, 374 | "attach": { 375 | "required": [ 376 | "program" 377 | ], 378 | "properties": { 379 | "program": { 380 | "type": "string", 381 | "description": "Absolute path to a text file.", 382 | "default": "${file}" 383 | } 384 | } 385 | } 386 | } 387 | } 388 | ] 389 | }, 390 | "dependencies": { 391 | "@hpcc-js/wasm": "^2.13.0", 392 | "child_process": "^1.0.2", 393 | "d3": "^7.8.5", 394 | "d3-graphviz": "^5.0.2", 395 | "semver": "^7.6.2", 396 | "util": "^0.12.5", 397 | "vscode-languageclient": "^8.0.0-next.14" 398 | }, 399 | "devDependencies": { 400 | "@types/node": "^12.20.46", 401 | "@types/vscode": "^1.66.0", 402 | "@typescript-eslint/eslint-plugin": "^4.33.0", 403 | "@typescript-eslint/parser": "^4.33.0", 404 | "@vscode/test-electron": "^1.6.1", 405 | "esbuild": "^0.20.1", 406 | "eslint": "^7.32.0", 407 | "prettier": "^2.5.1", 408 | "prettier-plugin-organize-imports": "^3.2.2", 409 | "typescript": "^4.5.5", 410 | "vsce": "^2.5.3", 411 | "vscode-tmgrammar-test": "^0.1.1" 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /client/test/syntaxes/particle.sw.snap: -------------------------------------------------------------------------------- 1 | >script; 2 | #^^^^^^ source.sway source.sway meta.attribute.sway 3 | # ^ source.sway 4 | > 5 | >/// A simple Particle struct 6 | #^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.sway comment.line.documentation.sway 7 | >pub struct Particle { 8 | #^^^ source.sway keyword.other.sway 9 | # ^ source.sway 10 | # ^^^^^^ source.sway keyword.declaration.struct.sway 11 | # ^ source.sway 12 | # ^^^^^^^^ source.sway entity.name.type.struct.sway 13 | # ^ source.sway 14 | # ^ source.sway punctuation.brackets.curly.sway 15 | > position: [u64; 3], 16 | #^^^^ source.sway 17 | # ^^^^^^^^ source.sway variable.other.sway 18 | # ^ source.sway keyword.operator.key-value.sway 19 | # ^ source.sway 20 | # ^ source.sway punctuation.brackets.square.sway 21 | # ^^^ source.sway entity.name.type.numeric.sway 22 | # ^ source.sway punctuation.semi.sway 23 | # ^ source.sway 24 | # ^ source.sway constant.numeric.decimal.sway 25 | # ^ source.sway punctuation.brackets.square.sway 26 | # ^ source.sway punctuation.comma.sway 27 | > velocity: [u64; 3], 28 | #^^^^ source.sway 29 | # ^^^^^^^^ source.sway variable.other.sway 30 | # ^ source.sway keyword.operator.key-value.sway 31 | # ^ source.sway 32 | # ^ source.sway punctuation.brackets.square.sway 33 | # ^^^ source.sway entity.name.type.numeric.sway 34 | # ^ source.sway punctuation.semi.sway 35 | # ^ source.sway 36 | # ^ source.sway constant.numeric.decimal.sway 37 | # ^ source.sway punctuation.brackets.square.sway 38 | # ^ source.sway punctuation.comma.sway 39 | > acceleration: [u64; 3], 40 | #^^^^ source.sway 41 | # ^^^^^^^^^^^^ source.sway variable.other.sway 42 | # ^ source.sway keyword.operator.key-value.sway 43 | # ^ source.sway 44 | # ^ source.sway punctuation.brackets.square.sway 45 | # ^^^ source.sway entity.name.type.numeric.sway 46 | # ^ source.sway punctuation.semi.sway 47 | # ^ source.sway 48 | # ^ source.sway constant.numeric.decimal.sway 49 | # ^ source.sway punctuation.brackets.square.sway 50 | # ^ source.sway punctuation.comma.sway 51 | > mass: u64, 52 | #^^^^ source.sway 53 | # ^^^^ source.sway variable.other.sway 54 | # ^ source.sway keyword.operator.key-value.sway 55 | # ^ source.sway 56 | # ^^^ source.sway entity.name.type.numeric.sway 57 | # ^ source.sway punctuation.comma.sway 58 | >} 59 | #^ source.sway punctuation.brackets.curly.sway 60 | > 61 | >impl Particle { 62 | #^^^^ source.sway keyword.other.sway 63 | # ^ source.sway 64 | # ^^^^^^^^ source.sway entity.name.type.sway 65 | # ^ source.sway 66 | # ^ source.sway punctuation.brackets.curly.sway 67 | > /// Creates a new Particle with the given position, velocity, acceleration, and mass 68 | #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.sway comment.line.documentation.sway 69 | > fn new(position: [u64; 3], velocity: [u64; 3], acceleration: [u64; 3], mass: u64) -> Particle { 70 | #^^^^ source.sway 71 | # ^^ source.sway meta.function.definition.sway keyword.other.fn.sway 72 | # ^ source.sway meta.function.definition.sway 73 | # ^^^ source.sway meta.function.definition.sway entity.name.function.sway 74 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 75 | # ^^^^^^^^ source.sway meta.function.definition.sway variable.other.sway 76 | # ^ source.sway meta.function.definition.sway keyword.operator.key-value.sway 77 | # ^ source.sway meta.function.definition.sway 78 | # ^ source.sway meta.function.definition.sway punctuation.brackets.square.sway 79 | # ^^^ source.sway meta.function.definition.sway entity.name.type.numeric.sway 80 | # ^ source.sway meta.function.definition.sway punctuation.brackets.curly.sway 81 | # ^ source.sway 82 | # ^ source.sway constant.numeric.decimal.sway 83 | # ^ source.sway punctuation.brackets.square.sway 84 | # ^ source.sway punctuation.comma.sway 85 | # ^ source.sway 86 | # ^^^^^^^^ source.sway variable.other.sway 87 | # ^ source.sway keyword.operator.key-value.sway 88 | # ^ source.sway 89 | # ^ source.sway punctuation.brackets.square.sway 90 | # ^^^ source.sway entity.name.type.numeric.sway 91 | # ^ source.sway punctuation.semi.sway 92 | # ^ source.sway 93 | # ^ source.sway constant.numeric.decimal.sway 94 | # ^ source.sway punctuation.brackets.square.sway 95 | # ^ source.sway punctuation.comma.sway 96 | # ^ source.sway 97 | # ^^^^^^^^^^^^ source.sway variable.other.sway 98 | # ^ source.sway keyword.operator.key-value.sway 99 | # ^ source.sway 100 | # ^ source.sway punctuation.brackets.square.sway 101 | # ^^^ source.sway entity.name.type.numeric.sway 102 | # ^ source.sway punctuation.semi.sway 103 | # ^ source.sway 104 | # ^ source.sway constant.numeric.decimal.sway 105 | # ^ source.sway punctuation.brackets.square.sway 106 | # ^ source.sway punctuation.comma.sway 107 | # ^ source.sway 108 | # ^^^^ source.sway variable.other.sway 109 | # ^ source.sway keyword.operator.key-value.sway 110 | # ^ source.sway 111 | # ^^^ source.sway entity.name.type.numeric.sway 112 | # ^ source.sway punctuation.brackets.round.sway 113 | # ^ source.sway 114 | # ^^ source.sway keyword.operator.arrow.skinny.sway 115 | # ^ source.sway 116 | # ^^^^^^^^ source.sway entity.name.type.sway 117 | # ^ source.sway 118 | # ^ source.sway punctuation.brackets.curly.sway 119 | > Particle { 120 | #^^^^^^^^ source.sway 121 | # ^^^^^^^^ source.sway entity.name.type.sway 122 | # ^ source.sway 123 | # ^ source.sway punctuation.brackets.curly.sway 124 | > position: position, 125 | #^^^^^^^^^^^^ source.sway 126 | # ^^^^^^^^ source.sway variable.other.sway 127 | # ^ source.sway keyword.operator.key-value.sway 128 | # ^ source.sway 129 | # ^^^^^^^^ source.sway variable.other.sway 130 | # ^ source.sway punctuation.comma.sway 131 | > velocity: velocity, 132 | #^^^^^^^^^^^^ source.sway 133 | # ^^^^^^^^ source.sway variable.other.sway 134 | # ^ source.sway keyword.operator.key-value.sway 135 | # ^ source.sway 136 | # ^^^^^^^^ source.sway variable.other.sway 137 | # ^ source.sway punctuation.comma.sway 138 | > acceleration: acceleration, 139 | #^^^^^^^^^^^^ source.sway 140 | # ^^^^^^^^^^^^ source.sway variable.other.sway 141 | # ^ source.sway keyword.operator.key-value.sway 142 | # ^ source.sway 143 | # ^^^^^^^^^^^^ source.sway variable.other.sway 144 | # ^ source.sway punctuation.comma.sway 145 | > mass: mass, 146 | #^^^^^^^^^^^^ source.sway 147 | # ^^^^ source.sway variable.other.sway 148 | # ^ source.sway keyword.operator.key-value.sway 149 | # ^ source.sway 150 | # ^^^^ source.sway variable.other.sway 151 | # ^ source.sway punctuation.comma.sway 152 | > } 153 | #^^^^^^^^ source.sway 154 | # ^ source.sway punctuation.brackets.curly.sway 155 | > } 156 | #^^^^ source.sway 157 | # ^ source.sway punctuation.brackets.curly.sway 158 | >} 159 | #^ source.sway punctuation.brackets.curly.sway 160 | > 161 | >fn main() { 162 | #^^ source.sway meta.function.definition.sway keyword.other.fn.sway 163 | # ^ source.sway meta.function.definition.sway 164 | # ^^^^ source.sway meta.function.definition.sway entity.name.function.sway 165 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 166 | # ^ source.sway meta.function.definition.sway punctuation.brackets.round.sway 167 | # ^ source.sway meta.function.definition.sway 168 | # ^ source.sway meta.function.definition.sway punctuation.brackets.curly.sway 169 | > let position = [0, 0, 0]; 170 | #^^^^ source.sway 171 | # ^^^ source.sway storage.modifier.sway 172 | # ^ source.sway 173 | # ^^^^^^^^ source.sway variable.other.sway 174 | # ^ source.sway 175 | # ^ source.sway keyword.operator.assignment.equal.sway 176 | # ^ source.sway 177 | # ^ source.sway punctuation.brackets.square.sway 178 | # ^ source.sway constant.numeric.decimal.sway 179 | # ^ source.sway punctuation.comma.sway 180 | # ^ source.sway 181 | # ^ source.sway constant.numeric.decimal.sway 182 | # ^ source.sway punctuation.comma.sway 183 | # ^ source.sway 184 | # ^ source.sway constant.numeric.decimal.sway 185 | # ^ source.sway punctuation.brackets.square.sway 186 | # ^ source.sway punctuation.semi.sway 187 | > let velocity = [0, 1, 0]; 188 | #^^^^ source.sway 189 | # ^^^ source.sway storage.modifier.sway 190 | # ^ source.sway 191 | # ^^^^^^^^ source.sway variable.other.sway 192 | # ^ source.sway 193 | # ^ source.sway keyword.operator.assignment.equal.sway 194 | # ^ source.sway 195 | # ^ source.sway punctuation.brackets.square.sway 196 | # ^ source.sway constant.numeric.decimal.sway 197 | # ^ source.sway punctuation.comma.sway 198 | # ^ source.sway 199 | # ^ source.sway constant.numeric.decimal.sway 200 | # ^ source.sway punctuation.comma.sway 201 | # ^ source.sway 202 | # ^ source.sway constant.numeric.decimal.sway 203 | # ^ source.sway punctuation.brackets.square.sway 204 | # ^ source.sway punctuation.semi.sway 205 | > let acceleration = [1, 1, position[1]]; 206 | #^^^^ source.sway 207 | # ^^^ source.sway storage.modifier.sway 208 | # ^ source.sway 209 | # ^^^^^^^^^^^^ source.sway variable.other.sway 210 | # ^ source.sway 211 | # ^ source.sway keyword.operator.assignment.equal.sway 212 | # ^ source.sway 213 | # ^ source.sway punctuation.brackets.square.sway 214 | # ^ source.sway constant.numeric.decimal.sway 215 | # ^ source.sway punctuation.comma.sway 216 | # ^ source.sway 217 | # ^ source.sway constant.numeric.decimal.sway 218 | # ^ source.sway punctuation.comma.sway 219 | # ^ source.sway 220 | # ^^^^^^^^ source.sway variable.other.sway 221 | # ^ source.sway punctuation.brackets.square.sway 222 | # ^ source.sway constant.numeric.decimal.sway 223 | # ^ source.sway punctuation.brackets.square.sway 224 | # ^ source.sway punctuation.brackets.square.sway 225 | # ^ source.sway punctuation.semi.sway 226 | > let mass = 10; 227 | #^^^^ source.sway 228 | # ^^^ source.sway storage.modifier.sway 229 | # ^ source.sway 230 | # ^^^^ source.sway variable.other.sway 231 | # ^ source.sway 232 | # ^ source.sway keyword.operator.assignment.equal.sway 233 | # ^ source.sway 234 | # ^^ source.sway constant.numeric.decimal.sway 235 | # ^ source.sway punctuation.semi.sway 236 | > let p = ~Particle::new(position, velocity, acceleration, mass); 237 | #^^^^ source.sway 238 | # ^^^ source.sway storage.modifier.sway 239 | # ^ source.sway 240 | # ^ source.sway variable.other.sway 241 | # ^ source.sway 242 | # ^ source.sway keyword.operator.assignment.equal.sway 243 | # ^^ source.sway 244 | # ^^^^^^^^ source.sway entity.name.type.sway 245 | # ^^ source.sway keyword.operator.namespace.sway 246 | # ^^^ source.sway meta.function.call.sway entity.name.function.sway 247 | # ^ source.sway meta.function.call.sway punctuation.brackets.round.sway 248 | # ^^^^^^^^ source.sway meta.function.call.sway variable.other.sway 249 | # ^ source.sway meta.function.call.sway punctuation.comma.sway 250 | # ^ source.sway meta.function.call.sway 251 | # ^^^^^^^^ source.sway meta.function.call.sway variable.other.sway 252 | # ^ source.sway meta.function.call.sway punctuation.comma.sway 253 | # ^ source.sway meta.function.call.sway 254 | # ^^^^^^^^^^^^ source.sway meta.function.call.sway variable.other.sway 255 | # ^ source.sway meta.function.call.sway punctuation.comma.sway 256 | # ^ source.sway meta.function.call.sway 257 | # ^^^^ source.sway meta.function.call.sway variable.other.sway 258 | # ^ source.sway meta.function.call.sway punctuation.brackets.round.sway 259 | # ^ source.sway punctuation.semi.sway 260 | >} 261 | #^ source.sway punctuation.brackets.curly.sway 262 | > -------------------------------------------------------------------------------- /syntaxes/sway.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sway", 3 | "fileTypes": ["sw"], 4 | "scopeName": "source.sway", 5 | "patterns": [ 6 | { 7 | "comment": "boxed slice literal", 8 | "begin": "(<)(\\[)", 9 | "beginCaptures": { 10 | "1": { 11 | "name": "punctuation.brackets.angle.sway" 12 | }, 13 | "2": { 14 | "name": "punctuation.brackets.square.sway" 15 | } 16 | }, 17 | "end": ">", 18 | "endCaptures": { 19 | "0": { 20 | "name": "punctuation.brackets.angle.sway" 21 | } 22 | }, 23 | "patterns": [ 24 | { 25 | "include": "#block-comments" 26 | }, 27 | { 28 | "include": "#comments" 29 | }, 30 | { 31 | "include": "#gtypes" 32 | }, 33 | { 34 | "include": "#lvariables" 35 | }, 36 | { 37 | "include": "#lifetimes" 38 | }, 39 | { 40 | "include": "#punctuation" 41 | }, 42 | { 43 | "include": "#types" 44 | } 45 | ] 46 | }, 47 | { 48 | "comment": "attributes", 49 | "name": "meta.attribute.sway", 50 | "begin": "(#)(\\!?)(\\[)", 51 | "beginCaptures": { 52 | "1": { 53 | "name": "punctuation.definition.attribute.sway" 54 | }, 55 | "2": { 56 | "name": "keyword.operator.attribute.inner.sway" 57 | }, 58 | "3": { 59 | "name": "punctuation.brackets.attribute.sway" 60 | } 61 | }, 62 | "end": "\\]", 63 | "endCaptures": { 64 | "0": { 65 | "name": "punctuation.brackets.attribute.sway" 66 | } 67 | }, 68 | "patterns": [ 69 | { 70 | "include": "#block-comments" 71 | }, 72 | { 73 | "include": "#comments" 74 | }, 75 | { 76 | "include": "#keywords" 77 | }, 78 | { 79 | "include": "#lifetimes" 80 | }, 81 | { 82 | "include": "#punctuation" 83 | }, 84 | { 85 | "include": "#strings" 86 | }, 87 | { 88 | "include": "#gtypes" 89 | }, 90 | { 91 | "include": "#types" 92 | } 93 | ] 94 | }, 95 | { 96 | "comment": "dependency", 97 | "match": "(dep)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)", 98 | "captures": { 99 | "1": { 100 | "name": "storage.type.sway" 101 | }, 102 | "2": { 103 | "name": "entity.name.dependency.sway" 104 | } 105 | } 106 | }, 107 | { 108 | "comment": "external crate imports", 109 | "name": "meta.import.sway", 110 | "begin": "\\b(extern)\\s+(crate)", 111 | "beginCaptures": { 112 | "1": { 113 | "name": "storage.type.sway" 114 | }, 115 | "2": { 116 | "name": "keyword.other.crate.sway" 117 | } 118 | }, 119 | "end": ";", 120 | "endCaptures": { 121 | "0": { 122 | "name": "punctuation.semi.sway" 123 | } 124 | }, 125 | "patterns": [ 126 | { 127 | "include": "#block-comments" 128 | }, 129 | { 130 | "include": "#comments" 131 | }, 132 | { 133 | "include": "#keywords" 134 | }, 135 | { 136 | "include": "#punctuation" 137 | } 138 | ] 139 | }, 140 | { 141 | "comment": "use statements", 142 | "name": "meta.use.sway", 143 | "begin": "\\b(use)\\s", 144 | "beginCaptures": { 145 | "1": { 146 | "name": "keyword.other.sway" 147 | } 148 | }, 149 | "end": ";", 150 | "endCaptures": { 151 | "0": { 152 | "name": "punctuation.semi.sway" 153 | } 154 | }, 155 | "patterns": [ 156 | { 157 | "include": "#block-comments" 158 | }, 159 | { 160 | "include": "#comments" 161 | }, 162 | { 163 | "include": "#keywords" 164 | }, 165 | { 166 | "include": "#namespaces" 167 | }, 168 | { 169 | "include": "#punctuation" 170 | }, 171 | { 172 | "include": "#types" 173 | }, 174 | { 175 | "include": "#lvariables" 176 | } 177 | ] 178 | }, 179 | { 180 | "include": "#block-comments" 181 | }, 182 | { 183 | "include": "#comments" 184 | }, 185 | { 186 | "include": "#lvariables" 187 | }, 188 | { 189 | "include": "#constants" 190 | }, 191 | { 192 | "include": "#gtypes" 193 | }, 194 | { 195 | "include": "#functions" 196 | }, 197 | { 198 | "include": "#types" 199 | }, 200 | { 201 | "include": "#keywords" 202 | }, 203 | { 204 | "include": "#lifetimes" 205 | }, 206 | { 207 | "include": "#macros" 208 | }, 209 | { 210 | "include": "#namespaces" 211 | }, 212 | { 213 | "include": "#punctuation" 214 | }, 215 | { 216 | "include": "#strings" 217 | }, 218 | { 219 | "include": "#variables" 220 | } 221 | ], 222 | "repository": { 223 | "comments": { 224 | "patterns": [ 225 | { 226 | "comment": "documentation comments", 227 | "name": "comment.line.documentation.sway", 228 | "match": "^\\s*///.*" 229 | }, 230 | { 231 | "comment": "line comments", 232 | "name": "comment.line.double-slash.sway", 233 | "match": "\\s*//.*" 234 | } 235 | ] 236 | }, 237 | "block-comments": { 238 | "patterns": [ 239 | { 240 | "comment": "empty block comments", 241 | "name": "comment.block.sway", 242 | "match": "/\\*\\*/" 243 | }, 244 | { 245 | "comment": "block documentation comments", 246 | "name": "comment.block.documentation.sway", 247 | "begin": "/\\*\\*", 248 | "end": "\\*/", 249 | "patterns": [ 250 | { 251 | "include": "#block-comments" 252 | } 253 | ] 254 | }, 255 | { 256 | "comment": "block comments", 257 | "name": "comment.block.sway", 258 | "begin": "/\\*(?!\\*)", 259 | "end": "\\*/", 260 | "patterns": [ 261 | { 262 | "include": "#block-comments" 263 | } 264 | ] 265 | } 266 | ] 267 | }, 268 | "constants": { 269 | "patterns": [ 270 | { 271 | "comment": "ALL CAPS constants", 272 | "name": "constant.other.caps.sway", 273 | "match": "\\b[A-Z]{2}[A-Z0-9_]*\\b" 274 | }, 275 | { 276 | "comment": "constant declarations", 277 | "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b", 278 | "captures": { 279 | "1": { 280 | "name": "storage.type.sway" 281 | }, 282 | "2": { 283 | "name": "constant.other.caps.sway" 284 | } 285 | } 286 | }, 287 | { 288 | "comment": "decimal integers and floats", 289 | "name": "constant.numeric.decimal.sway", 290 | "match": "\\b\\d[\\d_]*(\\.?)[\\d_]*(?:(E)([+-])([\\d_]+))?(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", 291 | "captures": { 292 | "1": { 293 | "name": "punctuation.separator.dot.decimal.sway" 294 | }, 295 | "2": { 296 | "name": "keyword.operator.exponent.sway" 297 | }, 298 | "3": { 299 | "name": "keyword.operator.exponent.sign.sway" 300 | }, 301 | "4": { 302 | "name": "constant.numeric.decimal.exponent.mantissa.sway" 303 | }, 304 | "5": { 305 | "name": "entity.name.type.numeric.sway" 306 | } 307 | } 308 | }, 309 | { 310 | "comment": "hexadecimal integers", 311 | "name": "constant.numeric.hex.sway", 312 | "match": "\\b0x[\\da-fA-F_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", 313 | "captures": { 314 | "1": { 315 | "name": "entity.name.type.numeric.sway" 316 | } 317 | } 318 | }, 319 | { 320 | "comment": "octal integers", 321 | "name": "constant.numeric.oct.sway", 322 | "match": "\\b0o[0-7_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", 323 | "captures": { 324 | "1": { 325 | "name": "entity.name.type.numeric.sway" 326 | } 327 | } 328 | }, 329 | { 330 | "comment": "binary integers", 331 | "name": "constant.numeric.bin.sway", 332 | "match": "\\b0b[01_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", 333 | "captures": { 334 | "1": { 335 | "name": "entity.name.type.numeric.sway" 336 | } 337 | } 338 | }, 339 | { 340 | "comment": "booleans", 341 | "name": "constant.language.bool.sway", 342 | "match": "\\b(true|false)\\b" 343 | } 344 | ] 345 | }, 346 | "escapes": { 347 | "comment": "escapes: ASCII, byte, Unicode, quote, regex", 348 | "name": "constant.character.escape.sway", 349 | "match": "(\\\\)(?:(?:(x[0-7][0-7a-fA-F])|(u(\\{)[\\da-fA-F]{4,6}(\\}))|.))", 350 | "captures": { 351 | "1": { 352 | "name": "constant.character.escape.backslash.sway" 353 | }, 354 | "2": { 355 | "name": "constant.character.escape.bit.sway" 356 | }, 357 | "3": { 358 | "name": "constant.character.escape.unicode.sway" 359 | }, 360 | "4": { 361 | "name": "constant.character.escape.unicode.punctuation.sway" 362 | }, 363 | "5": { 364 | "name": "constant.character.escape.unicode.punctuation.sway" 365 | } 366 | } 367 | }, 368 | "functions": { 369 | "patterns": [ 370 | { 371 | "comment": "pub as a function", 372 | "match": "\\b(pub)(\\()", 373 | "captures": { 374 | "1": { 375 | "name": "keyword.other.sway" 376 | }, 377 | "2": { 378 | "name": "punctuation.brackets.round.sway" 379 | } 380 | } 381 | }, 382 | { 383 | "comment": "assembly block", 384 | "name": "meta.asm.definition.sway", 385 | "begin": "\\b(asm)((\\())", 386 | "beginCaptures": { 387 | "1": { 388 | "name": "meta.attribute.asm.sway" 389 | }, 390 | "2": { 391 | "name": "punctuation.brackets.round.sway" 392 | } 393 | }, 394 | "end": "\\{|;", 395 | "endCaptures": { 396 | "0": { 397 | "name": "punctuation.brackets.curly.sway" 398 | } 399 | }, 400 | "patterns": [ 401 | { 402 | "include": "#block-comments" 403 | }, 404 | { 405 | "include": "#comments" 406 | }, 407 | { 408 | "include": "#keywords" 409 | }, 410 | { 411 | "include": "#lvariables" 412 | }, 413 | { 414 | "include": "#constants" 415 | }, 416 | { 417 | "include": "#gtypes" 418 | }, 419 | { 420 | "include": "#functions" 421 | }, 422 | { 423 | "include": "#lifetimes" 424 | }, 425 | { 426 | "include": "#macros" 427 | }, 428 | { 429 | "include": "#namespaces" 430 | }, 431 | { 432 | "include": "#punctuation" 433 | }, 434 | { 435 | "include": "#strings" 436 | }, 437 | { 438 | "include": "#types" 439 | }, 440 | { 441 | "include": "#variables" 442 | } 443 | ] 444 | }, 445 | { 446 | "comment": "function definition", 447 | "name": "meta.function.definition.sway", 448 | "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))", 449 | "beginCaptures": { 450 | "1": { 451 | "name": "keyword.other.fn.sway" 452 | }, 453 | "2": { 454 | "name": "entity.name.function.sway" 455 | }, 456 | "4": { 457 | "name": "punctuation.brackets.round.sway" 458 | }, 459 | "5": { 460 | "name": "punctuation.brackets.angle.sway" 461 | } 462 | }, 463 | "end": "\\{|;", 464 | "endCaptures": { 465 | "0": { 466 | "name": "punctuation.brackets.curly.sway" 467 | } 468 | }, 469 | "patterns": [ 470 | { 471 | "include": "#block-comments" 472 | }, 473 | { 474 | "include": "#comments" 475 | }, 476 | { 477 | "include": "#keywords" 478 | }, 479 | { 480 | "include": "#lvariables" 481 | }, 482 | { 483 | "include": "#constants" 484 | }, 485 | { 486 | "include": "#gtypes" 487 | }, 488 | { 489 | "include": "#functions" 490 | }, 491 | { 492 | "include": "#lifetimes" 493 | }, 494 | { 495 | "include": "#macros" 496 | }, 497 | { 498 | "include": "#namespaces" 499 | }, 500 | { 501 | "include": "#punctuation" 502 | }, 503 | { 504 | "include": "#strings" 505 | }, 506 | { 507 | "include": "#types" 508 | }, 509 | { 510 | "include": "#variables" 511 | } 512 | ] 513 | }, 514 | { 515 | "comment": "function/method calls, chaining", 516 | "name": "meta.function.call.sway", 517 | "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(\\()", 518 | "beginCaptures": { 519 | "1": { 520 | "name": "entity.name.function.sway" 521 | }, 522 | "2": { 523 | "name": "punctuation.brackets.round.sway" 524 | } 525 | }, 526 | "end": "\\)", 527 | "endCaptures": { 528 | "0": { 529 | "name": "punctuation.brackets.round.sway" 530 | } 531 | }, 532 | "patterns": [ 533 | { 534 | "include": "#block-comments" 535 | }, 536 | { 537 | "include": "#comments" 538 | }, 539 | { 540 | "include": "#keywords" 541 | }, 542 | { 543 | "include": "#lvariables" 544 | }, 545 | { 546 | "include": "#constants" 547 | }, 548 | { 549 | "include": "#gtypes" 550 | }, 551 | { 552 | "include": "#functions" 553 | }, 554 | { 555 | "include": "#lifetimes" 556 | }, 557 | { 558 | "include": "#macros" 559 | }, 560 | { 561 | "include": "#namespaces" 562 | }, 563 | { 564 | "include": "#punctuation" 565 | }, 566 | { 567 | "include": "#strings" 568 | }, 569 | { 570 | "include": "#types" 571 | }, 572 | { 573 | "include": "#variables" 574 | } 575 | ] 576 | }, 577 | { 578 | "comment": "function/method calls with turbofish", 579 | "name": "meta.function.call.sway", 580 | "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(?=::<.*>\\()", 581 | "beginCaptures": { 582 | "1": { 583 | "name": "entity.name.function.sway" 584 | } 585 | }, 586 | "end": "\\)", 587 | "endCaptures": { 588 | "0": { 589 | "name": "punctuation.brackets.round.sway" 590 | } 591 | }, 592 | "patterns": [ 593 | { 594 | "include": "#block-comments" 595 | }, 596 | { 597 | "include": "#comments" 598 | }, 599 | { 600 | "include": "#keywords" 601 | }, 602 | { 603 | "include": "#lvariables" 604 | }, 605 | { 606 | "include": "#constants" 607 | }, 608 | { 609 | "include": "#gtypes" 610 | }, 611 | { 612 | "include": "#functions" 613 | }, 614 | { 615 | "include": "#lifetimes" 616 | }, 617 | { 618 | "include": "#macros" 619 | }, 620 | { 621 | "include": "#namespaces" 622 | }, 623 | { 624 | "include": "#punctuation" 625 | }, 626 | { 627 | "include": "#strings" 628 | }, 629 | { 630 | "include": "#types" 631 | }, 632 | { 633 | "include": "#variables" 634 | } 635 | ] 636 | } 637 | ] 638 | }, 639 | "keywords": { 640 | "patterns": [ 641 | { 642 | "comment": "control flow keywords", 643 | "name": "keyword.control.sway", 644 | "match": "\\b(await|break|continue|do|else|for|if|loop|match|return|try|while|yield)\\b" 645 | }, 646 | { 647 | "comment": "storage keywords", 648 | "name": "keyword.other.sway storage.type.sway", 649 | "match": "\\b(extern|macro|dep)\\b" 650 | }, 651 | { 652 | "comment": "const keyword", 653 | "name": "storage.modifier.sway", 654 | "match": "\\b(const)\\b" 655 | }, 656 | { 657 | "comment": "let keyword", 658 | "name": "storage.modifier.sway", 659 | "match": "\\b(let)\\b" 660 | }, 661 | { 662 | "comment": "type keyword", 663 | "name": "keyword.declaration.type.sway", 664 | "match": "\\b(type)\\b" 665 | }, 666 | { 667 | "comment": "enum keyword", 668 | "name": "keyword.declaration.enum.sway", 669 | "match": "\\b(enum)\\b" 670 | }, 671 | { 672 | "comment": "trait keyword", 673 | "name": "keyword.declaration.trait.sway", 674 | "match": "\\b(trait)\\b" 675 | }, 676 | { 677 | "comment": "abi keyword", 678 | "name": "keyword.declaration.abi.sway", 679 | "match": "\\b(abi)\\b" 680 | }, 681 | { 682 | "comment": "struct keyword", 683 | "name": "keyword.declaration.struct.sway", 684 | "match": "\\b(struct)\\b" 685 | }, 686 | { 687 | "comment": "storage modifiers", 688 | "name": "storage.modifier.sway", 689 | "match": "\\b(abstract|static)\\b" 690 | }, 691 | { 692 | "comment": "other keywords", 693 | "name": "keyword.other.sway", 694 | "match": "\\b(as|async|become|box|dyn|move|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual|where)\\b" 695 | }, 696 | { 697 | "comment": "fn", 698 | "name": "keyword.other.fn.sway", 699 | "match": "\\bfn\\b" 700 | }, 701 | { 702 | "comment": "asm", 703 | "name": "keyword.other.asm.sway", 704 | "match": "\\basm\\b" 705 | }, 706 | { 707 | "comment": "crate", 708 | "name": "keyword.other.crate.sway", 709 | "match": "\\bcrate\\b" 710 | }, 711 | { 712 | "comment": "mut", 713 | "name": "storage.modifier.mut.sway", 714 | "match": "\\bmut\\b" 715 | }, 716 | { 717 | "comment": "logical operators", 718 | "name": "keyword.operator.logical.sway", 719 | "match": "(\\^|\\||\\|\\||&&|<<|>>|!)(?!=)" 720 | }, 721 | { 722 | "comment": "logical AND, borrow references", 723 | "name": "keyword.operator.borrow.and.sway", 724 | "match": "&(?![&=])" 725 | }, 726 | { 727 | "comment": "assignment operators", 728 | "name": "keyword.operator.assignment.sway", 729 | "match": "(\\+=|-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)" 730 | }, 731 | { 732 | "comment": "single equal", 733 | "name": "keyword.operator.assignment.equal.sway", 734 | "match": "(?])=(?!=|>)" 735 | }, 736 | { 737 | "comment": "comparison operators", 738 | "name": "keyword.operator.comparison.sway", 739 | "match": "(=(=)?(?!>)|!=|<=|(?=)" 740 | }, 741 | { 742 | "comment": "math operators", 743 | "name": "keyword.operator.math.sway", 744 | "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))" 745 | }, 746 | { 747 | "comment": "less than, greater than (special case)", 748 | "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))", 749 | "captures": { 750 | "1": { 751 | "name": "punctuation.brackets.round.sway" 752 | }, 753 | "2": { 754 | "name": "punctuation.brackets.square.sway" 755 | }, 756 | "3": { 757 | "name": "punctuation.brackets.curly.sway" 758 | }, 759 | "4": { 760 | "name": "keyword.operator.comparison.sway" 761 | }, 762 | "5": { 763 | "name": "punctuation.brackets.round.sway" 764 | }, 765 | "6": { 766 | "name": "punctuation.brackets.square.sway" 767 | }, 768 | "7": { 769 | "name": "punctuation.brackets.curly.sway" 770 | } 771 | } 772 | }, 773 | { 774 | "comment": "namespace operator", 775 | "name": "keyword.operator.namespace.sway", 776 | "match": "::" 777 | }, 778 | { 779 | "comment": "dereference asterisk", 780 | "match": "(\\*)(?=\\w+)", 781 | "captures": { 782 | "1": { 783 | "name": "keyword.operator.dereference.sway" 784 | } 785 | } 786 | }, 787 | { 788 | "comment": "subpattern binding", 789 | "name": "keyword.operator.subpattern.sway", 790 | "match": "@" 791 | }, 792 | { 793 | "comment": "dot access", 794 | "name": "keyword.operator.access.dot.sway", 795 | "match": "\\.(?!\\.)" 796 | }, 797 | { 798 | "comment": "ranges, range patterns", 799 | "name": "keyword.operator.range.sway", 800 | "match": "\\.{2}(=|\\.)?" 801 | }, 802 | { 803 | "comment": "colon", 804 | "name": "keyword.operator.key-value.sway", 805 | "match": ":(?!:)" 806 | }, 807 | { 808 | "comment": "dashrocket, skinny arrow", 809 | "name": "keyword.operator.arrow.skinny.sway", 810 | "match": "->" 811 | }, 812 | { 813 | "comment": "hashrocket, fat arrow", 814 | "name": "keyword.operator.arrow.fat.sway", 815 | "match": "=>" 816 | }, 817 | { 818 | "comment": "dollar macros", 819 | "name": "keyword.operator.macro.dollar.sway", 820 | "match": "\\$" 821 | }, 822 | { 823 | "comment": "question mark operator, questionably sized, macro kleene matcher", 824 | "name": "keyword.operator.question.sway", 825 | "match": "\\?" 826 | } 827 | ] 828 | }, 829 | "interpolations": { 830 | "comment": "curly brace interpolations", 831 | "name": "meta.interpolation.sway", 832 | "match": "({)[^\"{}]*(})", 833 | "captures": { 834 | "1": { 835 | "name": "punctuation.definition.interpolation.sway" 836 | }, 837 | "2": { 838 | "name": "punctuation.definition.interpolation.sway" 839 | } 840 | } 841 | }, 842 | "lifetimes": { 843 | "patterns": [ 844 | { 845 | "comment": "named lifetime parameters", 846 | "match": "(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b", 847 | "captures": { 848 | "1": { 849 | "name": "punctuation.definition.lifetime.sway" 850 | }, 851 | "2": { 852 | "name": "entity.name.type.lifetime.sway" 853 | } 854 | } 855 | }, 856 | { 857 | "comment": "borrowing references to named lifetimes", 858 | "match": "(\\&)(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b", 859 | "captures": { 860 | "1": { 861 | "name": "keyword.operator.borrow.sway" 862 | }, 863 | "2": { 864 | "name": "punctuation.definition.lifetime.sway" 865 | }, 866 | "3": { 867 | "name": "entity.name.type.lifetime.sway" 868 | } 869 | } 870 | } 871 | ] 872 | }, 873 | "macros": { 874 | "patterns": [ 875 | { 876 | "comment": "macros", 877 | "name": "meta.macro.sway", 878 | "match": "(([a-z_][A-Za-z0-9_]*!)|([A-Z_][A-Za-z0-9_]*!))", 879 | "captures": { 880 | "2": { 881 | "name": "entity.name.function.macro.sway" 882 | }, 883 | "3": { 884 | "name": "entity.name.type.macro.sway" 885 | } 886 | } 887 | } 888 | ] 889 | }, 890 | "namespaces": { 891 | "patterns": [ 892 | { 893 | "comment": "namespace (non-type, non-function path segment)", 894 | "match": "(?", 929 | "endCaptures": { 930 | "0": { 931 | "name": "punctuation.brackets.angle.sway" 932 | } 933 | }, 934 | "patterns": [ 935 | { 936 | "include": "#block-comments" 937 | }, 938 | { 939 | "include": "#comments" 940 | }, 941 | { 942 | "include": "#keywords" 943 | }, 944 | { 945 | "include": "#lvariables" 946 | }, 947 | { 948 | "include": "#lifetimes" 949 | }, 950 | { 951 | "include": "#punctuation" 952 | }, 953 | { 954 | "include": "#types" 955 | }, 956 | { 957 | "include": "#variables" 958 | } 959 | ] 960 | }, 961 | { 962 | "comment": "primitive types", 963 | "name": "entity.name.type.primitive.sway", 964 | "match": "\\b(bool|char|str)\\b" 965 | }, 966 | { 967 | "comment": "trait declarations", 968 | "match": "\\b(trait)\\s+([A-Z][A-Za-z0-9]*)\\b", 969 | "captures": { 970 | "1": { 971 | "name": "keyword.declaration.trait.sway" 972 | }, 973 | "2": { 974 | "name": "entity.name.type.trait.sway" 975 | } 976 | } 977 | }, 978 | { 979 | "comment": "abi declarations", 980 | "match": "\\b(abi)\\s+([A-Z][A-Za-z0-9]*)\\b", 981 | "captures": { 982 | "1": { 983 | "name": "keyword.declaration.abi.sway" 984 | }, 985 | "2": { 986 | "name": "entity.name.type.abi.sway" 987 | } 988 | } 989 | }, 990 | { 991 | "comment": "struct declarations", 992 | "match": "\\b(struct)\\s+([A-Z][A-Za-z0-9]*)\\b", 993 | "captures": { 994 | "1": { 995 | "name": "keyword.declaration.struct.sway" 996 | }, 997 | "2": { 998 | "name": "entity.name.type.struct.sway" 999 | } 1000 | } 1001 | }, 1002 | { 1003 | "comment": "enum declarations", 1004 | "match": "\\b(enum)\\s+([A-Z][A-Za-z0-9_]*)\\b", 1005 | "captures": { 1006 | "1": { 1007 | "name": "keyword.declaration.enum.sway" 1008 | }, 1009 | "2": { 1010 | "name": "entity.name.type.enum.sway" 1011 | } 1012 | } 1013 | }, 1014 | { 1015 | "comment": "type declarations", 1016 | "match": "\\b(type)\\s+([A-Z][A-Za-z0-9_]*)\\b", 1017 | "captures": { 1018 | "1": { 1019 | "name": "keyword.declaration.type.sway" 1020 | }, 1021 | "2": { 1022 | "name": "entity.name.type.declaration.sway" 1023 | } 1024 | } 1025 | }, 1026 | { 1027 | "comment": "types", 1028 | "name": "entity.name.type.sway", 1029 | "match": "\\b[A-Z][A-Za-z0-9]*\\b(?!!)" 1030 | }, 1031 | { 1032 | "comment": "top level declaration", 1033 | "begin": "\\b(library)\\s+([a-zA-Z_][a-zA-Z0-9_]*)", 1034 | "end": "[\\{\\(;]", 1035 | "beginCaptures": { 1036 | "1": { 1037 | "name": "source.sway meta.attribute.sway" 1038 | }, 1039 | "2": { 1040 | "name": "entity.name.type.sway" 1041 | } 1042 | }, 1043 | "patterns": [ 1044 | { 1045 | "include": "#block-comments" 1046 | }, 1047 | { 1048 | "include": "#comments" 1049 | }, 1050 | { 1051 | "include": "#keywords" 1052 | }, 1053 | { 1054 | "include": "#namespaces" 1055 | }, 1056 | { 1057 | "include": "#punctuation" 1058 | }, 1059 | { 1060 | "include": "#types" 1061 | }, 1062 | { 1063 | "include": "#lvariables" 1064 | } 1065 | ] 1066 | }, 1067 | { 1068 | "comment": "top level declaration without name", 1069 | "match": "(contract|script|predicate);", 1070 | "captures": { 1071 | "1": { 1072 | "name": "source.sway meta.attribute.sway" 1073 | } 1074 | } 1075 | } 1076 | ] 1077 | }, 1078 | "gtypes": { 1079 | "patterns": [ 1080 | { 1081 | "comment": "option types", 1082 | "name": "entity.name.type.option.sway", 1083 | "match": "\\b(Some|None)\\b" 1084 | }, 1085 | { 1086 | "comment": "result types", 1087 | "name": "entity.name.type.result.sway", 1088 | "match": "\\b(Ok|Err)\\b" 1089 | } 1090 | ] 1091 | }, 1092 | "punctuation": { 1093 | "patterns": [ 1094 | { 1095 | "comment": "comma", 1096 | "name": "punctuation.comma.sway", 1097 | "match": "," 1098 | }, 1099 | { 1100 | "comment": "curly braces", 1101 | "name": "punctuation.brackets.curly.sway", 1102 | "match": "[{}]" 1103 | }, 1104 | { 1105 | "comment": "parentheses, round brackets", 1106 | "name": "punctuation.brackets.round.sway", 1107 | "match": "[()]" 1108 | }, 1109 | { 1110 | "comment": "semicolon", 1111 | "name": "punctuation.semi.sway", 1112 | "match": ";" 1113 | }, 1114 | { 1115 | "comment": "square brackets", 1116 | "name": "punctuation.brackets.square.sway", 1117 | "match": "[\\[\\]]" 1118 | }, 1119 | { 1120 | "comment": "angle brackets", 1121 | "name": "punctuation.brackets.angle.sway", 1122 | "match": "(?]" 1123 | } 1124 | ] 1125 | }, 1126 | "strings": { 1127 | "patterns": [ 1128 | { 1129 | "comment": "double-quoted strings and byte strings", 1130 | "name": "string.quoted.double.sway", 1131 | "begin": "(b?)(\")", 1132 | "beginCaptures": { 1133 | "1": { 1134 | "name": "string.quoted.byte.raw.sway" 1135 | }, 1136 | "2": { 1137 | "name": "punctuation.definition.string.sway" 1138 | } 1139 | }, 1140 | "end": "\"", 1141 | "endCaptures": { 1142 | "0": { 1143 | "name": "punctuation.definition.string.sway" 1144 | } 1145 | }, 1146 | "patterns": [ 1147 | { 1148 | "include": "#escapes" 1149 | }, 1150 | { 1151 | "include": "#interpolations" 1152 | } 1153 | ] 1154 | }, 1155 | { 1156 | "comment": "double-quoted raw strings and raw byte strings", 1157 | "name": "string.quoted.double.sway", 1158 | "begin": "(b?r)(#*)(\")", 1159 | "beginCaptures": { 1160 | "1": { 1161 | "name": "string.quoted.byte.raw.sway" 1162 | }, 1163 | "2": { 1164 | "name": "punctuation.definition.string.raw.sway" 1165 | }, 1166 | "3": { 1167 | "name": "punctuation.definition.string.sway" 1168 | } 1169 | }, 1170 | "end": "(\")(\\2)", 1171 | "endCaptures": { 1172 | "1": { 1173 | "name": "punctuation.definition.string.sway" 1174 | }, 1175 | "2": { 1176 | "name": "punctuation.definition.string.raw.sway" 1177 | } 1178 | } 1179 | }, 1180 | { 1181 | "comment": "characters and bytes", 1182 | "name": "string.quoted.single.char.sway", 1183 | "begin": "(b)?(')", 1184 | "beginCaptures": { 1185 | "1": { 1186 | "name": "string.quoted.byte.raw.sway" 1187 | }, 1188 | "2": { 1189 | "name": "punctuation.definition.char.sway" 1190 | } 1191 | }, 1192 | "end": "'", 1193 | "endCaptures": { 1194 | "0": { 1195 | "name": "punctuation.definition.char.sway" 1196 | } 1197 | }, 1198 | "patterns": [ 1199 | { 1200 | "include": "#escapes" 1201 | } 1202 | ] 1203 | } 1204 | ] 1205 | }, 1206 | "lvariables": { 1207 | "patterns": [ 1208 | { 1209 | "comment": "self", 1210 | "name": "variable.language.self.sway", 1211 | "match": "\\b[Ss]elf\\b" 1212 | }, 1213 | { 1214 | "comment": "super", 1215 | "name": "variable.language.super.sway", 1216 | "match": "\\bsuper\\b" 1217 | } 1218 | ] 1219 | }, 1220 | "variables": { 1221 | "patterns": [ 1222 | { 1223 | "comment": "variables", 1224 | "name": "variable.other.sway", 1225 | "match": "\\b(?