Wolfram Computation Meets Knowledge

SaveReadableNotebook

Contributed by: Richard Hennigan (Wolfram Research)

Save a notebook to a file that’s formatted to maximize readability when viewing changes in version control systems

ResourceFunction["SaveReadableNotebook"][notebook,"file"]

saves the notebook to "file" as a Notebook expression formatted for readability.

Details and Options

In ResourceFunction["SaveReadableNotebook"][notebook,], the notebook can be any of the following:
Notebook [ ] a notebook expression
NotebookObject [ ] a currently open notebook
"path" a path name of a saved notebook
SaveReadableNotebook has the following options:
"ExcludedCellOptions" { CellChangeTimes , ExpressionUUID } cell options that should be discarded from the saved notebook
"ExcludedNotebookOptions" { WindowSize , WindowMargins } notebook options that should be discarded

Examples

Basic Examples

Get the ResourceFunction:

In[1]:=
ResourceFunction["SaveReadableNotebook"]
Out[1]=

Save a NotebookObject to a human-readable file:

In[2]:=
nb = CreateDocument[{
   TextCell["Test notebook", "Title"],
   TextCell["This is a test", "Text"],
   ExpressionCell[Defer[1 + 1], "Input"],
   ExpressionCell[2, "Output"]
   }]
Out[218]=
In[219]:=
ResourceFunction["SaveReadableNotebook"][nb, FileNameJoin[{$TemporaryDirectory, "readable.nb"}]]
Out[219]=
In[220]:=
FilePrint[%]

Save a Notebook expression:

In[221]:=
ResourceFunction["SaveReadableNotebook"][
 Notebook[{Cell["Hello world", "Text"]}],
 FileNameJoin[{$TemporaryDirectory, "readable.nb"}]
 ]
Out[222]=
In[223]:=
FilePrint[%]

Save a copy of a notebook file that already exists:

In[224]:=
ResourceFunction["SaveReadableNotebook"][
 FileNameJoin[{$InstallationDirectory, "Documentation", "English", "System", "ExampleData", "document.nb"}],
 FileNameJoin[{$TemporaryDirectory, "readable.nb"}]
 ]
Out[226]=
In[227]:=
FilePrint[%, 15]

Options

ExcludedCellOptions

By default, some cell options are automatically stripped:

In[228]:=
ResourceFunction["SaveReadableNotebook"][
  Notebook[{Cell["Hello world", "Text", ExpressionUUID -> CreateUUID[]]}],
  FileNameJoin[{$TemporaryDirectory, "readable.nb"}]
  ] // FilePrint

Preserve all cell options:

In[229]:=
ResourceFunction["SaveReadableNotebook"][
  Notebook[{Cell["Hello world", "Text", ExpressionUUID -> CreateUUID[]]}],
  FileNameJoin[{$TemporaryDirectory, "readable.nb"}],
  "ExcludedCellOptions" -> {}
  ] // FilePrint

Ignore specific options:

In[230]:=
ResourceFunction["SaveReadableNotebook"][
  Notebook[{Cell["Hello world", "Text", FontColor -> Red, FontSize -> 14]}],
  FileNameJoin[{$TemporaryDirectory, "readable.nb"}],
  "ExcludedCellOptions" -> {FontColor}
  ] // FilePrint

ExcludedNotebookOptions

By default, some notebook options are automatically stripped:

In[231]:=
ResourceFunction["SaveReadableNotebook"][
  Notebook[{Cell["Hello world", "Text"]}, WindowSize -> {1000, 1000}],
  FileNameJoin[{$TemporaryDirectory, "readable.nb"}]
  ] // FilePrint

Preserve all notebook options:

In[232]:=
ResourceFunction["SaveReadableNotebook"][
  Notebook[{Cell["Hello world", "Text"]}, WindowSize -> {1000, 1000}],
  FileNameJoin[{$TemporaryDirectory, "readable.nb"}],
  "ExcludedNotebookOptions" -> {}
  ] // FilePrint

Ignore specific options:

In[233]:=
ResourceFunction["SaveReadableNotebook"][
  Notebook[{Cell["Hello world", "Text"]}, Background -> Red],
  FileNameJoin[{$TemporaryDirectory, "readable.nb"}],
  "ExcludedNotebookOptions" -> {Background}
  ] // FilePrint

Applications

SaveReadableNotebook can be used to create notebook files that are well-suited for version control systems that look at changes to files on a line-by-line basis.

Here’s a test notebook:

In[234]:=
nb = NotebookPut@Notebook[{
     Cell["Test notebook", "Title"],
     Cell["This is a test.", "Text"],
     Cell[BoxData[RowBox[{"1", "+", "1"}]], "Input"],
     Cell[BoxData["2"], "Output"]
     }];
NotebookSave[nb, FileNameJoin[{$TemporaryDirectory, "original.nb"}]];

Now make changes to the notebook then save to a new file:

In[235]:=
NotebookSave[nb, FileNameJoin[{$TemporaryDirectory, "changed.nb"}]];

It can be difficult to visualize what actually changed:

In[236]:=
fileDiff[file1_, file2_, max_: Infinity] :=
  Column@Take[
    Flatten@Replace[
      SequenceAlignment[Import[file1, "Lines"], Import[file2, "Lines"]],
      {{a___String}, {b___String}} :>
       Grid[{
         {Item[StringRiffle[{a}, "\n"], Background -> LightRed]},
         {Item[StringRiffle[{b}, "\n"], Background -> LightGreen]}
         }],
      {1}
      ],
    UpTo[max]
    ];
In[237]:=
fileDiff[
 FileNameJoin[{$TemporaryDirectory, "original.nb"}],
 FileNameJoin[{$TemporaryDirectory, "changed.nb"}],
 25
 ]
Out[238]=

Create a formatted version of each notebook and view changes for those files instead:

In[239]:=
ResourceFunction["SaveReadableNotebook"][
  FileNameJoin[{$TemporaryDirectory, "original.nb"}],
  FileNameJoin[{$TemporaryDirectory, "original_readable.nb"}]
  ];
ResourceFunction["SaveReadableNotebook"][
  FileNameJoin[{$TemporaryDirectory, "changed.nb"}],
  FileNameJoin[{$TemporaryDirectory, "changed_readable.nb"}]
  ];
fileDiff[
 FileNameJoin[{$TemporaryDirectory, "original_readable.nb"}],
 FileNameJoin[{$TemporaryDirectory, "changed_readable.nb"}]
 ]
Out[240]=

The formatted notebook files still open normally:

In[241]:=
NotebookOpen[
 FileNameJoin[{$TemporaryDirectory, "changed_readable.nb"}]]
Out[241]=

Resource History