11
$\begingroup$

When you type ->, it is automatically turned into the \[Rule] character. Can we configure our frontends to do the same for the [[ and ]] characters, which could be turned into \[LeftDoubleBracket] and \[RightDoubleBracket]?

You can easily do this manually by typing <esc>[[<esc>, but I would like to do this automatically.

This question was originally asked by me in the chat and has answers there; this question is so that those answers can be added and kept for the community.

$\endgroup$
4
  • 3
    $\begingroup$ Do you really want that when writing func[func2[arg]]? I changed KeyEventTranslations.tr to have <kbd>Alt gr</kbd>+<kbd>i</kbd>/<kbd>o</kbd>. $\endgroup$
    – Coolwater
    Apr 7, 2019 at 19:04
  • $\begingroup$ Yes, this is part of the problem. Ideally it would only happen after you have typed the matching [[, but more than likely it needs to be done at shift-enter at the earliest. For [[ the problem is simpler because there is no time when you would type [[ except for as part of a Part expression. $\endgroup$
    – Carl Lange
    Apr 7, 2019 at 19:05
  • 1
    $\begingroup$ closely related: 98175 $\endgroup$
    – Kuba
    Apr 7, 2019 at 20:50
  • 1
    $\begingroup$ Related using KeyEventTranslations 5221 $\endgroup$ Apr 7, 2019 at 21:41

4 Answers 4

9
$\begingroup$

You can use CellEpilog to modify the contents of the cell upon evaluation:

SetOptions[EvaluationNotebook[],
 CellEpilog :> If[
   ! TrueQ[
     Max@CurrentValue[CellChangeTimes] <= 
       CurrentValue[EvaluationCell[], {TaggingRules, "autoreplaceTime"}]
     ],
   CurrentValue[EvaluationCell[], {TaggingRules, "autoreplaceTime"}] = 
     Max@CurrentValue[CellChangeTimes];
   NotebookWrite[
    EvaluationCell[],
    Replace[NotebookRead[EvaluationCell[]],
     RowBox@{head_, "[", RowBox@{"[", part_, "]"}, "]"} :>
      RowBox@{head, "\[LeftDoubleBracket]", part, "\[RightDoubleBracket]"}, All]
    ]
   ]
 ]

Demonstration: https://i.stack.imgur.com/4V5WJ.gif

This could of course be extended to do similar replacements for <|…|> etc. To minimize the performance hit, we keep track of the last CellChangeTimes value and only rerun the replacement code when it changed.

The nice thing about an approach like this (i.e. manipulation of the cell's box structure) is that it's rather straightforward to identify the bracket pairs to replace, since the front-end does all the necessary parsing for us.

An alternative to putting the code into CellEpilog might be to create a menu item for it, one could also modify the "Evaluate cells" menu item to run this code on all selected cells first.

$\endgroup$
9
$\begingroup$

Here is a stylesheet solution modeled after Lucas' answer, except that I use CellEventActions to tie the transformation to a keyboard short cut:

SetOptions[
    EvaluationNotebook[],
    StyleDefinitions -> Notebook[{
        Cell[StyleData[StyleDefinitions->"Default.nb"]],
        Cell[StyleData["Input"],
            CellEventActions -> {
                {"KeyDown","y"} :> If[CurrentValue["ControlKey"],
                    NotebookWrite[
                        EvaluationCell[],
                        With[{read=NotebookRead[EvaluationCell[]]},
                            Cell[
                                ReplaceAll[
                                    First[read],
                                    RuleDelayed[
                                        RowBox[{head_,"[",RowBox[{"[",part_,"]"}],"]"}],
                                        RowBox[{head,"\[LeftDoubleBracket]",part,"\[RightDoubleBracket]"}]
                                    ]
                                ],
                                Sequence@@Rest[read]
                            ]
                        ]
                    ];
                    SelectionMove[EvaluationNotebook[],Before,CellContents];
                    SelectionMove[EvaluationNotebook[],After,CellContents]
                ],
                PassEventsDown->True
            }]
        },
        StyleDefinitions->"PrivateStylesheetFormatting.nb"
    ]
]

Using the short cut ctrl + y will convert [[ and ]] to the double bracketing bar versions.

$\endgroup$
5
$\begingroup$

It's maybe not exactly what you want but you can always select the cell, expression, or part of it ( Ctrl-. is helpful for this ) and then do Ctrl-Shift-n to change the selection to Standard Form. This includes the changes to [[ and ]] and doesn't require any set up or customisation.

$\endgroup$
5
$\begingroup$

As of version 12.3, this behaviour is now introduced as the default, governed by the AutoOperatorRenderings setting. It now works quite like "programming ligatures" in other IDEs and text editors, in that when you type [[, \[LeftDoubleBracket] is displayed, but when you edit it (for example by hitting backspace), the actual typed characters are modified - basically, just as -> and \[Rule].

From the 12.3 announcement blog:

It might seem odd that it’s taken so many years to go from “automatic →” to “automatic 〚 〛”. But it’s a lot more subtle than you might think, and in fact it’s required a whole new as-you-type approach to code rendering. Back in Version 3.0, the idea was to replace -> with → when you type it. So, for example, if you then backspace one character, you’ll delete the whole →, rather than simply “removing the >” and reverting to -.

But if you’re dealing with [[ ... ]] you can’t just do this kind of “local replacement” without the potential for confusion with some ]] showing up as 〛 while others break apart into ]] as a result of routine editing.

In Version 12.3 what we’re doing is not to make replacements at all, but instead just to render specified sequences of characters (like ]]) in special ways. The result is that we can support very general “ligature-like” behavior, and that backspacing will always exactly reverse characters that were entered.

Which seems like a bit of a Wolfram-y way to describe a feature that's been in text editing at large for at least 30 years, with standard ligatures like "fi", and in programming text editors for programming-specific use cases for probably the last ten or so, becoming quite popular with fonts like Fira Code.

(Actually, to be fair, there's some semantic parsing cleverness going on with regard to ]] working correctly at the end of a Part expression, and not replacing at the end of a bunch of function calls.)

Anyway, it's nice to see this as the default behaviour now - I find my code significantly easier to read in this way.

$\endgroup$
2
  • $\begingroup$ Carl, Do you know how to enable this behavior in a presentation notebook? It works fine with notebooks based on the Default.nb stylesheet but not with notebooks based on the PresenterTools/Default.nb stylesheet. $\endgroup$ Aug 11, 2021 at 13:16
  • $\begingroup$ Unfortunately, I haven't got a clue. Wish I could help but I know exactly nothing about how that works. Perhaps you could do something like CreateDocument[..., AutoOperatorRenderings -> ....] instead of using the menu bar option? $\endgroup$
    – Carl Lange
    Aug 11, 2021 at 15:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.