Skip to content

Commit 01941f9

Browse files
committed
add initial quick fix for utility conflict diagnostic
1 parent b79dbfc commit 01941f9

File tree

2 files changed

+125
-55
lines changed

2 files changed

+125
-55
lines changed

src/lsp/providers/codeActionProvider/index.ts

Lines changed: 115 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import {
2222
isInvalidApplyDiagnostic,
2323
AugmentedDiagnostic,
2424
InvalidApplyDiagnostic,
25+
isUtilityConflictsDiagnostic,
26+
UtilityConflictsDiagnostic,
2527
} from '../diagnostics/types'
28+
import { flatten, dedupeBy } from '../../../util/array'
2629

2730
async function getDiagnosticsFromCodeActionParams(
2831
state: State,
@@ -35,7 +38,11 @@ async function getDiagnosticsFromCodeActionParams(
3538
return params.context.diagnostics
3639
.map((diagnostic) => {
3740
return diagnostics.find((d) => {
38-
return rangesEqual(d.range, diagnostic.range)
41+
return (
42+
d.code === diagnostic.code &&
43+
d.message === diagnostic.message &&
44+
rangesEqual(d.range, diagnostic.range)
45+
)
3946
})
4047
})
4148
.filter(Boolean)
@@ -55,40 +62,46 @@ export async function provideCodeActions(
5562
codes
5663
)
5764

58-
return Promise.all(
59-
diagnostics
60-
.map((diagnostic) => {
61-
if (isInvalidApplyDiagnostic(diagnostic)) {
62-
return provideInvalidApplyCodeAction(state, params, diagnostic)
63-
}
65+
let actions = diagnostics.map((diagnostic) => {
66+
if (isInvalidApplyDiagnostic(diagnostic)) {
67+
return provideInvalidApplyCodeActions(state, params, diagnostic)
68+
}
6469

65-
let match = findLast(
66-
/ Did you mean (?:something like )?'(?<replacement>[^']+)'\?$/g,
67-
diagnostic.message
68-
)
70+
if (isUtilityConflictsDiagnostic(diagnostic)) {
71+
return provideUtilityConflictsCodeActions(state, params, diagnostic)
72+
}
6973

70-
if (!match) {
71-
return null
72-
}
74+
let match = findLast(
75+
/ Did you mean (?:something like )?'(?<replacement>[^']+)'\?$/g,
76+
diagnostic.message
77+
)
7378

74-
return {
75-
title: `Replace with '${match.groups.replacement}'`,
76-
kind: CodeActionKind.QuickFix,
77-
diagnostics: [diagnostic],
78-
edit: {
79-
changes: {
80-
[params.textDocument.uri]: [
81-
{
82-
range: diagnostic.range,
83-
newText: match.groups.replacement,
84-
},
85-
],
86-
},
79+
if (!match) {
80+
return []
81+
}
82+
83+
return [
84+
{
85+
title: `Replace with '${match.groups.replacement}'`,
86+
kind: CodeActionKind.QuickFix,
87+
diagnostics: [diagnostic],
88+
edit: {
89+
changes: {
90+
[params.textDocument.uri]: [
91+
{
92+
range: diagnostic.range,
93+
newText: match.groups.replacement,
94+
},
95+
],
8796
},
88-
}
89-
})
90-
.filter(Boolean)
91-
)
97+
},
98+
},
99+
]
100+
})
101+
102+
return Promise.all(actions)
103+
.then(flatten)
104+
.then((x) => dedupeBy(x, (item) => JSON.stringify(item.edit)))
92105
}
93106

94107
function classNameToAst(
@@ -154,11 +167,56 @@ function classNameToAst(
154167
return cssObjToAst(obj, state.modules.postcss)
155168
}
156169

157-
async function provideInvalidApplyCodeAction(
170+
async function provideUtilityConflictsCodeActions(
171+
state: State,
172+
params: CodeActionParams,
173+
diagnostic: UtilityConflictsDiagnostic
174+
): Promise<CodeAction[]> {
175+
return [
176+
{
177+
title: `Delete '${diagnostic.className.className}'`,
178+
kind: CodeActionKind.QuickFix,
179+
diagnostics: [diagnostic],
180+
edit: {
181+
changes: {
182+
[params.textDocument.uri]: [
183+
{
184+
range: diagnostic.className.classList.range,
185+
newText: removeRangeFromString(
186+
diagnostic.className.classList.classList,
187+
diagnostic.className.relativeRange
188+
),
189+
},
190+
],
191+
},
192+
},
193+
},
194+
{
195+
title: `Delete '${diagnostic.otherClassName.className}'`,
196+
kind: CodeActionKind.QuickFix,
197+
diagnostics: [diagnostic],
198+
edit: {
199+
changes: {
200+
[params.textDocument.uri]: [
201+
{
202+
range: diagnostic.className.classList.range,
203+
newText: removeRangeFromString(
204+
diagnostic.className.classList.classList,
205+
diagnostic.otherClassName.relativeRange
206+
),
207+
},
208+
],
209+
},
210+
},
211+
},
212+
]
213+
}
214+
215+
async function provideInvalidApplyCodeActions(
158216
state: State,
159217
params: CodeActionParams,
160218
diagnostic: InvalidApplyDiagnostic
161-
): Promise<CodeAction> {
219+
): Promise<CodeAction[]> {
162220
let document = state.editor.documents.get(params.textDocument.uri)
163221
let documentText = document.getText()
164222
const { postcss } = state.modules
@@ -250,30 +308,32 @@ async function provideInvalidApplyCodeAction(
250308
]).process(documentText, { from: undefined })
251309

252310
if (!change) {
253-
return null
311+
return []
254312
}
255313

256-
return {
257-
title: 'Extract to new rule.',
258-
kind: CodeActionKind.QuickFix,
259-
diagnostics: [diagnostic],
260-
edit: {
261-
changes: {
262-
[params.textDocument.uri]: [
263-
...(totalClassNamesInClassList > 1
264-
? [
265-
{
266-
range: diagnostic.className.classList.range,
267-
newText: removeRangeFromString(
268-
diagnostic.className.classList.classList,
269-
diagnostic.className.relativeRange
270-
),
271-
},
272-
]
273-
: []),
274-
change,
275-
],
314+
return [
315+
{
316+
title: 'Extract to new rule',
317+
kind: CodeActionKind.QuickFix,
318+
diagnostics: [diagnostic],
319+
edit: {
320+
changes: {
321+
[params.textDocument.uri]: [
322+
...(totalClassNamesInClassList > 1
323+
? [
324+
{
325+
range: diagnostic.className.classList.range,
326+
newText: removeRangeFromString(
327+
diagnostic.className.classList.classList,
328+
diagnostic.className.relativeRange
329+
),
330+
},
331+
]
332+
: []),
333+
change,
334+
],
335+
},
276336
},
277337
},
278-
}
338+
]
279339
}

src/util/array.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@ export function dedupe<T>(arr: Array<T>): Array<T> {
22
return arr.filter((value, index, self) => self.indexOf(value) === index)
33
}
44

5+
export function dedupeBy<T>(
6+
arr: Array<T>,
7+
transform: (item: T) => any
8+
): Array<T> {
9+
return arr.filter(
10+
(value, index, self) =>
11+
self.map(transform).indexOf(transform(value)) === index
12+
)
13+
}
14+
515
export function ensureArray<T>(value: T | T[]): T[] {
616
return Array.isArray(value) ? value : [value]
717
}

0 commit comments

Comments
 (0)