Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/vscode-tailwindcss/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
"command": "tailwindCSS.sortSelection",
"title": "Tailwind CSS: Sort Selection",
"enablement": "editorHasSelection && (resourceScheme == file || resourceScheme == vscode-remote) && tailwindCSS.activeTextEditorSupportsClassSorting"
},
{
"command": "tailwindCSS.applyAllCanonicalClasses",
"title": "Tailwind CSS: Apply All Canonical Class Suggestions",
"enablement": "editorTextFocus && (resourceScheme == file || resourceScheme == vscode-remote)"
}
],
"grammars": [
Expand Down
76 changes: 76 additions & 0 deletions packages/vscode-tailwindcss/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import {
SymbolInformation,
Position,
Range,
languages,
WorkspaceEdit,
TextEdit,
} from 'vscode'
import type {
DocumentFilter,
Expand Down Expand Up @@ -205,6 +208,79 @@ export async function activate(context: ExtensionContext) {
}),
)

async function applyAllCanonicalClasses(): Promise<void> {
if (!Window.activeTextEditor) {
return
}

let { document } = Window.activeTextEditor
let folder = Workspace.getWorkspaceFolder(document.uri)

if (!folder || isExcluded(document.uri.fsPath, folder)) {
return
}

// Get all diagnostics for the current document
let allDiagnostics = languages.getDiagnostics(document.uri)

// Filter for SuggestCanonicalClasses diagnostics
// The diagnostic code will be 'suggestCanonicalClasses' based on DiagnosticKind enum
let canonicalDiagnostics = allDiagnostics.filter(
(diagnostic) => diagnostic.code === 'suggestCanonicalClasses',
)

if (canonicalDiagnostics.length === 0) {
Window.showInformationMessage('No canonical class suggestions found in this document.')
return
}

// Create a workspace edit to apply all suggestions
let workspaceEdit = new WorkspaceEdit()
let edits: TextEdit[] = []

// Sort diagnostics by position (from end to start) to avoid range invalidation
canonicalDiagnostics.sort((a, b) => {
if (a.range.start.line !== b.range.start.line) {
return b.range.start.line - a.range.start.line
}
return b.range.start.character - a.range.start.character
})

Comment on lines +241 to +248
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sorting logic here is redundant when using WorkspaceEdit.set(). VS Code automatically handles the application order of multiple text edits to prevent range invalidation, so sorting from end to start is unnecessary. Consider removing this sorting or adding a comment explaining why it's needed if there's a specific reason.

Suggested change
// Sort diagnostics by position (from end to start) to avoid range invalidation
canonicalDiagnostics.sort((a, b) => {
if (a.range.start.line !== b.range.start.line) {
return b.range.start.line - a.range.start.line
}
return b.range.start.character - a.range.start.character
})

Copilot uses AI. Check for mistakes.
for (let diagnostic of canonicalDiagnostics) {
// Access the canonical class suggestion directly from the suggestions property
// Type assertion needed since languages.getDiagnostics returns vscode.Diagnostic
let canonicalClass = (diagnostic as any).suggestions?.[0]
if (canonicalClass) {
edits.push(TextEdit.replace(diagnostic.range, canonicalClass))
}
}
Comment on lines +249 to +256
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When diagnostics don't have a suggestions property (or it's empty), those diagnostics will be silently skipped, potentially causing a mismatch between the number of diagnostics found and the number of suggestions applied. Consider tracking skipped diagnostics and warning the user if some suggestions couldn't be applied:

let skipped = 0
for (let diagnostic of canonicalDiagnostics) {
  let canonicalClass = (diagnostic as any).suggestions?.[0]
  if (canonicalClass) {
    edits.push(TextEdit.replace(diagnostic.range, canonicalClass))
  } else {
    skipped++
  }
}

if (skipped > 0) {
  Window.showWarningMessage(
    `Applied ${edits.length} suggestion${edits.length === 1 ? '' : 's'}, but ${skipped} could not be processed.`
  )
}

Copilot uses AI. Check for mistakes.

workspaceEdit.set(document.uri, edits)

// Apply the workspace edit
let success = await Workspace.applyEdit(workspaceEdit)

if (success) {
let count = edits.length
Window.showInformationMessage(
`Applied ${count} canonical class suggestion${count === 1 ? '' : 's'}.`,
)
} else {
Window.showWarningMessage('Failed to apply canonical class suggestions.')
}
}

context.subscriptions.push(
commands.registerCommand('tailwindCSS.applyAllCanonicalClasses', async () => {
try {
await applyAllCanonicalClasses()
} catch (error) {
let message = error instanceof Error ? error.message : 'Unknown error'
Window.showWarningMessage(`Couldn't apply canonical class suggestions: ${message}`)
}
}),
)

context.subscriptions.push(
Window.onDidChangeActiveTextEditor(async () => {
await updateActiveTextEditorContext()
Expand Down