diff --git a/gulie/cli.scm b/gulie/cli.scm index 46f1b15..b6e2ef2 100644 --- a/gulie/cli.scm +++ b/gulie/cli.scm @@ -92,6 +92,10 @@ (let* ((config-path (option-ref options 'config #f)) (user-config (load-config config-path)) (config (merge-configs default-config user-config)) + ;; Wire up --fix + (config (if (option-ref options 'fix #f) + (cons '(%fix . #t) config) + config)) ;; Wire up --pass (pass-str (option-ref options 'pass #f)) (config (if pass-str diff --git a/gulie/engine.scm b/gulie/engine.scm index ad4ba22..721287b 100644 --- a/gulie/engine.scm +++ b/gulie/engine.scm @@ -127,24 +127,73 @@ and '%min-severity with value 'error, 'warning, or 'info (default)." ;; Sort by location (sort diagnostics diagnosticvector (string-split text #\newline))) + (count 0)) + ;; Apply fixes: each fix is a replace-line, apply by line number + ;; Process in reverse line order so indices stay valid + (for-each + (lambda (fix-pair) + (let* ((line-num (car fix-pair)) + (fix (cdr fix-pair)) + (idx (1- line-num))) + (when (and (>= idx 0) (< idx (vector-length lines)) + (eq? (fix-type fix) 'replace-line)) + (vector-set! lines idx (fix-replacement fix)) + (set! count (1+ count))))) + (sort fixes (lambda (a b) (> (car a) (car b))))) + ;; Write back + (when (> count 0) + (call-with-output-file file + (lambda (port) + (let ((len (vector-length lines))) + (let lp ((i 0)) + (when (< i len) + (display (vector-ref lines i) port) + (when (< i (1- len)) + (newline port)) + (lp (1+ i)))))))) + count)))) (define (lint-files files config) "Lint multiple FILES in parallel. Returns total diagnostic count. Uses n-par-map to distribute work across threads, then outputs -diagnostics sequentially to maintain deterministic file order." +diagnostics sequentially to maintain deterministic file order. +When '%fix is #t in config, apply auto-fixes and report unfixed." (let* ((ncpus (max 1 (total-processor-count))) + (fix-mode? (assq-ref config '%fix)) (results (n-par-map ncpus (lambda (file) (cons file (lint-file file config))) files)) - (total 0)) - ;; Output in original file order (n-par-map preserves it) + (total-diags 0) + (total-fixed 0)) (for-each (lambda (result) - (let ((diags (cdr result))) - (set! total (+ total (length diags))) - (format-diagnostics diags (current-output-port)))) + (let* ((file (car result)) + (diags (cdr result))) + (if fix-mode? + ;; In fix mode: apply fixes, then report only unfixed diagnostics + (let* ((fixed-count (apply-fixes-to-file file diags)) + (unfixed (filter (lambda (d) (not (diagnostic-fix d))) diags))) + (set! total-fixed (+ total-fixed fixed-count)) + (set! total-diags (+ total-diags (length unfixed))) + (format-diagnostics unfixed (current-output-port))) + ;; Normal mode: report everything + (begin + (set! total-diags (+ total-diags (length diags))) + (format-diagnostics diags (current-output-port)))))) results) - total)) + (when (and fix-mode? (> total-fixed 0)) + (format (current-error-port) "Fixed ~a issue~a.~%" + total-fixed (if (= total-fixed 1) "" "s"))) + total-diags)) (define (scheme-file? path) "Is PATH a Scheme source file?" diff --git a/gulie/rules/comments.scm b/gulie/rules/comments.scm index 1ec6629..233f18c 100644 --- a/gulie/rules/comments.scm +++ b/gulie/rules/comments.scm @@ -54,18 +54,30 @@ ;; Inline comment (after code) should use single ; ;; But we don't enforce this strictly — just flag ;;; or more inline ((and (not own-line?) (>= semis 3)) - (list (make-diagnostic - file line-num pos - 'info 'comment-semicolons - "inline comments should use ; or ;; not ;;;" - #f))) + (let ((fixed (string-append + (substring line-text 0 pos) + ";;" + (substring line-text (+ pos semis))))) + (list (make-diagnostic + file line-num pos + 'info 'comment-semicolons + "inline comments should use ; or ;; not ;;;" + (make-fix 'replace-line line-num 0 + line-num (string-length line-text) + fixed))))) ;; Own-line comment with single ; (should be ;;) ((and own-line? (= semis 1) (> (string-length line-text) (1+ pos)) (not (char=? (string-ref line-text (1+ pos)) #\!))) - (list (make-diagnostic - file line-num pos - 'info 'comment-semicolons - "line comments should use ;; not ;" - #f))) + (let ((fixed (string-append + (substring line-text 0 pos) + ";;" + (substring line-text (1+ pos))))) + (list (make-diagnostic + file line-num pos + 'info 'comment-semicolons + "line comments should use ;; not ;" + (make-fix 'replace-line line-num 0 + line-num (string-length line-text) + fixed))))) (else '())))))))) #f))