| undefined {
65 | if (!client) {
66 | return undefined;
67 | }
68 | return client.stop();
69 | }
70 |
71 | function getServer(_output: vscode.OutputChannel, cwd: string): { valid: boolean, name: string } {
72 | try {
73 | // let name = "/home/uros/.cache/cargo/target/debug/jinja-lsp";
74 | let name = path.join(cwd, "media", binaryName);
75 | const validation = child_process.spawnSync(name);
76 | if (validation.status === 0) {
77 | return { valid: true, name: name };
78 | }
79 | else {
80 | return { valid: false, name: "Jinja language server not installed." }
81 | }
82 |
83 | }
84 | catch (e) {
85 | return { valid: false, name: "Jinja language server not installed." }
86 | }
87 | }
88 |
89 |
--------------------------------------------------------------------------------
/editors/code/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es2020",
5 | "lib": [
6 | "es2020"
7 | ],
8 | "outDir": "out",
9 | "rootDir": "src",
10 | "sourceMap": true
11 | },
12 | "include": [
13 | "src"
14 | ],
15 | "exclude": [
16 | "node_modules",
17 | ".vscode-test"
18 | ]
19 | }
--------------------------------------------------------------------------------
/editors/code/media/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uros-5/jinja-lsp/ef8025bed811a2b282c50edfd53a4230fc93b4f9/editors/code/media/icon.png
--------------------------------------------------------------------------------
/editors/code/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jinja-lsp",
3 | "description": "jinja-lsp",
4 | "license": "MIT",
5 | "version": "0.1.86",
6 | "repository": {
7 | "url": "https://github.com/uros-5/jinja-lsp"
8 | },
9 | "icon": "media/icon.png",
10 | "publisher": "urosmrkobrada",
11 | "categories": [
12 | "Linters",
13 | "Programming Languages",
14 | "Other"
15 | ],
16 | "keywords": [
17 | "multi-root ready",
18 | "jinja",
19 | "minijinja",
20 | "rust",
21 | "jinja-lsp",
22 | "python"
23 | ],
24 | "engines": {
25 | "vscode": "^1.75.0"
26 | },
27 | "activationEvents": [
28 | "onLanguage:jinja-html",
29 | "onLanguage:rust",
30 | "onLanguage:python"
31 | ],
32 | "main": "dist/extension",
33 | "contributes": {
34 | "configuration": {
35 | "type": "object",
36 | "title": "Example configuration",
37 | "properties": {
38 | "jinja-lsp.templates": {
39 | "type": "string",
40 | "description": "Templates directory"
41 | },
42 | "jinja-lsp.backend": {
43 | "type": "array",
44 | "description": "Backend directories"
45 | },
46 | "jinja-lsp.lang": {
47 | "type": "string",
48 | "enum": [
49 | "rust",
50 | "python"
51 | ],
52 | "description": "Language that is used on backend"
53 | },
54 | "jinja-lsp.hide_undefined": {
55 | "type": "boolean",
56 | "description": "Disable warnings about undefined variables"
57 | },
58 | "jinja-lsp.template_extension": {
59 | "type": "array",
60 | "default": [
61 | "html",
62 | "jinja",
63 | "j2"
64 | ],
65 | "description": "Add custom template extension"
66 | }
67 | }
68 | }
69 | },
70 | "scripts": {
71 | "vscode:prepublish": "npm run esbuild-base -- --minify",
72 | "esbuild-base": "esbuild ./client/src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node",
73 | "esbuild": "npm run esbuild-base -- --sourcemap",
74 | "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
75 | "test-compile": "tsc -p ./",
76 | "compile": "tsc -b",
77 | "watch": "tsc -b -w",
78 | "lint": "eslint ./client/src --ext .ts,.tsx",
79 | "test": "sh ./scripts/e2e.sh",
80 | "test2": "vscode-test"
81 | },
82 | "devDependencies": {
83 | "@types/mocha": "^10.0.6",
84 | "@types/node": "^18.14.6",
85 | "@typescript-eslint/eslint-plugin": "^7.1.0",
86 | "@typescript-eslint/parser": "^7.1.0",
87 | "@vscode/test-cli": "^0.0.9",
88 | "@vscode/test-electron": "^2.3.9",
89 | "esbuild": "0.20.2",
90 | "eslint": "^8.57.0",
91 | "mocha": "^10.3.0",
92 | "typescript": "5.3.3"
93 | }
94 | }
--------------------------------------------------------------------------------
/editors/code/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "es2020",
4 | "target": "es2020",
5 | "lib": [
6 | "ES2020"
7 | ],
8 | "outDir": "out",
9 | "rootDir": "src",
10 | "sourceMap": true
11 | },
12 | "include": [
13 | "src"
14 | ],
15 | "exclude": [
16 | "node_modules",
17 | ".vscode-test"
18 | ],
19 | "references": [
20 | {
21 | "path": "./client"
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/editors/code/webpack.config.js:
--------------------------------------------------------------------------------
1 | //@ts-check
2 |
3 | "use strict";
4 |
5 | const path = require("path");
6 |
7 | /**@type {import('webpack').Configuration}*/
8 | const config = {
9 | target: "webworker", // vscode extensions run in webworker context for VS Code web 📖 -> https://webpack.js.org/configuration/target/#target
10 |
11 | entry: "./client/src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
12 | output: {
13 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
14 | path: path.resolve(__dirname, "dist"),
15 | filename: "extension.js",
16 | libraryTarget: "commonjs2",
17 | devtoolModuleFilenameTemplate: "../[resource-path]",
18 | },
19 | devtool: "hidden-source-map",
20 | externals: {
21 | vscode:
22 | "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
23 | },
24 | resolve: {
25 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
26 | mainFields: ["browser", "module", "main"], // look for `browser` entry point in imported node modules
27 | extensions: [".ts", ".js"],
28 | alias: {},
29 | },
30 | module: {
31 | rules: [
32 | {
33 | test: /\.ts$/,
34 | exclude: /node_modules/,
35 | use: [
36 | {
37 | loader: "ts-loader",
38 | },
39 | ],
40 | },
41 | ],
42 | },
43 | };
44 | module.exports = config;
45 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # python generated files
2 | __pycache__/
3 | *.py[oc]
4 | build/
5 | dist/
6 | wheels/
7 | *.egg-info
8 |
9 | # venv
10 | .venv
11 | venv
12 |
--------------------------------------------------------------------------------
/example/.python-version:
--------------------------------------------------------------------------------
1 | 3.10.13
2 |
--------------------------------------------------------------------------------
/example/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 |
3 | name = "example"
4 | version = "0.1.0"
5 | edition = "2021"
6 |
7 | [dependencies]
8 | minijinja = "1.0.10"
9 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | ```bash
2 | # from base directory(jinja-lsp)
3 | cd example
4 | git init .
5 | ```
6 |
--------------------------------------------------------------------------------
/example/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "example"
3 | version = "0.1.0"
4 | description = "Simple jinja-lsp example for Python"
5 | authors = [
6 | { name = "uros-5"}
7 | ]
8 | dependencies = [
9 | "jinja2>=3.1.4",
10 | ]
11 | readme = "README.md"
12 | requires-python = ">= 3.10"
13 |
14 | [build-system]
15 | requires = ["hatchling"]
16 | build-backend = "hatchling.build"
17 |
18 | [tool.rye]
19 | managed = true
20 | dev-dependencies = []
21 |
22 | [tool.hatch.metadata]
23 | allow-direct-references = true
24 |
25 | [tool.hatch.build.targets.wheel]
26 | packages = ["src"]
27 |
28 | [tool.pyright]
29 | extraPaths = ["example/.venv/lib/python3.10/site-packages"]
30 |
31 |
32 | [tool.jinja-lsp]
33 | hide_undefined = false
34 |
--------------------------------------------------------------------------------
/example/requirements-dev.lock:
--------------------------------------------------------------------------------
1 | # generated by rye
2 | # use `rye lock` or `rye sync` to update this lockfile
3 | #
4 | # last locked with the following flags:
5 | # pre: false
6 | # features: []
7 | # all-features: false
8 | # with-sources: false
9 |
10 | -e file:.
11 | jinja2==3.1.4
12 | # via example
13 | markupsafe==2.1.5
14 | # via jinja2
15 |
--------------------------------------------------------------------------------
/example/requirements.lock:
--------------------------------------------------------------------------------
1 | # generated by rye
2 | # use `rye lock` or `rye sync` to update this lockfile
3 | #
4 | # last locked with the following flags:
5 | # pre: false
6 | # features: []
7 | # all-features: false
8 | # with-sources: false
9 |
10 | -e file:.
11 | jinja2==3.1.4
12 | # via example
13 | markupsafe==2.1.5
14 | # via jinja2
15 |
--------------------------------------------------------------------------------
/example/src/main.py:
--------------------------------------------------------------------------------
1 | from jinja2 import Environment
2 |
3 |
4 | def main():
5 | jinja_env = Environment()
6 |
7 | template = jinja_env.get_template("account.jinja")
8 | template.render(
9 | first_name="John",
10 | last_name="Doe",
11 | email="johndoe@example.com",
12 | phone_number="(123) 456-7890",
13 | street="123 Main St",
14 | city="Dallas",
15 | header_info="This is some information about the user.",
16 | )
17 |
18 | jinja_env.globals["PROJECT_NAME"] = "example"
19 |
--------------------------------------------------------------------------------
/example/src/main.rs:
--------------------------------------------------------------------------------
1 | use minijinja::{context, Environment};
2 |
3 | fn main() {
4 | let mut jinja = Environment::new();
5 | let _user = context! {
6 | first_name => "John",
7 | last_name => "Doe",
8 | email => "johndoe@example.com",
9 | phone_number => "(123) 456-7890",
10 | street => "123 Main St",
11 | city => "Dallas",
12 | header_info => "This is some information about the user.",
13 | };
14 | jinja.add_global("PROJECT_NAME", "Example");
15 | }
16 |
--------------------------------------------------------------------------------
/example/templates/account.jinja:
--------------------------------------------------------------------------------
1 |
2 | {% include './templates/header.jinja' %}
3 | {{ hello_world() }}
4 | {% set abc = 55 %}
5 |
6 |
7 |
8 |
-
9 | Full name
10 |
11 | -
12 | {{ first_name }} {{ last_name }}
13 |
14 |
15 |
16 |
-
17 | Email address
18 |
19 | -
20 | {{ email }}
21 |
22 |
23 |
24 |
-
25 | Phone number
26 |
27 | -
28 | {{ phone_number }}
29 |
30 |
31 |
32 |
-
33 | Address
34 |
35 | -
36 | {{ street }}
37 | {{ city | capitalize }}, USA 12345
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/example/templates/header.jinja:
--------------------------------------------------------------------------------
1 |
2 |
3 | User Profile
4 |
5 |
6 | {{ header_info | capitalize }}
7 | {{ additional_info }}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/templates/hello.jinja:
--------------------------------------------------------------------------------
1 | {% macro hello_world() -%}
2 | hello world
3 | {{ PROJECT_NAME | length }}
4 | {% endmacro %}
5 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [target.aarch64-unknown-linux-musl]
2 | linker = "aarch64-linux-musl-gcc"
3 | rustflags = ["-C", "target-feature=-crt-static"]
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/node
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node
3 |
4 | ### Node ###
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # Diagnostic reports (https://nodejs.org/api/report.html)
14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 | *.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 | *.lcov
28 |
29 | # nyc test coverage
30 | .nyc_output
31 |
32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
33 | .grunt
34 |
35 | # Bower dependency directory (https://bower.io/)
36 | bower_components
37 |
38 | # node-waf configuration
39 | .lock-wscript
40 |
41 | # Compiled binary addons (https://nodejs.org/api/addons.html)
42 | build/Release
43 |
44 | # Dependency directories
45 | node_modules/
46 | jspm_packages/
47 |
48 | # TypeScript v1 declaration files
49 | typings/
50 |
51 | # TypeScript cache
52 | *.tsbuildinfo
53 |
54 | # Optional npm cache directory
55 | .npm
56 |
57 | # Optional eslint cache
58 | .eslintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variables file
76 | .env
77 | .env.test
78 |
79 | # parcel-bundler cache (https://parceljs.org/)
80 | .cache
81 |
82 | # Next.js build output
83 | .next
84 |
85 | # Nuxt.js build / generate output
86 | .nuxt
87 | dist
88 |
89 | # Gatsby files
90 | .cache/
91 | # Comment in the public line in if your project uses Gatsby and not Next.js
92 | # https://nextjs.org/blog/next-9-1#public-directory-support
93 | # public
94 |
95 | # vuepress build output
96 | .vuepress/dist
97 |
98 | # Serverless directories
99 | .serverless/
100 |
101 | # FuseBox cache
102 | .fusebox/
103 |
104 | # DynamoDB Local files
105 | .dynamodb/
106 |
107 | # TernJS port file
108 | .tern-port
109 |
110 | # Stores VSCode versions used for testing VSCode extensions
111 | .vscode-test
112 |
113 | # End of https://www.toptal.com/developers/gitignore/api/node
114 |
115 | # Created by https://www.toptal.com/developers/gitignore/api/macos
116 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos
117 |
118 | ### macOS ###
119 | # General
120 | .DS_Store
121 | .AppleDouble
122 | .LSOverride
123 |
124 | # Icon must end with two
125 | Icon
126 |
127 |
128 | # Thumbnails
129 | ._*
130 |
131 | # Files that might appear in the root of a volume
132 | .DocumentRevisions-V100
133 | .fseventsd
134 | .Spotlight-V100
135 | .TemporaryItems
136 | .Trashes
137 | .VolumeIcon.icns
138 | .com.apple.timemachine.donotpresent
139 |
140 | # Directories potentially created on remote AFP share
141 | .AppleDB
142 | .AppleDesktop
143 | Network Trash Folder
144 | Temporary Items
145 | .apdisk
146 |
147 | ### macOS Patch ###
148 | # iCloud generated files
149 | *.icloud
150 |
151 | # End of https://www.toptal.com/developers/gitignore/api/macos
152 |
153 | # Created by https://www.toptal.com/developers/gitignore/api/windows
154 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows
155 |
156 | ### Windows ###
157 | # Windows thumbnail cache files
158 | Thumbs.db
159 | Thumbs.db:encryptable
160 | ehthumbs.db
161 | ehthumbs_vista.db
162 |
163 | # Dump file
164 | *.stackdump
165 |
166 | # Folder config file
167 | [Dd]esktop.ini
168 |
169 | # Recycle Bin used on file shares
170 | $RECYCLE.BIN/
171 |
172 | # Windows Installer files
173 | *.cab
174 | *.msi
175 | *.msix
176 | *.msm
177 | *.msp
178 |
179 | # Windows shortcuts
180 | *.lnk
181 |
182 | # End of https://www.toptal.com/developers/gitignore/api/windows
183 |
184 | #Added by cargo
185 |
186 | /target
187 | Cargo.lock
188 |
189 | .pnp.*
190 | .yarn/*
191 | !.yarn/patches
192 | !.yarn/plugins
193 | !.yarn/releases
194 | !.yarn/sdks
195 | !.yarn/versions
196 |
197 | *.node
198 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/.npmignore:
--------------------------------------------------------------------------------
1 | target
2 | Cargo.lock
3 | .cargo
4 | .github
5 | npm
6 | .eslintrc
7 | .prettierignore
8 | rustfmt.toml
9 | yarn.lock
10 | *.node
11 | .yarn
12 | __test__
13 | renovate.json
14 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | yarnPath: .yarn/releases/yarn-4.2.2.cjs
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | edition = "2021"
3 | name = "jinja-lsp-nodejs"
4 | version = "0.1.85"
5 | license = "MIT"
6 | authors = ["uros-5"]
7 | description = "Bindings for jinja-lsp"
8 |
9 | [lib]
10 | crate-type = ["cdylib"]
11 |
12 | [dependencies]
13 | # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
14 | napi = { version = "2.12.2", default-features = false, features = ["napi4"] }
15 | napi-derive = "2.12.2"
16 | jinja-lsp-queries = { path = "../jinja-lsp-queries", version = "0.1.85"}
17 | jinja-lsp = { path = "../jinja-lsp", version = "0.1.83"}
18 | tree-sitter = "0.23.0"
19 | tower-lsp = { version = "0.20.0", features = ["proposed"] }
20 | ropey = "1.5.0"
21 | tree-sitter-language = "0.1.0"
22 |
23 | [build-dependencies]
24 | napi-build = "2.0.1"
25 |
26 | [profile.release]
27 | lto = true
28 | strip = "symbols"
29 | opt-level = 3
30 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/__test__/index.spec.mjs:
--------------------------------------------------------------------------------
1 | import test from 'ava'
2 |
3 | import { NodejsLspFiles } from '../index.js'
4 |
5 | test('main class', (t) => {
6 | t.is(new NodejsLspFiles().getVariables("id", 11), null);
7 | })
8 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/build.rs:
--------------------------------------------------------------------------------
1 | extern crate napi_build;
2 |
3 | fn main() {
4 | napi_build::setup();
5 | }
6 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/example.mjs:
--------------------------------------------------------------------------------
1 | import { sum } from './index.js'
2 | console.log(sum(1,2))
3 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/index.d.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 |
4 | /* auto-generated by NAPI-RS */
5 |
6 | export function basic(content: string): number | null
7 | export interface JsPosition {
8 | line: number
9 | character: number
10 | }
11 | export const enum JsIdentifierType {
12 | ForLoopKey = 0,
13 | ForLoopValue = 1,
14 | ForLoopCount = 2,
15 | SetVariable = 3,
16 | WithVariable = 4,
17 | MacroName = 5,
18 | MacroParameter = 6,
19 | TemplateBlock = 7,
20 | BackendVariable = 8,
21 | UndefinedVariable = 9,
22 | JinjaTemplate = 10,
23 | Link = 11
24 | }
25 | export interface JsIdentifier {
26 | start: JsPosition
27 | end: JsPosition
28 | name: string
29 | identifierType: JsIdentifierType
30 | error?: string
31 | }
32 | export interface JsHover {
33 | kind: string
34 | value: string
35 | range?: JsRange
36 | label?: string
37 | documentaion?: string
38 | }
39 | export interface JsRange {
40 | start: JsPosition
41 | end: JsPosition
42 | }
43 | export interface JsLocation {
44 | uri: string
45 | range: JsRange
46 | isBackend: boolean
47 | name: string
48 | }
49 | export interface JsCompletionItem {
50 | completionType: JsCompletionType
51 | label: string
52 | kind: Kind2
53 | description: string
54 | newText?: string
55 | insert?: JsRange
56 | replace?: JsRange
57 | }
58 | export const enum Kind2 {
59 | VARIABLE = 0,
60 | FIELD = 1,
61 | FUNCTION = 2,
62 | MODULE = 3,
63 | CONSTANT = 4,
64 | FILE = 5,
65 | TEXT = 6
66 | }
67 | export const enum JsCompletionType {
68 | Filter = 0,
69 | Identifier = 1,
70 | Snippets = 2
71 | }
72 | export interface Action {
73 | name: string
74 | description: string
75 | }
76 | export class NodejsLspFiles {
77 | constructor()
78 | /** Actions can come from unsaved context. */
79 | addLinkHints(uri: string, actions?: Array | undefined | null): void
80 | saveLinkHint(actions?: Array | undefined | null, hint?: string | undefined | null): void
81 | removeTempLinkHint(hint?: string | undefined | null): void
82 | deleteAll(filename: string): void
83 | addOne(id: number, filename: string, content: string, line: number, ext: string, col?: number | undefined | null): Array
84 | getVariables(id: string, line: number): Array | null
85 | hover(id: number, filename: string, line: number, position: JsPosition): JsHover | null
86 | complete(id: number, filename: string, line: number, position: JsPosition): Array | null
87 | gotoDefinition(id: number, filename: string, line: number, position: JsPosition): Array | null
88 | }
89 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/index.js:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 | /* prettier-ignore */
4 |
5 | /* auto-generated by NAPI-RS */
6 |
7 | const { existsSync, readFileSync } = require('fs')
8 | const { join } = require('path')
9 |
10 | const { platform, arch } = process
11 |
12 | let nativeBinding = null
13 | let localFileExisted = false
14 | let loadError = null
15 |
16 | function isMusl() {
17 | // For Node 10
18 | if (!process.report || typeof process.report.getReport !== 'function') {
19 | try {
20 | const lddPath = require('child_process').execSync('which ldd').toString().trim()
21 | return readFileSync(lddPath, 'utf8').includes('musl')
22 | } catch (e) {
23 | return true
24 | }
25 | } else {
26 | const { glibcVersionRuntime } = process.report.getReport().header
27 | return !glibcVersionRuntime
28 | }
29 | }
30 |
31 | switch (platform) {
32 | case 'android':
33 | switch (arch) {
34 | case 'arm64':
35 | localFileExisted = existsSync(join(__dirname, 'functions.android-arm64.node'))
36 | try {
37 | if (localFileExisted) {
38 | nativeBinding = require('./functions.android-arm64.node')
39 | } else {
40 | nativeBinding = require('@jinja-lsp/functions-android-arm64')
41 | }
42 | } catch (e) {
43 | loadError = e
44 | }
45 | break
46 | case 'arm':
47 | localFileExisted = existsSync(join(__dirname, 'functions.android-arm-eabi.node'))
48 | try {
49 | if (localFileExisted) {
50 | nativeBinding = require('./functions.android-arm-eabi.node')
51 | } else {
52 | nativeBinding = require('@jinja-lsp/functions-android-arm-eabi')
53 | }
54 | } catch (e) {
55 | loadError = e
56 | }
57 | break
58 | default:
59 | throw new Error(`Unsupported architecture on Android ${arch}`)
60 | }
61 | break
62 | case 'win32':
63 | switch (arch) {
64 | case 'x64':
65 | localFileExisted = existsSync(
66 | join(__dirname, 'functions.win32-x64-msvc.node')
67 | )
68 | try {
69 | if (localFileExisted) {
70 | nativeBinding = require('./functions.win32-x64-msvc.node')
71 | } else {
72 | nativeBinding = require('@jinja-lsp/functions-win32-x64-msvc')
73 | }
74 | } catch (e) {
75 | loadError = e
76 | }
77 | break
78 | case 'ia32':
79 | localFileExisted = existsSync(
80 | join(__dirname, 'functions.win32-ia32-msvc.node')
81 | )
82 | try {
83 | if (localFileExisted) {
84 | nativeBinding = require('./functions.win32-ia32-msvc.node')
85 | } else {
86 | nativeBinding = require('@jinja-lsp/functions-win32-ia32-msvc')
87 | }
88 | } catch (e) {
89 | loadError = e
90 | }
91 | break
92 | case 'arm64':
93 | localFileExisted = existsSync(
94 | join(__dirname, 'functions.win32-arm64-msvc.node')
95 | )
96 | try {
97 | if (localFileExisted) {
98 | nativeBinding = require('./functions.win32-arm64-msvc.node')
99 | } else {
100 | nativeBinding = require('@jinja-lsp/functions-win32-arm64-msvc')
101 | }
102 | } catch (e) {
103 | loadError = e
104 | }
105 | break
106 | default:
107 | throw new Error(`Unsupported architecture on Windows: ${arch}`)
108 | }
109 | break
110 | case 'darwin':
111 | localFileExisted = existsSync(join(__dirname, 'functions.darwin-universal.node'))
112 | try {
113 | if (localFileExisted) {
114 | nativeBinding = require('./functions.darwin-universal.node')
115 | } else {
116 | nativeBinding = require('@jinja-lsp/functions-darwin-universal')
117 | }
118 | break
119 | } catch {}
120 | switch (arch) {
121 | case 'x64':
122 | localFileExisted = existsSync(join(__dirname, 'functions.darwin-x64.node'))
123 | try {
124 | if (localFileExisted) {
125 | nativeBinding = require('./functions.darwin-x64.node')
126 | } else {
127 | nativeBinding = require('@jinja-lsp/functions-darwin-x64')
128 | }
129 | } catch (e) {
130 | loadError = e
131 | }
132 | break
133 | case 'arm64':
134 | localFileExisted = existsSync(
135 | join(__dirname, 'functions.darwin-arm64.node')
136 | )
137 | try {
138 | if (localFileExisted) {
139 | nativeBinding = require('./functions.darwin-arm64.node')
140 | } else {
141 | nativeBinding = require('@jinja-lsp/functions-darwin-arm64')
142 | }
143 | } catch (e) {
144 | loadError = e
145 | }
146 | break
147 | default:
148 | throw new Error(`Unsupported architecture on macOS: ${arch}`)
149 | }
150 | break
151 | case 'freebsd':
152 | if (arch !== 'x64') {
153 | throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154 | }
155 | localFileExisted = existsSync(join(__dirname, 'functions.freebsd-x64.node'))
156 | try {
157 | if (localFileExisted) {
158 | nativeBinding = require('./functions.freebsd-x64.node')
159 | } else {
160 | nativeBinding = require('@jinja-lsp/functions-freebsd-x64')
161 | }
162 | } catch (e) {
163 | loadError = e
164 | }
165 | break
166 | case 'linux':
167 | switch (arch) {
168 | case 'x64':
169 | if (isMusl()) {
170 | localFileExisted = existsSync(
171 | join(__dirname, 'functions.linux-x64-musl.node')
172 | )
173 | try {
174 | if (localFileExisted) {
175 | nativeBinding = require('./functions.linux-x64-musl.node')
176 | } else {
177 | nativeBinding = require('@jinja-lsp/functions-linux-x64-musl')
178 | }
179 | } catch (e) {
180 | loadError = e
181 | }
182 | } else {
183 | localFileExisted = existsSync(
184 | join(__dirname, 'functions.linux-x64-gnu.node')
185 | )
186 | try {
187 | if (localFileExisted) {
188 | nativeBinding = require('./functions.linux-x64-gnu.node')
189 | } else {
190 | nativeBinding = require('@jinja-lsp/functions-linux-x64-gnu')
191 | }
192 | } catch (e) {
193 | loadError = e
194 | }
195 | }
196 | break
197 | case 'arm64':
198 | if (isMusl()) {
199 | localFileExisted = existsSync(
200 | join(__dirname, 'functions.linux-arm64-musl.node')
201 | )
202 | try {
203 | if (localFileExisted) {
204 | nativeBinding = require('./functions.linux-arm64-musl.node')
205 | } else {
206 | nativeBinding = require('@jinja-lsp/functions-linux-arm64-musl')
207 | }
208 | } catch (e) {
209 | loadError = e
210 | }
211 | } else {
212 | localFileExisted = existsSync(
213 | join(__dirname, 'functions.linux-arm64-gnu.node')
214 | )
215 | try {
216 | if (localFileExisted) {
217 | nativeBinding = require('./functions.linux-arm64-gnu.node')
218 | } else {
219 | nativeBinding = require('@jinja-lsp/functions-linux-arm64-gnu')
220 | }
221 | } catch (e) {
222 | loadError = e
223 | }
224 | }
225 | break
226 | case 'arm':
227 | if (isMusl()) {
228 | localFileExisted = existsSync(
229 | join(__dirname, 'functions.linux-arm-musleabihf.node')
230 | )
231 | try {
232 | if (localFileExisted) {
233 | nativeBinding = require('./functions.linux-arm-musleabihf.node')
234 | } else {
235 | nativeBinding = require('@jinja-lsp/functions-linux-arm-musleabihf')
236 | }
237 | } catch (e) {
238 | loadError = e
239 | }
240 | } else {
241 | localFileExisted = existsSync(
242 | join(__dirname, 'functions.linux-arm-gnueabihf.node')
243 | )
244 | try {
245 | if (localFileExisted) {
246 | nativeBinding = require('./functions.linux-arm-gnueabihf.node')
247 | } else {
248 | nativeBinding = require('@jinja-lsp/functions-linux-arm-gnueabihf')
249 | }
250 | } catch (e) {
251 | loadError = e
252 | }
253 | }
254 | break
255 | case 'riscv64':
256 | if (isMusl()) {
257 | localFileExisted = existsSync(
258 | join(__dirname, 'functions.linux-riscv64-musl.node')
259 | )
260 | try {
261 | if (localFileExisted) {
262 | nativeBinding = require('./functions.linux-riscv64-musl.node')
263 | } else {
264 | nativeBinding = require('@jinja-lsp/functions-linux-riscv64-musl')
265 | }
266 | } catch (e) {
267 | loadError = e
268 | }
269 | } else {
270 | localFileExisted = existsSync(
271 | join(__dirname, 'functions.linux-riscv64-gnu.node')
272 | )
273 | try {
274 | if (localFileExisted) {
275 | nativeBinding = require('./functions.linux-riscv64-gnu.node')
276 | } else {
277 | nativeBinding = require('@jinja-lsp/functions-linux-riscv64-gnu')
278 | }
279 | } catch (e) {
280 | loadError = e
281 | }
282 | }
283 | break
284 | case 's390x':
285 | localFileExisted = existsSync(
286 | join(__dirname, 'functions.linux-s390x-gnu.node')
287 | )
288 | try {
289 | if (localFileExisted) {
290 | nativeBinding = require('./functions.linux-s390x-gnu.node')
291 | } else {
292 | nativeBinding = require('@jinja-lsp/functions-linux-s390x-gnu')
293 | }
294 | } catch (e) {
295 | loadError = e
296 | }
297 | break
298 | default:
299 | throw new Error(`Unsupported architecture on Linux: ${arch}`)
300 | }
301 | break
302 | default:
303 | throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304 | }
305 |
306 | if (!nativeBinding) {
307 | if (loadError) {
308 | throw loadError
309 | }
310 | throw new Error(`Failed to load native binding`)
311 | }
312 |
313 | const { basic, NodejsLspFiles, JsIdentifierType, Kind2, JsCompletionType } = nativeBinding
314 |
315 | module.exports.basic = basic
316 | module.exports.NodejsLspFiles = NodejsLspFiles
317 | module.exports.JsIdentifierType = JsIdentifierType
318 | module.exports.Kind2 = Kind2
319 | module.exports.JsCompletionType = JsCompletionType
320 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/android-arm-eabi/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-android-arm-eabi`
2 |
3 | This is the **armv7-linux-androideabi** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/android-arm-eabi/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-android-arm-eabi",
3 | "version": "0.0.22",
4 | "os": [
5 | "android"
6 | ],
7 | "cpu": [
8 | "arm"
9 | ],
10 | "main": "functions.android-arm-eabi.node",
11 | "files": [
12 | "functions.android-arm-eabi.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/android-arm64/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-android-arm64`
2 |
3 | This is the **aarch64-linux-android** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/android-arm64/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-android-arm64",
3 | "version": "0.0.22",
4 | "os": [
5 | "android"
6 | ],
7 | "cpu": [
8 | "arm64"
9 | ],
10 | "main": "functions.android-arm64.node",
11 | "files": [
12 | "functions.android-arm64.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/darwin-arm64/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-darwin-arm64`
2 |
3 | This is the **aarch64-apple-darwin** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/darwin-arm64/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-darwin-arm64",
3 | "version": "0.0.22",
4 | "os": [
5 | "darwin"
6 | ],
7 | "cpu": [
8 | "arm64"
9 | ],
10 | "main": "functions.darwin-arm64.node",
11 | "files": [
12 | "functions.darwin-arm64.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/darwin-universal/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-darwin-universal`
2 |
3 | This is the **universal-apple-darwin** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/darwin-universal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-darwin-universal",
3 | "version": "0.0.22",
4 | "os": [
5 | "darwin"
6 | ],
7 | "main": "functions.darwin-universal.node",
8 | "files": [
9 | "functions.darwin-universal.node"
10 | ],
11 | "description": "Bindings for jinja-lsp",
12 | "license": "MIT",
13 | "engines": {
14 | "node": ">= 10"
15 | },
16 | "repository": "https://github.com/uros-5/jinja-lsp"
17 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/freebsd-x64/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-freebsd-x64`
2 |
3 | This is the **x86_64-unknown-freebsd** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/freebsd-x64/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-freebsd-x64",
3 | "version": "0.0.22",
4 | "os": [
5 | "freebsd"
6 | ],
7 | "cpu": [
8 | "x64"
9 | ],
10 | "main": "functions.freebsd-x64.node",
11 | "files": [
12 | "functions.freebsd-x64.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/gnu-linux/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-gnu-linux`
2 |
3 | This is the **linux-x64-gnu** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/gnu-linux/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-gnu-linux",
3 | "version": "0.0.6",
4 | "os": [
5 | "gnu",
6 | "linux"
7 | ],
8 | "cpu": [
9 | "linux",
10 | "x64"
11 | ],
12 | "main": "functions.gnu-linux.node",
13 | "files": [
14 | "functions.gnu-linux.node"
15 | ],
16 | "description": "Bindings for jinja-lsp",
17 | "license": "MIT",
18 | "engines": {
19 | "node": ">= 10"
20 | },
21 | "repository": "https://github.com/uros-5/jinja-lsp"
22 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-arm-gnueabihf/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-linux-arm-gnueabihf`
2 |
3 | This is the **armv7-unknown-linux-gnueabihf** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-arm-gnueabihf/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-linux-arm-gnueabihf",
3 | "version": "0.0.22",
4 | "os": [
5 | "linux"
6 | ],
7 | "cpu": [
8 | "arm"
9 | ],
10 | "main": "functions.linux-arm-gnueabihf.node",
11 | "files": [
12 | "functions.linux-arm-gnueabihf.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-arm-musleabihf/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-linux-arm-musleabihf`
2 |
3 | This is the **armv7-unknown-linux-musleabihf** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-arm-musleabihf/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-linux-arm-musleabihf",
3 | "version": "0.0.22",
4 | "os": [
5 | "linux"
6 | ],
7 | "cpu": [
8 | "arm"
9 | ],
10 | "main": "functions.linux-arm-musleabihf.node",
11 | "files": [
12 | "functions.linux-arm-musleabihf.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-arm64-gnu/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-linux-arm64-gnu`
2 |
3 | This is the **aarch64-unknown-linux-gnu** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-arm64-gnu/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-linux-arm64-gnu",
3 | "version": "0.0.22",
4 | "os": [
5 | "linux"
6 | ],
7 | "cpu": [
8 | "arm64"
9 | ],
10 | "main": "functions.linux-arm64-gnu.node",
11 | "files": [
12 | "functions.linux-arm64-gnu.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp",
20 | "libc": [
21 | "glibc"
22 | ]
23 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-arm64-musl/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-linux-arm64-musl`
2 |
3 | This is the **aarch64-unknown-linux-musl** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-arm64-musl/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-linux-arm64-musl",
3 | "version": "0.0.22",
4 | "os": [
5 | "linux"
6 | ],
7 | "cpu": [
8 | "arm64"
9 | ],
10 | "main": "functions.linux-arm64-musl.node",
11 | "files": [
12 | "functions.linux-arm64-musl.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp",
20 | "libc": [
21 | "musl"
22 | ]
23 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-riscv64-gnu/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-linux-riscv64-gnu`
2 |
3 | This is the **riscv64gc-unknown-linux-gnu** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-riscv64-gnu/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-linux-riscv64-gnu",
3 | "version": "0.0.22",
4 | "os": [
5 | "linux"
6 | ],
7 | "cpu": [
8 | "riscv64"
9 | ],
10 | "main": "functions.linux-riscv64-gnu.node",
11 | "files": [
12 | "functions.linux-riscv64-gnu.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp",
20 | "libc": [
21 | "glibc"
22 | ]
23 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-x64-gnu/README.md:
--------------------------------------------------------------------------------
1 | # `@uros/jinja-lsp-nodejs-linux-x64-musl`
2 |
3 | This is the **x86_64-unknown-linux-musl** binary for `@uros/jinja-lsp-nodejs`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-x64-gnu/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-linux-x64-gnu",
3 | "version": "0.0.22",
4 | "os": [
5 | "linux"
6 | ],
7 | "cpu": [
8 | "x64"
9 | ],
10 | "main": "functions.linux-x64-gnu.node",
11 | "files": [
12 | "functions.linux-x64-gnu.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "libc": [
20 | "gnu",
21 | "glibc"
22 | ],
23 | "repository": "https://github.com/uros-5/jinja-lsp"
24 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-x64-musl/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-linux-x64-musl`
2 |
3 | This is the **x86_64-unknown-linux-musl** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/linux-x64-musl/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-linux-x64-musl",
3 | "version": "0.0.22",
4 | "os": [
5 | "linux"
6 | ],
7 | "cpu": [
8 | "x64"
9 | ],
10 | "main": "functions.linux-x64-musl.node",
11 | "files": [
12 | "functions.linux-x64-musl.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp",
20 | "libc": [
21 | "musl"
22 | ]
23 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/win32-arm64-msvc/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-win32-arm64-msvc`
2 |
3 | This is the **aarch64-pc-windows-msvc** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/win32-arm64-msvc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-win32-arm64-msvc",
3 | "version": "0.0.22",
4 | "os": [
5 | "win32"
6 | ],
7 | "cpu": [
8 | "arm64"
9 | ],
10 | "main": "functions.win32-arm64-msvc.node",
11 | "files": [
12 | "functions.win32-arm64-msvc.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/win32-ia32-msvc/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-win32-ia32-msvc`
2 |
3 | This is the **i686-pc-windows-msvc** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/win32-ia32-msvc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-win32-ia32-msvc",
3 | "version": "0.0.22",
4 | "os": [
5 | "win32"
6 | ],
7 | "cpu": [
8 | "ia32"
9 | ],
10 | "main": "functions.win32-ia32-msvc.node",
11 | "files": [
12 | "functions.win32-ia32-msvc.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/win32-x64-msvc/README.md:
--------------------------------------------------------------------------------
1 | # `@jinja-lsp/functions-win32-x86_64-pc-windows-msvc`
2 |
3 | This is the **win32-x86_64-pc-windows-msvc** binary for `@jinja-lsp/functions`
4 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/npm/win32-x64-msvc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions-win32-x64-msvc",
3 | "version": "0.0.22",
4 | "os": [
5 | "win32"
6 | ],
7 | "cpu": [
8 | "x64"
9 | ],
10 | "main": "functions.win32-x64-msvc.node",
11 | "files": [
12 | "functions.win32-x64-msvc.node"
13 | ],
14 | "description": "Bindings for jinja-lsp",
15 | "license": "MIT",
16 | "engines": {
17 | "node": ">= 10"
18 | },
19 | "repository": "https://github.com/uros-5/jinja-lsp"
20 | }
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jinja-lsp/functions",
3 | "version": "0.0.22",
4 | "main": "index.js",
5 | "types": "index.d.ts",
6 | "napi": {
7 | "name": "functions",
8 | "triples": {
9 | "defaults": false,
10 | "additional": [
11 | "aarch64-apple-darwin",
12 | "aarch64-linux-android",
13 | "aarch64-unknown-linux-gnu",
14 | "aarch64-unknown-linux-musl",
15 | "aarch64-pc-windows-msvc",
16 | "armv7-unknown-linux-gnueabihf",
17 | "armv7-unknown-linux-musleabihf",
18 | "x86_64-unknown-linux-musl",
19 | "x86_64-unknown-freebsd",
20 | "i686-pc-windows-msvc",
21 | "x86_64-pc-windows-msvc",
22 | "armv7-linux-androideabi",
23 | "universal-apple-darwin",
24 | "riscv64gc-unknown-linux-gnu",
25 | "x86_64-unknown-linux-gnu"
26 | ]
27 | }
28 | },
29 | "license": "MIT",
30 | "devDependencies": {
31 | "@napi-rs/cli": "^2.18.3",
32 | "ava": "^6.0.1"
33 | },
34 | "ava": {
35 | "timeout": "3m"
36 | },
37 | "engines": {
38 | "node": ">= 10"
39 | },
40 | "scripts": {
41 | "artifacts": "napi artifacts",
42 | "build": "napi build --platform --release",
43 | "build:debug": "napi build --platform",
44 | "prepublishOnly": "napi prepublish -t npm",
45 | "test": "ava",
46 | "universal": "napi universal",
47 | "version": "napi version"
48 | },
49 | "packageManager": "yarn@4.2.2",
50 | "repository": "https://github.com/uros-5/jinja-lsp",
51 | "description": "Bindings for jinja-lsp"
52 | }
53 |
--------------------------------------------------------------------------------
/jinja-lsp-nodejs/rustfmt.toml:
--------------------------------------------------------------------------------
1 | tab_spaces = 2
2 | edition = "2021"
3 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "jinja-lsp-queries"
3 | version = "0.1.85"
4 | edition = "2021"
5 | description = "TreeSitter queries for jinja-lsp"
6 | license = "MIT"
7 |
8 | [dependencies]
9 | tree-sitter = "0.23.0"
10 | tree-sitter-jinja2 = "0.0.12"
11 | tree-sitter-rust = "0.23.0"
12 | tower-lsp = { version = "0.20.0", features = ["proposed"] }
13 | ropey = "1.5.0"
14 | tree-sitter-python = "=0.23.0"
15 | tree-sitter-language = "0.1.0"
16 |
17 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod lsp_helper;
2 | pub mod parsers;
3 | pub mod search;
4 | pub mod to_input_edit;
5 | pub mod tree_builder;
6 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/lsp_helper.rs:
--------------------------------------------------------------------------------
1 | use std::{collections::HashMap, io::ErrorKind, path::PathBuf};
2 |
3 | use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
4 | use tree_sitter::{Point, Tree};
5 |
6 | use crate::{
7 | search::{
8 | objects::objects_query, queries::Queries, templates::templates_query, Identifier,
9 | IdentifierType,
10 | },
11 | tree_builder::{JinjaDiagnostic, LangType},
12 | };
13 |
14 | #[allow(clippy::too_many_arguments)]
15 | pub fn search_errors(
16 | root: &Tree,
17 | source: &str,
18 | queries: &Queries,
19 | variables: &HashMap>,
20 | file_name: &String,
21 | templates: PathBuf,
22 | lang_type: LangType,
23 | ignore_globals: bool,
24 | ) -> Option> {
25 | let mut diagnostics = vec![];
26 | match lang_type {
27 | LangType::Template => {
28 | let trigger_point = Point::new(0, 0);
29 | let query = &queries.jinja_objects;
30 | let objects = objects_query(query, root, trigger_point, source, true);
31 | let objects = objects.show();
32 | let this_file = variables.get(file_name)?;
33 | for object in objects {
34 | if object.is_filter || object.is_test {
35 | continue;
36 | }
37 | let mut exist = false;
38 | let mut err_type = JinjaDiagnostic::Undefined;
39 | let mut to_warn = false;
40 | let located = this_file
41 | .iter()
42 | .filter(|variable| {
43 | variable.name == object.name
44 | && variable.identifier_type != IdentifierType::TemplateBlock
45 | })
46 | .filter(|variable| {
47 | exist = true;
48 | let bigger = object.location.1 >= variable.start;
49 | let global = variable.scope_ends.1 == Point::default();
50 | let in_scope = object.location.0 < variable.scope_ends.1;
51 | if bigger && global {
52 | true
53 | } else {
54 | bigger && in_scope
55 | }
56 | });
57 | let empty = located.count() == 0;
58 | if empty && exist {
59 | to_warn = true;
60 | } else if empty {
61 | if ignore_globals {
62 | continue;
63 | }
64 | to_warn = true;
65 | for file in variables {
66 | let temp = file
67 | .1
68 | .iter()
69 | .filter(|variable| variable.name == object.name);
70 | if temp.count() != 0 {
71 | err_type = JinjaDiagnostic::DefinedSomewhere;
72 | to_warn = true;
73 | break;
74 | }
75 | }
76 | }
77 | if to_warn {
78 | if ignore_globals {
79 | continue;
80 | }
81 | let diagnostic = (err_type, Identifier::from(&object));
82 | diagnostics.push(diagnostic);
83 | }
84 | }
85 |
86 | let mut variables = vec![];
87 | let query_templates = &queries.jinja_imports;
88 | let jinja_imports = templates_query(query_templates, root, trigger_point, source, true);
89 | jinja_imports.collect(&mut variables);
90 |
91 | let id_templates = variables
92 | .iter()
93 | .filter(|identifier| identifier.identifier_type == IdentifierType::JinjaTemplate);
94 | for i in id_templates {
95 | let err_type = JinjaDiagnostic::TemplateNotFound;
96 | if i.name.is_empty() {
97 | let diagnostic = (err_type, i.to_owned());
98 | diagnostics.push(diagnostic);
99 | } else {
100 | let mut templates = templates.clone();
101 | templates.push(path_items(&i.name));
102 | if let Err(err) = std::fs::canonicalize(templates) {
103 | if err.kind() == ErrorKind::NotFound {
104 | let diagnostic = (err_type, i.to_owned());
105 | diagnostics.push(diagnostic);
106 | }
107 | }
108 | }
109 | }
110 | Some(diagnostics)
111 | }
112 | LangType::Backend => {
113 | let all_variables = variables.get(file_name)?;
114 | let templates2 = all_variables
115 | .iter()
116 | .filter(|id| id.identifier_type == IdentifierType::JinjaTemplate);
117 | for template in templates2 {
118 | let mut templates = templates.clone();
119 | templates.push(path_items(&template.name));
120 | if let Err(err) = std::fs::canonicalize(templates) {
121 | if err.kind() == ErrorKind::NotFound {
122 | let diagnostic = (JinjaDiagnostic::TemplateNotFound, template.to_owned());
123 | diagnostics.push(diagnostic);
124 | }
125 | }
126 | }
127 | Some(diagnostics)
128 | }
129 | }
130 | }
131 |
132 | pub fn create_diagnostic(
133 | template: &Identifier,
134 | severity: DiagnosticSeverity,
135 | message: String,
136 | ) -> Diagnostic {
137 | Diagnostic {
138 | range: Range::new(
139 | Position::new(template.start.row as u32, template.start.column as u32),
140 | Position::new(template.end.row as u32, template.end.column as u32),
141 | ),
142 | severity: Some(severity),
143 | message,
144 | source: Some(String::from("jinja-lsp")),
145 | ..Default::default()
146 | }
147 | }
148 |
149 | pub fn path_items(template: &str) -> PathBuf {
150 | template.split('/').collect()
151 | }
152 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/parsers.rs:
--------------------------------------------------------------------------------
1 | use tree_sitter::{Parser, Tree};
2 |
3 | use crate::tree_builder::LangType;
4 |
5 | pub struct Parsers {
6 | jinja: Parser,
7 | backend: Parser,
8 | }
9 |
10 | impl Parsers {
11 | pub fn parse(
12 | &mut self,
13 | lang_type: LangType,
14 | text: &str,
15 | old_tree: Option<&Tree>,
16 | ) -> Option {
17 | match lang_type {
18 | LangType::Template => self.jinja.parse(text, old_tree),
19 | LangType::Backend => self.backend.parse(text, old_tree),
20 | }
21 | }
22 |
23 | pub fn update_backend(&mut self, lang: &str) {
24 | if lang == "python" {
25 | self.backend = Parser::new();
26 | let _ = self
27 | .backend
28 | .set_language(&tree_sitter_python::LANGUAGE.into());
29 | }
30 | }
31 | }
32 |
33 | impl Default for Parsers {
34 | fn default() -> Self {
35 | let mut jinja = Parser::new();
36 | let _ = jinja.set_language(&tree_sitter_jinja2::LANGUAGE.into());
37 | let mut backend = Parser::new();
38 | let _ = backend.set_language(&tree_sitter_rust::LANGUAGE.into());
39 | Self { jinja, backend }
40 | }
41 | }
42 |
43 | impl Clone for Parsers {
44 | fn clone(&self) -> Self {
45 | Self::default()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/definition.rs:
--------------------------------------------------------------------------------
1 | use std::collections::{HashSet, LinkedList};
2 |
3 | use tree_sitter::{Point, Query, QueryCapture, QueryCursor, Tree};
4 |
5 | use super::{Identifier, IdentifierType};
6 |
7 | #[derive(Debug, Clone)]
8 | pub enum Definition {
9 | ForLoop {
10 | key: Identifier,
11 | value: Option,
12 | },
13 | Set {
14 | key: Identifier,
15 | equals: bool,
16 | },
17 | With {
18 | keys: Vec,
19 | },
20 | Macro {
21 | keys: Vec,
22 | scope: usize,
23 | },
24 | Block {
25 | name: Identifier,
26 | },
27 | }
28 |
29 | impl Definition {
30 | fn collect(self, ids: &mut Vec) {
31 | match self {
32 | Definition::ForLoop { mut key, value, .. } => {
33 | key.identifier_type = IdentifierType::ForLoopKey;
34 | ids.push(key);
35 | if let Some(mut value) = value {
36 | value.identifier_type = IdentifierType::ForLoopValue;
37 | ids.push(value);
38 | }
39 | }
40 | Definition::Set { mut key, .. } => {
41 | key.identifier_type = IdentifierType::SetVariable;
42 | ids.push(key);
43 | }
44 | Definition::With { keys, .. } => {
45 | for mut key in keys {
46 | key.identifier_type = IdentifierType::WithVariable;
47 | ids.push(key);
48 | }
49 | }
50 | Definition::Macro { keys, .. } => {
51 | for mut key in keys.into_iter().enumerate() {
52 | if key.0 == 0 {
53 | key.1.identifier_type = IdentifierType::MacroName;
54 | } else {
55 | key.1.identifier_type = IdentifierType::MacroParameter;
56 | }
57 | ids.push(key.1);
58 | }
59 | }
60 | Definition::Block { mut name, .. } => {
61 | name.identifier_type = IdentifierType::TemplateBlock;
62 | ids.push(name);
63 | }
64 | }
65 | }
66 | }
67 |
68 | impl From<&str> for Definition {
69 | fn from(value: &str) -> Self {
70 | match value {
71 | "for" => Self::ForLoop {
72 | key: Identifier::default(),
73 | value: None,
74 | },
75 | "set" => Self::Set {
76 | key: Identifier::default(),
77 | equals: false,
78 | },
79 | "with" => Self::With { keys: vec![] },
80 | "macro" => Definition::Macro {
81 | keys: vec![],
82 | scope: 0,
83 | },
84 | "block" => Self::Block {
85 | name: Identifier::default(),
86 | },
87 | _ => Self::ForLoop {
88 | key: Identifier::default(),
89 | value: None,
90 | },
91 | }
92 | }
93 | }
94 |
95 | #[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Debug)]
96 | pub enum Current {
97 | For,
98 | Set,
99 | With,
100 | Macro,
101 | Block,
102 | If,
103 | Else,
104 | Filter,
105 | Autoescape,
106 | Raw,
107 | #[default]
108 | NoDefinition,
109 | }
110 |
111 | impl Current {
112 | fn _from_end(name: &str) -> Self {
113 | match name {
114 | "endfor" => Self::For,
115 | "endset" => Self::Set,
116 | "endwith" => Self::With,
117 | "endmacro" => Self::Macro,
118 | "endblock" => Self::Block,
119 | "endif" => Self::If,
120 | "endelse" => Self::Else,
121 | "endfilter" => Self::Filter,
122 | "endautoescape" => Self::Autoescape,
123 | "endraw" => Self::Raw,
124 | _ => Self::NoDefinition,
125 | }
126 | }
127 | }
128 |
129 | #[derive(Default, Debug)]
130 | pub struct Scope {
131 | pub id: usize,
132 | pub start: Point,
133 | pub end: Point,
134 | }
135 |
136 | impl Scope {
137 | pub fn new(end: Point) -> Self {
138 | Self {
139 | id: 0,
140 | start: Point::default(),
141 | end,
142 | }
143 | }
144 | }
145 |
146 | #[derive(Default, Debug)]
147 | pub struct JinjaDefinitions {
148 | pub definitions: Vec,
149 | can_close_scope: bool,
150 | can_open_scope: bool,
151 | can_add_id: bool,
152 | is_end: bool,
153 | pub current_scope: LinkedList,
154 | pub all_scopes: Vec,
155 | all_ids: HashSet,
156 | }
157 |
158 | impl JinjaDefinitions {
159 | fn check(&mut self, name: &str, capture: &QueryCapture<'_>, text: &str) -> Option {
160 | let id = capture.node.id();
161 | match name {
162 | "definition" => {
163 | if self.all_ids.contains(&id) {
164 | return Some(false);
165 | }
166 | let content = capture.node.utf8_text(text.as_bytes()).unwrap();
167 | self.all_ids.insert(id);
168 | let mut add_new_scope = true;
169 | let mut definition = Definition::from(content);
170 | if let Definition::Set { .. } = definition {
171 | add_new_scope = false;
172 | } else if let Definition::Macro { ref mut scope, .. } = &mut definition {
173 | let current_scope = self.current_scope.front().unwrap_or(&Scope::default()).id;
174 | *scope = current_scope;
175 | }
176 | self.definitions.push(definition);
177 | if add_new_scope {
178 | self.current_scope.push_front(Scope {
179 | id,
180 | ..Default::default()
181 | });
182 | self.can_close_scope = false;
183 | self.can_open_scope = true;
184 | self.is_end = false;
185 | self.can_add_id = true;
186 | } else {
187 | self.can_add_id = true;
188 | }
189 | }
190 | "scope" => {
191 | self.can_close_scope = false;
192 | self.can_open_scope = true;
193 | self.is_end = false;
194 | self.can_add_id = false;
195 | self.current_scope.push_front(Scope {
196 | id,
197 | ..Default::default()
198 | });
199 | }
200 | "endblock" => {
201 | self.is_end = true;
202 | self.can_close_scope = true;
203 | self.can_open_scope = false;
204 | }
205 | "equals" => {
206 | let last = self.definitions.last_mut();
207 | if let Some(Definition::Set { equals, .. }) = last {
208 | *equals = true;
209 | self.can_open_scope = false;
210 | }
211 | }
212 | "error" => {
213 | return None;
214 | }
215 | "id" => {
216 | if !self.can_add_id {
217 | return Some(false);
218 | }
219 | let mut identifier = Identifier::default();
220 | let start = capture.node.start_position();
221 | let end = capture.node.end_position();
222 | let content = capture.node.utf8_text(text.as_bytes()).ok()?;
223 | let current_scope = self.current_scope.front().unwrap_or(&Scope::default()).id;
224 | identifier.start = start;
225 | identifier.end = end;
226 | content.to_owned().clone_into(&mut identifier.name);
227 | identifier.scope_ends.0 = current_scope;
228 | let last = self.definitions.last_mut();
229 | if let Some(last) = last {
230 | match last {
231 | Definition::ForLoop { key, value } => {
232 | if key.name.is_empty() {
233 | *key = identifier;
234 | } else if let Some(value) = value {
235 | if value.name.is_empty() {
236 | *value = identifier;
237 | self.can_add_id = false;
238 | }
239 | }
240 | }
241 | Definition::Set { key, .. } => {
242 | if key.name.is_empty() {
243 | *key = identifier;
244 | self.can_add_id = false;
245 | }
246 | }
247 | Definition::With { keys } => {
248 | keys.push(identifier);
249 | }
250 | Definition::Macro { keys, scope } => {
251 | if keys.is_empty() {
252 | identifier.scope_ends.0 = *scope;
253 | }
254 | keys.push(identifier);
255 | }
256 | Definition::Block { name } => {
257 | if name.name.is_empty() {
258 | *name = identifier;
259 | self.can_add_id = false;
260 | }
261 | }
262 | }
263 | }
264 | }
265 | "scope_end" => {
266 | if self.can_close_scope && self.is_end {
267 | self.can_close_scope = false;
268 | self.can_add_id = false;
269 | self.is_end = false;
270 | if let Some(mut last) = self.current_scope.pop_front() {
271 | last.end = capture.node.start_position();
272 | self.all_scopes.push(last);
273 | }
274 | }
275 | }
276 | "scope_start" => {
277 | if self.can_open_scope {
278 | self.can_open_scope = false;
279 | self.can_add_id = false;
280 | if let Some(last) = self.current_scope.front_mut() {
281 | last.start = capture.node.end_position();
282 | }
283 | }
284 | }
285 | _ => {}
286 | }
287 | Some(true)
288 | }
289 |
290 | pub fn identifiers(self) -> Vec {
291 | let mut ids = vec![];
292 | for id in self.definitions {
293 | id.collect(&mut ids);
294 | }
295 |
296 | ids
297 | }
298 |
299 | fn fix_end(&mut self, last_line: Point) {
300 | let global_scope = Scope::new(last_line);
301 | for def in self.definitions.iter_mut() {
302 | match def {
303 | Definition::ForLoop { key, value } => {
304 | let scope = self
305 | .all_scopes
306 | .iter()
307 | .find(|item| item.id == key.scope_ends.0);
308 | let scope = scope.unwrap_or(&global_scope);
309 | key.scope_ends.1 = scope.end;
310 | if let Some(value) = value {
311 | value.scope_ends.1 = scope.end;
312 | }
313 | }
314 | Definition::Set { key, .. } => {
315 | let scope = self
316 | .all_scopes
317 | .iter()
318 | .find(|item| item.id == key.scope_ends.0);
319 | let scope = scope.unwrap_or(&global_scope);
320 | key.scope_ends.1 = scope.end;
321 | }
322 | Definition::With { keys } => {
323 | for key in keys {
324 | let scope = self
325 | .all_scopes
326 | .iter()
327 | .find(|item| item.id == key.scope_ends.0);
328 | let scope = scope.unwrap_or(&global_scope);
329 | key.scope_ends.1 = scope.end;
330 | }
331 | }
332 | Definition::Macro { keys, scope } => {
333 | let scope = self.all_scopes.iter().find(|item| item.id == *scope);
334 | let scope = scope.unwrap_or(&global_scope);
335 | for (index, key) in keys.iter_mut().enumerate() {
336 | if index == 0 {
337 | key.scope_ends.1 = scope.end;
338 | } else {
339 | let scope = self
340 | .all_scopes
341 | .iter()
342 | .find(|item| item.id == key.scope_ends.0);
343 | let scope = scope.unwrap_or(&global_scope);
344 | key.scope_ends.1 = scope.end;
345 | }
346 | }
347 | }
348 | Definition::Block { name } => {
349 | let scope = self
350 | .all_scopes
351 | .iter()
352 | .find(|item| item.id == name.scope_ends.0);
353 | let scope = scope.unwrap_or(&global_scope);
354 | name.scope_ends.1 = scope.end;
355 | }
356 | }
357 | // let id = self.all_scopes.iter().find(|scope| scope.id == )
358 | }
359 | }
360 | }
361 |
362 | pub fn definition_query(
363 | query: &Query,
364 | tree: &Tree,
365 | trigger_point: Point,
366 | text: &str,
367 | all: bool,
368 | ) -> JinjaDefinitions {
369 | let closest_node = tree.root_node();
370 | let mut definitions = JinjaDefinitions::default();
371 | let mut cursor_qry = QueryCursor::new();
372 | let capture_names = query.capture_names();
373 | let matches = cursor_qry.matches(query, closest_node, text.as_bytes());
374 | let captures = matches.into_iter().flat_map(|m| {
375 | m.captures
376 | .iter()
377 | .filter(|capture| all || capture.node.start_position() <= trigger_point)
378 | });
379 | for capture in captures {
380 | let name = &capture_names[capture.index as usize];
381 | let err = definitions.check(name, capture, text);
382 | if err.is_none() {
383 | break;
384 | }
385 | }
386 | let root = tree.root_node().end_position();
387 | definitions.fix_end(root);
388 | definitions
389 | }
390 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/mod.rs:
--------------------------------------------------------------------------------
1 | use tower_lsp::lsp_types::{CompletionItemKind, Position, Range, SymbolKind};
2 | use tree_sitter::Point;
3 |
4 | use self::objects::JinjaObject;
5 |
6 | pub mod definition;
7 | pub mod objects;
8 | pub mod python_identifiers;
9 | pub mod queries;
10 | pub mod rust_identifiers;
11 | pub mod rust_template_completion;
12 | pub mod snippets_completion;
13 | pub mod templates;
14 | pub mod test_queries;
15 |
16 | #[derive(Default, Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
17 | pub struct Identifier {
18 | pub start: Point,
19 | pub end: Point,
20 | pub name: String,
21 | pub scope_ends: (usize, Point),
22 | pub identifier_type: IdentifierType,
23 | pub fields: Vec<(String, (Point, Point))>,
24 | }
25 |
26 | impl Identifier {
27 | pub fn new(name: &str, start: Point, end: Point) -> Self {
28 | Self {
29 | name: String::from(name),
30 | start,
31 | end,
32 | scope_ends: (0, Point::default()),
33 | identifier_type: IdentifierType::UndefinedVariable,
34 | fields: Vec::new(),
35 | }
36 | }
37 |
38 | pub fn merge(&self) -> String {
39 | let mut merged = self.name.to_string();
40 | for field in &self.fields {
41 | merged.push('.');
42 | merged.push_str(&field.0);
43 | }
44 | merged
45 | }
46 | }
47 |
48 | impl From<&JinjaObject> for Identifier {
49 | fn from(value: &JinjaObject) -> Self {
50 | let mut identifier = Identifier::new(&value.name, value.location.0, value.location.1);
51 | identifier.fields.clone_from(&value.fields);
52 | identifier
53 | }
54 | }
55 |
56 | pub fn completion_start(mut trigger_point: Point, identifier: &Identifier) -> Option<&str> {
57 | if trigger_point.column > 0 {
58 | trigger_point.column -= 1;
59 | }
60 | let len = identifier.name.len();
61 | if len == 0 {
62 | return Some("");
63 | }
64 | let diff = identifier.end.column - trigger_point.column;
65 | if diff == 0 || diff == 1 {
66 | return Some(&identifier.name);
67 | }
68 | if diff > len {
69 | return Some(&identifier.name);
70 | // return None;
71 | }
72 | let to = len - diff;
73 | let s = identifier.name.get(0..to + 1);
74 | s
75 | }
76 | pub fn to_range(points: (Point, Point)) -> Range {
77 | let start = Position::new(points.0.row as u32, points.0.column as u32);
78 | let end = Position::new(points.1.row as u32, points.1.column as u32);
79 | Range::new(start, end)
80 | }
81 |
82 | pub fn to_point(position: Position) -> Point {
83 | Point::new(position.line as usize, position.character as usize)
84 | }
85 |
86 | pub fn to_range2(range: Range, point: Point) -> bool {
87 | let start = to_point(range.start);
88 | let end = to_point(range.end);
89 | point >= start && point <= end
90 | }
91 |
92 | #[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
93 | pub enum IdentifierType {
94 | ForLoopKey,
95 | ForLoopValue,
96 | ForLoopCount,
97 | SetVariable,
98 | WithVariable,
99 | MacroName,
100 | MacroParameter,
101 | TemplateBlock,
102 | BackendVariable,
103 | #[default]
104 | UndefinedVariable,
105 | JinjaTemplate,
106 | }
107 |
108 | impl IdentifierType {
109 | pub fn completion_detail(&self) -> &'static str {
110 | match self {
111 | IdentifierType::ForLoopKey => "For loop key",
112 | IdentifierType::ForLoopValue => "For loop value",
113 | IdentifierType::ForLoopCount => "For loop count",
114 | IdentifierType::SetVariable => "Set variable",
115 | IdentifierType::WithVariable => "With variable",
116 | IdentifierType::MacroName => "Macro",
117 | IdentifierType::MacroParameter => "Macro parameter",
118 | IdentifierType::TemplateBlock => "Template block",
119 | IdentifierType::BackendVariable => "Backend variable",
120 | IdentifierType::UndefinedVariable => "Undefined variable",
121 | IdentifierType::JinjaTemplate => "Jinja template",
122 | }
123 | }
124 |
125 | pub fn completion_kind(&self) -> CompletionItemKind {
126 | match self {
127 | IdentifierType::ForLoopKey => CompletionItemKind::VARIABLE,
128 | IdentifierType::ForLoopValue => CompletionItemKind::VARIABLE,
129 | IdentifierType::ForLoopCount => CompletionItemKind::FIELD,
130 | IdentifierType::SetVariable => CompletionItemKind::VARIABLE,
131 | IdentifierType::WithVariable => CompletionItemKind::VARIABLE,
132 | IdentifierType::MacroName => CompletionItemKind::FUNCTION,
133 | IdentifierType::MacroParameter => CompletionItemKind::FIELD,
134 | IdentifierType::TemplateBlock => CompletionItemKind::MODULE,
135 | IdentifierType::BackendVariable => CompletionItemKind::VARIABLE,
136 | IdentifierType::UndefinedVariable => CompletionItemKind::CONSTANT,
137 | IdentifierType::JinjaTemplate => CompletionItemKind::FILE,
138 | }
139 | }
140 |
141 | pub fn symbol_kind(&self) -> SymbolKind {
142 | match self {
143 | IdentifierType::ForLoopKey => SymbolKind::VARIABLE,
144 | IdentifierType::ForLoopValue => SymbolKind::VARIABLE,
145 | IdentifierType::ForLoopCount => SymbolKind::FIELD,
146 | IdentifierType::SetVariable => SymbolKind::VARIABLE,
147 | IdentifierType::WithVariable => SymbolKind::VARIABLE,
148 | IdentifierType::MacroName => SymbolKind::FUNCTION,
149 | IdentifierType::MacroParameter => SymbolKind::FIELD,
150 | IdentifierType::TemplateBlock => SymbolKind::MODULE,
151 | IdentifierType::BackendVariable => SymbolKind::VARIABLE,
152 | IdentifierType::UndefinedVariable => SymbolKind::CONSTANT,
153 | IdentifierType::JinjaTemplate => SymbolKind::FILE,
154 | }
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/objects.rs:
--------------------------------------------------------------------------------
1 | use tower_lsp::lsp_types::Range;
2 | use tree_sitter::{Point, Query, QueryCapture, QueryCursor, Tree};
3 |
4 | use super::{completion_start, to_range, to_range2, Identifier};
5 |
6 | #[derive(Default, Debug, Clone)]
7 | pub struct JinjaObject {
8 | pub name: String,
9 | pub location: (Point, Point),
10 | pub is_filter: bool,
11 | pub is_test: bool,
12 | pub fields: Vec<(String, (Point, Point))>,
13 | pub capture_first: bool,
14 | }
15 |
16 | impl JinjaObject {
17 | pub fn new(name: String, start: Point, end: Point, is_filter: bool, is_test: bool) -> Self {
18 | Self {
19 | name,
20 | location: (start, end),
21 | fields: vec![],
22 | is_filter,
23 | capture_first: false,
24 | is_test,
25 | }
26 | }
27 |
28 | pub fn add_field(&mut self, field: String, start: Point, end: Point) {
29 | self.fields.push((field, (start, end)));
30 | }
31 |
32 | pub fn last_field_end(&self) -> Point {
33 | let last = self.fields.last().map_or(self.location.1, |v| v.1 .1);
34 | last
35 | }
36 |
37 | pub fn full_range(&self) -> Range {
38 | let start = self.location.0;
39 | let end = self.last_field_end();
40 | to_range((start, end))
41 | }
42 | }
43 |
44 | #[derive(Default, Debug)]
45 | pub struct JinjaObjects {
46 | objects: Vec,
47 | dot: (Point, Point),
48 | pipe: (Point, Point),
49 | test: (Point, Point, bool),
50 | expr: (Point, Point, ExpressionRange),
51 | ident: (Point, Point),
52 | }
53 |
54 | impl JinjaObjects {
55 | fn check(
56 | &mut self,
57 | name: &str,
58 | capture: &QueryCapture<'_>,
59 | source: &str,
60 | ) -> Option {
61 | let start = capture.node.start_position();
62 | let end = capture.node.end_position();
63 | match name {
64 | "error" => {
65 | return None;
66 | }
67 | "just_id" => {
68 | return Some(self.build_object(capture, source));
69 | }
70 | "dot" => {
71 | self.dot = (start, end);
72 | return Some(ObjectAction::NewField);
73 | }
74 | "pipe" => {
75 | let content = capture.node.utf8_text(source.as_bytes()).ok()?;
76 | if content.starts_with('|') {
77 | self.pipe = (start, end);
78 | }
79 | return Some(ObjectAction::NewFilter);
80 | }
81 | "is" => {
82 | self.test = (start, end, true);
83 | return Some(ObjectAction::NewTest);
84 | }
85 | "expr" => {
86 | let mut cursor = capture.node.walk();
87 | cursor.goto_first_child();
88 | let first = cursor.node();
89 | cursor.reset(capture.node);
90 | cursor.goto_last_child();
91 | let last = cursor.node();
92 | let expr = ExpressionRange {
93 | begin: (first.start_position(), first.end_position()),
94 | end: (last.start_position(), last.end_position()),
95 | };
96 | self.expr = (start, end, expr);
97 | return Some(ObjectAction::Expression);
98 | }
99 | _ => (),
100 | }
101 | Some(ObjectAction::Invalid)
102 | }
103 |
104 | pub fn build_object(&mut self, capture: &QueryCapture<'_>, source: &str) -> ObjectAction {
105 | let value = capture.node.utf8_text(source.as_bytes());
106 | let start = capture.node.start_position();
107 | let end = capture.node.end_position();
108 | if let Ok(value) = value {
109 | if start.row == self.dot.1.row && start.column == self.dot.1.column {
110 | let last_object = self.objects.last_mut().map(|last| {
111 | last.fields.push((String::from(value), (start, end)));
112 | self.ident = (start, end);
113 | });
114 | match last_object {
115 | Some(_) => {}
116 | None => {
117 | // TODO: in future add those to main library
118 | if VALID_IDENTIFIERS.contains(&value) {
119 | return ObjectAction::Invalid;
120 | }
121 | self.ident = (start, end);
122 | let is_test = self.test.2;
123 | let is_filter = self.is_hover(start) && self.is_filter();
124 | let obj =
125 | JinjaObject::new(String::from(value), start, end, is_filter, is_test);
126 | self.objects.push(obj);
127 | self.test.2 = false;
128 | return ObjectAction::NewObject;
129 | }
130 | }
131 | } else {
132 | // TODO: in future add those to main library
133 | if VALID_IDENTIFIERS.contains(&value) {
134 | return ObjectAction::Invalid;
135 | }
136 | self.ident = (start, end);
137 | let is_test = self.test.2;
138 | let is_filter = self.is_hover(start) && self.is_filter();
139 | self.objects.push(JinjaObject::new(
140 | String::from(value),
141 | start,
142 | end,
143 | is_filter,
144 | is_test,
145 | ));
146 | self.test.2 = false;
147 | return ObjectAction::NewObject;
148 | }
149 | }
150 | ObjectAction::Invalid
151 | }
152 |
153 | pub fn completion(&self, trigger_point: Point) -> Option<(CompletionType, bool)> {
154 | let autoclose = self.should_autoclose();
155 | if self.in_pipe(trigger_point) {
156 | return Some((CompletionType::Filter, autoclose));
157 | } else if self.in_expr(trigger_point) {
158 | if trigger_point == self.expr.2.begin.1 && trigger_point == self.expr.2.end.0 {
159 | return Some((CompletionType::Identifier, autoclose));
160 | }
161 | if trigger_point > self.ident.1 {
162 | return Some((CompletionType::Identifier, autoclose));
163 | }
164 | if let Some(ident_value) = self.is_ident(trigger_point) {
165 | // if let Some(ident2) = self.objects.last().map(|last| last) {
166 | let identifier = Identifier::new(&ident_value, self.ident.0, self.ident.1);
167 | let start = completion_start(trigger_point, &identifier);
168 | // let range = to_range((self.ident.0, self.ident.1));
169 | let range = self.full_range();
170 | return Some((
171 | CompletionType::IncompleteIdentifier {
172 | name: start?.to_string(),
173 | range,
174 | },
175 | autoclose,
176 | ));
177 | // }
178 | }
179 | return Some((CompletionType::Identifier, autoclose));
180 | } else if self.is_test(trigger_point) {
181 | return Some((CompletionType::Test, false));
182 | }
183 | None
184 | }
185 |
186 | pub fn in_pipe(&self, trigger_point: Point) -> bool {
187 | trigger_point >= self.pipe.0 && trigger_point <= self.pipe.1
188 | }
189 |
190 | pub fn in_expr(&self, trigger_point: Point) -> bool {
191 | let in_expr = trigger_point >= self.expr.0 && trigger_point < self.expr.1;
192 | let after_ident = trigger_point > self.ident.0;
193 | let no_ident = self.expr.2.begin.1 == self.expr.2.end.0;
194 | if !in_expr {
195 | return false;
196 | }
197 | in_expr && after_ident || no_ident
198 | }
199 |
200 | pub fn should_autoclose(&self) -> bool {
201 | self.expr.2.end.0 == self.expr.2.end.1
202 | }
203 |
204 | pub fn is_ident(&self, trigger_point: Point) -> Option {
205 | if trigger_point >= self.ident.0 && trigger_point <= self.ident.1 {
206 | self.objects.last().map(|last| last.name.to_string())
207 | } else {
208 | None
209 | }
210 | }
211 |
212 | pub fn is_hover(&self, trigger_point: Point) -> bool {
213 | let full_range = self.full_range();
214 | trigger_point >= self.ident.0 && trigger_point <= self.ident.1
215 | || to_range2(full_range, trigger_point)
216 | }
217 |
218 | pub fn is_filter(&self) -> bool {
219 | self.pipe.1 == self.ident.0
220 | }
221 |
222 | pub fn get_last_id(&self) -> Option<&JinjaObject> {
223 | self.objects.last()
224 | }
225 |
226 | pub fn show(&self) -> Vec {
227 | self.objects.clone()
228 | }
229 |
230 | pub fn full_range(&self) -> Range {
231 | self.objects
232 | .last()
233 | .map_or(Range::default(), |item| item.full_range())
234 | }
235 |
236 | pub fn is_test(&self, trigger_point: Point) -> bool {
237 | self.test.2 && trigger_point >= self.test.1 && trigger_point.row == self.test.1.row
238 | }
239 | }
240 |
241 | pub fn objects_query(
242 | query: &Query,
243 | tree: &Tree,
244 | trigger_point: Point,
245 | text: &str,
246 | all: bool,
247 | ) -> JinjaObjects {
248 | let closest_node = tree.root_node();
249 | let mut objects = JinjaObjects::default();
250 | let mut cursor_qry = QueryCursor::new();
251 | let capture_names = query.capture_names();
252 | let mut continued = false;
253 | let mut my_id = 0;
254 | let mut my_expr = (
255 | Point::default(),
256 | Point::default(),
257 | ExpressionRange::default(),
258 | );
259 | let matches = cursor_qry.matches(query, closest_node, text.as_bytes());
260 | 'loop1: for m in matches {
261 | for capture in m.captures {
262 | let smaller = trigger_point <= capture.node.start_position();
263 | if all || trigger_point >= capture.node.start_position() {
264 | let name = &capture_names[capture.index as usize];
265 | let checked = objects.check(name, capture, text);
266 | if checked.is_none() {
267 | break 'loop1;
268 | }
269 | } else if smaller {
270 | let name = capture_names[capture.index as usize];
271 | if objects.is_filter() || name == "expr" {
272 | break 'loop1;
273 | } else if !continued {
274 | if objects.is_hover(trigger_point) {
275 | continued = true;
276 | my_id = objects.objects.len() - 1;
277 | my_expr = objects.expr;
278 | continue;
279 | } else {
280 | break 'loop1;
281 | }
282 | } else if continued {
283 | let name = &capture_names[capture.index as usize];
284 | let checked = objects.check(name, capture, text);
285 | if checked.is_none() {
286 | break 'loop1;
287 | } else if checked.is_some_and(|item| {
288 | matches!(
289 | item,
290 | ObjectAction::Expression
291 | | ObjectAction::NewObject
292 | | ObjectAction::Invalid
293 | )
294 | }) {
295 | objects
296 | .objects
297 | .get_mut(my_id)
298 | .and_then(|obj| -> Option<()> {
299 | objects.ident = obj.location;
300 | obj.capture_first = true;
301 | None
302 | });
303 | if my_id != objects.objects.len() - 1 {
304 | objects.objects.pop();
305 | objects.expr = my_expr;
306 | }
307 | break 'loop1;
308 | }
309 | }
310 | }
311 | }
312 | }
313 | objects
314 | }
315 |
316 | #[derive(PartialEq, Debug)]
317 | pub enum CompletionType {
318 | Filter,
319 | Test,
320 | Identifier,
321 | IncludedTemplate { name: String, range: Range },
322 | Snippets { range: Range },
323 | IncompleteIdentifier { name: String, range: Range },
324 | IncompleteFilter { name: String, range: Range },
325 | }
326 |
327 | static VALID_IDENTIFIERS: [&str; 8] = [
328 | "loop", "true", "false", "not", "as", "module", "super", "url_for",
329 | ];
330 |
331 | #[derive(Default, Debug, Clone, Copy)]
332 | pub struct ExpressionRange {
333 | begin: (Point, Point),
334 | end: (Point, Point),
335 | }
336 |
337 | #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
338 | pub enum ObjectAction {
339 | Expression,
340 | Invalid,
341 | NewField,
342 | NewFilter,
343 | NewTest,
344 | NewObject,
345 | }
346 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/python_identifiers.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | use tree_sitter::{Point, Query, QueryCursor, Tree};
4 |
5 | pub struct PythonAttributes {
6 | pub attributes: HashMap>,
7 | }
8 |
9 | impl PythonAttributes {
10 | pub fn merge(&self, line: u32) -> Vec {
11 | let mut identifiers = vec![];
12 | for i in &self.attributes {
13 | let mut start = i.0.to_owned();
14 | start.row += line as usize;
15 | let mut end = i.0;
16 | let mut name = String::new();
17 | let len = i.1.len();
18 | for (index, identifier) in i.1.iter().enumerate() {
19 | name.push_str(&identifier.field);
20 | end = &identifier.end;
21 | if index != len - 1 {
22 | name.push('.');
23 | }
24 | }
25 | let mut end = end.to_owned();
26 | end.row += line as usize;
27 | let identifier = PythonIdentifier {
28 | id: 0,
29 | start,
30 | end,
31 | field: name,
32 | };
33 | identifiers.push(identifier);
34 | }
35 | identifiers
36 | }
37 | }
38 |
39 | #[derive(Default, Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
40 | pub struct PythonIdentifier {
41 | pub id: usize,
42 | pub start: Point,
43 | pub end: Point,
44 | pub field: String,
45 | }
46 |
47 | pub fn python_identifiers(
48 | query: &Query,
49 | tree: &Tree,
50 | mut _trigger_point: Point,
51 | text: &str,
52 | line: u32,
53 | ) -> Vec {
54 | let closest_node = tree.root_node();
55 | let mut cursor_qry = QueryCursor::new();
56 | let _capture_names = query.capture_names();
57 | let mut attributes = PythonAttributes {
58 | attributes: HashMap::new(),
59 | };
60 | let matches = cursor_qry.matches(query, closest_node, text.as_bytes());
61 | for i in matches {
62 | for capture in i.captures {
63 | if let Some(parent) = capture.node.parent() {
64 | let attribute = attributes
65 | .attributes
66 | .entry(parent.start_position())
67 | .or_default();
68 | let field = capture.node.utf8_text(text.as_bytes()).unwrap_or_default();
69 | let identifier = PythonIdentifier {
70 | id: capture.node.id(),
71 | start: capture.node.start_position(),
72 | end: capture.node.end_position(),
73 | field: field.to_string(),
74 | };
75 | attribute.push(identifier);
76 | }
77 | }
78 | }
79 | attributes.merge(line)
80 | }
81 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/queries.rs:
--------------------------------------------------------------------------------
1 | use tree_sitter::Query;
2 |
3 | #[derive(Debug)]
4 | pub struct Queries {
5 | pub jinja_definitions: Query,
6 | pub jinja_objects: Query,
7 | pub jinja_imports: Query,
8 | pub backend_definitions: Query,
9 | pub backend_templates: Query,
10 | pub jinja_snippets: Query,
11 | pub python_identifiers: Query,
12 | }
13 |
14 | impl Clone for Queries {
15 | fn clone(&self) -> Self {
16 | Self::default()
17 | }
18 | }
19 |
20 | impl Default for Queries {
21 | fn default() -> Self {
22 | Self {
23 | jinja_definitions: Query::new(&tree_sitter_jinja2::LANGUAGE.into(), DEFINITIONS)
24 | .unwrap(),
25 | jinja_objects: Query::new(&tree_sitter_jinja2::LANGUAGE.into(), OBJECTS).unwrap(),
26 | backend_definitions: Query::new(&tree_sitter_rust::LANGUAGE.into(), RUST_DEFINITIONS)
27 | .unwrap(),
28 | jinja_imports: Query::new(&tree_sitter_jinja2::LANGUAGE.into(), JINJA_IMPORTS).unwrap(),
29 | backend_templates: Query::new(&tree_sitter_rust::LANGUAGE.into(), RUST_TEMPLATES)
30 | .unwrap(),
31 | jinja_snippets: Query::new(&tree_sitter_jinja2::LANGUAGE.into(), JINJA_SNIPPETS)
32 | .unwrap(),
33 | python_identifiers: Query::new(
34 | &tree_sitter_python::LANGUAGE.into(),
35 | PYTHON_IDENTIFIERS,
36 | )
37 | .unwrap(),
38 | }
39 | }
40 | }
41 |
42 | impl Queries {
43 | pub fn update_backend(&mut self, lang: &str) {
44 | if lang == "python" {
45 | self.backend_templates =
46 | Query::new(&tree_sitter_python::LANGUAGE.into(), PYTHON_TEMPLATES).unwrap();
47 | self.backend_definitions =
48 | Query::new(&tree_sitter_python::LANGUAGE.into(), PYTHON_DEFINITIONS).unwrap();
49 | self.python_identifiers =
50 | Query::new(&tree_sitter_python::LANGUAGE.into(), PYTHON_IDENTIFIERS).unwrap();
51 | }
52 | }
53 | }
54 |
55 | const OBJECTS: &str = r#"
56 | (
57 | [
58 | (
59 | (operator) @dot
60 | (#eq? @dot "\.")
61 | )
62 |
63 | (
64 | (identifier) @just_id
65 | (#not-match? @just_id "(^\\d+$)")
66 | )
67 |
68 | (
69 | (operator) @pipe
70 | )
71 |
72 | (expression) @expr
73 |
74 | (
75 | (keyword) @is
76 | (#eq? @is "is")
77 | )
78 |
79 | (ERROR) @error
80 |
81 | ]
82 | )
83 | "#;
84 |
85 | pub static RUST_DEFINITIONS: &str = r#"
86 | ([
87 | (macro_invocation
88 | (identifier) @context
89 | (#eq? @context "context")
90 | ) @macro
91 |
92 | (token_tree
93 | (identifier) @key_id
94 | (#not-eq? @key_id "context")
95 | )
96 |
97 | (
98 | (field_expression
99 | (identifier) @jinja
100 | (field_identifier) @method
101 | )
102 | (arguments
103 | (string_literal)+ @name
104 | )
105 |
106 | (#eq? @jinja "jinja")
107 | (#match? @method "(add_global|add_filter|add_function)")
108 |
109 | ) @function
110 |
111 | (ERROR) @error
112 | ])
113 | "#;
114 |
115 | const JINJA_IMPORTS: &str = r#"
116 |
117 | (
118 | [
119 |
120 | (statement
121 | (statement_begin)
122 | (keyword) @extends_keyword
123 | (string) @template_name
124 | (statement_end)
125 | (#eq? @extends_keyword "extends")
126 | ) @extends
127 |
128 |
129 | (statement
130 | (statement_begin)
131 | (keyword) @include_keyword
132 | (string) @template_name
133 | (statement_end)
134 | (#eq? @include_keyword "include")
135 | ) @include
136 |
137 | (statement
138 | (statement_begin)
139 | (keyword) @from_keyword
140 | (string) @template_name
141 | (keyword)? @import_keyword
142 | (identifier)? @import_identifier
143 | (#not-match? @import_identifier "(^\\d)")
144 | (statement_end)
145 | (#eq? @from_keyword "from")
146 | (#eq? @import_keyword "import")
147 | ) @from
148 |
149 |
150 | (statement
151 | (statement_begin)
152 | (keyword) @import_keyword
153 | (string) @template_name
154 | (identifier)? @as_keyword
155 | (identifier)? @import_identifier
156 | (#not-match? @import_identifier "(^\\d)")
157 | (#eq? @import_keyword "import")
158 | (#eq? @as_keyword "as")
159 | (statement_end)
160 | ) @import
161 |
162 | (ERROR) @error
163 | ]
164 | )
165 | "#;
166 |
167 | const RUST_TEMPLATES: &str = r#"
168 | (call_expression
169 | [
170 | (field_expression
171 | (field_identifier) @method_name
172 | )
173 | (identifier) @method_name
174 | (#any-of? @method_name "render_jinja" "get_template")
175 | ;;(#match? @method_name "(render_jinja|get_template)")
176 | ]
177 | (arguments
178 | (string_literal)+ @template_name
179 | )
180 | )
181 | "#;
182 |
183 | const JINJA_SNIPPETS: &str = r#"
184 | [
185 | (statement_begin) @start
186 | (statement_end) @end
187 | (ERROR
188 | (ERROR)? @error
189 | ) @error_block
190 |
191 | (
192 | (keyword) @keyword
193 | )
194 | ]
195 | "#;
196 |
197 | const DEFINITIONS: &str = r#"
198 | (statement
199 | (statement_begin) @scope_end
200 |
201 | (statement_end) @scope_start
202 | )
203 |
204 | (
205 | (identifier) @id
206 | (#not-match? @id "^(\\d+)$")
207 | )
208 | (
209 | (keyword) @definition
210 | (#match? @definition "^(for|set|with|macro|block)$")
211 | )
212 |
213 | (
214 | (keyword) @scope
215 | (#match? @scope "^(if|elif|else|filter|autoescape|raw)$")
216 | )
217 |
218 | (
219 | (keyword) @endblock
220 | (#match? @endblock "^end")
221 | )
222 |
223 | (
224 | (operator) @equals
225 | (#match? @equals "=")
226 | )
227 |
228 | (ERROR) @error
229 | "#;
230 |
231 | const PYTHON_TEMPLATES: &str = r#"
232 | (call
233 | [
234 | (attribute
235 | (identifier) @method_name
236 | )
237 | (identifier) @method_name
238 | (#any-of? @method_name "render_jinja" "get_template")
239 | ]
240 | (argument_list
241 | (string)+ @template_name
242 | )
243 | )
244 | "#;
245 |
246 | pub static PYTHON_DEFINITIONS: &str = r#"
247 |
248 | (
249 | (subscript
250 | (attribute
251 | object: (identifier)* @object
252 | attribute: (identifier) @field
253 | (#match? @field "^(globals|filters)$")
254 | (#eq? @object "jinja_env")
255 | )
256 | (string
257 | (string_content) @key_id
258 | )
259 | )
260 | )
261 |
262 | (
263 | [
264 | (call
265 | function: (identifier) @method
266 | arguments: (argument_list
267 | (keyword_argument
268 | name: (identifier) @key_id
269 | )
270 | )
271 | )
272 |
273 | (call
274 | function: (attribute
275 | object: (identifier)
276 | attribute: (identifier) @method
277 | )
278 | arguments: (argument_list
279 | (keyword_argument
280 | name: (identifier) @key_id
281 | )
282 | )
283 | )
284 | ]
285 | (#match? @method "^(render_template|render)$")
286 | )
287 |
288 | (ERROR) @error
289 |
290 | "#;
291 |
292 | const PYTHON_IDENTIFIERS: &str = r#"
293 | (attribute
294 | (identifier) @identifier
295 | )
296 |
297 | (ERROR) @error
298 | "#;
299 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/rust_identifiers.rs:
--------------------------------------------------------------------------------
1 | use tree_sitter::{Point, Query, QueryCapture, QueryCursor, Tree};
2 |
3 | use super::{Identifier, IdentifierType};
4 |
5 | #[derive(Default, Debug, Clone, PartialEq, Eq)]
6 | pub enum Current {
7 | InMacro(Point),
8 | #[default]
9 | Free,
10 | }
11 |
12 | #[derive(Default, Debug, Clone)]
13 | pub struct BackendIdentifiers {
14 | variables: Vec,
15 | }
16 |
17 | impl BackendIdentifiers {
18 | pub fn show(self) -> Vec {
19 | self.variables
20 | }
21 |
22 | pub fn check(&mut self, name: &str, capture: &QueryCapture<'_>, text: &str) -> Option<()> {
23 | match name {
24 | "key_id" => {
25 | let start = capture.node.start_position();
26 | let end = capture.node.end_position();
27 | let name = capture.node.utf8_text(text.as_bytes()).ok()?;
28 | let mut identifier = Identifier::new(name, start, end);
29 | identifier.identifier_type = IdentifierType::BackendVariable;
30 | self.variables.push(identifier);
31 | }
32 | "name" => {
33 | let start = capture.node.start_position();
34 | let end = capture.node.end_position();
35 | let name = capture.node.utf8_text(text.as_bytes()).ok()?;
36 | let name = name.replace(['\"', '\''], "");
37 | let mut identifier = Identifier::new(&name, start, end);
38 | identifier.identifier_type = IdentifierType::BackendVariable;
39 | self.variables.push(identifier);
40 | }
41 | "error" => {
42 | return None;
43 | }
44 | _ => (),
45 | }
46 | Some(())
47 | }
48 | }
49 |
50 | pub fn backend_definition_query(
51 | query: &Query,
52 | tree: &Tree,
53 | trigger_point: Point,
54 | text: &str,
55 | all: bool,
56 | ) -> BackendIdentifiers {
57 | let closest_node = tree.root_node();
58 | let mut cursor_qry = QueryCursor::new();
59 | let mut rust = BackendIdentifiers::default();
60 | let capture_names = query.capture_names();
61 | let matches = cursor_qry.matches(query, closest_node, text.as_bytes());
62 | let captures = matches.into_iter().flat_map(|m| {
63 | m.captures
64 | .iter()
65 | .filter(|capture| all || capture.node.start_position() <= trigger_point)
66 | });
67 | for capture in captures {
68 | let name = &capture_names[capture.index as usize];
69 | if rust.check(name, capture, text).is_none() {
70 | break;
71 | }
72 | }
73 | rust
74 | }
75 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/rust_template_completion.rs:
--------------------------------------------------------------------------------
1 | use tree_sitter::{Point, Query, QueryCapture, QueryCursor, Tree};
2 |
3 | use super::{Identifier, IdentifierType};
4 |
5 | #[derive(Default, Debug, Clone, PartialEq, Eq)]
6 | pub struct BackendTemplates {
7 | pub templates: Vec,
8 | in_method: bool,
9 | }
10 |
11 | impl BackendTemplates {
12 | pub fn in_template(&self, trigger_point: Point) -> Option<&Identifier> {
13 | let last = self.templates.last()?;
14 | if trigger_point >= last.start && trigger_point <= last.end {
15 | Some(last)
16 | } else {
17 | None
18 | }
19 | }
20 |
21 | pub fn check(&mut self, name: &str, capture: &QueryCapture<'_>, text: &str) -> Option<()> {
22 | if name == "template_name" && self.in_method {
23 | let template = capture.node.utf8_text(text.as_bytes()).ok()?;
24 | let template = template.replace(['\"', '\''], "");
25 | let start = capture.node.start_position();
26 | let end = capture.node.end_position();
27 | let mut identifer = Identifier::new(&template, start, end);
28 | identifer.identifier_type = IdentifierType::JinjaTemplate;
29 | self.templates.push(identifer);
30 | self.in_method = false;
31 | } else if name == "method_name" {
32 | let content = capture.node.utf8_text(text.as_bytes()).ok()?;
33 | if !METHODS.contains(&content) {
34 | return None;
35 | }
36 | self.in_method = true;
37 | }
38 | None
39 | }
40 |
41 | pub fn collect(self) -> Vec {
42 | self.templates
43 | }
44 | }
45 |
46 | pub fn backend_templates_query(
47 | query: &Query,
48 | tree: &Tree,
49 | trigger_point: Point,
50 | text: &str,
51 | all: bool,
52 | ) -> BackendTemplates {
53 | let mut templates = BackendTemplates::default();
54 | let closest_node = tree.root_node();
55 | let mut cursor_qry = QueryCursor::new();
56 | let capture_names = query.capture_names();
57 | let matches = cursor_qry.matches(query, closest_node, text.as_bytes());
58 | let captures = matches.into_iter().flat_map(|m| {
59 | m.captures
60 | .iter()
61 | .filter(|capture| all || capture.node.start_position() <= trigger_point)
62 | });
63 | for capture in captures {
64 | let name = &capture_names[capture.index as usize];
65 | templates.check(name, capture, text);
66 | }
67 | templates
68 | }
69 |
70 | static METHODS: [&str; 2] = ["render_jinja", "get_template"];
71 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/snippets_completion.rs:
--------------------------------------------------------------------------------
1 | use crate::to_input_edit::to_position2;
2 | use tower_lsp::lsp_types::{
3 | CompletionItem, CompletionItemKind, CompletionTextEdit, Range, TextEdit,
4 | };
5 | use tree_sitter::{Point, Query, QueryCapture, QueryCursor, Tree};
6 |
7 | #[derive(Default, Debug)]
8 | pub struct Snippets {
9 | start: Point,
10 | end: Point,
11 | keyword: Point,
12 | pub is_error: bool,
13 | }
14 |
15 | impl Snippets {
16 | pub fn check(&mut self, name: &str, capture: &QueryCapture<'_>) -> Option<()> {
17 | match name {
18 | "start" => {
19 | let start = capture.node.start_position();
20 | self.start = start;
21 | }
22 | "end" => {
23 | let end = capture.node.end_position();
24 | self.end = end;
25 | }
26 | "error_block" => {
27 | self.is_error = true;
28 | self.end = capture.node.end_position();
29 | return None;
30 | }
31 | "keyword" => {
32 | self.keyword = capture.node.start_position();
33 | }
34 | _ => (),
35 | }
36 | Some(())
37 | }
38 |
39 | pub fn to_complete(&self, trigger_point: Point) -> Option {
40 | if self.is_error && trigger_point <= self.end {
41 | return Some(Range::default());
42 | }
43 | if self.is_error && trigger_point >= self.start && trigger_point <= self.end {
44 | if self.keyword >= self.start && self.keyword <= self.end {
45 | return None;
46 | }
47 | let start_position = to_position2(trigger_point);
48 | let mut end_position = to_position2(trigger_point);
49 | end_position.character += 1;
50 | return Some(Range::new(start_position, end_position));
51 | }
52 | None
53 | }
54 | }
55 |
56 | pub fn snippets_query(
57 | query: &Query,
58 | tree: &Tree,
59 | mut trigger_point: Point,
60 | text: &str,
61 | all: bool,
62 | ) -> Snippets {
63 | let closest_node = tree.root_node();
64 | let mut snippets = Snippets::default();
65 | let mut cursor_qry = QueryCursor::new();
66 | let capture_names = query.capture_names();
67 | let matches = cursor_qry.matches(query, closest_node, text.as_bytes());
68 | trigger_point.column += 2;
69 | let captures = matches.into_iter().flat_map(|m| {
70 | m.captures
71 | .iter()
72 | .filter(|capture| all || capture.node.start_position() <= trigger_point)
73 | });
74 | for capture in captures {
75 | let name = &capture_names[capture.index as usize];
76 | let check = snippets.check(name, capture);
77 | if check.is_none() {
78 | break;
79 | }
80 | }
81 | snippets
82 | }
83 |
84 | /// Range will be updated
85 | pub fn snippets() -> Vec {
86 | // label detail text
87 | let all = [
88 | (
89 | "for1",
90 | "Basic for loop",
91 | r#" for ${1:i} in ${2:items} %}
92 | {% endfor %}
93 | "#,
94 | ),
95 | (
96 | "for2",
97 | "For loop with key and value",
98 | r#" for (${1:key}, ${2:value}) in ${3:items} %}
99 | {% endfor %}
100 | "#,
101 | ),
102 | (
103 | "with",
104 | "With block",
105 | r#" with $1 %}
106 | {% endwith %}
107 | "#,
108 | ),
109 | (
110 | "set1",
111 | "Set variable that is current scope",
112 | r#" set ${1:key} = ${2:value} %}
113 | "#,
114 | ),
115 | (
116 | "set2",
117 | "Set with scope",
118 | r#" set ${1:data} %}
119 | {% endset %}
120 |
121 | "#,
122 | ),
123 | (
124 | "include",
125 | "Include template",
126 | r#" include "$1" %}
127 | "#,
128 | ),
129 | (
130 | "from",
131 | "Import from other template",
132 | r#" from "$1" import ${2:module} %}
133 | "#,
134 | ),
135 | (
136 | "import",
137 | "Import entire template as module",
138 | r#" import "$1" as ${2:module} %}
139 | "#,
140 | ),
141 | (
142 | "extends",
143 | "Extend parent template",
144 | r#" extends "$1" %}
145 | "#,
146 | ),
147 | (
148 | "if1",
149 | "If statement",
150 | r#" if $1 %}
151 | {% endif %}
152 | "#,
153 | ),
154 | (
155 | "if2",
156 | "If statement",
157 | r#" if $1 %}
158 | {% elif $2 %}
159 | {% endif %}
160 | "#,
161 | ),
162 | ];
163 |
164 | let mut snippets = vec![];
165 |
166 | for snippet in all {
167 | let edit = TextEdit {
168 | new_text: snippet.2.to_owned(),
169 | ..Default::default()
170 | };
171 | let text_edit = CompletionTextEdit::Edit(edit);
172 | let item = CompletionItem {
173 | label: snippet.0.to_owned(),
174 | detail: Some(snippet.1.to_owned()),
175 | kind: Some(CompletionItemKind::SNIPPET),
176 | text_edit: Some(text_edit),
177 | ..Default::default()
178 | };
179 | snippets.push(item);
180 | }
181 | snippets
182 | }
183 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/search/templates.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | use tree_sitter::{Point, Query, QueryCapture, QueryCursor, Tree};
4 |
5 | use super::{Identifier, IdentifierType};
6 |
7 | #[derive(Debug)]
8 | pub enum Import {
9 | Extends {
10 | template: Identifier,
11 | },
12 | Include {
13 | templates: Vec,
14 | },
15 | From {
16 | template: Identifier,
17 | identifiers: Vec,
18 | },
19 | Import {
20 | template: Identifier,
21 | identifier: Identifier,
22 | },
23 | }
24 |
25 | impl Import {
26 | pub fn get_identifier(&self, trigger_point: Point) -> Option<&Identifier> {
27 | match &self {
28 | Import::Extends { template }
29 | | Import::From { template, .. }
30 | | Import::Import { template, .. } => {
31 | if trigger_point >= template.start && trigger_point <= template.end {
32 | Some(template)
33 | } else {
34 | None
35 | }
36 | }
37 | Import::Include { templates } => {
38 | let template = templates.iter().find(|template| {
39 | trigger_point >= template.start && trigger_point <= template.end
40 | })?;
41 | Some(template)
42 | }
43 | }
44 | }
45 |
46 | fn collect(self, ids: &mut Vec) {
47 | match self {
48 | Import::Extends { template } => ids.push(template),
49 | Import::Include { templates } => {
50 | for i in templates {
51 | ids.push(i);
52 | }
53 | }
54 | Import::From {
55 | template,
56 | identifiers,
57 | } => {
58 | ids.push(template);
59 | for i in identifiers {
60 | ids.push(i);
61 | }
62 | }
63 | Import::Import { template, .. } => {
64 | ids.push(template);
65 | }
66 | }
67 | }
68 | }
69 |
70 | #[derive(Default, Debug)]
71 | pub enum Current {
72 | Id(usize),
73 | #[default]
74 | Nothing,
75 | }
76 |
77 | #[derive(Default)]
78 | pub struct JinjaImports {
79 | pub imports: HashMap,
80 | pub current: Current,
81 | pub last_id: usize,
82 | }
83 |
84 | impl JinjaImports {
85 | pub fn in_template(&self, _: Point) -> Option<&Import> {
86 | if let Current::Id(id) = self.current {
87 | let last = self.imports.get(&id)?;
88 | return Some(last);
89 | }
90 | None
91 | }
92 | pub fn check(&mut self, name: &str, capture: &QueryCapture<'_>, text: &str) -> Option<()> {
93 | match name {
94 | "error" => {
95 | return None;
96 | }
97 | "extends" => {
98 | let id = capture.node.id();
99 | let last = self.imports.get_mut(&id);
100 | if last.is_some() {
101 | self.current = Current::Nothing;
102 | } else {
103 | let import = Import::Extends {
104 | template: Identifier::default(),
105 | };
106 | self.imports.insert(id, import);
107 | self.current = Current::Id(id);
108 | self.last_id = id;
109 | }
110 | }
111 | "include" => {
112 | let id = capture.node.id();
113 | let last = self.imports.get_mut(&id);
114 | if last.is_some() {
115 | self.current = Current::Id(id);
116 | } else {
117 | let import = Import::Include { templates: vec![] };
118 | self.imports.insert(id, import);
119 | self.current = Current::Id(id);
120 | self.last_id = id;
121 | }
122 | }
123 | "import" => {
124 | let id = capture.node.id();
125 | let last = self.imports.get_mut(&id);
126 | if last.is_some() {
127 | self.current = Current::Id(id);
128 | } else {
129 | let import = Import::Import {
130 | template: Identifier::default(),
131 | identifier: Identifier::default(),
132 | };
133 | self.imports.insert(id, import);
134 | self.current = Current::Id(id);
135 | self.last_id = id;
136 | }
137 | }
138 | "from" => {
139 | let id = capture.node.id();
140 | let last = self.imports.get_mut(&id);
141 | if last.is_some() {
142 | self.current = Current::Id(id);
143 | } else {
144 | let import = Import::From {
145 | template: Identifier::default(),
146 | identifiers: vec![],
147 | };
148 | self.imports.insert(id, import);
149 | self.current = Current::Id(id);
150 | self.last_id = id;
151 | }
152 | }
153 | "template_name" => {
154 | if let Current::Id(id) = self.current {
155 | let last = self.imports.get_mut(&id)?;
156 | let name = capture.node.utf8_text(text.as_bytes()).ok()?;
157 | let name = name.replace(['\"', '\''], "");
158 | let start = capture.node.start_position();
159 | let end = capture.node.end_position();
160 | match last {
161 | Import::Extends { template } => {
162 | if template.name.is_empty() {
163 | template.name = name;
164 | template.start = start;
165 | template.end = end;
166 | template.identifier_type = IdentifierType::JinjaTemplate;
167 | }
168 | }
169 | Import::Include { templates } => {
170 | let mut template = Identifier::new(&name, start, end);
171 | template.identifier_type = IdentifierType::JinjaTemplate;
172 | templates.push(template);
173 | }
174 | Import::From { template, .. } => {
175 | if template.name.is_empty() {
176 | template.name = name;
177 | template.start = start;
178 | template.end = end;
179 | template.identifier_type = IdentifierType::JinjaTemplate;
180 | }
181 | }
182 | Import::Import { template, .. } => {
183 | if template.name.is_empty() {
184 | template.name = name;
185 | template.start = start;
186 | template.end = end;
187 | template.identifier_type = IdentifierType::JinjaTemplate;
188 | }
189 | }
190 | }
191 | }
192 | }
193 | "import_identifier" => {
194 | if let Current::Id(id) = self.current {
195 | let name = capture.node.utf8_text(text.as_bytes()).ok()?;
196 | let start = capture.node.start_position();
197 | let end = capture.node.end_position();
198 | let last = self.imports.get_mut(&id)?;
199 | match last {
200 | Import::From { identifiers, .. } => {
201 | let identifier = Identifier::new(name, start, end);
202 | identifiers.push(identifier);
203 | }
204 | Import::Import { identifier, .. } => {
205 | if identifier.name.is_empty() {
206 | identifier.name = String::from(name);
207 | self.current = Current::Nothing;
208 | }
209 | }
210 | _ => self.current = Current::Nothing,
211 | }
212 | }
213 | }
214 |
215 | _ => (),
216 | }
217 | Some(())
218 | }
219 |
220 | pub fn collect(self, ids: &mut Vec) {
221 | for i in self.imports {
222 | i.1.collect(ids);
223 | }
224 | }
225 | }
226 | pub fn templates_query(
227 | query: &Query,
228 | tree: &Tree,
229 | trigger_point: Point,
230 | text: &str,
231 | all: bool,
232 | ) -> JinjaImports {
233 | let closest_node = tree.root_node();
234 | let mut imports = JinjaImports::default();
235 | let mut cursor_qry = QueryCursor::new();
236 | let capture_names = query.capture_names();
237 | let matches = cursor_qry.matches(query, closest_node, text.as_bytes());
238 | let captures = matches.into_iter().flat_map(|m| {
239 | m.captures
240 | .iter()
241 | .filter(|capture| all || capture.node.start_position() <= trigger_point)
242 | });
243 | for capture in captures {
244 | let name = &capture_names[capture.index as usize];
245 | let res = imports.check(name, capture, text);
246 | if res.is_none() {
247 | break;
248 | }
249 | }
250 | imports
251 | }
252 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/to_input_edit.rs:
--------------------------------------------------------------------------------
1 | use ropey::Rope;
2 | use tower_lsp::lsp_types::{Position, Range};
3 | use tree_sitter::{InputEdit, Point};
4 |
5 | // use crate::lsp_files::JinjaVariable;
6 |
7 | pub trait ToInputEdit {
8 | fn to_point(&self, position: Position) -> Point;
9 | fn to_byte(&self, position: Position) -> usize;
10 | fn to_position(&self, offset: usize) -> Position;
11 | fn to_input_edit(&self, range: Range, text: &str) -> InputEdit;
12 | }
13 |
14 | impl ToInputEdit for Rope {
15 | fn to_point(&self, position: Position) -> Point {
16 | Point::new(position.line as usize, position.character as usize)
17 | }
18 |
19 | fn to_byte(&self, position: Position) -> usize {
20 | let start_line = self.line_to_byte(position.line as usize);
21 | start_line + position.character as usize
22 | }
23 |
24 | fn to_position(&self, mut offset: usize) -> Position {
25 | offset = offset.min(self.len_bytes());
26 | let mut low = 0usize;
27 | let mut high = self.len_lines();
28 | if high == 0 {
29 | return Position {
30 | line: 0,
31 | character: offset as u32,
32 | };
33 | }
34 | while low < high {
35 | let mid = low + (high - low) / 2;
36 | if self.line_to_byte(mid) > offset {
37 | high = mid;
38 | } else {
39 | low = mid + 1;
40 | }
41 | }
42 | let line = low - 1;
43 | let character = offset - self.line_to_byte(line);
44 | Position::new(line as u32, character as u32)
45 | }
46 |
47 | fn to_input_edit(&self, range: Range, text: &str) -> InputEdit {
48 | let start = range.start;
49 | let end = range.end;
50 |
51 | let start_byte = self.to_byte(start);
52 | let start_position = self.to_point(start);
53 |
54 | let new_end_byte = start_byte + text.len();
55 | let new_end_position = self.to_position(new_end_byte);
56 | let new_end_position = self.to_point(new_end_position);
57 |
58 | let old_end_byte = self.to_byte(end);
59 | let old_end_position = self.to_point(end);
60 |
61 | InputEdit {
62 | start_byte,
63 | old_end_byte,
64 | new_end_byte,
65 | start_position,
66 | old_end_position,
67 | new_end_position,
68 | }
69 | }
70 | }
71 |
72 | pub fn to_position2(point: Point) -> Position {
73 | Position::new(point.row as u32, point.column as u32)
74 | }
75 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/tree_builder.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::Display;
2 |
3 | use tower_lsp::lsp_types::DiagnosticSeverity;
4 |
5 | #[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
6 | pub enum LangType {
7 | Template,
8 | Backend,
9 | }
10 |
11 | #[derive(PartialEq, Eq, Debug)]
12 | pub enum JinjaDiagnostic {
13 | DefinedSomewhere,
14 | Undefined,
15 | TemplateNotFound,
16 | }
17 |
18 | impl JinjaDiagnostic {
19 | pub fn severity(&self) -> DiagnosticSeverity {
20 | match &self {
21 | JinjaDiagnostic::DefinedSomewhere => DiagnosticSeverity::INFORMATION,
22 | JinjaDiagnostic::Undefined => DiagnosticSeverity::WARNING,
23 | JinjaDiagnostic::TemplateNotFound => DiagnosticSeverity::WARNING,
24 | }
25 | }
26 | }
27 |
28 | impl Display for JinjaDiagnostic {
29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 | match self {
31 | JinjaDiagnostic::Undefined => f.write_str("Undefined variable"),
32 | JinjaDiagnostic::DefinedSomewhere => f.write_str("Variable is defined in other file."),
33 | JinjaDiagnostic::TemplateNotFound => f.write_str("Template not found"),
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/jinja-lsp-queries/src/types.rs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uros-5/jinja-lsp/ef8025bed811a2b282c50edfd53a4230fc93b4f9/jinja-lsp-queries/src/types.rs
--------------------------------------------------------------------------------
/jinja-lsp/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "jinja-lsp"
3 | version = "0.1.86"
4 | edition = "2021"
5 | license = "MIT"
6 | authors = ["uros-5"]
7 | description = "Language server for jinja2"
8 | homepage = "https://github.com/uros-5/jinja-lsp"
9 | repository = "https://github.com/uros-5/jinja-lsp"
10 | keywords = ["jinja", "templates", "lsp"]
11 | readme = "README.md"
12 |
13 | [profile.release]
14 | strip = true
15 | opt-level = 3
16 |
17 | [[bin]]
18 | name = "jinja-lsp"
19 | path = "src/main.rs"
20 |
21 |
22 | [dependencies]
23 | env_logger = "0.9.0"
24 | ropey = "1.5.0"
25 | serde_json = "1.0.78"
26 | tokio = { version = "1.17.0", features = ["full"] }
27 | tower-lsp = { version = "0.20.0", features = ["proposed"]}
28 | serde = { version = "1.0", features = ["derive"] }
29 | tree-sitter = "^0.23.0"
30 | walkdir = "2.4.0"
31 | anyhow = "1.0.75"
32 | tree-sitter-jinja2 = "0.0.12"
33 | tree-sitter-rust = "0.23.0"
34 | jinja-lsp-queries = { path = "../jinja-lsp-queries", version = "0.1.85"}
35 | tree-sitter-language = "0.1.0"
36 | toml = {version = "0.8.19", features = ["display"]}
37 | clap = { version = "4.5.39", features = ["derive"] }
38 |
--------------------------------------------------------------------------------
/jinja-lsp/README.md:
--------------------------------------------------------------------------------
1 | ../README.md
--------------------------------------------------------------------------------
/jinja-lsp/src/backend.rs:
--------------------------------------------------------------------------------
1 | use serde_json::Value;
2 | use tokio::sync::{
3 | mpsc::{self, Sender},
4 | oneshot,
5 | };
6 | use tower_lsp::{
7 | jsonrpc::Result,
8 | lsp_types::{
9 | CompletionParams, CompletionResponse, DidChangeConfigurationParams,
10 | DidCloseTextDocumentParams, DidOpenTextDocumentParams, DocumentSymbolParams,
11 | DocumentSymbolResponse, InitializeParams, InitializeResult,
12 | },
13 | Client, LanguageServer,
14 | };
15 |
16 | use tower_lsp::lsp_types::{
17 | CodeActionParams, CodeActionResponse, DidChangeTextDocumentParams, DidSaveTextDocumentParams,
18 | ExecuteCommandParams, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams,
19 | InitializedParams,
20 | };
21 |
22 | use crate::channels::{
23 | diagnostics::diagnostics_task,
24 | lsp::{lsp_task, LspMessage},
25 | };
26 |
27 | pub struct Backend {
28 | main_channel: Sender,
29 | }
30 |
31 | #[tower_lsp::async_trait]
32 | impl LanguageServer for Backend {
33 | async fn initialize(&self, params: InitializeParams) -> Result {
34 | let (sender, rx) = oneshot::channel();
35 | let _ = self
36 | .main_channel
37 | .send(LspMessage::Initialize(Box::new(params), sender))
38 | .await;
39 | if let Ok(msg) = rx.await {
40 | Ok(msg)
41 | } else {
42 | Ok(InitializeResult::default())
43 | }
44 | }
45 |
46 | async fn initialized(&self, _params: InitializedParams) {
47 | let (sender, _) = oneshot::channel();
48 | let _ = self
49 | .main_channel
50 | .send(LspMessage::Initialized(sender))
51 | .await;
52 | }
53 |
54 | async fn did_open(&self, params: DidOpenTextDocumentParams) {
55 | let _ = self.main_channel.send(LspMessage::DidOpen(params)).await;
56 | }
57 |
58 | async fn did_close(&self, _params: DidCloseTextDocumentParams) {}
59 |
60 | async fn did_change(&self, params: DidChangeTextDocumentParams) {
61 | let _ = self.main_channel.send(LspMessage::DidChange(params)).await;
62 | }
63 |
64 | async fn did_save(&self, params: DidSaveTextDocumentParams) {
65 | let _ = self.main_channel.send(LspMessage::DidSave(params)).await;
66 | }
67 |
68 | async fn completion(&self, params: CompletionParams) -> Result