# Get tab key to indent a block of code

Is there some way I can change Mathematica keyboard behavior so I could select a block of text/code, press tab, and the selection would be indented to right? Similarly, I would like shift+tab to produce indenting to the left.

If it were possible to get this behavior just for multi-line string text, or just in code cells, that would be great, too.

-
Normal mma cells don't have a clear notion of whitespace.. Tab works in code cells, but not for selected blocks, just as the first char of the line. If you're asking such questions, it's usually time to switch to WorkBench. Once I did my life became much easier:) For code cells this would still be very useful though (+1). – Ajasja Jan 6 '14 at 5:40
Hi @Ajasja. All my current important code is inside code cells or in packages. I tried workbench, but did not get used to it. I love the new autocomplete interface, collapse and expand code parts and the code execution in place for test, and it's not nice in Workbench. But maybe I have to try Workbench again. – Murta Jan 6 '14 at 10:34
Thanks for the accept. So, is my solution really usable for you? It is certainly not refined, and needs some more work to be robust and general. – Leonid Shifrin Feb 22 '14 at 16:19

The following solution seems to work reasonably well, at least on a few examples I have tested. It will be in the spirit of the one I gave to a rather similar earlier question you asked. I wasn't able to make the Tab key work, instead I bound the indenting to the CTRL+  combination - but in practice this is almost as easy as pressing Tab key. Also, the following is only a solution for indenting to the right. Indenting to the left is likely also possible, but might be a bit trickier to implement.

Here is the code. This is a generic function to construct a self-overwriting cell:

ClearAll[generateAutoOverwriteCell];
generateAutoOverwriteCell[
boxes : Except[_?OptionQ] : "",
type_String: "Input",
opts___?OptionQ
] :=
Module[{},
SelectionMove[EvaluationNotebook[], All, Cell, AutoScroll -> False];
SelectionMove[EvaluationNotebook[], Previous, Cell, AutoScroll -> False];
NotebookWrite[EvaluationNotebook[], Cell[BoxData[boxes], type, opts], All];
SelectionMove[EvaluationNotebook[], All, CellContents, AutoScroll -> False];
SelectionMove[EvaluationNotebook[], Previous, Character];
]


This is a relatively simple "formatter" for the selected part of your code:

ClearAll[process, $newlinePattern,$inner];

$newlinePattern = ("\n"|"\[IndentingNewLine]"); process[r_RowBox/;FreeQ[r,$newlinePattern]]:=RowBox[{"\t",r}];

process[x_]:=process[x,False];

process[RowBox[conts_List],flag_]:= RowBox[process[conts,flag]];

process[{left__,sep:$newlinePattern, right___}, flag_]:= { RowBox[{If[!TrueQ[$inner],"\t",Sequence@@{}],Sequence@@#}] & [
Block[{$inner = True},process[{left},False]] ] , sep , Sequence @@ Block[{$inner = False},process[{right}, True]]
};

process[{args__}/;FreeQ[{args},$newlinePattern],True]:={RowBox[{"\t",args}]}; process[x_,_]:=x/.block:{__,$newlinePattern, ___}:>process[block,False];


This is the key action rule, which I bound to the CTRL+  combination:

ClearAll[selectionTabRule]
selectionTabRule =
{"KeyDown", ""} :>
With[{nb = InputNotebook[]},
If[MemberQ[CurrentValue["ModifierKeys"], "Control"],
NotebookWrite[nb, process[sel]]
],
(* else *)
NotebookWrite[nb, ""]
]
];


Finally, here is the short-cut to construct such cells:

ClearAll[incell];
incell := generateAutoOverwriteCell[CellEventActions -> {selectionTabRule}];


Now, if you type incell into a new cell and evaluate, you will get a new cell with desired behavior. If you then copy and paste some code into this cell, you can start playing with it. Select the piece of code you'd like to move to the right, and press CTRL+ . The code you select should be a complete expression or a sequence of expressions.

I am sure there are bugs and limitations in this simple solution. It is more in the spirit of showing how things might work, than being a complete solution here. I just find it interesting to explore the possibilities the FrontEnd can give us.

-

Here is a much more modest try for blocks of code. :) You could expand it to cover text like @LeonidShifrin has shown. It could also be reversed to unindent.

SetOptions[$FrontEnd, FrontEndEventActions -> { {"KeyDown", "\t"} :> NotebookWrite[ InputNotebook[], Insert[ NotebookRead[InputNotebook[]] /. "\[IndentingNewLine]" -> Sequence["\[IndentingNewLine]", "\t"], "\t", {1, 1}]]}]  Select the whole lines (at least make sure you have the beginning of each line, including the first) and press tab: a b c  ... a ... b ... c - This is nice. Is it possible to retain basic tabbing (inserting a tab character) when there's no selection? – Rico Picone May 27 '14 at 4:17 @RicoPicone after :> check to see if CurrentValue["SelectionData"] ===$Failed (= nothing is selected), then decide what to do based on that. (Too cryptic?) – mfvonh May 27 '14 at 4:19
Thanks, I was able to get that working. However, I noticed that under many circumstances, the block-tabbing fails. Even when selecting entire lines, often only the first line is indented. Sometimes the other lines are also indented, but one line from the selection is not (like the second line). I was unable to detect rules for these occurrences, and I suspect some extra hidden characters are running around that I can't see (is there any way to show them?). – Rico Picone May 27 '14 at 15:28
@RicoPicone Yeah, that code won't work unless you have manually inserted line breaks (it's just adding a tab at the beginning of the selection and after every line break). Maybe what you want is changing LineIndent`. Press Ctrl+Shift+O, then type LineIndent in the "Lookup" box. You can change it for individual cells or the whole notebook that way. Or you can do it via the StyleSheet. – mfvonh May 27 '14 at 16:51
@RicoPicone Also, I am just repeating others here, but you may consider Wolfram Workbench or (in my opinion much better) halirutan's MMA plugin for IDEA. I find it much easier to code in an IDE and use notebooks for testing/etc. – mfvonh May 27 '14 at 16:55