├── src ├── span.rs ├── lib.rs ├── symbol_table.rs ├── semantic_token.rs ├── completion.rs ├── semantic_analyze.rs ├── nrs_lang.rs └── main.rs ├── rust-toolchain.toml ├── .cargo └── config.toml ├── .gitignore ├── justfile ├── examples ├── basic.nrs └── index.rs ├── .vscodeignore ├── client ├── tsconfig.json ├── package.json ├── src │ └── extension.ts └── pnpm-lock.yaml ├── tsconfig.json ├── .vscode ├── tasks.json ├── settings.json └── launch.json ├── .github └── workflows │ └── rust.yml ├── Cargo.toml ├── LICENSE ├── esbuild.js ├── package.json ├── README.md └── Cargo.lock /src/span.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | pub type Span = Range; 4 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.88.0" 3 | profile = "default" 4 | 5 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | lint = "clippy --workspace --all-targets -- --deny warnings" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | node_modules 3 | out/ 4 | .pnpm-debug.log 5 | *.ast 6 | dist/ 7 | *.vsix -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | build: 2 | build 3 | 4 | lint: 5 | cargo clippy --workspace --all-targets -- --deny warnings 6 | -------------------------------------------------------------------------------- /examples/basic.nrs: -------------------------------------------------------------------------------- 1 | 2 | // The main function 3 | fn main() { 4 | let a = 100; 5 | let b = 100; 6 | let c = a + b; 7 | c 8 | } 9 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod completion; 2 | pub mod nrs_lang; 3 | pub mod semantic_analyze; 4 | pub mod semantic_token; 5 | pub mod span; 6 | pub mod symbol_table; 7 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | out/ 4 | src/ 5 | tsconfig.json 6 | webpack.config.js 7 | client/** 8 | target 9 | Cargo.lock 10 | .github 11 | .cargo 12 | examples/ 13 | justfile 14 | rust-toolchains.toml 15 | 16 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2019", 5 | "lib": ["ES2019"], 6 | "outDir": "../dist", 7 | "rootDir": "src", 8 | "sourceMap": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules", ".vscode-test"] 12 | } 13 | -------------------------------------------------------------------------------- /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": [ 11 | "src" 12 | ], 13 | "exclude": [ 14 | "node_modules", 15 | ".vscode-test" 16 | ], 17 | "references": [ 18 | { "path": "./client" }, 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "compile", 9 | "group": "build", 10 | "presentation": { 11 | "panel": "dedicated", 12 | "reveal": "never" 13 | }, 14 | "problemMatcher": [ 15 | "$tsc" 16 | ] 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nrs-language-client", 3 | "description": "VSCode part of a language server", 4 | "license": "MIT", 5 | "version": "0.0.1", 6 | "engines": { 7 | "vscode": "^1.85.0" 8 | }, 9 | "dependencies": { 10 | "vscode-languageclient": "9.0.1" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "^17.0.18", 14 | "vscode-test": "^1.3.0", 15 | "@types/vscode": "1.85.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run lint 22 | run: cargo lint 23 | 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nrs-language-server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | chumsky = "0.9.3" 10 | env_logger = "0.11.5" 11 | ropey = "1.5.0" 12 | serde_json = "1.0.78" 13 | tokio = { version = "1.17.0", features = ["full"] } 14 | tower-lsp = { version = "0.20.0", features = ["proposed"]} 15 | serde = { version = "1.0", features = ["derive"] } 16 | dashmap = "6.1.0" 17 | log = "0.4.14" 18 | im-rc = "15.0.0" 19 | oxc_index = "0.36.0" 20 | anyhow = "1.0.93" 21 | thiserror = "2.0.3" 22 | rust-lapper = "1.1.0" 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/index.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Ok; 2 | use nrs_language_server::{ 3 | nrs_lang::{parse, ParserResult}, 4 | semantic_analyze, 5 | }; 6 | 7 | fn main() -> anyhow::Result<()> { 8 | let source = include_str!("./basic.nrs"); 9 | // let source = r#" 10 | // test 11 | // println!("{:?}", &source[10..11]); 12 | let ParserResult { 13 | ast, 14 | parse_errors, 15 | semantic_tokens: _, 16 | } = parse(source); 17 | println!("{parse_errors:?}"); 18 | let ast = if let Some(ref ast) = ast { 19 | println!("{ast:#?}"); 20 | ast 21 | } else { 22 | println!("{parse_errors:?}"); 23 | return Ok(()); 24 | }; 25 | let table = semantic_analyze::analyze_program(ast)?; 26 | dbg!(&table); 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 IWANABETHATGUY 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /esbuild.js: -------------------------------------------------------------------------------- 1 | const esbuild = require('esbuild'); 2 | 3 | const production = process.argv.includes('--production'); 4 | const watch = process.argv.includes('--watch'); 5 | 6 | async function main() { 7 | const ctx = await esbuild.context({ 8 | entryPoints: ['client/src/extension.ts'], 9 | bundle: true, 10 | format: 'cjs', 11 | minify: production, 12 | sourcemap: !production, 13 | sourcesContent: false, 14 | platform: 'node', 15 | outfile: 'dist/extension.js', 16 | external: ['vscode'], 17 | logLevel: 'silent', 18 | plugins: [ 19 | /* add to the end of plugins array */ 20 | esbuildProblemMatcherPlugin 21 | ] 22 | }); 23 | if (watch) { 24 | await ctx.watch(); 25 | } else { 26 | await ctx.rebuild(); 27 | await ctx.dispose(); 28 | } 29 | } 30 | 31 | /** 32 | * @type {import('esbuild').Plugin} 33 | */ 34 | const esbuildProblemMatcherPlugin = { 35 | name: 'esbuild-problem-matcher', 36 | 37 | setup(build) { 38 | build.onStart(() => { 39 | console.log('[watch] build started'); 40 | }); 41 | build.onEnd(result => { 42 | result.errors.forEach(({ text, location }) => { 43 | console.error(`✘ [ERROR] ${text}`); 44 | console.error(` ${location.file}:${location.line}:${location.column}:`); 45 | }); 46 | console.log('[watch] build finished'); 47 | }); 48 | } 49 | }; 50 | 51 | main().catch(e => { 52 | console.error(e); 53 | process.exit(1); 54 | }); 55 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "name": "Launch Client", 9 | "runtimeExecutable": "${execPath}", 10 | "args": [ 11 | "--extensionDevelopmentPath=${workspaceRoot}" 12 | ], 13 | "outFiles": [ 14 | "${workspaceRoot}/client/out/**/*.js" 15 | ], 16 | "env": { 17 | "SERVER_PATH": "${workspaceRoot}/target/debug/nrs-language-server" 18 | } 19 | }, 20 | // { 21 | // "type": "node", 22 | // "request": "attach", 23 | // "name": "Attach to Server", 24 | // "port": 6009, 25 | // "restart": true, 26 | // "outFiles": ["${workspaceRoot}/server/out/**/*.js"] 27 | // }, 28 | { 29 | "name": "Language Server E2E Test", 30 | "type": "extensionHost", 31 | "request": "launch", 32 | "runtimeExecutable": "${execPath}", 33 | "args": [ 34 | "--extensionDevelopmentPath=${workspaceRoot}", 35 | "--extensionTestsPath=${workspaceRoot}/client/out/test/index", 36 | "${workspaceRoot}/client/testFixture" 37 | ], 38 | "outFiles": [ 39 | "${workspaceRoot}/client/out/test/**/*.js" 40 | ] 41 | } 42 | ], 43 | "compounds": [ 44 | { 45 | "name": "Client + Server", 46 | "configurations": [ 47 | "Launch Client", 48 | // "Attach to Server" 49 | ] 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /src/symbol_table.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use oxc_index::IndexVec; 4 | 5 | use crate::span::Span; 6 | 7 | oxc_index::define_index_type! { 8 | pub struct SymbolId = u32; 9 | IMPL_RAW_CONVERSIONS = true; 10 | } 11 | 12 | oxc_index::define_index_type! { 13 | pub struct ReferenceId = u32; 14 | IMPL_RAW_CONVERSIONS = true; 15 | } 16 | pub type SymbolIdToSpan = IndexVec; 17 | 18 | pub type ReferenceIdToReference = IndexVec; 19 | 20 | #[derive(Default, Debug)] 21 | pub struct SymbolTable { 22 | pub span_to_symbol_id: HashMap, 23 | pub symbol_id_to_span: SymbolIdToSpan, 24 | pub reference_id_to_reference: ReferenceIdToReference, 25 | pub span_to_reference_id: HashMap, 26 | pub symbol_id_to_references: HashMap>, 27 | } 28 | 29 | #[derive(Debug)] 30 | pub struct Reference { 31 | pub span: Span, 32 | pub symbol_id: Option, 33 | } 34 | 35 | impl SymbolTable { 36 | pub fn add_symbol(&mut self, span: Span) -> SymbolId { 37 | let symbol_id = self.symbol_id_to_span.push(span.clone()); 38 | self.span_to_symbol_id.insert(span.clone(), symbol_id); 39 | symbol_id 40 | } 41 | 42 | pub fn add_reference(&mut self, span: Span, symbol_id: Option) { 43 | let reference_id = self.reference_id_to_reference.push(Reference { 44 | span: span.clone(), 45 | symbol_id, 46 | }); 47 | self.span_to_reference_id.insert(span, reference_id); 48 | if let Some(symbol_id) = symbol_id { 49 | self.symbol_id_to_references 50 | .entry(symbol_id) 51 | .or_default() 52 | .push(reference_id); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nrs-language-server", 3 | "description": "nano rust language server", 4 | "license": "MIT", 5 | "version": "1.5.0", 6 | "categories": [], 7 | "keywords": [ 8 | "language-server", 9 | "tower-lsp" 10 | ], 11 | "repository": { 12 | "url": "https://github.com/IWANABETHATGUY/tower-lsp-boilerplate" 13 | }, 14 | "engines": { 15 | "vscode": "^1.66.0" 16 | }, 17 | "enabledApiProposals": [], 18 | "activationEvents": [ 19 | "onLanguage:nrs" 20 | ], 21 | "main": "./dist/extension.js", 22 | "contributes": { 23 | "languages": [ 24 | { 25 | "id": "nrs", 26 | "extensions": [ 27 | ".nrs" 28 | ] 29 | } 30 | ], 31 | "configuration": { 32 | "type": "object", 33 | "title": "nrs-language-server", 34 | "properties": { 35 | "nrs-language-server.trace.server": { 36 | "type": "string", 37 | "scope": "window", 38 | "enum": [ 39 | "off", 40 | "messages", 41 | "verbose" 42 | ], 43 | "enumDescriptions": [ 44 | "No traces", 45 | "Error only", 46 | "Full log" 47 | ], 48 | "default": "off", 49 | "description": "Traces the communication between VS Code and the language server." 50 | } 51 | } 52 | } 53 | }, 54 | "scripts": { 55 | "postinstall": "cd client && pnpm i", 56 | "compile": "npm run check-types && node esbuild.js --production", 57 | "check-types": "tsc --noEmit", 58 | "watch": "npm-run-all -p watch:*", 59 | "watch:esbuild": "node esbuild.js --watch", 60 | "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", 61 | "package": "vsce package --no-dependencies" 62 | }, 63 | "devDependencies": { 64 | "@types/glob": "8.1.0", 65 | "@types/mocha": "10.0.10", 66 | "@types/node": "22.10.2", 67 | "@typescript-eslint/eslint-plugin": "^3.8.0", 68 | "@typescript-eslint/parser": "^3.8.0", 69 | "@vscode/vsce": "^3.2.1", 70 | "cross-env": "^7.0.2", 71 | "esbuild": "^0.24.2", 72 | "eslint": "9.17.0", 73 | "glob": "11.0.0", 74 | "mocha": "11.0.2", 75 | "npm-run-all": "^4.1.5", 76 | "ts-loader": "^9.5.1", 77 | "typescript": "5.4.5", 78 | "vscode-test": "^1.4.0", 79 | "vscode-uri": "^3.0.2" 80 | }, 81 | "dependencies": { 82 | }, 83 | "packageManager": "pnpm@8.15.7" 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # boilerplate for a rust language server powered by `tower-lsp` 2 | 3 | > [!note] 4 | > This repo use a language which is a very small subset of `rust` called `nano rust` 5 | 6 | ## A valid program in nano rust 7 | ```rust 8 | fn factorial(x) { 9 | // Conditionals are supported! 10 | if x == 0 { 11 | 1 12 | } else { 13 | x * factorial(x - 1) 14 | } 15 | } 16 | 17 | // The main function 18 | fn main() { 19 | let three = 3; 20 | let meaning_of_life = three * 14 + 1; 21 | 22 | print("Hello, world!"); 23 | print("The meaning of life is..."); 24 | 25 | if meaning_of_life == 42 { 26 | print(meaning_of_life); 27 | } else { 28 | print("...something we cannot know"); 29 | 30 | print("However, I can tell you that the factorial of 10 is..."); 31 | // Function calling 32 | print(factorial(10)); 33 | } 34 | } 35 | ``` 36 | ## Introduction 37 | This repo is a template for `tower-lsp`, a useful github project template which makes writing new language servers easier. 38 | ## Development using VSCode 39 | 1. `pnpm i` 40 | 2. `cargo build` 41 | 3. Open the project in VSCode: `code .` 42 | 4. In VSCode, press F5 or change to the Debug panel and click Launch Client. 43 | 5. In the newly launched VSCode instance, open the file `examples/test.nrs` from this project. 44 | 6. If the LSP is working correctly you should see syntax highlighting and the features described below should work. 45 | 46 | > [!note] 47 | > If encountered errors like `Cannot find module '/xxx/xxx/dist/extension.js'` 48 | > please try run command `tsc -b` manually, you could refer https://github.com/IWANABETHATGUY/tower-lsp-boilerplate/issues/6 for more details 49 | 50 | ### Preview and test extension locally with `VsCode` 51 | 1. Make sure all dependency are installed. 52 | 2. Make sure the `nrs-language-server` is under your `PATH` 53 | 3. `pnpm run package` 54 | 4. `code --install-extension nrs-language-server-${version}.vsix`, the `version` you could inspect in file system. 55 | 5. Restart the `VsCode`, and write a minimal `nano rust` file, then inspect the effect. 56 | 57 | For other editor, please refer the related manual, you could skip steps above. 58 | 59 | 60 | ## Features 61 | This repo use a language `nano rust` which first introduced by [ chumsky ](https://github.com/zesterer/chumsky/blob/master/examples/nano_rust.rs). Most common language feature has been implemented, you could preview via the video below. 62 | 63 | - [x] InlayHint for LiteralType 64 | ![inlay hint](https://user-images.githubusercontent.com/17974631/156926412-c3823dac-664e-430e-96c1-c003a86eabb2.gif) 65 | 66 | - [x] semantic token 67 | make sure your semantic token is enabled, you could enable your `semantic token` by 68 | adding this line to your `settings.json` 69 | ```json 70 | { 71 | "editor.semanticHighlighting.enabled": true, 72 | } 73 | ``` 74 | - [x] syntactic error diagnostic 75 | 76 | https://user-images.githubusercontent.com/17974631/156926382-a1c4c911-7ea1-4d3a-8e08-3cf7271da170.mp4 77 | 78 | - [x] code completion 79 | 80 | https://user-images.githubusercontent.com/17974631/156926355-010ef2cd-1d04-435b-bd1e-8b0dab9f44f1.mp4 81 | 82 | - [x] go to definition 83 | 84 | https://user-images.githubusercontent.com/17974631/156926103-94d90bd3-f31c-44e7-a2ce-4ddfde89bc33.mp4 85 | 86 | - [x] find reference 87 | 88 | https://user-images.githubusercontent.com/17974631/157367235-7091a36c-631a-4347-9c1e-a3b78db81714.mp4 89 | 90 | - [x] rename 91 | 92 | https://user-images.githubusercontent.com/17974631/157367229-99903896-5583-4f67-a6da-1ae1cf206876.mp4 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/semantic_token.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use tower_lsp::lsp_types::SemanticTokenType; 4 | 5 | use crate::nrs_lang::{Expr, Func, ImCompleteSemanticToken, Spanned}; 6 | 7 | pub const LEGEND_TYPE: &[SemanticTokenType] = &[ 8 | SemanticTokenType::FUNCTION, 9 | SemanticTokenType::VARIABLE, 10 | SemanticTokenType::STRING, 11 | SemanticTokenType::COMMENT, 12 | SemanticTokenType::NUMBER, 13 | SemanticTokenType::KEYWORD, 14 | SemanticTokenType::OPERATOR, 15 | SemanticTokenType::PARAMETER, 16 | ]; 17 | 18 | pub fn semantic_token_from_ast(ast: &HashMap) -> Vec { 19 | let mut semantic_tokens = vec![]; 20 | 21 | ast.iter().for_each(|(_func_name, function)| { 22 | function.args.iter().for_each(|(_, span)| { 23 | semantic_tokens.push(ImCompleteSemanticToken { 24 | start: span.start, 25 | length: span.len(), 26 | token_type: LEGEND_TYPE 27 | .iter() 28 | .position(|item| item == &SemanticTokenType::PARAMETER) 29 | .unwrap(), 30 | }); 31 | }); 32 | let (_, span) = &function.name; 33 | semantic_tokens.push(ImCompleteSemanticToken { 34 | start: span.start, 35 | length: span.len(), 36 | token_type: LEGEND_TYPE 37 | .iter() 38 | .position(|item| item == &SemanticTokenType::FUNCTION) 39 | .unwrap(), 40 | }); 41 | semantic_token_from_expr(&function.body, &mut semantic_tokens); 42 | }); 43 | 44 | semantic_tokens 45 | } 46 | 47 | pub fn semantic_token_from_expr( 48 | expr: &Spanned, 49 | semantic_tokens: &mut Vec, 50 | ) { 51 | match &expr.0 { 52 | Expr::Error => {} 53 | Expr::Value(_) => {} 54 | Expr::List(_) => {} 55 | Expr::Local((_name, span)) => { 56 | semantic_tokens.push(ImCompleteSemanticToken { 57 | start: span.start, 58 | length: span.len(), 59 | token_type: LEGEND_TYPE 60 | .iter() 61 | .position(|item| item == &SemanticTokenType::VARIABLE) 62 | .unwrap(), 63 | }); 64 | } 65 | Expr::Let(_, rhs, rest, name_span) => { 66 | semantic_tokens.push(ImCompleteSemanticToken { 67 | start: name_span.start, 68 | length: name_span.len(), 69 | token_type: LEGEND_TYPE 70 | .iter() 71 | .position(|item| item == &SemanticTokenType::VARIABLE) 72 | .unwrap(), 73 | }); 74 | semantic_token_from_expr(rhs, semantic_tokens); 75 | semantic_token_from_expr(rest, semantic_tokens); 76 | } 77 | Expr::Then(first, rest) => { 78 | semantic_token_from_expr(first, semantic_tokens); 79 | semantic_token_from_expr(rest, semantic_tokens); 80 | } 81 | Expr::Binary(lhs, _op, rhs) => { 82 | semantic_token_from_expr(lhs, semantic_tokens); 83 | semantic_token_from_expr(rhs, semantic_tokens); 84 | } 85 | Expr::Call(expr, params) => { 86 | semantic_token_from_expr(expr, semantic_tokens); 87 | params.0.iter().for_each(|p| { 88 | semantic_token_from_expr(p, semantic_tokens); 89 | }); 90 | } 91 | Expr::If(test, consequent, alternative) => { 92 | semantic_token_from_expr(test, semantic_tokens); 93 | semantic_token_from_expr(consequent, semantic_tokens); 94 | semantic_token_from_expr(alternative, semantic_tokens); 95 | } 96 | Expr::Print(expr) => { 97 | semantic_token_from_expr(expr, semantic_tokens); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/completion.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::nrs_lang::{Expr, Func, Spanned}; 4 | pub enum ImCompleteCompletionItem { 5 | Variable(String), 6 | Function(String, Vec), 7 | } 8 | /// return (need_to_continue_search, founded reference) 9 | pub fn completion( 10 | ast: &[Spanned], 11 | ident_offset: usize, 12 | ) -> HashMap { 13 | let mut map = HashMap::new(); 14 | for (func, _) in ast.iter() { 15 | if func.name.1.end < ident_offset { 16 | map.insert( 17 | func.name.0.clone(), 18 | ImCompleteCompletionItem::Function( 19 | func.name.0.clone(), 20 | func.args 21 | .clone() 22 | .into_iter() 23 | .map(|(name, _)| name) 24 | .collect(), 25 | ), 26 | ); 27 | } 28 | } 29 | 30 | // collect params variable 31 | for (func, _) in ast.iter() { 32 | if func.span.end > ident_offset && func.span.start < ident_offset { 33 | // log::debug!("this is completion from body {}", name); 34 | func.args.iter().for_each(|(item, _)| { 35 | map.insert( 36 | item.clone(), 37 | ImCompleteCompletionItem::Variable(item.clone()), 38 | ); 39 | }); 40 | get_completion_of(&func.body, &mut map, ident_offset); 41 | } 42 | } 43 | map 44 | } 45 | 46 | pub fn get_completion_of( 47 | expr: &Spanned, 48 | definition_map: &mut HashMap, 49 | ident_offset: usize, 50 | ) -> bool { 51 | match &expr.0 { 52 | Expr::Error => true, 53 | Expr::Value(_) => true, 54 | Expr::Local(local) => !(ident_offset >= local.1.start && ident_offset < local.1.end), 55 | Expr::Let(name, lhs, rest, _name_span) => { 56 | definition_map.insert( 57 | name.clone(), 58 | ImCompleteCompletionItem::Variable(name.clone()), 59 | ); 60 | match get_completion_of(lhs, definition_map, ident_offset) { 61 | true => get_completion_of(rest, definition_map, ident_offset), 62 | false => false, 63 | } 64 | } 65 | Expr::Then(first, second) => match get_completion_of(first, definition_map, ident_offset) { 66 | true => get_completion_of(second, definition_map, ident_offset), 67 | false => false, 68 | }, 69 | Expr::Binary(lhs, _op, rhs) => match get_completion_of(lhs, definition_map, ident_offset) { 70 | true => get_completion_of(rhs, definition_map, ident_offset), 71 | false => false, 72 | }, 73 | Expr::Call(callee, args) => { 74 | match get_completion_of(callee, definition_map, ident_offset) { 75 | true => {} 76 | false => return false, 77 | } 78 | for expr in &args.0 { 79 | match get_completion_of(expr, definition_map, ident_offset) { 80 | true => continue, 81 | false => return false, 82 | } 83 | } 84 | true 85 | } 86 | Expr::If(test, consequent, alternative) => { 87 | match get_completion_of(test, definition_map, ident_offset) { 88 | true => {} 89 | false => return false, 90 | } 91 | match get_completion_of(consequent, definition_map, ident_offset) { 92 | true => {} 93 | false => return false, 94 | } 95 | get_completion_of(alternative, definition_map, ident_offset) 96 | } 97 | Expr::Print(expr) => get_completion_of(expr, definition_map, ident_offset), 98 | Expr::List(lst) => { 99 | for expr in lst { 100 | match get_completion_of(expr, definition_map, ident_offset) { 101 | true => continue, 102 | false => return false, 103 | } 104 | } 105 | true 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /client/src/extension.ts: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | * ------------------------------------------------------------------------------------------ */ 5 | 6 | import { 7 | languages, 8 | workspace, 9 | EventEmitter, 10 | ExtensionContext, 11 | window, 12 | InlayHintsProvider, 13 | TextDocument, 14 | CancellationToken, 15 | Range, 16 | InlayHint, 17 | TextDocumentChangeEvent, 18 | ProviderResult, 19 | commands, 20 | WorkspaceEdit, 21 | TextEdit, 22 | Selection, 23 | Uri, 24 | } from "vscode"; 25 | 26 | import { 27 | Disposable, 28 | Executable, 29 | LanguageClient, 30 | LanguageClientOptions, 31 | ServerOptions, 32 | } from "vscode-languageclient/node"; 33 | 34 | let client: LanguageClient; 35 | // type a = Parameters<>; 36 | 37 | export async function activate(context: ExtensionContext) { 38 | 39 | const traceOutputChannel = window.createOutputChannel("Nrs Language Server trace"); 40 | const command = process.env.SERVER_PATH || "nrs-language-server"; 41 | const run: Executable = { 42 | command, 43 | options: { 44 | env: { 45 | ...process.env, 46 | // eslint-disable-next-line @typescript-eslint/naming-convention 47 | RUST_LOG: "debug", 48 | }, 49 | }, 50 | }; 51 | const serverOptions: ServerOptions = { 52 | run, 53 | debug: run, 54 | }; 55 | // If the extension is launched in debug mode then the debug server options are used 56 | // Otherwise the run options are used 57 | // Options to control the language client 58 | let clientOptions: LanguageClientOptions = { 59 | // Register the server for plain text documents 60 | documentSelector: [{ scheme: "file", language: "nrs" }], 61 | synchronize: { 62 | // Notify the server about file changes to '.clientrc files contained in the workspace 63 | fileEvents: workspace.createFileSystemWatcher("**/.clientrc"), 64 | }, 65 | traceOutputChannel, 66 | }; 67 | 68 | // Create the language client and start the client. 69 | client = new LanguageClient("nrs-language-server", "nrs language server", serverOptions, clientOptions); 70 | // activateInlayHints(context); 71 | client.start(); 72 | } 73 | 74 | export function deactivate(): Thenable | undefined { 75 | if (!client) { 76 | return undefined; 77 | } 78 | return client.stop(); 79 | } 80 | 81 | export function activateInlayHints(ctx: ExtensionContext) { 82 | const maybeUpdater = { 83 | hintsProvider: null as Disposable | null, 84 | updateHintsEventEmitter: new EventEmitter(), 85 | 86 | async onConfigChange() { 87 | this.dispose(); 88 | 89 | const event = this.updateHintsEventEmitter.event; 90 | // this.hintsProvider = languages.registerInlayHintsProvider( 91 | // { scheme: "file", language: "nrs" }, 92 | // // new (class implements InlayHintsProvider { 93 | // // onDidChangeInlayHints = event; 94 | // // resolveInlayHint(hint: InlayHint, token: CancellationToken): ProviderResult { 95 | // // const ret = { 96 | // // label: hint.label, 97 | // // ...hint, 98 | // // }; 99 | // // return ret; 100 | // // } 101 | // // async provideInlayHints( 102 | // // document: TextDocument, 103 | // // range: Range, 104 | // // token: CancellationToken 105 | // // ): Promise { 106 | // // const hints = (await client 107 | // // .sendRequest("custom/inlay_hint", { path: document.uri.toString() }) 108 | // // .catch(err => null)) as [number, number, string][]; 109 | // // if (hints == null) { 110 | // // return []; 111 | // // } else { 112 | // // return hints.map(item => { 113 | // // const [start, end, label] = item; 114 | // // let startPosition = document.positionAt(start); 115 | // // let endPosition = document.positionAt(end); 116 | // // return { 117 | // // position: endPosition, 118 | // // paddingLeft: true, 119 | // // label: [ 120 | // // { 121 | // // value: `${label}`, 122 | // // // location: { 123 | // // // uri: document.uri, 124 | // // // range: new Range(1, 0, 1, 0) 125 | // // // } 126 | // // command: { 127 | // // title: "hello world", 128 | // // command: "helloworld.helloWorld", 129 | // // arguments: [document.uri], 130 | // // }, 131 | // // }, 132 | // // ], 133 | // // }; 134 | // // }); 135 | // // } 136 | // // } 137 | // // })() 138 | // ); 139 | }, 140 | 141 | onDidChangeTextDocument({ contentChanges, document }: TextDocumentChangeEvent) { 142 | // debugger 143 | // this.updateHintsEventEmitter.fire(); 144 | }, 145 | 146 | dispose() { 147 | this.hintsProvider?.dispose(); 148 | this.hintsProvider = null; 149 | this.updateHintsEventEmitter.dispose(); 150 | }, 151 | }; 152 | 153 | workspace.onDidChangeConfiguration(maybeUpdater.onConfigChange, maybeUpdater, ctx.subscriptions); 154 | workspace.onDidChangeTextDocument(maybeUpdater.onDidChangeTextDocument, maybeUpdater, ctx.subscriptions); 155 | 156 | maybeUpdater.onConfigChange().catch(console.error); 157 | } 158 | -------------------------------------------------------------------------------- /src/semantic_analyze.rs: -------------------------------------------------------------------------------- 1 | use rust_lapper::{Interval, Lapper}; 2 | use std::fmt::Display; 3 | 4 | use crate::{ 5 | nrs_lang::{Ast, Expr, Func}, 6 | span::Span, 7 | symbol_table::{ReferenceId, SymbolId, SymbolTable}, 8 | }; 9 | use thiserror::Error; 10 | 11 | pub type Result = std::result::Result; 12 | 13 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 14 | pub enum IdentType { 15 | Binding(SymbolId), 16 | Reference(ReferenceId), 17 | } 18 | type IdentRangeLapper = Lapper; 19 | 20 | #[derive(Debug, Clone, PartialEq)] 21 | pub enum Type { 22 | Number, 23 | String, 24 | Bool, 25 | Null, 26 | Function, 27 | List(Box), 28 | Unknown, 29 | } 30 | 31 | impl Display for Type { 32 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 33 | match self { 34 | Type::Number => write!(f, "number"), 35 | Type::String => write!(f, "string"), 36 | Type::Bool => write!(f, "bool"), 37 | Type::Null => write!(f, "null"), 38 | Type::Function => write!(f, "function"), 39 | Type::List(ty) => write!(f, "list<{ty}>"), 40 | Type::Unknown => write!(f, "unknown"), 41 | } 42 | } 43 | } 44 | 45 | #[derive(Error, Debug)] 46 | pub enum SemanticError { 47 | #[error("Undefined variable {name}")] 48 | UndefinedVariable { name: String, span: Span }, 49 | #[error("Expect element type: {expect_ty}, but got {actual_ty}")] 50 | ImConsistentArrayType { 51 | expect_ty: String, 52 | actual_ty: String, 53 | span: Span, 54 | }, 55 | } 56 | 57 | impl SemanticError { 58 | pub fn span(&self) -> Span { 59 | match self { 60 | SemanticError::UndefinedVariable { span, .. } => span.clone(), 61 | SemanticError::ImConsistentArrayType { span, .. } => span.clone(), 62 | } 63 | } 64 | } 65 | 66 | #[derive(Debug)] 67 | pub struct Semantic { 68 | pub table: SymbolTable, 69 | pub ident_range: IdentRangeLapper, 70 | } 71 | 72 | impl Semantic {} 73 | 74 | pub struct Function { 75 | pub name: String, 76 | pub params: Vec, 77 | } 78 | 79 | #[derive(Debug)] 80 | pub struct Ctx { 81 | env: im_rc::Vector<(String, Span)>, 82 | table: SymbolTable, 83 | } 84 | 85 | impl Ctx { 86 | fn find_symbol(&self, name: &str) -> Option { 87 | self.env 88 | .iter() 89 | .rev() 90 | .find_map(|(n, t)| if n == name { Some(t.clone()) } else { None }) 91 | } 92 | } 93 | 94 | pub fn analyze_program(ast: &Ast) -> Result { 95 | let table = SymbolTable::default(); 96 | let env = im_rc::Vector::new(); 97 | let mut ctx = Ctx { env, table }; 98 | for (func, _) in ast.iter() { 99 | let name = func.name.0.clone(); 100 | ctx.env.push_back((name, func.name.1.clone())); 101 | ctx.table.add_symbol(func.name.1.clone()); 102 | analyze_function(func, &mut ctx)?; 103 | } 104 | let mut ident_range = IdentRangeLapper::new(vec![]); 105 | for (symbol_id, range) in ctx.table.symbol_id_to_span.iter_enumerated() { 106 | ident_range.insert(Interval { 107 | start: range.start, 108 | stop: range.end, 109 | val: IdentType::Binding(symbol_id), 110 | }); 111 | } 112 | for (reference_id, reference) in ctx.table.reference_id_to_reference.iter_enumerated() { 113 | let range = &reference.span; 114 | ident_range.insert(Interval { 115 | start: range.start, 116 | stop: range.end, 117 | val: IdentType::Reference(reference_id), 118 | }); 119 | } 120 | Ok(Semantic { 121 | table: ctx.table, 122 | ident_range, 123 | }) 124 | } 125 | 126 | fn analyze_function(func: &Func, ctx: &mut Ctx) -> Result<()> { 127 | analyze_expr(&func.body.0, ctx) 128 | } 129 | 130 | fn analyze_expr(expr: &Expr, ctx: &mut Ctx) -> Result<()> { 131 | match expr { 132 | Expr::Error => {} 133 | Expr::Value(_) => {} 134 | Expr::List(list) => { 135 | for item in list.iter() { 136 | analyze_expr(&item.0, ctx)?; 137 | } 138 | } 139 | Expr::Local(name) => { 140 | let span = match ctx.find_symbol(&name.0) { 141 | Some(ty) => ty, 142 | None => { 143 | dbg!(&ctx); 144 | return Err(SemanticError::UndefinedVariable { 145 | name: name.0.clone(), 146 | span: name.1.clone(), 147 | }); 148 | } 149 | }; 150 | let symbol_id = *ctx.table.span_to_symbol_id.get(&span).unwrap(); 151 | ctx.table.add_reference(name.1.clone(), Some(symbol_id)); 152 | } 153 | Expr::Let(name, rhs, then, name_range) => { 154 | analyze_expr(&rhs.0, ctx)?; 155 | ctx.table.add_symbol(name_range.clone()); 156 | ctx.env.push_back((name.clone(), name_range.clone())); 157 | analyze_expr(&then.0, ctx)?; 158 | ctx.env.pop_back(); 159 | } 160 | Expr::Then(first, second) => { 161 | analyze_expr(&first.0, ctx)?; 162 | analyze_expr(&second.0, ctx)? 163 | } 164 | Expr::Binary(lhs, _, rhs) => { 165 | analyze_expr(&lhs.0, ctx)?; 166 | analyze_expr(&rhs.0, ctx)?; 167 | } 168 | Expr::Call(callee, args) => { 169 | analyze_expr(&callee.0, ctx)?; 170 | for arg in args.0.iter() { 171 | analyze_expr(&arg.0, ctx)?; 172 | } 173 | } 174 | Expr::If(cond, consequent, alternative) => { 175 | analyze_expr(&cond.0, ctx)?; 176 | analyze_expr(&consequent.0, ctx)?; 177 | analyze_expr(&alternative.0, ctx)?; 178 | } 179 | Expr::Print(_) => {} 180 | }; 181 | Ok(()) 182 | } 183 | -------------------------------------------------------------------------------- /client/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | vscode-languageclient: 9 | specifier: 9.0.1 10 | version: 9.0.1 11 | 12 | devDependencies: 13 | '@types/node': 14 | specifier: ^17.0.18 15 | version: 17.0.21 16 | '@types/vscode': 17 | specifier: 1.85.0 18 | version: 1.85.0 19 | vscode-test: 20 | specifier: ^1.3.0 21 | version: 1.6.1 22 | 23 | packages: 24 | 25 | /@tootallnate/once@1.1.2: 26 | resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} 27 | engines: {node: '>= 6'} 28 | dev: true 29 | 30 | /@types/node@17.0.21: 31 | resolution: {integrity: sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==} 32 | dev: true 33 | 34 | /@types/vscode@1.85.0: 35 | resolution: {integrity: sha512-CF/RBon/GXwdfmnjZj0WTUMZN5H6YITOfBCP4iEZlOtVQXuzw6t7Le7+cR+7JzdMrnlm7Mfp49Oj2TuSXIWo3g==} 36 | dev: true 37 | 38 | /agent-base@6.0.2: 39 | resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} 40 | engines: {node: '>= 6.0.0'} 41 | dependencies: 42 | debug: 4.3.3 43 | transitivePeerDependencies: 44 | - supports-color 45 | dev: true 46 | 47 | /balanced-match@1.0.2: 48 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 49 | 50 | /big-integer@1.6.51: 51 | resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} 52 | engines: {node: '>=0.6'} 53 | dev: true 54 | 55 | /binary@0.3.0: 56 | resolution: {integrity: sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=} 57 | dependencies: 58 | buffers: 0.1.1 59 | chainsaw: 0.1.0 60 | dev: true 61 | 62 | /bluebird@3.4.7: 63 | resolution: {integrity: sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=} 64 | dev: true 65 | 66 | /brace-expansion@1.1.11: 67 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 68 | dependencies: 69 | balanced-match: 1.0.2 70 | concat-map: 0.0.1 71 | dev: true 72 | 73 | /brace-expansion@2.0.1: 74 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 75 | dependencies: 76 | balanced-match: 1.0.2 77 | dev: false 78 | 79 | /buffer-indexof-polyfill@1.0.2: 80 | resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} 81 | engines: {node: '>=0.10'} 82 | dev: true 83 | 84 | /buffers@0.1.1: 85 | resolution: {integrity: sha1-skV5w77U1tOWru5tmorn9Ugqt7s=} 86 | engines: {node: '>=0.2.0'} 87 | dev: true 88 | 89 | /chainsaw@0.1.0: 90 | resolution: {integrity: sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=} 91 | dependencies: 92 | traverse: 0.3.9 93 | dev: true 94 | 95 | /concat-map@0.0.1: 96 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} 97 | dev: true 98 | 99 | /core-util-is@1.0.3: 100 | resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} 101 | dev: true 102 | 103 | /debug@4.3.3: 104 | resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} 105 | engines: {node: '>=6.0'} 106 | peerDependencies: 107 | supports-color: '*' 108 | peerDependenciesMeta: 109 | supports-color: 110 | optional: true 111 | dependencies: 112 | ms: 2.1.2 113 | dev: true 114 | 115 | /duplexer2@0.1.4: 116 | resolution: {integrity: sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=} 117 | dependencies: 118 | readable-stream: 2.3.7 119 | dev: true 120 | 121 | /fs.realpath@1.0.0: 122 | resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} 123 | dev: true 124 | 125 | /fstream@1.0.12: 126 | resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} 127 | engines: {node: '>=0.6'} 128 | dependencies: 129 | graceful-fs: 4.2.9 130 | inherits: 2.0.4 131 | mkdirp: 0.5.5 132 | rimraf: 2.7.1 133 | dev: true 134 | 135 | /glob@7.2.0: 136 | resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} 137 | dependencies: 138 | fs.realpath: 1.0.0 139 | inflight: 1.0.6 140 | inherits: 2.0.4 141 | minimatch: 3.1.2 142 | once: 1.4.0 143 | path-is-absolute: 1.0.1 144 | dev: true 145 | 146 | /graceful-fs@4.2.9: 147 | resolution: {integrity: sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==} 148 | dev: true 149 | 150 | /http-proxy-agent@4.0.1: 151 | resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} 152 | engines: {node: '>= 6'} 153 | dependencies: 154 | '@tootallnate/once': 1.1.2 155 | agent-base: 6.0.2 156 | debug: 4.3.3 157 | transitivePeerDependencies: 158 | - supports-color 159 | dev: true 160 | 161 | /https-proxy-agent@5.0.0: 162 | resolution: {integrity: sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==} 163 | engines: {node: '>= 6'} 164 | dependencies: 165 | agent-base: 6.0.2 166 | debug: 4.3.3 167 | transitivePeerDependencies: 168 | - supports-color 169 | dev: true 170 | 171 | /inflight@1.0.6: 172 | resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} 173 | dependencies: 174 | once: 1.4.0 175 | wrappy: 1.0.2 176 | dev: true 177 | 178 | /inherits@2.0.4: 179 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 180 | dev: true 181 | 182 | /isarray@1.0.0: 183 | resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=} 184 | dev: true 185 | 186 | /listenercount@1.0.1: 187 | resolution: {integrity: sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=} 188 | dev: true 189 | 190 | /minimatch@3.1.2: 191 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 192 | dependencies: 193 | brace-expansion: 1.1.11 194 | dev: true 195 | 196 | /minimatch@5.1.6: 197 | resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 198 | engines: {node: '>=10'} 199 | dependencies: 200 | brace-expansion: 2.0.1 201 | dev: false 202 | 203 | /minimist@1.2.5: 204 | resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} 205 | dev: true 206 | 207 | /mkdirp@0.5.5: 208 | resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==} 209 | hasBin: true 210 | dependencies: 211 | minimist: 1.2.5 212 | dev: true 213 | 214 | /ms@2.1.2: 215 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 216 | dev: true 217 | 218 | /once@1.4.0: 219 | resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} 220 | dependencies: 221 | wrappy: 1.0.2 222 | dev: true 223 | 224 | /path-is-absolute@1.0.1: 225 | resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} 226 | engines: {node: '>=0.10.0'} 227 | dev: true 228 | 229 | /process-nextick-args@2.0.1: 230 | resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} 231 | dev: true 232 | 233 | /readable-stream@2.3.7: 234 | resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} 235 | dependencies: 236 | core-util-is: 1.0.3 237 | inherits: 2.0.4 238 | isarray: 1.0.0 239 | process-nextick-args: 2.0.1 240 | safe-buffer: 5.1.2 241 | string_decoder: 1.1.1 242 | util-deprecate: 1.0.2 243 | dev: true 244 | 245 | /rimraf@2.7.1: 246 | resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} 247 | hasBin: true 248 | dependencies: 249 | glob: 7.2.0 250 | dev: true 251 | 252 | /rimraf@3.0.2: 253 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 254 | hasBin: true 255 | dependencies: 256 | glob: 7.2.0 257 | dev: true 258 | 259 | /safe-buffer@5.1.2: 260 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 261 | dev: true 262 | 263 | /semver@7.6.3: 264 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} 265 | engines: {node: '>=10'} 266 | hasBin: true 267 | dev: false 268 | 269 | /setimmediate@1.0.5: 270 | resolution: {integrity: sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=} 271 | dev: true 272 | 273 | /string_decoder@1.1.1: 274 | resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} 275 | dependencies: 276 | safe-buffer: 5.1.2 277 | dev: true 278 | 279 | /traverse@0.3.9: 280 | resolution: {integrity: sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=} 281 | dev: true 282 | 283 | /unzipper@0.10.11: 284 | resolution: {integrity: sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==} 285 | dependencies: 286 | big-integer: 1.6.51 287 | binary: 0.3.0 288 | bluebird: 3.4.7 289 | buffer-indexof-polyfill: 1.0.2 290 | duplexer2: 0.1.4 291 | fstream: 1.0.12 292 | graceful-fs: 4.2.9 293 | listenercount: 1.0.1 294 | readable-stream: 2.3.7 295 | setimmediate: 1.0.5 296 | dev: true 297 | 298 | /util-deprecate@1.0.2: 299 | resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} 300 | dev: true 301 | 302 | /vscode-jsonrpc@8.2.0: 303 | resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} 304 | engines: {node: '>=14.0.0'} 305 | dev: false 306 | 307 | /vscode-languageclient@9.0.1: 308 | resolution: {integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==} 309 | engines: {vscode: ^1.82.0} 310 | dependencies: 311 | minimatch: 5.1.6 312 | semver: 7.6.3 313 | vscode-languageserver-protocol: 3.17.5 314 | dev: false 315 | 316 | /vscode-languageserver-protocol@3.17.5: 317 | resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} 318 | dependencies: 319 | vscode-jsonrpc: 8.2.0 320 | vscode-languageserver-types: 3.17.5 321 | dev: false 322 | 323 | /vscode-languageserver-types@3.17.5: 324 | resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} 325 | dev: false 326 | 327 | /vscode-test@1.6.1: 328 | resolution: {integrity: sha512-086q88T2ca1k95mUzffvbzb7esqQNvJgiwY4h29ukPhFo8u+vXOOmelUoU5EQUHs3Of8+JuQ3oGdbVCqaxuTXA==} 329 | engines: {node: '>=8.9.3'} 330 | deprecated: This package has been renamed to @vscode/test-electron, please update to the new name 331 | dependencies: 332 | http-proxy-agent: 4.0.1 333 | https-proxy-agent: 5.0.0 334 | rimraf: 3.0.2 335 | unzipper: 0.10.11 336 | transitivePeerDependencies: 337 | - supports-color 338 | dev: true 339 | 340 | /wrappy@1.0.2: 341 | resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} 342 | dev: true 343 | -------------------------------------------------------------------------------- /src/nrs_lang.rs: -------------------------------------------------------------------------------- 1 | use chumsky::Parser; 2 | use chumsky::{prelude::*, stream::Stream}; 3 | use core::fmt; 4 | use serde::{Deserialize, Serialize}; 5 | use std::collections::HashMap; 6 | use tower_lsp::lsp_types::SemanticTokenType; 7 | 8 | use crate::semantic_token::LEGEND_TYPE; 9 | use crate::span::Span; 10 | 11 | /// This is the parser and interpreter for the 'Foo' language. See `tutorial.md` in the repository's root to learn 12 | /// about it. 13 | 14 | #[derive(Debug)] 15 | pub struct ImCompleteSemanticToken { 16 | pub start: usize, 17 | pub length: usize, 18 | pub token_type: usize, 19 | } 20 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] 21 | pub enum Token { 22 | Null, 23 | Bool(bool), 24 | Num(String), 25 | Str(String), 26 | Op(String), 27 | Ctrl(char), 28 | Ident(String), 29 | Fn, 30 | Let, 31 | Print, 32 | If, 33 | Else, 34 | } 35 | 36 | pub type Ast = Vec>; 37 | 38 | impl fmt::Display for Token { 39 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 40 | match self { 41 | Token::Null => write!(f, "null"), 42 | Token::Bool(x) => write!(f, "{x}"), 43 | Token::Num(n) => write!(f, "{n}"), 44 | Token::Str(s) => write!(f, "{s}"), 45 | Token::Op(s) => write!(f, "{s}"), 46 | Token::Ctrl(c) => write!(f, "{c}"), 47 | Token::Ident(s) => write!(f, "{s}"), 48 | Token::Fn => write!(f, "fn"), 49 | Token::Let => write!(f, "let"), 50 | Token::Print => write!(f, "print"), 51 | Token::If => write!(f, "if"), 52 | Token::Else => write!(f, "else"), 53 | } 54 | } 55 | } 56 | 57 | fn lexer() -> impl Parser, Error = Simple> { 58 | // A parser for numbers 59 | let num = text::int(10) 60 | .chain::(just('.').chain(text::digits(10)).or_not().flatten()) 61 | .collect::() 62 | .map(Token::Num); 63 | 64 | // A parser for strings 65 | let str_ = just('"') 66 | .ignore_then(filter(|c| *c != '"').repeated()) 67 | .then_ignore(just('"')) 68 | .collect::() 69 | .map(Token::Str); 70 | 71 | // A parser for operators 72 | let op = one_of("+-*/!=") 73 | .repeated() 74 | .at_least(1) 75 | .collect::() 76 | .map(Token::Op); 77 | 78 | // A parser for control characters (delimiters, semicolons, etc.) 79 | let ctrl = one_of("()[]{};,").map(Token::Ctrl); 80 | 81 | // A parser for identifiers and keywords 82 | let ident = text::ident().map(|ident: String| match ident.as_str() { 83 | "fn" => Token::Fn, 84 | "let" => Token::Let, 85 | "print" => Token::Print, 86 | "if" => Token::If, 87 | "else" => Token::Else, 88 | "true" => Token::Bool(true), 89 | "false" => Token::Bool(false), 90 | "null" => Token::Null, 91 | _ => Token::Ident(ident), 92 | }); 93 | 94 | // A single token can be one of the above 95 | let token = num 96 | .or(str_) 97 | .or(op) 98 | .or(ctrl) 99 | .or(ident) 100 | .recover_with(skip_then_retry_until([])); 101 | 102 | let comment = just("//").then(take_until(just('\n'))).padded(); 103 | 104 | token 105 | .padded_by(comment.repeated()) 106 | .map_with_span(|tok, span| (tok, span)) 107 | .padded() 108 | .repeated() 109 | } 110 | 111 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 112 | pub enum Value { 113 | Null, 114 | Bool(bool), 115 | Num(f64), 116 | Str(String), 117 | } 118 | 119 | impl std::fmt::Display for Value { 120 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 121 | match self { 122 | Self::Null => write!(f, "null"), 123 | Self::Bool(x) => write!(f, "{x}"), 124 | Self::Num(x) => write!(f, "{x}"), 125 | Self::Str(x) => write!(f, "{x}"), 126 | } 127 | } 128 | } 129 | 130 | #[derive(Clone, Debug)] 131 | pub enum BinaryOp { 132 | Add, 133 | Sub, 134 | Mul, 135 | Div, 136 | Eq, 137 | NotEq, 138 | } 139 | 140 | pub type Spanned = (T, Span); 141 | 142 | // An expression node in the AST. Children are spanned so we can generate useful runtime errors. 143 | #[derive(Debug)] 144 | pub enum Expr { 145 | Error, 146 | Value(Value), 147 | List(Vec>), 148 | Local(Spanned), 149 | Let(String, Box>, Box>, Span), 150 | Then(Box>, Box>), 151 | Binary(Box>, BinaryOp, Box>), 152 | Call(Box>, Spanned>>), 153 | If(Box>, Box>, Box>), 154 | Print(Box>), 155 | } 156 | 157 | #[allow(unused)] 158 | impl Expr { 159 | /// Returns `true` if the expr is [`Error`]. 160 | /// 161 | /// [`Error`]: Expr::Error 162 | fn is_error(&self) -> bool { 163 | matches!(self, Self::Error) 164 | } 165 | 166 | /// Returns `true` if the expr is [`Let`]. 167 | /// 168 | /// [`Let`]: Expr::Let 169 | fn is_let(&self) -> bool { 170 | matches!(self, Self::Let(..)) 171 | } 172 | 173 | /// Returns `true` if the expr is [`Value`]. 174 | /// 175 | /// [`Value`]: Expr::Value 176 | fn is_value(&self) -> bool { 177 | matches!(self, Self::Value(..)) 178 | } 179 | 180 | fn try_into_value(self) -> Result { 181 | if let Self::Value(v) = self { 182 | Ok(v) 183 | } else { 184 | Err(self) 185 | } 186 | } 187 | 188 | fn as_value(&self) -> Option<&Value> { 189 | if let Self::Value(v) = self { 190 | Some(v) 191 | } else { 192 | None 193 | } 194 | } 195 | } 196 | 197 | // A function node in the AST. 198 | #[derive(Debug)] 199 | pub struct Func { 200 | pub args: Vec>, 201 | pub body: Spanned, 202 | pub name: Spanned, 203 | pub span: Span, 204 | } 205 | 206 | fn expr_parser() -> impl Parser, Error = Simple> + Clone { 207 | recursive(|expr| { 208 | let raw_expr = recursive(|raw_expr| { 209 | let val = filter_map(|span, tok| match tok { 210 | Token::Null => Ok(Expr::Value(Value::Null)), 211 | Token::Bool(x) => Ok(Expr::Value(Value::Bool(x))), 212 | Token::Num(n) => Ok(Expr::Value(Value::Num(n.parse().unwrap()))), 213 | Token::Str(s) => Ok(Expr::Value(Value::Str(s))), 214 | _ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))), 215 | }) 216 | .labelled("value"); 217 | 218 | let ident = filter_map(|span, tok| match tok { 219 | Token::Ident(ident) => Ok((ident, span)), 220 | _ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))), 221 | }) 222 | .labelled("identifier"); 223 | 224 | // A list of expressions 225 | let items = expr 226 | .clone() 227 | .chain(just(Token::Ctrl(',')).ignore_then(expr.clone()).repeated()) 228 | .then_ignore(just(Token::Ctrl(',')).or_not()) 229 | .or_not() 230 | .map(|item| item.unwrap_or_default()); 231 | 232 | // A let expression 233 | let let_ = just(Token::Let) 234 | .ignore_then(ident) 235 | .then_ignore(just(Token::Op("=".to_string()))) 236 | .then(raw_expr) 237 | .then_ignore(just(Token::Ctrl(';'))) 238 | .then(expr.clone()) 239 | .map(|((name, val), body)| { 240 | Expr::Let(name.0, Box::new(val), Box::new(body), name.1) 241 | }); 242 | 243 | let list = items 244 | .clone() 245 | .delimited_by(just(Token::Ctrl('[')), just(Token::Ctrl(']'))) 246 | .map(Expr::List); 247 | 248 | // 'Atoms' are expressions that contain no ambiguity 249 | let atom = val 250 | .or(ident.map(Expr::Local)) 251 | .or(let_) 252 | .or(list) 253 | // In Nano Rust, `print` is just a keyword, just like Python 2, for simplicity 254 | .or(just(Token::Print) 255 | .ignore_then( 256 | expr.clone() 257 | .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))), 258 | ) 259 | .map(|expr| Expr::Print(Box::new(expr)))) 260 | .map_with_span(|expr, span| (expr, span)) 261 | // Atoms can also just be normal expressions, but surrounded with parentheses 262 | .or(expr 263 | .clone() 264 | .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')')))) 265 | // Attempt to recover anything that looks like a parenthesised expression but contains errors 266 | .recover_with(nested_delimiters( 267 | Token::Ctrl('('), 268 | Token::Ctrl(')'), 269 | [ 270 | (Token::Ctrl('['), Token::Ctrl(']')), 271 | (Token::Ctrl('{'), Token::Ctrl('}')), 272 | ], 273 | |span| (Expr::Error, span), 274 | )) 275 | // Attempt to recover anything that looks like a list but contains errors 276 | .recover_with(nested_delimiters( 277 | Token::Ctrl('['), 278 | Token::Ctrl(']'), 279 | [ 280 | (Token::Ctrl('('), Token::Ctrl(')')), 281 | (Token::Ctrl('{'), Token::Ctrl('}')), 282 | ], 283 | |span| (Expr::Error, span), 284 | )); 285 | 286 | // Function calls have very high precedence so we prioritise them 287 | let call = atom 288 | .then( 289 | items 290 | .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))) 291 | .map_with_span(|args, span| (args, span)) 292 | .repeated(), 293 | ) 294 | .foldl(|f, args| { 295 | let span = f.1.start..args.1.end; 296 | (Expr::Call(Box::new(f), args), span) 297 | }); 298 | 299 | // Product ops (multiply and divide) have equal precedence 300 | let op = just(Token::Op("*".to_string())) 301 | .to(BinaryOp::Mul) 302 | .or(just(Token::Op("/".to_string())).to(BinaryOp::Div)); 303 | let product = call 304 | .clone() 305 | .then(op.then(call).repeated()) 306 | .foldl(|a, (op, b)| { 307 | let span = a.1.start..b.1.end; 308 | (Expr::Binary(Box::new(a), op, Box::new(b)), span) 309 | }); 310 | 311 | // Sum ops (add and subtract) have equal precedence 312 | let op = just(Token::Op("+".to_string())) 313 | .to(BinaryOp::Add) 314 | .or(just(Token::Op("-".to_string())).to(BinaryOp::Sub)); 315 | let sum = product 316 | .clone() 317 | .then(op.then(product).repeated()) 318 | .foldl(|a, (op, b)| { 319 | let span = a.1.start..b.1.end; 320 | (Expr::Binary(Box::new(a), op, Box::new(b)), span) 321 | }); 322 | 323 | // Comparison ops (equal, not-equal) have equal precedence 324 | let op = just(Token::Op("==".to_string())) 325 | .to(BinaryOp::Eq) 326 | .or(just(Token::Op("!=".to_string())).to(BinaryOp::NotEq)); 327 | 328 | sum.clone() 329 | .then(op.then(sum).repeated()) 330 | .foldl(|a, (op, b)| { 331 | let span = a.1.start..b.1.end; 332 | (Expr::Binary(Box::new(a), op, Box::new(b)), span) 333 | }) 334 | }); 335 | 336 | // Blocks are expressions but delimited with braces 337 | let block = expr 338 | .clone() 339 | .delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}'))) 340 | // Attempt to recover anything that looks like a block but contains errors 341 | .recover_with(nested_delimiters( 342 | Token::Ctrl('{'), 343 | Token::Ctrl('}'), 344 | [ 345 | (Token::Ctrl('('), Token::Ctrl(')')), 346 | (Token::Ctrl('['), Token::Ctrl(']')), 347 | ], 348 | |span| (Expr::Error, span), 349 | )); 350 | 351 | let if_ = recursive(|if_| { 352 | just(Token::If) 353 | .ignore_then(expr.clone()) 354 | .then(block.clone()) 355 | .then( 356 | just(Token::Else) 357 | .ignore_then(block.clone().or(if_)) 358 | .or_not(), 359 | ) 360 | .map_with_span(|((cond, a), b), span| { 361 | ( 362 | Expr::If( 363 | Box::new(cond), 364 | Box::new(a), 365 | Box::new(match b { 366 | Some(b) => b, 367 | // If an `if` expression has no trailing `else` block, we magic up one that just produces null 368 | None => (Expr::Value(Value::Null), span.clone()), 369 | }), 370 | ), 371 | span, 372 | ) 373 | }) 374 | }); 375 | 376 | // Both blocks and `if` are 'block expressions' and can appear in the place of statements 377 | let block_expr = block.or(if_).labelled("block"); 378 | 379 | let block_chain = block_expr 380 | .clone() 381 | .then(block_expr.clone().repeated()) 382 | .foldl(|a, b| { 383 | let span = a.1.start..b.1.end; 384 | (Expr::Then(Box::new(a), Box::new(b)), span) 385 | }); 386 | 387 | block_chain 388 | // Expressions, chained by semicolons, are statements 389 | .or(raw_expr.clone()) 390 | .then(just(Token::Ctrl(';')).ignore_then(expr.or_not()).repeated()) 391 | .foldl(|a, b| { 392 | let span = a.1.clone(); // TODO: Not correct 393 | ( 394 | Expr::Then( 395 | Box::new(a), 396 | Box::new(match b { 397 | Some(b) => b, 398 | None => (Expr::Value(Value::Null), span.clone()), 399 | }), 400 | ), 401 | span, 402 | ) 403 | }) 404 | }) 405 | } 406 | 407 | pub fn funcs_parser() -> impl Parser>, Error = Simple> + Clone { 408 | let ident = filter_map(|span, tok| match tok { 409 | Token::Ident(ident) => Ok(ident), 410 | _ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))), 411 | }); 412 | 413 | // Argument lists are just identifiers separated by commas, surrounded by parentheses 414 | let args = ident 415 | .map_with_span(|name, span| (name, span)) 416 | .separated_by(just(Token::Ctrl(','))) 417 | .allow_trailing() 418 | .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))) 419 | .labelled("function args"); 420 | 421 | let func = just(Token::Fn) 422 | .ignore_then( 423 | ident 424 | .map_with_span(|name, span| (name, span)) 425 | .labelled("function name"), 426 | ) 427 | .then(args) 428 | .then( 429 | expr_parser() 430 | .delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}'))) 431 | // Attempt to recover anything that looks like a function body but contains errors 432 | .recover_with(nested_delimiters( 433 | Token::Ctrl('{'), 434 | Token::Ctrl('}'), 435 | [ 436 | (Token::Ctrl('('), Token::Ctrl(')')), 437 | (Token::Ctrl('['), Token::Ctrl(']')), 438 | ], 439 | |span| (Expr::Error, span), 440 | )), 441 | ) 442 | .map_with_span(|((name, args), body), span| { 443 | ( 444 | name.clone(), 445 | Func { 446 | args, 447 | body, 448 | name, 449 | span, 450 | }, 451 | ) 452 | }) 453 | .labelled("function"); 454 | 455 | func.repeated() 456 | .try_map(|fs, _| { 457 | Ok(fs 458 | .into_iter() 459 | .map(|item| (item.1, item.0 .1)) 460 | .collect::>()) 461 | }) 462 | .then_ignore(end()) 463 | } 464 | 465 | pub fn type_inference(expr: &Spanned, symbol_type_table: &mut HashMap) { 466 | match &expr.0 { 467 | Expr::Error => {} 468 | Expr::Value(_) => {} 469 | Expr::List(exprs) => exprs 470 | .iter() 471 | .for_each(|expr| type_inference(expr, symbol_type_table)), 472 | Expr::Local(_) => {} 473 | Expr::Let(_name, lhs, rest, name_span) => { 474 | if let Some(value) = lhs.0.as_value() { 475 | symbol_type_table.insert(name_span.clone(), value.clone()); 476 | } 477 | type_inference(rest, symbol_type_table); 478 | } 479 | Expr::Then(first, second) => { 480 | type_inference(first, symbol_type_table); 481 | type_inference(second, symbol_type_table); 482 | } 483 | Expr::Binary(_, _, _) => {} 484 | Expr::Call(_, _) => {} 485 | Expr::If(_test, consequent, alternative) => { 486 | type_inference(consequent, symbol_type_table); 487 | type_inference(alternative, symbol_type_table); 488 | } 489 | Expr::Print(expr) => { 490 | type_inference(expr, symbol_type_table); 491 | } 492 | } 493 | } 494 | 495 | #[derive(Debug)] 496 | pub struct ParserResult { 497 | pub ast: Option>>, 498 | pub parse_errors: Vec>, 499 | pub semantic_tokens: Vec, 500 | } 501 | 502 | pub fn parse(src: &str) -> ParserResult { 503 | let (tokens, errs) = lexer().parse_recovery(src); 504 | 505 | let (ast, tokenize_errors, semantic_tokens) = if let Some(tokens) = tokens { 506 | // info!("Tokens = {:?}", tokens); 507 | let semantic_tokens = tokens 508 | .iter() 509 | .filter_map(|(token, span)| match token { 510 | Token::Null => None, 511 | Token::Bool(_) => None, 512 | 513 | Token::Num(_) => Some(ImCompleteSemanticToken { 514 | start: span.start, 515 | length: span.len(), 516 | token_type: LEGEND_TYPE 517 | .iter() 518 | .position(|item| item == &SemanticTokenType::NUMBER) 519 | .unwrap(), 520 | }), 521 | Token::Str(_) => Some(ImCompleteSemanticToken { 522 | start: span.start, 523 | length: span.len(), 524 | token_type: LEGEND_TYPE 525 | .iter() 526 | .position(|item| item == &SemanticTokenType::STRING) 527 | .unwrap(), 528 | }), 529 | Token::Op(_) => Some(ImCompleteSemanticToken { 530 | start: span.start, 531 | length: span.len(), 532 | token_type: LEGEND_TYPE 533 | .iter() 534 | .position(|item| item == &SemanticTokenType::OPERATOR) 535 | .unwrap(), 536 | }), 537 | Token::Ctrl(_) => None, 538 | Token::Ident(_) => None, 539 | Token::Fn => Some(ImCompleteSemanticToken { 540 | start: span.start, 541 | length: span.len(), 542 | token_type: LEGEND_TYPE 543 | .iter() 544 | .position(|item| item == &SemanticTokenType::KEYWORD) 545 | .unwrap(), 546 | }), 547 | Token::Let => Some(ImCompleteSemanticToken { 548 | start: span.start, 549 | length: span.len(), 550 | token_type: LEGEND_TYPE 551 | .iter() 552 | .position(|item| item == &SemanticTokenType::KEYWORD) 553 | .unwrap(), 554 | }), 555 | Token::Print => Some(ImCompleteSemanticToken { 556 | start: span.start, 557 | length: span.len(), 558 | token_type: LEGEND_TYPE 559 | .iter() 560 | .position(|item| item == &SemanticTokenType::FUNCTION) 561 | .unwrap(), 562 | }), 563 | Token::If => Some(ImCompleteSemanticToken { 564 | start: span.start, 565 | length: span.len(), 566 | token_type: LEGEND_TYPE 567 | .iter() 568 | .position(|item| item == &SemanticTokenType::KEYWORD) 569 | .unwrap(), 570 | }), 571 | Token::Else => Some(ImCompleteSemanticToken { 572 | start: span.start, 573 | length: span.len(), 574 | token_type: LEGEND_TYPE 575 | .iter() 576 | .position(|item| item == &SemanticTokenType::KEYWORD) 577 | .unwrap(), 578 | }), 579 | }) 580 | .collect::>(); 581 | let len = src.chars().count(); 582 | let (ast, parse_errs) = 583 | funcs_parser().parse_recovery(Stream::from_iter(len..len + 1, tokens.into_iter())); 584 | 585 | (ast, parse_errs, semantic_tokens) 586 | } else { 587 | (None, Vec::new(), vec![]) 588 | }; 589 | 590 | let parse_errors = errs 591 | .into_iter() 592 | .map(|e| e.map(|c| c.to_string())) 593 | .chain( 594 | tokenize_errors 595 | .into_iter() 596 | .map(|e| e.map(|tok| tok.to_string())), 597 | ) 598 | .collect::>(); 599 | 600 | ParserResult { 601 | ast, 602 | parse_errors, 603 | semantic_tokens, 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use dashmap::DashMap; 4 | use log::debug; 5 | use nrs_language_server::completion::completion; 6 | use nrs_language_server::nrs_lang::{ 7 | parse, type_inference, Ast, ImCompleteSemanticToken, ParserResult, 8 | }; 9 | use nrs_language_server::semantic_analyze::{analyze_program, IdentType, Semantic}; 10 | use nrs_language_server::semantic_token::LEGEND_TYPE; 11 | use nrs_language_server::span::Span; 12 | use ropey::Rope; 13 | use serde::{Deserialize, Serialize}; 14 | use serde_json::Value; 15 | use tower_lsp::jsonrpc::Result; 16 | use tower_lsp::lsp_types::notification::Notification; 17 | use tower_lsp::lsp_types::*; 18 | use tower_lsp::{Client, LanguageServer, LspService, Server}; 19 | #[derive(Debug)] 20 | struct Backend { 21 | client: Client, 22 | ast_map: DashMap, 23 | semantic_map: DashMap, 24 | document_map: DashMap, 25 | semantic_token_map: DashMap>, 26 | } 27 | 28 | #[tower_lsp::async_trait] 29 | impl LanguageServer for Backend { 30 | async fn initialize(&self, _: InitializeParams) -> Result { 31 | Ok(InitializeResult { 32 | server_info: None, 33 | offset_encoding: None, 34 | capabilities: ServerCapabilities { 35 | inlay_hint_provider: Some(OneOf::Left(true)), 36 | text_document_sync: Some(TextDocumentSyncCapability::Options( 37 | TextDocumentSyncOptions { 38 | open_close: Some(true), 39 | change: Some(TextDocumentSyncKind::FULL), 40 | save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions { 41 | include_text: Some(true), 42 | })), 43 | ..Default::default() 44 | }, 45 | )), 46 | completion_provider: Some(CompletionOptions { 47 | resolve_provider: Some(false), 48 | trigger_characters: Some(vec![".".to_string()]), 49 | work_done_progress_options: Default::default(), 50 | all_commit_characters: None, 51 | completion_item: None, 52 | }), 53 | execute_command_provider: Some(ExecuteCommandOptions { 54 | commands: vec!["dummy.do_something".to_string()], 55 | work_done_progress_options: Default::default(), 56 | }), 57 | 58 | workspace: Some(WorkspaceServerCapabilities { 59 | workspace_folders: Some(WorkspaceFoldersServerCapabilities { 60 | supported: Some(true), 61 | change_notifications: Some(OneOf::Left(true)), 62 | }), 63 | file_operations: None, 64 | }), 65 | semantic_tokens_provider: Some( 66 | SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions( 67 | SemanticTokensRegistrationOptions { 68 | text_document_registration_options: { 69 | TextDocumentRegistrationOptions { 70 | document_selector: Some(vec![DocumentFilter { 71 | language: Some("nrs".to_string()), 72 | scheme: Some("file".to_string()), 73 | pattern: None, 74 | }]), 75 | } 76 | }, 77 | semantic_tokens_options: SemanticTokensOptions { 78 | work_done_progress_options: WorkDoneProgressOptions::default(), 79 | legend: SemanticTokensLegend { 80 | token_types: LEGEND_TYPE.into(), 81 | token_modifiers: vec![], 82 | }, 83 | range: Some(true), 84 | full: Some(SemanticTokensFullOptions::Bool(true)), 85 | }, 86 | static_registration_options: StaticRegistrationOptions::default(), 87 | }, 88 | ), 89 | ), 90 | // definition: Some(GotoCapability::default()), 91 | definition_provider: Some(OneOf::Left(true)), 92 | references_provider: Some(OneOf::Left(true)), 93 | rename_provider: Some(OneOf::Left(true)), 94 | ..ServerCapabilities::default() 95 | }, 96 | }) 97 | } 98 | async fn initialized(&self, _: InitializedParams) { 99 | debug!("initialized!"); 100 | } 101 | 102 | async fn shutdown(&self) -> Result<()> { 103 | Ok(()) 104 | } 105 | 106 | async fn did_open(&self, params: DidOpenTextDocumentParams) { 107 | debug!("file opened"); 108 | self.on_change(TextDocumentItem { 109 | uri: params.text_document.uri, 110 | text: ¶ms.text_document.text, 111 | version: Some(params.text_document.version), 112 | }) 113 | .await 114 | } 115 | 116 | async fn did_change(&self, params: DidChangeTextDocumentParams) { 117 | self.on_change(TextDocumentItem { 118 | text: ¶ms.content_changes[0].text, 119 | uri: params.text_document.uri, 120 | version: Some(params.text_document.version), 121 | }) 122 | .await 123 | } 124 | 125 | async fn did_save(&self, params: DidSaveTextDocumentParams) { 126 | dbg!(¶ms.text); 127 | if let Some(text) = params.text { 128 | let item = TextDocumentItem { 129 | uri: params.text_document.uri, 130 | text: &text, 131 | version: None, 132 | }; 133 | self.on_change(item).await; 134 | _ = self.client.semantic_tokens_refresh().await; 135 | } 136 | debug!("file saved!"); 137 | } 138 | async fn did_close(&self, _: DidCloseTextDocumentParams) { 139 | debug!("file closed!"); 140 | } 141 | 142 | async fn goto_definition( 143 | &self, 144 | params: GotoDefinitionParams, 145 | ) -> Result> { 146 | let definition = || -> Option { 147 | let uri = params.text_document_position_params.text_document.uri; 148 | let semantic = self.semantic_map.get(uri.as_str())?; 149 | let rope = self.document_map.get(uri.as_str())?; 150 | let position = params.text_document_position_params.position; 151 | let offset = position_to_offset(position, &rope)?; 152 | 153 | let interval = semantic.ident_range.find(offset, offset + 1).next()?; 154 | let interval_val = interval.val; 155 | let range = match interval_val { 156 | IdentType::Binding(symbol_id) => { 157 | let span = &semantic.table.symbol_id_to_span[symbol_id]; 158 | Some(span.clone()) 159 | } 160 | IdentType::Reference(reference_id) => { 161 | let reference = semantic.table.reference_id_to_reference.get(reference_id)?; 162 | let symbol_id = reference.symbol_id?; 163 | let symbol_range = semantic.table.symbol_id_to_span.get(symbol_id)?; 164 | Some(symbol_range.clone()) 165 | } 166 | }; 167 | 168 | range.and_then(|range| { 169 | let start_position = offset_to_position(range.start, &rope)?; 170 | let end_position = offset_to_position(range.end, &rope)?; 171 | Some(GotoDefinitionResponse::Scalar(Location::new( 172 | uri, 173 | Range::new(start_position, end_position), 174 | ))) 175 | }) 176 | }(); 177 | Ok(definition) 178 | } 179 | 180 | async fn references(&self, params: ReferenceParams) -> Result>> { 181 | let reference_list = || -> Option> { 182 | let uri = params.text_document_position.text_document.uri; 183 | let semantic = self.semantic_map.get(uri.as_str())?; 184 | let rope = self.document_map.get(uri.as_str())?; 185 | let position = params.text_document_position.position; 186 | let offset = position_to_offset(position, &rope)?; 187 | let reference_span_list = get_references(&semantic, offset, offset + 1, false)?; 188 | 189 | let ret = reference_span_list 190 | .into_iter() 191 | .filter_map(|range| { 192 | let start_position = offset_to_position(range.start, &rope)?; 193 | let end_position = offset_to_position(range.end, &rope)?; 194 | 195 | let range = Range::new(start_position, end_position); 196 | 197 | Some(Location::new(uri.clone(), range)) 198 | }) 199 | .collect::>(); 200 | Some(ret) 201 | }(); 202 | Ok(reference_list) 203 | } 204 | 205 | async fn semantic_tokens_full( 206 | &self, 207 | params: SemanticTokensParams, 208 | ) -> Result> { 209 | let uri = params.text_document.uri.to_string(); 210 | debug!("semantic_token_full"); 211 | let semantic_tokens = || -> Option> { 212 | let mut im_complete_tokens = self.semantic_token_map.get_mut(&uri)?; 213 | let rope = self.document_map.get(&uri)?; 214 | im_complete_tokens.sort_by(|a, b| a.start.cmp(&b.start)); 215 | let mut pre_line = 0; 216 | let mut pre_start = 0; 217 | let semantic_tokens = im_complete_tokens 218 | .iter() 219 | .filter_map(|token| { 220 | let line = rope.try_byte_to_line(token.start).ok()? as u32; 221 | let first = rope.try_line_to_char(line as usize).ok()? as u32; 222 | let start = rope.try_byte_to_char(token.start).ok()? as u32 - first; 223 | let delta_line = line - pre_line; 224 | let delta_start = if delta_line == 0 { 225 | start - pre_start 226 | } else { 227 | start 228 | }; 229 | let ret = Some(SemanticToken { 230 | delta_line, 231 | delta_start, 232 | length: token.length as u32, 233 | token_type: token.token_type as u32, 234 | token_modifiers_bitset: 0, 235 | }); 236 | pre_line = line; 237 | pre_start = start; 238 | ret 239 | }) 240 | .collect::>(); 241 | Some(semantic_tokens) 242 | }(); 243 | if let Some(semantic_token) = semantic_tokens { 244 | return Ok(Some(SemanticTokensResult::Tokens(SemanticTokens { 245 | result_id: None, 246 | data: semantic_token, 247 | }))); 248 | } 249 | Ok(None) 250 | } 251 | 252 | async fn semantic_tokens_range( 253 | &self, 254 | params: SemanticTokensRangeParams, 255 | ) -> Result> { 256 | let uri = params.text_document.uri.to_string(); 257 | let semantic_tokens = || -> Option> { 258 | let im_complete_tokens = self.semantic_token_map.get(&uri)?; 259 | let rope = self.document_map.get(&uri)?; 260 | let mut pre_line = 0; 261 | let mut pre_start = 0; 262 | let semantic_tokens = im_complete_tokens 263 | .iter() 264 | .filter_map(|token| { 265 | let line = rope.try_byte_to_line(token.start).ok()? as u32; 266 | let first = rope.try_line_to_char(line as usize).ok()? as u32; 267 | let start = rope.try_byte_to_char(token.start).ok()? as u32 - first; 268 | let ret = Some(SemanticToken { 269 | delta_line: line - pre_line, 270 | delta_start: if start >= pre_start { 271 | start - pre_start 272 | } else { 273 | start 274 | }, 275 | length: token.length as u32, 276 | token_type: token.token_type as u32, 277 | token_modifiers_bitset: 0, 278 | }); 279 | pre_line = line; 280 | pre_start = start; 281 | ret 282 | }) 283 | .collect::>(); 284 | Some(semantic_tokens) 285 | }(); 286 | Ok(semantic_tokens.map(|data| { 287 | SemanticTokensRangeResult::Tokens(SemanticTokens { 288 | result_id: None, 289 | data, 290 | }) 291 | })) 292 | } 293 | 294 | async fn inlay_hint( 295 | &self, 296 | params: tower_lsp::lsp_types::InlayHintParams, 297 | ) -> Result>> { 298 | debug!("inlay hint"); 299 | let uri = ¶ms.text_document.uri; 300 | let mut hashmap = HashMap::new(); 301 | if let Some(ast) = self.ast_map.get(uri.as_str()) { 302 | ast.iter().for_each(|(func, _)| { 303 | type_inference(&func.body, &mut hashmap); 304 | }); 305 | } 306 | 307 | let document = match self.document_map.get(uri.as_str()) { 308 | Some(rope) => rope, 309 | None => return Ok(None), 310 | }; 311 | let inlay_hint_list = hashmap 312 | .into_iter() 313 | .map(|(k, v)| { 314 | ( 315 | k.start, 316 | k.end, 317 | match v { 318 | nrs_language_server::nrs_lang::Value::Null => "null".to_string(), 319 | nrs_language_server::nrs_lang::Value::Bool(_) => "bool".to_string(), 320 | nrs_language_server::nrs_lang::Value::Num(_) => "number".to_string(), 321 | nrs_language_server::nrs_lang::Value::Str(_) => "string".to_string(), 322 | }, 323 | ) 324 | }) 325 | .filter_map(|item| { 326 | // let start_position = offset_to_position(item.0, document)?; 327 | let end_position = offset_to_position(item.1, &document)?; 328 | let inlay_hint = InlayHint { 329 | text_edits: None, 330 | tooltip: None, 331 | kind: Some(InlayHintKind::TYPE), 332 | padding_left: None, 333 | padding_right: None, 334 | data: None, 335 | position: end_position, 336 | label: InlayHintLabel::LabelParts(vec![InlayHintLabelPart { 337 | value: item.2, 338 | tooltip: None, 339 | location: Some(Location { 340 | uri: params.text_document.uri.clone(), 341 | range: Range { 342 | start: Position::new(0, 4), 343 | end: Position::new(0, 10), 344 | }, 345 | }), 346 | command: None, 347 | }]), 348 | }; 349 | Some(inlay_hint) 350 | }) 351 | .collect::>(); 352 | 353 | Ok(Some(inlay_hint_list)) 354 | } 355 | 356 | async fn completion(&self, params: CompletionParams) -> Result> { 357 | let uri = params.text_document_position.text_document.uri; 358 | let position = params.text_document_position.position; 359 | let completions = || -> Option> { 360 | let rope = self.document_map.get(&uri.to_string())?; 361 | let ast = self.ast_map.get(&uri.to_string())?; 362 | let char = rope.try_line_to_char(position.line as usize).ok()?; 363 | let offset = char + position.character as usize; 364 | let completions = completion(&ast, offset); 365 | let mut ret = Vec::with_capacity(completions.len()); 366 | for (_, item) in completions { 367 | match item { 368 | nrs_language_server::completion::ImCompleteCompletionItem::Variable(var) => { 369 | ret.push(CompletionItem { 370 | label: var.clone(), 371 | insert_text: Some(var.clone()), 372 | kind: Some(CompletionItemKind::VARIABLE), 373 | detail: Some(var), 374 | ..Default::default() 375 | }); 376 | } 377 | nrs_language_server::completion::ImCompleteCompletionItem::Function( 378 | name, 379 | args, 380 | ) => { 381 | ret.push(CompletionItem { 382 | label: name.clone(), 383 | kind: Some(CompletionItemKind::FUNCTION), 384 | detail: Some(name.clone()), 385 | insert_text: Some(format!( 386 | "{}({})", 387 | name, 388 | args.iter() 389 | .enumerate() 390 | .map(|(index, item)| { format!("${{{}:{}}}", index + 1, item) }) 391 | .collect::>() 392 | .join(",") 393 | )), 394 | insert_text_format: Some(InsertTextFormat::SNIPPET), 395 | ..Default::default() 396 | }); 397 | } 398 | } 399 | } 400 | Some(ret) 401 | }(); 402 | Ok(completions.map(CompletionResponse::Array)) 403 | } 404 | 405 | async fn rename(&self, params: RenameParams) -> Result> { 406 | let workspace_edit = || -> Option { 407 | let uri = params.text_document_position.text_document.uri; 408 | let semantic = self.semantic_map.get(uri.as_str())?; 409 | let rope = self.document_map.get(uri.as_str())?; 410 | let position = params.text_document_position.position; 411 | let offset = position_to_offset(position, &rope)?; 412 | let reference_list = get_references(&semantic, offset, offset + 1, true)?; 413 | 414 | let new_name = params.new_name; 415 | (!reference_list.is_empty()).then_some(()).map(|_| { 416 | let edit_list = reference_list 417 | .into_iter() 418 | .filter_map(|range| { 419 | let start_position = offset_to_position(range.start, &rope)?; 420 | let end_position = offset_to_position(range.end, &rope)?; 421 | Some(TextEdit::new( 422 | Range::new(start_position, end_position), 423 | new_name.clone(), 424 | )) 425 | }) 426 | .collect::>(); 427 | let mut map = HashMap::new(); 428 | map.insert(uri, edit_list); 429 | WorkspaceEdit::new(map) 430 | }) 431 | }(); 432 | Ok(workspace_edit) 433 | } 434 | 435 | async fn did_change_configuration(&self, _: DidChangeConfigurationParams) { 436 | debug!("configuration changed!"); 437 | } 438 | 439 | async fn did_change_workspace_folders(&self, _: DidChangeWorkspaceFoldersParams) { 440 | debug!("workspace folders changed!"); 441 | } 442 | 443 | async fn did_change_watched_files(&self, _: DidChangeWatchedFilesParams) { 444 | debug!("watched files have changed!"); 445 | } 446 | 447 | async fn execute_command(&self, _: ExecuteCommandParams) -> Result> { 448 | debug!("command executed!"); 449 | 450 | match self.client.apply_edit(WorkspaceEdit::default()).await { 451 | Ok(res) if res.applied => self.client.log_message(MessageType::INFO, "applied").await, 452 | Ok(_) => self.client.log_message(MessageType::INFO, "rejected").await, 453 | Err(err) => self.client.log_message(MessageType::ERROR, err).await, 454 | } 455 | 456 | Ok(None) 457 | } 458 | } 459 | #[derive(Debug, Deserialize, Serialize)] 460 | struct InlayHintParams { 461 | path: String, 462 | } 463 | 464 | #[allow(unused)] 465 | enum CustomNotification {} 466 | impl Notification for CustomNotification { 467 | type Params = InlayHintParams; 468 | const METHOD: &'static str = "custom/notification"; 469 | } 470 | struct TextDocumentItem<'a> { 471 | uri: Url, 472 | text: &'a str, 473 | version: Option, 474 | } 475 | 476 | impl Backend { 477 | async fn on_change<'a>(&self, params: TextDocumentItem<'a>) { 478 | dbg!(¶ms.version); 479 | let rope = ropey::Rope::from_str(params.text); 480 | self.document_map 481 | .insert(params.uri.to_string(), rope.clone()); 482 | let ParserResult { 483 | ast, 484 | parse_errors, 485 | semantic_tokens, 486 | } = parse(params.text); 487 | let mut diagnostics = parse_errors 488 | .into_iter() 489 | .filter_map(|item| { 490 | let (message, span) = match item.reason() { 491 | chumsky::error::SimpleReason::Unclosed { span, delimiter } => { 492 | (format!("Unclosed delimiter {delimiter}"), span.clone()) 493 | } 494 | chumsky::error::SimpleReason::Unexpected => ( 495 | format!( 496 | "{}, expected {}", 497 | if item.found().is_some() { 498 | "Unexpected token in input" 499 | } else { 500 | "Unexpected end of input" 501 | }, 502 | if item.expected().len() == 0 { 503 | "something else".to_string() 504 | } else { 505 | item.expected() 506 | .map(|expected| match expected { 507 | Some(expected) => expected.to_string(), 508 | None => "end of input".to_string(), 509 | }) 510 | .collect::>() 511 | .join(", ") 512 | } 513 | ), 514 | item.span(), 515 | ), 516 | chumsky::error::SimpleReason::Custom(msg) => (msg.to_string(), item.span()), 517 | }; 518 | 519 | let start_position = offset_to_position(span.start, &rope)?; 520 | let end_position = offset_to_position(span.end, &rope)?; 521 | Some(Diagnostic::new_simple( 522 | Range::new(start_position, end_position), 523 | message, 524 | )) 525 | }) 526 | .collect::>(); 527 | 528 | if let Some(ast) = ast { 529 | match analyze_program(&ast) { 530 | Ok(semantic) => { 531 | self.semantic_map.insert(params.uri.to_string(), semantic); 532 | } 533 | Err(err) => { 534 | self.semantic_token_map.remove(¶ms.uri.to_string()); 535 | let span = err.span(); 536 | let start_position = offset_to_position(span.start, &rope); 537 | let end_position = offset_to_position(span.end, &rope); 538 | let diag = start_position 539 | .and_then(|start| end_position.map(|end| (start, end))) 540 | .map(|(start, end)| { 541 | Diagnostic::new_simple(Range::new(start, end), format!("{err:?}")) 542 | }); 543 | if let Some(diag) = diag { 544 | diagnostics.push(diag); 545 | } 546 | } 547 | }; 548 | self.ast_map.insert(params.uri.to_string(), ast); 549 | } 550 | 551 | self.client 552 | .publish_diagnostics(params.uri.clone(), diagnostics, params.version) 553 | .await; 554 | self.semantic_token_map 555 | .insert(params.uri.to_string(), semantic_tokens); 556 | } 557 | } 558 | 559 | #[tokio::main] 560 | async fn main() { 561 | env_logger::init(); 562 | 563 | let stdin = tokio::io::stdin(); 564 | let stdout = tokio::io::stdout(); 565 | 566 | let (service, socket) = LspService::build(|client| Backend { 567 | client, 568 | ast_map: DashMap::new(), 569 | document_map: DashMap::new(), 570 | semantic_token_map: DashMap::new(), 571 | semantic_map: DashMap::new(), 572 | }) 573 | .finish(); 574 | 575 | Server::new(stdin, stdout, socket).serve(service).await; 576 | } 577 | 578 | fn offset_to_position(offset: usize, rope: &Rope) -> Option { 579 | let line = rope.try_char_to_line(offset).ok()?; 580 | let first_char_of_line = rope.try_line_to_char(line).ok()?; 581 | let column = offset - first_char_of_line; 582 | Some(Position::new(line as u32, column as u32)) 583 | } 584 | 585 | fn position_to_offset(position: Position, rope: &Rope) -> Option { 586 | let line_char_offset = rope.try_line_to_char(position.line as usize).ok()?; 587 | let slice = rope.slice(0..line_char_offset + position.character as usize); 588 | Some(slice.len_bytes()) 589 | } 590 | 591 | fn get_references( 592 | semantic: &Semantic, 593 | start: usize, 594 | end: usize, 595 | include_definition: bool, 596 | ) -> Option> { 597 | let interval = semantic.ident_range.find(start, end).next()?; 598 | let interval_val = interval.val; 599 | match interval_val { 600 | IdentType::Binding(symbol_id) => { 601 | let references = semantic.table.symbol_id_to_references.get(&symbol_id)?; 602 | let mut reference_span_list: Vec = references 603 | .iter() 604 | .map(|reference_id| { 605 | semantic.table.reference_id_to_reference[*reference_id] 606 | .span 607 | .clone() 608 | }) 609 | .collect(); 610 | if include_definition { 611 | let symbol_range = semantic.table.symbol_id_to_span.get(symbol_id)?; 612 | reference_span_list.push(symbol_range.clone()); 613 | } 614 | Some(reference_span_list) 615 | } 616 | IdentType::Reference(_) => None, 617 | } 618 | } 619 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.8.11" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 10 | dependencies = [ 11 | "cfg-if", 12 | "once_cell", 13 | "version_check", 14 | "zerocopy", 15 | ] 16 | 17 | [[package]] 18 | name = "aho-corasick" 19 | version = "0.7.20" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 22 | dependencies = [ 23 | "memchr", 24 | ] 25 | 26 | [[package]] 27 | name = "allocator-api2" 28 | version = "0.2.20" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" 31 | 32 | [[package]] 33 | name = "anstream" 34 | version = "0.6.18" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 37 | dependencies = [ 38 | "anstyle", 39 | "anstyle-parse", 40 | "anstyle-query", 41 | "anstyle-wincon", 42 | "colorchoice", 43 | "is_terminal_polyfill", 44 | "utf8parse", 45 | ] 46 | 47 | [[package]] 48 | name = "anstyle" 49 | version = "1.0.10" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 52 | 53 | [[package]] 54 | name = "anstyle-parse" 55 | version = "0.2.6" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 58 | dependencies = [ 59 | "utf8parse", 60 | ] 61 | 62 | [[package]] 63 | name = "anstyle-query" 64 | version = "1.1.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 67 | dependencies = [ 68 | "windows-sys 0.59.0", 69 | ] 70 | 71 | [[package]] 72 | name = "anstyle-wincon" 73 | version = "3.0.6" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" 76 | dependencies = [ 77 | "anstyle", 78 | "windows-sys 0.59.0", 79 | ] 80 | 81 | [[package]] 82 | name = "anyhow" 83 | version = "1.0.93" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" 86 | 87 | [[package]] 88 | name = "async-trait" 89 | version = "0.1.65" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "095183a3539c7c7649b2beb87c2d3f0591f3a7fed07761cc546d244e27e0238c" 92 | dependencies = [ 93 | "proc-macro2", 94 | "quote", 95 | "syn 1.0.109", 96 | ] 97 | 98 | [[package]] 99 | name = "auto_impl" 100 | version = "1.0.1" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" 103 | dependencies = [ 104 | "proc-macro-error", 105 | "proc-macro2", 106 | "quote", 107 | "syn 1.0.109", 108 | ] 109 | 110 | [[package]] 111 | name = "autocfg" 112 | version = "1.1.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 115 | 116 | [[package]] 117 | name = "bitflags" 118 | version = "1.3.2" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 121 | 122 | [[package]] 123 | name = "bitflags" 124 | version = "2.6.0" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 127 | 128 | [[package]] 129 | name = "bitmaps" 130 | version = "2.1.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" 133 | dependencies = [ 134 | "typenum", 135 | ] 136 | 137 | [[package]] 138 | name = "bytes" 139 | version = "1.4.0" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 142 | 143 | [[package]] 144 | name = "cc" 145 | version = "1.2.1" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" 148 | dependencies = [ 149 | "shlex", 150 | ] 151 | 152 | [[package]] 153 | name = "cfg-if" 154 | version = "1.0.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 157 | 158 | [[package]] 159 | name = "chumsky" 160 | version = "0.9.3" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" 163 | dependencies = [ 164 | "hashbrown", 165 | "stacker", 166 | ] 167 | 168 | [[package]] 169 | name = "colorchoice" 170 | version = "1.0.3" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 173 | 174 | [[package]] 175 | name = "crossbeam-utils" 176 | version = "0.8.20" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 179 | 180 | [[package]] 181 | name = "dashmap" 182 | version = "5.5.3" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" 185 | dependencies = [ 186 | "cfg-if", 187 | "hashbrown", 188 | "lock_api", 189 | "once_cell", 190 | "parking_lot_core", 191 | ] 192 | 193 | [[package]] 194 | name = "dashmap" 195 | version = "6.1.0" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 198 | dependencies = [ 199 | "cfg-if", 200 | "crossbeam-utils", 201 | "hashbrown", 202 | "lock_api", 203 | "once_cell", 204 | "parking_lot_core", 205 | ] 206 | 207 | [[package]] 208 | name = "env_filter" 209 | version = "0.1.2" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" 212 | dependencies = [ 213 | "log", 214 | "regex", 215 | ] 216 | 217 | [[package]] 218 | name = "env_logger" 219 | version = "0.11.5" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" 222 | dependencies = [ 223 | "anstream", 224 | "anstyle", 225 | "env_filter", 226 | "humantime", 227 | "log", 228 | ] 229 | 230 | [[package]] 231 | name = "form_urlencoded" 232 | version = "1.1.0" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 235 | dependencies = [ 236 | "percent-encoding", 237 | ] 238 | 239 | [[package]] 240 | name = "futures" 241 | version = "0.3.26" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" 244 | dependencies = [ 245 | "futures-channel", 246 | "futures-core", 247 | "futures-io", 248 | "futures-sink", 249 | "futures-task", 250 | "futures-util", 251 | ] 252 | 253 | [[package]] 254 | name = "futures-channel" 255 | version = "0.3.26" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" 258 | dependencies = [ 259 | "futures-core", 260 | "futures-sink", 261 | ] 262 | 263 | [[package]] 264 | name = "futures-core" 265 | version = "0.3.26" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" 268 | 269 | [[package]] 270 | name = "futures-io" 271 | version = "0.3.26" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" 274 | 275 | [[package]] 276 | name = "futures-macro" 277 | version = "0.3.26" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" 280 | dependencies = [ 281 | "proc-macro2", 282 | "quote", 283 | "syn 1.0.109", 284 | ] 285 | 286 | [[package]] 287 | name = "futures-sink" 288 | version = "0.3.26" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" 291 | 292 | [[package]] 293 | name = "futures-task" 294 | version = "0.3.26" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" 297 | 298 | [[package]] 299 | name = "futures-util" 300 | version = "0.3.26" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" 303 | dependencies = [ 304 | "futures-channel", 305 | "futures-core", 306 | "futures-io", 307 | "futures-macro", 308 | "futures-sink", 309 | "futures-task", 310 | "memchr", 311 | "pin-project-lite", 312 | "pin-utils", 313 | "slab", 314 | ] 315 | 316 | [[package]] 317 | name = "hashbrown" 318 | version = "0.14.5" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 321 | dependencies = [ 322 | "ahash", 323 | "allocator-api2", 324 | ] 325 | 326 | [[package]] 327 | name = "hermit-abi" 328 | version = "0.2.6" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 331 | dependencies = [ 332 | "libc", 333 | ] 334 | 335 | [[package]] 336 | name = "httparse" 337 | version = "1.8.0" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 340 | 341 | [[package]] 342 | name = "humantime" 343 | version = "2.1.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 346 | 347 | [[package]] 348 | name = "idna" 349 | version = "0.3.0" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 352 | dependencies = [ 353 | "unicode-bidi", 354 | "unicode-normalization", 355 | ] 356 | 357 | [[package]] 358 | name = "im-rc" 359 | version = "15.1.0" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" 362 | dependencies = [ 363 | "bitmaps", 364 | "rand_core", 365 | "rand_xoshiro", 366 | "sized-chunks", 367 | "typenum", 368 | "version_check", 369 | ] 370 | 371 | [[package]] 372 | name = "is_terminal_polyfill" 373 | version = "1.70.1" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 376 | 377 | [[package]] 378 | name = "itoa" 379 | version = "1.0.6" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 382 | 383 | [[package]] 384 | name = "libc" 385 | version = "0.2.164" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" 388 | 389 | [[package]] 390 | name = "lock_api" 391 | version = "0.4.12" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 394 | dependencies = [ 395 | "autocfg", 396 | "scopeguard", 397 | ] 398 | 399 | [[package]] 400 | name = "log" 401 | version = "0.4.22" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 404 | 405 | [[package]] 406 | name = "lsp-types" 407 | version = "0.94.1" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1" 410 | dependencies = [ 411 | "bitflags 1.3.2", 412 | "serde", 413 | "serde_json", 414 | "serde_repr", 415 | "url", 416 | ] 417 | 418 | [[package]] 419 | name = "memchr" 420 | version = "2.5.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 423 | 424 | [[package]] 425 | name = "mio" 426 | version = "0.8.6" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" 429 | dependencies = [ 430 | "libc", 431 | "log", 432 | "wasi", 433 | "windows-sys 0.45.0", 434 | ] 435 | 436 | [[package]] 437 | name = "nrs-language-server" 438 | version = "0.1.0" 439 | dependencies = [ 440 | "anyhow", 441 | "chumsky", 442 | "dashmap 6.1.0", 443 | "env_logger", 444 | "im-rc", 445 | "log", 446 | "oxc_index", 447 | "ropey", 448 | "rust-lapper", 449 | "serde", 450 | "serde_json", 451 | "thiserror", 452 | "tokio", 453 | "tower-lsp", 454 | ] 455 | 456 | [[package]] 457 | name = "num-traits" 458 | version = "0.2.19" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 461 | dependencies = [ 462 | "autocfg", 463 | ] 464 | 465 | [[package]] 466 | name = "num_cpus" 467 | version = "1.15.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 470 | dependencies = [ 471 | "hermit-abi", 472 | "libc", 473 | ] 474 | 475 | [[package]] 476 | name = "once_cell" 477 | version = "1.20.2" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 480 | 481 | [[package]] 482 | name = "oxc_index" 483 | version = "0.36.0" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "ae76739229d0cc5e834e0e3b9e8c4078a86828ff47f5bc71138e4571ce528f83" 486 | 487 | [[package]] 488 | name = "parking_lot" 489 | version = "0.12.1" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 492 | dependencies = [ 493 | "lock_api", 494 | "parking_lot_core", 495 | ] 496 | 497 | [[package]] 498 | name = "parking_lot_core" 499 | version = "0.9.10" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 502 | dependencies = [ 503 | "cfg-if", 504 | "libc", 505 | "redox_syscall", 506 | "smallvec", 507 | "windows-targets 0.52.6", 508 | ] 509 | 510 | [[package]] 511 | name = "percent-encoding" 512 | version = "2.2.0" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 515 | 516 | [[package]] 517 | name = "pin-project" 518 | version = "1.0.12" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" 521 | dependencies = [ 522 | "pin-project-internal", 523 | ] 524 | 525 | [[package]] 526 | name = "pin-project-internal" 527 | version = "1.0.12" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" 530 | dependencies = [ 531 | "proc-macro2", 532 | "quote", 533 | "syn 1.0.109", 534 | ] 535 | 536 | [[package]] 537 | name = "pin-project-lite" 538 | version = "0.2.9" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 541 | 542 | [[package]] 543 | name = "pin-utils" 544 | version = "0.1.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 547 | 548 | [[package]] 549 | name = "proc-macro-error" 550 | version = "1.0.4" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 553 | dependencies = [ 554 | "proc-macro-error-attr", 555 | "proc-macro2", 556 | "quote", 557 | "syn 1.0.109", 558 | "version_check", 559 | ] 560 | 561 | [[package]] 562 | name = "proc-macro-error-attr" 563 | version = "1.0.4" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 566 | dependencies = [ 567 | "proc-macro2", 568 | "quote", 569 | "version_check", 570 | ] 571 | 572 | [[package]] 573 | name = "proc-macro2" 574 | version = "1.0.89" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" 577 | dependencies = [ 578 | "unicode-ident", 579 | ] 580 | 581 | [[package]] 582 | name = "psm" 583 | version = "0.1.24" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" 586 | dependencies = [ 587 | "cc", 588 | ] 589 | 590 | [[package]] 591 | name = "quote" 592 | version = "1.0.36" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 595 | dependencies = [ 596 | "proc-macro2", 597 | ] 598 | 599 | [[package]] 600 | name = "rand_core" 601 | version = "0.6.4" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 604 | 605 | [[package]] 606 | name = "rand_xoshiro" 607 | version = "0.6.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" 610 | dependencies = [ 611 | "rand_core", 612 | ] 613 | 614 | [[package]] 615 | name = "redox_syscall" 616 | version = "0.5.7" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 619 | dependencies = [ 620 | "bitflags 2.6.0", 621 | ] 622 | 623 | [[package]] 624 | name = "regex" 625 | version = "1.7.1" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 628 | dependencies = [ 629 | "aho-corasick", 630 | "memchr", 631 | "regex-syntax", 632 | ] 633 | 634 | [[package]] 635 | name = "regex-syntax" 636 | version = "0.6.28" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 639 | 640 | [[package]] 641 | name = "ropey" 642 | version = "1.6.0" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "53ce7a2c43a32e50d666e33c5a80251b31147bb4b49024bcab11fb6f20c671ed" 645 | dependencies = [ 646 | "smallvec", 647 | "str_indices", 648 | ] 649 | 650 | [[package]] 651 | name = "rust-lapper" 652 | version = "1.1.0" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "ee43d8e721ac803031dbab6a944b957b49a3b11eadbc099880c8aaaebf23ed27" 655 | dependencies = [ 656 | "num-traits", 657 | ] 658 | 659 | [[package]] 660 | name = "ryu" 661 | version = "1.0.13" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 664 | 665 | [[package]] 666 | name = "scopeguard" 667 | version = "1.1.0" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 670 | 671 | [[package]] 672 | name = "serde" 673 | version = "1.0.152" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" 676 | dependencies = [ 677 | "serde_derive", 678 | ] 679 | 680 | [[package]] 681 | name = "serde_derive" 682 | version = "1.0.152" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" 685 | dependencies = [ 686 | "proc-macro2", 687 | "quote", 688 | "syn 1.0.109", 689 | ] 690 | 691 | [[package]] 692 | name = "serde_json" 693 | version = "1.0.93" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" 696 | dependencies = [ 697 | "itoa", 698 | "ryu", 699 | "serde", 700 | ] 701 | 702 | [[package]] 703 | name = "serde_repr" 704 | version = "0.1.10" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" 707 | dependencies = [ 708 | "proc-macro2", 709 | "quote", 710 | "syn 1.0.109", 711 | ] 712 | 713 | [[package]] 714 | name = "shlex" 715 | version = "1.3.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 718 | 719 | [[package]] 720 | name = "signal-hook-registry" 721 | version = "1.4.1" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 724 | dependencies = [ 725 | "libc", 726 | ] 727 | 728 | [[package]] 729 | name = "sized-chunks" 730 | version = "0.6.5" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" 733 | dependencies = [ 734 | "bitmaps", 735 | "typenum", 736 | ] 737 | 738 | [[package]] 739 | name = "slab" 740 | version = "0.4.8" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 743 | dependencies = [ 744 | "autocfg", 745 | ] 746 | 747 | [[package]] 748 | name = "smallvec" 749 | version = "1.10.0" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 752 | 753 | [[package]] 754 | name = "socket2" 755 | version = "0.4.9" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 758 | dependencies = [ 759 | "libc", 760 | "winapi", 761 | ] 762 | 763 | [[package]] 764 | name = "stacker" 765 | version = "0.1.17" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" 768 | dependencies = [ 769 | "cc", 770 | "cfg-if", 771 | "libc", 772 | "psm", 773 | "windows-sys 0.59.0", 774 | ] 775 | 776 | [[package]] 777 | name = "str_indices" 778 | version = "0.4.1" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "5f026164926842ec52deb1938fae44f83dfdb82d0a5b0270c5bd5935ab74d6dd" 781 | 782 | [[package]] 783 | name = "syn" 784 | version = "1.0.109" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 787 | dependencies = [ 788 | "proc-macro2", 789 | "quote", 790 | "unicode-ident", 791 | ] 792 | 793 | [[package]] 794 | name = "syn" 795 | version = "2.0.87" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" 798 | dependencies = [ 799 | "proc-macro2", 800 | "quote", 801 | "unicode-ident", 802 | ] 803 | 804 | [[package]] 805 | name = "thiserror" 806 | version = "2.0.3" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" 809 | dependencies = [ 810 | "thiserror-impl", 811 | ] 812 | 813 | [[package]] 814 | name = "thiserror-impl" 815 | version = "2.0.3" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" 818 | dependencies = [ 819 | "proc-macro2", 820 | "quote", 821 | "syn 2.0.87", 822 | ] 823 | 824 | [[package]] 825 | name = "tinyvec" 826 | version = "1.6.0" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 829 | dependencies = [ 830 | "tinyvec_macros", 831 | ] 832 | 833 | [[package]] 834 | name = "tinyvec_macros" 835 | version = "0.1.1" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 838 | 839 | [[package]] 840 | name = "tokio" 841 | version = "1.26.0" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" 844 | dependencies = [ 845 | "autocfg", 846 | "bytes", 847 | "libc", 848 | "memchr", 849 | "mio", 850 | "num_cpus", 851 | "parking_lot", 852 | "pin-project-lite", 853 | "signal-hook-registry", 854 | "socket2", 855 | "tokio-macros", 856 | "windows-sys 0.45.0", 857 | ] 858 | 859 | [[package]] 860 | name = "tokio-macros" 861 | version = "1.8.2" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" 864 | dependencies = [ 865 | "proc-macro2", 866 | "quote", 867 | "syn 1.0.109", 868 | ] 869 | 870 | [[package]] 871 | name = "tokio-util" 872 | version = "0.7.7" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" 875 | dependencies = [ 876 | "bytes", 877 | "futures-core", 878 | "futures-sink", 879 | "pin-project-lite", 880 | "tokio", 881 | "tracing", 882 | ] 883 | 884 | [[package]] 885 | name = "tower" 886 | version = "0.4.13" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 889 | dependencies = [ 890 | "futures-core", 891 | "futures-util", 892 | "pin-project", 893 | "pin-project-lite", 894 | "tower-layer", 895 | "tower-service", 896 | ] 897 | 898 | [[package]] 899 | name = "tower-layer" 900 | version = "0.3.2" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 903 | 904 | [[package]] 905 | name = "tower-lsp" 906 | version = "0.20.0" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "d4ba052b54a6627628d9b3c34c176e7eda8359b7da9acd497b9f20998d118508" 909 | dependencies = [ 910 | "async-trait", 911 | "auto_impl", 912 | "bytes", 913 | "dashmap 5.5.3", 914 | "futures", 915 | "httparse", 916 | "lsp-types", 917 | "memchr", 918 | "serde", 919 | "serde_json", 920 | "tokio", 921 | "tokio-util", 922 | "tower", 923 | "tower-lsp-macros", 924 | "tracing", 925 | ] 926 | 927 | [[package]] 928 | name = "tower-lsp-macros" 929 | version = "0.9.0" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" 932 | dependencies = [ 933 | "proc-macro2", 934 | "quote", 935 | "syn 2.0.87", 936 | ] 937 | 938 | [[package]] 939 | name = "tower-service" 940 | version = "0.3.2" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 943 | 944 | [[package]] 945 | name = "tracing" 946 | version = "0.1.37" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 949 | dependencies = [ 950 | "cfg-if", 951 | "pin-project-lite", 952 | "tracing-attributes", 953 | "tracing-core", 954 | ] 955 | 956 | [[package]] 957 | name = "tracing-attributes" 958 | version = "0.1.23" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" 961 | dependencies = [ 962 | "proc-macro2", 963 | "quote", 964 | "syn 1.0.109", 965 | ] 966 | 967 | [[package]] 968 | name = "tracing-core" 969 | version = "0.1.30" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 972 | dependencies = [ 973 | "once_cell", 974 | ] 975 | 976 | [[package]] 977 | name = "typenum" 978 | version = "1.16.0" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 981 | 982 | [[package]] 983 | name = "unicode-bidi" 984 | version = "0.3.10" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" 987 | 988 | [[package]] 989 | name = "unicode-ident" 990 | version = "1.0.7" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7" 993 | 994 | [[package]] 995 | name = "unicode-normalization" 996 | version = "0.1.22" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 999 | dependencies = [ 1000 | "tinyvec", 1001 | ] 1002 | 1003 | [[package]] 1004 | name = "url" 1005 | version = "2.3.1" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 1008 | dependencies = [ 1009 | "form_urlencoded", 1010 | "idna", 1011 | "percent-encoding", 1012 | "serde", 1013 | ] 1014 | 1015 | [[package]] 1016 | name = "utf8parse" 1017 | version = "0.2.2" 1018 | source = "registry+https://github.com/rust-lang/crates.io-index" 1019 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1020 | 1021 | [[package]] 1022 | name = "version_check" 1023 | version = "0.9.4" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1026 | 1027 | [[package]] 1028 | name = "wasi" 1029 | version = "0.11.0+wasi-snapshot-preview1" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1032 | 1033 | [[package]] 1034 | name = "winapi" 1035 | version = "0.3.9" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1038 | dependencies = [ 1039 | "winapi-i686-pc-windows-gnu", 1040 | "winapi-x86_64-pc-windows-gnu", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "winapi-i686-pc-windows-gnu" 1045 | version = "0.4.0" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1048 | 1049 | [[package]] 1050 | name = "winapi-x86_64-pc-windows-gnu" 1051 | version = "0.4.0" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1054 | 1055 | [[package]] 1056 | name = "windows-sys" 1057 | version = "0.45.0" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1060 | dependencies = [ 1061 | "windows-targets 0.42.1", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "windows-sys" 1066 | version = "0.59.0" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1069 | dependencies = [ 1070 | "windows-targets 0.52.6", 1071 | ] 1072 | 1073 | [[package]] 1074 | name = "windows-targets" 1075 | version = "0.42.1" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" 1078 | dependencies = [ 1079 | "windows_aarch64_gnullvm 0.42.1", 1080 | "windows_aarch64_msvc 0.42.1", 1081 | "windows_i686_gnu 0.42.1", 1082 | "windows_i686_msvc 0.42.1", 1083 | "windows_x86_64_gnu 0.42.1", 1084 | "windows_x86_64_gnullvm 0.42.1", 1085 | "windows_x86_64_msvc 0.42.1", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "windows-targets" 1090 | version = "0.52.6" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1093 | dependencies = [ 1094 | "windows_aarch64_gnullvm 0.52.6", 1095 | "windows_aarch64_msvc 0.52.6", 1096 | "windows_i686_gnu 0.52.6", 1097 | "windows_i686_gnullvm", 1098 | "windows_i686_msvc 0.52.6", 1099 | "windows_x86_64_gnu 0.52.6", 1100 | "windows_x86_64_gnullvm 0.52.6", 1101 | "windows_x86_64_msvc 0.52.6", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "windows_aarch64_gnullvm" 1106 | version = "0.42.1" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 1109 | 1110 | [[package]] 1111 | name = "windows_aarch64_gnullvm" 1112 | version = "0.52.6" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1115 | 1116 | [[package]] 1117 | name = "windows_aarch64_msvc" 1118 | version = "0.42.1" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 1121 | 1122 | [[package]] 1123 | name = "windows_aarch64_msvc" 1124 | version = "0.52.6" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1127 | 1128 | [[package]] 1129 | name = "windows_i686_gnu" 1130 | version = "0.42.1" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 1133 | 1134 | [[package]] 1135 | name = "windows_i686_gnu" 1136 | version = "0.52.6" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1139 | 1140 | [[package]] 1141 | name = "windows_i686_gnullvm" 1142 | version = "0.52.6" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1145 | 1146 | [[package]] 1147 | name = "windows_i686_msvc" 1148 | version = "0.42.1" 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" 1150 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 1151 | 1152 | [[package]] 1153 | name = "windows_i686_msvc" 1154 | version = "0.52.6" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1157 | 1158 | [[package]] 1159 | name = "windows_x86_64_gnu" 1160 | version = "0.42.1" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 1163 | 1164 | [[package]] 1165 | name = "windows_x86_64_gnu" 1166 | version = "0.52.6" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1169 | 1170 | [[package]] 1171 | name = "windows_x86_64_gnullvm" 1172 | version = "0.42.1" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 1175 | 1176 | [[package]] 1177 | name = "windows_x86_64_gnullvm" 1178 | version = "0.52.6" 1179 | source = "registry+https://github.com/rust-lang/crates.io-index" 1180 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1181 | 1182 | [[package]] 1183 | name = "windows_x86_64_msvc" 1184 | version = "0.42.1" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 1187 | 1188 | [[package]] 1189 | name = "windows_x86_64_msvc" 1190 | version = "0.52.6" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1193 | 1194 | [[package]] 1195 | name = "zerocopy" 1196 | version = "0.7.35" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 1199 | dependencies = [ 1200 | "zerocopy-derive", 1201 | ] 1202 | 1203 | [[package]] 1204 | name = "zerocopy-derive" 1205 | version = "0.7.35" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1208 | dependencies = [ 1209 | "proc-macro2", 1210 | "quote", 1211 | "syn 2.0.87", 1212 | ] 1213 | --------------------------------------------------------------------------------