add lsp boilerplate
|
@ -1,5 +1,6 @@
|
|||
out
|
||||
dist
|
||||
node_modules
|
||||
.vscode-test/
|
||||
.vsix
|
||||
.DS_Store
|
||||
.rts2_cache_cjs
|
||||
|
|
|
@ -1,28 +1,15 @@
|
|||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
"outFiles": [ "${workspaceRoot}/out/**/*.js" ],
|
||||
"preLaunchTask": "npm: watch"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
"outFiles": [ "${workspaceRoot}/out/test/**/*.js" ],
|
||||
"preLaunchTask": "npm: watch"
|
||||
}
|
||||
]
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceRoot}"],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
"outFiles": ["${workspaceRoot}/dist/**/*.js"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
21
CHANGELOG.md
|
@ -1,21 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
## 0.1.16
|
||||
|
||||
- add support for [EEx templates](https://hexdocs.pm/phoenix/templates.html), via [vscode-elixir](https://marketplace.visualstudio.com/items?itemName=mjmcloug.vscode-elixir) – thanks [@dhc02](https://github.com/dhc02)
|
||||
|
||||
## 0.1.15
|
||||
|
||||
- add support for [leaf](https://github.com/vapor/leaf) files (#16)
|
||||
|
||||
## 0.1.10
|
||||
|
||||
- add syntax definitions for `@apply` and `config()`:
|
||||
|
||||
**Before:**
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/2615508/44740655-ed02ee80-aaf2-11e8-8d3e-1075e0801fd7.png" alt="Syntax highlighting before update" width="345" />
|
||||
|
||||
**After:**
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/2615508/44740606-cba20280-aaf2-11e8-92b8-42adbfe54c61.png" alt="Syntax highlighting after update" width="345" />
|
50
README.md
|
@ -1,50 +0,0 @@
|
|||
# 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)**
|
||||
|
||||
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/img/html.gif" alt="HTML autocompletion" width="750">
|
||||
|
||||
## 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()`](#suggestions-when-using-apply-and-config)
|
||||
- Suggestions when using the `@screen` directive
|
||||
- [Improves syntax highlighting when using `@apply` and `config()`](#improves-syntax-highlighting-when-using-apply-and-config)
|
||||
|
||||
## Examples
|
||||
|
||||
#### Class name suggestions, including support for Emmet syntax
|
||||
|
||||
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/img/html.gif" alt="HTML autocompletion" width="750">
|
||||
|
||||
#### CSS preview when hovering over class names
|
||||
|
||||
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/img/html-hover.gif" alt="HTML hover preview" width="750">
|
||||
|
||||
#### Suggestions when using `@apply` and `config()`
|
||||
|
||||
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/img/css.gif" alt="CSS autocompletion" width="750">
|
||||
|
||||
#### Improves syntax highlighting when using `@apply` and `config()`
|
||||
|
||||
Before:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/img/css-highlighting-before.png" alt="CSS syntax highlighting before" width="400">
|
||||
|
||||
After:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/bradlc/vscode-tailwindcss/master/img/css-highlighting-after.png" alt="CSS syntax highlighting after" width="400">
|
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 32 KiB |
BIN
img/css.gif
Before Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 592 KiB |
BIN
img/html.gif
Before Width: | Height: | Size: 2.8 MiB |
Before Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 1.3 MiB |
30
package.json
|
@ -5,7 +5,7 @@
|
|||
"version": "0.1.16",
|
||||
"publisher": "bradlc",
|
||||
"engines": {
|
||||
"vscode": "^1.20.0"
|
||||
"vscode": "^1.23.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
|
@ -25,7 +25,8 @@
|
|||
"activationEvents": [
|
||||
"workspaceContains:**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"source": "./src/extension.ts",
|
||||
"main": "./dist/extension.js",
|
||||
"contributes": {
|
||||
"grammars": [
|
||||
{
|
||||
|
@ -42,21 +43,11 @@
|
|||
},
|
||||
"preview": true,
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"vscode:prepublish": "npm run build",
|
||||
"build": "microbundle -f cjs --external vscode,child_process,os,crypto,net",
|
||||
"watch": "npm run build -- --watch",
|
||||
"postinstall": "node ./node_modules/vscode/bin/install",
|
||||
"test": "npm run compile && node ./node_modules/vscode/bin/test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^7.0.43",
|
||||
"typescript": "^2.6.1",
|
||||
"vscode": "^1.1.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"color": "^3.0.0",
|
||||
"dlv": "^1.1.1",
|
||||
"tailwind-class-names": "0.6.0"
|
||||
"test": "npm run build && node ./node_modules/vscode/bin/test"
|
||||
},
|
||||
"author": "Brad Cornes <bradlc41@gmail.com>",
|
||||
"license": "MIT",
|
||||
|
@ -68,5 +59,12 @@
|
|||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bradlc/vscode-tailwindcss.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"microbundle": "^0.8.3",
|
||||
"vscode": "^1.1.26"
|
||||
}
|
||||
}
|
||||
|
|
952
src/extension.ts
|
@ -1,829 +1,139 @@
|
|||
'use strict'
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import {
|
||||
workspace as Workspace,
|
||||
window as Window,
|
||||
ExtensionContext,
|
||||
TextDocument,
|
||||
OutputChannel,
|
||||
WorkspaceFolder,
|
||||
Uri
|
||||
} from 'vscode'
|
||||
|
||||
import * as vscode from 'vscode'
|
||||
import { dirname } from 'path'
|
||||
const htmlElements = require('./htmlElements.js')
|
||||
// const tailwindClassNames = require('tailwind-class-names')
|
||||
const tailwindClassNames = require('/Users/brad/Code/tailwind-class-names/dist')
|
||||
const dlv = require('dlv')
|
||||
const Color = require('color')
|
||||
import {
|
||||
LanguageClient,
|
||||
LanguageClientOptions,
|
||||
TransportKind
|
||||
} from 'vscode-languageclient'
|
||||
|
||||
const CONFIG_GLOB =
|
||||
'**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js'
|
||||
const JS_TYPES = ['typescriptreact', 'javascript', 'javascriptreact']
|
||||
const HTML_TYPES = [
|
||||
'html',
|
||||
'jade',
|
||||
'razor',
|
||||
'php',
|
||||
'blade',
|
||||
'twig',
|
||||
'markdown',
|
||||
'erb',
|
||||
'handlebars',
|
||||
'ejs',
|
||||
'nunjucks',
|
||||
'haml',
|
||||
'leaf',
|
||||
'HTML (Eex)'
|
||||
]
|
||||
const CSS_TYPES = ['css', 'sass', 'scss', 'less', 'stylus']
|
||||
let LANGUAGES: string[] = ['html']
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
let tw
|
||||
let defaultClient: LanguageClient
|
||||
let clients: Map<string, LanguageClient> = new Map()
|
||||
|
||||
try {
|
||||
tw = await getTailwind()
|
||||
} catch (err) {}
|
||||
|
||||
let intellisense = new TailwindIntellisense(tw)
|
||||
context.subscriptions.push(intellisense)
|
||||
|
||||
let watcher = vscode.workspace.createFileSystemWatcher(CONFIG_GLOB)
|
||||
|
||||
watcher.onDidChange(onFileChange)
|
||||
watcher.onDidCreate(onFileChange)
|
||||
watcher.onDidDelete(onFileChange)
|
||||
|
||||
async function onFileChange(event) {
|
||||
try {
|
||||
tw = await getTailwind()
|
||||
} catch (err) {
|
||||
intellisense.dispose()
|
||||
return
|
||||
}
|
||||
|
||||
if (!tw) {
|
||||
intellisense.dispose()
|
||||
return
|
||||
}
|
||||
|
||||
intellisense.reload(tw)
|
||||
}
|
||||
}
|
||||
|
||||
async function getTailwind() {
|
||||
if (!vscode.workspace.name) return
|
||||
|
||||
let files = await vscode.workspace.findFiles(
|
||||
CONFIG_GLOB,
|
||||
'**/node_modules/**',
|
||||
1
|
||||
)
|
||||
|
||||
if (!files.length) return null
|
||||
|
||||
let configPath = files[0].fsPath
|
||||
delete require.cache[configPath]
|
||||
|
||||
let tailwindPackage = await vscode.workspace.findFiles(
|
||||
'**/node_modules/tailwindcss/package.json',
|
||||
null,
|
||||
1
|
||||
)
|
||||
|
||||
if (!tailwindPackage.length) return null
|
||||
|
||||
let pluginPath = dirname(tailwindPackage[0].fsPath)
|
||||
|
||||
let tw
|
||||
|
||||
try {
|
||||
tw = await tailwindClassNames({
|
||||
configPath,
|
||||
pluginPath,
|
||||
tree: true,
|
||||
strings: true
|
||||
})
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
|
||||
return tw
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
|
||||
function createCompletionItemProvider({
|
||||
items,
|
||||
prefixedItems,
|
||||
languages,
|
||||
regex,
|
||||
triggerCharacters,
|
||||
config,
|
||||
enable = () => true,
|
||||
emmet = false
|
||||
}: {
|
||||
items?
|
||||
prefixedItems?
|
||||
languages?: string[]
|
||||
regex?: RegExp
|
||||
triggerCharacters?: string[]
|
||||
config?
|
||||
prefix?: string
|
||||
enable?: (text: string) => boolean
|
||||
emmet?: boolean
|
||||
} = {}): vscode.Disposable {
|
||||
return vscode.languages.registerCompletionItemProvider(
|
||||
languages,
|
||||
{
|
||||
provideCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
): vscode.CompletionItem[] {
|
||||
const separator = config.options.separator || ':'
|
||||
let str
|
||||
|
||||
const range: vscode.Range = new vscode.Range(
|
||||
new vscode.Position(0, 0),
|
||||
position
|
||||
)
|
||||
const text: string = document.getText(range)
|
||||
|
||||
if (!enable(text)) return []
|
||||
|
||||
let lines = text.split(/[\n\r]/)
|
||||
|
||||
let matches = lines
|
||||
.slice(-5)
|
||||
.join('\n')
|
||||
.match(regex)
|
||||
|
||||
if (matches) {
|
||||
let parts = matches[matches.length - 1].split(' ')
|
||||
str = parts[parts.length - 1]
|
||||
} else if (emmet) {
|
||||
// match emmet style syntax
|
||||
// e.g. .flex.items-center
|
||||
let currentLine = lines[lines.length - 1]
|
||||
let currentWord = currentLine.split(' ').pop()
|
||||
matches = currentWord.match(/^\.([^.()#>*^ \[\]=$@{}]*)$/)
|
||||
if (!matches) {
|
||||
matches = currentWord.match(
|
||||
new RegExp(
|
||||
`^([A-Z][a-zA-Z0-9]*|[a-z][a-z0-9]*-[a-z0-9-]+|${htmlElements
|
||||
.map(x => `${x}\\b`)
|
||||
.join('|')}).*?\\.([^.()#>*^ \\[\\]=$@{}]*)$`
|
||||
)
|
||||
)
|
||||
}
|
||||
if (matches) {
|
||||
let parts = matches[matches.length - 1].split('.')
|
||||
str = parts[parts.length - 1]
|
||||
}
|
||||
}
|
||||
|
||||
let screens = Object.keys(dlv(config, 'screens', {}))
|
||||
let states = ['hover', 'focus', 'active', 'group-hover']
|
||||
|
||||
if (typeof str !== 'undefined') {
|
||||
console.log(str)
|
||||
const pth = str
|
||||
.replace(
|
||||
new RegExp(
|
||||
`^(${[...screens, ...states].join('|')})${separator}`,
|
||||
'g'
|
||||
),
|
||||
'$1.'
|
||||
)
|
||||
.replace(new RegExp(`\\.(${states.join('|')})${separator}`), '.$1.')
|
||||
.replace(/\.$/, '')
|
||||
.replace(/^\./, '')
|
||||
.replace(/\./g, '.children.')
|
||||
|
||||
let hasSep = new RegExp(
|
||||
`^(${[...screens, ...states].join('|')})${separator}`
|
||||
).test(str)
|
||||
if (!hasSep && str.endsWith(separator)) {
|
||||
// token.cancel()
|
||||
return getItemsWithRange(
|
||||
items,
|
||||
new vscode.Range(position.translate(0, -str.length), position)
|
||||
)
|
||||
let mobNav = new vscode.CompletionItem(
|
||||
'mob-nav-',
|
||||
vscode.CompletionItemKind.Constant
|
||||
)
|
||||
mobNav.range = new vscode.Range(position.translate(0, -4), position)
|
||||
return [mobNav]
|
||||
}
|
||||
|
||||
if (pth !== '') {
|
||||
const itms =
|
||||
prefixedItems && str.indexOf('.') === 0 && !hasSep
|
||||
? dlv(prefixedItems, pth)
|
||||
: dlv(items, pth)
|
||||
if (itms) {
|
||||
return getItemsWithRange(itms.children)
|
||||
let _sortedWorkspaceFolders: string[] | undefined
|
||||
function sortedWorkspaceFolders(): string[] {
|
||||
if (_sortedWorkspaceFolders === void 0) {
|
||||
_sortedWorkspaceFolders = Workspace.workspaceFolders
|
||||
? Workspace.workspaceFolders
|
||||
.map(folder => {
|
||||
let result = folder.uri.toString()
|
||||
if (result.charAt(result.length - 1) !== '/') {
|
||||
result = result + '/'
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasSep) {
|
||||
return prefixedItems && str.indexOf('.') === 0
|
||||
? getItemsWithRange(prefixedItems)
|
||||
: getItemsWithRange(items)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
},
|
||||
...triggerCharacters
|
||||
)
|
||||
}
|
||||
|
||||
function getItemsWithRange(items, range: vscode.Range = undefined) {
|
||||
return Object.keys(items).map(x => {
|
||||
let i = items[x].item
|
||||
i.range = range
|
||||
return i
|
||||
})
|
||||
}
|
||||
|
||||
function createConfigItemProvider({
|
||||
languages,
|
||||
items,
|
||||
enable = () => true
|
||||
}: {
|
||||
languages?: string[]
|
||||
items?: vscode.CompletionItem[]
|
||||
enable?: (text: string) => boolean
|
||||
} = {}) {
|
||||
return vscode.languages.registerCompletionItemProvider(
|
||||
languages,
|
||||
{
|
||||
provideCompletionItems: (
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
): vscode.CompletionItem[] => {
|
||||
const range: vscode.Range = new vscode.Range(
|
||||
new vscode.Position(0, 0),
|
||||
position
|
||||
)
|
||||
const text: string = document.getText(range)
|
||||
|
||||
if (!enable(text)) return []
|
||||
|
||||
let lines = text.split(/[\n\r]/)
|
||||
|
||||
let matches = lines
|
||||
.slice(-5)
|
||||
.join('\n')
|
||||
.match(/config\(["']([^"']*)$/)
|
||||
|
||||
if (!matches) return []
|
||||
|
||||
let objPath =
|
||||
matches[1]
|
||||
.replace(/\.[^.]*$/, '')
|
||||
.replace('.', '.children.')
|
||||
.trim() + '.children'
|
||||
let foo = dlv(items, objPath)
|
||||
|
||||
if (foo) {
|
||||
return Object.keys(foo).map(x => foo[x].item)
|
||||
}
|
||||
|
||||
return Object.keys(items).map(x => items[x].item)
|
||||
}
|
||||
},
|
||||
"'",
|
||||
'"',
|
||||
'.'
|
||||
)
|
||||
}
|
||||
|
||||
function prefixItems(items, str, prefix) {
|
||||
const addPrefix =
|
||||
typeof prefix !== 'undefined' && prefix !== '' && str === prefix
|
||||
|
||||
return Object.keys(items).map(x => {
|
||||
const item = items[x].item
|
||||
if (addPrefix) {
|
||||
item.filterText = item.insertText = `${prefix}${item.label}`
|
||||
} else {
|
||||
item.filterText = item.insertText = item.label
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
function depthOf(obj) {
|
||||
if (typeof obj !== 'object' || Array.isArray(obj)) return 0
|
||||
|
||||
let level = 1
|
||||
|
||||
for (let key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) continue
|
||||
|
||||
if (typeof obj[key] === 'object') {
|
||||
const depth = depthOf(obj[key]) + 1
|
||||
level = Math.max(depth, level)
|
||||
}
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
function createItems(classNames, separator, config, prefix = '', parent = '') {
|
||||
let items = {}
|
||||
let i = 0
|
||||
|
||||
Object.keys(classNames).forEach(key => {
|
||||
if (depthOf(classNames[key]) === 0) {
|
||||
const item = new vscode.CompletionItem(
|
||||
key,
|
||||
vscode.CompletionItemKind.Constant
|
||||
)
|
||||
item.filterText = item.insertText = `${prefix}${key}`
|
||||
item.sortText = naturalExpand(i.toString())
|
||||
if (key !== 'container' && key !== 'group') {
|
||||
if (parent) {
|
||||
item.detail = classNames[key].replace(
|
||||
new RegExp(`:${parent} \{(.*?)\}`),
|
||||
'$1'
|
||||
)
|
||||
} else {
|
||||
item.detail = classNames[key]
|
||||
}
|
||||
|
||||
let color = getColorFromDecl(item.detail)
|
||||
if (color) {
|
||||
item.kind = vscode.CompletionItemKind.Color
|
||||
item.documentation = color
|
||||
}
|
||||
}
|
||||
items[key] = {
|
||||
item
|
||||
}
|
||||
i++
|
||||
} else {
|
||||
const item = new vscode.CompletionItem(
|
||||
`${key}${separator}`,
|
||||
vscode.CompletionItemKind.Constant
|
||||
)
|
||||
item.filterText = item.insertText = `${prefix}${key}${separator}`
|
||||
item.sortText = naturalExpand(i.toString())
|
||||
item.command = { title: '', command: 'editor.action.triggerSuggest' }
|
||||
if (key === 'hover' || key === 'focus' || key === 'active') {
|
||||
item.detail = `:${key}`
|
||||
item.sortText = `a${item.sortText}`
|
||||
} else if (key === 'group-hover') {
|
||||
item.detail = '.group:hover &'
|
||||
item.sortText = `a${item.sortText}`
|
||||
} else if (
|
||||
config.screens &&
|
||||
Object.keys(config.screens).indexOf(key) !== -1
|
||||
) {
|
||||
item.detail = `@media (min-width: ${config.screens[key]})`
|
||||
item.sortText = `m${item.sortText}`
|
||||
}
|
||||
items[key] = {
|
||||
item,
|
||||
children: createItems(classNames[key], separator, config, prefix, key)
|
||||
}
|
||||
i++
|
||||
}
|
||||
})
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
function createConfigItems(config, prefix = '') {
|
||||
let items = {}
|
||||
let i = 0
|
||||
|
||||
Object.keys(config).forEach(key => {
|
||||
let item = new vscode.CompletionItem(
|
||||
key,
|
||||
vscode.CompletionItemKind.Constant
|
||||
)
|
||||
|
||||
if (depthOf(config[key]) === 0) {
|
||||
if (key === 'plugins') return
|
||||
|
||||
item.filterText = item.insertText = `${prefix}${key}`
|
||||
item.sortText = naturalExpand(i.toString())
|
||||
if (typeof config[key] === 'string' || typeof config[key] === 'number') {
|
||||
item.detail = config[key].toString()
|
||||
|
||||
let color = getColorFromValue(item.detail)
|
||||
if (color) {
|
||||
item.kind = vscode.CompletionItemKind.Color
|
||||
item.documentation = color
|
||||
}
|
||||
} else if (Array.isArray(config[key])) {
|
||||
item.detail = stringifyArray(config[key])
|
||||
}
|
||||
items[key] = { item }
|
||||
} else {
|
||||
if (key === 'modules' || key === 'options') return
|
||||
|
||||
item.filterText = item.insertText = `${key}.`
|
||||
item.sortText = naturalExpand(i.toString())
|
||||
item.command = { title: '', command: 'editor.action.triggerSuggest' }
|
||||
items[key] = { item, children: createConfigItems(config[key], prefix) }
|
||||
}
|
||||
|
||||
i++
|
||||
})
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
class TailwindIntellisense {
|
||||
private _providers: vscode.Disposable[]
|
||||
private _disposable: vscode.Disposable
|
||||
private _tailwind
|
||||
private _items
|
||||
private _prefixedItems
|
||||
private _configItems
|
||||
private _prefixedConfigItems
|
||||
|
||||
constructor(tailwind) {
|
||||
if (tailwind) {
|
||||
this._tailwind = tailwind
|
||||
this.reload(tailwind)
|
||||
}
|
||||
}
|
||||
|
||||
public reload(tailwind) {
|
||||
this.dispose()
|
||||
|
||||
const separator = dlv(tailwind.config, 'options.separator', ':')
|
||||
|
||||
// if (separator !== ':') return
|
||||
|
||||
this._items = createItems(tailwind.classNames, separator, tailwind.config)
|
||||
this._prefixedItems = createItems(
|
||||
tailwind.classNames,
|
||||
separator,
|
||||
tailwind.config,
|
||||
'.'
|
||||
)
|
||||
this._configItems = createConfigItems(tailwind.config)
|
||||
this._prefixedConfigItems = createConfigItems(tailwind.config, '.')
|
||||
|
||||
this._providers = []
|
||||
|
||||
this._providers.push(
|
||||
createCompletionItemProvider({
|
||||
items: this._items,
|
||||
languages: JS_TYPES,
|
||||
regex: /\btw`([^`]*)$/,
|
||||
triggerCharacters: ['`', ' ', separator],
|
||||
config: tailwind.config
|
||||
})
|
||||
)
|
||||
|
||||
this._providers.push(
|
||||
createCompletionItemProvider({
|
||||
items: this._items,
|
||||
prefixedItems: this._prefixedItems,
|
||||
languages: CSS_TYPES,
|
||||
regex: /@apply ([^;}]*)$/,
|
||||
triggerCharacters: ['.', separator],
|
||||
config: tailwind.config
|
||||
})
|
||||
)
|
||||
this._providers.push(
|
||||
createCompletionItemProvider({
|
||||
items: this._items,
|
||||
prefixedItems: this._items,
|
||||
languages: ['postcss'],
|
||||
regex: /@apply ([^;}]*)$/,
|
||||
triggerCharacters: ['.', separator],
|
||||
config: tailwind.config
|
||||
})
|
||||
)
|
||||
|
||||
this._providers.push(
|
||||
createCompletionItemProvider({
|
||||
items: this._items,
|
||||
languages: HTML_TYPES,
|
||||
regex: /\bclass=["']([^"']*)$/, // /\bclass(Name)?=(["'])(?!.*?\2)/
|
||||
triggerCharacters: ["'", '"', ' ', '.', separator],
|
||||
config: tailwind.config,
|
||||
emmet: true
|
||||
})
|
||||
)
|
||||
|
||||
this._providers.push(
|
||||
createCompletionItemProvider({
|
||||
items: this._items,
|
||||
languages: JS_TYPES,
|
||||
regex: /\bclass(Name)?=["']([^"']*)$/, // /\bclass(Name)?=(["'])(?!.*?\2)/
|
||||
triggerCharacters: ["'", '"', ' ', separator]
|
||||
.concat([
|
||||
Object.keys(
|
||||
vscode.workspace.getConfiguration('emmet.includeLanguages')
|
||||
).indexOf('javascript') !== -1 && '.'
|
||||
])
|
||||
.filter(Boolean),
|
||||
config: tailwind.config,
|
||||
emmet:
|
||||
Object.keys(
|
||||
vscode.workspace.getConfiguration('emmet.includeLanguages')
|
||||
).indexOf('javascript') !== -1
|
||||
})
|
||||
)
|
||||
|
||||
// Vue.js
|
||||
this._providers.push(
|
||||
createCompletionItemProvider({
|
||||
items: this._items,
|
||||
languages: ['vue'],
|
||||
regex: /\bclass=["']([^"']*)$/,
|
||||
enable: isWithinTemplate,
|
||||
triggerCharacters: ["'", '"', ' ', separator]
|
||||
.concat([
|
||||
Object.keys(
|
||||
vscode.workspace.getConfiguration('emmet.includeLanguages')
|
||||
).indexOf('vue-html') !== -1 && '.'
|
||||
])
|
||||
.filter(Boolean),
|
||||
config: tailwind.config,
|
||||
emmet:
|
||||
Object.keys(
|
||||
vscode.workspace.getConfiguration('emmet.includeLanguages')
|
||||
).indexOf('vue-html') !== -1
|
||||
})
|
||||
)
|
||||
this._providers.push(
|
||||
createCompletionItemProvider({
|
||||
items: this._items,
|
||||
languages: ['vue'],
|
||||
regex: /\bclass=["']([^"']*)$/,
|
||||
enable: text => {
|
||||
if (
|
||||
text.indexOf('<script') !== -1 &&
|
||||
text.indexOf('</script>') === -1
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
triggerCharacters: ["'", '"', ' ', separator],
|
||||
config: tailwind.config
|
||||
})
|
||||
)
|
||||
this._providers.push(
|
||||
createCompletionItemProvider({
|
||||
items: this._items,
|
||||
languages: ['vue'],
|
||||
regex: /@apply ([^;}]*)$/,
|
||||
triggerCharacters: ['.', separator],
|
||||
config: tailwind.config,
|
||||
enable: text => {
|
||||
if (
|
||||
text.indexOf('<style') !== -1 &&
|
||||
text.indexOf('</style>') === -1
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
this._providers.push(
|
||||
createConfigItemProvider({
|
||||
languages: CSS_TYPES,
|
||||
items: this._prefixedConfigItems
|
||||
})
|
||||
)
|
||||
this._providers.push(
|
||||
createConfigItemProvider({
|
||||
languages: ['postcss'],
|
||||
items: this._configItems
|
||||
})
|
||||
)
|
||||
|
||||
this._providers.push(
|
||||
createConfigItemProvider({
|
||||
languages: ['vue'],
|
||||
items: this._configItems,
|
||||
enable: text => {
|
||||
if (
|
||||
text.indexOf('<style') !== -1 &&
|
||||
text.indexOf('</style>') === -1
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
this._providers.push(
|
||||
vscode.languages.registerHoverProvider(
|
||||
[...HTML_TYPES, ...JS_TYPES, 'vue'],
|
||||
{
|
||||
provideHover: (document, position, token) => {
|
||||
const range1: vscode.Range = new vscode.Range(
|
||||
new vscode.Position(Math.max(position.line - 5, 0), 0),
|
||||
position
|
||||
)
|
||||
const text1: string = document.getText(range1)
|
||||
|
||||
if (!/\bclass(Name)?=['"][^'"]*$/.test(text1)) return
|
||||
|
||||
const range2: vscode.Range = new vscode.Range(
|
||||
new vscode.Position(Math.max(position.line - 5, 0), 0),
|
||||
position.with({ line: position.line + 1 })
|
||||
)
|
||||
const text2: string = document.getText(range2)
|
||||
|
||||
let str = text1 + text2.substr(text1.length).match(/^([^"' ]*)/)[0]
|
||||
let matches = str.match(/\bclass(Name)?=["']([^"']*)$/)
|
||||
|
||||
if (matches && matches[2]) {
|
||||
let className = matches[2].split(' ').pop()
|
||||
let parts = className.split(':')
|
||||
|
||||
if (typeof dlv(this._tailwind.classNames, parts) === 'string') {
|
||||
let base = parts.pop()
|
||||
let selector = `.${escapeClassName(className)}`
|
||||
|
||||
if (parts.indexOf('hover') !== -1) {
|
||||
selector += ':hover'
|
||||
} else if (parts.indexOf('focus') !== -1) {
|
||||
selector += ':focus'
|
||||
} else if (parts.indexOf('active') !== -1) {
|
||||
selector += ':active'
|
||||
} else if (parts.indexOf('group-hover') !== -1) {
|
||||
selector = `.group:hover ${selector}`
|
||||
}
|
||||
|
||||
let hoverStr = new vscode.MarkdownString()
|
||||
let css = this._tailwind.classNames[base]
|
||||
let m = css.match(/^(::?[a-z-]+) {(.*?)}/)
|
||||
if (m) {
|
||||
selector += m[1]
|
||||
css = m[2].trim()
|
||||
}
|
||||
css = css.replace(/([;{]) /g, '$1\n').replace(/^/gm, ' ')
|
||||
let code = `${selector} {\n${css}\n}`
|
||||
let screens = dlv(this._tailwind.config, 'screens', {})
|
||||
|
||||
Object.keys(screens).some(screen => {
|
||||
if (parts.indexOf(screen) !== -1) {
|
||||
code = `@media (min-width: ${
|
||||
screens[screen]
|
||||
}) {\n${code.replace(/^/gm, ' ')}\n}`
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
hoverStr.appendCodeblock(code, 'css')
|
||||
|
||||
let hoverRange = new vscode.Range(
|
||||
new vscode.Position(
|
||||
position.line,
|
||||
position.character +
|
||||
str.length -
|
||||
text1.length -
|
||||
className.length
|
||||
),
|
||||
new vscode.Position(
|
||||
position.line,
|
||||
position.character + str.length - text1.length
|
||||
)
|
||||
)
|
||||
|
||||
return new vscode.Hover(hoverStr, hoverRange)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// @screen
|
||||
this._providers.push(
|
||||
createScreenCompletionItemProvider({
|
||||
config: tailwind.config,
|
||||
languages: [...CSS_TYPES, 'postcss', 'vue']
|
||||
})
|
||||
)
|
||||
|
||||
this._disposable = vscode.Disposable.from(...this._providers)
|
||||
}
|
||||
|
||||
dispose() {
|
||||
if (this._disposable) {
|
||||
this._disposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pad(n) {
|
||||
return ('00000000' + n).substr(-8)
|
||||
}
|
||||
|
||||
function naturalExpand(a: string) {
|
||||
return a.replace(/\d+/g, pad)
|
||||
}
|
||||
|
||||
function stringifyArray(arr: Array<any>): string {
|
||||
return arr
|
||||
.reduce((acc, curr) => {
|
||||
let str = curr.toString()
|
||||
if (str.includes(' ')) {
|
||||
acc.push(`"${str.replace(/\s\s+/g, ' ')}"`)
|
||||
} else {
|
||||
acc.push(str)
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
.join(', ')
|
||||
}
|
||||
|
||||
function escapeClassName(className) {
|
||||
return className.replace(/([^A-Za-z0-9\-])/g, '\\$1')
|
||||
}
|
||||
|
||||
function getColorFromDecl(cssStr: string) {
|
||||
let matches = cssStr.match(/: ([^;]+);/g)
|
||||
if (matches === null || matches.length > 1) return
|
||||
|
||||
let color = matches[0].slice(2, -1).trim()
|
||||
|
||||
return getColorFromValue(color)
|
||||
}
|
||||
|
||||
function getColorFromValue(value: string) {
|
||||
if (value === 'transparent') {
|
||||
return 'rgba(0, 0, 0, 0.01)'
|
||||
}
|
||||
|
||||
try {
|
||||
let parsed = Color(value)
|
||||
if (parsed.valpha === 0) return 'rgba(0, 0, 0, 0.01)'
|
||||
return parsed.rgb().string()
|
||||
} catch (err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function createScreenCompletionItemProvider({
|
||||
languages,
|
||||
config
|
||||
}): vscode.Disposable {
|
||||
return vscode.languages.registerCompletionItemProvider(
|
||||
languages,
|
||||
{
|
||||
provideCompletionItems: (
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
): vscode.CompletionItem[] => {
|
||||
let range: vscode.Range = new vscode.Range(
|
||||
new vscode.Position(0, 0),
|
||||
position
|
||||
)
|
||||
let text: string = document.getText(range)
|
||||
|
||||
if (
|
||||
document.languageId === 'vue' &&
|
||||
!(text.indexOf('<style') !== -1 && text.indexOf('</style>') === -1)
|
||||
)
|
||||
return []
|
||||
|
||||
let line = text.split(/[\n\r]/).pop()
|
||||
|
||||
if (/@screen $/.test(line)) {
|
||||
return Object.keys(dlv(config, 'screens', {})).map((screen, i) => {
|
||||
let item = new vscode.CompletionItem(
|
||||
screen,
|
||||
vscode.CompletionItemKind.Constant
|
||||
)
|
||||
item.insertText = new vscode.SnippetString(`${screen} {\n\t$0\n}`)
|
||||
item.detail =
|
||||
typeof config.screens[screen] === 'string'
|
||||
? config.screens[screen]
|
||||
: ''
|
||||
item.sortText = naturalExpand(i.toString())
|
||||
return item
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
},
|
||||
' '
|
||||
)
|
||||
}
|
||||
|
||||
function isWithinTemplate(text: string) {
|
||||
let regex = /(<\/?template\b)/g
|
||||
let match
|
||||
let d = 0
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
d += match[0] === '</template' ? -1 : 1
|
||||
.sort((a, b) => {
|
||||
return a.length - b.length
|
||||
})
|
||||
: []
|
||||
}
|
||||
return d !== 0
|
||||
return _sortedWorkspaceFolders
|
||||
}
|
||||
Workspace.onDidChangeWorkspaceFolders(
|
||||
() => (_sortedWorkspaceFolders = undefined)
|
||||
)
|
||||
|
||||
function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder {
|
||||
let sorted = sortedWorkspaceFolders()
|
||||
for (let element of sorted) {
|
||||
let uri = folder.uri.toString()
|
||||
if (uri.charAt(uri.length - 1) !== '/') {
|
||||
uri = uri + '/'
|
||||
}
|
||||
if (uri.startsWith(element)) {
|
||||
return Workspace.getWorkspaceFolder(Uri.parse(element))!
|
||||
}
|
||||
}
|
||||
return folder
|
||||
}
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
// let module = context.asAbsolutePath(path.join('server', 'out', 'server.js'))
|
||||
let module = '/Users/brad/Code/tailwindcss-language-server/dist/index.js'
|
||||
let outputChannel: OutputChannel = Window.createOutputChannel(
|
||||
'lsp-multi-server-example'
|
||||
)
|
||||
|
||||
function didOpenTextDocument(document: TextDocument): void {
|
||||
if (
|
||||
document.uri.scheme !== 'file' ||
|
||||
LANGUAGES.indexOf(document.languageId) === -1
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
let uri = document.uri
|
||||
let folder = Workspace.getWorkspaceFolder(uri)
|
||||
// Files outside a folder can't be handled. This might depend on the language.
|
||||
// Single file languages like JSON might handle files outside the workspace folders.
|
||||
if (!folder) {
|
||||
return
|
||||
}
|
||||
// If we have nested workspace folders we only start a server on the outer most workspace folder.
|
||||
folder = getOuterMostWorkspaceFolder(folder)
|
||||
|
||||
if (!clients.has(folder.uri.toString())) {
|
||||
let debugOptions = {
|
||||
execArgv: ['--nolazy', `--inspect=${6011 + clients.size}`]
|
||||
}
|
||||
let serverOptions = {
|
||||
run: { module, transport: TransportKind.ipc },
|
||||
debug: { module, transport: TransportKind.ipc, options: debugOptions }
|
||||
}
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
documentSelector: LANGUAGES.map(language => ({
|
||||
scheme: 'file',
|
||||
language,
|
||||
pattern: `${folder.uri.fsPath}/**/*`
|
||||
})),
|
||||
diagnosticCollectionName: 'lsp-multi-server-example',
|
||||
workspaceFolder: folder,
|
||||
outputChannel: outputChannel
|
||||
}
|
||||
let client = new LanguageClient(
|
||||
'lsp-multi-server-example',
|
||||
'LSP Multi Server Example',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
)
|
||||
client.start()
|
||||
clients.set(folder.uri.toString(), client)
|
||||
}
|
||||
}
|
||||
|
||||
Workspace.onDidOpenTextDocument(didOpenTextDocument)
|
||||
Workspace.textDocuments.forEach(didOpenTextDocument)
|
||||
Workspace.onDidChangeWorkspaceFolders(event => {
|
||||
for (let folder of event.removed) {
|
||||
let client = clients.get(folder.uri.toString())
|
||||
if (client) {
|
||||
clients.delete(folder.uri.toString())
|
||||
client.stop()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function deactivate(): Thenable<void> {
|
||||
let promises: Thenable<void>[] = []
|
||||
if (defaultClient) {
|
||||
promises.push(defaultClient.stop())
|
||||
}
|
||||
for (let client of clients.values()) {
|
||||
promises.push(client.stop())
|
||||
}
|
||||
return Promise.all(promises).then(() => undefined)
|
||||
}
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
module.exports = [
|
||||
'a',
|
||||
'abbr',
|
||||
'acronym',
|
||||
'address',
|
||||
'applet',
|
||||
'area',
|
||||
'article',
|
||||
'aside',
|
||||
'audio',
|
||||
'b',
|
||||
'base',
|
||||
'basefont',
|
||||
'bdi',
|
||||
'bdo',
|
||||
'bgsound',
|
||||
'big',
|
||||
'blink',
|
||||
'blockquote',
|
||||
'body',
|
||||
'br',
|
||||
'button',
|
||||
'canvas',
|
||||
'caption',
|
||||
'center',
|
||||
'cite',
|
||||
'code',
|
||||
'col',
|
||||
'colgroup',
|
||||
'command',
|
||||
'content',
|
||||
'data',
|
||||
'datalist',
|
||||
'dd',
|
||||
'del',
|
||||
'details',
|
||||
'dfn',
|
||||
'dialog',
|
||||
'dir',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'element',
|
||||
'em',
|
||||
'embed',
|
||||
'fieldset',
|
||||
'figcaption',
|
||||
'figure',
|
||||
'font',
|
||||
'footer',
|
||||
'form',
|
||||
'frame',
|
||||
'frameset',
|
||||
'h1',
|
||||
'head',
|
||||
'header',
|
||||
'hgroup',
|
||||
'hr',
|
||||
'html',
|
||||
'i',
|
||||
'iframe',
|
||||
'image',
|
||||
'img',
|
||||
'input',
|
||||
'ins',
|
||||
'isindex',
|
||||
'kbd',
|
||||
'keygen',
|
||||
'label',
|
||||
'legend',
|
||||
'li',
|
||||
'link',
|
||||
'listing',
|
||||
'main',
|
||||
'map',
|
||||
'mark',
|
||||
'marquee',
|
||||
'menu',
|
||||
'menuitem',
|
||||
'meta',
|
||||
'meter',
|
||||
'multicol',
|
||||
'nav',
|
||||
'nextid',
|
||||
'nobr',
|
||||
'noembed',
|
||||
'noframes',
|
||||
'noscript',
|
||||
'object',
|
||||
'ol',
|
||||
'optgroup',
|
||||
'option',
|
||||
'output',
|
||||
'p',
|
||||
'param',
|
||||
'picture',
|
||||
'plaintext',
|
||||
'pre',
|
||||
'progress',
|
||||
'q',
|
||||
'rb',
|
||||
'rp',
|
||||
'rt',
|
||||
'rtc',
|
||||
'ruby',
|
||||
's',
|
||||
'samp',
|
||||
'script',
|
||||
'section',
|
||||
'select',
|
||||
'shadow',
|
||||
'slot',
|
||||
'small',
|
||||
'source',
|
||||
'spacer',
|
||||
'span',
|
||||
'strike',
|
||||
'strong',
|
||||
'style',
|
||||
'sub',
|
||||
'summary',
|
||||
'sup',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'template',
|
||||
'textarea',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'time',
|
||||
'title',
|
||||
'tr',
|
||||
'track',
|
||||
'tt',
|
||||
'u',
|
||||
'ul',
|
||||
'var',
|
||||
'video',
|
||||
'wbr',
|
||||
'xmp'
|
||||
]
|
|
@ -1,75 +0,0 @@
|
|||
{
|
||||
"scopeName": "source.css.tailwind",
|
||||
"fileTypes": [],
|
||||
"injectionSelector": "meta.property-list.css, meta.property-list.scss",
|
||||
"name": "TailwindCSS",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "^\\s*(@)apply\\b",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "keyword.control.at-rule.apply.tailwind"
|
||||
},
|
||||
"1": {
|
||||
"name": "punctuation.definition.keyword.tailwind"
|
||||
}
|
||||
},
|
||||
"end": ";",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.terminator.rule.tailwind"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?x)\n(?=\n (?:\\|)? # Possible anonymous namespace prefix\n (?:\n [-\\[:.*\\#a-zA-Z_] # Valid selector character\n |\n [^\\x00-\\x7F] # Which can include non-ASCII symbols\n |\n \\\\ # Or an escape sequence\n (?:[0-9a-fA-F]{1,6}|.)\n )\n)",
|
||||
"end": "(?=\\s*[;])",
|
||||
"patterns": [
|
||||
{
|
||||
"match": "!\\s*important(?![\\w-])",
|
||||
"name": "keyword.other.important.tailwind"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.entity.tailwind"
|
||||
},
|
||||
"2": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "source.css#escapes"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"match": "(?x)\n(\\.)? # Valid class-name\n(\n (?: [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n )+\n) # Followed by either:\n(?= $ # - End of the line\n | [\\s,.\\#)\\[:{>;+~|] # - Another selector\n | /\\* # - A block comment\n)",
|
||||
"name": "entity.other.attribute-name.class.tailwind"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?i)(?<![\\w-])(config)(\\()",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "support.function.config.tailwind"
|
||||
},
|
||||
"2": {
|
||||
"name": "punctuation.section.function.begin.bracket.round.tailwind"
|
||||
}
|
||||
},
|
||||
"end": "\\)",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.section.function.end.bracket.round.tailwind"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "source.css#property-values"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
]
|
||||
}
|