Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
Merge pull request #5 from bradlc/vue Vue
Signature
-----BEGIN PGP SIGNATURE----- wsBcBAABCAAQBQJbgTZgCRBK7hj4Ov3rIwAAdHIIAEid6eqylkgX27jutjBuMnaP Nsn5u5iZhR43meq1DyhbLJLDrq2l3kd8lqPZ3PeqMnenBy6yr3qx+RUZqFzkq/EG 9vVdLz4PslZNhxbbTohCCyCiIBIJQ2AN/NsDOt0kmF311Msa8QqWA6F+4waTLDK4 NZ1iftKzKaNsGI03aYBYtrkCORmXL6l47KQ0Dp4qPKTjaGMB3ewZBxCcd8rPPzLY 1comhc3HE4CE3JyaY4GGE7NBf5gZ+0H6dhqJp/512xGLgy9YaJtiDFJZ+F7s0PQw HMg1fEI4V+JN3gHPDt0OnEGnn2ZeGW4fI2UUB9P5AnuyR4dGe2vv0qI5jZXFKCk= =tiVB -----END PGP SIGNATURE-----
Brad Cornes <bradlc41@gmail.com>
6 years ago
2 changed files, 436 additions(+), 141 deletions(-)
M src/extension.ts -> src/extension.ts
diff --git a/src/extension.ts b/src/extension.ts
index fd552df0330b3610650c27f8cfbc485a0fb6f18f..c4598ae87dd8ca51c151bfc2d884b4b6a6913040 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,7 +1,8 @@
 'use strict'
 
 import * as vscode from 'vscode'
-import { join, dirname } from 'path'
+import { dirname } from 'path'
+const htmlElements = require('./htmlElements.js')
 const tailwindClassNames = require('tailwind-class-names')
 const dlv = require('dlv')
 const Color = require('color')
@@ -15,16 +16,13 @@   'jade',
   'razor',
   'php',
   'blade',
-  'vue',
   'twig',
   'markdown',
   'erb',
   'handlebars',
   'ejs',
   'nunjucks',
-  'haml',
-  // for jsx
-  ...JS_TYPES
+  'haml'
 ]
 const CSS_TYPES = ['css', 'sass', 'scss', 'less', 'postcss', 'stylus']
 
@@ -97,14 +95,25 @@ }
 
 export function deactivate() {}
 
-function createCompletionItemProvider(
+function createCompletionItemProvider({
   items,
-  languages: string[],
-  regex: RegExp,
-  triggerCharacters: string[],
+  languages,
+  regex,
+  triggerCharacters,
   config,
-  prefix = ''
-): vscode.Disposable {
+  prefix = '',
+  enable = () => true,
+  emmet = false
+}: {
+  items?
+  languages?: string[]
+  regex?: RegExp
+  triggerCharacters?: string[]
+  config?
+  prefix?: string
+  enable?: (text: string) => boolean
+  emmet?: boolean
+} = {}): vscode.Disposable {
   return vscode.languages.registerCompletionItemProvider(
     languages,
     {
@@ -116,21 +125,38 @@         const separator = config.options.separator || ':'
         let str
 
         const range: vscode.Range = new vscode.Range(
-          new vscode.Position(Math.max(position.line - 5, 0), 0),
+          new vscode.Position(0, 0),
           position
         )
         const text: string = document.getText(range)
 
-        let matches = text.match(regex)
+        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 (languages.indexOf('html') !== -1) {
+        } else if (emmet) {
           // match emmet style syntax
           // e.g. .flex.items-center
-          let lineText = text.split('\n').pop()
-          matches = lineText.match(/\.([^()#>*^ \[\]=$@{}]*)$/i)
+          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.join(
+                  '|'
+                )}).*?\\.([^.()#>*^ \\[\\]=$@{}]*)$`
+              )
+            )
+          }
           let parts = matches[matches.length - 1].split('.')
           str = parts[parts.length - 1]
         }
@@ -163,6 +189,59 @@     ...triggerCharacters
   )
 }
 
+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
@@ -257,7 +336,7 @@
   return items
 }
 
-function createConfigItems(config) {
+function createConfigItems(config, prefix = '') {
   let items = {}
   let i = 0
 
@@ -270,7 +349,7 @@
     if (depthOf(config[key]) === 0) {
       if (key === 'plugins') return
 
-      item.filterText = item.insertText = `.${key}`
+      item.filterText = item.insertText = `${prefix}${key}`
       item.sortText = naturalExpand(i.toString())
       if (typeof config[key] === 'string' || typeof config[key] === 'number') {
         item.detail = config[key]
@@ -290,7 +369,7 @@
       item.filterText = item.insertText = `${key}.`
       item.sortText = naturalExpand(i.toString())
       item.command = { title: '', command: 'editor.action.triggerSuggest' }
-      items[key] = { item, children: createConfigItems(config[key]) }
+      items[key] = { item, children: createConfigItems(config[key], prefix) }
     }
 
     i++
@@ -305,6 +384,7 @@   private _disposable: vscode.Disposable
   private _tailwind
   private _items
   private _configItems
+  private _prefixedConfigItems
 
   constructor(tailwind) {
     if (tailwind) {
@@ -322,162 +402,235 @@     if (separator !== ':') return
 
     this._items = createItems(tailwind.classNames, separator, tailwind.config)
     this._configItems = createConfigItems(tailwind.config)
+    this._prefixedConfigItems = createConfigItems(tailwind.config, '.')
 
     this._providers = []
 
     this._providers.push(
-      createCompletionItemProvider(
-        this._items,
-        JS_TYPES,
-        /\btw`([^`]*)$/,
-        ['`', ' ', separator],
-        tailwind.config
-      )
+      createCompletionItemProvider({
+        items: this._items,
+        languages: JS_TYPES,
+        regex: /\btw`([^`]*)$/,
+        triggerCharacters: ['`', ' ', separator],
+        config: tailwind.config
+      })
     )
 
     this._providers.push(
-      createCompletionItemProvider(
-        this._items,
-        CSS_TYPES,
-        /@apply ([^;}]*)$/,
-        ['.', separator],
-        tailwind.config,
-        '.'
-      )
+      createCompletionItemProvider({
+        items: this._items,
+        languages: CSS_TYPES,
+        regex: /@apply ([^;}]*)$/,
+        triggerCharacters: ['.', separator],
+        config: tailwind.config,
+        prefix: '.'
+      })
     )
 
     this._providers.push(
-      createCompletionItemProvider(
-        this._items,
-        HTML_TYPES,
-        /\bclass(Name)?=["']([^"']*)$/, // /\bclass(Name)?=(["'])(?!.*?\2)/
-        ["'", '"', ' ', '.', separator],
-        tailwind.config
-      )
+      createCompletionItemProvider({
+        items: this._items,
+        languages: HTML_TYPES,
+        regex: /\bclass=["']([^"']*)$/, // /\bclass(Name)?=(["'])(?!.*?\2)/
+        triggerCharacters: ["'", '"', ' ', '.', separator],
+        config: tailwind.config,
+        emmet: true
+      })
     )
 
     this._providers.push(
-      vscode.languages.registerCompletionItemProvider(
-        CSS_TYPES,
-        {
-          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)
+      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
+      })
+    )
 
