Skip to content
Merged
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
103 changes: 103 additions & 0 deletions htmlhint-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,105 @@ function createAttrNoDuplicationFix(
};
}

/**
* Create auto-fix action for form-method-require rule
*/
function createFormMethodRequireFix(
document: TextDocument,
diagnostic: Diagnostic,
): CodeAction | null {
trace(
`[DEBUG] createFormMethodRequireFix called with diagnostic: ${JSON.stringify(diagnostic)}`,
);

if (!diagnostic.data || diagnostic.data.ruleId !== "form-method-require") {
trace(
`[DEBUG] createFormMethodRequireFix: Invalid diagnostic data or ruleId`,
);
return null;
}

const text = document.getText();
const diagnosticOffset = document.offsetAt(diagnostic.range.start);

// Use robust tag boundary detection to find the form tag
const tagBoundaries = findTagBoundaries(text, diagnosticOffset);
if (!tagBoundaries) {
trace(`[DEBUG] createFormMethodRequireFix: Could not find tag boundaries`);
return null;
}

const { tagStart, tagEnd } = tagBoundaries;
const tagContent = text.substring(tagStart, tagEnd + 1);
trace(`[DEBUG] createFormMethodRequireFix: Found tag: ${tagContent}`);

// Verify this is a form tag
const formTagMatch = tagContent.match(/^<\s*form\b/i);
if (!formTagMatch) {
trace(`[DEBUG] createFormMethodRequireFix: Not a form tag`);
return null;
}

// Check if method attribute already exists
const methodAttrMatch = tagContent.match(/\bmethod\s*=/i);
if (methodAttrMatch) {
trace(
`[DEBUG] createFormMethodRequireFix: Method attribute already exists`,
);
return null;
}

// Find the best position to insert the method attribute
// We'll add it after the opening form tag name but before the closing >
const formMatch = tagContent.match(/^(<\s*form)(\s+[^>]*?)?(\/?\s*>)$/i);
if (!formMatch) {
trace(
`[DEBUG] createFormMethodRequireFix: Could not parse form tag structure`,
);
return null;
}

const beforeAttrs = formMatch[1]; // "<form"
const existingAttrs = formMatch[2] || ""; // existing attributes

// Calculate insertion position
const newText = ' method=""';
let insertPosition: number;

if (existingAttrs.trim()) {
// There are existing attributes, add method after them
insertPosition = tagStart + beforeAttrs.length + existingAttrs.length;
} else {
// No existing attributes, add method right after "form"
insertPosition = tagStart + beforeAttrs.length;
}

const edit: TextEdit = {
range: {
start: document.positionAt(insertPosition),
end: document.positionAt(insertPosition),
},
newText: newText,
};

trace(
`[DEBUG] createFormMethodRequireFix: Will insert "${newText}" at position ${insertPosition}`,
);

const workspaceEdit: WorkspaceEdit = {
changes: {
[document.uri]: [edit],
},
};

return {
title: 'Add method="" attribute to form',
kind: CodeActionKind.QuickFix,
edit: workspaceEdit,
isPreferred: true,
};
}

/**
* Create auto-fix actions for supported rules
*/
Expand Down Expand Up @@ -2064,6 +2163,10 @@ async function createAutoFixes(
trace(`[DEBUG] Calling createAttrNoDuplicationFix`);
fix = createAttrNoDuplicationFix(document, diagnostic);
break;
case "form-method-require":
trace(`[DEBUG] Calling createFormMethodRequireFix`);
fix = createFormMethodRequireFix(document, diagnostic);
break;
default:
trace(`[DEBUG] No autofix function found for rule: ${ruleId}`);
break;
Expand Down
1 change: 1 addition & 0 deletions htmlhint/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All notable changes to the "vscode-htmlhint" extension will be documented in thi
### v1.14.0 (2025-11-26)

- Add autofix for the `attr-no-duplication` rule
- Add autofix for the `form-method-require` rule

### v1.13.0 (2025-11-25)

Expand Down
1 change: 1 addition & 0 deletions htmlhint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The extension provides automatic fixes for many common HTML issues. Currently su
- **`button-type-require`** - Adds type attribute to buttons
- **`doctype-first`** - Adds DOCTYPE declaration at the beginning
- **`doctype-html5`** - Updates DOCTYPE to HTML5
- **`form-method-require`** - Adds empty method attribute to forms
- **`html-lang-require`** - Adds `lang` attribute to `<html>` tag
- **`meta-charset-require`** - Adds charset meta tag
- **`meta-description-require`** - Adds description meta tag
Expand Down