Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I guess my programming working style is not unique. I develop programs one cell at a time, and when I have some confidence about what I did, I put all my mess together refining the code, building up functions from nailed constructs, scoping variables, etc.

That usually means being on a dirt road most of the time: evaluating a lot of cells, inspecting FullForms, checking different conditions, clicking the |Show More| button, etc.

Before I perform any action on my ongoing .nb I should save my work in advance.
But alas! My mind is weak, my memory fades and my feeble fingers are too quick for my tired eyes.
So, when I press Shift-Enter or |Show More| and my Front End hangs, I remember Michael Douglas' D-FENS character in "Falling Down" with utmost sympathy.

I would like an automatic way to save my input cells (not the output ones, as they could be really large) in a backup .nb file before and every time any computation is requested. How?

PS:
I must confess, my FrontEnd-fu is still in its teens.
I think $pre could be set to the desired command, but I don't know how to select only the input cells and save them programmatically.

share|improve this question
1  
Related (possible duplicate): mathematica.stackexchange.com/q/6435/5 –  rm -rf Jul 27 '12 at 3:33
    
@R.M perhaps, but at first sight he is asking for a timed backup, which is not my target –  belisarius Jul 27 '12 at 3:36
    
There's the NotebookAutoSave option in the option inspector, or SetOptions[EvaluationNotebook[], NotebookAutoSave -> True]. This saves after each evaluation---not precisely what you asked for, but may be useful. –  Szabolcs Jul 27 '12 at 6:54
    
@Szabolcs Yes, two main differences: 1) I lost my last editing session 2) I don't like my "emergency backup" to overwrite the original file (as I perhaps made some mistake and don't want to permanently regret it) –  belisarius Jul 27 '12 at 13:40

2 Answers 2

up vote 14 down vote accepted

The following definition to $Pre will save a copy of your notebook as filename.nb.backup.nb, where filename.nb is your original notebook and strip it of all output cells every time you evaluate a cell.

$Pre = With[
    {nb = NotebookPut[NotebookGet[EvaluationNotebook[]]]},
    FrontEndExecute[{
        FrontEnd`NotebookFind[nb, "Output", All, CellStyle, AutoScroll -> False], 
        FrontEnd`FrontEndToken["Clear"]
    }];
    NotebookSave[nb, NotebookFileName[] <> ".backup"];
    NotebookClose[nb];
    #
] &
share|improve this answer
1  
Seems to work pretty well! –  belisarius Jul 27 '12 at 4:05
1  
It will change the result of evaluation in some special cases, for example when you evaluate an expression with Head Sequence or Unevaluated. Probably $PreRead would be more universal. –  Alexey Popkov Jul 27 '12 at 6:14
2  
I don't like auto-save as it seems to inevitably result in the far more frustrating event of auto-saving just after an accidental deletion or other major incorrect change. By saving to a separate backup notebook you eliminate this. +1 –  Mr.Wizard Jul 27 '12 at 10:22
    
One of my fu-moves is Alt-/, which toggles comments. I save very frequently, and almost always modify code by copying it into a new cell and tweaking it there, commenting out the old version when necessary. An accident with the code being tweaked, then, is isolated to a copy, and I can always safely back up. This is a verbose style, and the final notebooks need a LOT of cleanup, but it's robust and easy on the fingers. –  Reb.Cabin Jul 31 '12 at 20:36
    
@Reb.Cabin Easier than an automated solution? ;) –  rm -rf Jul 31 '12 at 20:39

Motivated by the same concerns, I wrote the following code and added it to my init.m file. There are two main entries you'll want to change to use this. The global variable $SwapDirectory is where the swap files are saved (by swap file, I mean it in the VIm sense; an "extra" copy of your notebook, separate from your manually saved copy that periodically saves any new work). The swap files are organized within the swap directory in a directory structure which "mirrors" their original file locations, and have ".swp" appended to their file names. The other variable you might want to change is the number of seconds between autosaves, indicated by the "300" (corresponding to 5 minutes) near the bottom of the code below. At the appropriate times, this code will (automatically in the background) save swap files for ALL open notebooks, unless they are unmodified from their manually-saved versions (this exception makes the code more efficient, and more importantly, prevents the storage of swap files for documentation notebooks, for example).

In its current form, the code does not filter for only the input cells, but hopefully you can use the other answers to make that modification yourself.

Some things to note:
1) the Mathematica Put command seems to have trouble writing to network drives, even when offline access is enabled. Therefore, it is probably best to choose a SwapDirectory that is on your local machine.

2) Within SwapDirectory, you should create a sub-directory called "Recovery". This is where the AutoSaveSwap routine will make an initial save of any notebooks for which there is NO existing manual save location.

3) Simply evaluate

RecoverSwap["filePath"]
where "filePath" is a string representing the filePath of the MANUALLY-SAVED copy of the file (i.e., not the file that was created by AutoSave). This will then pop up a window containing the most recent auto-saved version of the file. The manually saved version is NEVER overwritten, unless you explicitly choose to do so. Once the recovered version pops up, you can save it whereever you like, or discard it at your discretion.

4) You should probably add this code to the KERNEL version of init.m ($UserBaseDirectory/Kernel/init.m) rather than the frontend version... this way, if you quit and restart the kernel, the autosave feature will also restart. On the other hand, this means that you must evaluate at least one expression after each start or restart to begin auto-saving. Once this initial evaluation is done, you do NOT need to have evaluated a cell for it to be backed up (unlike the built-in autosave utility).

Hope this helps someone! Feel free to respond with any questions, suggestions, or requests for improvement you may have. And, if you find this post useful, upvotes would be most appeciated! Take care.


$SwapDirectory= "C:\\Users\\pacoj\\Swap Files\\"; 
SaveSwap[nb_NotebookObject]:=Module[
    {fileName, swapFileName, nbout, nbdir, nbdirout, recoveryDir},
    If[ ! SameQ[Quiet[NotebookFileName[nb]], $Failed],
    (* if the notebook is already saved to the file system *)
        fileName = Last[ FileNameSplit[ NotebookFileName[nb]] ];
        swapFileName = fileName <> ".swp";
        nbdir = Rest[FileNameSplit @ NotebookDirectory[nb]];
        nbdirout= FileNameJoin[ FileNameSplit[$SwapDirectory]~Join~nbdir]<>"\\";
        If[!DirectoryQ[nbdirout], CreateDirectory[nbdirout]];
        nbout = NotebookGet[nb];
        Put[nbout, nbdirout <> swapFileName],
        (* else, if the file has never been saved, save as untitled *)
        recoveryDir= $SwapDirectory <> "Recovery\\";
        fileName= ("WindowTitle" /. NotebookInformation[nb])<>".nb";
        NotebookSave[nb, recoveryDir <> fileName]
    ] 
]; 
RecoverSwap::noswp= "swap file 1 not found in expected location";
    RecoverSwap[nbfilename_String]:=Module[
    {fileName, swapFileName, nbin, nbdir, nbdirout},
    fileName= Last[ FileNameSplit[ nbfilename] ];
    swapFileName= fileName <> ".swp";
    nbdir= Most[ Rest[FileNameSplit @ nbfilename] ];
    nbdirout= FileNameJoin[ FileNameSplit[$SwapDirectory]~Join~nbdir]<>"\\";
    If[ FileNames[swapFileName, {nbdirout}] == {},
       Message[RecoverSwap::noswp,nbdirout <> swapFileName]; Return[],
       nbin= Get[nbdirout <> swapFileName]; NotebookPut[nbin]
    ] 
]; 
AutoSaveSwaps= CreateScheduledTask[
    SaveSwap /@ Select[Notebooks[], "ModifiedInMemory" /. NotebookInformation[#]&],
    300 
] 
StartScheduledTask[AutoSaveSwaps] 

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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