Skip to content

Commit cc42400

Browse files
committed
improve emmet syntax handling (#3)
1 parent 3ed7a35 commit cc42400

File tree

2 files changed

+285
-85
lines changed

2 files changed

+285
-85
lines changed

src/extension.ts

Lines changed: 143 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use strict'
22

33
import * as vscode from 'vscode'
4-
import { join, dirname } from 'path'
4+
import { dirname } from 'path'
5+
const htmlElements = require('./htmlElements.js')
56
const tailwindClassNames = require('tailwind-class-names')
67
const dlv = require('dlv')
78
const Color = require('color')
@@ -21,9 +22,7 @@ const HTML_TYPES = [
2122
'handlebars',
2223
'ejs',
2324
'nunjucks',
24-
'haml',
25-
// for jsx
26-
...JS_TYPES
25+
'haml'
2726
]
2827
const CSS_TYPES = ['css', 'sass', 'scss', 'less', 'postcss', 'stylus']
2928

@@ -147,7 +146,17 @@ function createCompletionItemProvider({
147146
// match emmet style syntax
148147
// e.g. .flex.items-center
149148
let currentLine = lines[lines.length - 1]
150-
matches = currentLine.match(/\.([^()#>*^ \[\]=$@{}]*)$/i)
149+
let currentWord = currentLine.split(' ').pop()
150+
matches = currentWord.match(/^\.([^.()#>*^ \[\]=$@{}]*)$/)
151+
if (!matches) {
152+
matches = currentWord.match(
153+
new RegExp(
154+
`^([A-Z][a-zA-Z0-9]*|[a-z][a-z0-9]*-[a-z0-9-]+|${htmlElements.join(
155+
'|'
156+
)}).*?\\.([^.()#>*^ \\[\\]=$@{}]*)$`
157+
)
158+
)
159+
}
151160
let parts = matches[matches.length - 1].split('.')
152161
str = parts[parts.length - 1]
153162
}
@@ -422,32 +431,78 @@ class TailwindIntellisense {
422431
createCompletionItemProvider({
423432
items: this._items,
424433
languages: HTML_TYPES,
425-
regex: /\bclass(Name)?=["']([^"']*)$/, // /\bclass(Name)?=(["'])(?!.*?\2)/
434+
regex: /\bclass=["']([^"']*)$/, // /\bclass(Name)?=(["'])(?!.*?\2)/
426435
triggerCharacters: ["'", '"', ' ', '.', separator],
427436
config: tailwind.config,
428437
emmet: true
429438
})
430439
)
431440

441+
this._providers.push(
442+
createCompletionItemProvider({
443+
items: this._items,
444+
languages: JS_TYPES,
445+
regex: /\bclass(Name)?=["']([^"']*)$/, // /\bclass(Name)?=(["'])(?!.*?\2)/
446+
triggerCharacters: ["'", '"', ' ', separator]
447+
.concat([
448+
Object.keys(
449+
vscode.workspace.getConfiguration('emmet.includeLanguages')
450+
).indexOf('javascript') !== -1 && '.'
451+
])
452+
.filter(Boolean),
453+
config: tailwind.config,
454+
emmet:
455+
Object.keys(
456+
vscode.workspace.getConfiguration('emmet.includeLanguages')
457+
).indexOf('javascript') !== -1
458+
})
459+
)
460+
432461
// Vue.js
433462
this._providers.push(
434463
createCompletionItemProvider({
435464
items: this._items,
436465
languages: ['vue'],
437-
regex: /\bclass(Name)?=["']([^"']*)$/,
466+
regex: /\bclass=["']([^"']*)$/,
438467
enable: text => {
439468
if (
440-
(text.indexOf('<template') !== -1 &&
441-
text.indexOf('</template>') === -1) ||
442-
(text.indexOf('<script') !== -1 && text.indexOf('</script>') === -1)
469+
text.indexOf('<template') !== -1 &&
470+
text.indexOf('</template>') === -1
443471
) {
444472
return true
445473
}
446474
return false
447475
},
448-
triggerCharacters: ["'", '"', ' ', '.', separator],
476+
triggerCharacters: ["'", '"', ' ', separator]
477+
.concat([
478+
Object.keys(
479+
vscode.workspace.getConfiguration('emmet.includeLanguages')
480+
).indexOf('vue-html') !== -1 && '.'
481+
])
482+
.filter(Boolean),
449483
config: tailwind.config,
450-
emmet: true
484+
emmet:
485+
Object.keys(
486+
vscode.workspace.getConfiguration('emmet.includeLanguages')
487+
).indexOf('vue-html') !== -1
488+
})
489+
)
490+
this._providers.push(
491+
createCompletionItemProvider({
492+
items: this._items,
493+
languages: ['vue'],
494+
regex: /\bclass=["']([^"']*)$/,
495+
enable: text => {
496+
if (
497+
text.indexOf('<script') !== -1 &&
498+
text.indexOf('</script>') === -1
499+
) {
500+
return true
501+
}
502+
return false
503+
},
504+
triggerCharacters: ["'", '"', ' ', separator],
505+
config: tailwind.config
451506
})
452507
)
453508
this._providers.push(
@@ -493,86 +548,89 @@ class TailwindIntellisense {
493548
)
494549

495550
this._providers.push(
496-
vscode.languages.registerHoverProvider(HTML_TYPES, {
497-
provideHover: (document, position, token) => {
498-
const range1: vscode.Range = new vscode.Range(
499-
new vscode.Position(Math.max(position.line - 5, 0), 0),
500-
position
501-
)
502-
const text1: string = document.getText(range1)
503-
504-
if (!/\bclass(Name)?=['"][^'"]*$/.test(text1)) return
505-
506-
const range2: vscode.Range = new vscode.Range(
507-
new vscode.Position(Math.max(position.line - 5, 0), 0),
508-
position.with({ line: position.line + 1 })
509-
)
510-
const text2: string = document.getText(range2)
511-
512-
let str = text1 + text2.substr(text1.length).match(/^([^"' ]*)/)[0]
513-
let matches = str.match(/\bclass(Name)?=["']([^"']*)$/)
514-
515-
if (matches && matches[2]) {
516-
let className = matches[2].split(' ').pop()
517-
let parts = className.split(':')
518-
519-
if (typeof dlv(this._tailwind.classNames, parts) === 'string') {
520-
let base = parts.pop()
521-
let selector = `.${escapeClassName(className)}`
522-
523-
if (parts.indexOf('hover') !== -1) {
524-
selector += ':hover'
525-
} else if (parts.indexOf('focus') !== -1) {
526-
selector += ':focus'
527-
} else if (parts.indexOf('active') !== -1) {
528-
selector += ':active'
529-
} else if (parts.indexOf('group-hover') !== -1) {
530-
selector = `.group:hover ${selector}`
531-
}
551+
vscode.languages.registerHoverProvider(
552+
[...HTML_TYPES, ...JS_TYPES, 'vue'],
553+
{
554+
provideHover: (document, position, token) => {
555+
const range1: vscode.Range = new vscode.Range(
556+
new vscode.Position(Math.max(position.line - 5, 0), 0),
557+
position
558+
)
559+
const text1: string = document.getText(range1)
560+
561+
if (!/\bclass(Name)?=['"][^'"]*$/.test(text1)) return
562+
563+
const range2: vscode.Range = new vscode.Range(
564+
new vscode.Position(Math.max(position.line - 5, 0), 0),
565+
position.with({ line: position.line + 1 })
566+
)
567+
const text2: string = document.getText(range2)
568+
569+
let str = text1 + text2.substr(text1.length).match(/^([^"' ]*)/)[0]
570+
let matches = str.match(/\bclass(Name)?=["']([^"']*)$/)
571+
572+
if (matches && matches[2]) {
573+
let className = matches[2].split(' ').pop()
574+
let parts = className.split(':')
575+
576+
if (typeof dlv(this._tailwind.classNames, parts) === 'string') {
577+
let base = parts.pop()
578+
let selector = `.${escapeClassName(className)}`
579+
580+
if (parts.indexOf('hover') !== -1) {
581+
selector += ':hover'
582+
} else if (parts.indexOf('focus') !== -1) {
583+
selector += ':focus'
584+
} else if (parts.indexOf('active') !== -1) {
585+
selector += ':active'
586+
} else if (parts.indexOf('group-hover') !== -1) {
587+
selector = `.group:hover ${selector}`
588+
}
532589

533-
let hoverStr = new vscode.MarkdownString()
534-
let css = this._tailwind.classNames[base]
535-
let m = css.match(/^(::?[a-z-]+) {(.*?)}/)
536-
if (m) {
537-
selector += m[1]
538-
css = m[2].trim()
539-
}
540-
css = css.replace(/([;{]) /g, '$1\n').replace(/^/gm, ' ')
541-
let code = `${selector} {\n${css}\n}`
542-
let screens = dlv(this._tailwind.config, 'screens', {})
543-
544-
Object.keys(screens).some(screen => {
545-
if (parts.indexOf(screen) !== -1) {
546-
code = `@media (min-width: ${
547-
screens[screen]
548-
}) {\n${code.replace(/^/gm, ' ')}\n}`
549-
return true
590+
let hoverStr = new vscode.MarkdownString()
591+
let css = this._tailwind.classNames[base]
592+
let m = css.match(/^(::?[a-z-]+) {(.*?)}/)
593+
if (m) {
594+
selector += m[1]
595+
css = m[2].trim()
550596
}
551-
return false
552-
})
553-
hoverStr.appendCodeblock(code, 'css')
554-
555-
let hoverRange = new vscode.Range(
556-
new vscode.Position(
557-
position.line,
558-
position.character +
559-
str.length -
560-
text1.length -
561-
className.length
562-
),
563-
new vscode.Position(
564-
position.line,
565-
position.character + str.length - text1.length
597+
css = css.replace(/([;{]) /g, '$1\n').replace(/^/gm, ' ')
598+
let code = `${selector} {\n${css}\n}`
599+
let screens = dlv(this._tailwind.config, 'screens', {})
600+
601+
Object.keys(screens).some(screen => {
602+
if (parts.indexOf(screen) !== -1) {
603+
code = `@media (min-width: ${
604+
screens[screen]
605+
}) {\n${code.replace(/^/gm, ' ')}\n}`
606+
return true
607+
}
608+
return false
609+
})
610+
hoverStr.appendCodeblock(code, 'css')
611+
612+
let hoverRange = new vscode.Range(
613+
new vscode.Position(
614+
position.line,
615+
position.character +
616+
str.length -
617+
text1.length -
618+
className.length
619+
),
620+
new vscode.Position(
621+
position.line,
622+
position.character + str.length - text1.length
623+
)
566624
)
567-
)
568625

569-
return new vscode.Hover(hoverStr, hoverRange)
626+
return new vscode.Hover(hoverStr, hoverRange)
627+
}
570628
}
571-
}
572629

573-
return null
630+
return null
631+
}
574632
}
575-
})
633+
)
576634
)
577635

578636
this._disposable = vscode.Disposable.from(...this._providers)

0 commit comments

Comments
 (0)