greghendershott / racket-mode

Emacs major and minor modes for Racket: edit, REPL, check-syntax, debug, profile, packages, and more.
https://www.racket-mode.com/
GNU General Public License v3.0
683 stars 93 forks source link

Automatically adjust opening square brackets? #74

Closed 3rdLaw closed 9 years ago

3rdLaw commented 9 years ago

Does racket mode have an option like drracket's Automatically adjust opening square brackets?

This is where DrRacket intelligently rewrites open square brackets, usually to an open parenthesis but sometimes as '[' based on context, as described here. This is a precious feature as it means you never have to use the Shift key.

greghendershott commented 9 years ago

When the blinking caret is next to a parenthesis, DrRacket shades the region between the parenthesis and its matching parenthesis. This feature is especially helpful when for balancing parentheses to complete an expression.

The standard Emacs thing for this is show-paren-mode.

When you type Enter or Return, the editor inserts a new line and automatically indents it. To make DrRacket re-indent an existing line, move the blinking caret to the line and hit the Tab key. (The caret can be anywhere in the line.) You can re-indent an entire region by selecting the region and typing Tab.

Yes, racket-mode does this.

Also, as of a very recent commit: When things are already indented, TAB will also try completion-at-point.

If you type a closing parenthesis ), a closing square bracket ], or a closing curly brace }, and if DrRacket can match it back to some earlier opening parenthesis, bracket, or brace, then DrRacket changes what you type to match.

Yes, racket-mode does this.

However:

DrRacket also rewrites open square brackets, usually to an open parenthesis. There are some exceptions where opening square brackets are not automatically changed to parentheses:

If the square bracket is after cond-like keyword, potentially skipping some of the sub-expressions in the cond-like expression (for example, in a case expression, the square brackets start in the second sub-expression).

  • If the square bracket begins a new expression immediately after a local-like keyword. Note that the second expression after a local-like keyword will automatically become an ordinary parenthesis.
  • If the square bracket is after a parenthesis that is after a letrec-like keyword,
  • If the square bracket is in a sequence and the s-expression before in the sequence is a compound expression, DrRacket uses the same kind parenthesis, brace, or bracket as before, or
  • If the square bracket is in the middle of string, comment, character, or symbol.

The upshot of DrRacket’s help is that you can always use the (presumably unshifted) square brackets on your keyboard to type parenthesis. For example, when typing

   (define (length l)
     (cond
      [(empty? l) 0]
      [else (+ 1 (length (rest l)))]))

If you always type [ and ] where any of the square brackets or parentheses appear, DrRacket will change the square brackets to match the code above.

racket-mode does not do this. Which IIUC is precisely the part you want?

It definitely sounds handy. I'm not sure how hard it would be to implement.

greghendershott commented 9 years ago

@3rdLaw I pushed this to a topic branch. I'm not yet ready to merge it to master.

If you'd like to go ahead and try it from the topic branch, let me know if you need help doing so, and please let me know if it works as you expect.

Note that you will have to Customize racket-smart-open-bracket-enabled to t. It's nil (off) by default. (After using it for awhile, I like it. But I think it may be confusing unless people intentionally turn it on. So even after I merge to master, I expect to leave it turned off by default. (FWIW that's how DrR defaults it, at least for me.))

greghendershott commented 9 years ago

On Twitter @technomancy effectively pointed out that this should be (or at least could be) compatible with paredit-mode.

Status quo: paredit is rebinding ( to paredit-open-round and [ to paredit-open-square. So this racket-mode feature is bypassed.

Instead: racket-mode would notice paredit-mode being active (or notice these specific rebindings happened), rebind back to itself, and decide which of those paredit functions to call (instead of directly insert-ing a ( or [). That would allow paredit to do all its usual good stuff.

3rdLaw commented 9 years ago

Wow Greg: such fast service :) I cloned the branch, and it's working as expected! I just made up a brief pull request to include the match-lambda family as well as allow for newline to terminate the regexps.

I'm stilll getting used to transitioning from drracket to emacs, so while your comment about how this should play nicely with paredit-mode makes sense to me, I'm afraid I can't add anything just yet.

And leaving it off by default makes sense, although once you get used to having it on you'll never turn it off!

greghendershott commented 9 years ago

@3rdLaw Thanks for trying it and letting me know it's working for you as expected.

I reopened the issue because I still want to ensure paredit compatibility. I just pushed a commit for that, although it needs more testing. Unfortunately there are quite a few permutations:

The interesting thing about using smart open bracket and paredit, both? You don't really ever need to press ] (except just as navigation) -- just [.

greghendershott commented 9 years ago

So I ran through all the permutations manually (ouch) and all checked out OK.

I could probably go ahead and merge to master, but will probably wait a day or two to see if I (or anyone else) notices anything.

3rdLaw commented 9 years ago

Awesome! I'll play around with it more in the next couple days as well. Thanks again.