From feae95efbb72852487b62d994c83dbb51fcb2ec1 Mon Sep 17 00:00:00 2001 From: ecmel Date: Sat, 30 Dec 2023 13:13:19 +0300 Subject: [PATCH] refactorings for v2 --- README.md | 146 +------- package-lock.json | 682 ++++++++++++++-------------------- package.json | 16 +- src/completer.ts | 144 +++++++ src/completion.ts | 315 ---------------- src/extension.ts | 44 +-- src/parser.ts | 30 ++ src/settings.ts | 18 + test/suite/completion.test.ts | 219 ----------- test/suite/extension.test.ts | 9 +- test/suite/mocks.ts | 78 ---- 11 files changed, 500 insertions(+), 1201 deletions(-) create mode 100644 src/completer.ts delete mode 100644 src/completion.ts create mode 100644 src/parser.ts create mode 100644 src/settings.ts delete mode 100644 test/suite/completion.test.ts delete mode 100644 test/suite/mocks.ts diff --git a/README.md b/README.md index fcd1827..90dec2a 100644 --- a/README.md +++ b/README.md @@ -7,130 +7,17 @@ HTML `id` and `class` attribute completion for Visual Studio Code. ## Features - HTML `id` and `class` attribute completion. -- Supports linked and embedded style sheets. -- Supports template inheritance. -- Supports additional style sheets. -- Supports other HTML like languages. +- Supports specifying remote and local style sheets for completion. +- Supports any language for completion. - Validates CSS selectors on demand. ## Usage -You can view a list of `id` and `class` attribute suggestions via `ctrl + space`. - -## Linked and Embedded Style Sheets - -Linked `[]` and embedded `[]` style sheets are used in completion for `id` and `class` attributes. Links support local and remote files. Absolute local file paths are relative to the workspace folder while others are relative to HTML file: - -**`index.html`** - -```html - - - - - - - - - - - - - - - - - -
-
-
1 of 2
-
2 of 2
-
-
- - -``` - -All local links point to the same file which is in the root of workspace folder: - -**`style.css`** - -```css -.external { - display: block; -} -``` - -## Template Inheritance - -Template inheritance is supported for the following tags: - -``` -{% extends "base" %} - -@extends('base') - -{{< base }} - -{{> base }} -``` - -Styles defined in `base.html` will also be available for completion in `home.html`: - -**`base.html`** - -```html - - - - +You can view a list of `id` and `class` attribute suggestions in configured languages. - - - {{ title }} - - - - {% block content %}{% endblock %} - - -``` - -**`home.html`** - -```html -{% extends "base" %} {% block content %} -
-

Home

