Wolfram Research

Function Repository Resource:

SaveReadableNotebook

Source Notebook

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

Contributed by: Richard Hennigan (Wolfram Research)

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

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

Details and Options

In ResourceFunction["SaveReadableNotebook"][notebook,], notebook can be any of the following:
Notebook[] a notebook expression
NotebookObject[] a currently open notebook
"path" a pathname 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[2]=
In[3]:=
ResourceFunction["SaveReadableNotebook"][nb, FileNameJoin[{$TemporaryDirectory, "readable.nb"}]]
Out[3]=
In[4]:=
FilePrint[%]

Save a Notebook expression:

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

Save a copy of a notebook file that already exists:

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

Options

ExcludedCellOptions

By default, some cell options are automatically stripped:

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

Preserve all cell options:

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

Ignore specific options:

In[11]:=
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[12]:=
ResourceFunction["SaveReadableNotebook"][
  Notebook[{Cell["Hello world", "Text"]}, WindowSize -> {1000, 1000}],
  FileNameJoin[{$TemporaryDirectory, "readable.nb"}]
  ] // FilePrint

Preserve all notebook options:

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

Ignore specific options:

In[14]:=
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[15]:=
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[16]:=
NotebookSave[nb, FileNameJoin[{$TemporaryDirectory, "changed.nb"}]];

It can be difficult to visualize what actually changed:

In[17]:=
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[18]:=
fileDiff[
 FileNameJoin[{$TemporaryDirectory, "original.nb"}],
 FileNameJoin[{$TemporaryDirectory, "changed.nb"}],
 25
 ]
Out[19]=

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

In[20]:=
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[21]=

The formatted notebook files still open normally:

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

Requirements

Wolfram Language 11.3 (March 2018) or above

Resource History

See Also

License Information