Skip to content

Commit 4749af6

Browse files
committed
Add ShellCheck autofix
1 parent 9e00c2d commit 4749af6

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

index.html

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ <h1 class="text-3xl font-semibold mb-2">Bash Script Tools</h1>
5858
>
5959
<span>Lint</span>
6060
</button>
61+
62+
<button
63+
onclick="autofixCode()"
64+
class="inline-flex items-center px-4 py-2 bg-zinc-900 text-zinc-100 rounded-md hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-zinc-700 transition-colors font-medium text-sm border border-zinc-800"
65+
>
66+
<span>Autofix</span>
67+
</button>
6168
</div>
6269

6370
<!-- Output Box -->
@@ -146,6 +153,28 @@ <h1 class="text-3xl font-semibold mb-2">Bash Script Tools</h1>
146153
'<div class="text-xs text-red-600 font-mono">Error running ShellCheck</div>';
147154
}
148155
}
156+
157+
async function autofixCode() {
158+
const code = editor.getValue();
159+
160+
try {
161+
const response = await fetch("/autofix", {
162+
method: "POST",
163+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
164+
body: "code=" + encodeURIComponent(code),
165+
});
166+
167+
const fixedCode = await response.text();
168+
const cursor = editor.getCursorPosition();
169+
editor.setValue(fixedCode, -1);
170+
editor.moveCursorToPosition(cursor);
171+
172+
// Run shellcheck again to update annotations
173+
await checkCode();
174+
} catch (error) {
175+
console.error("Autofix error:", error);
176+
}
177+
}
149178
</script>
150179
</body>
151180
</html>

main.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ func main() {
5454
http.HandleFunc("/", handleIndex)
5555
http.HandleFunc("/format", handleFormat)
5656
http.HandleFunc("/shellcheck", handleShellcheck)
57+
http.HandleFunc("/autofix", handleAutofix)
5758

5859
port := getEnvOrDefault("PORT", "8080")
5960
log.Printf("Server starting on http://localhost:%s", port)
@@ -221,6 +222,64 @@ func parseShellcheckOutput(output string) []Annotation {
221222
return annotations
222223
}
223224

225+
func handleAutofix(w http.ResponseWriter, r *http.Request) {
226+
if r.Method != http.MethodPost {
227+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
228+
return
229+
}
230+
231+
code := r.FormValue("code")
232+
if code == "" {
233+
w.Write([]byte(code))
234+
return
235+
}
236+
237+
// Create temporary file for shellcheck
238+
tmpFile := filepath.Join(os.TempDir(), "script.sh")
239+
if err := os.WriteFile(tmpFile, []byte(code), 0644); err != nil {
240+
log.Printf("autofix error: %v", err)
241+
w.Write([]byte(code))
242+
return
243+
}
244+
defer os.Remove(tmpFile)
245+
246+
// Run shellcheck with --format=diff to get fixes
247+
cmd := exec.Command(shellcheckPath, "-f", "diff", tmpFile)
248+
var out, stderr bytes.Buffer
249+
cmd.Stdout = &out
250+
cmd.Stderr = &stderr
251+
cmd.Run()
252+
253+
diff := out.String()
254+
if diff == "" {
255+
// No fixes available, return original code
256+
w.Write([]byte(code))
257+
return
258+
}
259+
260+
// Apply the diff using patch
261+
patchCmd := exec.Command("patch", tmpFile)
262+
patchCmd.Stdin = bytes.NewBufferString(diff)
263+
var patchStderr bytes.Buffer
264+
patchCmd.Stderr = &patchStderr
265+
266+
if err := patchCmd.Run(); err != nil {
267+
log.Printf("patch error: %v - %s", err, patchStderr.String())
268+
w.Write([]byte(code))
269+
return
270+
}
271+
272+
// Read the fixed file
273+
fixed, err := os.ReadFile(tmpFile)
274+
if err != nil {
275+
log.Printf("read error: %v", err)
276+
w.Write([]byte(code))
277+
return
278+
}
279+
280+
w.Write(fixed)
281+
}
282+
224283
func formatShellcheckHTML(output string) string {
225284
if output == "" {
226285
return `<div class="text-sm text-green-600">✓ No issues found</div>`

0 commit comments

Comments
 (0)