Skip to content

Commit 6add64c

Browse files
committed
make "invalid @apply" quick fix work in mixed-language documents
1 parent a6a8f7e commit 6add64c

File tree

1 file changed

+89
-63
lines changed
  • src/lsp/providers/codeActionProvider

1 file changed

+89
-63
lines changed

src/lsp/providers/codeActionProvider/index.ts

Lines changed: 89 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
TextEdit,
77
} from 'vscode-languageserver'
88
import { State } from '../../util/state'
9-
import { findLast } from '../../util/find'
109
import { isWithinRange } from '../../util/isWithinRange'
1110
import { getClassNameParts } from '../../util/getClassNameAtPosition'
1211
const dlv = require('dlv')
@@ -31,6 +30,9 @@ import {
3130
} from '../diagnostics/types'
3231
import { flatten, dedupeBy } from '../../../util/array'
3332
import { joinWithAnd } from '../../util/joinWithAnd'
33+
import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
34+
import { isCssDoc } from '../../util/css'
35+
import { absoluteRange } from '../../util/absoluteRange'
3436

3537
async function getDiagnosticsFromCodeActionParams(
3638
state: State,
@@ -210,61 +212,76 @@ async function provideInvalidApplyCodeActions(
210212
): Promise<CodeAction[]> {
211213
let document = state.editor.documents.get(params.textDocument.uri)
212214
let documentText = document.getText()
215+
let cssRange: Range
216+
let cssText = documentText
213217
const { postcss } = state.modules
214218
let change: TextEdit
215219

216220
let totalClassNamesInClassList = diagnostic.className.classList.classList.split(
217221
/\s+/
218222
).length
219223

220-
await postcss([
221-
postcss.plugin('', (_options = {}) => {
222-
return (root) => {
223-
root.walkRules((rule) => {
224-
if (change) return false
224+
if (!isCssDoc(state, document)) {
225+
let languageBoundaries = getLanguageBoundaries(state, document)
226+
if (!languageBoundaries) return []
227+
cssRange = languageBoundaries.css.find((range) =>
228+
isWithinRange(diagnostic.range.start, range)
229+
)
230+
if (!cssRange) return []
231+
cssText = document.getText(cssRange)
232+
}
225233

226-
rule.walkAtRules('apply', (atRule) => {
227-
let { start, end } = atRule.source
228-
let range: Range = {
229-
start: {
230-
line: start.line - 1,
231-
character: start.column - 1,
232-
},
233-
end: {
234-
line: end.line - 1,
235-
character: end.column - 1,
236-
},
237-
}
234+
try {
235+
await postcss([
236+
postcss.plugin('', (_options = {}) => {
237+
return (root) => {
238+
root.walkRules((rule) => {
239+
if (change) return false
240+
241+
rule.walkAtRules('apply', (atRule) => {
242+
let { start, end } = atRule.source
243+
let atRuleRange: Range = {
244+
start: {
245+
line: start.line - 1,
246+
character: start.column - 1,
247+
},
248+
end: {
249+
line: end.line - 1,
250+
character: end.column - 1,
251+
},
252+
}
253+
if (cssRange) {
254+
atRuleRange = absoluteRange(atRuleRange, cssRange)
255+
}
238256

239-
if (!isWithinRange(diagnostic.range.start, range)) {
240-
// keep looking
241-
return true
242-
}
257+
if (!isWithinRange(diagnostic.range.start, atRuleRange)) {
258+
// keep looking
259+
return true
260+
}
243261

244-
let className = document.getText(diagnostic.range)
245-
let ast = classNameToAst(
246-
state,
247-
className,
248-
rule.selector,
249-
diagnostic.className.classList.important
250-
)
262+
let className = diagnostic.className.className
263+
let ast = classNameToAst(
264+
state,
265+
className,
266+
rule.selector,
267+
diagnostic.className.classList.important
268+
)
251269

252-
if (!ast) {
253-
return false
254-
}
270+
if (!ast) {
271+
return false
272+
}
255273

256-
rule.after(ast.nodes)
257-
let insertedRule = rule.next()
274+
rule.after(ast.nodes)
275+
let insertedRule = rule.next()
258276

259-
if (totalClassNamesInClassList === 1) {
260-
atRule.remove()
261-
}
277+
if (totalClassNamesInClassList === 1) {
278+
atRule.remove()
279+
}
262280

263-
let outputIndent: string
264-
let documentIndent = detectIndent(documentText)
281+
let outputIndent: string
282+
let documentIndent = detectIndent(documentText)
265283

266-
change = {
267-
range: {
284+
let ruleRange: Range = {
268285
start: {
269286
line: rule.source.start.line - 1,
270287
character: rule.source.start.column - 1,
@@ -273,30 +290,39 @@ async function provideInvalidApplyCodeActions(
273290
line: rule.source.end.line - 1,
274291
character: rule.source.end.column,
275292
},
276-
},
277-
newText:
278-
rule.toString() +
279-
(insertedRule.raws.before || '\n\n') +
280-
insertedRule
281-
.toString()
282-
.replace(/\n\s*\n/g, '\n')
283-
.replace(/(@apply [^;\n]+)$/gm, '$1;')
284-
.replace(/([^\s^]){$/gm, '$1 {')
285-
.replace(/^\s+/gm, (m: string) => {
286-
if (typeof outputIndent === 'undefined') outputIndent = m
287-
return m.replace(
288-
new RegExp(outputIndent, 'g'),
289-
documentIndent.indent
290-
)
291-
}),
292-
}
293+
}
294+
if (cssRange) {
295+
ruleRange = absoluteRange(ruleRange, cssRange)
296+
}
297+
298+
change = {
299+
range: ruleRange,
300+
newText:
301+
rule.toString() +
302+
(insertedRule.raws.before || '\n\n') +
303+
insertedRule
304+
.toString()
305+
.replace(/\n\s*\n/g, '\n')
306+
.replace(/(@apply [^;\n]+)$/gm, '$1;')
307+
.replace(/([^\s^]){$/gm, '$1 {')
308+
.replace(/^\s+/gm, (m: string) => {
309+
if (typeof outputIndent === 'undefined') outputIndent = m
310+
return m.replace(
311+
new RegExp(outputIndent, 'g'),
312+
documentIndent.indent
313+
)
314+
}),
315+
}
293316

294-
return false
317+
return false
318+
})
295319
})
296-
})
297-
}
298-
}),
299-
]).process(documentText, { from: undefined })
320+
}
321+
}),
322+
]).process(cssText, { from: undefined })
323+
} catch (_) {
324+
return []
325+
}
300326

301327
if (!change) {
302328
return []

0 commit comments

Comments
 (0)