separate out tailwindcss-language-service package

master
Brad Cornes 2020-10-08 16:20:54 +01:00
parent b3fef941b6
commit ccf7cd57a7
101 changed files with 24667 additions and 7950 deletions

2
.gitignore vendored
View File

@ -1,3 +1 @@
node_modules
dist
*.vsix

105
README.md
View File

@ -1,105 +0,0 @@
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/.github/banner-dark.png" alt="" />
Tailwind CSS IntelliSense enhances the Tailwind development experience by providing Visual Studio Code users with advanced features such as autocomplete, syntax highlighting, and linting.
## Installation
**[Install via the Visual Studio Code Marketplace →](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)**
In order for the extension to activate you must have [`tailwindcss` installed](https://tailwindcss.com/docs/installation/#1-install-tailwind-via-npm) and a [Tailwind config file](https://tailwindcss.com/docs/installation/#3-create-your-tailwind-config-file-optional) named `tailwind.config.js` or `tailwind.js` in your workspace.
## Features
### Autocomplete
Intelligent suggestions for class names, as well as [CSS functions and directives](https://tailwindcss.com/docs/functions-and-directives/).
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/.github/autocomplete.png" alt="" />
### Linting
Highlights errors and potential bugs in both your CSS and your markup.
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/.github/linting.png" alt="" />
### Hover Preview
See the complete CSS for a Tailwind class name by hovering over it.
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/.github/hover.png" alt="" />
### CSS Syntax Highlighting
Provides syntax definitions so that Tailwind features are highlighted correctly.
## Settings
### `tailwindCSS.includeLanguages`
This setting allows you to add additional language support. The key of each entry is the new language ID and the value is any one of the extensions built-in languages, depending on how you want the new language to be treated (e.g. `html`, `css`, or `javascript`):
```json
{
"tailwindCSS.includeLanguages": {
"plaintext": "html"
}
}
```
### `tailwindCSS.emmetCompletions`
Enable completions when using [Emmet](https://emmet.io/)-style syntax, for example `div.bg-red-500.uppercase`. **Default: `false`**
```json
{
"tailwindCSS.emmetCompletions": true
}
```
### `tailwindCSS.colorDecorators`
Controls whether the editor should render inline color decorators for Tailwind CSS classes and helper functions.
- `inherit`: Color decorators are rendered if `editor.colorDecorators` is enabled.
- `on`: Color decorators are rendered.
- `off`: Color decorators are not rendered.
### `tailwindCSS.validate`
Enable linting. Rules can be configured individually using the `tailwindcss.lint` settings:
- `ignore`: disable lint rule entirely
- `warning`: rule violations will be considered "warnings," typically represented by a yellow underline
- `error`: rule violations will be considered "errors," typically represented by a red underline
#### `tailwindCSS.lint.invalidScreen`
Unknown screen name used with the [`@screen` directive](https://tailwindcss.com/docs/functions-and-directives/#screen). **Default: `error`**
#### `tailwindCSS.lint.invalidVariant`
Unknown variant name used with the [`@variants` directive](https://tailwindcss.com/docs/functions-and-directives/#variants). **Default: `error`**
#### `tailwindCSS.lint.invalidTailwindDirective`
Unknown value used with the [`@tailwind` directive](https://tailwindcss.com/docs/functions-and-directives/#tailwind). **Default: `error`**
#### `tailwindCSS.lint.invalidApply`
Unsupported use of the [`@apply` directive](https://tailwindcss.com/docs/functions-and-directives/#apply). **Default: `error`**
#### `tailwindCSS.lint.invalidConfigPath`
Unknown or invalid path used with the [`theme` helper](https://tailwindcss.com/docs/functions-and-directives/#theme). **Default: `error`**
#### `tailwindCSS.lint.cssConflict`
Class names on the same HTML element which apply the same CSS property or properties. **Default: `warning`**
## Troubleshooting
If youre having issues getting the IntelliSense features to activate, there are a few things you can check:
- Ensure that you have a Tailwind config file in your workspace and that this is named `tailwind.config.js` or `tailwind.js`. Check out the Tailwind documentation for details on [creating a config file](https://tailwindcss.com/docs/installation/#3-create-your-tailwind-config-file-optional).
- Ensure that the `tailwindcss` module is installed in your workspace, via `npm`, `yarn`, or `pnpm`. Tailwind CSS IntelliSense does not currently support Yarn Plug'n'Play.
- If you installed `tailwindcss` or created your config file while your project was already open in Visual Studio Code you may need to reload the editor. You can either restart VS Code entirely, or use the `Developer: Reload Window` command which can be found in the command palette.

1
README.md 120000
View File

@ -0,0 +1 @@
packages/tailwindcss-intellisense/README.md

7
lerna.json 100644
View File

@ -0,0 +1,7 @@
{
"packages": [
"packages/*"
],
"version": "independent",
"npmClient": "npm"
}

13324
package-lock.json generated 100755 → 100644

File diff suppressed because it is too large Load Diff

217
package.json 100755 → 100644
View File

@ -1,220 +1,9 @@
{
"name": "vscode-tailwindcss",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"preview": true,
"author": "Brad Cornes <hello@bradley.dev>",
"license": "MIT",
"version": "0.4.3",
"homepage": "https://github.com/tailwindcss/intellisense",
"bugs": {
"url": "https://github.com/tailwindcss/intellisense/issues",
"email": "hello@bradley.dev"
},
"repository": {
"type": "git",
"url": "https://github.com/tailwindcss/intellisense.git"
},
"publisher": "bradlc",
"keywords": [
"tailwind",
"tailwindcss",
"css",
"intellisense",
"autocomplete",
"vscode"
],
"engines": {
"vscode": "^1.33.0"
},
"categories": [
"Linters",
"Other"
],
"galleryBanner": {
"color": "#f9fafb"
},
"icon": "media/icon.png",
"activationEvents": [
"workspaceContains:**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js"
],
"main": "dist/extension/index.js",
"contributes": {
"grammars": [
{
"scopeName": "tailwindcss.injection",
"path": "./syntaxes/tailwind.tmLanguage.json",
"injectTo": [
"source.css",
"source.css.scss",
"source.css.less",
"source.css.postcss",
"source.vue",
"source.svelte",
"text.html"
]
}
],
"configuration": {
"title": "Tailwind CSS IntelliSense",
"properties": {
"tailwindCSS.emmetCompletions": {
"type": "boolean",
"default": false,
"markdownDescription": "Enable class name completions when using Emmet-style syntax, for example `div.bg-red-500.uppercase`"
},
"tailwindCSS.includeLanguages": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"default": {},
"markdownDescription": "Enable features in languages that are not supported by default. Add a mapping here between the new language and an already supported language.\n E.g.: `{\"plaintext\": \"html\"}`"
},
"tailwindCSS.colorDecorators": {
"type": "string",
"enum": [
"inherit",
"on",
"off"
],
"markdownEnumDescriptions": [
"Color decorators are rendered if `editor.colorDecorators` is enabled.",
"Color decorators are rendered.",
"Color decorators are not rendered."
],
"default": "inherit",
"markdownDescription": "Controls whether the editor should render inline color decorators for Tailwind CSS classes and helper functions.",
"scope": "language-overridable"
},
"tailwindCSS.validate": {
"type": "boolean",
"default": true,
"markdownDescription": "Enable linting. Rules can be configured individually using the `tailwindcss.lint.*` settings",
"scope": "language-overridable"
},
"tailwindCSS.lint.cssConflict": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "warning",
"markdownDescription": "Class names on the same HTML element which apply the same CSS property or properties",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidApply": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unsupported use of the [`@apply` directive](https://tailwindcss.com/docs/functions-and-directives/#apply)",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidScreen": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unknown screen name used with the [`@screen` directive](https://tailwindcss.com/docs/functions-and-directives/#screen)",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidVariant": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unknown variant name used with the [`@variants` directive](https://tailwindcss.com/docs/functions-and-directives/#variants)",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidConfigPath": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unknown or invalid path used with the [`theme` helper](https://tailwindcss.com/docs/functions-and-directives/#theme)",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidTailwindDirective": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unknown value used with the [`@tailwind` directive](https://tailwindcss.com/docs/functions-and-directives/#tailwind)",
"scope": "language-overridable"
}
}
}
},
"private": true,
"scripts": {
"dev": "glob-exec --foreach --parallel \"src/*.ts\" -- \"ncc build {{file}} --watch -o dist/{{file.toString().replace(/^src\\//, '').replace(/\\.ts$/, '')}}\"",
"build": "glob-exec --foreach --parallel \"src/*.ts\" -- \"ncc build {{file}} -o dist/{{file.toString().replace(/^src\\//, '').replace(/\\.ts$/, '')}}\"",
"minify": "glob-exec --foreach --parallel \"dist/**/*.js\" -- \"terser {{file}} --compress --mangle --output {{file.toString()}}\"",
"package": "vsce package",
"vscode:prepublish": "npm run clean && npm run build && npm run minify",
"clean": "rimraf dist",
"test": "jest"
"bootstrap": "lerna clean && lerna bootstrap --hoist"
},
"devDependencies": {
"@ctrl/tinycolor": "^3.1.0",
"@types/debounce": "^1.2.0",
"@types/mocha": "^5.2.0",
"@types/moo": "^0.5.3",
"@types/node": "^13.9.3",
"@types/vscode": "^1.32.0",
"@zeit/ncc": "^0.22.0",
"callsite": "^1.0.0",
"chokidar": "^3.3.1",
"concurrently": "^5.1.0",
"css.escape": "^1.5.1",
"debounce": "^1.2.0",
"detect-indent": "^6.0.0",
"dlv": "^1.1.3",
"dset": "^2.0.1",
"esm": "^3.2.25",
"fast-glob": "^3.2.4",
"glob-exec": "^0.1.1",
"globalyzer": "^0.1.4",
"globrex": "^0.1.2",
"import-from": "^3.0.0",
"jest": "^25.5.4",
"line-column": "^1.0.2",
"mitt": "^1.2.0",
"mkdirp": "^1.0.3",
"moo": "^0.5.1",
"normalize-path": "^3.0.0",
"pkg-up": "^3.1.0",
"postcss": "^7.0.27",
"postcss-selector-parser": "^6.0.2",
"resolve-from": "^5.0.0",
"rimraf": "^3.0.2",
"semver": "^7.3.2",
"sift-string": "0.0.2",
"stack-trace": "0.0.10",
"terser": "^4.6.12",
"tiny-invariant": "^1.1.0",
"tslint": "^5.16.0",
"typescript": "^3.8.3",
"vsce": "^1.76.1",
"vscode-emmet-helper-bundled": "0.0.1",
"vscode-languageclient": "^5.2.1",
"vscode-languageserver": "^5.2.1",
"vscode-uri": "^2.1.1"
"lerna": "^3.22.1"
}
}

View File

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 235 KiB

View File

Before

Width:  |  Height:  |  Size: 267 KiB

After

Width:  |  Height:  |  Size: 267 KiB

View File

@ -0,0 +1,3 @@
node_modules
dist
*.vsix

View File

@ -0,0 +1,105 @@
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/.github/banner-dark.png" alt="" />
Tailwind CSS IntelliSense enhances the Tailwind development experience by providing Visual Studio Code users with advanced features such as autocomplete, syntax highlighting, and linting.
## Installation
**[Install via the Visual Studio Code Marketplace →](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)**
In order for the extension to activate you must have [`tailwindcss` installed](https://tailwindcss.com/docs/installation/#1-install-tailwind-via-npm) and a [Tailwind config file](https://tailwindcss.com/docs/installation/#3-create-your-tailwind-config-file-optional) named `tailwind.config.js` or `tailwind.js` in your workspace.
## Features
### Autocomplete
Intelligent suggestions for class names, as well as [CSS functions and directives](https://tailwindcss.com/docs/functions-and-directives/).
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/.github/autocomplete.png" alt="" />
### Linting
Highlights errors and potential bugs in both your CSS and your markup.
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/.github/linting.png" alt="" />
### Hover Preview
See the complete CSS for a Tailwind class name by hovering over it.
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/.github/hover.png" alt="" />
### CSS Syntax Highlighting
Provides syntax definitions so that Tailwind features are highlighted correctly.
## Settings
### `tailwindCSS.includeLanguages`
This setting allows you to add additional language support. The key of each entry is the new language ID and the value is any one of the extensions built-in languages, depending on how you want the new language to be treated (e.g. `html`, `css`, or `javascript`):
```json
{
"tailwindCSS.includeLanguages": {
"plaintext": "html"
}
}
```
### `tailwindCSS.emmetCompletions`
Enable completions when using [Emmet](https://emmet.io/)-style syntax, for example `div.bg-red-500.uppercase`. **Default: `false`**
```json
{
"tailwindCSS.emmetCompletions": true
}
```
### `tailwindCSS.colorDecorators`
Controls whether the editor should render inline color decorators for Tailwind CSS classes and helper functions.
- `inherit`: Color decorators are rendered if `editor.colorDecorators` is enabled.
- `on`: Color decorators are rendered.
- `off`: Color decorators are not rendered.
### `tailwindCSS.validate`
Enable linting. Rules can be configured individually using the `tailwindcss.lint` settings:
- `ignore`: disable lint rule entirely
- `warning`: rule violations will be considered "warnings," typically represented by a yellow underline
- `error`: rule violations will be considered "errors," typically represented by a red underline
#### `tailwindCSS.lint.invalidScreen`
Unknown screen name used with the [`@screen` directive](https://tailwindcss.com/docs/functions-and-directives/#screen). **Default: `error`**
#### `tailwindCSS.lint.invalidVariant`
Unknown variant name used with the [`@variants` directive](https://tailwindcss.com/docs/functions-and-directives/#variants). **Default: `error`**
#### `tailwindCSS.lint.invalidTailwindDirective`
Unknown value used with the [`@tailwind` directive](https://tailwindcss.com/docs/functions-and-directives/#tailwind). **Default: `error`**
#### `tailwindCSS.lint.invalidApply`
Unsupported use of the [`@apply` directive](https://tailwindcss.com/docs/functions-and-directives/#apply). **Default: `error`**
#### `tailwindCSS.lint.invalidConfigPath`
Unknown or invalid path used with the [`theme` helper](https://tailwindcss.com/docs/functions-and-directives/#theme). **Default: `error`**
#### `tailwindCSS.lint.cssConflict`
Class names on the same HTML element which apply the same CSS property or properties. **Default: `warning`**
## Troubleshooting
If youre having issues getting the IntelliSense features to activate, there are a few things you can check:
- Ensure that you have a Tailwind config file in your workspace and that this is named `tailwind.config.js` or `tailwind.js`. Check out the Tailwind documentation for details on [creating a config file](https://tailwindcss.com/docs/installation/#3-create-your-tailwind-config-file-optional).
- Ensure that the `tailwindcss` module is installed in your workspace, via `npm`, `yarn`, or `pnpm`. Tailwind CSS IntelliSense does not currently support Yarn Plug'n'Play.
- If you installed `tailwindcss` or created your config file while your project was already open in Visual Studio Code you may need to reload the editor. You can either restart VS Code entirely, or use the `Developer: Reload Window` command which can be found in the command palette.

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,18 @@
{
"name": "vscode-tailwindcss",
"version": "0.4.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"mitt": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz",
"integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw=="
},
"typescript": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w=="
}
}
}

View File

@ -0,0 +1,209 @@
{
"name": "vscode-tailwindcss",
"displayName": "Tailwind CSS IntelliSense",
"description": "Intelligent Tailwind CSS tooling for VS Code",
"preview": true,
"author": "Brad Cornes <hello@bradley.dev>",
"license": "MIT",
"version": "0.4.3",
"homepage": "https://github.com/tailwindcss/intellisense",
"bugs": {
"url": "https://github.com/tailwindcss/intellisense/issues",
"email": "hello@bradley.dev"
},
"repository": {
"type": "git",
"url": "https://github.com/tailwindcss/intellisense.git"
},
"publisher": "bradlc",
"keywords": [
"tailwind",
"tailwindcss",
"css",
"intellisense",
"autocomplete",
"vscode"
],
"engines": {
"vscode": "^1.33.0"
},
"categories": [
"Linters",
"Other"
],
"galleryBanner": {
"color": "#f9fafb"
},
"icon": "media/icon.png",
"activationEvents": [
"workspaceContains:**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js"
],
"main": "dist/extension/index.js",
"contributes": {
"grammars": [
{
"scopeName": "tailwindcss.injection",
"path": "./syntaxes/tailwind.tmLanguage.json",
"injectTo": [
"source.css",
"source.css.scss",
"source.css.less",
"source.css.postcss",
"source.vue",
"source.svelte",
"text.html"
]
}
],
"configuration": {
"title": "Tailwind CSS IntelliSense",
"properties": {
"tailwindCSS.emmetCompletions": {
"type": "boolean",
"default": false,
"markdownDescription": "Enable class name completions when using Emmet-style syntax, for example `div.bg-red-500.uppercase`"
},
"tailwindCSS.includeLanguages": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"default": {},
"markdownDescription": "Enable features in languages that are not supported by default. Add a mapping here between the new language and an already supported language.\n E.g.: `{\"plaintext\": \"html\"}`"
},
"tailwindCSS.colorDecorators": {
"type": "string",
"enum": [
"inherit",
"on",
"off"
],
"markdownEnumDescriptions": [
"Color decorators are rendered if `editor.colorDecorators` is enabled.",
"Color decorators are rendered.",
"Color decorators are not rendered."
],
"default": "inherit",
"markdownDescription": "Controls whether the editor should render inline color decorators for Tailwind CSS classes and helper functions.",
"scope": "language-overridable"
},
"tailwindCSS.validate": {
"type": "boolean",
"default": true,
"markdownDescription": "Enable linting. Rules can be configured individually using the `tailwindcss.lint.*` settings",
"scope": "language-overridable"
},
"tailwindCSS.lint.cssConflict": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "warning",
"markdownDescription": "Class names on the same HTML element which apply the same CSS property or properties",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidApply": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unsupported use of the [`@apply` directive](https://tailwindcss.com/docs/functions-and-directives/#apply)",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidScreen": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unknown screen name used with the [`@screen` directive](https://tailwindcss.com/docs/functions-and-directives/#screen)",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidVariant": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unknown variant name used with the [`@variants` directive](https://tailwindcss.com/docs/functions-and-directives/#variants)",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidConfigPath": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unknown or invalid path used with the [`theme` helper](https://tailwindcss.com/docs/functions-and-directives/#theme)",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidTailwindDirective": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "error",
"markdownDescription": "Unknown value used with the [`@tailwind` directive](https://tailwindcss.com/docs/functions-and-directives/#tailwind)",
"scope": "language-overridable"
}
}
}
},
"scripts": {
"dev": "glob-exec --foreach --parallel \"src/*.ts\" -- \"ncc build {{file}} --watch -o dist/{{file.toString().replace(/^src\\//, '').replace(/\\.ts$/, '')}}\"",
"build": "glob-exec --foreach --parallel \"src/*.ts\" -- \"ncc build {{file}} -o dist/{{file.toString().replace(/^src\\//, '').replace(/\\.ts$/, '')}}\"",
"minify": "glob-exec --foreach --parallel \"dist/**/*.js\" -- \"terser {{file}} --compress --mangle --output {{file.toString()}}\"",
"package": "vsce package",
"vscode:prepublish": "npm run clean && npm run build && npm run minify",
"clean": "rimraf dist",
"test": "jest"
},
"dependencies": {
"@types/debounce": "^1.2.0",
"@types/mocha": "^5.2.0",
"@types/node": "^13.9.3",
"@types/vscode": "^1.32.0",
"@zeit/ncc": "^0.22.0",
"callsite": "^1.0.0",
"chokidar": "^3.3.1",
"debounce": "^1.2.0",
"dlv": "^1.1.3",
"dset": "^2.0.1",
"esm": "^3.2.25",
"fast-glob": "^3.2.4",
"glob-exec": "^0.1.1",
"import-from": "^3.0.0",
"jest": "^25.5.4",
"mitt": "^1.2.0",
"normalize-path": "^3.0.0",
"pkg-up": "^3.1.0",
"postcss": "^7.0.27",
"postcss-selector-parser": "^6.0.2",
"resolve-from": "^5.0.0",
"rimraf": "^3.0.2",
"semver": "^7.3.2",
"stack-trace": "0.0.10",
"tailwindcss-language-service": "0.0.1",
"terser": "^4.6.12",
"tiny-invariant": "^1.1.0",
"tslint": "^5.16.0",
"typescript": "^3.8.3",
"vsce": "^1.76.1",
"vscode-languageclient": "^5.2.1",
"vscode-languageserver": "^5.2.1",
"vscode-uri": "^2.1.1"
}
}

View File

@ -0,0 +1,29 @@
import { TextDocument } from 'vscode-languageserver'
import { State } from '../../util/state'
import { doValidate } from 'tailwindcss-language-service'
export async function provideDiagnostics(state: State, document: TextDocument) {
state.editor.connection.sendDiagnostics({
uri: document.uri,
diagnostics: await doValidate(state, document),
})
}
export function clearDiagnostics(state: State, document: TextDocument): void {
state.editor.connection.sendDiagnostics({
uri: document.uri,
diagnostics: [],
})
}
export function clearAllDiagnostics(state: State): void {
state.editor.documents.all().forEach((document) => {
clearDiagnostics(state, document)
})
}
export function updateAllDiagnostics(state: State): void {
state.editor.documents.all().forEach((document) => {
provideDiagnostics(state, document)
})
}

View File

@ -0,0 +1,16 @@
import { onMessage } from '../notifications'
import { State } from '../util/state'
import { getDocumentColors } from 'tailwindcss-language-service'
export function registerDocumentColorProvider(state: State) {
onMessage(
state.editor.connection,
'getDocumentColors',
async ({ document }) => {
let doc = state.editor.documents.get(document)
if (!doc) return { colors: [] }
return { colors: getDocumentColors(state, doc) }
}
)
}

View File

@ -20,12 +20,13 @@ import {
CodeAction,
} from 'vscode-languageserver'
import getTailwindState from '../class-names/index'
import { State, Settings, EditorState } from './util/state'
import { State, Settings, EditorState } from 'tailwindcss-language-service'
import {
provideCompletions,
resolveCompletionItem,
} from './providers/completionProvider'
import { provideHover } from './providers/hoverProvider'
doComplete,
doHover,
doCodeActions,
} from 'tailwindcss-language-service'
import { URI } from 'vscode-uri'
import { getDocumentSettings } from './util/getDocumentSettings'
import {
@ -34,7 +35,6 @@ import {
clearAllDiagnostics,
} from './providers/diagnostics/diagnosticsProvider'
import { createEmitter } from '../lib/emitter'
import { provideCodeActions } from './providers/codeActions/codeActionProvider'
import { registerDocumentColorProvider } from './providers/documentColorProvider'
let connection = createConnection(ProposedFeatures.all)
@ -202,7 +202,9 @@ connection.onDidChangeConfiguration((change) => {
connection.onCompletion(
(params: CompletionParams): Promise<CompletionList> => {
if (!state.enabled) return null
return provideCompletions(state, params)
let document = state.editor.documents.get(params.textDocument.uri)
if (!document) return null
return doComplete(state, document, params.position)
}
)
@ -216,14 +218,16 @@ connection.onCompletionResolve(
connection.onHover(
(params: TextDocumentPositionParams): Hover => {
if (!state.enabled) return null
return provideHover(state, params)
let document = state.editor.documents.get(params.textDocument.uri)
if (!document) return null
return doHover(state, document, params.position)
}
)
connection.onCodeAction(
(params: CodeActionParams): Promise<CodeAction[]> => {
if (!state.enabled) return null
return provideCodeActions(state, params)
return doCodeActions(state, params)
}
)

View File

@ -0,0 +1 @@
export * from 'tailwindcss-language-service'

View File

@ -2,11 +2,11 @@
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"rootDir": "src",
"rootDir": "../",
"sourceMap": true,
"moduleResolution": "node",
"esModuleInterop": true,
"allowJs": true
},
"include": ["src"]
"include": ["src", "../tailwindcss-language-service"]
}

View File

@ -0,0 +1,2 @@
/node_modules
/dist

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
{
"name": "tailwindcss-language-service",
"version": "0.0.1",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
"src",
"dist"
],
"scripts": {
"start": "tsdx watch",
"build": "tsdx build",
"test": "tsdx test",
"lint": "tsdx lint"
},
"dependencies": {
"@ctrl/tinycolor": "^3.1.4",
"@types/moo": "^0.5.3",
"css.escape": "^1.5.1",
"detect-indent": "^6.0.0",
"dlv": "^1.1.3",
"line-column": "^1.0.2",
"mitt": "^2.1.0",
"moo": "^0.5.1",
"semver": "^7.3.2",
"sift-string": "^0.0.2",
"tsdx": "^0.13.3",
"tslib": "^2.0.1",
"typescript": "^4.0.2",
"vscode-emmet-helper-bundled": "^0.0.1",
"vscode-languageclient": "^5.2.1",
"vscode-languageserver": "^5.2.1"
}
}

View File

@ -1,7 +1,7 @@
import { CodeAction, CodeActionParams } from 'vscode-languageserver'
import { State } from '../../util/state'
import { getDiagnostics } from '../diagnostics/diagnosticsProvider'
import { rangesEqual } from '../../util/rangesEqual'
import { State } from '../util/state'
import { doValidate } from '../diagnostics/diagnosticsProvider'
import { rangesEqual } from '../util/rangesEqual'
import {
DiagnosticKind,
isInvalidApplyDiagnostic,
@ -12,7 +12,7 @@ import {
isInvalidScreenDiagnostic,
isInvalidVariantDiagnostic,
} from '../diagnostics/types'
import { flatten, dedupeBy } from '../../../util/array'
import { flatten, dedupeBy } from '../util/array'
import { provideCssConflictCodeActions } from './provideCssConflictCodeActions'
import { provideInvalidApplyCodeActions } from './provideInvalidApplyCodeActions'
import { provideSuggestionCodeActions } from './provideSuggestionCodeActions'
@ -23,7 +23,7 @@ async function getDiagnosticsFromCodeActionParams(
only?: DiagnosticKind[]
): Promise<AugmentedDiagnostic[]> {
let document = state.editor.documents.get(params.textDocument.uri)
let diagnostics = await getDiagnostics(state, document, only)
let diagnostics = await doValidate(state, document, only)
return params.context.diagnostics
.map((diagnostic) => {
@ -38,7 +38,7 @@ async function getDiagnosticsFromCodeActionParams(
.filter(Boolean)
}
export async function provideCodeActions(
export async function doCodeActions(
state: State,
params: CodeActionParams
): Promise<CodeAction[]> {

View File

@ -1,12 +1,12 @@
import { State } from '../../util/state'
import { State } from '../util/state'
import {
CodeActionParams,
CodeAction,
CodeActionKind,
} from 'vscode-languageserver'
import { CssConflictDiagnostic } from '../diagnostics/types'
import { joinWithAnd } from '../../util/joinWithAnd'
import { removeRangesFromString } from '../../util/removeRangesFromString'
import { joinWithAnd } from '../util/joinWithAnd'
import { removeRangesFromString } from '../util/removeRangesFromString'
export async function provideCssConflictCodeActions(
_state: State,

View File

@ -5,24 +5,24 @@ import {
TextEdit,
Range,
} from 'vscode-languageserver'
import { State } from '../../util/state'
import { State } from '../util/state'
import { InvalidApplyDiagnostic } from '../diagnostics/types'
import { getClassNameParts } from '../../util/getClassNameAtPosition'
import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
import { isCssDoc } from '../../util/css'
import { isWithinRange } from '../../util/isWithinRange'
import { isCssDoc } from '../util/css'
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
import { getClassNameMeta } from '../util/getClassNameMeta'
import { getClassNameParts } from '../util/getClassNameAtPosition'
import { validateApply } from '../util/validateApply'
import { isWithinRange } from '../util/isWithinRange'
const dlv = require('dlv')
import type { Root, NodeSource } from 'postcss'
import { absoluteRange } from '../../util/absoluteRange'
import { removeRangesFromString } from '../../util/removeRangesFromString'
import { absoluteRange } from '../util/absoluteRange'
import { removeRangesFromString } from '../util/removeRangesFromString'
import detectIndent from 'detect-indent'
import isObject from '../../../util/isObject'
import { cssObjToAst } from '../../util/cssObjToAst'
import isObject from '../util/isObject'
import { cssObjToAst } from '../util/cssObjToAst'
import dset from 'dset'
import selectorParser from 'postcss-selector-parser'
import { flatten } from '../../../util/array'
import { getClassNameMeta } from '../../util/getClassNameMeta'
import { validateApply } from '../../util/validateApply'
import { flatten } from '../util/array'
export async function provideInvalidApplyCodeActions(
state: State,
@ -129,6 +129,8 @@ export async function provideInvalidApplyCodeActions(
return false
})
return true
})
}
}),

View File

@ -1,4 +1,4 @@
import { State } from '../../util/state'
import { State } from '../util/state'
import {
CodeActionParams,
CodeAction,

View File

@ -1,35 +1,36 @@
import { State } from '../util/state'
import {
import { State } from './util/state'
import type {
CompletionItem,
CompletionItemKind,
CompletionParams,
Range,
MarkupKind,
CompletionList,
TextDocument,
Position,
} from 'vscode-languageserver'
const dlv = require('dlv')
import removeMeta from '../util/removeMeta'
import { getColor, getColorFromValue } from '../util/color'
import { isHtmlContext } from '../util/html'
import { isCssContext } from '../util/css'
import { findLast } from '../util/find'
import { stringifyConfigValue, stringifyCss } from '../util/stringify'
import { stringifyScreen, Screen } from '../util/screens'
import isObject from '../../util/isObject'
import removeMeta from './util/removeMeta'
import { getColor, getColorFromValue } from './util/color'
import { isHtmlContext } from './util/html'
import { isCssContext } from './util/css'
import { findLast } from './util/find'
import { stringifyConfigValue, stringifyCss } from './util/stringify'
import { stringifyScreen, Screen } from './util/screens'
import isObject from './util/isObject'
import * as emmetHelper from 'vscode-emmet-helper-bundled'
import { isValidLocationForEmmetAbbreviation } from '../util/isValidLocationForEmmetAbbreviation'
import { getDocumentSettings } from '../util/getDocumentSettings'
import { isJsContext } from '../util/js'
import { naturalExpand } from '../util/naturalExpand'
import { isValidLocationForEmmetAbbreviation } from './util/isValidLocationForEmmetAbbreviation'
import { getDocumentSettings } from './util/getDocumentSettings'
import { isJsContext } from './util/js'
import { naturalExpand } from './util/naturalExpand'
import semver from 'semver'
import { docsUrl } from '../util/docsUrl'
import { ensureArray } from '../../util/array'
import { docsUrl } from './util/docsUrl'
import { ensureArray } from './util/array'
import {
getClassAttributeLexer,
getComputedClassAttributeLexer,
} from '../util/lexers'
import { validateApply } from '../util/validateApply'
import { flagEnabled } from '../util/flagEnabled'
} from './util/lexers'
import { validateApply } from './util/validateApply'
import { flagEnabled } from './util/flagEnabled'
function completionsFromClassList(
state: State,
@ -78,19 +79,19 @@ function completionsFromClassList(
items: Object.keys(isSubset ? subset : state.classNames.classNames)
.map((className, index) => {
let label = className
let kind: CompletionItemKind = CompletionItemKind.Constant
let kind: CompletionItemKind = 21
let documentation: string = null
let command: any
let sortText = naturalExpand(index)
if (isContextItem(state, [...subsetKey, className])) {
kind = CompletionItemKind.Module
kind = 9
command = { title: '', command: 'editor.action.triggerSuggest' }
label += sep
sortText = '-' + sortText // move to top
} else {
const color = getColor(state, [className])
if (color !== null) {
kind = CompletionItemKind.Color
kind = 16
if (typeof color !== 'string' && color.a !== 0) {
documentation = color.toRgbString()
}
@ -122,10 +123,10 @@ function completionsFromClassList(
function provideClassAttributeCompletions(
state: State,
{ context, position, textDocument }: CompletionParams
document: TextDocument,
position: Position
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
let str = doc.getText({
let str = document.getText({
start: { line: Math.max(position.line - 10, 0), character: 0 },
end: position,
})
@ -170,10 +171,10 @@ function provideClassAttributeCompletions(
function provideAtApplyCompletions(
state: State,
{ context, position, textDocument }: CompletionParams
document: TextDocument,
position: Position
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
let str = doc.getText({
let str = document.getText({
start: { line: Math.max(position.line - 30, 0), character: 0 },
end: position,
})
@ -197,7 +198,7 @@ function provideAtApplyCompletions(
end: position,
},
(item) => {
if (item.kind === CompletionItemKind.Module) {
if (item.kind === 9) {
return flagEnabled(state, 'applyComplexClasses')
}
let validated = validateApply(state, item.data)
@ -208,19 +209,18 @@ function provideAtApplyCompletions(
function provideClassNameCompletions(
state: State,
params: CompletionParams
document: TextDocument,
position: Position
): CompletionList {
let doc = state.editor.documents.get(params.textDocument.uri)
if (
isHtmlContext(state, doc, params.position) ||
isJsContext(state, doc, params.position)
isHtmlContext(state, document, position) ||
isJsContext(state, document, position)
) {
return provideClassAttributeCompletions(state, params)
return provideClassAttributeCompletions(state, document, position)
}
if (isCssContext(state, doc, params.position)) {
return provideAtApplyCompletions(state, params)
if (isCssContext(state, document, position)) {
return provideAtApplyCompletions(state, document, position)
}
return null
@ -228,15 +228,14 @@ function provideClassNameCompletions(
function provideCssHelperCompletions(
state: State,
{ position, textDocument }: CompletionParams
document: TextDocument,
position: Position
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
if (!isCssContext(state, doc, position)) {
if (!isCssContext(state, document, position)) {
return null
}
let text = doc.getText({
let text = document.getText({
start: { line: position.line, character: 0 },
// read one extra character so we can see if it's a ] later
end: { line: position.line, character: position.character + 1 },
@ -303,11 +302,7 @@ function provideCssHelperCompletions(
label: item,
filterText: `${replaceDot ? '.' : ''}${item}`,
sortText: naturalExpand(index),
kind: color
? CompletionItemKind.Color
: isObject(obj[item])
? CompletionItemKind.Module
: CompletionItemKind.Property,
kind: color ? 16 : isObject(obj[item]) ? 9 : 10,
// VS Code bug causes '0' to not display in some cases
detail: detail === '0' ? '0 ' : detail,
documentation: color,
@ -336,15 +331,14 @@ function provideCssHelperCompletions(
// TODO: vary docs links based on Tailwind version
function provideTailwindDirectiveCompletions(
state: State,
{ position, textDocument }: CompletionParams
document: TextDocument,
position: Position
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
if (!isCssContext(state, doc, position)) {
if (!isCssContext(state, document, position)) {
return null
}
let text = doc.getText({
let text = document.getText({
start: { line: position.line, character: 0 },
end: position,
})
@ -360,7 +354,7 @@ function provideTailwindDirectiveCompletions(
? {
label: 'base',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `This injects Tailwinds base styles and any base styles registered by plugins.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#tailwind'
@ -370,7 +364,7 @@ function provideTailwindDirectiveCompletions(
: {
label: 'preflight',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `This injects Tailwinds base styles, which is a combination of Normalize.css and some additional base styles.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#tailwind'
@ -380,7 +374,7 @@ function provideTailwindDirectiveCompletions(
{
label: 'components',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `This injects Tailwinds component classes and any component classes registered by plugins.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#tailwind'
@ -390,7 +384,7 @@ function provideTailwindDirectiveCompletions(
{
label: 'utilities',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `This injects Tailwinds utility classes and any utility classes registered by plugins.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#tailwind'
@ -400,7 +394,7 @@ function provideTailwindDirectiveCompletions(
{
label: 'screens',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `Use this directive to control where Tailwind injects the responsive variations of each utility.\n\nIf omitted, Tailwind will append these classes to the very end of your stylesheet by default.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#tailwind'
@ -409,7 +403,7 @@ function provideTailwindDirectiveCompletions(
},
].map((item) => ({
...item,
kind: CompletionItemKind.Constant,
kind: 21,
data: '@tailwind',
textEdit: {
newText: item.label,
@ -427,15 +421,14 @@ function provideTailwindDirectiveCompletions(
function provideVariantsDirectiveCompletions(
state: State,
{ position, textDocument }: CompletionParams
document: TextDocument,
position: Position
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
if (!isCssContext(state, doc, position)) {
if (!isCssContext(state, document, position)) {
return null
}
let text = doc.getText({
let text = document.getText({
start: { line: position.line, character: 0 },
end: position,
})
@ -457,7 +450,7 @@ function provideVariantsDirectiveCompletions(
.map((variant) => ({
// TODO: detail
label: variant,
kind: CompletionItemKind.Constant,
kind: 21,
data: 'variant',
textEdit: {
newText: variant,
@ -475,15 +468,14 @@ function provideVariantsDirectiveCompletions(
function provideScreenDirectiveCompletions(
state: State,
{ position, textDocument }: CompletionParams
document: TextDocument,
position: Position
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
if (!isCssContext(state, doc, position)) {
if (!isCssContext(state, document, position)) {
return null
}
let text = doc.getText({
let text = document.getText({
start: { line: position.line, character: 0 },
end: position,
})
@ -504,7 +496,7 @@ function provideScreenDirectiveCompletions(
isIncomplete: false,
items: Object.keys(screens).map((screen, index) => ({
label: screen,
kind: CompletionItemKind.Constant,
kind: 21,
data: 'screen',
sortText: naturalExpand(index),
textEdit: {
@ -523,15 +515,14 @@ function provideScreenDirectiveCompletions(
function provideCssDirectiveCompletions(
state: State,
{ position, textDocument }: CompletionParams
document: TextDocument,
position: Position
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
if (!isCssContext(state, doc, position)) {
if (!isCssContext(state, document, position)) {
return null
}
let text = doc.getText({
let text = document.getText({
start: { line: position.line, character: 0 },
end: position,
})
@ -544,7 +535,7 @@ function provideCssDirectiveCompletions(
{
label: '@tailwind',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `Use the \`@tailwind\` directive to insert Tailwinds \`base\`, \`components\`, \`utilities\` and \`screens\` styles into your CSS.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#tailwind'
@ -554,7 +545,7 @@ function provideCssDirectiveCompletions(
{
label: '@variants',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `You can generate \`responsive\`, \`hover\`, \`focus\`, \`active\`, and \`group-hover\` versions of your own utilities by wrapping their definitions in the \`@variants\` directive.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#variants'
@ -564,7 +555,7 @@ function provideCssDirectiveCompletions(
{
label: '@responsive',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `You can generate responsive variants of your own classes by wrapping their definitions in the \`@responsive\` directive.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#responsive'
@ -574,7 +565,7 @@ function provideCssDirectiveCompletions(
{
label: '@screen',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `The \`@screen\` directive allows you to create media queries that reference your breakpoints by name instead of duplicating their values in your own CSS.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#screen'
@ -584,7 +575,7 @@ function provideCssDirectiveCompletions(
{
label: '@apply',
documentation: {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: `Use \`@apply\` to inline any existing utility classes into your own custom CSS.\n\n[Tailwind CSS Documentation](${docsUrl(
state.version,
'functions-and-directives/#apply'
@ -597,7 +588,7 @@ function provideCssDirectiveCompletions(
isIncomplete: false,
items: items.map((item) => ({
...item,
kind: CompletionItemKind.Keyword,
kind: 14,
data: 'directive',
textEdit: {
newText: item.label,
@ -615,16 +606,15 @@ function provideCssDirectiveCompletions(
async function provideEmmetCompletions(
state: State,
{ position, textDocument }: CompletionParams
document: TextDocument,
position: Position
): Promise<CompletionList> {
let doc = state.editor.documents.get(textDocument.uri)
let settings = await getDocumentSettings(state, doc)
let settings = await getDocumentSettings(state, document)
if (settings.emmetCompletions !== true) return null
const syntax = isHtmlContext(state, doc, position)
const syntax = isHtmlContext(state, document, position)
? 'html'
: isJsContext(state, doc, position)
: isJsContext(state, document, position)
? 'jsx'
: null
@ -633,7 +623,7 @@ async function provideEmmetCompletions(
}
const extractAbbreviationResults = emmetHelper.extractAbbreviation(
doc,
document,
position,
true
)
@ -649,14 +639,14 @@ async function provideEmmetCompletions(
if (
!isValidLocationForEmmetAbbreviation(
doc,
document,
extractAbbreviationResults.abbreviationRange
)
) {
return null
}
const emmetItems = emmetHelper.doComplete(doc, position, syntax, {})
const emmetItems = emmetHelper.doComplete(document, position, syntax, {})
if (!emmetItems || !emmetItems.items || emmetItems.items.length !== 1) {
return null
@ -679,23 +669,24 @@ async function provideEmmetCompletions(
})
}
export async function provideCompletions(
export async function doComplete(
state: State,
params: CompletionParams
): Promise<CompletionList> {
document: TextDocument,
position: Position
) {
if (state === null) return { items: [], isIncomplete: false }
const result =
provideClassNameCompletions(state, params) ||
provideCssHelperCompletions(state, params) ||
provideCssDirectiveCompletions(state, params) ||
provideScreenDirectiveCompletions(state, params) ||
provideVariantsDirectiveCompletions(state, params) ||
provideTailwindDirectiveCompletions(state, params)
provideClassNameCompletions(state, document, position) ||
provideCssHelperCompletions(state, document, position) ||
provideCssDirectiveCompletions(state, document, position) ||
provideScreenDirectiveCompletions(state, document, position) ||
provideVariantsDirectiveCompletions(state, document, position) ||
provideTailwindDirectiveCompletions(state, document, position)
if (result) return result
return provideEmmetCompletions(state, params)
return provideEmmetCompletions(state, document, position)
}
export function resolveCompletionItem(
@ -728,7 +719,7 @@ export function resolveCompletionItem(
const css = stringifyCss(item.data.join(':'), className)
if (css) {
item.documentation = {
kind: MarkupKind.Markdown,
kind: 'markdown' as typeof MarkupKind.Markdown,
value: ['```css', css, '```'].join('\n'),
}
}

View File

@ -1,6 +1,6 @@
import { TextDocument } from 'vscode-languageserver'
import { State } from '../../util/state'
import { getDocumentSettings } from '../../util/getDocumentSettings'
import type { TextDocument } from 'vscode-languageserver'
import { State } from '../util/state'
import { getDocumentSettings } from '../util/getDocumentSettings'
import { DiagnosticKind, AugmentedDiagnostic } from './types'
import { getCssConflictDiagnostics } from './getCssConflictDiagnostics'
import { getInvalidApplyDiagnostics } from './getInvalidApplyDiagnostics'
@ -9,7 +9,7 @@ import { getInvalidVariantDiagnostics } from './getInvalidVariantDiagnostics'
import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnostics'
import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics'
export async function getDiagnostics(
export async function doValidate(
state: State,
document: TextDocument,
only: DiagnosticKind[] = [
@ -50,7 +50,7 @@ export async function getDiagnostics(
export async function provideDiagnostics(state: State, document: TextDocument) {
state.editor.connection.sendDiagnostics({
uri: document.uri,
diagnostics: await getDiagnostics(state, document),
diagnostics: await doValidate(state, document),
})
}

View File

@ -1,14 +1,14 @@
import { joinWithAnd } from '../../util/joinWithAnd'
import { State, Settings } from '../../util/state'
import { TextDocument, DiagnosticSeverity } from 'vscode-languageserver'
import { joinWithAnd } from '../util/joinWithAnd'
import { State, Settings } from '../util/state'
import type { TextDocument, DiagnosticSeverity } from 'vscode-languageserver'
import { CssConflictDiagnostic, DiagnosticKind } from './types'
import {
findClassListsInDocument,
getClassNamesInClassList,
} from '../../util/find'
import { getClassNameDecls } from '../../util/getClassNameDecls'
import { getClassNameMeta } from '../../util/getClassNameMeta'
import { equal } from '../../../util/array'
} from '../util/find'
import { getClassNameDecls } from '../util/getClassNameDecls'
import { getClassNameMeta } from '../util/getClassNameMeta'
import { equal } from '../util/array'
export function getCssConflictDiagnostics(
state: State,
@ -58,8 +58,8 @@ export function getCssConflictDiagnostics(
range: className.range,
severity:
severity === 'error'
? DiagnosticSeverity.Error
: DiagnosticSeverity.Warning,
? 1 /* DiagnosticSeverity.Error */
: 2 /* DiagnosticSeverity.Warning */,
message: `'${className.className}' applies the same CSS ${
properties.length === 1 ? 'property' : 'properties'
} as ${joinWithAnd(

View File

@ -1,9 +1,9 @@
import { findClassNamesInRange } from '../../util/find'
import { findClassNamesInRange } from '../util/find'
import { InvalidApplyDiagnostic, DiagnosticKind } from './types'
import { Settings, State } from '../../util/state'
import { TextDocument, DiagnosticSeverity } from 'vscode-languageserver'
import { validateApply } from '../../util/validateApply'
import { flagEnabled } from '../../util/flagEnabled'
import { Settings, State } from '../util/state'
import type { TextDocument, DiagnosticSeverity } from 'vscode-languageserver'
import { validateApply } from '../util/validateApply'
import { flagEnabled } from '../util/flagEnabled'
export function getInvalidApplyDiagnostics(
state: State,
@ -27,8 +27,8 @@ export function getInvalidApplyDiagnostics(
code: DiagnosticKind.InvalidApply,
severity:
severity === 'error'
? DiagnosticSeverity.Error
: DiagnosticSeverity.Warning,
? 1 /* DiagnosticSeverity.Error */
: 2 /* DiagnosticSeverity.Warning */,
range: className.range,
message: result.reason,
className,

View File

@ -1,14 +1,14 @@
import { State, Settings } from '../../util/state'
import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
import { State, Settings } from '../util/state'
import type { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
import { isCssDoc } from '../../util/css'
import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
import { findAll, indexToPosition } from '../../util/find'
import { stringToPath } from '../../util/stringToPath'
import isObject from '../../../util/isObject'
import { closest } from '../../util/closest'
import { absoluteRange } from '../../util/absoluteRange'
import { combinations } from '../../util/combinations'
import { isCssDoc } from '../util/css'
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
import { findAll, indexToPosition } from '../util/find'
import { stringToPath } from '../util/stringToPath'
import isObject from '../util/isObject'
import { closest } from '../util/closest'
import { absoluteRange } from '../util/absoluteRange'
import { combinations } from '../util/combinations'
const dlv = require('dlv')
function pathToString(path: string | string[]): string {
@ -218,8 +218,8 @@ export function getInvalidConfigPathDiagnostics(
),
severity:
severity === 'error'
? DiagnosticSeverity.Error
: DiagnosticSeverity.Warning,
? 1 /* DiagnosticSeverity.Error */
: 2 /* DiagnosticSeverity.Warning */,
message: result.reason,
suggestions: result.suggestions,
})

View File

@ -1,11 +1,11 @@
import { State, Settings } from '../../util/state'
import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
import { State, Settings } from '../util/state'
import type { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
import { InvalidScreenDiagnostic, DiagnosticKind } from './types'
import { isCssDoc } from '../../util/css'
import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
import { findAll, indexToPosition } from '../../util/find'
import { closest } from '../../util/closest'
import { absoluteRange } from '../../util/absoluteRange'
import { isCssDoc } from '../util/css'
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
import { findAll, indexToPosition } from '../util/find'
import { closest } from '../util/closest'
import { absoluteRange } from '../util/absoluteRange'
const dlv = require('dlv')
export function getInvalidScreenDiagnostics(
@ -63,8 +63,8 @@ export function getInvalidScreenDiagnostics(
),
severity:
severity === 'error'
? DiagnosticSeverity.Error
: DiagnosticSeverity.Warning,
? 1 /* DiagnosticSeverity.Error */
: 2 /* DiagnosticSeverity.Warning */,
message,
suggestions,
})

View File

@ -1,12 +1,12 @@
import { State, Settings } from '../../util/state'
import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
import { State, Settings } from '../util/state'
import type { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
import { InvalidTailwindDirectiveDiagnostic, DiagnosticKind } from './types'
import { isCssDoc } from '../../util/css'
import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
import { findAll, indexToPosition } from '../../util/find'
import { isCssDoc } from '../util/css'
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
import { findAll, indexToPosition } from '../util/find'
import semver from 'semver'
import { closest } from '../../util/closest'
import { absoluteRange } from '../../util/absoluteRange'
import { closest } from '../util/closest'
import { absoluteRange } from '../util/absoluteRange'
export function getInvalidTailwindDirectiveDiagnostics(
state: State,
@ -71,8 +71,8 @@ export function getInvalidTailwindDirectiveDiagnostics(
),
severity:
severity === 'error'
? DiagnosticSeverity.Error
: DiagnosticSeverity.Warning,
? 1 /* DiagnosticSeverity.Error */
: 2 /* DiagnosticSeverity.Warning */,
message,
suggestions,
})

View File

@ -1,11 +1,11 @@
import { State, Settings } from '../../util/state'
import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
import { State, Settings } from '../util/state'
import type { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
import { InvalidVariantDiagnostic, DiagnosticKind } from './types'
import { isCssDoc } from '../../util/css'
import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
import { findAll, indexToPosition } from '../../util/find'
import { closest } from '../../util/closest'
import { absoluteRange } from '../../util/absoluteRange'
import { isCssDoc } from '../util/css'
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
import { findAll, indexToPosition } from '../util/find'
import { closest } from '../util/closest'
import { absoluteRange } from '../util/absoluteRange'
export function getInvalidVariantDiagnostics(
state: State,
@ -64,8 +64,8 @@ export function getInvalidVariantDiagnostics(
),
severity:
severity === 'error'
? DiagnosticSeverity.Error
: DiagnosticSeverity.Warning,
? 1 /* DiagnosticSeverity.Error */
: 2 /* DiagnosticSeverity.Warning */,
message,
suggestions,
})

View File

@ -1,5 +1,5 @@
import { Diagnostic } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from '../../util/state'
import type { Diagnostic } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from '../util/state'
export enum DiagnosticKind {
CssConflict = 'cssConflict',

View File

@ -0,0 +1,43 @@
import { State } from './util/state'
import {
findClassListsInDocument,
getClassNamesInClassList,
findHelperFunctionsInDocument,
} from './util/find'
import { getClassNameParts } from './util/getClassNameAtPosition'
import { getColor, getColorFromValue } from './util/color'
import { stringToPath } from './util/stringToPath'
import type { TextDocument } from 'vscode-languageserver'
const dlv = require('dlv')
export function getDocumentColors(state: State, document: TextDocument) {
let colors = []
if (!state.enabled) return colors
let classLists = findClassListsInDocument(state, document)
classLists.forEach((classList) => {
let classNames = getClassNamesInClassList(classList)
classNames.forEach((className) => {
let parts = getClassNameParts(state, className.className)
if (!parts) return
let color = getColor(state, parts)
if (color === null || typeof color === 'string' || color.a === 0) {
return
}
colors.push({ range: className.range, color: color.toRgbString() })
})
})
let helperFns = findHelperFunctionsInDocument(state, document)
helperFns.forEach((fn) => {
let keys = stringToPath(fn.value)
let base = fn.helper === 'theme' ? ['theme'] : []
let value = dlv(state.config, [...base, ...keys])
let color = getColorFromValue(value)
if (color) {
colors.push({ range: fn.valueRange, color })
}
})
return colors
}

View File

@ -1,30 +1,31 @@
import { State } from '../util/state'
import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
import { stringifyCss, stringifyConfigValue } from '../util/stringify'
import { State } from './util/state'
import type { Hover, TextDocument, Position } from 'vscode-languageserver'
import { stringifyCss, stringifyConfigValue } from './util/stringify'
const dlv = require('dlv')
import { isCssContext } from '../util/css'
import { findClassNameAtPosition } from '../util/find'
import { validateApply } from '../util/validateApply'
import { getClassNameParts } from '../util/getClassNameAtPosition'
import { isCssContext } from './util/css'
import { findClassNameAtPosition } from './util/find'
import { validateApply } from './util/validateApply'
import { getClassNameParts } from './util/getClassNameAtPosition'
export function provideHover(
export function doHover(
state: State,
params: TextDocumentPositionParams
document: TextDocument,
position: Position
): Hover {
return (
provideClassNameHover(state, params) || provideCssHelperHover(state, params)
provideClassNameHover(state, document, position) ||
provideCssHelperHover(state, document, position)
)
}
function provideCssHelperHover(
state: State,
{ textDocument, position }: TextDocumentPositionParams
document: TextDocument,
position: Position
): Hover {
let doc = state.editor.documents.get(textDocument.uri)
if (!isCssContext(state, document, position)) return null
if (!isCssContext(state, doc, position)) return null
const line = doc.getText({
const line = document.getText({
start: { line: position.line, character: 0 },
end: { line: position.line + 1, character: 0 },
})
@ -72,17 +73,16 @@ function provideCssHelperHover(
function provideClassNameHover(
state: State,
{ textDocument, position }: TextDocumentPositionParams
document: TextDocument,
position: Position
): Hover {
let doc = state.editor.documents.get(textDocument.uri)
let className = findClassNameAtPosition(state, doc, position)
let className = findClassNameAtPosition(state, document, position)
if (className === null) return null
const parts = getClassNameParts(state, className.className)
if (!parts) return null
if (isCssContext(state, doc, position)) {
if (isCssContext(state, document, position)) {
let validated = validateApply(state, parts)
if (validated === null || validated.isApplyable === false) {
return null

View File

@ -0,0 +1,7 @@
export { doComplete, resolveCompletionItem } from './completionProvider'
export { doValidate } from './diagnostics/diagnosticsProvider'
export { doHover } from './hoverProvider'
export { doCodeActions } from './codeActions/codeActionProvider'
export { getDocumentColors } from './documentColorProvider'
export * from './util/state'
export * from './diagnostics/types'

View File

@ -1,4 +1,4 @@
import { Range } from 'vscode-languageserver'
import type { Range } from 'vscode-languageserver'
export function absoluteRange(range: Range, reference?: Range) {
return {

View File

@ -0,0 +1,28 @@
export function dedupe<T>(arr: Array<T>): Array<T> {
return arr.filter((value, index, self) => self.indexOf(value) === index)
}
export function dedupeBy<T>(
arr: Array<T>,
transform: (item: T) => any
): Array<T> {
return arr.filter(
(value, index, self) =>
self.map(transform).indexOf(transform(value)) === index
)
}
export function ensureArray<T>(value: T | T[]): T[] {
return Array.isArray(value) ? value : [value]
}
export function flatten<T>(arrays: T[][]): T[] {
return [].concat.apply([], arrays)
}
export function equal(arr1: any[], arr2: any[]): boolean {
return (
JSON.stringify(arr1.concat([]).sort()) ===
JSON.stringify(arr2.concat([]).sort())
)
}

View File

@ -2,7 +2,7 @@ const dlv = require('dlv')
import { State } from './state'
import removeMeta from './removeMeta'
import { TinyColor } from '@ctrl/tinycolor'
import { ensureArray, dedupe, flatten } from '../../util/array'
import { ensureArray, dedupe, flatten } from './array'
const COLOR_PROPS = [
'caret-color',

View File

@ -1,6 +1,6 @@
export function combinations(str: string): string[] {
let fn = function (active: string, rest: string, a: string[]) {
if (!active && !rest) return
if (!active && !rest) return undefined
if (!rest) {
a.push(active)
} else {

View File

@ -1,4 +1,4 @@
import { TextDocument, Position } from 'vscode-languageserver'
import type { TextDocument, Position } from 'vscode-languageserver'
import { isInsideTag, isVueDoc, isSvelteDoc, isHtmlDoc } from './html'
import { State } from './state'

View File

@ -1,4 +1,4 @@
import { TextDocument, Range, Position } from 'vscode-languageserver'
import type { TextDocument, Range, Position } from 'vscode-languageserver'
import {
DocumentClassName,
DocumentClassList,
@ -10,7 +10,7 @@ import { isCssContext, isCssDoc } from './css'
import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html'
import { isWithinRange } from './isWithinRange'
import { isJsContext, isJsDoc } from './js'
import { flatten } from '../../util/array'
import { flatten } from './array'
import {
getClassAttributeLexer,
getComputedClassAttributeLexer,

View File

@ -0,0 +1,19 @@
import { State, Settings } from './state'
import type { TextDocument } from 'vscode-languageserver'
export async function getDocumentSettings(
state: State,
document: TextDocument
): Promise<Settings> {
if (!state.editor.capabilities.configuration) {
return Promise.resolve(state.editor.globalSettings)
}
let result = state.editor.documentSettings.get(document.uri)
if (!result) {
result = await state.emitter.emit('getConfiguration', {
languageId: document.languageId,
})
state.editor.documentSettings.set(document.uri, result)
}
return result
}

View File

@ -1,4 +1,4 @@
import { TextDocument, Range } from 'vscode-languageserver'
import type { TextDocument, Range } from 'vscode-languageserver'
import { isVueDoc, isHtmlDoc, isSvelteDoc } from './html'
import { State } from './state'
import { findAll, indexToPosition } from './find'

View File

@ -1,4 +1,4 @@
import { TextDocument, Position } from 'vscode-languageserver'
import type { TextDocument, Position } from 'vscode-languageserver'
import { State } from './state'
export const HTML_LANGUAGES = [

View File

@ -0,0 +1,3 @@
export default function isObject(variable: any): boolean {
return Object.prototype.toString.call(variable) === '[object Object]'
}

View File

@ -1,4 +1,4 @@
import { TextDocument, Range, Position } from 'vscode-languageserver'
import type { TextDocument, Range, Position } from 'vscode-languageserver'
export function isValidLocationForEmmetAbbreviation(
document: TextDocument,

View File

@ -1,4 +1,4 @@
import { Position, Range } from 'vscode-languageserver'
import type { Position, Range } from 'vscode-languageserver'
export function isWithinRange(position: Position, range: Range): boolean {
if (

View File

@ -1,4 +1,4 @@
import { TextDocument, Position } from 'vscode-languageserver'
import type { TextDocument, Position } from 'vscode-languageserver'
import { isHtmlDoc, isInsideTag, isVueDoc, isSvelteDoc } from './html'
import { State } from './state'

View File

@ -0,0 +1,97 @@
import moo from 'moo'
import { lazy } from './lazy'
const classAttributeStates: () => { [x: string]: moo.Rules } = () => ({
doubleClassList: {
lbrace: { match: new RegExp('(?<!\\\\)\\{'), push: 'interpBrace' },
rbrace: { match: new RegExp('(?<!\\\\)\\}'), pop: 1 },
end: { match: new RegExp('(?<!\\\\)"'), pop: 1 },
classlist: { match: new RegExp('[\\s\\S]'), lineBreaks: true },
},
singleClassList: {
lbrace: { match: new RegExp('(?<!\\\\)\\{'), push: 'interpBrace' },
rbrace: { match: new RegExp('(?<!\\\\)\\}'), pop: 1 },
end: { match: new RegExp("(?<!\\\\)'"), pop: 1 },
classlist: { match: new RegExp('[\\s\\S]'), lineBreaks: true },
},
tickClassList: {
lbrace: { match: new RegExp('(?<=(?<!\\\\)\\$)\\{'), push: 'interpBrace' },
rbrace: { match: new RegExp('(?<!\\\\)\\}'), pop: 1 },
end: { match: new RegExp('(?<!\\\\)`'), pop: 1 },
classlist: { match: new RegExp('[\\s\\S]'), lineBreaks: true },
},
interpBrace: {
startSingle: { match: new RegExp("(?<!\\\\)'"), push: 'singleClassList' },
startDouble: { match: new RegExp('(?<!\\\\)"'), push: 'doubleClassList' },
startTick: { match: new RegExp('(?<!\\\\)`'), push: 'tickClassList' },
lbrace: { match: new RegExp('(?<!\\\\)\\{'), push: 'interpBrace' },
rbrace: { match: new RegExp('(?<!\\\\)\\}'), pop: 1 },
text: { match: new RegExp('[\\s\\S]'), lineBreaks: true },
},
interpSingle: {
startDouble: { match: new RegExp('(?<!\\\\)"'), push: 'doubleClassList' },
startTick: { match: new RegExp('(?<!\\\\)`'), push: 'tickClassList' },
single: { match: new RegExp("(?<!\\\\)'"), pop: 1 },
text: { match: new RegExp('[\\s\\S]'), lineBreaks: true },
},
interpDouble: {
startSingle: { match: new RegExp("(?<!\\\\)'"), push: 'singleClassList' },
startTick: { match: new RegExp('(?<!\\\\)`'), push: 'tickClassList' },
double: { match: new RegExp('(?<!\\\\)"'), pop: 1 },
text: { match: new RegExp('[\\s\\S]'), lineBreaks: true },
},
})
const simpleClassAttributeStates: { [x: string]: moo.Rules } = {
main: {
start: { match: '"', push: 'doubleClassList' },
},
doubleClassList: {
end: { match: '"', pop: 1 },
classlist: { match: /[\s\S]/, lineBreaks: true },
},
}
export const getClassAttributeLexer = lazy(() => {
let supportsNegativeLookbehind = true
try {
new RegExp('(?<!)')
} catch (_) {
supportsNegativeLookbehind = false
}
if (supportsNegativeLookbehind) {
return moo.states({
main: {
start1: { match: '"', push: 'doubleClassList' },
start2: { match: "'", push: 'singleClassList' },
start3: { match: '{', push: 'interpBrace' },
},
...classAttributeStates(),
})
}
return moo.states(simpleClassAttributeStates)
})
export const getComputedClassAttributeLexer = lazy(() => {
let supportsNegativeLookbehind = true
try {
new RegExp('(?<!)')
} catch (_) {
supportsNegativeLookbehind = false
}
if (supportsNegativeLookbehind) {
return moo.states({
main: {
lbrace: { match: '{', push: 'interpBrace' },
single: { match: "'", push: 'interpSingle' },
double: { match: '"', push: 'interpDouble' },
},
...classAttributeStates(),
})
}
return moo.states(simpleClassAttributeStates)
})

View File

@ -1,4 +1,4 @@
import isObject from '../../util/isObject'
import isObject from './isObject'
export default function removeMeta(obj: any): any {
let result = {}

View File

@ -1,6 +1,6 @@
import { Range } from 'vscode-languageserver'
import lineColumn from 'line-column'
import { ensureArray } from '../../util/array'
import { ensureArray } from './array'
export function removeRangesFromString(
str: string,

View File

@ -1,4 +1,4 @@
import { Range } from 'vscode-languageserver'
import type { Range } from 'vscode-languageserver'
export function resolveRange(range: Range, relativeTo?: Range) {
return {

View File

@ -1,4 +1,4 @@
import isObject from '../../util/isObject'
import isObject from './isObject'
export type MinMaxScreen = {
min?: string
@ -15,8 +15,8 @@ function isRawScreen(screen: unknown): screen is RawScreen {
return isObject(screen) && (screen as RawScreen).raw !== undefined
}
export function stringifyScreen(screen: Screen): string {
if (!screen) return
export function stringifyScreen(screen: Screen): string | undefined {
if (!screen) return undefined
if (typeof screen === 'string') return `@media (min-width: ${screen})`
if (isRawScreen(screen)) {
return `@media ${(screen as RawScreen).raw}`

View File

@ -1,5 +1,4 @@
import { TextDocuments, Connection, Range } from 'vscode-languageserver'
import { NotificationEmitter } from '../../lib/emitter'
import type { TextDocuments, Connection, Range } from 'vscode-languageserver'
export type ClassNamesTree = {
[key: string]: ClassNamesTree
@ -42,9 +41,15 @@ export type Settings = {
}
}
interface NotificationEmitter {
on: (name: string, handler: (args: any) => void) => void
off: (name: string, handler: (args: any) => void) => void
emit: (name: string, args: any) => Promise<any>
}
export type State = null | {
enabled: boolean
emitter: NotificationEmitter
emitter?: NotificationEmitter
version?: string
configPath?: string
config?: any

View File

@ -1,7 +1,7 @@
import removeMeta from './removeMeta'
const dlv = require('dlv')
import escapeClassName from 'css.escape'
import { ensureArray } from '../../util/array'
import { ensureArray } from './array'
export function stringifyConfigValue(x: any): string {
if (typeof x === 'string') return x

View File

@ -0,0 +1,19 @@
{
"include": ["src", "types"],
"compilerOptions": {
"module": "esnext",
"lib": ["dom", "esnext"],
"importHelpers": true,
"declaration": true,
"sourceMap": true,
"rootDir": "./src",
"strict": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"jsx": "react",
"esModuleInterop": true
}
}

Some files were not shown because too many files have changed in this diff Show More