emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/racket-mode ada192863b 2/2: racket-xp-mode: Annotate impor


From: ELPA Syncer
Subject: [nongnu] elpa/racket-mode ada192863b 2/2: racket-xp-mode: Annotate import bindings with modules
Date: Mon, 29 Jul 2024 13:00:37 -0400 (EDT)

branch: elpa/racket-mode
commit ada192863b0eb162c31015345f0158828e9dba22
Author: Greg Hendershott <git@greghendershott.com>
Commit: Greg Hendershott <git@greghendershott.com>

    racket-xp-mode: Annotate import bindings with modules
    
    After recently using an affixation-function in the new
    racket-package-mode, I revived an old branch to annotate imports with
    their modules.
    
    The annotations can be visible in both completion-at-point and
    read-from-minibuffer completion scenarios.
    
    Also add a racket-module completion category.
    
    Also review and improve or simplify some adjacent code.
---
 racket-complete.el               |  55 +++++---
 racket-doc.el                    |  28 +++--
 racket-package.el                |   2 +-
 racket-xp-complete.el            | 103 +++++++++++----
 racket-xp.el                     | 264 +++++++++++++++++++++++----------------
 racket/commands/check-syntax.rkt |  15 +--
 racket/imports.rkt               |  72 ++++++-----
 7 files changed, 337 insertions(+), 202 deletions(-)

diff --git a/racket-complete.el b/racket-complete.el
index 797b3b628d..7fda66fb91 100644
--- a/racket-complete.el
+++ b/racket-complete.el
@@ -18,18 +18,18 @@
                   (end (progn (forward-sexp  1) (point))))
               (when (<= (+ beg 2) end)  ;prefix at least 2 chars
                 (funcall proc beg end))))
