Add a diff driver for Scheme (R5RS and R6RS) which recognizes top level and local `define` forms, whether it is a function definition, binding, syntax definition or a user-defined `define-xyzzy` form. The rationale for picking `define` forms for the hunk headers is because it is usually the only significant form for defining the structure of the program, and it is a common pattern for schemers to have local function definitions to hide their visibility, so it is not only the top level `define`'s that are of interest. Schemers also extend the language with macros to provide their own define forms (for example, something like a `define-test-suite`) which is also captured in the hunk header. The word regex is a best-effort attempt to conform to R6RS[1] valid identifiers, symbols and numbers. [1] http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-7.html#node_chap_4 Signed-off-by: Atharva Raykar <raykar.ath@xxxxxxxxx> --- Hi, first-time contributor here, I wanted to have a go at this as a microproject. A few things I had to consider: - Going through the mailing list, there have already been two other patches that are for lispy languages that have taken slightly different approaches: Elisp[1] and Clojure[2]. Would it make any sense to have a single userdiff driver for lisp that just captures all top level forms in the hunk? I personally felt it's better to differentiate the drivers for each language, as they have different constructs. - It was hard to decide exactly which forms should appear on the hunk headers. Having programmed in a Scheme before, I went with the forms I would have liked to see when looking at git diffs, which would be the nearest `define` along with `define-syntax` and other define forms that are created as user-defined macros. I am willing to ask around in certain active scheme communities for some kind of consensus, but there is no single large consolidated group of schemers (the closest is probably comp.lang.scheme?). - By best-effort attempt at the wordregex, I mean that it is a little more permissive than it has to be, as it accepts a few words that are technically invalid in Scheme. Making it handle all cases like numbers and identifiers with separate regexen would be greatly complicated (Eg: #x#e10.2f3 is a valid number but #x#f10.2e3 is not; 10t1 is a valid identifier, but 10s1 is a number -- my wordregex just clubs all of these into a generic 'word match' which trades of granularity for simplicity, and it usually does the right thing). [1] http://public-inbox.org/git/20210213192447.6114-1-git@xxxxxxxxxxxxxx/ [2] http://public-inbox.org/git/pull.902.git.1615667191368.gitgitgadget@xxxxxxxxx/ Documentation/gitattributes.txt | 2 ++ t/t4018-diff-funcname.sh | 1 + t/t4018/scheme-define-syntax | 8 ++++++++ t/t4018/scheme-local-define | 4 ++++ t/t4018/scheme-top-level-define | 4 ++++ t/t4018/scheme-user-defined-define | 6 ++++++ t/t4034-diff-words.sh | 1 + t/t4034/scheme/expect | 9 +++++++++ t/t4034/scheme/post | 4 ++++ t/t4034/scheme/pre | 4 ++++ userdiff.c | 8 ++++++++ 11 files changed, 51 insertions(+) create mode 100644 t/t4018/scheme-define-syntax create mode 100644 t/t4018/scheme-local-define create mode 100644 t/t4018/scheme-top-level-define create mode 100644 t/t4018/scheme-user-defined-define create mode 100644 t/t4034/scheme/expect create mode 100644 t/t4034/scheme/post create mode 100644 t/t4034/scheme/pre diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 0a60472bb5..cfcfa800c2 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -845,6 +845,8 @@ patterns are available: - `rust` suitable for source code in the Rust language. +- `scheme` suitable for source code in the Scheme language. + - `tex` suitable for source code for LaTeX documents. diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index 9675bc17db..823ea96acb 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh @@ -48,6 +48,7 @@ diffpatterns=" python ruby rust + scheme tex custom1 custom2 diff --git a/t/t4018/scheme-define-syntax b/t/t4018/scheme-define-syntax new file mode 100644 index 0000000000..603b99cea4 --- /dev/null +++ b/t/t4018/scheme-define-syntax @@ -0,0 +1,8 @@ +(define-syntax define-test-suite RIGHT + (syntax-rules () + ((_ suite-name (name test) ChangeMe ...) + (define suite-name + (let ((tests + `((name . ,test) ...))) + (lambda () + (ChangeMe 'suite-name tests))))))) \ No newline at end of file diff --git a/t/t4018/scheme-local-define b/t/t4018/scheme-local-define new file mode 100644 index 0000000000..90e75dcce8 --- /dev/null +++ b/t/t4018/scheme-local-define @@ -0,0 +1,4 @@ +(define (higher-order) + (define local-function RIGHT + (lambda (x) + (car "this is" "ChangeMe")))) \ No newline at end of file diff --git a/t/t4018/scheme-top-level-define b/t/t4018/scheme-top-level-define new file mode 100644 index 0000000000..03acdc628d --- /dev/null +++ b/t/t4018/scheme-top-level-define @@ -0,0 +1,4 @@ +(define (some-func x y z) RIGHT + (let ((a x) + (b y)) + (ChangeMe a b))) \ No newline at end of file diff --git a/t/t4018/scheme-user-defined-define b/t/t4018/scheme-user-defined-define new file mode 100644 index 0000000000..401093bac3 --- /dev/null +++ b/t/t4018/scheme-user-defined-define @@ -0,0 +1,6 @@ +(define-test-suite record-case-tests RIGHT + (record-case-1 (lambda (fail) + (let ((a (make-foo 1 2))) + (record-case a + ((bar x) (ChangeMe)) + ((foo a b) (+ a b))))))) \ No newline at end of file diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index 56f1e62a97..ee7721ab91 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -325,6 +325,7 @@ test_language_driver perl test_language_driver php test_language_driver python test_language_driver ruby +test_language_driver scheme test_language_driver tex test_expect_success 'word-diff with diff.sbe' ' diff --git a/t/t4034/scheme/expect b/t/t4034/scheme/expect new file mode 100644 index 0000000000..eed21e803c --- /dev/null +++ b/t/t4034/scheme/expect @@ -0,0 +1,9 @@ +<BOLD>diff --git a/pre b/post<RESET> +<BOLD>index 6a5efba..7c4a6b4 100644<RESET> +<BOLD>--- a/pre<RESET> +<BOLD>+++ b/post<RESET> +<CYAN>@@ -1,4 +1,4 @@<RESET> +(define (<RED>myfunc a b<RESET><GREEN>my-func first second<RESET>) + ; This is a <RED>really<RESET><GREEN>(moderately)<RESET> cool function. + (let ((c (<RED>+ a b<RESET><GREEN>add1 first<RESET>))) + (format "one more than the total is %d" (<RED>add1<RESET><GREEN>+<RESET> c <GREEN>second<RESET>)))) diff --git a/t/t4034/scheme/post b/t/t4034/scheme/post new file mode 100644 index 0000000000..7c4a6b4f3d --- /dev/null +++ b/t/t4034/scheme/post @@ -0,0 +1,4 @@ +(define (my-func first second) + ; This is a (moderately) cool function. + (let ((c (add1 first))) + (format "one more than the total is %d" (+ c second)))) \ No newline at end of file diff --git a/t/t4034/scheme/pre b/t/t4034/scheme/pre new file mode 100644 index 0000000000..6a5efbae61 --- /dev/null +++ b/t/t4034/scheme/pre @@ -0,0 +1,4 @@ +(define (myfunc a b) + ; This is a really cool function. + (let ((c (+ a b))) + (format "one more than the total is %d" (add1 c)))) \ No newline at end of file diff --git a/userdiff.c b/userdiff.c index 3f81a2261c..c51a8c98ba 100644 --- a/userdiff.c +++ b/userdiff.c @@ -191,6 +191,14 @@ PATTERNS("rust", "[a-zA-Z_][a-zA-Z0-9_]*" "|[0-9][0-9_a-fA-Fiosuxz]*(\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?" "|[-+*\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\|\\||->|=>|\\.{2}=|\\.{3}|::"), +PATTERNS("scheme", + "^[\t ]*(\\(define-?.*)$", + /* + * Scheme allows symbol names to have any character, + * as long as it is not a form of a parenthesis. + * The spaces must be escaped. + */ + "(\\.|[^][)(\\}\\{ ])+"), PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", "[={}\"]|[^={}\" \t]+"), PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", -- 2.31.0