It's not possible to open notebooks in multiple tabs, at least for now, but it's possible to add tabs to a single notebook without too much difficulty.
Initialize a little cache plus some formatting options:
$notebookCache = <||>;
$activeBG = GrayLevel[.7]; $activeBD = Directive[GrayLevel[.6], Thick];
$inactiveBG = GrayLevel[.9]; $inactiveBD = GrayLevel[.7];
$tabShape =
With[{height = 10, flatspan = 30},
Polygon@Join[
Table[
{x, height*LogisticSigmoid[x]},
{x, Range[-4, 4, .5]}
],
Table[
{flatspan + 8 + x, height*LogisticSigmoid[-x]},
{x, Range[-4, 4, .5]}
]
]
];
Make a little tabbed event handler:
tabObject[name_] :=
Graphics[{
Dynamic@
If[($activeNotebook === name),
$activeBG,
$inactiveBG
],
Dynamic@
If[($activeNotebook === name),
EdgeForm@$activeBD,
EdgeForm@$inactiveBD
],
$tabShape,
{Black, Inset[name(*,
Mean@Cases[$tabShape,{_,_},\[Infinity]]*)]}
},
ImageMargins -> {{0, 0}, {0, 3}},
ImagePadding -> 0,
PlotRangePadding -> None
];
tabButton[name_] :=
EventHandler[
tabObject@name,
"MouseClicked" :>
If[$activeNotebook =!= name,
With[{nb = NotebookGet@EvaluationNotebook[]},
$notebookCache[$activeNotebook] = First@nb;
NotebookPut[
ReplacePart[nb,
1 -> Replace[
$notebookCache[name],
_Missing :> ($notebookCache[name] = {})
]
],
EvaluationNotebook[]
];
$activeNotebook = name
]
]
];
And then create a docked cell form for this:
tabDock[tabNames : {__}, ops___] :=
Cell[BoxData@ToBoxes@
Row@Table[
tabButton[t],
{t, tabNames}],
ops,
CellMargins -> {{0, 0}, {0, 0}},
CellFrame -> {{0, 0}, {2, 0}},
CellFrameColor -> Dynamic@$activeBD,
CellFrameMargins -> {{0, 0}, {-2, 3}},
Background -> LightBlue]
By wrapping all of this in a DynamicModule
one can get a notebook that permanently saves its $notebookCache
.
Then without too much difficulty, one could then write a custom editor that would save these tabs to their own .nb files, by applying a NotebookEventAction
binding to {"MenuCommand", "Save"}
and voilà, you have a notebook front end that supports tabination. It's hacky, yes, but happily not too hard.
Line numbering can be done (in cells with PageWidth->Infinity
set) by checking for \[IndentingNewLine]
counts from a CellFrameLabels
cell, for example:
setLineNumbering[cell_] :=
SetOptions[
cell,
{
PageWidth -> Infinity,
CellFrameLabels -> {
{Cell[BoxData@ToBoxes@Dynamic[
StringJoin@Riffle[
ToString /@
Range@Count[NotebookRead@cell,
"\[IndentingNewLine]", \[Infinity]],
"\n"
],
UpdateInterval -> .01,
TrackedSymbols :> {}
],
"Output",
FontWeight -> Plain
], None},
{None, None}
}
}
]
I assume these are features that have been ignored because I believe Mathematica isn't necessarily prioritized as a code development environment. However, by including this sort of functionality in a development stylesheet one can create a richer IDE without requiring WRI to code it into Mathematica.