-        (error nil)))
-  (let ((beg (save-excursion (skip-syntax-backward "^-()>") (point))))
-    (unless (or (eq beg (point-max))
-                (member (char-syntax (char-after beg)) '(?\" ?\( ?\))))
-      (condition-case _
-          (save-excursion
-            (goto-char beg)
-            (forward-sexp 1)
-            (let ((end (point)))
-              (when (<= (+ beg 2) end) ;prefix at least 2 chars
-               (funcall proc beg end))))
-        (error nil)))))
+        (error nil))
+    (let ((beg (save-excursion (skip-syntax-backward "^-()>") (point))))
+      (unless (or (eq beg (point-max))
+                  (member (char-syntax (char-after beg)) '(?\" ?\( ?\))))
+        (condition-case _
+            (save-excursion
+              (goto-char beg)
+              (forward-sexp 1)
+              (let ((end (point)))
+                (when (<= (+ beg 2) end) ;prefix at least 2 chars
+                  (funcall proc beg end))))
+          (error nil))))))
 
 (defun racket--in-require-form-p ()
   (unless forward-sexp-function ;not necessarily sexp lang
@@ -56,21 +56,36 @@
 (add-to-list 'completion-category-defaults
              `(,racket--identifier-category (styles basic)))
 
+(defconst racket--module-category 'racket-module
+  "Value for category metadata of module completion tables.")
+
+;; Suggest default; can customize via `completion-category-overrides'.
+(add-to-list 'completion-category-defaults
+             `(,racket--module-category (styles basic)))
+
 (defun racket--completion-table (completions &optional metadata)
-  "Like `completion-table-dynamic' but also metadata.
+  "Like `completion-table-dynamic' but also supplies metadata.
 
 METADATA defaults to `((category . ,`racket--identifier-category')).
 
-Category metadata needs to be returned by the completion table
-function itself, unlike metadata supplied as properties in the
-`completion-at-point-functions' list.
+Although sometimes completion metadata is specified as properties
+in a `completion-at-point-functions' item, sometimes that is
+insufficient or irrelevant -- as with category metadata, or, when
+CAPF isn't involved and instead the completion table is given
+directly to `completing-read'.
 
 Supplying category metadata allows the user to configure a
-completion matching style for that category."
+completion matching style for that category. It also prevents
+third party packages like marginalia from misclassifying and
+displaying inappropriate annotations."
   (lambda (prefix predicate action)
-    (if (eq action 'metadata)
-        (cons 'metadata (or metadata `((category . 
,racket--identifier-category))))
-      (complete-with-action action completions prefix predicate))))
+    (pcase action
+      ('metadata
+       (cons 'metadata
+             (or metadata
+                 `((category . ,racket--identifier-category)))))
+      (_
+       (complete-with-action action completions prefix predicate)))))
 
 (provide 'racket-complete)
 
diff --git a/racket-doc.el b/racket-doc.el
index a334f8106a..574be59136 100644
--- a/racket-doc.el
+++ b/racket-doc.el
@@ -22,19 +22,21 @@
 
 (defun racket--doc (prefix how completions)
   "A helper for `racket-xp-documentation' and `racket-repl-documentation'."
-  (let ((search-p (equal prefix '(16))))
-    (pcase (racket--symbol-at-point-or-prompt prefix
-                                              "Documentation for: "
-                                              (unless search-p completions)
-                                              search-p)
-      ((and (pred stringp) str)
-       (if search-p
-           (racket--search-doc str)
-         (racket--doc-assert-local-back-end)
-         (racket--doc-command (when (eq how 'namespace)
-                                (racket--repl-session-id))
-                              how
-                              str))))))
+  (racket--doc-assert-local-back-end)
+  (cond
+   ((equal prefix '(16))
+    (when-let (str (read-from-minibuffer
+                    "Search documentation for text: "))
+      (racket--search-doc str)))
+   (t
+    (when-let (str (racket--symbol-at-point-or-prompt
+                    prefix
+                    "Documentation for: "
+                    completions))
+      (racket--doc-command (when (eq how 'namespace)
+                             (racket--repl-session-id))
+                           how
+                           str)))))
 
 (defun racket--doc-command (repl-session-id how str)
   "A helper for `racket--doc', `racket-xp-describe', and 
`racket-repl-describe'.
diff --git a/racket-package.el b/racket-package.el
index cc6af4cf46..bc8002f813 100644
--- a/racket-package.el
+++ b/racket-package.el
@@ -173,7 +173,7 @@ Allows users to customize via 
`completion-category-overrides'.")
                                stat
                                (make-string (- max-stat (length stat)) 32)
                                desc)
-                       'face 'font-lock-comment-face)))))
+                       'face 'completions-annotations)))))
            vs)))
        (val (completing-read "Describe Racket package: "
                              (racket--completion-table
diff --git a/racket-xp-complete.el b/racket-xp-complete.el
index d57a9a0fe9..02f76aa3c8 100644
--- a/racket-xp-complete.el
+++ b/racket-xp-complete.el
@@ -8,17 +8,81 @@
 
 ;; SPDX-License-Identifier: GPL-3.0-or-later
 
+(require 'seq)
 (require 'racket-complete)
 (require 'racket-describe)
 (require 'racket-company-doc)
 
-(defvar-local racket--xp-binding-completions nil
-  "Completion candidates that are bindings.
-Set by `racket-xp-mode'. Used by `racket-xp-complete-at-point'.")
+(defvar-local racket--xp-completion-table-all nil
+  "A completion table of all bindings; for use by a CAPF.
+
+Includes both imports and lexical bindings. Better for use by
+`completion-at-point' in an edit buffer, because in general more
+completion candidates offer more opportunities to minimize
+typing.
+
+The table includes category and affixation-function metadata; the
+latter shows the module from which an identifier was imported,
+when not a lexical binding.")
+
+(defvar-local racket--xp-completion-table-imports nil
+  "A completion table of import bindings; for use in minibuffer.
+
+Includes only imports, not lexical bindings. Definitely better
+for use by commands that look up documentation. Sometimes better
+for use by `completing-read' in the minibuffer, because that
+returns strings stripped of all text properties -- unless a
+command is able to find a suitable matching string in the buffer
+and use its text properties.
+
+The table includes category and affixation-funciton metadata.")
+
+(defun racket--set-xp-binding-completions (mods+syms)
+  ;; The back end gives us data optimized for space when serializing:
+  ;;
+  ;;  ((modA symA0 symA1 ...)
+  ;;   (modB symB0 symB1 ...) ...)
+  ;;
+  ;; Reshape that to a list of strings, each propertized with is mod,
+  ;; for use as completion table.
+  (let ((all nil)
+        (imports nil)
+        (metadata `((category . ,racket--identifier-category)
+                    (affixation-function . 
,#'racket--xp-binding-completion-affixator))))
+    (dolist (mod+syms mods+syms)
+      (pcase-let ((`(,mod . ,syms) mod+syms))
+        (dolist (sym syms)
+          (push (propertize sym 'racket-module mod) all)
+          (when mod
+            (push (propertize sym 'racket-module mod) imports)))))
+    (setq racket--xp-completion-table-all
+          (racket--completion-table all metadata))
+    (setq racket--xp-completion-table-imports
+          (racket--completion-table imports metadata))))
+
+(defun racket--xp-binding-completion-affixator (strs)
+  "Value for :affixation-function."
+  (let ((max-len (seq-reduce (lambda (max-len str)
+                               (max max-len (1+ (length str))))
+                             strs
+                             15)))
+    (seq-map (lambda (str)
+               (let* ((leading-space (make-string (- max-len (length str)) 32))
+                      (mod (get-text-property 0 'racket-module str))
+                      (suffix (or mod ""))
+                      (suffix (propertize suffix 'face 
'completions-annotations))
+                      (suffix (concat leading-space suffix)))
+                 (list str "" suffix)))
+             strs)))
 
 (defvar-local racket--xp-module-completions nil
-  "Completion candidates that are available collection module paths.
-Set by `racket-xp-mode'. Used by `racket-xp-complete-at-point'.")
+  "A completion table for available collection module paths.
+Do not `setq' directly; instead call `racket--xp-set-module-completions'.")
+
+(defun racket--set-xp-module-completions (completions)
+  (setq-local racket--xp-module-completions
+              (racket--completion-table completions
+                                        `((category . 
,racket--module-category)))))
 
 (defun racket-xp-complete-at-point ()
   "A value for the variable `completion-at-point-functions'.
@@ -51,10 +115,11 @@ Set by `racket-xp-mode'. Used by 
`racket-xp-complete-at-point'.")
 (defun racket--xp-capf-bindings (beg end)
   (list beg
         end
-        (racket--completion-table racket--xp-binding-completions)
-        :exclusive          'no
-        :company-location   (racket--xp-make-company-location-proc)
-        :company-doc-buffer (racket--xp-make-company-doc-buffer-proc)))
+        racket--xp-completion-table-all
+        :affixation-function #'racket--xp-binding-completion-affixator
+        :exclusive           'no
+        :company-location    (racket--xp-make-company-location-proc)
+        :company-doc-buffer  (racket--xp-make-company-doc-buffer-proc)))
 
 (defun racket--xp-capf-require-transformers (beg end)
   "Note: Currently this returns too many candidates -- all
@@ -70,23 +135,15 @@ that are require transformers."
 (defun racket--xp-capf-absolute-module-paths (beg end)
   (list beg
         end
-        (racket--completion-table racket--xp-module-completions)
+        racket--xp-module-completions
         :exclusive 'no))
 
 (defun racket--xp-capf-relative-module-paths ()
-  (pcase (thing-at-point 'filename t)
-    ((and (pred stringp) str)
-     (pcase-let ((`(,beg . ,end) (bounds-of-thing-at-point 'filename)))
-       (pcase (completion-file-name-table str #'file-exists-p t)
-         ((and (pred listp) table)
-          (let* ((dir (file-name-directory str))
-                 (table (mapcar (lambda (v) (concat dir v)) ;#466
-                                table)))
-            (list beg
-                  end
-                  (racket--completion-table table
-                                            '((category . file)))
-                  :exclusive 'no))))))))
+  (when-let (bounds (bounds-of-thing-at-point 'filename))
+    (list (car bounds)
+          (cdr bounds)
+          #'completion-file-name-table
+          :exclusive 'no)))
 
 (defun racket--xp-make-company-location-proc ()
   (when (racket--cmd-open-p)
diff --git a/racket-xp.el b/racket-xp.el
index a7d439a428..78342d3658 100644
--- a/racket-xp.el
+++ b/racket-xp.el
@@ -278,7 +278,7 @@ commands directly to whatever keys you prefer.
          (racket--cmd/async nil
                             `(module-names)
                             (lambda (result)
-                              (setq racket--xp-module-completions result)))
+                              (racket--set-xp-module-completions result)))
          (add-hook 'xref-backend-functions
                    #'racket-xp-xref-backend-function
                    nil t)
@@ -427,7 +427,7 @@ manually."
             (when (equal next-error-last-buffer (current-buffer))
               (setq next-error-last-buffer nil))
             (racket--xp-clear)
-            (setq racket--xp-binding-completions completions)
+            (racket--set-xp-binding-completions completions)
             (setq racket--xp-imenu-index imenu)
             (racket--xp-insert annotations)
             (racket--xp-set-status 'ok)
@@ -572,7 +572,7 @@ point."
     (remove-text-properties (point-min) (point-max)
                             (list 'help-echo nil))
     (unless only-errors-p
-      (setq racket--xp-binding-completions nil)
+      (racket--set-xp-binding-completions nil)
       (setq racket--xp-imenu-index nil)
       (racket--remove-overlays-in-buffer racket-xp-def-face
                                          racket-xp-use-face
@@ -594,63 +594,6 @@ point."
                                     ;; remove only those.
                                     'font-lock-face          nil)))))
 
-;;; xref
-
-(defun racket-xp-describe (&optional prefix)
-  "Describe the identifier at point.
-
-The command varies based on how many \\[universal-argument] command prefixes 
you supply.
-\\<racket-xp-mode-map>
-
-- \\[racket-xp-describe]
-
-  Uses the symbol at point. If no such symbol exists, you are
-  prompted enter the identifier, but in this case it only
-  considers definitions or imports at the file's module level --
-  not local bindings nor definitions in submodules.
-
-  - If the identifier has installed Racket documentation, then a
-    simplified version of the HTML is presented in the buffer,
-    including the \"blue box\", documentation prose, and
-    examples.
-
-  - Otherwise, if the identifier is a function, then its
-    signature is displayed, for example \"\(name arg-1-name
-    arg-2-name\)\".
-
-- \\[universal-argument] \\[racket-xp-describe]
-
-  Always prompts you to enter a symbol, defaulting to the symbol
-  at point if any.
-
-- \\[universal-argument] \\[universal-argument] \\[racket-xp-describe]
-
-  This is an alias for `racket-describe-search', which uses
-  installed documentation in a `racket-describe-mode' buffer
-  instead of an external web browser.
-
-The intent is to give a quick reminder or introduction to
-something, regardless of whether it has installed documentation
--- and to do so within Emacs, without switching to a web browser.
-
-This buffer is also displayed when you use `company-mode' and
-press F1 or C-h in its pop up completion list."
-  (interactive "P")
-  (if (equal prefix '(16))
-      (racket-describe-search)
-    (pcase (racket--symbol-at-point-or-prompt prefix "Describe: "
-                                              racket--xp-binding-completions)
-      ((and (pred stringp) str)
-       ;; When there is a racket-xp-doc property, use its path and
-       ;; anchor, because that will be correct even for an identifier
-       ;; in a submodule with different imports than the file module.
-       ;; Else supply the file path-str, and the "describe" command
-       ;; will treat it as a file module identifier.
-       (let ((how (pcase (get-text-property (point) 'racket-xp-doc)
-                    (`(,path ,anchor) `(,path . ,anchor))
-                    (_                (racket--buffer-file-name)))))
-         (racket--do-describe how nil str))))))
-
 (defun racket-xp-eldoc-function ()
   "A value for the variable `eldoc-documentation-function'.
 
@@ -835,7 +778,7 @@ command prefixes you supply.
     ((and `(,path ,anchor) (guard (not prefix)))
      (racket-browse-file-url path anchor))
     (_
-     (racket--doc prefix (buffer-file-name) racket--xp-binding-completions))))
+     (racket--doc prefix (buffer-file-name) 
racket--xp-completion-table-imports))))
 
 ;;; Navigation
 
@@ -1044,8 +987,99 @@ around at the first and last errors."
   (interactive)
   (previous-error))
 
+;;; describe
+
+(defun racket-xp-describe (&optional prefix)
+  "Describe the identifier at point.
+
+The command varies based on how many \\[universal-argument] command prefixes 
you supply.
+\\<racket-xp-mode-map>
+
+- \\[racket-xp-describe]
+
+  Uses the symbol at point. If no such symbol exists, you are
+  prompted enter the identifier, but in this case it only
+  considers definitions or imports at the file's module level --
+  not local bindings nor definitions in submodules.
+
+  - If the identifier has installed Racket documentation, then a
+    simplified version of the HTML is presented in the buffer,
+    including the \"blue box\", documentation prose, and
+    examples.
+
+  - Otherwise, if the identifier is a function, then its
+    signature is displayed, for example \"\(name arg-1-name
+    arg-2-name\)\".
+
+- \\[universal-argument] \\[racket-xp-describe]
+
+  Always prompts you to enter a symbol, defaulting to the symbol
+  at point if any.
+
+- \\[universal-argument] \\[universal-argument] \\[racket-xp-describe]
+
+  This is an alias for `racket-describe-search', which uses
+  installed documentation in a `racket-describe-mode' buffer
+  instead of an external web browser.
+
+The intent is to give a quick reminder or introduction to
+something, regardless of whether it has installed documentation
+-- and to do so within Emacs, without switching to a web browser.
+
+This buffer is also displayed when you use `company-mode' and
+press F1 or C-h in its pop up completion list."
+  (interactive "P")
+  (if (equal prefix '(16))
+      (racket-describe-search)
+    (pcase (racket--symbol-at-point-or-prompt
+            prefix "Describe: "
+            racket--xp-completion-table-all)
+      ((and (pred stringp) str)
+       ;; When user did /not/ supply command prefix to input an
+       ;; arbitrary string, we can look for a racket-xp-doc property
+       ;; at point. If available, use its path and anchor, because
+       ;; that will be correct even for an identifier in a submodule
+       ;; with different imports than the file module. Otherwise
+       ;; supply the file's path, and the "describe" command will
+       ;; treat str as a file module identifier.
+       (let ((how (pcase (and (not prefix)
+                              (get-text-property (point) 'racket-xp-doc))
+                    (`(,path ,anchor) `(,path . ,anchor))
+                    (_                (racket--buffer-file-name)))))
+         (racket--do-describe how nil str))))))
+
 ;;; xref
 
+(defconst racket--xp-props-for-xref
+  '(racket-xp-require
+    racket-xp-visit
+    racket-xp-use
+    racket-xp-def))
+
+(defun racket--xp-props-for-xref-at (pos &optional object)
+  (when-let (plist (text-properties-at pos object))
+    (seq-some (lambda (p) (plist-member plist p))
+              racket--xp-props-for-xref)))
+
+(defun racket--xp-find-propertized-string (str)
+  "Find string in buffer matching STR and having one of our properties.
+When found, returns the buffer string and all its properties,
+else returns STR."
+  (save-restriction
+    (widen)
+    (save-excursion
+      (goto-char (point-min))
+      (save-match-data
+        (let ((result nil))
+          (while (and (not result)
+                      (< (point) (point-max)))
+            (if (search-forward str nil t)
+                (when (and (equal (thing-at-point 'symbol) str)
+                           (racket--xp-props-for-xref-at (match-beginning 0)))
+                  (setq result (match-string 0)))
+             (goto-char (point-max))))
+          (or result str))))))
+
 (defun racket-xp-xref-backend-function ()
   'racket-xp-xref)
 
@@ -1055,60 +1089,72 @@ around at the first and last errors."
                     (let* ((end (next-single-property-change (point) prop))
                            (beg (previous-single-property-change end prop)))
                       (save-restriction (widen) (buffer-substring beg end)))))
-                ;; Consider same props our xref-backend-definitions
-                ;; method looks for.
-                '(racket-xp-require
-                  racket-xp-visit
-                  racket-xp-use
-                  racket-xp-def))
+                racket--xp-props-for-xref)
       (thing-at-point 'symbol)))
 
 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql 
racket-xp-xref)))
-  (completion-table-dynamic
-   (lambda (prefix)
-     (all-completions prefix racket--xp-binding-completions))))
+  racket--xp-completion-table-all)
 
 (cl-defmethod xref-backend-definitions ((_backend (eql racket-xp-xref)) str)
-  (or
-   ;; Something annotated as add-open-reuqire-menu by drracket/check-syntax
-   (when-let (path (get-text-property 0 'racket-xp-require str))
-     (list (xref-make str (xref-make-file-location path 1 0))))
-   ;; Something annotated for jump-to-definition by drracket/check-syntax
-   (pcase (get-text-property 0 'racket-xp-visit str)
-     (`(,path ,subs ,ids)
-      (pcase (racket--cmd/await nil
-                                `(def/drr
-                                   ,(racket-file-name-front-to-back
-                                     (racket--buffer-file-name))
-                                   ,(racket-file-name-front-to-back path)
-                                   ,subs
-                                   ,ids))
+  ;; If xref used `completing-read', then STR will have no text
+  ;; properties, which limits what we can do with it. Try to find a
+  ;; matching string in the buffer to use, so we can use one of its
+  ;; `racket--xp-props-for-xref'.
+  (let ((str (if (not (racket--xp-props-for-xref-at 0 str))
+                 (racket--xp-find-propertized-string str)
+               str)))
+    (list
+     (or
+      ;; Something annotated as add-open-require-menu by drracket/check-syntax
+      (when-let (path (get-text-property 0 'racket-xp-require str))
+        (list (xref-make str (xref-make-file-location path 1 0))))
+      ;; Something annotated for jump-to-definition by drracket/check-syntax
+      (pcase (get-text-property 0 'racket-xp-visit str)
+        (`(,path ,subs ,ids)
+         (pcase (racket--cmd/await nil
+                                   `(def/drr
+                                      ,(racket-file-name-front-to-back
+                                        (racket--buffer-file-name))
+                                      ,(racket-file-name-front-to-back path)
+                                      ,subs
+                                      ,ids))
+           (`(,path ,line ,col)
+            (xref-make str
+                       (xref-make-file-location
+                        (racket-file-name-back-to-front path) line col))))))
+      (pcase (get-text-property 0 'racket-xp-use str)
+        (`(,beg ,end)
+         (xref-make (save-restriction (widen) (buffer-substring beg end))
+                    (xref-make-buffer-location (current-buffer)
+                                               (marker-position beg)))))
+      (pcase (get-text-property 0 'racket-xp-def str)
+        (`(local ,id ((,use-beg ,_use-end) . ,_))
+         (when-let (def-beg (car (get-text-property use-beg 'racket-xp-use)))
+           (xref-make id
+                      (xref-make-buffer-location (current-buffer)
+                                                 (marker-position def-beg)))))
+        ;; Annotated by dr/cs as imported module; visit the module
+        (`(import ,id . ,_)
+         (xref-backend-definitions 'racket-xref-module id)))
+      ;; Something that, for whatever reason, drracket/check-syntax
+      ;; did not annotate. Use the back end `def` command (although it
+      ;; can only find definitions imported at the file module level,
+      ;; not submodules, since all we give it is a plain string and no
+      ;; position.)
+      (pcase (racket--cmd/await nil `(def ,(racket-file-name-front-to-back
+                                            (racket--buffer-file-name))
+                                          ,(substring-no-properties str)))
         (`(,path ,line ,col)
-         (list (xref-make str
-                          (xref-make-file-location
-                           (racket-file-name-back-to-front path) line 
col)))))))
-   (pcase (get-text-property 0 'racket-xp-use str)
-     (`(,beg ,end)
-      (list
-       (xref-make (save-restriction (widen) (buffer-substring beg end))
-                  (xref-make-buffer-location (current-buffer)
-                                             (marker-position beg))))))
-   ;; Annotated by dr/cs as imported module; visit the module
-   (pcase (get-text-property 0 'racket-xp-def str)
-     (`(import ,id . ,_)
-      (xref-backend-definitions 'racket-xref-module id)))
-   ;; Something that, for whatever reason, drracket/check-syntax did
-   ;; not annotate.
-   (pcase (racket--cmd/await nil `(def ,(racket-file-name-front-to-back
-                                         (racket--buffer-file-name))
-                                       ,(substring-no-properties str)))
-     (`(,path ,line ,col)
-      (list (xref-make str
-                       (xref-make-file-location path line col))))
-     (`kernel
-      (list (xref-make str
-                       (xref-make-bogus-location
-                        "Defined in #%%kernel -- source not available")))))))
+         (xref-make str
+                    (xref-make-file-location path line col)))
+        (`kernel
+         (xref-make str
+                    (xref-make-bogus-location
+                     "Defined in #%%kernel -- source not available")))
+        (_
+         (xref-make str
+                    (xref-make-bogus-location
+                     "Cannot find definition; maybe if identifier is imported 
in a submodule but not used"))))))))
 
 (cl-defmethod xref-backend-references ((backend (eql racket-xp-xref)) str)
   ;; Note: Our ability to find references is limited to those
diff --git a/racket/commands/check-syntax.rkt b/racket/commands/check-syntax.rkt
index 21a5c1aeb3..b4bf912527 100644
--- a/racket/commands/check-syntax.rkt
+++ b/racket/commands/check-syntax.rkt
@@ -102,11 +102,10 @@
     (with-time/log 'get-annotations
       (send (current-annotations) get-annotations)))
 
-  (define completions-set (send (current-annotations) 
get-local-completion-candidates))
-  (with-time/log 'imports
-    (imports stx completions-set))
-  (define completions (sort (set->list completions-set)
-                            string<=?))
+  (define completions
+    (cons (cons #f (send (current-annotations) 
get-local-completion-candidates))
+          (for/list ([(k v) (in-hash (imports stx))])
+            (cons k (set->list v)))))
 
   (define imenu (send (current-annotations) get-imenu-index))
 
@@ -211,7 +210,8 @@
         ;; imported that _could_ be used.
         (when (or (regexp-match? #px"^\\d+ bound occurrences?$" status)
                   (equal? status "no bound occurrences"))
-          (set-add! local-completion-candidates (substring code-str beg 
end)))))
+          (set-add! local-completion-candidates
+                    (substring code-str beg end)))))
 
     ;; These are module-level definitions (not lexical bindings). So
     ;; they are useful for things like imenu. Also these are a good
@@ -231,7 +231,8 @@
         (match keys
           [(list key)      (hash-set! ht key (add1 beg))]
           [(cons key more) (trie-set! (hash-ref! ht key (make-hash)) more)]))
-      (set-add! local-completion-candidates (~a symbol)))
+      (set-add! local-completion-candidates
+                (~a symbol)))
 
     (define/override (syncheck:add-jump-to-definition _src beg end id-sym path 
submods)
       ;; - drracket/check-syntax only reports the file, not the
diff --git a/racket/imports.rkt b/racket/imports.rkt
index 8f95115375..44b6de44b5 100644
--- a/racket/imports.rkt
+++ b/racket/imports.rkt
@@ -37,8 +37,19 @@
 
 ;; It is important to run this with the correct parameterization of
 ;; current-namespace and current-load-relative-directory.
-(define/contract (imports stx [sos (mutable-set)])
-  (->* (syntax?) (set-mutable?) set-mutable?)
+(define/contract (imports stx)
+  (->* (syntax?) hash?)
+  (define ht (make-hash))
+  (define (update! rmp f)
+    (hash-update! ht (->str rmp) f (set)))
+  (define (add! rmp v)
+    (update! rmp (λ (s) (set-add s (->str v)))))
+  (define (remove! rmp v)
+    (update! rmp (λ (s) (set-remove s (->str v)))))
+  (define (add-set! rmp set-of-strings)
+    (update! rmp (λ (s) (set-union s set-of-strings))))
+  (define (remove-set! rmp set-of-strings)
+    (update! rmp (λ (s) (set-subtract s set-of-strings))))
 
   (define (handle-module stx)
     (syntax-case stx (module #%module-begin #%plain-module-begin)
@@ -99,9 +110,9 @@
   (define (handle-phaseless-spec spec lang)
     (syntax-case* spec (only prefix all-except prefix-all-except rename)
         symbolic-compare?
-      [(only _raw-module-path id ...)
-       (set-union! sos
-                   (syntax->string-set #'(id ...)))]
+      [(only raw-module-path id ...)
+       (add-set! #'raw-module-path
+                 (syntax->string-set (syntax->list #'(id ...))))]
       [(prefix prefix-id raw-module-path)
        (module-exported-strings #'raw-module-path
                                 lang
@@ -118,8 +129,8 @@
       [(rename raw-module-path local-id exported-id)
        (begin
          (unless (eq? (syntax-e #'raw-module-path) (syntax-e lang))
-           (set-remove! sos (->str #'exported-id)))
-         (set-add! sos (->str #'local-id)))]
+           (remove! #'raw-module-path #'exported-id))
+         (add! #'raw-module-path #'local-id))]
       [raw-module-path
        (module-path? (syntax->datum #'raw-module-path))
        (module-exported-strings #'raw-module-path
@@ -128,7 +139,7 @@
   (define (module-exported-strings raw-module-path
                                    lang
                                    #:except [exceptions (set)]
-                                   #:prefix [prefix #'""])
+                                   #:prefix [prefix #f])
     ;; NOTE: Important to run module->exports with the correct
     ;; parameterization of current-namespace and
     ;; current-load-relative-directory.
@@ -138,25 +149,28 @@
     ;; completion candidates from drracket/check-syntax for
     ;; non-imported bindings. Our contribution is imported
     ;; definitions.
-    (with-handlers ([exn:fail? (λ _ sos)])
-      (define-values (vars stxs)
-        (module->exports (syntax->datum raw-module-path)))
+    (define-values (vars stxs)
+      (with-handlers ([exn:fail? (λ _ (values #f #f))])
+        (module->exports (syntax->datum raw-module-path))))
+    (when (and vars stxs)
       (define orig
-        (for*/mutable-set ([vars+stxs (in-list (list vars stxs))]
-                           [phases    (in-list vars+stxs)]
-                           [export    (in-list (cdr phases))])
+        (for*/set ([vars+stxs (in-list (list vars stxs))]
+                   [phases    (in-list vars+stxs)]
+                   [export    (in-list (cdr phases))])
           (->str (car export))))
       ;; If imports are from the module language, then {except rename
       ;; prefix}-in do NOT remove imports under the original name.
       ;; Otherwise they do.
       (if (eq? (syntax-e raw-module-path) (syntax-e lang))
-          (set-union! sos orig)
-          (set-subtract! sos orig exceptions))
-      (for ([v (in-set orig)])
-        (set-add! sos (~a (->str prefix) v)))))
+          (add-set! raw-module-path orig)
+          (remove-set! raw-module-path exceptions))
+      (if prefix
+          (for ([v (in-set orig)])
+            (add! raw-module-path (~a prefix (->str v))))
+          (add-set! raw-module-path orig))))
 
   (handle-module stx)
-  sos)
+  ht)
 
 (define (->str v)
   (match v
@@ -165,7 +179,7 @@
     [(? string?) v]))
 
 (define (syntax->string-set s)
-  (for/mutable-set ([s (in-syntax s)])
+  (for/set ([s (in-syntax s)])
     (->str s)))
 
 (define (symbolic-compare? x y)
@@ -187,13 +201,13 @@
 (module+ completions-example-2
   (require "syntax.rkt")
   (parameterize ([current-namespace (make-base-empty-namespace)])
-    (string->expanded-syntax "/tmp/foo.rkt" "#lang rhombus\n1"
+    (string->expanded-syntax "/tmp/foo.rkt" "#lang rhombus\n"
                              imports)))
 
 (module+ test
   (require rackunit
            version/utils)
-  ;; Compare the results to namespace-mapped-symbols.
+  ;; To compare the results to namespace-mapped-symbols.
   (module mod racket/base
     (module sub racket/base
       (define provided-by-submodule 42)
@@ -236,17 +250,17 @@
     ;; Test {prefix rename except}-in, keeping mind that they work
     ;; differently for requires that modify the module language
     ;; imports.
-    (check-false (set-member? cs "path-only")
+    (check-false (set-member? (hash-ref cs "racket/path") "path-only")
                  "rename-in not from module language hides old name")
-    (check-true (set-member? cs "PATH-ONLY")
+    (check-true (set-member? (hash-ref cs "racket/path") "PATH-ONLY")
                 "rename-in not from module language has new name ")
-    (check-true (set-member? cs "display")
+    (check-true (set-member? (hash-ref cs "racket/base") "display")
                 "rename-in from module language does not hide old name")
-    (check-true (set-member? cs "DISPLAY")
+    (check-true (set-member? (hash-ref cs "racket/base") "DISPLAY")
                 "rename-in from module language has new name")
-    (check-true (set-member? cs "displayln")
+    (check-true (set-member? (hash-ref cs "racket/base") "displayln")
                 "prefix-in from module language does not hide old name")
-    (check-true (set-member? cs "PREFIX:displayln")
+    (check-true (set-member? (hash-ref cs "racket/base") "PREFIX:displayln")
                 "prefix-in from module language is available under new name")
     ;; namespace-mapped-symbols will return some definitions beyond
     ;; those imported -- it includes {top module}-level bindings. This
@@ -259,7 +273,7 @@
     ;; Well, _our_ results are correct. For now, let's just do the
     ;; test on Racket 7.0+.
     (when (version<=? "7.0" (version))
-      (check-equal? (set-subtract nsms cs)
+      (check-equal? (set-subtract nsms (apply set-union (hash-values cs)))
                     (set "tmp.1" "nsms" "nsa" "provided-by-submodule")
                     "namespace-mapped-symbols returns only a few more, 
non-imported definitions")))
   ;; Issue 481



reply via email to

[Prev in Thread] Current Thread [Next in Thread]