-            let matches = text.match(/config\(["']([^"']*)$/)
+    // Vue.js
+    this._providers.push(
+      createCompletionItemProvider({
+        items: this._items,
+        languages: ['vue'],
+        regex: /\bclass=["']([^"']*)$/,
+        enable: text => {
+          if (
+            text.indexOf('<template') !== -1 &&
+            text.indexOf('</template>') === -1
+          ) {
+            return true
+          }
+          return false
+        },
+        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
+        }
+      })
+    )
 
-            if (!matches) return []
+    this._providers.push(
+      createConfigItemProvider({
+        languages: CSS_TYPES,
+        items: this._prefixedConfigItems
+      })
+    )
 
-            let objPath =
-              matches[1]
-                .replace(/\.[^.]*$/, '')
-                .replace('.', '.children.')
-                .trim() + '.children'
-            let foo = dlv(this._configItems, objPath)
-
-            if (foo) {
-              console.log(Object.keys(foo).map(x => foo[x].item))
-              return Object.keys(foo).map(x => foo[x].item)
-            }
-
-            return Object.keys(this._configItems).map(
-              x => this._configItems[x].item
-            )
+    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, {
-        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)
+      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
+            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)
+            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)?=["']([^"']*)$/)
+            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 (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}`
-              }
+              if (typeof dlv(this._tailwind.classNames, parts) === 'string') {
+                let base = parts.pop()
+                let selector = `.${escapeClassName(className)}`
 
-              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', {})
+                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}`
+                }
 
-              Object.keys(screens).some(screen => {
-                if (parts.indexOf(screen) !== -1) {
-                  code = `@media (min-width: ${
-                    screens[screen]
-                  }) {\n${code.replace(/^/gm, '  ')}\n}`
-                  return true
+                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()
                 }
-                return false
-              })
-              hoverStr.appendCodeblock(code, 'css')
+                css = css.replace(/([;{]) /g, '$1\n').replace(/^/gm, '  ')
+                let code = `${selector} {\n${css}\n}`
+                let screens = dlv(this._tailwind.config, 'screens', {})
 
-              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
+                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 new vscode.Hover(hoverStr, hoverRange)
+              }
             }
-          }
 
-          return null
+            return null
+          }
         }
-      })
+      )
     )
 
     this._disposable = vscode.Disposable.from(...this._providers)
I src/htmlElements.ts
diff --git a/src/htmlElements.ts b/src/htmlElements.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4bb7b1c1220b17799c08d5b923dd5e8ac0e4f280
--- /dev/null
+++ b/src/htmlElements.ts
@@ -0,0 +1,142 @@
+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'
+]