diagnostics

master
Brad Cornes 2020-05-09 22:11:35 +01:00
parent 8eaa5c9326
commit 41b8fd104e
5 changed files with 96 additions and 7 deletions

View File

@ -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)
}
}

View File

@ -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,
}, },
} }

View File

@ -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]))

View File

@ -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,
}, },
}, },
} }

View File

@ -21,6 +21,7 @@ export type EditorState = {
userLanguages: Record<string, string> userLanguages: Record<string, string>
capabilities: { capabilities: {
configuration: boolean configuration: boolean
diagnosticRelatedInformation: boolean
} }
} }