make "invalid @apply" quick fix work in mixed-language documents
parent
a6a8f7e536
commit
6add64c19b
|
@ -6,7 +6,6 @@ import {
|
||||||
TextEdit,
|
TextEdit,
|
||||||
} from 'vscode-languageserver'
|
} from 'vscode-languageserver'
|
||||||
import { State } from '../../util/state'
|
import { State } from '../../util/state'
|
||||||
import { findLast } from '../../util/find'
|
|
||||||
import { isWithinRange } from '../../util/isWithinRange'
|
import { isWithinRange } from '../../util/isWithinRange'
|
||||||
import { getClassNameParts } from '../../util/getClassNameAtPosition'
|
import { getClassNameParts } from '../../util/getClassNameAtPosition'
|
||||||
const dlv = require('dlv')
|
const dlv = require('dlv')
|
||||||
|
@ -31,6 +30,9 @@ import {
|
||||||
} from '../diagnostics/types'
|
} from '../diagnostics/types'
|
||||||
import { flatten, dedupeBy } from '../../../util/array'
|
import { flatten, dedupeBy } from '../../../util/array'
|
||||||
import { joinWithAnd } from '../../util/joinWithAnd'
|
import { joinWithAnd } from '../../util/joinWithAnd'
|
||||||
|
import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
|
||||||
|
import { isCssDoc } from '../../util/css'
|
||||||
|
import { absoluteRange } from '../../util/absoluteRange'
|
||||||
|
|
||||||
async function getDiagnosticsFromCodeActionParams(
|
async function getDiagnosticsFromCodeActionParams(
|
||||||
state: State,
|
state: State,
|
||||||
|
@ -210,6 +212,8 @@ async function provideInvalidApplyCodeActions(
|
||||||
): Promise<CodeAction[]> {
|
): Promise<CodeAction[]> {
|
||||||
let document = state.editor.documents.get(params.textDocument.uri)
|
let document = state.editor.documents.get(params.textDocument.uri)
|
||||||
let documentText = document.getText()
|
let documentText = document.getText()
|
||||||
|
let cssRange: Range
|
||||||
|
let cssText = documentText
|
||||||
const { postcss } = state.modules
|
const { postcss } = state.modules
|
||||||
let change: TextEdit
|
let change: TextEdit
|
||||||
|
|
||||||
|
@ -217,54 +221,67 @@ async function provideInvalidApplyCodeActions(
|
||||||
/\s+/
|
/\s+/
|
||||||
).length
|
).length
|
||||||
|
|
||||||
await postcss([
|
if (!isCssDoc(state, document)) {
|
||||||
postcss.plugin('', (_options = {}) => {
|
let languageBoundaries = getLanguageBoundaries(state, document)
|
||||||
return (root) => {
|
if (!languageBoundaries) return []
|
||||||
root.walkRules((rule) => {
|
cssRange = languageBoundaries.css.find((range) =>
|
||||||
if (change) return false
|
isWithinRange(diagnostic.range.start, range)
|
||||||
|
)
|
||||||
|
if (!cssRange) return []
|
||||||
|
cssText = document.getText(cssRange)
|
||||||
|
}
|
||||||
|
|
||||||
rule.walkAtRules('apply', (atRule) => {
|
try {
|
||||||
let { start, end } = atRule.source
|
await postcss([
|
||||||
let range: Range = {
|
postcss.plugin('', (_options = {}) => {
|
||||||
start: {
|
return (root) => {
|
||||||
line: start.line - 1,
|
root.walkRules((rule) => {
|
||||||
character: start.column - 1,
|
if (change) return false
|
||||||
},
|
|
||||||
end: {
|
|
||||||
line: end.line - 1,
|
|
||||||
character: end.column - 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isWithinRange(diagnostic.range.start, range)) {
|
rule.walkAtRules('apply', (atRule) => {
|
||||||
// keep looking
|
let { start, end } = atRule.source
|
||||||
return true
|
let atRuleRange: Range = {
|
||||||
}
|
start: {
|
||||||
|
line: start.line - 1,
|
||||||
|
character: start.column - 1,
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
line: end.line - 1,
|
||||||
|
character: end.column - 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if (cssRange) {
|
||||||
|
atRuleRange = absoluteRange(atRuleRange, cssRange)
|
||||||
|
}
|
||||||
|
|
||||||
let className = document.getText(diagnostic.range)
|
if (!isWithinRange(diagnostic.range.start, atRuleRange)) {
|
||||||
let ast = classNameToAst(
|
// keep looking
|
||||||
state,
|
return true
|
||||||
className,
|
}
|
||||||
rule.selector,
|
|
||||||
diagnostic.className.classList.important
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!ast) {
|
let className = diagnostic.className.className
|
||||||
return false
|
let ast = classNameToAst(
|
||||||
}
|
state,
|
||||||
|
className,
|
||||||
|
rule.selector,
|
||||||
|
diagnostic.className.classList.important
|
||||||
|
)
|
||||||
|
|
||||||
rule.after(ast.nodes)
|
if (!ast) {
|
||||||
let insertedRule = rule.next()
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (totalClassNamesInClassList === 1) {
|
rule.after(ast.nodes)
|
||||||
atRule.remove()
|
let insertedRule = rule.next()
|
||||||
}
|
|
||||||
|
|
||||||
let outputIndent: string
|
if (totalClassNamesInClassList === 1) {
|
||||||
let documentIndent = detectIndent(documentText)
|
atRule.remove()
|
||||||
|
}
|
||||||
|
|
||||||
change = {
|
let outputIndent: string
|
||||||
range: {
|
let documentIndent = detectIndent(documentText)
|
||||||
|
|
||||||
|
let ruleRange: Range = {
|
||||||
start: {
|
start: {
|
||||||
line: rule.source.start.line - 1,
|
line: rule.source.start.line - 1,
|
||||||
character: rule.source.start.column - 1,
|
character: rule.source.start.column - 1,
|
||||||
|
@ -273,30 +290,39 @@ async function provideInvalidApplyCodeActions(
|
||||||
line: rule.source.end.line - 1,
|
line: rule.source.end.line - 1,
|
||||||
character: rule.source.end.column,
|
character: rule.source.end.column,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
newText:
|
if (cssRange) {
|
||||||
rule.toString() +
|
ruleRange = absoluteRange(ruleRange, cssRange)
|
||||||
(insertedRule.raws.before || '\n\n') +
|
}
|
||||||
insertedRule
|
|
||||||
.toString()
|
|
||||||
.replace(/\n\s*\n/g, '\n')
|
|
||||||
.replace(/(@apply [^;\n]+)$/gm, '$1;')
|
|
||||||
.replace(/([^\s^]){$/gm, '$1 {')
|
|
||||||
.replace(/^\s+/gm, (m: string) => {
|
|
||||||
if (typeof outputIndent === 'undefined') outputIndent = m
|
|
||||||
return m.replace(
|
|
||||||
new RegExp(outputIndent, 'g'),
|
|
||||||
documentIndent.indent
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
change = {
|
||||||
|
range: ruleRange,
|
||||||
|
newText:
|
||||||
|
rule.toString() +
|
||||||
|
(insertedRule.raws.before || '\n\n') +
|
||||||
|
insertedRule
|
||||||
|
.toString()
|
||||||
|
.replace(/\n\s*\n/g, '\n')
|
||||||
|
.replace(/(@apply [^;\n]+)$/gm, '$1;')
|
||||||
|
.replace(/([^\s^]){$/gm, '$1 {')
|
||||||
|
.replace(/^\s+/gm, (m: string) => {
|
||||||
|
if (typeof outputIndent === 'undefined') outputIndent = m
|
||||||
|
return m.replace(
|
||||||
|
new RegExp(outputIndent, 'g'),
|
||||||
|
documentIndent.indent
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
}
|
}),
|
||||||
}),
|
]).process(cssText, { from: undefined })
|
||||||
]).process(documentText, { from: undefined })
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
if (!change) {
|
if (!change) {
|
||||||
return []
|
return []
|
||||||
|
|
Loading…
Reference in New Issue