Files
gulie/gulie/config.scm
2026-04-01 23:35:50 +02:00

130 lines
3.7 KiB
Scheme

;;; (gulie config) — configuration loading and merging
;;;
;;; Reads .gulie.sexp from project root (or CLI-specified path),
;;; merges with built-in defaults, and provides config accessors.
(define-module (gulie config)
#:use-module (ice-9 rdelim)
#:use-module (srfi srfi-1)
#:export (default-config
load-config
merge-configs
config-ref
config-line-length
config-indent-width
config-max-blank-lines
config-enabled-rules
config-disabled-rules
config-ignore-patterns
config-indent-rules
generate-template))
(define default-config
'((line-length . 80)
(indent . 2)
(max-blank-lines . 2)
(enable . ())
(disable . ())
(ignore . ())
(indent-rules
(define . 1)
(define* . 1)
(define-public . 1)
(define-syntax . 1)
(define-syntax-rule . 1)
(define-module . 0)
(define-record-type . 1)
(lambda . 1)
(lambda* . 1)
(let . 1)
(let* . 1)
(letrec . 1)
(letrec* . 1)
(let-values . 1)
(if . special)
(cond . 0)
(case . 1)
(when . 1)
(unless . 1)
(match . 1)
(match-lambda . 0)
(match-lambda* . 0)
(syntax-case . 2)
(syntax-rules . 1)
(with-syntax . 1)
(begin . 0)
(do . 2)
(parameterize . 1)
(guard . 1)
(with-exception-handler . 1)
(call-with-values . 1)
(receive . 2)
(use-modules . 0)
(with-fluids . 1)
(dynamic-wind . 0))))
(define (config-ref config key . default)
"Look up KEY in CONFIG alist, returning DEFAULT if not found."
(let ((pair (assq key config)))
(if pair
(cdr pair)
(if (null? default) #f (car default)))))
(define (config-line-length config)
(or (config-ref config 'line-length) 80))
(define (config-indent-width config)
(or (config-ref config 'indent) 2))
(define (config-max-blank-lines config)
(or (config-ref config 'max-blank-lines) 2))
(define (config-enabled-rules config)
(or (config-ref config 'enable) '()))
(define (config-disabled-rules config)
(or (config-ref config 'disable) '()))
(define (config-ignore-patterns config)
(or (config-ref config 'ignore) '()))
(define (config-indent-rules config)
(or (config-ref config 'indent-rules) '()))
(define (load-config path)
"Load a .gulie.sexp config file at PATH. Returns an alist."
(if (and path (file-exists? path))
(call-with-input-file path
(lambda (port)
(let ((data (read port)))
(if (list? data) data '()))))
'()))
(define (merge-configs base override)
"Merge OVERRIDE config on top of BASE. Override wins for scalar values;
lists are replaced, not appended."
(let lp ((result base)
(pairs override))
(if (null? pairs)
result
(let ((pair (car pairs)))
(lp (assq-set! (list-copy result) (car pair) (cdr pair))
(cdr pairs))))))
(define (find-config-file start-dir)
"Search upward from START-DIR for .gulie.sexp. Returns path or #f."
(let lp ((dir start-dir))
(let ((candidate (string-append dir "/.gulie.sexp")))
(cond
((file-exists? candidate) candidate)
((string=? dir "/") #f)
(else (lp (dirname dir)))))))
(define (generate-template port)
"Write a template .gulie.sexp to PORT."
(display ";;; gulie configuration\n" port)
(display ";;; Place this file as .gulie.sexp in your project root.\n" port)
(display ";;; All fields are optional — defaults are shown below.\n\n" port)
(write default-config port)
(newline port))