add @apply hover provider
parent
cb20c3bc86
commit
16725980b7
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,7 @@
|
|||
"dlv": "^1.1.3",
|
||||
"emmet-helper": "0.0.1",
|
||||
"glob-exec": "^0.1.1",
|
||||
"line-column": "^1.0.2",
|
||||
"tailwindcss-class-names": "0.0.1",
|
||||
"typescript": "^3.8.3",
|
||||
"vscode-languageserver": "^5.2.1",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { State } from '../util/state'
|
||||
import { State, DocumentClassName } from '../util/state'
|
||||
import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
|
||||
import {
|
||||
getClassNameAtPosition,
|
||||
|
@ -9,6 +9,8 @@ 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'
|
||||
|
||||
export function provideHover(
|
||||
state: State,
|
||||
|
@ -73,7 +75,7 @@ function provideCssHelperHover(
|
|||
}
|
||||
}
|
||||
|
||||
function provideClassNameHover(
|
||||
function provideClassAttributeHover(
|
||||
state: State,
|
||||
{ textDocument, position }: TextDocumentPositionParams
|
||||
): Hover {
|
||||
|
@ -84,17 +86,53 @@ function provideClassNameHover(
|
|||
let hovered = getClassNameAtPosition(doc, position)
|
||||
if (!hovered) return null
|
||||
|
||||
const parts = getClassNameParts(state, hovered.className)
|
||||
return classNameToHover(state, hovered)
|
||||
}
|
||||
|
||||
function classNameToHover(
|
||||
state: State,
|
||||
{ className, range }: DocumentClassName
|
||||
): Hover {
|
||||
const parts = getClassNameParts(state, className)
|
||||
if (parts === null) return null
|
||||
|
||||
return {
|
||||
contents: {
|
||||
language: 'css',
|
||||
value: stringifyCss(
|
||||
hovered.className,
|
||||
dlv(state.classNames.classNames, parts)
|
||||
),
|
||||
value: stringifyCss(className, dlv(state.classNames.classNames, parts)),
|
||||
},
|
||||
range: hovered.range,
|
||||
range,
|
||||
}
|
||||
}
|
||||
|
||||
function provideAtApplyHover(
|
||||
state: State,
|
||||
{ textDocument, position }: TextDocumentPositionParams
|
||||
): Hover {
|
||||
let doc = state.editor.documents.get(textDocument.uri)
|
||||
|
||||
if (!isCssContext(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 },
|
||||
})
|
||||
|
||||
const className = classNames.find(({ range }) =>
|
||||
isWithinRange(position, range)
|
||||
)
|
||||
|
||||
if (!className) return null
|
||||
|
||||
return classNameToHover(state, className)
|
||||
}
|
||||
|
||||
function provideClassNameHover(
|
||||
state: State,
|
||||
params: TextDocumentPositionParams
|
||||
): Hover {
|
||||
return (
|
||||
provideClassAttributeHover(state, params) ||
|
||||
provideAtApplyHover(state, params)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import { TextDocument, Range, Position } from 'vscode-languageserver'
|
||||
import { DocumentClassName, DocumentClassList } from './state'
|
||||
import lineColumn from 'line-column'
|
||||
|
||||
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
|
||||
let match: RegExpMatchArray
|
||||
let matches: RegExpMatchArray[] = []
|
||||
|
@ -65,3 +69,76 @@ export function findJsxStrings(str: string): StringInfo[] {
|
|||
}
|
||||
return strings
|
||||
}
|
||||
|
||||
export function findClassNamesInRange(
|
||||
doc: TextDocument,
|
||||
range: Range
|
||||
): DocumentClassName[] {
|
||||
const classLists = findClassListsInRange(doc, range)
|
||||
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)
|
||||
const end = indexToPosition(classList, index + parts[i].length)
|
||||
names.push({
|
||||
className: parts[i],
|
||||
range: {
|
||||
start: {
|
||||
line: range.start.line + start.line,
|
||||
character:
|
||||
(end.line === 0 ? range.start.character : 0) +
|
||||
start.character,
|
||||
},
|
||||
end: {
|
||||
line: range.start.line + end.line,
|
||||
character:
|
||||
(end.line === 0 ? range.start.character : 0) + end.character,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
index += parts[i].length
|
||||
}
|
||||
return names
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export function findClassListsInRange(
|
||||
doc: TextDocument,
|
||||
range: Range
|
||||
): DocumentClassList[] {
|
||||
const text = doc.getText(range)
|
||||
const matches = findAll(/(@apply\s+)(?<classList>[^;}]+)[;}]/g, text)
|
||||
|
||||
return matches.map((match) => {
|
||||
const start = indexToPosition(text, match.index + match[1].length)
|
||||
const end = indexToPosition(
|
||||
text,
|
||||
match.index + match[1].length + match.groups.classList.length
|
||||
)
|
||||
return {
|
||||
classList: match.groups.classList,
|
||||
range: {
|
||||
start: {
|
||||
line: range.start.line + start.line,
|
||||
character: range.start.character + start.character,
|
||||
},
|
||||
end: {
|
||||
line: range.start.line + end.line,
|
||||
character: range.start.character + end.character,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function indexToPosition(str: string, index: number): Position {
|
||||
const { line, col } = lineColumn(str + '\n', index)
|
||||
return { line: line - 1, character: col - 1 }
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { TextDocument, Range, Position } from 'vscode-languageserver'
|
||||
import { State } from './state'
|
||||
import { State, DocumentClassName } from './state'
|
||||
const dlv = require('dlv')
|
||||
|
||||
export function getClassNameAtPosition(
|
||||
document: TextDocument,
|
||||
position: Position
|
||||
): { className: string; range: Range } {
|
||||
): DocumentClassName {
|
||||
const range1: Range = {
|
||||
start: { line: Math.max(position.line - 5, 0), character: 0 },
|
||||
end: position,
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { Position, Range } from 'vscode-languageserver'
|
||||
|
||||
export function isWithinRange(position: Position, range: Range): boolean {
|
||||
if (
|
||||
position.line === range.start.line &&
|
||||
position.character >= range.start.character
|
||||
) {
|
||||
if (
|
||||
position.line === range.end.line &&
|
||||
position.character > range.end.character
|
||||
) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (
|
||||
position.line === range.end.line &&
|
||||
position.character <= range.end.character
|
||||
) {
|
||||
if (
|
||||
position.line === range.start.line &&
|
||||
position.character < range.end.character
|
||||
) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (position.line > range.start.line && position.line < range.end.line) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { TextDocuments, Connection } from 'vscode-languageserver'
|
||||
import { TextDocuments, Connection, Range } from 'vscode-languageserver'
|
||||
|
||||
export type ClassNamesTree = {
|
||||
[key: string]: ClassNamesTree
|
||||
|
@ -36,3 +36,13 @@ export type State = null | {
|
|||
dependencies: string[]
|
||||
editor: EditorState
|
||||
}
|
||||
|
||||
export type DocumentClassList = {
|
||||
classList: string
|
||||
range: Range
|
||||
}
|
||||
|
||||
export type DocumentClassName = {
|
||||
className: string
|
||||
range: Range
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue