4
$\begingroup$

I have the following questions:

Is there a way to open multiple mathematica notebooks in tab group rather that each mathematica notebook opened in separate window?

Is there a way to display line numbers in mathematica notebooks as line numbers are displayed in differnt text editors?

$\endgroup$
4
  • 1
    $\begingroup$ You can create palette with default position under the menu of mma with listed links to all opened notebooks. $\endgroup$
    – Kuba
    Jun 8, 2014 at 8:21
  • $\begingroup$ @Kuba +1, I confirm this is a useful thing to do. I have automatically generated palettes with directory trees from the projects I'm working on. Instead of having tabs, I minimize notebooks and call those minimized now via palette(s). With FrontEnd, what Kuba suggested in their comment is probably as close to tab view as it gets for now. $\endgroup$
    – akater
    Jun 8, 2014 at 22:09
  • $\begingroup$ Could you please point out how to 'create palette with default position' and how to 'automatically generate palette with directory trees?' $\endgroup$
    – aark
    Jun 9, 2014 at 10:10
  • $\begingroup$ I'm not myself convinced that this is a satisfactory answer, but I think the current official WRI opinion to that is that for these features you should have a look at the Wolfram Workbench (mma eclipse plugin), which is the recommended environment when writing more complex Mathematica (Wolfram Language) code. So AFAIK there is nothing out of the box and probably also nothing to be expected in the near future, but of course you can use the workarounds that others have shown here. Maybe haluritan's mma-plugin for IntelliJ-IDEA is also worth a try... $\endgroup$ Nov 23, 2016 at 19:18

2 Answers 2

2
$\begingroup$

As far as I know, the answers are NO and NO.

The front end does not support tabbed panes. It doesn't even support splitting a notebook window into two panes, which is something most code editors can do.

The code editor for input cells is simply not line oriented. Select an input cell with some moderately complicated code, and type Ctrl+Shift+e. This will show you how the front end sees your code. It will probably not be what you were expecting.

$\endgroup$
5
$\begingroup$

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.

$\endgroup$
7
  • 1
    $\begingroup$ I decided to put my suggestions for tabbing into practice and created a package that more or less supports tabbing, opening multiple notebook files, saving those files, etc. It's got some glitches, but it works. The thing can be found here: github.com/b3m2a1/mathematica-tools $\endgroup$
    – b3m2a1
    Mar 31, 2017 at 22:26
  • $\begingroup$ hi b3m2a1, can your code be installed as a package. I made the file with tabs but when I opened it after closing, it doesn't let me switch tabs - SwitchTabs::nonb: NotebookObject NotebookObject[150] does not exist $\endgroup$
    – cleanplay
    Jun 20, 2017 at 1:03
  • 1
    $\begingroup$ @cleanplay sure thing. I think I know what's going on there (what's on GitHub is just a slapdash prototype). I'll polish it up and get a new version published (although I can't promise I'll get to it before Wednesday). I think I've got it as a stylesheet somewhere, to... $\endgroup$
    – b3m2a1
    Jun 20, 2017 at 1:36
  • 1
    $\begingroup$ @cleanplay looking back at the implementation each tab was supposed to be saving as it's own notebook when you pressed the "Save" button in that tab. I cannot remember why I did that, but that's how it seems to work. Alternatively if you called TabNotebookSaveBindings that would have set up the bindings so that when you used Command-S it save the tabs. If neither of those apply to you, I'm enormously sorry, but I'm not sure how to get the data back. I wrote this not using TaggingRules for some reason, so the data was all stored in the kernel. $\endgroup$
    – b3m2a1
    Jun 20, 2017 at 1:59
  • 1
    $\begingroup$ @cleanplay I fixed it so that notebooks now use TaggingRules so the tabs are automatically saved in the notebook itself. I also got the tabs to reload the package automatically when you open it. Unfortunately this won't get back the work you lost, but at least this version is somewhat more loss-proof. I'm sure there are still bugs hiding places though. $\endgroup$
    – b3m2a1
    Jun 20, 2017 at 3:26

Your Answer

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

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