greghendershott / racket-mode

Emacs major and minor modes for Racket: edit, REPL, check-syntax, debug, profile, and more.

Home Page:https://www.racket-mode.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Single quote autocompletion in racket-hash-lang-mode

sorawee opened this issue · comments

In racket-mode, when I type ', only one ' appears.

But in racket-hash-lang-mode, when I type ', '' appears.

I file this as a feature request and not as a bug report because it could totally be the case that I have a minor mode that pairs ' up, so it could be my fault. But the fact that things work fine in racket-mode suggests that perhaps racket-hash-lang-mode could offer something that works out of the box.

Maybe relevant: DrRacket reads this preference to determine if a character should be paired up or not:

https://docs.racket-lang.org/tools/lang-languages-customization.html#(idx._(gentag._11._(lib._scribblings%2Ftools%2Ftools..scrbl)))

This makes ' to not be paired up in #lang racket, but makes ' paired up in #lang rhombus, within DrRacket.

((alist-get 'racket-mode package-alist))
((emacs-version "29.1")
 (system-type darwin)
 (x-gtk-use-system-tooltips UNDEFINED)
 (major-mode help-mode)
 (racket--el-source-dir "/Users/sorawee/.config/emacs/.local/straight/build-29.1/racket-mode/")
 (racket--rkt-source-dir "/Users/sorawee/.config/emacs/.local/straight/build-29.1/racket-mode/racket/")
 (racket-program "racket")
 (racket-command-timeout 10)
 (racket-path-from-emacs-to-racket-function UNDEFINED)
 (racket-path-from-racket-to-emacs-function UNDEFINED)
 (racket-browse-url-function racket-browse-url-using-temporary-file)
 (racket-documentation-search-location "https://docs.racket-lang.org/search/index.html?q=%s")
 (racket-xp-after-change-refresh-delay 1)
 (racket-xp-mode-lighter
  (:eval
   (racket--xp-mode-lighter)))
 (racket-xp-highlight-unused-regexp "^[^_]")
 (racket-repl-buffer-name-function nil)
 (racket-submodules-to-run
  ((test)
   (main)))
 (racket-memory-limit 2048)
 (racket-error-context medium)
 (racket-repl-history-directory "~/.config/emacs/.local/cache/racket-mode/")
 (racket-history-filter-regexp "\\`\\s *\\'")
 (racket-images-inline t)
 (racket-imagemagick-props nil)
 (racket-images-keep-last 100)
 (racket-images-system-viewer "open")
 (racket-pretty-print t)
 (racket-use-repl-submit-predicate nil)
 (racket-pretty-print t)
 (racket-indent-curly-as-sequence t)
 (racket-indent-sequence-depth 0)
 (racket-pretty-lambda nil)
 (racket-smart-open-bracket-enable nil)
 (racket-module-forms "\\s(\\(?:module[*+]?\\|library\\)")
 (racket-logger-config
  ((cm-accomplice . warning)
   (GC . info)
   (module-prefetch . warning)
   (optimizer . info)
   (racket/contract . error)
   (racket-mode-debugger . info)
   (sequence-specialization . info)
   (* . fatal)))
 (racket-show-functions
  (racket-show-pseudo-tooltip)))
(enabled-minor-modes
 (+popup-mode)
 (anzu-mode)
 (auto-composition-mode)
 (auto-compression-mode)
 (auto-encryption-mode)
 (auto-fill-mode)
 (auto-save-mode)
 (better-jumper-local-mode)
 (better-jumper-mode)
 (buffer-read-only)
 (centaur-tabs-mode)
 (column-number-mode)
 (delete-selection-mode)
 (doom-modeline-mode)
 (electric-indent-mode)
 (eros-mode)
 (evil-escape-mode)
 (evil-goggles-mode)
 (evil-local-mode)
 (evil-mode)
 (evil-snipe-local-mode)
 (evil-snipe-mode)
 (evil-snipe-override-local-mode)
 (evil-snipe-override-mode)
 (evil-surround-mode)
 (file-name-shadow-mode)
 (flyspell-lazy-mode)
 (font-lock-mode)
 (gcmh-mode)
 (general-override-mode)
 (global-anzu-mode)
 (global-company-mode)
 (global-eldoc-mode)
 (global-evil-surround-mode)
 (global-flycheck-mode)
 (global-font-lock-mode)
 (global-git-commit-mode)
 (global-hl-line-mode)
 (global-so-long-mode)
 (global-visual-line-mode)
 (hl-line-mode)
 (isearch-fold-quotes-mode)
 (line-number-mode)
 (mac-mouse-wheel-mode)
 (marginalia-mode)
 (ns-auto-titlebar-mode)
 (override-global-mode)
 (persp-mode)
 (projectile-mode)
 (recentf-mode)
 (save-place-mode)
 (savehist-mode)
 (semantic-minor-modes-format)
 (server-mode)
 (shell-dirtrack-mode)
 (show-paren-mode)
 (show-smartparens-global-mode)
 (size-indication-mode)
 (smartparens-global-mode)
 (solaire-global-mode)
 (solaire-mode)
 (transient-mark-mode)
 (undo-fu-mode)
 (undo-fu-session-global-mode)
 (vertico-mode)
 (visual-line-mode)
 (which-key-mode)
 (window-divider-mode)
 (winner-mode)
 (ws-butler-global-mode)
 (yas-global-mode)
 (yas-minor-mode))
(disabled-minor-modes
 (+emacs-lisp-ert-mode)
 (+emacs-lisp-non-package-mode)
 (+javascript-gulp-mode)
 (+javascript-npm-mode)
 (+lsp-optimization-mode)
 (+org-pretty-mode)
 (+popup-buffer-mode)
 (+web-angularjs-mode)
 (+web-django-mode)
 (+web-jekyll-mode)
 (+web-phaser-mode)
 (+web-react-mode)
 (+web-wordpress-mode)
 (abbrev-mode)
 (auto-fill-function)
 (auto-image-file-mode)
 (auto-revert-mode)
 (auto-revert-tail-mode)
 (auto-save-visited-mode)
 (blink-cursor-mode)
 (buffer-face-mode)
 (bug-reference-mode)
 (bug-reference-prog-mode)
 (button-mode)
 (centaur-tabs-local-mode)
 (cl-old-struct-compat-mode)
 (comint-fontify-input-mode)
 (company-box-mode)
 (company-mode)
 (company-search-mode)
 (compilation-minor-mode)
 (compilation-shell-minor-mode)
 (completion-in-region-mode)
 (consult-preview-at-point-mode)
 (context-menu-mode)
 (copilot-mode)
 (cursor-face-highlight-mode)
 (cursor-intangible-mode)
 (cursor-sensor-mode)
 (dash-fontify-mode)
 (defining-kbd-macro)
 (diff-auto-refine-mode)
 (diff-minor-mode)
 (dired-hide-details-mode)
 (display-fill-column-indicator-mode)
 (display-line-numbers-mode)
 (doom-docs-mode)
 (dtrt-indent-global-mode)
 (dtrt-indent-mode)
 (edebug-backtrace-mode)
 (edebug-mode)
 (edit-indirect--overlay)
 (editorconfig-mode)
 (eldoc-mode)
 (electric-layout-mode)
 (electric-quote-mode)
 (evil-collection-magit-toggle-text-minor-mode)
 (evil-markdown-mode)
 (evil-mc-mode)
 (evil-org-mode)
 (evil-visualstar-mode)
 (flycheck-mode)
 (flycheck-popup-tip-mode)
 (flyspell-mode)
 (general-override-local-mode)
 (git-commit-mode)
 (git-gutter-mode)
 (global-auto-revert-mode)
 (global-copilot-mode)
 (global-dash-fontify-mode)
 (global-display-fill-column-indicator-mode)
 (global-display-line-numbers-mode)
 (global-evil-mc-mode)
 (global-evil-visualstar-mode)
 (global-git-gutter-mode)
 (global-goto-address-mode)
 (global-hide-mode-line-mode)
 (global-hl-todo-mode)
 (global-prettify-symbols-mode)
 (global-reveal-mode)
 (global-semantic-highlight-edits-mode)
 (global-semantic-highlight-func-mode)
 (global-semantic-show-parser-state-mode)
 (global-semantic-show-unmatched-syntax-mode)
 (global-semantic-stickyfunc-mode)
 (global-vi-tilde-fringe-mode)
 (global-whitespace-mode)
 (global-whitespace-newline-mode)
 (goto-address-mode)
 (goto-address-prog-mode)
 (header-line-indent-mode)
 (hide-mode-line-mode)
 (highlight-numbers-mode)
 (highlight-quoted-mode)
 (hl-todo-mode)
 (horizontal-scroll-bar-mode)
 (hs-minor-mode)
 (ibuffer-auto-mode)
 (indent-tabs-mode)
 (isearch-mode)
 (ispell-minor-mode)
 (jit-lock-debug-mode)
 (lock-file-mode)
 (lost-selection-mode)
 (mac-auto-ascii-mode)
 (mac-auto-operator-composition-mode)
 (mac-font-panel-mode)
 (magit-auto-revert-mode)
 (magit-blame-mode)
 (magit-blame-read-only-mode)
 (magit-blob-mode)
 (magit-todos-mode)
 (magit-wip-after-apply-mode)
 (magit-wip-after-save-local-mode)
 (magit-wip-after-save-mode)
 (magit-wip-before-change-mode)
 (magit-wip-initial-backup-mode)
 (magit-wip-mode)
 (mail-abbrevs-mode)
 (markdown-live-preview-mode)
 (menu-bar-mode)
 (mml-mode)
 (mouse-wheel-mode)
 (next-error-follow-minor-mode)
 (org-capture-mode)
 (org-cdlatex-mode)
 (org-indent-mode)
 (org-list-checkbox-radio-mode)
 (org-num-mode)
 (org-src-mode)
 (org-table-follow-field-mode)
 (org-table-header-line-mode)
 (orgtbl-mode)
 (outline-minor-mode)
 (overwrite-mode)
 (paragraph-indent-minor-mode)
 (pcre-mode)
 (prettify-symbols-mode)
 (racket-hash-lang-repl-mode)
 (racket-smart-open-bracket-mode)
 (racket-xp-mode)
 (rainbow-delimiters-mode)
 (read-extended-command-mode)
 (rectangle-mark-mode)
 (reveal-mode)
 (rxt--read-pcre-mode)
 (rxt-global-mode)
 (rxt-mode)
 (semantic-highlight-edits-mode)
 (semantic-highlight-func-mode)
 (semantic-mode)
 (semantic-show-parser-state-mode)
 (semantic-show-unmatched-syntax-mode)
 (semantic-stickyfunc-mode)
 (sh-electric-here-document-mode)
 (shell-command-with-editor-mode)
 (shell-highlight-undef-mode)
 (show-smartparens-mode)
 (smartparens-global-strict-mode)
 (smartparens-mode)
 (smartparens-strict-mode)
 (smerge-mode)
 (so-long-minor-mode)
 (tab-bar-history-mode)
 (tab-bar-mode)
 (temp-buffer-resize-mode)
 (text-scale-mode)
 (toc-org-mode)
 (tool-bar-mode)
 (tooltip-mode)
 (transient-resume-mode)
 (treesit-explore-mode)
 (treesit-inspect-mode)
 (undelete-frame-mode)
 (undo-fu-session-mode)
 (url-handler-mode)
 (use-hard-newlines)
 (vc-dir-git-mode)
 (vc-parent-buffer)
 (vi-tilde-fringe-mode)
 (view-mode)
 (visible-mode)
 (which-function-mode)
 (whitespace-mode)
 (whitespace-newline-mode)
 (with-editor-mode)
 (ws-butler-mode)
 (xref-etags-mode))

I guess another way to look at it is, this is a feature request to implement the support for drracket:quote-matches. This would make ' paired up in #lang rhombus, but not in #lang racket. On the other hand, this would make | paired in #lang racket, but not in #lang rhombus.

It already does use both paren-matches and quote-matches from the #lang to make an Emacs "char syntax table".

As a result, for me racket-hash-lang-mode already behaves as you describe.

With #lang racket pressing ' gives just that.

With #lang rhombus it gives you a pair, '' -- provided you have something like electric-pair-mode or electric-pair-local-mode enabled, which does this kind of thing.

Hmm. You are right about single quote. If I disable smartparens-mode (which is what I mainly use) and enable electric-pair-mode, I get the behavior that you described. I assumed that racket-hash-lang-mode didn't consult quote-matches because I grepped it (incorrectly) from the repo and found no match.

But what is confusing to me is that there's still a problem with vertical bar |. In #lang racket, | is a part of drracket:quote-matches, so it should be paired up. However, even with electric-pair-mode on (and smartparens-mode off), I can't get | to be paired up. Can you reproduce this issue?

To sum up in my understanding (which could be wrong), there are two issues here:

  • The first issue is that smartparens-mode will always insert paired '. The fix on my end is to disable it and use electric-pair-mode instead.
  • The second issue is that | is not paired. I don't know what is going on here.

In classic racket-mode | doesn't electric-pair, FWIW.

I over-simplified when I said:

It already does use both paren-matches and quote-matches from the #lang to make an Emacs "char syntax table".

Actually (from racket--hash-lang-on-new-lang):

      ;; If the lang uses racket-grouping-position, i.e. it uses
      ;; s-expressions, then use racket-mode-syntax-table. That way
      ;; other Emacs features and packages are more likely to work.
      ;; Otherwise, assume nothing about the lang and set a "plain"
      ;; syntax table where no characters are assumed to delimit
      ;; parens, comments, or strings.
      (set-syntax-table (if (plist-get plist 'racket-grouping)
                            racket-mode-syntax-table
                          (racket--make-non-sexp-syntax-table
                           (plist-get plist 'paren-matches)
                           (plist-get plist 'quote-matches))))

The first sentence is what matters here. We use drracket:grouping-position as a signal for sexp langs. #lang racket racket-hash-lang-mode uses the classic racket-mode char syntax table. It gets the behavior where | doesn't electric-pair.

[The last sentence of that comment needs to be updated. Better: "Otherwise, make a 'plain' syntax-table assuming nothing but what the lang reports for parens and quotes."]

p.s. My brain is overflowing with #669 at the moment so I'm just describing how it's intended to work. If you think it could/should work differently, I'd be happy to discuss more, I might just need more time... 😄

No worries! Take your time :)

I've been looking at this again.

I'm having trouble figuring out how to make electric-pairs-mode work well in all cases.

  • electric-pairs-mode relies primarily on the buffer's char-syntax table, with an electric-pair-pairs variable as a fallback, which is a list of pairs of chars. Note the word "char", singular. Langs like rhombus that include multi-char parens like '( and )' are awkward.
  • Despite me marking comment tokens with syntax-table text-properties, sometimes electric-pairs-mode gets confused about whether it should act inside comments. e.g. typing "don't" in a comment shouldn't result in "don't'", but sometimes it does.
  • rhombus says paren-matches are (("(" . ")") ("[" . "]") ("{" . "}") ("'(" . ")'") ("«" . "»")) and quote-matches are ("\"" "'").
  • although rhombus includes ' in quote-matches meta-data it actually lexes/tokenizes it as 'parenthesis. Is its quote-matches wrong??
  • For purposes of auto-matching when you type, should typing ' go ahead and insert '' then when you do ( add the () inside to get '()'? That makes sense but that would probably need its parens-matches list to be ( and ) and ' and ' -- but not '( and )'.
  • Maybe the above just means that rhombus' meta-data needs to be changed ... but maybe DrR needs the status quo data.
  • Regardless, I wonder if racket-hash-lang-mode needs to adds its own simple delimiter-matching feature, as an alternative to electric-pairs-mode and similar modes. Maybe the square peg just won't ever fit in the round Emacs ecosystem? (Similar to how we have our own expression navigation commands using the lang's supplied notion of "grouping-position", instead of trying to make forward-exp-function work in all cases well enough to use the normal Emacs commands.)

I guess one unresolved issue is whether Racket Mode should support smartparens as an alternative to electric-pair. I'm perfectly OK if there's no support, but the fact that the Racket Mode documentation does mention smartparens led me to believe that it was supported.

Oh, and thank you for your work!!!

Good point.

The section about paredit starts with this note:

Note: If you use racket-hash-lang-mode, you can use racket-hash-lang-mode-hook to enable/disable paredit based on the specific #lang.

So:

  1. The note is wrong. It should talk about using the racket-hash-lang-module-language-hook. And add, hey look at the helpful example there.
  2. The example there should probably explain that any paren-matching mode -- smartparens, paredit, electric-pair-mode, whatever -- is unlikely to work well unless the module language is "rackety".
  3. The smartparens section should get an equivalent (improved) note.

Although documentation is almost as hard as naming things, commit 5d3288b tries to do what I just described.