tailwind-ctp-intellisense @master -
refs -
log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
Merge branch 'next' into diagnostics
11 changed files, 354 additions(+), 131 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index d3461d15178fbd81895623d655bde63ebf413264..f844afb78a3c31adf7b940746d88f9e90ad99f41 100755
--- a/package-lock.json
+++ b/package-lock.json
@@ -1033,6 +1033,12 @@ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.1.tgz",
"integrity": "sha512-dOrgprHnkDaj1pmrwdcMAf0QRNQzqTB5rxJph+iIQshSmIvtgRqJ0nim8u1vvXU8iOXZrH96+M46JDFTPLingA==",
"dev": true
},
+ "@types/moo": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/@types/moo/-/moo-0.5.3.tgz",
+ "integrity": "sha512-PJJ/jvb5Gor8DWvXN3e75njfQyYNRz0PaFSZ3br9GfHM9N2FxvuJ/E/ytcQePJOLzHlvgFSsIJIvfUMUxWTbnA==",
+ "dev": true
+ },
"@types/node": {
"version": "13.13.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz",
@@ -4941,6 +4947,12 @@ "mkdirp": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz",
"integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==",
+ "dev": true
+ },
+ "moo": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz",
+ "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==",
"dev": true
},
"ms": {
diff --git a/package.json b/package.json
index 69d62d97f70770c055be6a9d69bc98fb4d3f295a..0b515cadd282395481a1489f142d1b951f894696 100755
--- a/package.json
+++ b/package.json
@@ -44,7 +44,8 @@ "source.css.scss",
"source.css.less",
"source.css.postcss",
"source.vue",
- "source.svelte"
+ "source.svelte",
+ "text.html"
]
}
],
@@ -75,6 +76,7 @@ },
"devDependencies": {
"@ctrl/tinycolor": "^3.1.0",
"@types/mocha": "^5.2.0",
+ "@types/moo": "^0.5.3",
"@types/node": "^13.9.3",
"@types/vscode": "^1.32.0",
"@zeit/ncc": "^0.22.0",
@@ -93,6 +95,7 @@ "jest": "^25.5.4",
"line-column": "^1.0.2",
"mitt": "^1.2.0",
"mkdirp": "^1.0.3",
+ "moo": "^0.5.1",
"pkg-up": "^3.1.0",
"postcss": "^7.0.27",
"postcss-selector-parser": "^6.0.2",
diff --git a/src/lib/languages.ts b/src/lib/languages.ts
index b9238edde46be72d6b7a5db044b063e57666efff..777f5885f033174f1163d970cece03505d7dd0e8 100644
--- a/src/lib/languages.ts
+++ b/src/lib/languages.ts
@@ -31,10 +31,12 @@ 'postcss',
'sass',
'scss',
'stylus',
+ 'sugarss',
// js
'javascript',
'javascriptreact',
'reason',
+ 'typescript',
'typescriptreact',
// mixed
'vue',
diff --git a/src/lsp/providers/completionProvider.ts b/src/lsp/providers/completionProvider.ts
index 8f2c43d8325bc847a7b05a87dc180025a647f2a9..eae7546bffc68d70c05ef1ebbdd0353fac33a42d 100644
--- a/src/lsp/providers/completionProvider.ts
+++ b/src/lsp/providers/completionProvider.ts
@@ -12,7 +12,7 @@ import removeMeta from '../util/removeMeta'
import { getColor, getColorFromValue } from '../util/color'
import { isHtmlContext } from '../util/html'
import { isCssContext } from '../util/css'
-import { findLast, findJsxStrings, arrFindLast } from '../util/find'
+import { findLast } from '../util/find'
import { stringifyConfigValue, stringifyCss } from '../util/stringify'
import { stringifyScreen, Screen } from '../util/screens'
import isObject from '../../util/isObject'
@@ -23,6 +23,11 @@ import { isJsContext } from '../util/js'
import { naturalExpand } from '../util/naturalExpand'
import semver from 'semver'
import { docsUrl } from '../util/docsUrl'
+import { ensureArray } from '../../util/array'
+import {
+ getClassAttributeLexer,
+ getComputedClassAttributeLexer,
+ CompletionItemKind,
import { ensureArray } from '../../util/array'
function completionsFromClassList(
@@ -122,35 +127,41 @@ start: { line: Math.max(position.line - 10, 0), character: 0 },
end: position,
})
- const match = findLast(/\bclass(?:Name)?=(?<initial>['"`{])/gi, str)
+ const match = findLast(/[\s:]class(?:Name)?=['"`{]/gi, str)
if (match === null) {
return null
}
-import { State } from '../util/state'
+ const lexer =
+ match[0][0] === ':'
+ ? getComputedClassAttributeLexer()
+ start: {
import { State } from '../util/state'
CompletionItemKind,
+ filter?: (item: CompletionItem) => boolean
-import removeMeta from '../util/removeMeta'
+ try {
+ let tokens = Array.from(lexer)
+ start: {
CompletionParams,
-import removeMeta from '../util/removeMeta'
+ start: {
Range,
-import removeMeta from '../util/removeMeta'
+ start: {
MarkupKind,
-import removeMeta from '../util/removeMeta'
+ start: {
CompletionList,
-import removeMeta from '../util/removeMeta'
+ start: {
} from 'vscode-languageserver'
-import { getColor, getColorFromValue } from '../util/color'
+ classList = tokens[i].value + classList
+ CompletionList,
-import { getColor, getColorFromValue } from '../util/color'
+ ...classListRange.start,
import { State } from '../util/state'
- const classList = str.substr(
+ }
-import { getColor, getColorFromValue } from '../util/color'
+ Range,
CompletionItem,
-import { State } from '../util/state'
import {
- CompletionItemKind,
+ MarkupKind,
return completionsFromClassList(state, classList, {
start: {
line: position.line,
@@ -159,24 +169,13 @@ },
end: position,
})
}
- return null
CompletionItemKind,
- MarkupKind,
-
- if (rest.indexOf(match.groups.initial) !== -1) {
- return null
CompletionItemKind,
- MarkupKind,
+import {
import { State } from '../util/state'
- classList: string,
- start: {
- line: position.line,
- character: position.character - rest.length,
- CompletionItemKind,
Range,
- end: position,
- })
+ MarkupKind,
}
function provideAtApplyCompletions(
diff --git a/src/lsp/providers/diagnosticsProvider.ts b/src/lsp/providers/diagnosticsProvider.ts
index 66e0f4cdc90d8f09bd9c9c619e8177875cae5ba9..152f3b76a3c0a9d10f37c63f42bdcdc6e5d8a4e1 100644
--- a/src/lsp/providers/diagnosticsProvider.ts
+++ b/src/lsp/providers/diagnosticsProvider.ts
@@ -10,7 +10,7 @@ import { getClassNameParts } from '../util/getClassNameAtPosition'
const dlv = require('dlv')
function provideCssDiagnostics(state: State, document: TextDocument): void {
- const classNames = findClassNamesInRange(document)
+ const classNames = findClassNamesInRange(document, undefined, 'css')
let diagnostics: Diagnostic[] = classNames
.map(({ className, range }) => {
diff --git a/src/lsp/providers/hoverProvider.ts b/src/lsp/providers/hoverProvider.ts
index 7e27d9ac9833ec166a6332607b38562ec0b03f80..a9010a3142cb4e9700f63e3bd06426a144b9afa0 100644
--- a/src/lsp/providers/hoverProvider.ts
+++ b/src/lsp/providers/hoverProvider.ts
@@ -1,16 +1,11 @@
-import { State, DocumentClassName } from '../util/state'
+import { State } from '../util/state'
import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
-import {
- getClassNameAtPosition,
+const dlv = require('dlv')
getClassNameParts,
-} from '../util/getClassNameAtPosition'
import { stringifyCss, stringifyConfigValue } from '../util/stringify'
const dlv = require('dlv')
-import { isHtmlContext } from '../util/html'
import { isCssContext } from '../util/css'
-import { isJsContext } from '../util/js'
-import { isWithinRange } from '../util/isWithinRange'
-import { findClassNamesInRange } from '../util/find'
+import { findClassNameAtPosition } from '../util/find'
export function provideHover(
state: State,
@@ -75,77 +70,34 @@ },
}
}
- getClassNameParts,
const dlv = require('dlv')
+import { State, DocumentClassName } from '../util/state'
state: State,
{ textDocument, position }: TextDocumentPositionParams
): Hover {
let doc = state.editor.documents.get(textDocument.uri)
- if (
- !isHtmlContext(state, doc, position) &&
- !isJsContext(state, doc, position)
- )
-import {
+const dlv = require('dlv')
import { stringifyCss, stringifyConfigValue } from '../util/stringify'
-
- let hovered = getClassNameAtPosition(doc, position)
- if (!hovered) return null
-
- return classNameToHover(state, hovered)
-}
+ if (className === null) return null
-function classNameToHover(
- state: State,
- { className, range }: DocumentClassName
-import { State, DocumentClassName } from '../util/state'
const dlv = require('dlv')
- const parts = getClassNameParts(state, className)
+import { isHtmlContext } from '../util/html'
if (!parts) return null
return {
contents: {
language: 'css',
- value: stringifyCss(className, dlv(state.classNames.classNames, parts)),
- },
- range,
- }
-}
-
-function provideAtApplyHover(
- state: State,
- { textDocument, position }: TextDocumentPositionParams
-): Hover {
- let doc = state.editor.documents.get(textDocument.uri)
-
- if (!isCssContext(state, doc, position)) return null
-
- const classNames = findClassNamesInRange(doc, {
- start: { line: Math.max(position.line - 10, 0), character: 0 },
- end: { line: position.line + 10, character: 0 },
-import {
+import { isHtmlContext } from '../util/html'
-
- const className = classNames.find(({ range }) =>
- isWithinRange(position, range)
- )
-
-import { stringifyCss, stringifyConfigValue } from '../util/stringify'
import { isHtmlContext } from '../util/html'
import { State, DocumentClassName } from '../util/state'
- getClassNameAtPosition,
- return classNameToHover(state, className)
-import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
+import { isHtmlContext } from '../util/html'
import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
-
+ ),
-function provideClassNameHover(
- state: State,
-import { State, DocumentClassName } from '../util/state'
+ getClassNameParts,
import { stringifyCss, stringifyConfigValue } from '../util/stringify'
-): Hover {
-import { State, DocumentClassName } from '../util/state'
import { isHtmlContext } from '../util/html'
- provideClassAttributeHover(state, params) ||
+ getClassNameAtPosition,
-const dlv = require('dlv')
import {
- )
+const dlv = require('dlv')
}
diff --git a/src/lsp/util/css.ts b/src/lsp/util/css.ts
index d1acbea2b4e6e166e57d7d604ac2e615d1e94a0b..e6dbd097d4b97c42c4f7f650fc1e1186d33c0047 100644
--- a/src/lsp/util/css.ts
+++ b/src/lsp/util/css.ts
@@ -1,5 +1,5 @@
import { TextDocument, Position } from 'vscode-languageserver'
-import { isInsideTag, isVueDoc, isSvelteDoc } from './html'
+import { isInsideTag, isVueDoc, isSvelteDoc, isHtmlDoc } from './html'
import { State } from './state'
export const CSS_LANGUAGES = [
@@ -9,6 +9,7 @@ 'postcss',
'sass',
'scss',
'stylus',
+ 'sugarss',
]
export function isCssDoc(state: State, doc: TextDocument): boolean {
@@ -28,7 +29,7 @@ if (isCssDoc(state, doc)) {
return true
}
- if (isVueDoc(doc) || isSvelteDoc(doc)) {
+ if (isHtmlDoc(state, doc) || isVueDoc(doc) || isSvelteDoc(doc)) {
let str = doc.getText({
start: { line: 0, character: 0 },
end: position,
diff --git a/src/lsp/util/find.ts b/src/lsp/util/find.ts
index 17b6a127654ae8b748941e828167512078286361..800d0a3ce5dd1d133e5dfb1e246faac7abf1ca1c 100644
--- a/src/lsp/util/find.ts
+++ b/src/lsp/util/find.ts
@@ -1,5 +1,12 @@
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 { isWithinRange } from './isWithinRange'
+}
import { DocumentClassName, DocumentClassList } from './state'
+}
import lineColumn from 'line-column'
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
@@ -19,172 +26,345 @@ }
return matches[matches.length - 1]
}
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
while ((match = re.exec(str)) !== null) {
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
matches.push({ ...match })
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from './state'
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from './state'
+export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
+ let match: RegExpMatchArray
import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from './state'
+ let match: RegExpMatchArray
+ return [].concat.apply(
+ [],
+ classLists.map(({ classList, range }) => {
+ const parts = classList.split(/(\s+)/)
+ const names: DocumentClassName[] = []
+ let index = 0
+ for (let i = 0; i < parts.length; i++) {
+ if (i % 2 === 0) {
+ const start = indexToPosition(classList, index)
+ let matches: RegExpMatchArray[] = []
import { DocumentClassName, DocumentClassList } from './state'
+ names.push({
+ className: parts[i],
+ range: {
+ start: {
+ line: range.start.line + start.line,
+ character:
+ (end.line === 0 ? range.start.character : 0) +
+ start.character,
+ },
+ while ((match = re.exec(str)) !== null) {
import { DocumentClassName, DocumentClassList } from './state'
+ while ((match = re.exec(str)) !== null) {
import lineColumn from 'line-column'
+ character:
+ (end.line === 0 ? range.start.character : 0) + end.character,
+ while ((match = re.exec(str)) !== null) {
import { TextDocument, Range, Position } from 'vscode-languageserver'
+ },
+ })
+ }
+ index += parts[i].length
+ }
+ return names
+ matches.push({ ...match })
+ )
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from './state'
}
+ let matches: RegExpMatchArray[] = []
+ doc: TextDocument,
+ range?: Range
+): DocumentClassList[] {
+ matches.push({ ...match })
-import { DocumentClassName, DocumentClassList } from './state'
+ matches.push({ ...match })
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
-import { DocumentClassName, DocumentClassList } from './state'
+ matches.push({ ...match })
let match: RegExpMatchArray
-import { DocumentClassName, DocumentClassList } from './state'
+
+ matches.push({ ...match })
let matches: RegExpMatchArray[] = []
-import { DocumentClassName, DocumentClassList } from './state'
+ matches.push({ ...match })
while ((match = re.exec(str)) !== null) {
+ const end = indexToPosition(
+ text,
import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { TextDocument, Range, Position } from 'vscode-languageserver'
+ }
import { DocumentClassName, DocumentClassList } from './state'
+ return {
+ classList: match.groups.classList,
+ range: {
+ start: {
+ line: globalStart.line + start.line,
+ character: globalStart.character + start.character,
+ },
+ end: {
+ line: globalStart.line + end.line,
+ return matches
import { DocumentClassName, DocumentClassList } from './state'
+ }
matches.push({ ...match })
+ return matches
import lineColumn from 'line-column'
+import { DocumentClassName, DocumentClassList } from './state'
import lineColumn from 'line-column'
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import { TextDocument, Range, Position } from 'vscode-languageserver'
+
-import lineColumn from 'line-column'
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from './state'
+
}
+ while ((match = re.exec(str)) !== null) {
+ doc: TextDocument,
+ range: Range
+): DocumentClassList[] {
+ matches.push({ ...match })
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import lineColumn from 'line-column'
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import lineColumn from 'line-column'
+import { TextDocument, Range, Position } from 'vscode-languageserver'
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import lineColumn from 'line-column'
+import { DocumentClassName, DocumentClassList } from './state'
+ const subtext = text.substr(match.index + match[0].length - 1, 200)
+ let lexer = getClassAttributeLexer()
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const strings: StringInfo[] = []
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
let bracketCount = 0
+import { TextDocument, Range, Position } from 'vscode-languageserver'
for (let i = 0; i < chars.length; i++) {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const char = chars[i]
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
if (char === '{') {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
bracketCount += 1
+import { TextDocument, Range, Position } from 'vscode-languageserver'
} else if (char === '}') {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
bracketCount -= 1
+import { TextDocument, Range, Position } from 'vscode-languageserver'
} else if (
+import { TextDocument, Range, Position } from 'vscode-languageserver'
char === Quote.SINGLE ||
+import { TextDocument, Range, Position } from 'vscode-languageserver'
char === Quote.DOUBLE ||
+import { TextDocument, Range, Position } from 'vscode-languageserver'
char === Quote.TICK
+import { TextDocument, Range, Position } from 'vscode-languageserver'
) {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
let open = arrFindLast(strings, (string) => string.char === char)
+import { TextDocument, Range, Position } from 'vscode-languageserver'
if (strings.length === 0 || !open || (open && open.end)) {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
strings.push({ start: i + 1, char })
+ if (currentClassList) {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
} else {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
open.end = i
+import { TextDocument, Range, Position } from 'vscode-languageserver'
}
- }
+ })
-export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
+ matches.push({ ...match })
+import { TextDocument, Range, Position } from 'vscode-languageserver'
// end
- break
+ }
-import { DocumentClassName, DocumentClassList } from './state'
+export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
import lineColumn from 'line-column'
import { TextDocument, Range, Position } from 'vscode-languageserver'
+ break
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
return strings
import { TextDocument, Range, Position } from 'vscode-languageserver'
-import { DocumentClassName, DocumentClassList } from './state'
-
export function findClassNamesInRange(
+import { TextDocument, Range, Position } from 'vscode-languageserver'
doc: TextDocument,
+import { TextDocument, Range, Position } from 'vscode-languageserver'
range?: Range
+import { TextDocument, Range, Position } from 'vscode-languageserver'
): DocumentClassName[] {
+ }
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const classLists = findClassListsInRange(doc, range)
+import { TextDocument, Range, Position } from 'vscode-languageserver'
return [].concat.apply(
+import { TextDocument, Range, Position } from 'vscode-languageserver'
[],
+import { TextDocument, Range, Position } from 'vscode-languageserver'
classLists.map(({ classList, range }) => {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const parts = classList.split(/(\s+)/)
+ }
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const names: DocumentClassName[] = []
+import { TextDocument, Range, Position } from 'vscode-languageserver'
let index = 0
+import { TextDocument, Range, Position } from 'vscode-languageserver'
for (let i = 0; i < parts.length; i++) {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
if (i % 2 === 0) {
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const start = indexToPosition(classList, index)
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const end = indexToPosition(classList, index + parts[i].length)
+import { TextDocument, Range, Position } from 'vscode-languageserver'
names.push({
+import { TextDocument, Range, Position } from 'vscode-languageserver'
className: parts[i],
+import { TextDocument, Range, Position } from 'vscode-languageserver'
range: {
+ text,
+import { TextDocument, Range, Position } from 'vscode-languageserver'
start: {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
line: range.start.line + start.line,
+import { TextDocument, Range, Position } from 'vscode-languageserver'
character:
+import { TextDocument, Range, Position } from 'vscode-languageserver'
(end.line === 0 ? range.start.character : 0) +
+import { TextDocument, Range, Position } from 'vscode-languageserver'
start.character,
+import { TextDocument, Range, Position } from 'vscode-languageserver'
},
+ )
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
end: {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
line: range.start.line + end.line,
let matches: RegExpMatchArray[] = []
- while ((match = re.exec(str)) !== null) {
+export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
+ start: {
+ line: range.start.line + start.line,
+import { TextDocument, Range, Position } from 'vscode-languageserver'
(end.line === 0 ? range.start.character : 0) + end.character,
},
while ((match = re.exec(str)) !== null) {
+import { DocumentClassName, DocumentClassList } from './state'
+ line: range.start.line + end.line,
+export function arrFindLast<T>(arr: T[], predicate: (item: T) => boolean): T {
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
+ },
+ },
+ }
+import { TextDocument, Range, Position } from 'vscode-languageserver'
})
+import { TextDocument, Range, Position } from 'vscode-languageserver'
}
+ )
+ })
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
index += parts[i].length
+}
+
+export function findClassListsInRange(
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
-import lineColumn from 'line-column'
+ matches.push({ ...match })
+import { TextDocument, Range, Position } from 'vscode-languageserver'
return names
+import { TextDocument, Range, Position } from 'vscode-languageserver'
})
matches.push({ ...match })
+import lineColumn from 'line-column'
+ for (let i = arr.length - 1; i >= 0; --i) {
import { TextDocument, Range, Position } from 'vscode-languageserver'
import { TextDocument, Range, Position } from 'vscode-languageserver'
+ matches.push({ ...match })
import { DocumentClassName, DocumentClassList } from './state'
-
+ }
+import { TextDocument, Range, Position } from 'vscode-languageserver'
matches.push({ ...match })
+import lineColumn from 'line-column'
+import { TextDocument, Range, Position } from 'vscode-languageserver'
import { DocumentClassName, DocumentClassList } from './state'
+
+ return matches
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
- matches.push({ ...match })
+ return matches
let match: RegExpMatchArray
-): DocumentClassList[] {
+ return { line: line - 1, character: col - 1 }
+}
+
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const text = doc.getText(range)
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const matches = findAll(/(@apply\s+)(?<classList>[^;}]+)[;}]/g, text)
+export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
matches.push({ ...match })
- let match: RegExpMatchArray
-
+ position: Position
+import { TextDocument, Range, Position } from 'vscode-languageserver'
return matches.map((match) => {
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const start = indexToPosition(text, match.index + match[1].length)
+import { TextDocument, Range, Position } from 'vscode-languageserver'
const end = indexToPosition(
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
import { TextDocument, Range, Position } from 'vscode-languageserver'
}
+
+ const x = arr[i]
import { DocumentClassName, DocumentClassList } from './state'
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
import lineColumn from 'line-column'
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
let match: RegExpMatchArray
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
let matches: RegExpMatchArray[] = []
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
while ((match = re.exec(str)) !== null) {
}
+
+ const x = arr[i]
matches.push({ ...match })
import { TextDocument, Range, Position } from 'vscode-languageserver'
+ let match: RegExpMatchArray
}
- line: globalStart.line + end.line,
+
- return matches
import { DocumentClassName, DocumentClassList } from './state'
}
- matches.push({ ...match })
- },
import { DocumentClassName, DocumentClassList } from './state'
-import lineColumn from 'line-column'
return matches
-
+ matches.push({ ...match })
import { TextDocument, Range, Position } from 'vscode-languageserver'
-import { DocumentClassName, DocumentClassList } from './state'
-import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList } from './state'
import { TextDocument, Range, Position } from 'vscode-languageserver'
-export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
+import { DocumentClassName, DocumentClassList } from './state'
- const { line, col } = lineColumn(str + '\n', index)
+
+import { DocumentClassName, DocumentClassList } from './state'
import { TextDocument, Range, Position } from 'vscode-languageserver'
- return matches[matches.length - 1]
+import lineColumn from 'line-column'
}
diff --git a/src/lsp/util/js.ts b/src/lsp/util/js.ts
index 8a62a5ff69c8ff4869e8541c6d0eac40a9b1744e..495ec5d828cd123e241f579d31711b541ded73a5 100644
--- a/src/lsp/util/js.ts
+++ b/src/lsp/util/js.ts
@@ -6,6 +6,7 @@ export const JS_LANGUAGES = [
'javascript',
'javascriptreact',
'reason',
+ 'typescript',
'typescriptreact',
]
diff --git a/src/lsp/util/lazy.ts b/src/lsp/util/lazy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..858dac58c1fcc51d0bddc53027a47816db0e942e
--- /dev/null
+++ b/src/lsp/util/lazy.ts
@@ -0,0 +1,19 @@
+// https://www.codementor.io/@agustinchiappeberrini/lazy-evaluation-and-javascript-a5m7g8gs3
+
+export interface Lazy<T> {
+ (): T
+ isLazy: boolean
+}
+
+export const lazy = <T>(getter: () => T): Lazy<T> => {
+ let evaluated: boolean = false
+ let _res: T = null
+ const res = <Lazy<T>>function (): T {
+ if (evaluated) return _res
+ _res = getter.apply(this, arguments)
+ evaluated = true
+ return _res
+ }
+ res.isLazy = true
+ return res
+}
diff --git a/src/lsp/util/lexers.ts b/src/lsp/util/lexers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..65197b943a3ef18cb7b5ab1435c10235bca67bbe
--- /dev/null
+++ b/src/lsp/util/lexers.ts
@@ -0,0 +1,53 @@
+import moo from 'moo'
+import { lazy } from './lazy'
+
+const classAttributeStates: { [x: string]: moo.Rules } = {
+ doubleClassList: {
+ lbrace: { match: /(?<!\\)\{/, push: 'interp' },
+ rbrace: { match: /(?<!\\)\}/, pop: 1 },
+ end: { match: /(?<!\\)"/, pop: 1 },
+ classlist: { match: /[\s\S]/, lineBreaks: true },
+ },
+ singleClassList: {
+ lbrace: { match: /(?<!\\)\{/, push: 'interp' },
+ rbrace: { match: /(?<!\\)\}/, pop: 1 },
+ end: { match: /(?<!\\)'/, pop: 1 },
+ classlist: { match: /[\s\S]/, lineBreaks: true },
+ },
+ tickClassList: {
+ lbrace: { match: /(?<=(?<!\\)\$)\{/, push: 'interp' },
+ rbrace: { match: /(?<!\\)\}/, pop: 1 },
+ end: { match: /(?<!\\)`/, pop: 1 },
+ classlist: { match: /[\s\S]/, lineBreaks: true },
+ },
+ interp: {
+ startSingle: { match: /(?<!\\)'/, push: 'singleClassList' },
+ startDouble: { match: /(?<!\\)"/, push: 'doubleClassList' },
+ startTick: { match: /(?<!\\)`/, push: 'tickClassList' },
+ lbrace: { match: /(?<!\\)\{/, push: 'interp' },
+ rbrace: { match: /(?<!\\)\}/, pop: 1 },
+ text: { match: /[\s\S]/, lineBreaks: true },
+ },
+}
+
+export const getClassAttributeLexer = lazy(() =>
+ moo.states({
+ main: {
+ start1: { match: '"', push: 'doubleClassList' },
+ start2: { match: "'", push: 'singleClassList' },
+ start3: { match: '{', push: 'interp' },
+ },
+ ...classAttributeStates,
+ })
+)
+
+export const getComputedClassAttributeLexer = lazy(() =>
+ moo.states({
+ main: {
+ quote: { match: /['"{]/, push: 'interp' },
+ },
+ // TODO: really this should use a different interp definition that is
+ // terminated correctly based on the initial quote type
+ ...classAttributeStates,
+ })
+)