initial commit

master
Brad Cornes 2018-04-19 21:18:23 +01:00
commit 6d1a8b2772
10 changed files with 3299 additions and 0 deletions

4
.gitignore vendored 100644
View File

@ -0,0 +1,4 @@
out
node_modules
.vscode-test/
.vsix

28
.vscode/launch.json vendored 100644
View File

@ -0,0 +1,28 @@
// 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"
}
]
}

9
.vscode/settings.json vendored 100644
View File

@ -0,0 +1,9 @@
// 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
}
}

20
.vscode/tasks.json vendored 100644
View File

@ -0,0 +1,20 @@
// 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
}
}
]
}

8
.vscodeignore 100644
View File

@ -0,0 +1,8 @@
.vscode/**
.vscode-test/**
out/test/**
out/**/*.map
src/**
.gitignore
tsconfig.json
vsc-extension-quickstart.md

1
README.md 100644
View File

@ -0,0 +1 @@
# vscode-tailwind

2953
package-lock.json generated 100644

File diff suppressed because it is too large Load Diff

32
package.json 100644
View File

@ -0,0 +1,32 @@
{
"name": "vscode-tailwind",
"displayName": "vscode-tailwind",
"description": "",
"version": "0.0.1",
"publisher": "bradlc",
"engines": {
"vscode": "^1.20.0"
},
"categories": ["Other"],
"activationEvents": [
"workspaceContains:**/{tailwind,tailwind.config,.tailwindrc}.js"
],
"main": "./out/extension",
"contributes": {},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"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": {
"dlv": "^1.1.1",
"tailwind-class-names": "^0.5.0"
}
}

228
src/extension.ts 100644
View File

@ -0,0 +1,228 @@
'use strict'
import * as vscode from 'vscode'
import { join } from 'path'
const tailwindClassNames = require('tailwind-class-names')
// const tailwindClassNames = require('/Users/brad/Code/tailwind-class-names/dist')
const dlv = require('dlv')
export async function activate(context: vscode.ExtensionContext) {
if (!vscode.workspace.name) return
const configFile = await vscode.workspace.findFiles(
'{tailwind,tailwind.config,.tailwindrc}.js',
'**/node_modules/**',
1
)
if (!configFile) return
const plugin = join(
vscode.workspace.workspaceFolders[0].uri.fsPath,
'node_modules',
'tailwindcss'
)
let tw
try {
tw = await tailwindClassNames(
configFile[0].fsPath,
{
tree: true,
strings: true
},
plugin
)
} catch (err) {
return
}
const separator = dlv(tw.config, 'options.separator', ':')
if (separator !== ':') return
const items = createItems(tw.classNames, separator, tw.config)
context.subscriptions.push(
createCompletionItemProvider(
items,
['typescriptreact', 'javascript', 'javascriptreact'],
/\btw`([^`]*)$/,
['`', ' ', separator],
tw.config
)
)
context.subscriptions.push(
createCompletionItemProvider(
items,
['css', 'sass', 'scss'],
/@apply ([^;}]*)$/,
['.', separator],
tw.config,
'.'
)
)
context.subscriptions.push(
createCompletionItemProvider(
items,
[
'html',
'jade',
'razor',
'php',
'blade',
'vue',
'twig',
'markdown',
'erb',
'handlebars',
'ejs',
// for jsx
'typescriptreact',
'javascript',
'javascriptreact'
],
/\bclass(Name)?=["']([^"']*)/, // /\bclass(Name)?=(["'])(?!.*?\2)/
["'", '"', ' ', separator],
tw.config
)
)
}
export function deactivate() {}
function createCompletionItemProvider(
items,
languages: string[],
regex: RegExp,
triggerCharacters: string[],
config,
prefix = ''
): vscode.Disposable {
return vscode.languages.registerCompletionItemProvider(
languages,
{
provideCompletionItems(
document: vscode.TextDocument,
position: vscode.Position
): vscode.CompletionItem[] {
const range: vscode.Range = new vscode.Range(
new vscode.Position(Math.max(position.line - 5, 0), 0),
position
)
const text: string = document.getText(range)
let p = prefix
const separator = config.options.separator || ':'
const matches = text.match(regex)
if (matches) {
const parts = matches[matches.length - 1].split(' ')
const str = parts[parts.length - 1]
const pth = str
.replace(new RegExp(`${separator}`, 'g'), '.')
.replace(/\.$/, '')
.replace(/^\./, '')
.replace(/\./g, '.children.')
if (pth !== '') {
const itms = dlv(items, pth)
if (itms) {
return prefixItems(itms.children, str, prefix)
}
}
return prefixItems(items, str, prefix)
}
return []
}
},
...triggerCharacters
)
}
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') 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, parent = '') {
let items = {}
Object.keys(classNames).forEach(key => {
if (depthOf(classNames[key]) === 0) {
const item = new vscode.CompletionItem(
key,
vscode.CompletionItemKind.Constant
)
if (key !== 'container' && key !== 'group') {
if (parent) {
item.detail = classNames[key].replace(
new RegExp(`:${parent} \{(.*?)\}`),
'$1'
)
} else {
item.detail = classNames[key]
}
}
items[key] = {
item
}
} else {
console.log(key)
const item = new vscode.CompletionItem(
`${key}${separator}`,
vscode.CompletionItemKind.Constant
)
item.command = { title: '', command: 'editor.action.triggerSuggest' }
if (key === 'hover' || key === 'focus' || key === 'active') {
item.detail = `:${key}`
} else if (key === 'group-hover') {
item.detail = '.group:hover &'
} else if (
config.screens &&
Object.keys(config.screens).indexOf(key) !== -1
) {
item.detail = `@media (min-width: ${config.screens[key]})`
}
items[key] = {
item,
children: createItems(classNames[key], separator, config, key)
}
}
})
return items
}

16
tsconfig.json 100644
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": "src"
},
"exclude": [
"node_modules",
".vscode-test"
]
}