diff --git a/package-lock.json b/package-lock.json index 412e399f..0874376a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -438,6 +438,12 @@ "node": ">= 6" } }, + "node_modules/@types/find": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@types/find/-/find-0.2.2.tgz", + "integrity": "sha512-Q67LLJvnvyiAu8XP1D2pPkxWh1hXLVOI3X77z4kw2orV4AIEz4I4UgVofabIIfypGoBZ2WAosTuu3gJVKPzlyw==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -4213,9 +4219,226 @@ "vscode-languageserver-textdocument": "^1.0.8", "winston": "^2.4.4" }, + "devDependencies": { + "@types/find": "^0.2.2", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "eslint": "^8.49.0", + "eslint-config-standard-with-typescript": "^39.0.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-n": "^16.1.0", + "eslint-plugin-promise": "^6.1.1", + "typescript": "^5.2.2" + }, "engines": { "node": "*" } + }, + "server/node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz", + "integrity": "sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/type-utils": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "server/node_modules/@typescript-eslint/parser": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz", + "integrity": "sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "server/node_modules/@typescript-eslint/scope-manager": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz", + "integrity": "sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "server/node_modules/@typescript-eslint/type-utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz", + "integrity": "sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "server/node_modules/@typescript-eslint/types": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "server/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "server/node_modules/@typescript-eslint/utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz", + "integrity": "sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "server/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "server/node_modules/eslint-config-standard-with-typescript": { + "version": "39.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-39.0.0.tgz", + "integrity": "sha512-CiV2LS4NUeeRmDTDf1ocUMpMxitSyW0g+Y/N7ecElwGj188GahbcQgqfBNyVsIXQxHlZVBlOjkbg3oUI0R3KBg==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^6.4.0", + "eslint-config-standard": "17.1.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.4.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } } }, "dependencies": { @@ -4360,6 +4583,12 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, + "@types/find": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@types/find/-/find-0.2.2.tgz", + "integrity": "sha512-Q67LLJvnvyiAu8XP1D2pPkxWh1hXLVOI3X77z4kw2orV4AIEz4I4UgVofabIIfypGoBZ2WAosTuu3gJVKPzlyw==", + "dev": true + }, "@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -6198,13 +6427,133 @@ "language-server-bitbake": { "version": "file:server", "requires": { + "@types/find": "^0.2.2", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "eslint": "^8.49.0", + "eslint-config-standard-with-typescript": "^39.0.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-n": "^16.1.0", + "eslint-plugin-promise": "^6.1.1", "execa": "^0.6.3", "find": "^0.2.7", "fs": "0.0.1-security", "path": "^0.12.7", + "typescript": "^5.2.2", "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8", "winston": "^2.4.4" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz", + "integrity": "sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/type-utils": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/parser": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz", + "integrity": "sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz", + "integrity": "sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz", + "integrity": "sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/types": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz", + "integrity": "sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "semver": "^7.5.4" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "eslint-config-standard-with-typescript": { + "version": "39.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-39.0.0.tgz", + "integrity": "sha512-CiV2LS4NUeeRmDTDf1ocUMpMxitSyW0g+Y/N7ecElwGj188GahbcQgqfBNyVsIXQxHlZVBlOjkbg3oUI0R3KBg==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^6.4.0", + "eslint-config-standard": "17.1.0" + } + } } }, "levn": { diff --git a/package.json b/package.json index 7341addf..30e04248 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,9 @@ "clean:server": "rm -fr ./server/node_modules && rm -fr ./server/out", "clean:client": "rm -fr ./client/node_modules && rm -fr ./client/out", "clean": "npm run clean:server && npm run clean:client && rm -fr node_modules", - "lint": "npm run lint --workspace client" + "lint": "npm run lint --workspaces", + "lint:client": "npm run lint --workspace client", + "lint:server": "npm run lint --workspace server" }, "devDependencies": { "@types/mocha": "^10.0.1", diff --git a/server/.eslintrc.js b/server/.eslintrc.js new file mode 100644 index 00000000..cac7322a --- /dev/null +++ b/server/.eslintrc.js @@ -0,0 +1,27 @@ +module.exports = { + env: { + browser: true, + es2021: true + }, + extends: 'standard-with-typescript', + overrides: [ + { + env: { + node: true + }, + files: [ + '.eslintrc.{js,cjs}', + '*.ts' + ], + parserOptions: { + sourceType: 'script' + } + } + ], + parserOptions: { + ecmaVersion: '1', + sourceType: 'module' + }, + rules: {}, + ignorePatterns: ['out'] +} diff --git a/server/package.json b/server/package.json index 399d7794..27681892 100644 --- a/server/package.json +++ b/server/package.json @@ -15,6 +15,9 @@ "type": "git", "url": "https://github.com/EugenWiens/vscode-bitbake.git" }, + "scripts": { + "lint": "eslint ." + }, "dependencies": { "execa": "^0.6.3", "find": "^0.2.7", @@ -23,5 +26,15 @@ "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8", "winston": "^2.4.4" + }, + "devDependencies": { + "@types/find": "^0.2.2", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "eslint": "^8.49.0", + "eslint-config-standard-with-typescript": "^39.0.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-n": "^16.1.0", + "eslint-plugin-promise": "^6.1.1", + "typescript": "^5.2.2" } } diff --git a/server/src/BasicKeywordMap.ts b/server/src/BasicKeywordMap.ts index 601d0d2d..6b8d38da 100644 --- a/server/src/BasicKeywordMap.ts +++ b/server/src/BasicKeywordMap.ts @@ -2,23 +2,21 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; -import { - CompletionItemKind, - CompletionItem -} from 'vscode-languageserver'; +import { CompletionItemKind } from 'vscode-languageserver' +import type { CompletionItem } from 'vscode-languageserver' -export let BasicKeywordMap: CompletionItem[] = [{ - label: 'require', - kind: CompletionItemKind.Keyword, - }, - { - label: 'inherit', - kind: CompletionItemKind.Keyword, - }, - { - label: 'include', - kind: CompletionItemKind.Keyword, - } -]; \ No newline at end of file +export const BasicKeywordMap: CompletionItem[] = [ + { + label: 'require', + kind: CompletionItemKind.Keyword + }, + { + label: 'inherit', + kind: CompletionItemKind.Keyword + }, + { + label: 'include', + kind: CompletionItemKind.Keyword + } +] diff --git a/server/src/BitBakeProjectScanner.ts b/server/src/BitBakeProjectScanner.ts index b0452179..81e5972a 100644 --- a/server/src/BitBakeProjectScanner.ts +++ b/server/src/BitBakeProjectScanner.ts @@ -2,446 +2,466 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information.Diagnstic * ------------------------------------------------------------------------------------------ */ -'use strict'; -const execa = require('execa'); -const find = require('find'); -const path = require('path'); -const fs = require('fs') +// @ts-expect-error -- execa has no declaration file +import execa from 'execa' +import find from 'find' +import path from 'path' +import fs from 'fs' -var logger = require('winston'); +import logger from 'winston' -import { - Connection} from "vscode-languageserver"; -import { - ElementInfo, - LayerInfo, - PathInfo -} from "./ElementInfo"; +import type { + Connection +} from 'vscode-languageserver' +import type { + ElementInfo, + LayerInfo, + PathInfo +} from './ElementInfo' import { - OutputParser -} from "./OutputParser"; + OutputParser +} from './OutputParser' -type ScannStatus = { - scanIsRunning: boolean, - scanIsPending: boolean +interface ScannStatus { + scanIsRunning: boolean + scanIsPending: boolean } /** * BitBakeProjectScanner */ export class BitBakeProjectScanner { - - private _classFileExtension: string = 'bbclass'; - private _includeFileExtension: string = 'inc'; - private _recipesFileExtension: string = 'bb'; - - private _projectPath: string; - private _layers: LayerInfo[] = new Array < LayerInfo > (); - private _classes: ElementInfo[] = new Array < ElementInfo > (); - private _includes: ElementInfo[] = new Array < ElementInfo > (); - private _recipes: ElementInfo[] = new Array < ElementInfo > (); - private _deepExamine: boolean = false; - private _settingsScriptInterpreter: string = '/bin/bash'; - private _settingsWorkingFolder: string = 'vscode-bitbake-build'; - private _settingsGenerateWorkingFolder: boolean = true; - private _settingsBitbakeSourceCmd: string = '.'; - private _settingsMachine: string = undefined; - private _outputParser: OutputParser; - private _oeEnvScript: string = 'oe-init-build-env'; - - constructor(connection: Connection) { - this._outputParser = new OutputParser(connection); + private readonly _classFileExtension: string = 'bbclass' + private readonly _includeFileExtension: string = 'inc' + private readonly _recipesFileExtension: string = 'bb' + + private _projectPath: string = '' + private _layers: LayerInfo[] = new Array < LayerInfo >() + private _classes: ElementInfo[] = new Array < ElementInfo >() + private _includes: ElementInfo[] = new Array < ElementInfo >() + private _recipes: ElementInfo[] = new Array < ElementInfo >() + private _deepExamine: boolean = false + private _settingsScriptInterpreter: string = '/bin/bash' + private _settingsWorkingFolder: string = 'vscode-bitbake-build' + private _settingsGenerateWorkingFolder: boolean = true + private readonly _settingsBitbakeSourceCmd: string = '.' + private _settingsMachine: string | undefined = undefined + private readonly _outputParser: OutputParser + private readonly _oeEnvScript: string = 'oe-init-build-env' + + constructor (connection: Connection) { + this._outputParser = new OutputParser(connection) + } + + private readonly _scanStatus: ScannStatus = { + scanIsRunning: false, + scanIsPending: false + } + + get projectPath (): string { + return this._projectPath + } + + get layers (): LayerInfo[] { + return this._layers + } + + get classes (): ElementInfo[] { + return this._classes + } + + get includes (): ElementInfo[] { + return this._includes + } + + get recipes (): ElementInfo[] { + return this._recipes + } + + get deepExamine (): boolean { + return this._deepExamine + } + + set deepExamine (deepExamine: boolean) { + this._deepExamine = deepExamine + } + + get scriptInterpreter (): string { + return this._settingsScriptInterpreter + } + + set scriptInterpreter (scriptInterpreter: string) { + this._settingsScriptInterpreter = scriptInterpreter + } + + get workingPath (): string { + return this._settingsWorkingFolder + } + + set workingPath (workingPath: string) { + this._settingsWorkingFolder = workingPath + } + + get generateWorkingPath (): boolean { + return this._settingsGenerateWorkingFolder + } + + set generateWorkingPath (generateWorkingPath: boolean) { + this._settingsGenerateWorkingFolder = generateWorkingPath + } + + get machineName (): string | undefined { + return this._settingsMachine + } + + set machineName (machine: string) { + if (machine === '') { + this._settingsMachine = undefined + } else { + this._settingsMachine = machine } + } - private _scanStatus: ScannStatus = { - scanIsRunning: false, - scanIsPending: false - }; - - get projectPath(): string { - return this._projectPath; - } + setProjectPath (projectPath: string): void { + this._projectPath = projectPath + } - get layers(): LayerInfo[] { - return this._layers; - } + rescanProject (): void { + logger.info(`request rescanProject ${this._projectPath}`) - get classes(): ElementInfo[] { - return this._classes; - } + if (this._scanStatus.scanIsRunning) { + this._scanStatus.scanIsRunning = true + logger.info('start rescanProject') - get includes(): ElementInfo[] { - return this._includes; - } + try { + if (this.parseAllRecipes()) { + this.scanAvailableLayers() + this.scanForClasses() + this.scanForIncludeFiles() + this.scanForRecipes() + this.scanRecipesAppends() - get recipes(): ElementInfo[] { - return this._recipes; + logger.info('scan ready') + this.printScanStatistic() + } + } catch (error) { + if (typeof error === 'string') { + logger.error(`scanning of project is abborted: ${error}`) + } + throw error + } + + this._scanStatus.scanIsRunning = false + + if (this._scanStatus.scanIsPending) { + this._scanStatus.scanIsPending = false + this.rescanProject() + } + } else { + logger.info('scan is already running, set the pending flag') + this._scanStatus.scanIsPending = true } - - set deepExamine(deepExamine: boolean) { - this._deepExamine = deepExamine; + } + + private printScanStatistic (): void { + logger.info(`Scan results for path: ${this._projectPath}`) + logger.info('******************************************************************') + logger.info(`Layer: ${this._layers.length}`) + logger.info(`Recipes: ${this._recipes.length}`) + logger.info(`Inc-Files: ${this._includes.length}`) + logger.info(`bbclass: ${this._classes.length}`) + } + + private scanForClasses (): void { + this._classes = this.searchFiles(this._classFileExtension) + } + + private scanForIncludeFiles (): void { + this._includes = this.searchFiles(this._includeFileExtension) + } + + private scanAvailableLayers (): void { + this._layers = new Array < LayerInfo >() + + const output: string = this.executeCommandInBitBakeEnvironment('bitbake-layers show-layers') + + if (output.length > 0) { + try { + let tempStr: string[] = output.split('\n') + tempStr = tempStr.slice(2) + + for (const element of tempStr) { + const tempElement: string[] = element.split(/\s+/) + const layerElement = { + name: tempElement[0], + path: tempElement[1], + priority: parseInt(tempElement[2]) + } + + if ((layerElement.name !== undefined) && (layerElement.path !== undefined) && layerElement.priority !== undefined) { + this._layers.push(layerElement) + } + } + } catch (error) { + if (typeof error !== 'string') { + throw error + } + logger.error(`can not scan available layers error: ${error}`) + this._outputParser.parse(error) + } } + } - set scriptInterpreter(scriptInterpreter: string) { - this._settingsScriptInterpreter = scriptInterpreter; - } + private searchFiles (pattern: string): ElementInfo[] { + const elements: ElementInfo[] = new Array < ElementInfo >() - set workingPath(workingPath: string) { - this._settingsWorkingFolder = workingPath; - } + for (const layer of this._layers) { + try { + const files = find.fileSync(new RegExp(`.${pattern}$`), layer.path) + for (const file of files) { + const pathObj: PathInfo = path.parse(file) - set generateWorkingPath(generateWorkingPath: boolean) { - this._settingsGenerateWorkingFolder = generateWorkingPath; - } + const element: ElementInfo = { + name: pathObj.name, + path: pathObj, + extraInfo: `layer: ${layer.name}`, + layerInfo: layer + } - set machineName(machine: string) { - - if (machine === "") { - this._settingsMachine = undefined; - } else { - this._settingsMachine = machine; + elements.push(element) } + } catch (error) { + logger.error(`find error: pattern: ${pattern} layer.path: ${layer.path} error: ${JSON.stringify(error)}`) + throw error + } } - setProjectPath(projectPath: string) { - this._projectPath = projectPath; - } - - - rescanProject() { - logger.info(`request rescanProject ${this._projectPath}`); + return elements + } - if (this._scanStatus.scanIsRunning === false) { - this._scanStatus.scanIsRunning = true; - logger.info('start rescanProject'); + scanForRecipes (): void { + this._recipes = new Array < ElementInfo >() - try { - if( this.parseAllRecipes() ) { - this.scanAvailableLayers(); - this.scanForClasses(); - this.scanForIncludeFiles(); - this.scanForRecipes(); - this.scanRecipesAppends(); + const output: string = this.executeCommandInBitBakeEnvironment('bitbake-layers show-recipes') - logger.info('scan ready'); - this.printScanStatistic(); - } - } catch (error) { - logger.error(`scanning of project is abborted: ${error}`) - throw error; - } - - this._scanStatus.scanIsRunning = false; + if (output.length > 0) { + const outerReg: RegExp = /(.+):\n((?:\s+\S+\s+\S+(?:\s+\(skipped\))?\n)+)/g + const innerReg: RegExp = /\s+(\S+)\s+(\S+(?:\s+\(skipped\))?)\n/g + let match: RegExpExecArray | null - if (this._scanStatus.scanIsPending === true) { - this._scanStatus.scanIsPending = false; - this.rescanProject(); - } - } else { - logger.info('scan is already running, set the pending flag'); - this._scanStatus.scanIsPending = true; + while ((match = outerReg.exec(output)) !== null) { + if (match.index === outerReg.lastIndex) { + outerReg.lastIndex++ } - } - private printScanStatistic() { - logger.info(`Scan results for path: ${this._projectPath}`); - logger.info('******************************************************************'); - logger.info(`Layer: ${this._layers.length}`); - logger.info(`Recipes: ${this._recipes.length}`); - logger.info(`Inc-Files: ${this._includes.length}`); - logger.info(`bbclass: ${this._classes.length}`); - } + let matchInner: RegExpExecArray | null + const extraInfoString: string[] = new Array < string >() + let layerName: string + let version: string = '' - private scanForClasses() { - this._classes = this.searchFiles(this._classFileExtension); - } + while ((matchInner = innerReg.exec(match[2])) !== null) { + if (matchInner.index === innerReg.lastIndex) { + innerReg.lastIndex++ + } - private scanForIncludeFiles() { - this._includes = this.searchFiles(this._includeFileExtension); - } + if (extraInfoString.length === 0) { + layerName = matchInner[1] + version = matchInner[2] + } - private scanAvailableLayers() { - this._layers = new Array < LayerInfo > (); + extraInfoString.push(`layer: ${matchInner[1]}`) + extraInfoString.push(`version: ${matchInner[2]} `) + } - let output: string = this.executeCommandInBitBakeEnvironment('bitbake-layers show-layers'); + const layer = this._layers.find((obj: LayerInfo): boolean => { + return obj.name === layerName + }) - if (output.length > 0) { - try { - let tempStr: string[] = output.split('\n'); - tempStr = tempStr.slice(2); - - for (let element of tempStr) { - let tempElement: string[] = element.split(/\s+/); - let layerElement = { - name: tempElement[0], - path: tempElement[1], - priority: parseInt(tempElement[2]) - }; - - if ((layerElement.name !== undefined) && (layerElement.path !== undefined) && layerElement.priority !== undefined) { - this._layers.push(layerElement); - } - } - } catch (error) { - logger.error(`can not scan available layers error: ${error}`); - this._outputParser.parse(error); - } + const element: ElementInfo = { + name: match[1], + extraInfo: extraInfoString.join('\n'), + layerInfo: layer, + version } - } - private searchFiles(pattern: string): ElementInfo[] { - let elements: ElementInfo[] = new Array < ElementInfo > (); + this._recipes.push(element) + } + } - for (let layer of this._layers) { - try { - let files = find.fileSync(new RegExp(`.${pattern}$`), layer.path); - for (let file of files) { - let pathObj: PathInfo = path.parse(file); + this.scanForRecipesPath() + } + + parseAllRecipes (): boolean { + logger.debug('parseAllRecipes') + let parsingOutput: string + let parsingSuccess: boolean = true + + try { + parsingOutput = this.executeCommandInBitBakeEnvironment('bitbake -p', this._settingsMachine) + } catch (error) { + if (typeof error !== 'string') { + throw error + } + logger.error(`parsing all recipes is abborted: ${error}`) + parsingOutput = error + } - let element: ElementInfo = { - name: pathObj.name, - path: pathObj, - extraInfo: `layer: ${layer.name}`, - layerInfo: layer, - }; + if (parsingOutput.length > 0) { + this._outputParser.parse(parsingOutput) + if (this._outputParser.errorsFound()) { + this._outputParser.reportProblems() + parsingSuccess = false + } + } + return parsingSuccess + } - elements.push(element); - } + private scanForRecipesPath (): void { + const tmpFiles = this.searchFiles(this._recipesFileExtension) - } catch (error) { - logger.error(`find error: pattern: ${pattern} layer.path: ${layer.path} error: ${JSON.stringify(error)}`); - throw error; - } + for (const file of tmpFiles) { + const recipeName: string = file.name.split(/[_]/g)[0] - } + const element: ElementInfo | undefined = this._recipes.find((obj: ElementInfo): boolean => { + return obj.name === recipeName + }) - return elements; + if (element !== undefined) { + element.path = file.path + } } - scanForRecipes() { - this._recipes = new Array < ElementInfo > (); + if (this._deepExamine) { + const recipesWithOutPath: ElementInfo[] = this._recipes.filter((obj: ElementInfo): boolean => { + return obj.path === undefined + }) + + logger.info(`${recipesWithOutPath.length} recipes must be examined more deeply.`) - let output: string = this.executeCommandInBitBakeEnvironment('bitbake-layers show-recipes'); + for (const recipeWithOutPath of recipesWithOutPath) { + const output: string = this.executeCommandInBitBakeEnvironment(`bitbake-layers show-recipes -f ${recipeWithOutPath.name}`) + const regExp: RegExp = /(\s.*\.bb)/g + let match: RegExpExecArray | null if (output.length > 0) { - let outerReg: RegExp = /(.+)\:\n((?:\s+\S+\s+\S+(?:\s+\(skipped\))?\n)+)/g; - let innerReg: RegExp = /\s+(\S+)\s+(\S+(?:\s+\(skipped\))?)\n/g; - let match: RegExpExecArray; - - while ((match = outerReg.exec(output)) !== null) { - if (match.index === outerReg.lastIndex) { - outerReg.lastIndex++; - } - - let matchInner: RegExpExecArray; - let extraInfoString: string[] = new Array < string > (); - let layerName: string; - let version: string; - - while ((matchInner = innerReg.exec(match[2])) !== null) { - if (matchInner.index === innerReg.lastIndex) { - innerReg.lastIndex++; - } - - if (extraInfoString.length === 0) { - layerName = matchInner[1]; - version = matchInner[2]; - } - - extraInfoString.push(`layer: ${matchInner[1]}`); - extraInfoString.push(`version: ${matchInner[2]} `); - } - - let layer: LayerInfo = this._layers.find((obj: LayerInfo): boolean => { - return obj.name === layerName; - }); - - let element: ElementInfo = { - name: match[1], - extraInfo: extraInfoString.join('\n'), - layerInfo: layer, - version: version - }; - - this._recipes.push(element); + while ((match = regExp.exec(output)) !== null) { + if (match.index === regExp.lastIndex) { + regExp.lastIndex++ } - } - - this.scanForRecipesPath(); - } - parseAllRecipes(): boolean { - logger.debug('parseAllRecipes'); - let parsingOutput: string; - let parsingSuccess:boolean = true; - - try { - parsingOutput = this.executeCommandInBitBakeEnvironment('bitbake -p', this._settingsMachine); - } catch (error) { - logger.error(`parsing all recipes is abborted: ${error}`) - parsingOutput = error; - } - - if( parsingOutput.length > 0 ) { - this._outputParser.parse(parsingOutput); - if (this._outputParser.errorsFound()) { - this._outputParser.reportProblems(); - parsingSuccess = false; - } + recipeWithOutPath.path = path.parse(match[0].trim()) + } } - return parsingSuccess; + } } + } - private scanForRecipesPath() { - - let tmpFiles = this.searchFiles(this._recipesFileExtension); + private scanRecipesAppends (): void { + const output: string = this.executeCommandInBitBakeEnvironment('bitbake-layers show-appends') - for (let file of tmpFiles) { - let recipeName: string = file.name.split(/[_]/g)[0]; + if (output.length > 0) { + const outerReg: RegExp = /(\S.*\.bb):(?:\s*\/\S*.bbappend)+/g - let element: ElementInfo = this._recipes.find((obj: ElementInfo): boolean => { - return obj.name === recipeName; - }); + let match: RegExpExecArray | null - if (element !== undefined) { - element.path = file.path; - } + while ((match = outerReg.exec(output)) !== null) { + if (match.index === outerReg.lastIndex) { + outerReg.lastIndex++ } + let matchInner: RegExpExecArray | null + const fullRecipeNameAsArray: string[] = match[1].split('_') - if (this._deepExamine === true) { - let recipesWithOutPath: ElementInfo[] = this._recipes.filter((obj: ElementInfo): boolean => { - return obj.path === undefined; - }); + if (fullRecipeNameAsArray.length > 0) { + const recipeName: string = fullRecipeNameAsArray[0] - logger.info(`${recipesWithOutPath.length} recipes must be examined more deeply.`); + const recipe: ElementInfo | undefined = this.recipes.find((obj: ElementInfo): boolean => { + return obj.name === recipeName + }) - for (let recipeWithOutPath of recipesWithOutPath) { - let output: string = this.executeCommandInBitBakeEnvironment(`bitbake-layers show-recipes -f ${recipeWithOutPath.name}`); - let regExp: RegExp = /(\s.*\.bb)/g; - let match: RegExpExecArray; + if (recipe !== undefined) { + const innerReg: RegExp = /(\S*\.bbappend)/g - if( output.length > 0 ) { - while ((match = regExp.exec(output)) !== null) { - if (match.index === regExp.lastIndex) { - regExp.lastIndex++; - } + while ((matchInner = innerReg.exec(match[0])) !== null) { + if (matchInner.index === innerReg.lastIndex) { + innerReg.lastIndex++ + } - recipeWithOutPath.path = path.parse(match[0].trim()); - } - } - } - } - } + if (recipe.appends === undefined) { + recipe.appends = new Array < PathInfo >() + } - private scanRecipesAppends() { - let output: string = this.executeCommandInBitBakeEnvironment('bitbake-layers show-appends'); - - if (output.length > 0) { - let outerReg: RegExp = new RegExp(`(\\S.*\\.bb)\\:(?:\\s*\\/\\S*.bbappend)+`, 'g'); - let match: RegExpExecArray; - - while ((match = outerReg.exec(output)) !== null) { - if (match.index === outerReg.lastIndex) { - outerReg.lastIndex++; - } - let matchInner: RegExpExecArray; - let fullRecipeNameAsArray: string[] = match[1].split('_'); - - if (fullRecipeNameAsArray.length > 0) { - let recipeName: string = fullRecipeNameAsArray[0]; - - let recipe: ElementInfo = this.recipes.find((obj: ElementInfo): boolean => { - return obj.name === recipeName; - }); - - if (recipe !== undefined) { - let innerReg: RegExp = /(\S*\.bbappend)/g; - - while ((matchInner = innerReg.exec(match[0])) !== null) { - if (matchInner.index === innerReg.lastIndex) { - innerReg.lastIndex++; - } - - if (recipe.appends === undefined) { - recipe.appends = new Array < PathInfo > (); - } - - recipe.appends.push(path.parse(matchInner[0])); - }; - } - } + recipe.appends.push(path.parse(matchInner[0])) } + } } + } } + } - private executeCommandInBitBakeEnvironment(command: string, machine: string = undefined): string { - let returnValue: string = ''; + private executeCommandInBitBakeEnvironment (command: string, machine: string | undefined = undefined): string { + let returnValue: string = '' - if( this.isBitbakeAvailable() === true) { - let scriptContent: string = this.generateBitBakeCommandScriptFileContent(command, machine); - let pathToScriptFile: string = this._projectPath + '/' + this._settingsWorkingFolder; - let scriptFileName: string = pathToScriptFile + '/executeBitBakeCmd.sh'; - - if( !fs.existsSync(pathToScriptFile) ){ - fs.mkdirSync(pathToScriptFile) - } - fs.writeFileSync(scriptFileName, scriptContent); - fs.chmodSync(scriptFileName, '0755'); + if (this.isBitbakeAvailable()) { + const scriptContent: string = this.generateBitBakeCommandScriptFileContent(command, machine) + const pathToScriptFile: string = this._projectPath + '/' + this._settingsWorkingFolder + const scriptFileName: string = pathToScriptFile + '/executeBitBakeCmd.sh' - returnValue = this.executeCommand(scriptFileName); - } + if (!fs.existsSync(pathToScriptFile)) { + fs.mkdirSync(pathToScriptFile) + } + fs.writeFileSync(scriptFileName, scriptContent) + fs.chmodSync(scriptFileName, '0755') - return returnValue; + returnValue = this.executeCommand(scriptFileName) } - private executeCommand(command: string): string { - let stdOutput: string; - - if (this._projectPath !== null) { - try { - let returnObject = execa.shellSync(command); - - if (returnObject.status === 0) { - stdOutput = returnObject.stdout; - } else { - let data: Buffer = fs.readFileSync(command); - logger.error('error on executing command: ' + data.toString()); - } - } catch (error) { - throw error; - } - } + return returnValue + } - return stdOutput; - } + private executeCommand (command: string): string { + let stdOutput: string = '' - private generateBitBakeCommandScriptFileContent(bitbakeCommand: string, machine: string = undefined): string { - let scriptFileBuffer: string[] = []; - let scriptBitbakeCommand: string = bitbakeCommand; + if (this._projectPath !== null) { + const returnObject = execa.shellSync(command) - scriptFileBuffer.push('#!' + this._settingsScriptInterpreter); - scriptFileBuffer.push(this._settingsBitbakeSourceCmd + ' ./' + this._oeEnvScript + ' ' + this._settingsWorkingFolder + ' > /dev/null'); + if (returnObject.status === 0) { + stdOutput = returnObject.stdout + } else { + const data: Buffer = fs.readFileSync(command) + logger.error('error on executing command: ' + data.toString()) + } + } - if (machine !== undefined) { - scriptBitbakeCommand = `MACHINE=${machine} ` + scriptBitbakeCommand; - } + return stdOutput + } - scriptFileBuffer.push(scriptBitbakeCommand); + private generateBitBakeCommandScriptFileContent (bitbakeCommand: string, machine: string | undefined = undefined): string { + const scriptFileBuffer: string[] = [] + let scriptBitbakeCommand: string = bitbakeCommand - return scriptFileBuffer.join('\n'); + scriptFileBuffer.push('#!' + this._settingsScriptInterpreter) + scriptFileBuffer.push(this._settingsBitbakeSourceCmd + ' ./' + this._oeEnvScript + ' ' + this._settingsWorkingFolder + ' > /dev/null') + + if (machine !== undefined) { + scriptBitbakeCommand = `MACHINE=${machine} ` + scriptBitbakeCommand } - private isBitbakeAvailable(): boolean { - let settingActive: boolean = this._settingsGenerateWorkingFolder; - let oeEnvScriptExists: boolean = fs.existsSync(this._oeEnvScript); - let bitbakeAvailable: boolean = false; + scriptFileBuffer.push(scriptBitbakeCommand) - if( settingActive && oeEnvScriptExists ) { - bitbakeAvailable = true; - } + return scriptFileBuffer.join('\n') + } - return bitbakeAvailable; + private isBitbakeAvailable (): boolean { + const settingActive: boolean = this._settingsGenerateWorkingFolder + const oeEnvScriptExists: boolean = fs.existsSync(this._oeEnvScript) + let bitbakeAvailable: boolean = false + + if (settingActive && oeEnvScriptExists) { + bitbakeAvailable = true } -} \ No newline at end of file + + return bitbakeAvailable + } +} diff --git a/server/src/CompletionProvider.ts b/server/src/CompletionProvider.ts index 2e0911e0..979fc36a 100644 --- a/server/src/CompletionProvider.ts +++ b/server/src/CompletionProvider.ts @@ -2,172 +2,172 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; import { - CompletionItem, - CompletionItemKind -} from "vscode-languageserver"; + type CompletionItem, + CompletionItemKind +} from 'vscode-languageserver' import { - BitBakeProjectScanner -} from "./BitBakeProjectScanner"; + type BitBakeProjectScanner +} from './BitBakeProjectScanner' import { - PathInfo, - ElementInfo -} from "./ElementInfo"; + type PathInfo, + type ElementInfo +} from './ElementInfo' import { - BasicKeywordMap -} from './BasicKeywordMap'; + BasicKeywordMap +} from './BasicKeywordMap' import { - SymbolScanner, - SymbolContent -} from "./SymbolScanner"; + type SymbolScanner, + type SymbolContent +} from './SymbolScanner' export class CompletionProvider { + private readonly _classCompletionItemKind: CompletionItemKind = CompletionItemKind.Class + private readonly _includeCompletionItemKind: CompletionItemKind = CompletionItemKind.Interface + private readonly _recipesCompletionItemKind: CompletionItemKind = CompletionItemKind.Method + private readonly _symbolComletionItemKind: CompletionItemKind = CompletionItemKind.Variable + private readonly _projectScanner: BitBakeProjectScanner + private _symbolScanner: SymbolScanner | null = null - private _classCompletionItemKind: CompletionItemKind = CompletionItemKind.Class; - private _includeCompletionItemKind: CompletionItemKind = CompletionItemKind.Interface; - private _recipesCompletionItemKind: CompletionItemKind = CompletionItemKind.Method; - private _symbolComletionItemKind: CompletionItemKind = CompletionItemKind.Variable; - private _projectScanner: BitBakeProjectScanner; - private _symbolScanner: SymbolScanner; + constructor (projectScanner: BitBakeProjectScanner) { + this._projectScanner = projectScanner + } + get symbolScanner (): SymbolScanner | null { + return this._symbolScanner + } - constructor(projectScanner: BitBakeProjectScanner) { - this._projectScanner = projectScanner; + set symbolScanner (symbolScanner: SymbolScanner | null) { + this._symbolScanner = symbolScanner + } - } - - set symbolScanner(symbolScanner: SymbolScanner) { - this._symbolScanner = symbolScanner; - } + getInsertStringForTheElement (item: CompletionItem): string { + let insertString: string = item.label - getInsertStringForTheElement(item: CompletionItem): string { - let insertString: string = item.label; + if (item.kind === this._includeCompletionItemKind) { + const path: PathInfo = item.data.path + let pathAsString: string = path.dir.replace(item.data.layerInfo.path, '') - if (item.kind === this._includeCompletionItemKind) { - let path: PathInfo = item.data.path; - let pathAsString: string = path.dir.replace(item.data.layerInfo.path, ''); + if (pathAsString.startsWith('/')) { + pathAsString = pathAsString.slice(1) + } - if (pathAsString.startsWith('/') === true) { - pathAsString = pathAsString.substr(1); - } - - insertString = pathAsString + '/' + item.data.path.base; - } - - return insertString + insertString = pathAsString + '/' + item.data.path.base } - createCompletionItem(keyword: string): CompletionItem[] { - let completionItem: CompletionItem[] = new Array < CompletionItem > (); - - switch (keyword) { - case 'inherit': - completionItem = this.convertElementInfoListToCompletionItemList( - this._projectScanner.classes, - this._classCompletionItemKind - ); - break; - - case 'require': - case 'include': - completionItem = completionItem.concat( - this.convertElementInfoListToCompletionItemList( - this._projectScanner.includes, - this._includeCompletionItemKind - ) - ); - break; - - default: - completionItem = completionItem.concat( - this.convertElementInfoListToCompletionItemList( - this._projectScanner.classes, - this._classCompletionItemKind - ), - this.convertElementInfoListToCompletionItemList( - this._projectScanner.includes, - this._includeCompletionItemKind - ), - this.convertElementInfoListToCompletionItemList( - this._projectScanner.recipes, - this._recipesCompletionItemKind - ), - this.convertSymbolContentListToCompletionItemList( - this._symbolScanner.symbols, - this._symbolComletionItemKind - ), - BasicKeywordMap - ); - - break; - } - - return completionItem; + return insertString + } + + createCompletionItem (keyword: string): CompletionItem[] { + let completionItem: CompletionItem[] = new Array < CompletionItem >() + + switch (keyword) { + case 'inherit': + completionItem = this.convertElementInfoListToCompletionItemList( + this._projectScanner.classes, + this._classCompletionItemKind + ) + break + + case 'require': + case 'include': + completionItem = completionItem.concat( + this.convertElementInfoListToCompletionItemList( + this._projectScanner.includes, + this._includeCompletionItemKind + ) + ) + break + + default: + completionItem = completionItem.concat( + this.convertElementInfoListToCompletionItemList( + this._projectScanner.classes, + this._classCompletionItemKind + ), + this.convertElementInfoListToCompletionItemList( + this._projectScanner.includes, + this._includeCompletionItemKind + ), + this.convertElementInfoListToCompletionItemList( + this._projectScanner.recipes, + this._recipesCompletionItemKind + ), + this.convertSymbolContentListToCompletionItemList( + this._symbolScanner?.symbols ?? [], + this._symbolComletionItemKind + ), + BasicKeywordMap + ) + + break } - private convertElementInfoListToCompletionItemList(elementInfoList: ElementInfo[], completionType: CompletionItemKind): CompletionItem[] { - let completionItems: CompletionItem[] = new Array < CompletionItem > (); + return completionItem + } - for (let element of elementInfoList) { - let completionItem: CompletionItem = { - label: element.name, - detail: this.getTypeAsString(completionType), - documentation: element.extraInfo, - data: element, - kind: completionType - }; + private convertElementInfoListToCompletionItemList (elementInfoList: ElementInfo[], completionType: CompletionItemKind): CompletionItem[] { + const completionItems: CompletionItem[] = new Array < CompletionItem >() - completionItems.push(completionItem); - } + for (const element of elementInfoList) { + const completionItem: CompletionItem = { + label: element.name, + detail: this.getTypeAsString(completionType), + documentation: element.extraInfo, + data: element, + kind: completionType + } - return completionItems; + completionItems.push(completionItem) } - private convertSymbolContentListToCompletionItemList(symbolContentList: SymbolContent[], completionType: CompletionItemKind): CompletionItem[] { - let completionItems: CompletionItem[] = new Array < CompletionItem > (); + return completionItems + } - for (let element of symbolContentList) { - let completionItem: CompletionItem = { - label: element.symbolName, - detail: this.getTypeAsString(completionType), - documentation: '', - data: element, - kind: completionType - }; + private convertSymbolContentListToCompletionItemList (symbolContentList: SymbolContent[], completionType: CompletionItemKind): CompletionItem[] { + const completionItems: CompletionItem[] = new Array < CompletionItem >() - completionItems.push(completionItem); - } + for (const element of symbolContentList) { + const completionItem: CompletionItem = { + label: element.symbolName, + detail: this.getTypeAsString(completionType), + documentation: '', + data: element, + kind: completionType + } - return completionItems; + completionItems.push(completionItem) } - private getTypeAsString(completionType: CompletionItemKind): string { - let typeAsString: string = ''; + return completionItems + } - switch (completionType) { - case this._classCompletionItemKind: - typeAsString = 'class'; - break; + private getTypeAsString (completionType: CompletionItemKind): string { + let typeAsString: string = '' - case this._includeCompletionItemKind: - typeAsString = 'inc'; - break; + switch (completionType) { + case this._classCompletionItemKind: + typeAsString = 'class' + break - case this._recipesCompletionItemKind: - typeAsString = 'recipe'; - break; + case this._includeCompletionItemKind: + typeAsString = 'inc' + break - case this._symbolComletionItemKind: - typeAsString = 'symbol'; - break; - } + case this._recipesCompletionItemKind: + typeAsString = 'recipe' + break - return typeAsString; + case this._symbolComletionItemKind: + typeAsString = 'symbol' + break } -} \ No newline at end of file + + return typeAsString + } +} diff --git a/server/src/ContextHandler.ts b/server/src/ContextHandler.ts index 70cb57dc..1f4e44ff 100644 --- a/server/src/ContextHandler.ts +++ b/server/src/ContextHandler.ts @@ -2,178 +2,170 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; +import type { + TextDocumentPositionParams, + CompletionItem, Definition +} from 'vscode-languageserver' -import { - TextDocumentPositionParams, - CompletionItem, - Definition -} from "vscode-languageserver"; - -import { - BitBakeProjectScanner, -} from "./BitBakeProjectScanner"; +import type { + BitBakeProjectScanner +} from './BitBakeProjectScanner' import { - BasicKeywordMap -} from './BasicKeywordMap'; + BasicKeywordMap +} from './BasicKeywordMap' import { - DefinitionProvider -} from "./DefinitionProvider"; + DefinitionProvider +} from './DefinitionProvider' import { - CompletionProvider -} from "./CompletionProvider"; + CompletionProvider +} from './CompletionProvider' -import { - SymbolScanner -} from "./SymbolScanner"; - -var logger = require('winston'); +import type { + SymbolScanner +} from './SymbolScanner' +import logger from 'winston' /** * ContextHandler */ export class ContextHandler { - - private _projectScanner: BitBakeProjectScanner = null; - private _definitionProvider: DefinitionProvider = null; - private _completionProvider: CompletionProvider = null; - - constructor(projectScanner: BitBakeProjectScanner) { - this._projectScanner = projectScanner; - this._definitionProvider = new DefinitionProvider(this._projectScanner); - this._completionProvider = new CompletionProvider(this._projectScanner); + private readonly _projectScanner: BitBakeProjectScanner + private readonly _definitionProvider: DefinitionProvider + private readonly _completionProvider: CompletionProvider + + constructor (projectScanner: BitBakeProjectScanner) { + this._projectScanner = projectScanner + this._definitionProvider = new DefinitionProvider(this._projectScanner) + this._completionProvider = new CompletionProvider(this._projectScanner) + } + + getDefinition (textDocumentPositionParams: TextDocumentPositionParams, documentAsText: string[]): Definition { + let definition: Definition = [] + + if (documentAsText.length > textDocumentPositionParams.position.line) { + const keyWord: string = this.getKeyWord(textDocumentPositionParams, documentAsText) + const currentLine: string = documentAsText[textDocumentPositionParams.position.line] + const symbol: string = this.extractSymbolFromLine(textDocumentPositionParams, currentLine) + + if ((keyWord !== undefined) && (keyWord !== '')) { + definition = this.getDefinitionForKeyWord(keyWord, currentLine, symbol) + } else { + definition = this._definitionProvider.createDefinitionForSymbol(symbol) + } } - - - getDefinition(textDocumentPositionParams: TextDocumentPositionParams, documentAsText: string[]): Definition { - let definition: Definition = null; - - if (documentAsText.length > textDocumentPositionParams.position.line) { - let keyWord: string = this.getKeyWord(textDocumentPositionParams, documentAsText); - let currentLine: string = documentAsText[textDocumentPositionParams.position.line]; - let symbol: string = this.extractSymbolFromLine(textDocumentPositionParams, currentLine); - - if ((keyWord !== undefined) && (keyWord !== '')) { - definition = this.getDefinitionForKeyWord(keyWord, currentLine, symbol); - } else { - definition = this._definitionProvider.createDefinitionForSymbol(symbol); - } + return definition + } + + get definitionProvider (): DefinitionProvider { + return this._definitionProvider + } + + // eslint-disable-next-line accessor-pairs -- adding a setter would be pointless and weird + set symbolScanner (symbolScanner: SymbolScanner | null) { + this._completionProvider.symbolScanner = symbolScanner + this._definitionProvider.symbolScanner = symbolScanner + } + + private getDefinitionForKeyWord (keyWord: string, currentLine: string, selectedSympbol?: string): Definition { + let definition: Definition = [] + const words: string[] = currentLine.split(' ') + + if (words.length >= 2) { + if (words[0] === keyWord) { + logger.debug(`getDefinitionForKeyWord: ${JSON.stringify(words)}`) + if (words.length === 2) { + definition = this._definitionProvider.createDefinitionForKeyword(keyWord, words[1]) + } else { + definition = this._definitionProvider.createDefinitionForKeyword(keyWord, words[1], selectedSympbol) } - return definition; + } } - - get definitionProvider(): DefinitionProvider { - return this._definitionProvider; + return definition + } + + private extractSymbolFromLine (textDocumentPositionParams: TextDocumentPositionParams, currentLine: string): string { + logger.debug(`getDefinitionForSymbol ${currentLine}`) + const linePosition: number = textDocumentPositionParams.position.character + let symbolEndPosition: number = currentLine.length + let symbolStartPosition: number = 0 + const rightBorderCharacter: string[] = [' ', '=', '/', '$', '+', '}', '\'', '\'', ']', '['] + const leftBorderCharacter: string[] = [' ', '=', '/', '+', '{', '\'', '\'', '[', ']'] + + for (const character of rightBorderCharacter) { + let temp: number = currentLine.indexOf(character, linePosition) + if (temp === -1) { + temp = currentLine.length + } + symbolEndPosition = Math.min(symbolEndPosition, temp) } - set symbolScanner(symbolScanner: SymbolScanner) { - this._completionProvider.symbolScanner = symbolScanner; - this._definitionProvider.symbolScanner = symbolScanner; - } - - private getDefinitionForKeyWord(keyWord: string, currentLine: string, selectedSympbol ? : string): Definition { - let definition: Definition = null; - let words: string[] = currentLine.split(' '); - - if (words.length >= 2) { - if (words[0] === keyWord) { - logger.debug(`getDefinitionForKeyWord: ${JSON.stringify(words)}`); - if (words.length === 2) { - definition = this._definitionProvider.createDefinitionForKeyword(keyWord, words[1]); - } else { - definition = this._definitionProvider.createDefinitionForKeyword(keyWord, words[1], selectedSympbol); - } - } - } - + const symbolRightTrimed = currentLine.substring(0, symbolEndPosition) + logger.debug(`symbolRightTrimed ${symbolRightTrimed}`) - return definition; + for (const character of leftBorderCharacter) { + let temp: number = symbolRightTrimed.lastIndexOf(character, linePosition) + if (temp === -1) { + temp = 0 + } + symbolStartPosition = Math.max(symbolStartPosition, temp) } - private extractSymbolFromLine(textDocumentPositionParams: TextDocumentPositionParams, currentLine: string): string { - - logger.debug(`getDefinitionForSymbol ${currentLine}`); - let linePosition: number = textDocumentPositionParams.position.character; - let symbolEndPosition: number = currentLine.length; - let symbolStartPosition: number = 0; - let rightBorderCharacter: string[] = [' ', '=', '/', '$', '+', '}', '"', "'", ']', '[']; - let leftBorderCharacter: string[] = [' ', '=', '/', '+', '{', '"', "'", '[', ']']; - - for (let character of rightBorderCharacter) { - let temp: number = currentLine.indexOf(character, linePosition); - if (temp === -1) { - temp = currentLine.length; - } - symbolEndPosition = Math.min(symbolEndPosition, temp); - } - - let symbolRightTrimed = currentLine.substring(0, symbolEndPosition); - logger.debug(`symbolRightTrimed ${symbolRightTrimed}`); - - for (let character of leftBorderCharacter) { - let temp: number = symbolRightTrimed.lastIndexOf(character, linePosition); - if (temp === -1) { - temp = 0; - } - symbolStartPosition = Math.max(symbolStartPosition, temp); - } - - let symbol: string = symbolRightTrimed.substring(symbolStartPosition); - - for (let character of leftBorderCharacter.concat('-')) { - if (symbol.startsWith(character)) { - symbol = symbol.substring(1); - break; - } - } - - logger.debug(`symbol ${symbol}`); + let symbol: string = symbolRightTrimed.substring(symbolStartPosition) - return symbol; + for (const character of leftBorderCharacter.concat('-')) { + if (symbol.startsWith(character)) { + symbol = symbol.substring(1) + break + } } - getComletionItems(textDocumentPosition: TextDocumentPositionParams, documentAsText: string[]): CompletionItem[] { - let completionItem: CompletionItem[]; + logger.debug(`symbol ${symbol}`) - if (documentAsText.length > textDocumentPosition.position.line) { - let keyWord: string = this.getKeyWord(textDocumentPosition, documentAsText); + return symbol + } - if ((keyWord === undefined) || (keyWord === '')) { - completionItem = this._completionProvider.createCompletionItem('*'); - } else { - completionItem = this._completionProvider.createCompletionItem(keyWord); - } - } + getComletionItems (textDocumentPosition: TextDocumentPositionParams, documentAsText: string[]): CompletionItem[] { + let completionItem: CompletionItem[] = [] - return completionItem; - } + if (documentAsText.length > textDocumentPosition.position.line) { + const keyWord: string = this.getKeyWord(textDocumentPosition, documentAsText) - getInsertStringForTheElement(item: CompletionItem): string { - return this._completionProvider.getInsertStringForTheElement(item); + if ((keyWord === undefined) || (keyWord === '')) { + completionItem = this._completionProvider.createCompletionItem('*') + } else { + completionItem = this._completionProvider.createCompletionItem(keyWord) + } } - private getKeyWord(textDocumentPosition: TextDocumentPositionParams, documentAsText: string[]): string { - let currentLine = documentAsText[textDocumentPosition.position.line]; - let lineTillCurrentPosition = currentLine.substr(0, textDocumentPosition.position.character); - let words: string[] = lineTillCurrentPosition.split(' '); + return completionItem + } - let basicKeywordMap: CompletionItem[] = BasicKeywordMap; - let keyword: string; + getInsertStringForTheElement (item: CompletionItem): string { + return this._completionProvider.getInsertStringForTheElement(item) + } - if (words.length > 1) { - let basicKey: CompletionItem = basicKeywordMap.find((obj: CompletionItem): boolean => { - return obj.label === words[0]; - }); + private getKeyWord (textDocumentPosition: TextDocumentPositionParams, documentAsText: string[]): string { + const currentLine = documentAsText[textDocumentPosition.position.line] + const lineTillCurrentPosition = currentLine.substr(0, textDocumentPosition.position.character) + const words: string[] = lineTillCurrentPosition.split(' ') - if (basicKey !== undefined) { - keyword = basicKey.label; - } - } + const basicKeywordMap: CompletionItem[] = BasicKeywordMap + let keyword: string = '' + + if (words.length > 1) { + const basicKey: CompletionItem | undefined = basicKeywordMap.find((obj: CompletionItem): boolean => { + return obj.label === words[0] + }) - return keyword; + if (basicKey !== undefined) { + keyword = basicKey.label + } } -} \ No newline at end of file + + return keyword + } +} diff --git a/server/src/DefinitionProvider.ts b/server/src/DefinitionProvider.ts index ce741895..0a4947f5 100644 --- a/server/src/DefinitionProvider.ts +++ b/server/src/DefinitionProvider.ts @@ -2,210 +2,197 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; import { - Definition, - Location, - Range -} from "vscode-languageserver"; + type Definition, + Location, + Range +} from 'vscode-languageserver' import { - ElementInfo, - PathInfo -} from "./ElementInfo"; + type ElementInfo, + type PathInfo +} from './ElementInfo' import { - BitBakeProjectScanner, -} from "./BitBakeProjectScanner"; + type BitBakeProjectScanner +} from './BitBakeProjectScanner' import { - SymbolScanner, - SymbolContent -} from "./SymbolScanner"; + type SymbolScanner, + type SymbolContent +} from './SymbolScanner' -var logger = require('winston'); - -const path = require('path'); +import logger from 'winston' +import path from 'path' export class DefinitionProvider { - private _projectScanner: BitBakeProjectScanner = null; - private _symbolScanner: SymbolScanner = null; - - constructor(projectScanner: BitBakeProjectScanner) { - this._projectScanner = projectScanner; - } - - set symbolScanner(symbolScanner: SymbolScanner) { - this._symbolScanner = symbolScanner; - } - - createDefinitionForKeyword(keyword: string, restOfLine: string, selectedSympbol ? : string): Definition { - let definition: Definition = null; - restOfLine = restOfLine.trim(); - - switch (keyword) { - case 'inherit': - { - let searchString: string; - if (selectedSympbol === undefined) { - searchString = restOfLine; - } else { - searchString = selectedSympbol; - } - - let elementInfos: ElementInfo[] = this._projectScanner.classes.filter((obj: ElementInfo): boolean => { - return obj.name === searchString; - }); - definition = this.createDefinitionForElementInfo(elementInfos); - } - break; - - case 'require': - case 'include': - { - let includeFile: PathInfo = path.parse(restOfLine); - let elementInfos: ElementInfo[] = this._projectScanner.includes.filter((obj: ElementInfo): boolean => { - return obj.name === includeFile.name; - }); - - if( elementInfos.length == 0 ) { - elementInfos = this._projectScanner.recipes.filter((obj: ElementInfo): boolean => { - return obj.name === includeFile.name; - }); - } - definition = this.createDefinitionForElementInfo(elementInfos); - } - break; - - default: + private readonly _projectScanner: BitBakeProjectScanner + private _symbolScanner: SymbolScanner | null = null + + constructor (projectScanner: BitBakeProjectScanner) { + this._projectScanner = projectScanner + } + + // eslint-disable-next-line accessor-pairs + set symbolScanner (symbolScanner: SymbolScanner | null) { + this._symbolScanner = symbolScanner + } + + createDefinitionForKeyword (keyword: string, restOfLine: string, selectedSympbol?: string): Definition { + let definition: Definition = [] + restOfLine = restOfLine.trim() + + switch (keyword) { + case 'inherit': + { + let searchString: string + if (selectedSympbol === undefined) { + searchString = restOfLine + } else { + searchString = selectedSympbol + } + + const elementInfos: ElementInfo[] = this._projectScanner.classes.filter((obj: ElementInfo): boolean => { + return obj.name === searchString + }) + definition = this.createDefinitionForElementInfo(elementInfos) } + break + + case 'require': + case 'include': + { + const includeFile: PathInfo = path.parse(restOfLine) + let elementInfos: ElementInfo[] = this._projectScanner.includes.filter((obj: ElementInfo): boolean => { + return obj.name === includeFile.name + }) + + if (elementInfos.length === 0) { + elementInfos = this._projectScanner.recipes.filter((obj: ElementInfo): boolean => { + return obj.name === includeFile.name + }) + } + definition = this.createDefinitionForElementInfo(elementInfos) + } + break - return definition; + default: } - createDefinitionForSymbol(symbol: string): Definition { - let definitions: Definition = this.createDefinitionForSymbolRecipes(symbol); - - if (definitions === null) { - definitions = this.createDefinitionForSymbolVariables(symbol); - } + return definition + } + createDefinitionForSymbol (symbol: string): Definition { + let definitions: Definition = this.createDefinitionForSymbolRecipes(symbol) - return definitions; + if (definitions === null) { + definitions = this.createDefinitionForSymbolVariables(symbol) } - private createDefinitionForSymbolRecipes(symbol: string): Definition { - let definitions: Definition = null; + return definitions + } - let recipe: ElementInfo = this._projectScanner.recipes.find((obj: ElementInfo): boolean => { - return obj.name === symbol; - }); + private createDefinitionForSymbolRecipes (symbol: string): Definition { + let definitions: Definition = [] - if (recipe !== undefined) { - let definitionsList: PathInfo[] = new Array < PathInfo > (recipe.path); + const recipe: ElementInfo | undefined = this._projectScanner.recipes.find((obj: ElementInfo): boolean => { + return obj.name === symbol + }) - if ((recipe.appends !== undefined) && (recipe.appends.length > 0)) { - definitionsList = definitionsList.concat(recipe.appends); - } - definitions = this.createDefinitionLocationForPathInfoList(definitionsList); - } + if (recipe?.path !== undefined) { + let definitionsList: PathInfo[] = new Array < PathInfo >(recipe.path) - return definitions; + if ((recipe.appends !== undefined) && (recipe.appends.length > 0)) { + definitionsList = definitionsList.concat(recipe.appends) + } + definitions = this.createDefinitionLocationForPathInfoList(definitionsList) } - private createDefinitionForSymbolVariables(symbol: string): Definition { - let definitions: Definition = null; + return definitions + } - if( this._symbolScanner !== null ) { - let symbols: SymbolContent[] = this._symbolScanner.symbols.filter((obj: SymbolContent): boolean => { - return obj.symbolName === symbol; - }); - definitions = this.createDefinitionForSymbolContentList(symbols); - } - else { - logger.debug(`Cannot create definitions for symbol ${symbol}: symbol scanner is null`); - } + private createDefinitionForSymbolVariables (symbol: string): Definition { + let definitions: Definition = [] - return definitions; + if (this._symbolScanner !== null) { + const symbols: SymbolContent[] = this._symbolScanner.symbols.filter((obj: SymbolContent): boolean => { + return obj.symbolName === symbol + }) + definitions = this.createDefinitionForSymbolContentList(symbols) + } else { + logger.debug(`Cannot create definitions for symbol ${symbol}: symbol scanner is null`) } - private createDefinitionForElementInfo(elementInfos: ElementInfo[]): Definition { - let definition: Definition = null; - - if ((elementInfos !== undefined) && (elementInfos.length > 0)) { - if (elementInfos.length > 1) { - definition = new Array < Location > (); - - for (let elementInfo of elementInfos) { - logger.debug(`definition ${JSON.stringify(elementInfo)}`); - let location: Location = this.createDefinitionLocationForPathInfo(elementInfo.path); + return definitions + } - definition.push(location); - } - } else { - definition = this.createDefinitionLocationForPathInfo(elementInfos[0].path); - } - } + private createDefinitionForElementInfo (elementInfos: ElementInfo[]): Definition { + const definition: Definition = [] - return definition; + for (const elementInfo of elementInfos) { + logger.debug(`definition ${JSON.stringify(elementInfo)}`) + if (elementInfo.path !== undefined) { + const location: Location = this.createDefinitionLocationForPathInfo(elementInfo.path) + definition.push(location) + } } - private createDefinitionLocationForPathInfoList(pathInfoList: PathInfo[]): Definition { - let definition: Definition = null; + return definition + } - if ((pathInfoList !== undefined) && (pathInfoList.length > 0)) { - if (pathInfoList.length > 1) { - definition = new Array < Location > (); + private createDefinitionLocationForPathInfoList (pathInfoList: PathInfo[]): Definition { + let definition: Definition = [] - for (let pathInfo of pathInfoList) { - logger.debug(`definition ${JSON.stringify(pathInfo)}`); - let location: Location = this.createDefinitionLocationForPathInfo(pathInfo); + if ((pathInfoList !== undefined) && (pathInfoList.length > 0)) { + if (pathInfoList.length > 1) { + definition = new Array < Location >() - definition.push(location); - } - } else { - definition = this.createDefinitionLocationForPathInfo(pathInfoList[0]); - } - } + for (const pathInfo of pathInfoList) { + logger.debug(`definition ${JSON.stringify(pathInfo)}`) + const location: Location = this.createDefinitionLocationForPathInfo(pathInfo) - return definition; - } - - private createDefinitionLocationForPathInfo(path: PathInfo): Location { - let url: string = 'file://' + path.dir + '/' + path.base; - let location: Location = Location.create(encodeURI(url), Range.create(0, 0, 0, 0)); - - return location; + definition.push(location) + } + } else { + definition = this.createDefinitionLocationForPathInfo(pathInfoList[0]) + } } - private createDefinitionForSymbolContentList(symbolContent: SymbolContent[]): Definition { - let definition: Definition = null; + return definition + } - if ((symbolContent !== undefined) && (symbolContent.length > 0)) { - if (symbolContent.length > 1) { - definition = new Array < Location > (); + private createDefinitionLocationForPathInfo (path: PathInfo): Location { + const url: string = 'file://' + path.dir + '/' + path.base + const location: Location = Location.create(encodeURI(url), Range.create(0, 0, 0, 0)) - for (let element of symbolContent) { - logger.debug(`definition ${JSON.stringify(element)}`); - let location: Location = this.createDefinitionForSymbolContent(element); + return location + } - definition.push(location); - } - } else { - definition = this.createDefinitionForSymbolContent(symbolContent[0]); - } - } + private createDefinitionForSymbolContentList (symbolContent: SymbolContent[]): Definition { + const definition: Definition = [] - return definition; + for (const element of symbolContent) { + logger.debug(`definition ${JSON.stringify(element)}`) + const location = this.createDefinitionForSymbolContent(element) + if (location !== undefined) { + definition.push(location) + } } - private createDefinitionForSymbolContent(symbolContent: SymbolContent): Location { - let url: string = 'file://' + symbolContent.filePath; - let range: Range = Range.create(symbolContent.lineNumber, symbolContent.startPosition, - symbolContent.lineNumber, symbolContent.endPostion); + return definition + } - return Location.create(encodeURI(url), range); + private createDefinitionForSymbolContent (symbolContent: SymbolContent): Location | undefined { + const url: string = 'file://' + symbolContent.filePath + if (symbolContent.lineNumber === undefined) { + return undefined } -} \ No newline at end of file + const range: Range = Range.create(symbolContent.lineNumber, symbolContent.startPosition, + symbolContent.lineNumber, symbolContent.endPostion + ) + + return Location.create(encodeURI(url), range) + } +} diff --git a/server/src/ElementInfo.ts b/server/src/ElementInfo.ts index 2e1af664..d1fce5b3 100644 --- a/server/src/ElementInfo.ts +++ b/server/src/ElementInfo.ts @@ -2,32 +2,26 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; +export interface LayerInfo { + name: string + path: string + priority: number +} +export interface PathInfo { + root: string + dir: string + base: string + ext: string + name: string +} - -export type LayerInfo = { - name: string, - path: string, - priority: number -}; - - -export type PathInfo = { - root: string, - dir: string, - base: string, - ext: string, - name: string -}; - - -export type ElementInfo = { - name: string, - extraInfo ? : string, - path ? : PathInfo, - layerInfo ? : LayerInfo, - appends ? : PathInfo[], - overlayes ? : PathInfo[], - version ? : string -}; \ No newline at end of file +export interface ElementInfo { + name: string + extraInfo?: string + path?: PathInfo + layerInfo?: LayerInfo + appends?: PathInfo[] + overlayes?: PathInfo[] + version?: string +} diff --git a/server/src/OutputParser.ts b/server/src/OutputParser.ts index 51a2598d..8e586e7d 100644 --- a/server/src/OutputParser.ts +++ b/server/src/OutputParser.ts @@ -2,98 +2,100 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; - -var logger = require('winston'); +import logger from 'winston' import { - Connection, -} from "vscode-languageserver"; + type Connection +} from 'vscode-languageserver' import { - ProblemsContainer -} from "./ProblemsContainer"; + type ProblemType, + ProblemsContainer +} from './ProblemsContainer' export class OutputParser { - - _problems: ProblemsContainer[] = []; - _connection: Connection; - - constructor(connection: Connection) { - this._connection = connection; - } - - parse(message: string) { - const regex: RegExp = /\s(WARNING:|ERROR:)\s(.*)/g; - let m; - this.clearAllProblemsAndReport(); - - while ((m = regex.exec(message)) !== null) { - // This is necessary to avoid infinite loops with zero-width matches - if (m.index === regex.lastIndex) { - regex.lastIndex++; - } - - let tempProblemContainer: ProblemsContainer[] = ProblemsContainer.createProblemContainer(m[1], m[2]); - - tempProblemContainer.forEach( (container: ProblemsContainer) => { - - let element: ProblemsContainer = this._problems.find( (other:ProblemsContainer) => { - if(other.url === container.url) { - return true; - } - else { - return false; - } - }); - - if( element ) { - element.problems = element.problems.concat( container.problems ); - } - else { - this._problems.push(container) - } - }); - + _problems: ProblemsContainer[] = [] + _connection: Connection + + constructor (connection: Connection) { + this._connection = connection + } + + parse (message: string): void { + const regex: RegExp = /\s(WARNING:|ERROR:)\s(.*)/g + let m + this.clearAllProblemsAndReport() + + while ((m = regex.exec(message)) !== null) { + // This is necessary to avoid infinite loops with zero-width matches + if (m.index === regex.lastIndex) { + regex.lastIndex++ + } + + let problemType: ProblemType + if (m[1] === 'ERROR:') { + problemType = 'error' + } else if (m[1] === 'WARNING:') { + problemType = 'warning' + } else { + return + } + + const tempProblemContainer: ProblemsContainer[] = ProblemsContainer.createProblemContainer(problemType, m[2]) + + tempProblemContainer.forEach((container: ProblemsContainer) => { + const element: ProblemsContainer | undefined = this._problems.find((other: ProblemsContainer) => { + if (other.url === container.url) { + return true + } else { + return false + } + }) + + if (element !== undefined) { + element.problems = element.problems.concat(container.problems) + } else { + this._problems.push(container) } + }) } + } - errorsFound(): boolean { - let errorFound: boolean = false; - var BreakException = {}; - - try { - this._problems.forEach((container: ProblemsContainer) => { - if (container.containsErrors() === true) { - errorFound = true; - throw BreakException; - } - }); - } catch (error) { - if (error !== BreakException) { - throw error; - } - } + errorsFound (): boolean { + let errorFound: boolean = false + const BreakException = new Error() - return errorFound; - } - - reportProblems() { - logger.debug(`reportProblems: ${this._problems.length}`); - this._problems.forEach((container: ProblemsContainer) => { - logger.debug(`send Diagnostic ${container.toString()}`); - this._connection.sendDiagnostics(container.getDignosticData()); - }); - } - - clearAllProblemsAndReport() { - logger.debug(`clearAllProblems: ${this._problems.length}`); - this._problems.forEach((container: ProblemsContainer) => { - logger.debug(`send Diagnostic ${container.toString()}`); - this._connection.sendDiagnostics(container.getClearedDiagnosticData()); - }); - - this._problems = []; + try { + this._problems.forEach((container: ProblemsContainer) => { + if (container.containsErrors()) { + errorFound = true + throw BreakException + } + }) + } catch (error) { + if (error !== BreakException) { + throw error + } } -} \ No newline at end of file + return errorFound + } + + reportProblems (): void { + logger.debug(`reportProblems: ${this._problems.length}`) + this._problems.forEach((container: ProblemsContainer) => { + logger.debug(`send Diagnostic ${container.toString()}`) + void this._connection.sendDiagnostics(container.getDignosticData()) + }) + } + + clearAllProblemsAndReport (): void { + logger.debug(`clearAllProblems: ${this._problems.length}`) + this._problems.forEach((container: ProblemsContainer) => { + logger.debug(`send Diagnostic ${container.toString()}`) + void this._connection.sendDiagnostics(container.getClearedDiagnosticData()) + }) + + this._problems = [] + } +} diff --git a/server/src/ProblemsContainer.ts b/server/src/ProblemsContainer.ts index 80b44d2f..904a942c 100644 --- a/server/src/ProblemsContainer.ts +++ b/server/src/ProblemsContainer.ts @@ -2,134 +2,139 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; import { - Diagnostic, - DiagnosticSeverity, - PublishDiagnosticsParams -} from "vscode-languageserver"; + type Diagnostic, + DiagnosticSeverity, + type PublishDiagnosticsParams +} from 'vscode-languageserver' +export type ProblemType = 'error' | 'warning' export class ProblemsContainer { - _url: string = 'file://'; - _problems: Diagnostic[] = [] - - get url() { - return this._url; - } - - get problems() { - return this._problems; - } - - set problems(problems: Diagnostic[]) { - this._problems = problems; + _url: string = 'file://' + _problems: Diagnostic[] = [] + + get url (): string { + return this._url + } + + get problems (): Diagnostic[] { + return this._problems + } + + set problems (problems: Diagnostic[]) { + this._problems = problems + } + + appendDiagnostic (diagnostic: Diagnostic): void { + this._problems.push(diagnostic) + } + + containsErrors (): boolean { + let errorFound: boolean = false + const BreakException = new Error() + + try { + this._problems.forEach((value: Diagnostic) => { + if (value.severity === DiagnosticSeverity.Error) { + errorFound = true + throw BreakException + } + }) + } catch (error) { + if (error !== BreakException) { + throw error + } } - appendDiagnostic(diagnostic: Diagnostic) { - this._problems.push(diagnostic); - } + return errorFound + } - containsErrors(): boolean { - let errorFound: boolean = false; - var BreakException = {}; - - try { - this._problems.forEach((value: Diagnostic) => { - if (value.severity === DiagnosticSeverity.Error) { - errorFound = true; - throw BreakException; - } - }); - } catch (error) { - if (error !== BreakException) { - throw error; - } - } - - return errorFound; + getDignosticData (): PublishDiagnosticsParams { + return { + uri: this._url, + diagnostics: this._problems } + } - getDignosticData(): PublishDiagnosticsParams { - return { - uri: this._url, - diagnostics: this._problems - }; + getClearedDiagnosticData (): PublishDiagnosticsParams { + return { + uri: this._url, + diagnostics: [] } - - getClearedDiagnosticData(): PublishDiagnosticsParams { - return { - uri: this._url, - diagnostics: [] - }; + } + + toString (): string { + let objectAsString: string = `{uri:${this._url} problems:[` + + this._problems.forEach((problem: Diagnostic) => { + objectAsString += `${problem.message},` + }) + + objectAsString += '}' + return objectAsString + } + + static createProblemContainer (type: ProblemType, message: string): ProblemsContainer[] { + const regex = /(ParseError)(?:\s|\w)*\s(\/.*\..*):(\d):\s(.*)/g + let m + const problemContainer: ProblemsContainer[] = [] + + while ((m = regex.exec(message)) !== null) { + // This is necessary to avoid infinite loops with zero-width matches + if (m.index === regex.lastIndex) { + regex.lastIndex++ + } + + const problem = new ProblemsContainer() + problem._url = encodeURI('file://' + m[2]) + problem.appendDiagnostic(this.createProblemElement(type, m[4], Number.parseInt(m[3]), m[1])) + problemContainer.push(problem) } - - toString(): string { - let objectAsString: string = `{uri:${this._url} problems:[` - - this._problems.forEach((problem: Diagnostic) => { - objectAsString += `${problem.message},`; - }); - - objectAsString += '}'; - return objectAsString; + if (problemContainer.length === 0) { + const problem = new ProblemsContainer() + problem.appendDiagnostic(this.createProblemElement(type, message)) + problemContainer.push(problem) } - static createProblemContainer(type: string, message: string): ProblemsContainer[] { - const regex = /(ParseError)(?:\s|\w)*\s(\/.*\..*):(\d):\s(.*)/g; - let m; - let problemContainer: ProblemsContainer[] = []; - - while ((m = regex.exec(message)) !== null) { - // This is necessary to avoid infinite loops with zero-width matches - if (m.index === regex.lastIndex) { - regex.lastIndex++; - } - - let problem = new ProblemsContainer(); - problem._url = encodeURI('file://' + m[2]); - problem.appendDiagnostic(this.createProblemElement(type, m[4], Number.parseInt(m[3]), m[1])); - problemContainer.push(problem); - } - - if( problemContainer.length === 0) { - let problem = new ProblemsContainer(); - problem.appendDiagnostic(this.createProblemElement(type, message)); - problemContainer.push( problem ); - } - - return problemContainer; + return problemContainer + } + + private static createProblemElement ( + type: ProblemType, + message: string, + lineNumber: number = 1, + problemCode: string = 'general' + ): Diagnostic { + let problemSeverity: DiagnosticSeverity + + if (type === 'error') { + problemSeverity = DiagnosticSeverity.Error + } else if (type === 'warning') { + problemSeverity = DiagnosticSeverity.Warning + } else { + const _exhaustiveCheck: never = type + return _exhaustiveCheck } - private static createProblemElement(type: string, message: string, - lineNumber: number =1, problemCode: string = 'general'): Diagnostic { - let problemSeverity: DiagnosticSeverity; - - if (type === 'ERROR:') { - problemSeverity = DiagnosticSeverity.Error; - } else if (type === 'WARNING:') { - problemSeverity = DiagnosticSeverity.Warning; + const problem: Diagnostic = { + range: { + start: { + line: lineNumber, + character: lineNumber + }, + end: { + line: lineNumber, + character: lineNumber } - - let problem: Diagnostic = { - range: { - start: { - line: lineNumber, - character: lineNumber - }, - end: { - line: lineNumber, - character: lineNumber - } - }, - severity: problemSeverity, - message: message, - code: problemCode - } - - return problem; + }, + severity: problemSeverity, + message, + code: problemCode } -} \ No newline at end of file + return problem + } +} diff --git a/server/src/SymbolScanner.ts b/server/src/SymbolScanner.ts index c760509b..b4c39623 100644 --- a/server/src/SymbolScanner.ts +++ b/server/src/SymbolScanner.ts @@ -2,192 +2,188 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; -const url = require('url') -const fs = require('fs'); +import fs from 'fs' import { - BasicKeywordMap -} from "./BasicKeywordMap"; + BasicKeywordMap +} from './BasicKeywordMap' + +import type { + CompletionItem, + Definition, + Location +} from 'vscode-languageserver' + +import type { + DefinitionProvider +} from './DefinitionProvider' + +import logger from 'winston' + +interface FileContent { + filePath: string + fileContent: string[] +} + +export interface SymbolContent { + symbolName: string + startPosition: number + endPostion: number + filePath?: string + lineNumber?: number +} -import { - CompletionItem, - Definition, - Location -} from 'vscode-languageserver'; - -import { - DefinitionProvider -} from "./DefinitionProvider"; +export class SymbolScanner { + private readonly _fileContent: FileContent[] = new Array < FileContent >() + private readonly _definitionProvider: DefinitionProvider + private readonly _symbolsDefinition: SymbolContent[] = new Array < SymbolContent >() -var logger = require('winston'); + constructor (fileUrlAsString: string, definitionProvider: DefinitionProvider) { + logger.debug(`scan for symbols file: ${fileUrlAsString}`) -type FileContent = { - filePath: string, - fileContent: string[] -}; + this._definitionProvider = definitionProvider -export type SymbolContent = { - symbolName: string, - startPosition: number, - endPostion: number, - filePath ? : string, - lineNumber ? : number -}; + this.extendsFile(this.convertUriStringToFilePath(fileUrlAsString)) + this.scanForSymbols() + } -export class SymbolScanner { + get symbols (): SymbolContent[] { + return this._symbolsDefinition + } - private _fileContent: FileContent[] = new Array < FileContent > (); - private _definitionProvider: DefinitionProvider = null; - private _symbolsDefinition: SymbolContent[] = new Array < SymbolContent > (); + private extendsFile (filePath: string): void { + logger.debug(`extendsFile file: ${filePath}`) - constructor(fileUrlAsString: string, definitionProvider: DefinitionProvider) { - logger.debug(`scan for symbols file: ${fileUrlAsString}`); + try { + const data: Buffer = fs.readFileSync(filePath) + const file: string[] = data.toString().split(/\r?\n/g) - this._definitionProvider = definitionProvider; + this._fileContent.push({ + filePath, + fileContent: file + }) - this.extendsFile(this.convertUriStringToFilePath(fileUrlAsString)); - this.scanForSymbols(); - } + for (const line of file) { + const keyword = this.lineContainsKeyword(line) - get symbols(): SymbolContent[] { - return this._symbolsDefinition; + if (keyword !== undefined) { + logger.debug(`keyword found: ${keyword}`) + this.handleKeyword(keyword, line) + } + } + } catch (error) { + if (typeof error !== 'string') { + throw error + } + logger.error(`can not open file error: ${error}`) } + } - private extendsFile(filePath: string) { - logger.debug(`extendsFile file: ${filePath}`); - - try { - let data: Buffer = fs.readFileSync(filePath); - let file: string[] = data.toString().split(/\r?\n/g); - - this._fileContent.push({ - filePath: filePath, - fileContent: file - }); - - for (let line of file) { - let keyword = this.lineContainsKeyword(line); + private lineContainsKeyword (line: string): string | undefined { + const keywords: CompletionItem[] = BasicKeywordMap - if (keyword !== undefined) { - logger.debug(`keyword found: ${keyword}`); - this.handleKeyword(keyword, line); - } - } + const trimedLine = line.trim() - } catch (error) { - logger.error(`can not open file error: ${error}`) - } + for (const keyword of keywords) { + if (trimedLine.startsWith(keyword.label)) { + return keyword.label + } } - private lineContainsKeyword(line: string): string { - let foundKeyword: string = undefined; - let keywords: CompletionItem[] = BasicKeywordMap; + return undefined + } - let trimedLine = line.trim(); + private handleKeyword (keyword: string, line: string): void { + const restOfLine: string[] = line.split(keyword).filter(String) - for (let keyword of keywords) { - if (trimedLine.startsWith(keyword.label) === true) { - foundKeyword = keyword.label; - } - } + if (restOfLine.length === 1) { + const listOfSymbols: string[] = restOfLine[0].split(' ').filter(String) + let definition: Definition = new Array < Location >() - return foundKeyword; - } - - private handleKeyword(keyword: string, line: string) { - let restOfLine: string[] = line.split(keyword).filter(String); - - if (restOfLine.length === 1) { - let listOfSymbols: string[] = restOfLine[0].split(' ').filter(String); - let definition: Definition = new Array < Location > (); - - if (listOfSymbols.length === 1) { - definition = definition.concat(this._definitionProvider.createDefinitionForKeyword(keyword, restOfLine[0])); - } else if (listOfSymbols.length > 1) { - for (let symbol of listOfSymbols) { - definition = definition.concat(this._definitionProvider.createDefinitionForKeyword(keyword, restOfLine[0], symbol)); - } - } - - for (let location of definition) { - if (location !== null) { - this.extendsFile(this.convertUriStringToFilePath(location.uri)); - } - } + if (listOfSymbols.length === 1) { + definition = definition.concat(this._definitionProvider.createDefinitionForKeyword(keyword, restOfLine[0])) + } else if (listOfSymbols.length > 1) { + for (const symbol of listOfSymbols) { + definition = definition.concat(this._definitionProvider.createDefinitionForKeyword(keyword, restOfLine[0], symbol)) } + } + for (const location of definition) { + if (location !== null) { + this.extendsFile(this.convertUriStringToFilePath(location.uri)) + } + } } + } - private convertUriStringToFilePath(fileUrlAsString: string): string { - let fileUrl = url.parse(fileUrlAsString); - let filePath: string = decodeURI(fileUrl.pathname); + private convertUriStringToFilePath (fileUrlAsString: string): string { + const fileUrl = new URL(fileUrlAsString) + const filePath: string = decodeURI(fileUrl.pathname) - return filePath; - } + return filePath + } - private scanForSymbols() { - for (let file of this._fileContent) { - for (let line of file.fileContent) { - let lineIndex: number = file.fileContent.indexOf(line); - const regex = /^\s*(?:export)?\s*(\w*(?:\[\w*\])?)\s*(?:=|:=|\+=|=\+|-=|=-|\?=|\?\?=|\.=|=\.)/g; - let symbolContent: SymbolContent = this.investigateLine(line, regex); + private scanForSymbols (): void { + for (const file of this._fileContent) { + for (const line of file.fileContent) { + const lineIndex: number = file.fileContent.indexOf(line) + const regex = /^\s*(?:export)?\s*(\w*(?:\[\w*\])?)\s*(?:=|:=|\+=|=\+|-=|=-|\?=|\?\?=|\.=|=\.)/g + const symbolContent = this.investigateLine(line, regex) - if (symbolContent !== undefined) { - symbolContent.filePath = file.filePath; - symbolContent.lineNumber = lineIndex; + if (symbolContent !== undefined) { + symbolContent.filePath = file.filePath + symbolContent.lineNumber = lineIndex - this._symbolsDefinition.push(symbolContent); - } - } + this._symbolsDefinition.push(symbolContent) } + } } - - private investigateLine(lineString: string, regex: RegExp): SymbolContent { - let symbolContent: SymbolContent = undefined; - - let m; - - while ((m = regex.exec(lineString)) !== null) { - // This is necessary to avoid infinite loops with zero-width matches - if (m.index === regex.lastIndex) { - regex.lastIndex++; - } - - if (m.length === 2) { - let symbol: string = m[1]; - let symbolStartPosition: number = lineString.indexOf(symbol); - let symbolEndPosition: number = symbolStartPosition + symbol.length; - let filterdSymbolName: string = this.filterSymbolName(symbol); - - symbolContent = { - symbolName: filterdSymbolName, - startPosition: symbolStartPosition, - endPostion: symbolEndPosition - }; - } + } + + private investigateLine (lineString: string, regex: RegExp): SymbolContent | undefined { + let m + + while ((m = regex.exec(lineString)) !== null) { + // This is necessary to avoid infinite loops with zero-width matches + if (m.index === regex.lastIndex) { + regex.lastIndex++ + } + + if (m.length === 2) { + const symbol: string = m[1] + const filterdSymbolName = this.filterSymbolName(symbol) + if (filterdSymbolName === undefined) { + return undefined } + const symbolStartPosition: number = lineString.indexOf(symbol) + const symbolEndPosition: number = symbolStartPosition + symbol.length - return symbolContent; + return { + symbolName: filterdSymbolName, + startPosition: symbolStartPosition, + endPostion: symbolEndPosition + } + } } - private filterSymbolName(symbol: string): string { - const regex = /^\w*/g; - let m; - let filterdSymbolName: string = undefined; + return undefined + } - while ((m = regex.exec(symbol)) !== null) { - // This is necessary to avoid infinite loops with zero-width matches - if (m.index === regex.lastIndex) { - regex.lastIndex++; - } + private filterSymbolName (symbol: string): string | undefined { + const regex = /^\w*/g + let m + let filterdSymbolName: string | undefined - filterdSymbolName = m[0]; - } + while ((m = regex.exec(symbol)) !== null) { + // This is necessary to avoid infinite loops with zero-width matches + if (m.index === regex.lastIndex) { + regex.lastIndex++ + } - return filterdSymbolName; + filterdSymbolName = m[0] } - -} \ No newline at end of file + return filterdSymbolName + } +} diff --git a/server/src/server.ts b/server/src/server.ts index 959ff57f..6581b8dc 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -2,176 +2,160 @@ * Copyright (c) Eugen Wiens. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -'use strict'; import { - createConnection, - Connection, - TextDocuments, - InitializeResult, - TextDocumentPositionParams, - CompletionItem, - Definition, - ProposedFeatures, - TextDocumentSyncKind -} from 'vscode-languageserver/node'; - -import { - BitBakeProjectScanner -} from "./BitBakeProjectScanner"; - -import { - ContextHandler -} from "./ContextHandler"; - -import { - SymbolScanner -} from "./SymbolScanner"; - -import { - TextDocument -} from 'vscode-languageserver-textdocument'; - -var logger = require('winston'); + createConnection, + type Connection, + TextDocuments, + type InitializeResult, + type TextDocumentPositionParams, + type CompletionItem, + type Definition, + ProposedFeatures, + TextDocumentSyncKind +} from 'vscode-languageserver/node' +import { BitBakeProjectScanner } from './BitBakeProjectScanner' +import { ContextHandler } from './ContextHandler' +import { SymbolScanner } from './SymbolScanner' +import { TextDocument } from 'vscode-languageserver-textdocument' +import logger from 'winston' // Create a connection for the server. The connection uses Node's IPC as a transport -let connection: Connection = createConnection(ProposedFeatures.all); -let documents: TextDocuments = new TextDocuments(TextDocument); -let documentMap: Map < string, string[] > = new Map(); -let bitBakeProjectScanner: BitBakeProjectScanner = new BitBakeProjectScanner(connection); -let contextHandler: ContextHandler = new ContextHandler(bitBakeProjectScanner); -let workspaceRoot: string; -let symbolScanner: SymbolScanner = null; - -documents.listen(connection); +const connection: Connection = createConnection(ProposedFeatures.all) +const documents = new TextDocuments(TextDocument) +const documentMap = new Map< string, string[] >() +const bitBakeProjectScanner: BitBakeProjectScanner = new BitBakeProjectScanner(connection) +const contextHandler: ContextHandler = new ContextHandler(bitBakeProjectScanner) +documents.listen(connection) connection.onInitialize((params): InitializeResult => { - workspaceRoot = params.rootPath; - bitBakeProjectScanner.setProjectPath(workspaceRoot); - - setTimeout( () => { - bitBakeProjectScanner.rescanProject(); - }, 500); - - return { - capabilities: { - textDocumentSync: TextDocumentSyncKind.Incremental, - completionProvider: { - resolveProvider: true - }, - definitionProvider: true, - executeCommandProvider: { - commands: [ - 'bitbake.rescan-project' - ] - } - } - } -}); + const workspaceRoot = params.rootPath ?? '' + bitBakeProjectScanner.setProjectPath(workspaceRoot) + + setTimeout(() => { + bitBakeProjectScanner.rescanProject() + }, 500) + + return { + capabilities: { + textDocumentSync: TextDocumentSyncKind.Incremental, + completionProvider: { + resolveProvider: true + }, + definitionProvider: true, + executeCommandProvider: { + commands: [ + 'bitbake.rescan-project' + ] + } + } + } +}) // The content of a text document has changed. This event is emitted // when the text document first opened or when its content has changed. documents.onDidChangeContent((change) => { - //TODO: add symbol parsing here - logger.debug(`onDidChangeContent: ${JSON.stringify(change)}`); -}); + // TODO: add symbol parsing here + logger.debug(`onDidChangeContent: ${JSON.stringify(change)}`) +}) // The settings interface describe the server relevant settings part interface Settings { - bitbake: BitbakeSettings; + bitbake: BitbakeSettings } interface BitbakeSettings { - loggingLevel: string; - deepExamine: boolean; - workingFolder: string; - pathToBashScriptInterpreter: string; - machine: string; - generateWorkingFolder: boolean; + loggingLevel: string + deepExamine: boolean + workingFolder: string + pathToBashScriptInterpreter: string + machine: string + generateWorkingFolder: boolean } -function setSymbolScanner( newSymbolScanner: SymbolScanner ) { - logger.debug( 'set new symbol scanner'); - symbolScanner = newSymbolScanner; - contextHandler.symbolScanner = symbolScanner; +function setSymbolScanner (newSymbolScanner: SymbolScanner | null): void { + logger.debug('set new symbol scanner') + contextHandler.symbolScanner = newSymbolScanner } - connection.onDidChangeConfiguration((change) => { - let settings = < Settings > change.settings; - bitBakeProjectScanner.deepExamine = settings.bitbake.deepExamine; - logger.level = settings.bitbake.loggingLevel; - bitBakeProjectScanner.workingPath = settings.bitbake.workingFolder; - bitBakeProjectScanner.generateWorkingPath = settings.bitbake.generateWorkingFolder; - bitBakeProjectScanner.scriptInterpreter = settings.bitbake.pathToBashScriptInterpreter; - bitBakeProjectScanner.machineName = settings.bitbake.machine; -}); + const settings = change.settings as Settings + bitBakeProjectScanner.deepExamine = settings.bitbake.deepExamine + logger.level = settings.bitbake.loggingLevel + bitBakeProjectScanner.workingPath = settings.bitbake.workingFolder + bitBakeProjectScanner.generateWorkingPath = settings.bitbake.generateWorkingFolder + bitBakeProjectScanner.scriptInterpreter = settings.bitbake.pathToBashScriptInterpreter + bitBakeProjectScanner.machineName = settings.bitbake.machine +}) connection.onDidChangeWatchedFiles((change) => { - logger.debug(`onDidChangeWatchedFiles: ${JSON.stringify(change)}`); - bitBakeProjectScanner.rescanProject(); -}); + logger.debug(`onDidChangeWatchedFiles: ${JSON.stringify(change)}`) + bitBakeProjectScanner.rescanProject() +}) connection.onCompletion((textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => { - logger.debug('onCompletion'); - let documentAsStringArray: string[] = documentMap.get(textDocumentPosition.textDocument.uri); - return contextHandler.getComletionItems(textDocumentPosition, documentAsStringArray); -}); + logger.debug('onCompletion') + const documentAsStringArray = documentMap.get(textDocumentPosition.textDocument.uri) + if (documentAsStringArray === undefined) { + return [] + } + return contextHandler.getComletionItems(textDocumentPosition, documentAsStringArray) +}) connection.onCompletionResolve((item: CompletionItem): CompletionItem => { - logger.debug(`onCompletionResolve ${JSON.stringify(item)}`); + logger.debug(`onCompletionResolve ${JSON.stringify(item)}`) - item.insertText = contextHandler.getInsertStringForTheElement(item); + item.insertText = contextHandler.getInsertStringForTheElement(item) - return item; -}); + return item +}) connection.onDidOpenTextDocument((params) => { - if (params.textDocument.text.length > 0) { - documentMap.set(params.textDocument.uri, params.textDocument.text.split(/\r?\n/g)); - } + if (params.textDocument.text.length > 0) { + documentMap.set(params.textDocument.uri, params.textDocument.text.split(/\r?\n/g)) + } - setSymbolScanner( new SymbolScanner(params.textDocument.uri, contextHandler.definitionProvider) ); -}); + setSymbolScanner(new SymbolScanner(params.textDocument.uri, contextHandler.definitionProvider)) +}) connection.onDidChangeTextDocument((params) => { - if (params.contentChanges.length > 0) { - documentMap.set(params.textDocument.uri, params.contentChanges[0].text.split(/\r?\n/g)); - } - - setSymbolScanner( new SymbolScanner(params.textDocument.uri, contextHandler.definitionProvider) ); -}); + if (params.contentChanges.length > 0) { + documentMap.set(params.textDocument.uri, params.contentChanges[0].text.split(/\r?\n/g)) + } + + setSymbolScanner(new SymbolScanner(params.textDocument.uri, contextHandler.definitionProvider)) +}) connection.onDidCloseTextDocument((params) => { - documentMap.delete(params.textDocument.uri); - setSymbolScanner( null ); -}); + documentMap.delete(params.textDocument.uri) + setSymbolScanner(null) +}) connection.onDidSaveTextDocument((params) => { - logger.debug(`onDidSaveTextDocument ${JSON.stringify(params)}`); + logger.debug(`onDidSaveTextDocument ${JSON.stringify(params)}`) - bitBakeProjectScanner.parseAllRecipes(); -}); + bitBakeProjectScanner.parseAllRecipes() +}) connection.onExecuteCommand((params) => { - logger.info(`executeCommand ${JSON.stringify(params)}`); + logger.info(`executeCommand ${JSON.stringify(params)}`) - if (params.command === 'bitbake.rescan-project') { - bitBakeProjectScanner.rescanProject(); - } -}); + if (params.command === 'bitbake.rescan-project') { + bitBakeProjectScanner.rescanProject() + } +}) connection.onDefinition((textDocumentPositionParams: TextDocumentPositionParams): Definition => { - logger.debug(`onDefinition ${JSON.stringify(textDocumentPositionParams)}`); - let documentAsText: string[] = documentMap.get(textDocumentPositionParams.textDocument.uri); - - let definition: Definition = contextHandler.getDefinition(textDocumentPositionParams, documentAsText);; - - return definition; -}); + logger.debug(`onDefinition ${JSON.stringify(textDocumentPositionParams)}`) + const documentAsText = documentMap.get(textDocumentPositionParams.textDocument.uri) + if (documentAsText === undefined) { + return [] + } + return contextHandler.getDefinition(textDocumentPositionParams, documentAsText) +}) // Listen on the connection -connection.listen(); +connection.listen() diff --git a/server/tsconfig.json b/server/tsconfig.json index bc869fe9..fa31b377 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -1,17 +1,16 @@ { - "compilerOptions": { - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "target": "ES2020", - "module": "commonjs", - "moduleResolution": "node", - "sourceMap": true, - "rootDir": "src", - "outDir": "out" - }, - "exclude": [ - "node_modules" - ] + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "rootDir": "src", + "outDir": "out", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src", ".eslintrc.js"], + "exclude": ["node_modules"] }