Skip to content

Commit 6878561

Browse files
committed
replace code action with formatting providers
1 parent acabecc commit 6878561

File tree

3 files changed

+161
-66
lines changed

3 files changed

+161
-66
lines changed

package-lock.json

Lines changed: 10 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@
2626
],
2727
"main": "./out/extension",
2828
"contributes": {
29+
"configuration": {
30+
"type": "object",
31+
"title": "Tailwind CSS IntelliSense configuration",
32+
"properties": {
33+
"tailwind.format.enable": {
34+
"type": "boolean",
35+
"default": false,
36+
"description": "Enable/disable Tailwind CSS class name formatter."
37+
}
38+
}
39+
},
2940
"grammars": [
3041
{
3142
"scopeName": "source.css.tailwind",
@@ -55,6 +66,7 @@
5566
"dependencies": {
5667
"color": "^3.0.0",
5768
"dlv": "^1.1.1",
69+
"line-column": "^1.0.2",
5870
"tailwind-class-names": "0.6.0"
5971
},
6072
"author": "Brad Cornes <bradlc41@gmail.com>",

src/extension.ts

Lines changed: 139 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const htmlElements = require('./htmlElements.js')
66
const tailwindClassNames = require('tailwind-class-names')
77
const dlv = require('dlv')
88
const Color = require('color')
9+
const _lineColumn = require('line-column')
910

1011
const CONFIG_GLOB =
1112
'**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js'
@@ -432,76 +433,67 @@ class TailwindIntellisense {
432433
this._providers = []
433434

434435
this._providers.push(
435-
vscode.commands.registerCommand(
436-
'tailwind.refactor-class-names',
437-
(document, range) => {
438-
let original = document.getText(range)
439-
440-
let order = [
441-
'hover',
442-
'group-hover',
443-
'active',
444-
'focus',
445-
'sm',
446-
'md',
447-
'lg',
448-
'xl'
449-
]
450-
451-
let next = original
452-
.split(/\s+/)
453-
.sort((a, b) => {
454-
let aPrefix = a.match(/^([^:]+):/)
455-
let bPrefix = b.match(/^([^:]+):/)
456-
if (!aPrefix && bPrefix) return -1
457-
if (!bPrefix && aPrefix) return 1
458-
if (!aPrefix && !bPrefix) return 0
459-
// at this point they both have a prefix
460-
aPrefix = aPrefix[1]
461-
bPrefix = bPrefix[1]
462-
463-
if (aPrefix === bPrefix) return 0
464-
465-
if (
466-
order.indexOf(aPrefix) === -1 &&
467-
order.indexOf(bPrefix) === -1
468-
)
469-
return 0
470-
if (
471-
order.indexOf(aPrefix) !== -1 &&
472-
order.indexOf(bPrefix) === -1
473-
)
474-
return -1
475-
if (
476-
order.indexOf(bPrefix) !== -1 &&
477-
order.indexOf(aPrefix) === -1
478-
)
479-
return 1
436+
vscode.languages.registerDocumentRangeFormattingEditProvider(HTML_TYPES, {
437+
provideDocumentRangeFormattingEdits(document, range) {
438+
if (
439+
!vscode.workspace.getConfiguration('tailwind').get('format.enable')
440+
) {
441+
return []
442+
}
443+
444+
let searchStart = new vscode.Position(
445+
Math.max(range.start.line - 5, 0),
446+
0
447+
)
448+
let searchEnd = new vscode.Position(range.end.line + 5, 0)
449+
450+
let ranges = findClassListsInRange(
451+
document,
452+
new vscode.Range(searchStart, searchEnd)
453+
)
480454

481-
return order.indexOf(aPrefix) - order.indexOf(bPrefix)
482-
})
483-
.join(' ')
455+
if (!ranges.some(r => r.contains(range))) return []
484456

485-
let edit = new vscode.WorkspaceEdit()
486-
edit.replace(document.uri, range, next)
487-
vscode.workspace.applyEdit(edit)
457+
return ranges.map(
458+
range =>
459+
new vscode.TextEdit(
460+
range,
461+
groupClassList(document.getText(range), tailwind.config)
462+
)
463+
)
488464
}
489-
)
465+
})
490466
)
491467

492468
this._providers.push(
493-
vscode.languages.registerCodeActionsProvider(HTML_TYPES, {
494-
provideCodeActions(document, range) {
495-
let action = new vscode.CodeAction(
496-
'Arrange class names',
497-
vscode.CodeActionKind.Refactor
498-
)
499-
action.command = {
500-
title: 'refactor-class-names',
501-
command: 'tailwind.refactor-class-names',
502-
arguments: [document, range]
469+
vscode.languages.registerDocumentFormattingEditProvider(HTML_TYPES, {
470+
provideDocumentFormattingEdits(document) {
471+
if (
472+
!vscode.workspace.getConfiguration('tailwind').get('format.enable')
473+
) {
474+
return []
503475
}
504-
return [action]
476+
477+
let firstLine = document.lineAt(0)
478+
let lastLine = document.lineAt(document.lineCount - 1)
479+
480+
let ranges = findClassListsInRange(
481+
document,
482+
new vscode.Range(
483+
0,
484+
firstLine.range.start.character,
485+
document.lineCount - 1,
486+
lastLine.range.end.character
487+
)
488+
)
489+
490+
return ranges.map(
491+
range =>
492+
new vscode.TextEdit(
493+
range,
494+
groupClassList(document.getText(range), tailwind.config)
495+
)
496+
)
505497
}
506498
})
507499
)
@@ -863,3 +855,87 @@ function createScreenCompletionItemProvider({
863855
' '
864856
)
865857
}
858+
859+
function findClassListsInRange(
860+
document: vscode.TextDocument,
861+
range: vscode.Range
862+
): vscode.Range[] {
863+
let text: string = document.getText(range)
864+
865+
let regex = /(class(Name)?=(['"]))([a-z0-9-_:|!\s]+)\3/gi
866+
let match
867+
let ranges = []
868+
while ((match = regex.exec(text)) !== null) {
869+
let start = regex.lastIndex - match[0].length + match[1].length
870+
let end = start + match[4].length
871+
start = lineColumn(text, start)
872+
end = lineColumn(text, end)
873+
ranges.push(
874+
new vscode.Range(
875+
new vscode.Position(
876+
range.start.line + start.line,
877+
range.start.character + start.col
878+
),
879+
new vscode.Position(
880+
range.start.line + end.line,
881+
range.start.character + end.col
882+
)
883+
)
884+
)
885+
}
886+
return ranges
887+
}
888+
889+
function groupClassList(str, twConfig) {
890+
let screens = []
891+
892+
try {
893+
screens = Object.keys(dlv(twConfig, 'screens', {}))
894+
} catch (_) {}
895+
896+
let states = ['hover', 'group-hover', 'active', 'focus']
897+
898+
let order = [
899+
...states,
900+
...flatten(
901+
screens.map(screen => [
902+
screen,
903+
...states.map(state => `${screen}:${state}`)
904+
])
905+
)
906+
]
907+
908+
return str
909+
.split(/\s+/)
910+
.sort((a, b) => {
911+
let aPrefix = a.match(/^(.*):/)
912+
let bPrefix = b.match(/^(.*):/)
913+
if (!aPrefix && bPrefix) return -1
914+
if (!bPrefix && aPrefix) return 1
915+
if (!aPrefix && !bPrefix) return 0
916+
// at this point they both have a prefix
917+
aPrefix = aPrefix[1]
918+
bPrefix = bPrefix[1]
919+
920+
if (aPrefix === bPrefix) return 0
921+
922+
if (order.indexOf(aPrefix) === -1 && order.indexOf(bPrefix) === -1)
923+
return 0
924+
if (order.indexOf(aPrefix) !== -1 && order.indexOf(bPrefix) === -1)
925+
return -1
926+
if (order.indexOf(bPrefix) !== -1 && order.indexOf(aPrefix) === -1)
927+
return 1
928+
929+
return order.indexOf(aPrefix) - order.indexOf(bPrefix)
930+
})
931+
.join(' ')
932+
}
933+
934+
function flatten(arr) {
935+
return arr.reduce((acc, val) => acc.concat(val), [])
936+
}
937+
938+
function lineColumn(str, index) {
939+
let { line, col } = _lineColumn(str, index)
940+
return { line: line - 1, col: col - 1 }
941+
}

0 commit comments

Comments
 (0)