-
-{% endblock %} -``` - -## Additional Style Sheets - -If it is not possible to specify local or remote styles in HTML or via template inheritance, they can be specified in VS Code settings per workspace folder in `.vscode/settings.json` and will suggest for all HTML files within that workspace folder. +Stylesheets can be specified in VS Code settings per workspace folder in `.vscode/settings.json` and will suggest for all configured languages within that workspace folder. ### Example @@ -139,26 +26,13 @@ If it is not possible to specify local or remote styles in HTML or via template ```json { "css.styleSheets": [ - "https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css", - "/style.css", - "style.css", - "${fileBasenameNoExtension}.css" + "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css", + "src/**/*.scss", + "dist/style.css" ] } ``` -All relative paths will be evaluated relative to the file being edited. `${fileBasenameNoExtension}` will be replaced with the file name of the file being edited without extension. - -Absolute paths can include glob patterns: - -```json -{ - "css.styleSheets": ["/dist/*.css"] -} -``` - -Note that absolute paths are still relative to the workspace folder. - ## Supported Languages Supported languages can be configured with the `css.enabledLanguages` setting. By default `html` is enabled: @@ -169,7 +43,7 @@ Supported languages can be configured with the `css.enabledLanguages` setting. B } ``` -Extension can be configured to support any language where it makes sense such as `nunjucks`, `twig`, `mustache`, etc. You should also install corresponding language extension which registers the language id in VS Code. +Extension can be configured to support any language where it makes sense such as `nunjucks`, `twig`, `mustache`, `typescript` etc. You should also install corresponding language extension which registers the language id in VS Code. This setting is application scoped and changing the setting requires restarting VS Code. @@ -181,7 +55,7 @@ Validates all `id` and `class` attributes in the active editor. ### Clear Cache -Clears file cache. +Clears stylesheet cache. ## Installation diff --git a/package-lock.json b/package-lock.json index 71fff1e..0c453b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-html-css", - "version": "1.14.2", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-html-css", - "version": "1.14.2", + "version": "2.0.0", "license": "MIT", "devDependencies": { "@rollup/plugin-commonjs": "^25.0.7", @@ -16,15 +16,15 @@ "@types/css-tree": "^2.3.4", "@types/glob": "^8.1.0", "@types/mocha": "^10.0.6", - "@types/node": "^20.10.5", + "@types/node": "^20.10.6", "@types/vscode": "^1.66.0", "@vscode/test-electron": "^2.3.8", "@vscode/vsce": "^2.22.0", "c8": "^8.0.1", "fast-glob": "^3.3.2", "mocha": "^10.2.0", - "prettier": "^3.1.1", - "rollup": "^4.9.1", + "prettier": "^2.8.8", + "rollup": "^4.9.2", "tslib": "^2.6.2", "typescript": "^5.3.3" }, @@ -165,46 +165,6 @@ } } }, - "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.2.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", @@ -301,9 +261,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.1.tgz", - "integrity": "sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.2.tgz", + "integrity": "sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA==", "cpu": [ "arm" ], @@ -314,9 +274,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.1.tgz", - "integrity": "sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.2.tgz", + "integrity": "sha512-yZ+MUbnwf3SHNWQKJyWh88ii2HbuHCFQnAYTeeO1Nb8SyEiWASEi5dQUygt3ClHWtA9My9RQAYkjvrsZ0WK8Xg==", "cpu": [ "arm64" ], @@ -327,9 +287,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.1.tgz", - "integrity": "sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.2.tgz", + "integrity": "sha512-vqJ/pAUh95FLc/G/3+xPqlSBgilPnauVf2EXOQCZzhZJCXDXt/5A8mH/OzU6iWhb3CNk5hPJrh8pqJUPldN5zw==", "cpu": [ "arm64" ], @@ -340,9 +300,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.1.tgz", - "integrity": "sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.2.tgz", + "integrity": "sha512-otPHsN5LlvedOprd3SdfrRNhOahhVBwJpepVKUN58L0RnC29vOAej1vMEaVU6DadnpjivVsNTM5eNt0CcwTahw==", "cpu": [ "x64" ], @@ -353,9 +313,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.1.tgz", - "integrity": "sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.2.tgz", + "integrity": "sha512-ewG5yJSp+zYKBYQLbd1CUA7b1lSfIdo9zJShNTyc2ZP1rcPrqyZcNlsHgs7v1zhgfdS+kW0p5frc0aVqhZCiYQ==", "cpu": [ "arm" ], @@ -366,9 +326,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.1.tgz", - "integrity": "sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.2.tgz", + "integrity": "sha512-pL6QtV26W52aCWTG1IuFV3FMPL1m4wbsRG+qijIvgFO/VBsiXJjDPE/uiMdHBAO6YcpV4KvpKtd0v3WFbaxBtg==", "cpu": [ "arm64" ], @@ -379,9 +339,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.1.tgz", - "integrity": "sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.2.tgz", + "integrity": "sha512-On+cc5EpOaTwPSNetHXBuqylDW+765G/oqB9xGmWU3npEhCh8xu0xqHGUA+4xwZLqBbIZNcBlKSIYfkBm6ko7g==", "cpu": [ "arm64" ], @@ -392,9 +352,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.1.tgz", - "integrity": "sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.2.tgz", + "integrity": "sha512-Wnx/IVMSZ31D/cO9HSsU46FjrPWHqtdF8+0eyZ1zIB5a6hXaZXghUKpRrC4D5DcRTZOjml2oBhXoqfGYyXKipw==", "cpu": [ "riscv64" ], @@ -405,9 +365,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.1.tgz", - "integrity": "sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.2.tgz", + "integrity": "sha512-ym5x1cj4mUAMBummxxRkI4pG5Vht1QMsJexwGP8547TZ0sox9fCLDHw9KCH9c1FO5d9GopvkaJsBIOkTKxksdw==", "cpu": [ "x64" ], @@ -418,9 +378,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.1.tgz", - "integrity": "sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.2.tgz", + "integrity": "sha512-m0hYELHGXdYx64D6IDDg/1vOJEaiV8f1G/iO+tejvRCJNSwK4jJ15e38JQy5Q6dGkn1M/9KcyEOwqmlZ2kqaZg==", "cpu": [ "x64" ], @@ -431,9 +391,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.1.tgz", - "integrity": "sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.2.tgz", + "integrity": "sha512-x1CWburlbN5JjG+juenuNa4KdedBdXLjZMp56nHFSHTOsb/MI2DYiGzLtRGHNMyydPGffGId+VgjOMrcltOksA==", "cpu": [ "arm64" ], @@ -444,9 +404,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.1.tgz", - "integrity": "sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.2.tgz", + "integrity": "sha512-VVzCB5yXR1QlfsH1Xw1zdzQ4Pxuzv+CPr5qpElpKhVxlxD3CRdfubAG9mJROl6/dmj5gVYDDWk8sC+j9BI9/kQ==", "cpu": [ "ia32" ], @@ -457,9 +417,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.1.tgz", - "integrity": "sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.2.tgz", + "integrity": "sha512-SYRedJi+mweatroB+6TTnJYLts0L0bosg531xnQWtklOI6dezEagx4Q0qDyvRdK+qgdA3YZpjjGuPFtxBmddBA==", "cpu": [ "x64" ], @@ -519,9 +479,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", - "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -612,9 +572,9 @@ } }, "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -644,6 +604,15 @@ "node": ">=6" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -860,157 +829,6 @@ "node": ">=12" } }, - "node_modules/c8/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/c8/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/c8/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/c8/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/c8/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/c8/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/c8/node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/c8/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/c8/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/c8/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/c8/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/c8/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/c8/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/call-bind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", @@ -1124,105 +942,17 @@ "optional": true }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cliui/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=12" } }, "node_modules/color-convert": { @@ -1467,6 +1197,12 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1594,6 +1330,19 @@ "flat": "cli.js" } }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -1661,6 +1410,25 @@ "dev": true, "optional": true }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -1673,6 +1441,27 @@ "node": ">= 6" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -2404,6 +2193,17 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2508,6 +2308,33 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2756,15 +2583,15 @@ } }, "node_modules/prettier": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", - "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { - "prettier": "bin/prettier.cjs" + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=14" + "node": ">=10.13.0" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -2968,9 +2795,9 @@ } }, "node_modules/rollup": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.1.tgz", - "integrity": "sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.2.tgz", + "integrity": "sha512-66RB8OtFKUTozmVEh3qyNfH+b+z2RXBVloqO2KCC/pjFaGaHtxP9fVfOQKPSGXg2mElmjmxjW/fZ7iKrEpMH5Q==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -2980,19 +2807,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.1", - "@rollup/rollup-android-arm64": "4.9.1", - "@rollup/rollup-darwin-arm64": "4.9.1", - "@rollup/rollup-darwin-x64": "4.9.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.1", - "@rollup/rollup-linux-arm64-gnu": "4.9.1", - "@rollup/rollup-linux-arm64-musl": "4.9.1", - "@rollup/rollup-linux-riscv64-gnu": "4.9.1", - "@rollup/rollup-linux-x64-gnu": "4.9.1", - "@rollup/rollup-linux-x64-musl": "4.9.1", - "@rollup/rollup-win32-arm64-msvc": "4.9.1", - "@rollup/rollup-win32-ia32-msvc": "4.9.1", - "@rollup/rollup-win32-x64-msvc": "4.9.1", + "@rollup/rollup-android-arm-eabi": "4.9.2", + "@rollup/rollup-android-arm64": "4.9.2", + "@rollup/rollup-darwin-arm64": "4.9.2", + "@rollup/rollup-darwin-x64": "4.9.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.2", + "@rollup/rollup-linux-arm64-gnu": "4.9.2", + "@rollup/rollup-linux-arm64-musl": "4.9.2", + "@rollup/rollup-linux-riscv64-gnu": "4.9.2", + "@rollup/rollup-linux-x64-gnu": "4.9.2", + "@rollup/rollup-linux-x64-musl": "4.9.2", + "@rollup/rollup-win32-arm64-msvc": "4.9.2", + "@rollup/rollup-win32-ia32-msvc": "4.9.2", + "@rollup/rollup-win32-x64-msvc": "4.9.2", "fsevents": "~2.3.2" } }, @@ -3111,6 +2938,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -3192,6 +3025,32 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3472,6 +3331,56 @@ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3516,30 +3425,30 @@ "dev": true }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -3557,47 +3466,6 @@ "node": ">=10" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index df6ba97..70698cb 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-html-css", "displayName": "HTML CSS Support", "description": "CSS Intellisense for HTML", - "version": "1.14.2", + "version": "2.0.0", "license": "MIT", "publisher": "ecmel", "author": { @@ -65,11 +65,11 @@ "commands": [ { "command": "vscode-html-css.validate", - "title": "CSS: Validate Attributes" + "title": "CSS: Validate attributes" }, { - "command": "vscode-html-css.dispose", - "title": "CSS: Clear Cache" + "command": "vscode-html-css.clear", + "title": "CSS: Clear cache" } ] }, @@ -79,7 +79,7 @@ "clean": "git clean -Xdf", "prereset": "npm run clean", "reset": "npm install", - "format": "prettier -w --list-different .", + "format": "prettier --ignore-path .gitignore -w --list-different .", "debug": "rollup -c -w", "build": "rollup -c --forceExit", "compile": "tsc -p ./ --outDir out --sourceMap true --module commonjs --moduleResolution node", @@ -98,15 +98,15 @@ "@types/css-tree": "^2.3.4", "@types/glob": "^8.1.0", "@types/mocha": "^10.0.6", - "@types/node": "^20.10.5", + "@types/node": "^20.10.6", "@types/vscode": "^1.66.0", "@vscode/test-electron": "^2.3.8", "@vscode/vsce": "^2.22.0", "c8": "^8.0.1", "fast-glob": "^3.3.2", "mocha": "^10.2.0", - "prettier": "^3.1.1", - "rollup": "^4.9.1", + "prettier": "^2.8.8", + "rollup": "^4.9.2", "tslib": "^2.6.2", "typescript": "^5.3.3" } diff --git a/src/completer.ts b/src/completer.ts new file mode 100644 index 0000000..28d1db7 --- /dev/null +++ b/src/completer.ts @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1986-2023 Ecmel Ercan + * Licensed under the MIT License + */ + +import { + CancellationToken, + CompletionContext, + CompletionItem, + CompletionItemKind, + CompletionItemProvider, + CompletionList, + Position, + ProviderResult, + Range, + RelativePattern, + TextDocument, + Uri, + window, + workspace, +} from "vscode"; +import { Style, StyleType, parse } from "./parser"; +import { getStyleSheets } from "./settings"; + +const start = new Position(0, 0); +const isRemote = /^https?:\/\//i; +const canComplete = /(id|class|className)\s*[=:]\s*(["'])(?:.(?!\2))*$/is; +const cache = new Map(); + +export class Completer implements CompletionItemProvider { + private async fetch(url: string) { + try { + const res = await fetch(url); + if (res.ok) { + return await res.text(); + } + throw new Error(res.statusText); + } catch (error) { + window.showErrorMessage(`Fetching ${url} failed. ${error}`); + } + return ""; + } + + private async getRemote(name: string) { + let styles = cache.get(name); + if (!styles) { + const content = await this.fetch(name); + styles = parse(content); + cache.set(name, styles); + } + return styles; + } + + private async getLocal(uri: Uri) { + const name = uri.toString(); + let styles = cache.get(name); + if (!styles) { + const content = await workspace.fs.readFile(uri); + styles = parse(content.toString()); + cache.set(name, styles); + } + return styles; + } + + private createCompletionItem(style: Style) { + return new CompletionItem( + style.label, + style.type === StyleType.ID + ? CompletionItemKind.Value + : CompletionItemKind.Enum + ); + } + + private populate( + styles: Style[], + type: StyleType, + items: Map + ) { + styles + .filter((style) => style.type === type) + .forEach((style) => + items.set(style.label, this.createCompletionItem(style)) + ); + } + + private async getCompletionItems(document: TextDocument, type: StyleType) { + const items = new Map(); + const folder = workspace.getWorkspaceFolder(document.uri); + + if (folder) { + const styleSheets = getStyleSheets(document.uri); + const globs: string[] = []; + for (const name of styleSheets) { + if (isRemote.test(name)) { + const styles = await this.getRemote(name); + this.populate(styles, type, items); + } else { + globs.push(name); + } + } + const relative = new RelativePattern(folder, `{${globs.join(",")}}`); + const names = await workspace.findFiles(relative.pattern); + for (const name of names) { + const styles = await this.getLocal(name); + this.populate(styles, type, items); + } + } + this.populate(parse(document.getText()), type, items); + + return [...items.values()]; + } + + provideCompletionItems( + document: TextDocument, + position: Position, + token: CancellationToken, + context: CompletionContext + ): ProviderResult> { + const range = new Range(start, position); + const text = document.getText(range); + const match = canComplete.exec(text); + + return new Promise((resolve, reject) => { + if (match && !token.isCancellationRequested) { + resolve( + this.getCompletionItems( + document, + match[1] === "id" ? StyleType.ID : StyleType.CLASS + ) + ); + } else { + reject(); + } + }); + } +} + +export function invalidate(name: string) { + cache.delete(name); +} + +export function clear() { + cache.clear(); +} diff --git a/src/completion.ts b/src/completion.ts deleted file mode 100644 index 3a4d012..0000000 --- a/src/completion.ts +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (c) 1986-2023 Ecmel Ercan - * Licensed under the MIT License - */ - -import { glob } from "fast-glob"; -import { basename, dirname, extname, isAbsolute, join } from "path"; -import { - CancellationToken, - CompletionContext, - CompletionItem, - CompletionItemKind, - CompletionItemProvider, - CompletionList, - Diagnostic, - DiagnosticSeverity, - Disposable, - Position, - ProviderResult, - Range, - TextDocument, - Uri, - workspace, -} from "vscode"; - -export type Context = { - ids: Map; - classes: Map; -}; - -export class SelectorCompletionItemProvider - implements CompletionItemProvider, Disposable -{ - readonly start = new Position(0, 0); - readonly cache = new Map(); - readonly files = new Map(); - readonly watchers = new Map(); - readonly isRemote = /^https?:\/\//i; - readonly canComplete = /(id|class|className)\s*[=:]\s*("|')(?:(?!\2).)*$/is; - readonly findLinkRel = /rel\s*=\s*("|')((?:(?!\1).)+)\1/is; - readonly findLinkHref = /href\s*=\s*("|')((?:(?!\1).)+)\1/is; - readonly findExtended = - /(?:{{<|{{>|{%\s*extends|@extends\s*\()\s*("|')?([./A-Za-z_0-9\\\-]+)\1\s*(?:\)|%}|}})/i; - - dispose() { - this.watchers.forEach((e) => e.dispose()); - this.watchers.clear(); - this.cache.clear(); - this.files.clear(); - } - - watchFile(path: string, listener: () => any) { - if (this.watchers.has(path)) { - return; - } - - const watcher = workspace.createFileSystemWatcher(path); - - watcher.onDidCreate(listener); - watcher.onDidChange(listener); - watcher.onDidDelete(listener); - - this.watchers.set(path, watcher); - } - - getStyleSheets(uri: Uri): string[] { - return workspace - .getConfiguration("css", uri) - .get("styleSheets", []); - } - - getPath(uri: Uri, path: string, ext?: string): string { - const folder = workspace.getWorkspaceFolder(uri); - const name = ext ? join(dirname(path), basename(path, ext) + ext) : path; - - return folder - ? join(isAbsolute(path) ? folder.uri.fsPath : dirname(uri.fsPath), name) - : join(dirname(uri.fsPath), name); - } - - parseTextToItems(path: string, text: string, items: CompletionItem[]) { - const regex = /([.#])(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)\s*{([^}]*)}/g; - - let match; - - while ((match = regex.exec(text))) { - items.push( - new CompletionItem( - { label: match[2], description: path }, - match[1] === "." ? CompletionItemKind.Enum : CompletionItemKind.Value, - ), - ); - } - } - - async fetchLocal(path: string): Promise { - if (this.cache.has(path)) { - return; - } - - const items: CompletionItem[] = []; - - try { - const content = await workspace.fs.readFile(Uri.file(path)); - const text = content.toString(); - this.parseTextToItems(basename(path), text, items); - } catch (error) {} - - this.cache.set(path, items); - this.watchFile(path, () => this.cache.delete(path)); - } - - async fetchRemote(path: string): Promise { - if (this.cache.has(path)) { - return; - } - - const items: CompletionItem[] = []; - - try { - const res = await fetch(path); - - if (res.ok) { - const text = await res.text(); - this.parseTextToItems(basename(path), text, items); - } - } catch (error) {} - - this.cache.set(path, items); - } - - async fetch(uri: Uri, path: string): Promise { - if (this.isRemote.test(path)) { - await this.fetchRemote(path); - } else { - const base = basename(uri.fsPath, extname(uri.fsPath)); - - path = this.getPath( - uri, - path.replace(/\${\s*fileBasenameNoExtension\s*}/, base), - ); - - let paths = isAbsolute(path) ? glob.sync(path) : [path]; - - for (const path of paths) { - await this.fetchLocal(path); - } - } - - return path; - } - - findEmbedded(uri: Uri, keys: Set, text: string) { - const key = uri.toString(); - const items: CompletionItem[] = []; - const findStyles = /(?:]*>([^<]+)<\/style>|css\s*`([^`]+)`)/gi; - - let style; - - while ((style = findStyles.exec(text)) !== null) { - this.parseTextToItems(basename(uri.fsPath), style[1] || style[2], items); - } - - this.cache.set(key, items); - keys.add(key); - } - - async findFixed(uri: Uri, keys: Set): Promise { - for (const key of this.getStyleSheets(uri)) { - keys.add(await this.fetch(uri, key)); - } - } - - async findLinks(uri: Uri, keys: Set, text: string): Promise { - const findLinks = /]+)>/gi; - - let link, rel, href; - - while ((link = findLinks.exec(text)) !== null) { - rel = this.findLinkRel.exec(link[1]); - - if (rel && rel[2] === "stylesheet") { - href = this.findLinkHref.exec(link[1]); - - if (href) { - keys.add(await this.fetch(uri, href[2])); - } - } - } - } - - async findInherited( - uri: Uri, - keys: Set, - text: string, - level: number = 0, - ): Promise { - const extended = this.findExtended.exec(text); - - if (extended && level < 3) { - level++; - - const name = extended[2]; - const ext = extname(name) || extname(uri.fsPath); - const path = this.getPath(uri, name, ext); - const file = Uri.file(path); - - let text = this.files.get(path); - - if (!text) { - try { - text = (await workspace.fs.readFile(file)).toString(); - } catch (error) { - text = "n/a"; - } - - this.files.set(path, text); - this.watchFile(path, () => this.files.delete(path)); - } - - this.findEmbedded(file, keys, text); - - await this.findLinks(file, keys, text); - await this.findInherited(file, keys, text, level); - } - } - - async findAll(document: TextDocument): Promise { - const keys = new Set(); - const uri = document.uri; - const text = document.getText(); - - this.findEmbedded(uri, keys, text); - - await this.findFixed(uri, keys); - await this.findLinks(uri, keys, text); - await this.findInherited(uri, keys, text); - - const ids = new Map(); - const classes = new Map(); - - for (const value of this.cache.values()) { - for (const item of value) { - const target = item.kind === CompletionItemKind.Value ? ids : classes; - target.set(item.label as string, item); - } - } - - return { ids, classes }; - } - - async validate(document: TextDocument): Promise { - const context = await this.findAll(document); - const text = document.getText(); - const diagnostics: Diagnostic[] = []; - const findAttribute = /(id|class|className)\s*=\s*("|')(.*?)\2/gis; - - let attribute, offset, findSelector, value, anchor, end, start; - - while ((attribute = findAttribute.exec(text)) !== null) { - offset = - findAttribute.lastIndex - - attribute[3].length + - attribute[3].indexOf(attribute[2]); - - findSelector = /([^(\[{}\])\s]+)(?![^(\[{]*[}\])])/gi; - - while ((value = findSelector.exec(attribute[3])) !== null) { - if ( - !(attribute[1] === "id" ? context.ids : context.classes).has(value[1]) - ) { - anchor = findSelector.lastIndex + offset; - end = document.positionAt(anchor); - start = document.positionAt(anchor - value[1].length); - - diagnostics.push( - new Diagnostic( - new Range(start, end), - `CSS selector '${value[1]}' not found.`, - DiagnosticSeverity.Information, - ), - ); - } - } - } - - return diagnostics; - } - - provideCompletionItems( - document: TextDocument, - position: Position, - token: CancellationToken, - context: CompletionContext, - ): ProviderResult> { - return new Promise((resolve, reject) => { - const range = new Range(this.start, position); - const text = document.getText(range); - const canComplete = this.canComplete.exec(text); - - if (canComplete) { - this.findAll(document).then((context) => - resolve([ - ...(canComplete[1] === "id" - ? context.ids - : context.classes - ).values(), - ]), - ); - } else { - reject(); - } - }); - } -} diff --git a/src/extension.ts b/src/extension.ts index 64d2e15..74c3ae9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,45 +3,21 @@ * Licensed under the MIT License */ -import { SelectorCompletionItemProvider } from "./completion"; -import { - ExtensionContext, - commands, - languages, - window, - workspace, -} from "vscode"; +import { ExtensionContext, commands, languages, workspace } from "vscode"; +import { getEnabledLanguages } from "./settings"; +import { Completer, clear, invalidate } from "./completer"; export function activate(context: ExtensionContext) { - const config = workspace.getConfiguration("css"); - const enabledLanguages = config.get("enabledLanguages", ["html"]); - const validations = languages.createDiagnosticCollection(); - const provider = new SelectorCompletionItemProvider(); + const enabledLanguages = getEnabledLanguages(); + const completer = new Completer(); context.subscriptions.push( - commands.registerCommand("vscode-html-css.validate", async () => { - const editor = window.activeTextEditor; - - if (editor) { - const document = editor.document; - - if (enabledLanguages.includes(document.languageId)) { - validations.set(document.uri, await provider.validate(document)); - } - } - }), - commands.registerCommand("vscode-html-css.dispose", () => - provider.dispose(), - ), - workspace.onDidChangeTextDocument((e) => - validations.delete(e.document.uri), - ), - workspace.onDidCloseTextDocument((document) => - validations.delete(document.uri), + languages.registerCompletionItemProvider(enabledLanguages, completer), + workspace.onDidChangeTextDocument((event) => + invalidate(event.document.uri.toString()) ), - languages.registerCompletionItemProvider(enabledLanguages, provider), - validations, - provider, + commands.registerCommand("vscode-html-css.clear", () => clear()), + commands.registerCommand("vscode-html-css.validate", async () => {}) ); } diff --git a/src/parser.ts b/src/parser.ts new file mode 100644 index 0000000..6dd773d --- /dev/null +++ b/src/parser.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1986-2023 Ecmel Ercan + * Licensed under the MIT License + */ + +const regex = /([.#])(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)\s*{([^}]*)}/g; + +export const enum StyleType { + ID = "#", + CLASS = ".", +} + +export interface Style { + type: StyleType; + label: string; + definition: string; +} + +export function parse(text: string) { + const styles: Style[] = []; + let match; + while ((match = regex.exec(text))) { + styles.push({ + type: match[1] as StyleType, + label: match[2], + definition: match[3].replace(/\s+/g, " ").trim(), + }); + } + return styles; +} diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..025d888 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,18 @@ +/* + * Copyright (c) 1986-2023 Ecmel Ercan + * Licensed under the MIT License + */ + +import { Uri, workspace } from "vscode"; + +export function getEnabledLanguages(): string[] { + return workspace + .getConfiguration("css") + .get("enabledLanguages", ["html"]); +} + +export function getStyleSheets(uri: Uri): string[] { + return workspace + .getConfiguration("css", uri) + .get("styleSheets", []); +} diff --git a/test/suite/completion.test.ts b/test/suite/completion.test.ts deleted file mode 100644 index 8a81660..0000000 --- a/test/suite/completion.test.ts +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 1986-2023 Ecmel Ercan - * Licensed under the MIT License - */ - -import * as assert from "assert"; -import { SelectorCompletionItemProvider } from "../../src/completion"; -import { - MockCancellationToken, - MockCompletionContext, - MockDocument, -} from "./mocks"; -import { CompletionItem, Position, Uri } from "vscode"; - -suite("SelectorCompletionItemProvider Test Suite", () => { - const position = new Position(0, 0); - const token = new MockCancellationToken(false); - const context = new MockCompletionContext(); - - test("RegEx: isRemote", () => { - const provider = new SelectorCompletionItemProvider(); - - assert.strictEqual( - provider.isRemote.test("http://example.com/example.css"), - true, - ); - assert.strictEqual( - provider.isRemote.test("https://example.com/example.css"), - true, - ); - }); - - test("RegEx: canComplete", () => { - const provider = new SelectorCompletionItemProvider(); - - assert.strictEqual(provider.canComplete.test(""), false); - assert.strictEqual(provider.canComplete.test('class="'), true); - assert.strictEqual(provider.canComplete.test('class=""'), false); - assert.strictEqual(provider.canComplete.test('class = "'), true); - assert.strictEqual(provider.canComplete.test('class = ""'), false); - - assert.strictEqual( - provider.canComplete.test(` - class = "someClass - `), - true, - ); - - assert.strictEqual( - provider.canComplete.test(` - class - = "someClass - `), - true, - ); - assert.strictEqual( - provider.canComplete.test(` - class = - "someClass - - `), - true, - ); - assert.strictEqual( - provider.canComplete.test(` - class = - "someClass - - "`), - false, - ); - assert.strictEqual( - provider.canComplete.test(` - class = "some" - class = - "someClass - - "`), - false, - ); - }); - - test("RegEx: findLinkRel", () => { - const provider = new SelectorCompletionItemProvider(); - - assert.strictEqual( - provider.findLinkRel.exec(` - - "`)?.[2], - "stylesheet", - ); - }); - - test("RegEx: findLinkHref", () => { - const provider = new SelectorCompletionItemProvider(); - - assert.strictEqual( - provider.findLinkHref.exec(` - - "`)?.[2], - "http://example.com/example.css", - ); - }); - - test("RegEx: findExtended (Twig)", () => { - const provider = new SelectorCompletionItemProvider(); - - assert.strictEqual( - provider.findExtended.exec(` - {% extends "base" %} - `)?.[2], - "base", - ); - }); - - test("RegEx: findExtended (Mustache)", () => { - const provider = new SelectorCompletionItemProvider(); - - assert.strictEqual( - provider.findExtended.exec(` - {{< base }} - `)?.[2], - "base", - ); - }); - - test("RegEx: findExtended (Handlebars)", () => { - const provider = new SelectorCompletionItemProvider(); - - assert.strictEqual( - provider.findExtended.exec(` - {{> base }} - `)?.[2], - "base", - ); - }); - - test("RegEx: findExtended (Blade)", () => { - const provider = new SelectorCompletionItemProvider(); - - assert.strictEqual( - provider.findExtended.exec(` - @extends('base') - `)?.[2], - "base", - ); - }); - - test("Rejects outside class attribute", (done) => { - const provider = new SelectorCompletionItemProvider(); - const document = new MockDocument(''); - - const result = provider.provideCompletionItems( - document, - position, - token, - context, - ) as Thenable; - - result.then( - () => done(new Error("Should reject!")), - () => done(), - ); - }); - - test("Completes from style tag", async () => { - const provider = new SelectorCompletionItemProvider(); - const document = new MockDocument(' { - const provider = new SelectorCompletionItemProvider(); - const document = new MockDocument(` - - { - const provider = new (class extends SelectorCompletionItemProvider { - getStyleSheets(uri: Uri): string[] { - return [ - "https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css", - ]; - } - })(); - - const document = new MockDocument(' { - test("Completes for html", async () => { + test("should complete for html", async () => { const document = await workspace.openTextDocument({ language: "html", - content: "\n", + content: "\n", }); const list = await commands.executeCommand( "vscode.executeCompletionItemProvider", document.uri, - new Position(1, 14), + new Position(1, 13) ); - assert.strictEqual(list?.items[0].insertText, "some"); + assert.ok(list.items.length > 0); + assert.strictEqual(list.items[0].insertText, "some"); }); }); diff --git a/test/suite/mocks.ts b/test/suite/mocks.ts deleted file mode 100644 index d01cba3..0000000 --- a/test/suite/mocks.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 1986-2023 Ecmel Ercan - * Licensed under the MIT License - */ - -import { - CancellationToken, - CompletionContext, - CompletionTriggerKind, - EndOfLine, - Event, - Position, - Range, - TextDocument, - TextLine, - Uri, -} from "vscode"; - -export class MockCancellationToken implements CancellationToken { - isCancellationRequested: boolean; - onCancellationRequested!: Event; - - constructor(isCancellationRequested: boolean) { - this.isCancellationRequested = isCancellationRequested; - } -} - -export class MockCompletionContext implements CompletionContext { - triggerKind = CompletionTriggerKind.Invoke; - triggerCharacter: string | undefined; -} - -export class MockDocument implements TextDocument { - fileName!: string; - isUntitled!: boolean; - languageId!: string; - version!: number; - isDirty!: boolean; - isClosed!: boolean; - eol!: EndOfLine; - lineCount!: number; - text: string; - - uri = Uri.parse("/test/test.css"); - - constructor(text: string) { - this.text = text; - } - - getText(range?: Range): string { - return this.text; - } - - save(): Thenable { - throw new Error("Method not implemented."); - } - lineAt(position: Position | number | any): TextLine { - throw new Error("Method not implemented."); - } - offsetAt(position: Position): number { - throw new Error("Method not implemented."); - } - positionAt(offset: number): Position { - throw new Error("Method not implemented."); - } - getWordRangeAtPosition( - position: Position, - regex?: RegExp, - ): Range | undefined { - throw new Error("Method not implemented."); - } - validateRange(range: Range): Range { - throw new Error("Method not implemented."); - } - validatePosition(position: Position): Position { - throw new Error("Method not implemented."); - } -}