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 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"
}

23204
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", "private": true,
"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": { "scripts": {
"dev": "glob-exec --foreach --parallel \"src/*.ts\" -- \"ncc build {{file}} --watch -o dist/{{file.toString().replace(/^src\\//, '').replace(/\\.ts$/, '')}}\"", "bootstrap": "lerna clean && lerna bootstrap --hoist"
"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"
}, },
"devDependencies": { "devDependencies": {
"@ctrl/tinycolor": "^3.1.0", "lerna": "^3.22.1"
"@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"
} }
} }

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, CodeAction,
} from 'vscode-languageserver' } from 'vscode-languageserver'
import getTailwindState from '../class-names/index' import getTailwindState from '../class-names/index'
import { State, Settings, EditorState } from './util/state' import { State, Settings, EditorState } from 'tailwindcss-language-service'
import { import {
provideCompletions,
resolveCompletionItem, resolveCompletionItem,
} from './providers/completionProvider' doComplete,
import { provideHover } from './providers/hoverProvider' doHover,
doCodeActions,
} from 'tailwindcss-language-service'
import { URI } from 'vscode-uri' import { URI } from 'vscode-uri'
import { getDocumentSettings } from './util/getDocumentSettings' import { getDocumentSettings } from './util/getDocumentSettings'
import { import {
@ -34,7 +35,6 @@ import {
clearAllDiagnostics, clearAllDiagnostics,
} from './providers/diagnostics/diagnosticsProvider' } from './providers/diagnostics/diagnosticsProvider'
import { createEmitter } from '../lib/emitter' import { createEmitter } from '../lib/emitter'
import { provideCodeActions } from './providers/codeActions/codeActionProvider'
import { registerDocumentColorProvider } from './providers/documentColorProvider' import { registerDocumentColorProvider } from './providers/documentColorProvider'
let connection = createConnection(ProposedFeatures.all) let connection = createConnection(ProposedFeatures.all)
@ -202,7 +202,9 @@ connection.onDidChangeConfiguration((change) => {
connection.onCompletion( connection.onCompletion(
(params: CompletionParams): Promise<CompletionList> => { (params: CompletionParams): Promise<CompletionList> => {
if (!state.enabled) return null 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( connection.onHover(
(params: TextDocumentPositionParams): Hover => { (params: TextDocumentPositionParams): Hover => {
if (!state.enabled) return null 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( connection.onCodeAction(
(params: CodeActionParams): Promise<CodeAction[]> => { (params: CodeActionParams): Promise<CodeAction[]> => {
if (!state.enabled) return null 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": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es6", "target": "es6",
"rootDir": "src", "rootDir": "../",
"sourceMap": true, "sourceMap": true,
"moduleResolution": "node", "moduleResolution": "node",
"esModuleInterop": true, "esModuleInterop": true,
"allowJs": 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 { CodeAction, CodeActionParams } from 'vscode-languageserver'
import { State } from '../../util/state' import { State } from '../util/state'
import { getDiagnostics } from '../diagnostics/diagnosticsProvider' import { doValidate } from '../diagnostics/diagnosticsProvider'
import { rangesEqual } from '../../util/rangesEqual' import { rangesEqual } from '../util/rangesEqual'
import { import {
DiagnosticKind, DiagnosticKind,
isInvalidApplyDiagnostic, isInvalidApplyDiagnostic,
@ -12,7 +12,7 @@ import {
isInvalidScreenDiagnostic, isInvalidScreenDiagnostic,
isInvalidVariantDiagnostic, isInvalidVariantDiagnostic,
} from '../diagnostics/types' } from '../diagnostics/types'
import { flatten, dedupeBy } from '../../../util/array' import { flatten, dedupeBy } from '../util/array'
import { provideCssConflictCodeActions } from './provideCssConflictCodeActions' import { provideCssConflictCodeActions } from './provideCssConflictCodeActions'
import { provideInvalidApplyCodeActions } from './provideInvalidApplyCodeActions' import { provideInvalidApplyCodeActions } from './provideInvalidApplyCodeActions'
import { provideSuggestionCodeActions } from './provideSuggestionCodeActions' import { provideSuggestionCodeActions } from './provideSuggestionCodeActions'
@ -23,7 +23,7 @@ async function getDiagnosticsFromCodeActionParams(
only?: DiagnosticKind[] only?: DiagnosticKind[]
): Promise<AugmentedDiagnostic[]> { ): Promise<AugmentedDiagnostic[]> {
let document = state.editor.documents.get(params.textDocument.uri) 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 return params.context.diagnostics
.map((diagnostic) => { .map((diagnostic) => {
@ -38,7 +38,7 @@ async function getDiagnosticsFromCodeActionParams(
.filter(Boolean) .filter(Boolean)
} }
export async function provideCodeActions( export async function doCodeActions(
state: State, state: State,
params: CodeActionParams params: CodeActionParams
): Promise<CodeAction[]> { ): Promise<CodeAction[]> {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { TextDocument } from 'vscode-languageserver' import type { TextDocument } from 'vscode-languageserver'
import { State } from '../../util/state' import { State } from '../util/state'
import { getDocumentSettings } from '../../util/getDocumentSettings' import { getDocumentSettings } from '../util/getDocumentSettings'
import { DiagnosticKind, AugmentedDiagnostic } from './types' import { DiagnosticKind, AugmentedDiagnostic } from './types'
import { getCssConflictDiagnostics } from './getCssConflictDiagnostics' import { getCssConflictDiagnostics } from './getCssConflictDiagnostics'
import { getInvalidApplyDiagnostics } from './getInvalidApplyDiagnostics' import { getInvalidApplyDiagnostics } from './getInvalidApplyDiagnostics'
@ -9,7 +9,7 @@ import { getInvalidVariantDiagnostics } from './getInvalidVariantDiagnostics'
import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnostics' import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnostics'
import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics' import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics'
export async function getDiagnostics( export async function doValidate(
state: State, state: State,
document: TextDocument, document: TextDocument,
only: DiagnosticKind[] = [ only: DiagnosticKind[] = [
@ -50,7 +50,7 @@ export async function getDiagnostics(
export async function provideDiagnostics(state: State, document: TextDocument) { export async function provideDiagnostics(state: State, document: TextDocument) {
state.editor.connection.sendDiagnostics({ state.editor.connection.sendDiagnostics({
uri: document.uri, 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 { joinWithAnd } from '../util/joinWithAnd'
import { State, Settings } from '../../util/state' import { State, Settings } from '../util/state'
import { TextDocument, DiagnosticSeverity } from 'vscode-languageserver' import type { TextDocument, DiagnosticSeverity } from 'vscode-languageserver'
import { CssConflictDiagnostic, DiagnosticKind } from './types' import { CssConflictDiagnostic, DiagnosticKind } from './types'
import { import {
findClassListsInDocument, findClassListsInDocument,
getClassNamesInClassList, getClassNamesInClassList,
} from '../../util/find' } from '../util/find'
import { getClassNameDecls } from '../../util/getClassNameDecls' import { getClassNameDecls } from '../util/getClassNameDecls'
import { getClassNameMeta } from '../../util/getClassNameMeta' import { getClassNameMeta } from '../util/getClassNameMeta'
import { equal } from '../../../util/array' import { equal } from '../util/array'
export function getCssConflictDiagnostics( export function getCssConflictDiagnostics(
state: State, state: State,
@ -58,8 +58,8 @@ export function getCssConflictDiagnostics(
range: className.range, range: className.range,
severity: severity:
severity === 'error' severity === 'error'
? DiagnosticSeverity.Error ? 1 /* DiagnosticSeverity.Error */
: DiagnosticSeverity.Warning, : 2 /* DiagnosticSeverity.Warning */,
message: `'${className.className}' applies the same CSS ${ message: `'${className.className}' applies the same CSS ${
properties.length === 1 ? 'property' : 'properties' properties.length === 1 ? 'property' : 'properties'
} as ${joinWithAnd( } as ${joinWithAnd(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { Diagnostic } from 'vscode-languageserver' import type { Diagnostic } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from '../../util/state' import { DocumentClassName, DocumentClassList } from '../util/state'
export enum DiagnosticKind { export enum DiagnosticKind {
CssConflict = 'cssConflict', 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 { State } from './util/state'
import { Hover, TextDocumentPositionParams } from 'vscode-languageserver' import type { Hover, TextDocument, Position } from 'vscode-languageserver'
import { stringifyCss, stringifyConfigValue } from '../util/stringify' import { stringifyCss, stringifyConfigValue } from './util/stringify'
const dlv = require('dlv') const dlv = require('dlv')
import { isCssContext } from '../util/css' import { isCssContext } from './util/css'
import { findClassNameAtPosition } from '../util/find' import { findClassNameAtPosition } from './util/find'
import { validateApply } from '../util/validateApply' import { validateApply } from './util/validateApply'
import { getClassNameParts } from '../util/getClassNameAtPosition' import { getClassNameParts } from './util/getClassNameAtPosition'
export function provideHover( export function doHover(
state: State, state: State,
params: TextDocumentPositionParams document: TextDocument,
position: Position
): Hover { ): Hover {
return ( return (
provideClassNameHover(state, params) || provideCssHelperHover(state, params) provideClassNameHover(state, document, position) ||
provideCssHelperHover(state, document, position)
) )
} }
function provideCssHelperHover( function provideCssHelperHover(
state: State, state: State,
{ textDocument, position }: TextDocumentPositionParams document: TextDocument,
position: Position
): Hover { ): 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 = document.getText({
const line = doc.getText({
start: { line: position.line, character: 0 }, start: { line: position.line, character: 0 },
end: { line: position.line + 1, character: 0 }, end: { line: position.line + 1, character: 0 },
}) })
@ -72,17 +73,16 @@ function provideCssHelperHover(
function provideClassNameHover( function provideClassNameHover(
state: State, state: State,
{ textDocument, position }: TextDocumentPositionParams document: TextDocument,
position: Position
): Hover { ): Hover {
let doc = state.editor.documents.get(textDocument.uri) let className = findClassNameAtPosition(state, document, position)
let className = findClassNameAtPosition(state, doc, position)
if (className === null) return null if (className === null) return null
const parts = getClassNameParts(state, className.className) const parts = getClassNameParts(state, className.className)
if (!parts) return null if (!parts) return null
if (isCssContext(state, doc, position)) { if (isCssContext(state, document, position)) {
let validated = validateApply(state, parts) let validated = validateApply(state, parts)
if (validated === null || validated.isApplyable === false) { if (validated === null || validated.isApplyable === false) {
return null 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) { export function absoluteRange(range: Range, reference?: Range) {
return { 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 { State } from './state'
import removeMeta from './removeMeta' import removeMeta from './removeMeta'
import { TinyColor } from '@ctrl/tinycolor' import { TinyColor } from '@ctrl/tinycolor'
import { ensureArray, dedupe, flatten } from '../../util/array' import { ensureArray, dedupe, flatten } from './array'
const COLOR_PROPS = [ const COLOR_PROPS = [
'caret-color', 'caret-color',

View File

@ -1,6 +1,6 @@
export function combinations(str: string): string[] { export function combinations(str: string): string[] {
let fn = function (active: string, rest: string, a: string[]) { let fn = function (active: string, rest: string, a: string[]) {
if (!active && !rest) return if (!active && !rest) return undefined
if (!rest) { if (!rest) {
a.push(active) a.push(active)
} else { } 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 { isInsideTag, isVueDoc, isSvelteDoc, isHtmlDoc } from './html'
import { State } from './state' 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 { import {
DocumentClassName, DocumentClassName,
DocumentClassList, DocumentClassList,
@ -10,7 +10,7 @@ import { isCssContext, isCssDoc } from './css'
import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html' import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html'
import { isWithinRange } from './isWithinRange' import { isWithinRange } from './isWithinRange'
import { isJsContext, isJsDoc } from './js' import { isJsContext, isJsDoc } from './js'
import { flatten } from '../../util/array' import { flatten } from './array'
import { import {
getClassAttributeLexer, getClassAttributeLexer,
getComputedClassAttributeLexer, 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 { isVueDoc, isHtmlDoc, isSvelteDoc } from './html'
import { State } from './state' import { State } from './state'
import { findAll, indexToPosition } from './find' 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' import { State } from './state'
export const HTML_LANGUAGES = [ 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( export function isValidLocationForEmmetAbbreviation(
document: TextDocument, 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 { export function isWithinRange(position: Position, range: Range): boolean {
if ( 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 { isHtmlDoc, isInsideTag, isVueDoc, isSvelteDoc } from './html'
import { State } from './state' 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 { export default function removeMeta(obj: any): any {
let result = {} let result = {}

View File

@ -1,6 +1,6 @@
import { Range } from 'vscode-languageserver' import { Range } from 'vscode-languageserver'
import lineColumn from 'line-column' import lineColumn from 'line-column'
import { ensureArray } from '../../util/array' import { ensureArray } from './array'
export function removeRangesFromString( export function removeRangesFromString(
str: string, 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) { export function resolveRange(range: Range, relativeTo?: Range) {
return { return {

View File

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

View File

@ -1,5 +1,4 @@
import { TextDocuments, Connection, Range } from 'vscode-languageserver' import type { TextDocuments, Connection, Range } from 'vscode-languageserver'
import { NotificationEmitter } from '../../lib/emitter'
export type ClassNamesTree = { export type ClassNamesTree = {
[key: string]: 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 | { export type State = null | {
enabled: boolean enabled: boolean
emitter: NotificationEmitter emitter?: NotificationEmitter
version?: string version?: string
configPath?: string configPath?: string
config?: any config?: any

View File

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