diff --git a/default-recommendations.rkt b/default-recommendations.rkt index dd32c8a..b0fbe7f 100644 --- a/default-recommendations.rkt +++ b/default-recommendations.rkt @@ -18,6 +18,7 @@ resyntax/default-recommendations/function-definition-shortcuts resyntax/default-recommendations/function-shortcuts resyntax/default-recommendations/hash-shortcuts + resyntax/default-recommendations/keep-sorted-suggestions resyntax/default-recommendations/legacy/define-simple-macro-migration resyntax/default-recommendations/legacy/legacy-contract-migrations resyntax/default-recommendations/legacy/legacy-struct-migrations @@ -60,6 +61,7 @@ resyntax/default-recommendations/function-definition-shortcuts resyntax/default-recommendations/function-shortcuts resyntax/default-recommendations/hash-shortcuts + resyntax/default-recommendations/keep-sorted-suggestions resyntax/default-recommendations/legacy/define-simple-macro-migration resyntax/default-recommendations/legacy/legacy-contract-migrations resyntax/default-recommendations/legacy/legacy-struct-migrations @@ -107,6 +109,7 @@ function-definition-shortcuts function-shortcuts hash-shortcuts + keep-sorted-suggestions legacy-contract-migrations ;; Excluded for lots of reasons. See the following github issues: diff --git a/default-recommendations/keep-sorted-suggestions-test.rkt b/default-recommendations/keep-sorted-suggestions-test.rkt new file mode 100644 index 0000000..63c6496 --- /dev/null +++ b/default-recommendations/keep-sorted-suggestions-test.rkt @@ -0,0 +1,54 @@ +#lang resyntax/test + + +require: resyntax/default-recommendations/keep-sorted-suggestions keep-sorted-suggestions + + +header: +-------------------- +#lang racket/base +(require resyntax/keep-sorted) +(define apple 'apple) +(define banana 'banana) +(define mango 'mango) +(define orange 'orange) +(define zebra 'zebra) +-------------------- + + +test: "unsorted marked list should be resorted" +------------------------------ +(void (keep-sorted (list apple orange banana))) +============================== +(void (keep-sorted (list apple banana orange))) +------------------------------ + + +test: "unsorted marked set should be resorted" +------------------------------ +(require racket/set) +(void (keep-sorted (set orange apple banana))) +============================== +(require racket/set) +(void (keep-sorted (set apple banana orange))) +------------------------------ + + +test: "unsorted marked vector should be resorted" +------------------------------ +(void (keep-sorted (vector zebra apple mango))) +============================== +(void (keep-sorted (vector apple mango zebra))) +------------------------------ + + +no-change-test: "already sorted marked list should not be changed" +------------------------------ +(void (keep-sorted (list apple banana orange))) +------------------------------ + + +no-change-test: "unmarked unsorted list should not be changed" +------------------------------ +(void (list orange apple banana)) +------------------------------ diff --git a/default-recommendations/keep-sorted-suggestions.rkt b/default-recommendations/keep-sorted-suggestions.rkt new file mode 100644 index 0000000..86ed1e3 --- /dev/null +++ b/default-recommendations/keep-sorted-suggestions.rkt @@ -0,0 +1,54 @@ +#lang racket/base + + +(require racket/contract/base) + + +(provide + (contract-out + [keep-sorted-suggestions refactoring-suite?])) + + +(require racket/list + resyntax/base + syntax/parse + syntax/parse/define) + + +;@---------------------------------------------------------------------------------------------------- + + +;; Convert an identifier syntax object to its string representation +(define (identifier->string id) + (symbol->string (syntax-e id))) + + +;; Check if a list of identifiers is sorted alphabetically by their symbol names +(define (identifiers-sorted? ids) + (define id-strings (map identifier->string ids)) + (equal? id-strings (sort id-strings stringstring)) + + +;; Syntax class for matching a list expression with identifier elements +(define-syntax-class list-of-ids + #:attributes (constructor [element 1] sorted?) + (pattern (constructor:id element:id ...) + #:attr sorted? (identifiers-sorted? (attribute element)))) + + +(define-refactoring-rule resort-keep-sorted-list + #:description "This list is marked with `keep-sorted` but its elements are not in sorted order." + body:list-of-ids + #:when (syntax-property this-syntax 'keep-sorted) + #:when (not (attribute body.sorted?)) + #:with (sorted-element ...) (sort-identifiers (attribute body.element)) + (body.constructor sorted-element ...)) + + +(define-refactoring-suite keep-sorted-suggestions + #:rules (resort-keep-sorted-list)) diff --git a/keep-sorted.rkt b/keep-sorted.rkt new file mode 100644 index 0000000..a9e3084 --- /dev/null +++ b/keep-sorted.rkt @@ -0,0 +1,26 @@ +#lang racket/base + + +(provide + keep-sorted) + + +(require (for-syntax racket/base + syntax/parse)) + + +;@---------------------------------------------------------------------------------------------------- + + +;; The keep-sorted macro marks a collection as needing to be kept in sorted order. +;; Resyntax refactoring rules can detect this syntax property and suggest reordering +;; the elements when they're not sorted. + +;; The macro expands to a syntax property on the inner expression that Resyntax can detect. +;; The first form in the list is ignored (it's the collection constructor like list, set, vector). + + +(define-syntax (keep-sorted stx) + (syntax-parse stx + [(_ body:expr) + (syntax-property #'body 'keep-sorted #true)]))