diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100755 index 0000000..9612267 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,37 @@ +{ + "version": "0.2.0", + // List of configurations. Add new configurations or edit existing ones. + "configurations": [ + { + "type": "extensionHost", + "request": "launch", + "name": "Launch Client", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}"], + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": ["${workspaceRoot}/dist/extension/**/*.js"], + // "preLaunchTask": "npm: dev" + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Server 6011", + "address": "localhost", + "protocol": "inspector", + "port": 6011, + "sourceMaps": true, + "outFiles": ["${workspaceRoot}/dist/server/**/*.js"] + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Server 6012", + "address": "localhost", + "protocol": "inspector", + "port": 6012, + "sourceMaps": true, + "outFiles": ["${workspaceRoot}/dist/server/**/*.js"] + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100755 index 0000000..4b2d95f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,29 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "build", + "group": "build", + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": ["$tsc"] + }, + { + "type": "npm", + "script": "dev", + "isBackground": true, + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": ["$tsc-watch"] + } + ] +} diff --git a/.vscodeignore b/.vscodeignore index 97c56c3..eef67fb 100755 --- a/.vscodeignore +++ b/.vscodeignore @@ -7,3 +7,4 @@ contributing.md node_modules/** src/** +tests/** diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f8bf3f3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,75 @@ +# Changelog + +## 0.3.0 + +### General + +- Added support for string values in Tailwind's `important` option (#96) +- Removed all unnecessary logs (#91) +- Added support for components in addition to utilities (#67) +- Added description to custom variant completion items where possible +- Config parsing errors are now displayed in the VS Code UI +- Class names from `@tailwind base` are now included (by default `@tailwind base` does not include any class names but plugins may contribute them) +- Color swatches can now be displayed for rules with multiple properties and/or colors with variable alpha (#113) +- Added `tailwindCSS.includeLanguages` setting: + ```json + { + "tailwindCSS.includeLanguages": { + "plaintext": "html" + } + } + ``` + 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`) + +### HTML + +- Added built-in support for `liquid`, `aspnetcorerazor`, `mustache`, `HTML (EEx)`, `html-eex`, `gohtml`, `GoHTML`, and `hbs` languages +- Added syntax definition to embedded stylesheets in HTML files + +### CSS + +- Added built-in support for `sugarss` language +- Added `theme` (and `config`) helper hovers +- Added `@apply` class name hovers +- Added directive completion items with links to documentation +- Added `@tailwind` completion items (`preflight`/`base`, `utilities`, `components`, `screens`) with links to documentation +- Helper completion items that contain the `.` character will now insert square brackets when selected +- `@apply` completion list now excludes class names that are not compatible +- Added CSS syntax highlighting in `.vue` files (#15) + +### JS(X) + +- Completions now trigger when using backticks (#50, #93): + ```js + const App = () =>
+ + **After:** + + Syntax highlighting after update diff --git a/README.md b/README.md new file mode 100644 index 0000000..27cc6e8 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# Tailwind CSS IntelliSense + +> [Tailwind CSS](https://tailwindcss.com/) class name completion for VS Code + +**[Get it from the VS Code Marketplace →](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)** + +HTML autocompletion + +## Requirements + +This extension requires: +- a `tailwind.config.js` file to be [present in your project folder](https://github.com/bradlc/vscode-tailwindcss/blob/master/package.json#L38). You can create it with `npx tailwind init`. +- `tailwindcss` to be installed (present in project `node_modules/`) + +## Features + +Tailwind CSS IntelliSense uses your projects Tailwind installation and configuration to provide suggestions as you type. + +It also includes features that improve the overall Tailwind experience, including improved syntax highlighting, and CSS previews. + +### HTML (including Vue, JSX, PHP etc.) + +- [Class name suggestions, including support for Emmet syntax](#class-name-suggestions-including-support-for-emmet-syntax) + - Suggestions include color previews where applicable, for example for text and background colors + - They also include a preview of the actual CSS for that class name +- [CSS preview when hovering over class names](#css-preview-when-hovering-over-class-names) + +### CSS + +- [Suggestions when using `@apply` and config helpers](#suggestions-when-using-apply-and-config) +- Suggestions when using the `@screen` directive +- Suggestions when using the `@variants` directive +- [Improves syntax highlighting when using `@apply` and config helpers](#improves-syntax-highlighting-when-using-apply-and-config-helpers) + +## Examples + +#### Class name suggestions, including support for Emmet syntax + +HTML autocompletion + +#### CSS preview when hovering over class names + +HTML hover preview + +#### Suggestions when using `@apply` and config helpers + +CSS autocompletion + +#### Improves syntax highlighting when using `@apply` and config helpers + +Before: + +CSS syntax highlighting before + +After: + +CSS syntax highlighting after + +## 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 +} +``` diff --git a/package-lock.json b/package-lock.json index f844afb..9f6cc9e 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-tailwindcss", - "version": "0.3.0-alpha.2", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4845,9 +4845,9 @@ }, "dependencies": { "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", "dev": true } } @@ -6896,9 +6896,9 @@ } }, "vsce": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.75.0.tgz", - "integrity": "sha512-qyAQTmolxKWc9bV1z0yBTSH4WEIWhDueBJMKB0GUFD6lM4MiaU1zJ9BtzekUORZu094YeNSKz0RmVVuxfqPq0g==", + "version": "1.76.1", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.76.1.tgz", + "integrity": "sha512-WNx6JzRywxAOuhVpjmrsI0eHMK0mCA0YKD8u++7sprmhwCHsoQIBpSf0vp6kVMHBmafknr1Z6K7IC5jIjsNL9Q==", "dev": true, "requires": { "azure-devops-node-api": "^7.2.0", diff --git a/package.json b/package.json index 0b515ca..456a3dc 100755 --- a/package.json +++ b/package.json @@ -5,10 +5,15 @@ "preview": true, "author": "Brad Cornes ", "license": "MIT", - "version": "0.3.0-alpha.2", + "version": "0.3.0", + "homepage": "https://github.com/bradlc/vscode-tailwindcss", + "bugs": { + "url": "https://github.com/bradlc/vscode-tailwindcss/issues", + "email": "hello@bradley.dev" + }, "repository": { "type": "git", - "url": "https://github.com/bradlc/" + "url": "https://github.com/bradlc/vscode-tailwindcss.git" }, "publisher": "bradlc", "keywords": [ @@ -55,11 +60,12 @@ "tailwindCSS.emmetCompletions": { "type": "boolean", "default": false, - "description": "" + "description": "Enable class name completions when using Emmet-style syntax, for example `div.bg-red-500.uppercase`." }, "tailwindCSS.includeLanguages": { "type": "object", - "default": {} + "default": {}, + "description": "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\"}`" } } } @@ -106,7 +112,7 @@ "tiny-invariant": "^1.1.0", "tslint": "^5.16.0", "typescript": "^3.8.3", - "vsce": "^1.75.0", + "vsce": "^1.76.1", "vscode-emmet-helper-bundled": "0.0.1", "vscode-languageclient": "^5.2.1", "vscode-languageserver": "^5.2.1", diff --git a/src/class-names/runPlugin.js b/src/class-names/runPlugin.js index dbdd2b5..118961e 100644 --- a/src/class-names/runPlugin.js +++ b/src/class-names/runPlugin.js @@ -17,6 +17,12 @@ export function runPlugin(plugin, params = {}) { theme: (path, defaultValue) => dlv(config, `theme.${path}`, defaultValue), variants: () => [], config: (path, defaultValue) => dlv(config, path, defaultValue), + corePlugins: (path) => { + if (Array.isArray(config.corePlugins)) { + return config.corePlugins.includes(path) + } + return dlv(config, `corePlugins.${path}`, true) + }, target: (path) => { if (typeof config.target === 'string') { return config.target === 'browserslist' diff --git a/src/lib/languages.ts b/src/lib/languages.ts index 777f588..0b9c450 100644 --- a/src/lib/languages.ts +++ b/src/lib/languages.ts @@ -6,6 +6,8 @@ export const DEFAULT_LANGUAGES = [ 'edge', 'ejs', 'erb', + 'gohtml', + 'GoHTML', 'haml', 'handlebars', 'hbs', diff --git a/src/lsp/util/find.ts b/src/lsp/util/find.ts index 800d0a3..6b1bfca 100644 --- a/src/lsp/util/find.ts +++ b/src/lsp/util/find.ts @@ -1,10 +1,10 @@ import { TextDocument, Range, Position } from 'vscode-languageserver' import { DocumentClassName, DocumentClassList, State } from './state' import lineColumn from 'line-column' -import { isCssContext } from './css' -import { isHtmlContext } from './html' +import { isCssContext, isCssDoc } from './css' +import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html' import { isWithinRange } from './isWithinRange' -import { isJsContext } from './js' +import { isJsContext, isJsDoc } from './js' import { getClassAttributeLexer } from './lexers' export function findAll(re: RegExp, str: string): RegExpMatchArray[] { @@ -83,11 +83,13 @@ export function findClassListsInCssRange( range: { start: { line: globalStart.line + start.line, - character: globalStart.character + start.character, + character: + (end.line === 0 ? globalStart.character : 0) + start.character, }, end: { line: globalStart.line + end.line, - character: globalStart.character + end.character, + character: + (end.line === 0 ? globalStart.character : 0) + end.character, }, }, } @@ -173,11 +175,14 @@ export function findClassListsInHtmlRange( range: { start: { line: range.start.line + start.line, - character: range.start.character + start.character, + character: + (end.line === 0 ? range.start.character : 0) + + start.character, }, end: { line: range.start.line + end.line, - character: range.start.character + end.character, + character: + (end.line === 0 ? range.start.character : 0) + end.character, }, }, } @@ -200,6 +205,80 @@ export function findClassListsInRange( return findClassListsInHtmlRange(doc, range) } +export function findClassListsInDocument( + state: State, + doc: TextDocument +): DocumentClassList[] { + if (isCssDoc(state, doc)) { + return findClassListsInCssRange(doc) + } + + if (isVueDoc(doc)) { + let text = doc.getText() + let blocks = findAll( + /<(?template|style|script)\b[^>]*>.*?(<\/\k>|$)/gis, + text + ) + let htmlRanges: Range[] = [] + let cssRanges: Range[] = [] + for (let i = 0; i < blocks.length; i++) { + let range = { + start: indexToPosition(text, blocks[i].index), + end: indexToPosition(text, blocks[i].index + blocks[i][0].length), + } + if (blocks[i].groups.type === 'style') { + cssRanges.push(range) + } else { + htmlRanges.push(range) + } + } + return [].concat.apply( + [], + [ + ...htmlRanges.map((range) => findClassListsInHtmlRange(doc, range)), + ...cssRanges.map((range) => findClassListsInCssRange(doc, range)), + ] + ) + } + + if (isHtmlDoc(state, doc) || isJsDoc(state, doc) || isSvelteDoc(doc)) { + let text = doc.getText() + let styleBlocks = findAll(/]*>|>).*?(<\/style>|$)/gis, text) + let htmlRanges: Range[] = [] + let cssRanges: Range[] = [] + let currentIndex = 0 + + for (let i = 0; i < styleBlocks.length; i++) { + htmlRanges.push({ + start: indexToPosition(text, currentIndex), + end: indexToPosition(text, styleBlocks[i].index), + }) + cssRanges.push({ + start: indexToPosition(text, styleBlocks[i].index), + end: indexToPosition( + text, + styleBlocks[i].index + styleBlocks[i][0].length + ), + }) + currentIndex = styleBlocks[i].index + styleBlocks[i][0].length + } + htmlRanges.push({ + start: indexToPosition(text, currentIndex), + end: indexToPosition(text, text.length), + }) + + return [].concat.apply( + [], + [ + ...htmlRanges.map((range) => findClassListsInHtmlRange(doc, range)), + ...cssRanges.map((range) => findClassListsInCssRange(doc, range)), + ] + ) + } + + return [] +} + function indexToPosition(str: string, index: number): Position { const { line, col } = lineColumn(str + '\n', index) return { line: line - 1, character: col - 1 } diff --git a/src/lsp/util/html.ts b/src/lsp/util/html.ts index 8808141..7f24b2e 100644 --- a/src/lsp/util/html.ts +++ b/src/lsp/util/html.ts @@ -8,6 +8,8 @@ export const HTML_LANGUAGES = [ 'edge', 'ejs', 'erb', + 'gohtml', + 'GoHTML', 'haml', 'handlebars', 'hbs',