diagnostics
parent
8eaa5c9326
commit
41b8fd104e
|
@ -0,0 +1,79 @@
|
||||||
|
import {
|
||||||
|
TextDocument,
|
||||||
|
Diagnostic,
|
||||||
|
DiagnosticSeverity,
|
||||||
|
} from 'vscode-languageserver'
|
||||||
|
import { State } from '../util/state'
|
||||||
|
import { isCssDoc } from '../util/css'
|
||||||
|
import { findClassNamesInRange } from '../util/find'
|
||||||
|
import { getClassNameParts } from '../util/getClassNameAtPosition'
|
||||||
|
const dlv = require('dlv')
|
||||||
|
|
||||||
|
function provideCssDiagnostics(state: State, document: TextDocument): void {
|
||||||
|
const classNames = findClassNamesInRange(document)
|
||||||
|
|
||||||
|
let diagnostics: Diagnostic[] = classNames
|
||||||
|
.map(({ className, range }) => {
|
||||||
|
const parts = getClassNameParts(state, className)
|
||||||
|
if (!parts) return null
|
||||||
|
const info = dlv(state.classNames.classNames, parts)
|
||||||
|
let message: string
|
||||||
|
if (info.__context && info.__context.length > 0) {
|
||||||
|
if (info.__context.length === 1) {
|
||||||
|
message = `\`@apply\` cannot be used with \`.${className}\` because it is nested inside of an at-rule (${info.__context[0]}).`
|
||||||
|
} else {
|
||||||
|
message = `\`@apply\` cannot be used with \`.${className}\` because it is nested inside of at-rules (${info.__context.join(
|
||||||
|
', '
|
||||||
|
)}).`
|
||||||
|
}
|
||||||
|
} else if (info.__pseudo && info.__pseudo.length > 0) {
|
||||||
|
if (info.__pseudo.length === 1) {
|
||||||
|
message = `\`@apply\` cannot be used with \`.${className}\` because its definition includes a pseudo-selector (${info.__pseudo[0]})`
|
||||||
|
} else {
|
||||||
|
message = `\`@apply\` cannot be used with \`.${className}\` because its definition includes pseudo-selectors (${info.__pseudo.join(
|
||||||
|
', '
|
||||||
|
)})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
severity: DiagnosticSeverity.Error,
|
||||||
|
range,
|
||||||
|
message,
|
||||||
|
// source: 'ex',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
// if (state.editor.capabilities.diagnosticRelatedInformation) {
|
||||||
|
// diagnostic.relatedInformation = [
|
||||||
|
// {
|
||||||
|
// location: {
|
||||||
|
// uri: document.uri,
|
||||||
|
// range: Object.assign({}, diagnostic.range),
|
||||||
|
// },
|
||||||
|
// message: '',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// location: {
|
||||||
|
// uri: document.uri,
|
||||||
|
// range: Object.assign({}, diagnostic.range),
|
||||||
|
// },
|
||||||
|
// message: '',
|
||||||
|
// },
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
|
||||||
|
state.editor.connection.sendDiagnostics({ uri: document.uri, diagnostics })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function provideDiagnostics(
|
||||||
|
state: State,
|
||||||
|
document: TextDocument
|
||||||
|
): Promise<void> {
|
||||||
|
if (isCssDoc(state, document)) {
|
||||||
|
return provideCssDiagnostics(state, document)
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import {
|
||||||
import { provideHover } from './providers/hoverProvider'
|
import { provideHover } from './providers/hoverProvider'
|
||||||
import { URI } from 'vscode-uri'
|
import { URI } from 'vscode-uri'
|
||||||
import { getDocumentSettings } from './util/getDocumentSettings'
|
import { getDocumentSettings } from './util/getDocumentSettings'
|
||||||
|
import { provideDiagnostics } from './providers/diagnosticsProvider'
|
||||||
|
|
||||||
let state: State = { enabled: false }
|
let state: State = { enabled: false }
|
||||||
let connection = createConnection(ProposedFeatures.all)
|
let connection = createConnection(ProposedFeatures.all)
|
||||||
|
@ -45,6 +46,9 @@ documents.onDidOpen((event) => {
|
||||||
documents.onDidClose((event) => {
|
documents.onDidClose((event) => {
|
||||||
documentSettings.delete(event.document.uri)
|
documentSettings.delete(event.document.uri)
|
||||||
})
|
})
|
||||||
|
documents.onDidChangeContent((change) => {
|
||||||
|
provideDiagnostics(state, change.document)
|
||||||
|
})
|
||||||
documents.listen(connection)
|
documents.listen(connection)
|
||||||
|
|
||||||
connection.onInitialize(
|
connection.onInitialize(
|
||||||
|
@ -64,6 +68,10 @@ connection.onInitialize(
|
||||||
capabilities: {
|
capabilities: {
|
||||||
configuration:
|
configuration:
|
||||||
capabilities.workspace && !!capabilities.workspace.configuration,
|
capabilities.workspace && !!capabilities.workspace.configuration,
|
||||||
|
diagnosticRelatedInformation:
|
||||||
|
capabilities.textDocument &&
|
||||||
|
capabilities.textDocument.publishDiagnostics &&
|
||||||
|
capabilities.textDocument.publishDiagnostics.relatedInformation,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const CSS_LANGUAGES = [
|
||||||
'stylus',
|
'stylus',
|
||||||
]
|
]
|
||||||
|
|
||||||
function isCssDoc(state: State, doc: TextDocument): boolean {
|
export function isCssDoc(state: State, doc: TextDocument): boolean {
|
||||||
const userCssLanguages = Object.keys(
|
const userCssLanguages = Object.keys(
|
||||||
state.editor.userLanguages
|
state.editor.userLanguages
|
||||||
).filter((lang) => CSS_LANGUAGES.includes(state.editor.userLanguages[lang]))
|
).filter((lang) => CSS_LANGUAGES.includes(state.editor.userLanguages[lang]))
|
||||||
|
|
|
@ -72,7 +72,7 @@ export function findJsxStrings(str: string): StringInfo[] {
|
||||||
|
|
||||||
export function findClassNamesInRange(
|
export function findClassNamesInRange(
|
||||||
doc: TextDocument,
|
doc: TextDocument,
|
||||||
range: Range
|
range?: Range
|
||||||
): DocumentClassName[] {
|
): DocumentClassName[] {
|
||||||
const classLists = findClassListsInRange(doc, range)
|
const classLists = findClassListsInRange(doc, range)
|
||||||
return [].concat.apply(
|
return [].concat.apply(
|
||||||
|
@ -111,10 +111,11 @@ export function findClassNamesInRange(
|
||||||
|
|
||||||
export function findClassListsInRange(
|
export function findClassListsInRange(
|
||||||
doc: TextDocument,
|
doc: TextDocument,
|
||||||
range: Range
|
range?: Range
|
||||||
): DocumentClassList[] {
|
): DocumentClassList[] {
|
||||||
const text = doc.getText(range)
|
const text = doc.getText(range)
|
||||||
const matches = findAll(/(@apply\s+)(?<classList>[^;}]+)[;}]/g, text)
|
const matches = findAll(/(@apply\s+)(?<classList>[^;}]+)[;}]/g, text)
|
||||||
|
const globalStart: Position = range ? range.start : { line: 0, character: 0 }
|
||||||
|
|
||||||
return matches.map((match) => {
|
return matches.map((match) => {
|
||||||
const start = indexToPosition(text, match.index + match[1].length)
|
const start = indexToPosition(text, match.index + match[1].length)
|
||||||
|
@ -126,12 +127,12 @@ export function findClassListsInRange(
|
||||||
classList: match.groups.classList,
|
classList: match.groups.classList,
|
||||||
range: {
|
range: {
|
||||||
start: {
|
start: {
|
||||||
line: range.start.line + start.line,
|
line: globalStart.line + start.line,
|
||||||
character: range.start.character + start.character,
|
character: globalStart.character + start.character,
|
||||||
},
|
},
|
||||||
end: {
|
end: {
|
||||||
line: range.start.line + end.line,
|
line: globalStart.line + end.line,
|
||||||
character: range.start.character + end.character,
|
character: globalStart.character + end.character,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ export type EditorState = {
|
||||||
userLanguages: Record<string, string>
|
userLanguages: Record<string, string>
|
||||||
capabilities: {
|
capabilities: {
|
||||||
configuration: boolean
|
configuration: boolean
|
||||||
|
diagnosticRelatedInformation: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue