/.emacs.d/lib/racket-mode/defn.rkt

https://github.com/technomancy/dotfiles · Racket · 247 lines · 188 code · 15 blank · 44 comment · 34 complexity · f0c5ee14915cd06ba037b2c7d31b3ac3 MD5 · raw file

  1. #lang racket/base
  2. (require racket/contract
  3. racket/function
  4. racket/match
  5. syntax/modread)
  6. (provide
  7. (contract-out
  8. [find-definition
  9. (-> string?
  10. (or/c #f 'kernel (list/c path-string?
  11. natural-number/c
  12. natural-number/c)))]
  13. [find-signature
  14. (-> string?
  15. (or/c #f pair?))]))
  16. ;; Try to find the definition of `str`, returning a list with the file
  17. ;; name, line and column, 'kernel, or #f if not found.
  18. (define (find-definition str)
  19. (match (find-definition/stx str)
  20. [(cons stx where)
  21. (list (path->string (or (syntax-source stx) where))
  22. (or (syntax-line stx) 1)
  23. (or (syntax-column stx) 0))]
  24. [v v]))
  25. ;; Try to find the definition of `str`, returning its signature or #f.
  26. ;; When defined in 'kernel, returns a form saying so, not #f.
  27. (define (find-signature str)
  28. (match (find-definition/stx str)
  29. ['kernel '("defined in #%kernel, signature unavailable")]
  30. [(cons stx where)
  31. (match (signature (syntax-e stx) (file->syntax where #:expand? #f))
  32. [(? syntax? stx) (syntax->datum stx)]
  33. [_ #f])]
  34. [v v]))
  35. (define (find-definition/stx str)
  36. ;; (-> string? (or/c #f 'kernel (cons/c syntax? path?)))
  37. (match (identifier-binding* str)
  38. [(? list? xs)
  39. (for/or ([x (in-list xs)])
  40. (match x
  41. [(cons id 'kernel) 'kernel]
  42. [(cons id (? path? where))
  43. (define expanded (file->syntax where #:expand? #t))
  44. (define stx
  45. (or (definition id expanded)
  46. ;; Handle rename + contract
  47. (match (renaming-provide id (file->syntax where #:expand? #f))
  48. [(? syntax? stx) (definition (syntax-e stx) expanded)]
  49. [_ #f])))
  50. (and stx
  51. (cons stx where))]))]
  52. [_ #f]))
  53. ;; A wrapper for identifier-binding. Keep in mind that unfortunately
  54. ;; it can't report the definition id in the case of a contract-out and
  55. ;; a rename-out, both. For `(provide (contract-out [rename orig new
  56. ;; contract]))` it reports (1) the contract-wrapper as the id, and (2)
  57. ;; `new` as the nominal-id -- but NOT (3) `orig`.
  58. (define/contract (identifier-binding* v)
  59. (-> (or/c string? symbol? identifier?)
  60. (or/c #f (listof (cons/c symbol? (or/c path? 'kernel #f)))))
  61. (define sym->id namespace-symbol->identifier)
  62. (define id (cond [(string? v) (sym->id (string->symbol v))]
  63. [(symbol? v) (sym->id v)]
  64. [(identifier? v) v]))
  65. (match (identifier-binding id)
  66. [(list source-mpi source-id
  67. nominal-source-mpi nominal-source-id
  68. source-phase import-phase nominal-export-phase)
  69. (define (mpi->path mpi)
  70. (match (resolved-module-path-name (module-path-index-resolve mpi))
  71. [(? path-string? path) path]
  72. ['#%kernel 'kernel]
  73. [(? symbol? sym) (sym->path sym)]
  74. [(list (? symbol? sym) _ ...) (sym->path sym)]
  75. [_ #f]))
  76. (list (cons source-id (mpi->path source-mpi))
  77. (cons nominal-source-id (mpi->path nominal-source-mpi)))]
  78. [_ #f]))
  79. ;; When module source is 'sym or '(sym sym1 ...) treat it as "sym.rkt"
  80. ;; in the current-load-relative-directory.
  81. (define (sym->path sym)
  82. (build-path (current-load-relative-directory) (format "~a.rkt" sym)))
  83. ;; Return a syntax object (or #f) for the contents of `file`.
  84. (define (file->syntax file #:expand? expand?)
  85. (define-values (base _ __) (split-path file))
  86. (parameterize ([current-load-relative-directory base]
  87. [current-namespace (make-base-namespace)])
  88. (define stx (with-handlers ([exn:fail? (const #f)])
  89. (with-module-reading-parameterization
  90. (thunk
  91. (with-input-from-file file read-syntax/count-lines)))))
  92. (if expand?
  93. (expand stx) ;expand while current-load-relative-directory is set
  94. stx)))
  95. (define (read-syntax/count-lines)
  96. (port-count-lines! (current-input-port))
  97. (read-syntax))
  98. ;; Given a symbol? and syntax?, return syntax? corresponding to the
  99. ;; definition.
  100. ;;
  101. ;; If `stx` is expanded we can find things defined via definer
  102. ;; macros.
  103. ;;
  104. ;; If `stx` is not expanded, we will miss some things, however the
  105. ;; syntax will be closer to what a human expects -- e.g. `(define (f
  106. ;; x) x)` instead of `(define-values (f) (lambda (x) x))`.
  107. (define (definition sym stx) ;;symbol? syntax? -> syntax?
  108. (define eq-sym? (make-eq-sym? sym))
  109. ;; This is a hack to handle definer macros that neglect to set
  110. ;; srcloc properly using syntx/loc or (format-id ___ #:source __):
  111. ;; If the stx lacks srcloc and its parent stx has srcloc, return the
  112. ;; parent stx instead. Caveats: 1. Assumes caller only cares about
  113. ;; the srcloc. 2. We only check immediate parent. 3. We only use
  114. ;; this for define-values and define-syntaxes, below, on the
  115. ;; assumption that this only matters for fully-expanded syntax.
  116. (define (loc s)
  117. (if (and (not (syntax-line s))
  118. (syntax-line stx))
  119. stx
  120. s))
  121. (syntax-case* stx
  122. (module #%module-begin define-values define-syntaxes
  123. define define/contract
  124. define-syntax struct define-struct)
  125. syntax-e-eq?
  126. [(module _ _ (#%module-begin . stxs))
  127. (ormap (λ (stx) (definition sym stx))
  128. (syntax->list #'stxs))]
  129. [(define (s . _) . _) (eq-sym? #'s) stx]
  130. [(define/contract (s . _) . _) (eq-sym? #'s) stx]
  131. [(define s . _) (eq-sym? #'s) stx]
  132. [(define-values (ss ...) . _) (ormap eq-sym? (syntax->list #'(ss ...)))
  133. (loc (ormap eq-sym? (syntax->list #'(ss ...))))]
  134. [(define-syntax (s . _) . _) (eq-sym? #'s) stx]
  135. [(define-syntax s . _) (eq-sym? #'s) stx]
  136. [(define-syntaxes (ss ...) . _) (ormap eq-sym? (syntax->list #'(ss ...)))
  137. (loc (ormap eq-sym? (syntax->list #'(ss ...))))]
  138. [(define-struct s . _) (eq-sym? #'s) stx]
  139. [(define-struct (s _) . _) (eq-sym? #'s) stx]
  140. [(struct s . _) (eq-sym? #'s) stx]
  141. [(struct (s _) . _) (eq-sym? #'s) stx]
  142. [_ #f]))
  143. ;; Given a symbol? and syntax?, return syntax? corresponding to the
  144. ;; function definition signature. Note that we do NOT want stx to be
  145. ;; run through `expand`.
  146. (define (signature sym stx) ;;symbol? syntax? -> (or/c #f list?)
  147. (define eq-sym? (make-eq-sym? sym))
  148. (syntax-case* stx
  149. (module #%module-begin define define/contract case-lambda)
  150. syntax-e-eq?
  151. [(module _ _ (#%module-begin . stxs))
  152. (ormap (λ (stx)
  153. (signature sym stx))
  154. (syntax->list #'stxs))]
  155. [(module _ _ . stxs)
  156. (ormap (λ (stx)
  157. (signature sym stx))
  158. (syntax->list #'stxs))]
  159. [(define (s . as) . _) (eq-sym? #'s) #'(s . as)]
  160. [(define/contract (s . as) . _) (eq-sym? #'s) #'(s . as)]
  161. [(define s (case-lambda [(ass ...) . _] ...)) (eq-sym? #'s) #'((s ass ...) ...)]
  162. [_ #f]))
  163. ;; Given a symbol? and syntax?, return syntax? corresponding to the
  164. ;; contracted provide. Note that we do NOT want stx to be run through
  165. ;; `expand` because we want the original contract definitions (if
  166. ;; any). ** This is currently not used. If we ever add a
  167. ;; `find-provision` function, it would use this.
  168. (define (contracting-provide sym stx) ;;symbol? syntax? -> syntax?
  169. (define eq-sym? (make-eq-sym? sym))
  170. (syntax-case* stx
  171. (module #%module-begin provide provide/contract)
  172. syntax-e-eq?
  173. [(module _ _ (#%module-begin . ss))
  174. (ormap (λ (stx) (contracting-provide sym stx))
  175. (syntax->list #'ss))]
  176. [(provide/contract . stxs)
  177. (for/or ([stx (syntax->list #'stxs)])
  178. (syntax-case stx ()
  179. [(s _) (eq-sym? #'s) stx]
  180. [_ #f]))]
  181. [(provide . stxs)
  182. (for/or ([stx (syntax->list #'stxs)])
  183. (syntax-case* stx (contract-out) syntax-e-eq?
  184. [(contract-out . stxs)
  185. (for/or ([stx (syntax->list #'stxs)])
  186. (syntax-case* stx (rename struct) syntax-e-eq?
  187. [(struct s _ ...) (eq-sym? #'s) stx]
  188. [(struct (s _) _ ...) (eq-sym? #'s) stx]
  189. [(rename _ s _) (eq-sym? #'s) stx]
  190. [(s _) (eq-sym? #'s) stx]
  191. [_ #f]))]
  192. ;; Only care about contracting provides.
  193. ;; [s (eq-sym? #'s) stx]
  194. [_ #f]))]
  195. [_ #f]))
  196. ;; Find sym in a contracting and/or renaming provide, and return the
  197. ;; syntax for the ORIGINAL identifier (before being contracted and/or
  198. ;; renamed).
  199. (define (renaming-provide sym stx) ;;symbol? syntax? -> syntax?
  200. (define eq-sym? (make-eq-sym? sym))
  201. (syntax-case* stx
  202. (module #%module-begin provide provide/contract)
  203. syntax-e-eq?
  204. [(module _ _ (#%module-begin . ss))
  205. (ormap (λ (stx) (renaming-provide sym stx))
  206. (syntax->list #'ss))]
  207. [(provide/contract . stxs)
  208. (for/or ([stx (syntax->list #'stxs)])
  209. (syntax-case stx ()
  210. [(s _) (eq-sym? #'s)]
  211. [_ #f]))]
  212. [(provide . stxs)
  213. (for/or ([stx (syntax->list #'stxs)])
  214. (syntax-case* stx (contract-out rename-out) syntax-e-eq?
  215. [(contract-out . stxs)
  216. (for/or ([stx (syntax->list #'stxs)])
  217. (syntax-case* stx (rename) syntax-e-eq?
  218. [(rename orig s _) (eq-sym? #'s) #'orig]
  219. [(s _) (eq-sym? #'s) #'s]
  220. [_ #f]))]
  221. [(rename-out . stxs)
  222. (for/or ([stx (syntax->list #'stxs)])
  223. (syntax-case* stx () syntax-e-eq?
  224. [(orig s) (eq-sym? #'s) #'orig]
  225. [_ #f]))]
  226. [_ #f]))]
  227. [_ #f]))
  228. ;; For use with syntax-case*. When we use syntax-case for syntax-e equality.
  229. (define (syntax-e-eq? a b)
  230. (eq? (syntax-e a) (syntax-e b)))
  231. (define ((make-eq-sym? sym) stx)
  232. (and (eq? sym (syntax-e stx)) stx))