Save upon compile any or all procedure files in procedure windows

These two functions allow specified procedure file windows to save automatically upon procedure compilation. I'm often writing out new code in a procedure window, and I hate losing code that I compiled but forgot to save. I think that Igor at least warns upon an attempt to close an experiment with a procedure file that's been modified without save, but that doesn't avoid losing new code due to an Igor or computer crash. It would be nice if Igor offered a built-in option to save upon compile--perhaps it does and I haven't found it yet. Even so, this function would allow one to save a back up automatically in a different file.

I originally imagined using this to automatically re-save my procedure in its associated file, but this can cause Igor 7 to think that the file was modified externally, leading to issues that are discussed in the code comments. It would be nice if there was one day a built in saveproc function that got around this issue. (To my knowledge the closest thing is closeproc/SAVE, but I don't want the procedure to close or the scroll position in the procedure window(s) to shift).

There are likely to be text encoding issues that I haven't worked out. I've only used this on Windows so far.

//Saves any procedures displayed in a procedure window. Include the AfterCompiledHook function below to call this after compiling procedure files
//In limited testing, UTF-8 encoding (#pragma TextEncoding = "UTF-8") re-opens better than Windows-1252 after saving in the way this procedure does (via fprintf with strings)
function saveProcs()
 
//some default settings
    String procsToBackup="*;"       //list (semi-colon delimited) any procedures that should be saved. ".ipf" should end each list item*
        //*with one exception; to save the main procedure window, one list item should be "Procedure"
        //to save the main procedure window and any other procedure window, use procsToBackup = "*"
        //To save procedures named myProcs0 and myProcs1, use procsToBackup="myProcs0;myProcs1;" (or procsToBackup="myProcs*;")
 
    String saveAppendStr = "_bu"        //optionally specify a string to append to each save file name
        //In Igor7, it's probably best to specify a saveAppendStr or choose a directory other than one currently containing the to-be-saved procedure file(s).
        //Using saveAppendStr="" and saving into the procedure file-containing directory will cause Igor7 to think the procedure file was modified externally.
        //Igor7 will keep trying to resolve the conflict after each compile, and when you choose to resolve it, each procedure needs to be recompiled, causing
        //them to save again, re-call this function, and so on. There might be some settings where this is advantageous: putting off the conflict resolution, a reload from
        //file is like restoring to the last compile point while using file->"save procedure as..." would allow new updates to be saved
 
    Variable numCharsPerWrite=500       //this has to below the limit of fprintf..not sure what that limit is, but 500 is ok
    String saveExtension = ".ipf"       //I've also tested ".txt". The txt files can be copied into a procedure file and then work fine. The code might display properly in a txt file opened with proper encoding settings?
 
//find procedures that should be backed up according to procsToBackup
    variable i,j; string saveList="", matchList, match, allProcWins=winlist("*",";","WIN:128")
    for (i=0;i<itemsinlist(procsToBackup);i+=1)
        matchList= ListMatch(allProcWins, stringfromlist(i,procsToBackup))
        for (j=0;j<itemsinlist(matchList);j+=1)
            match = stringfromlist(j,matchList)
            if (whichListItem(match,saveList) < 0)
                saveList += match + ";"
            endif
        endfor
    endfor
 
    if (!itemsinlist(saveList))
        return 0        //do nothing if no procedures match the list in procsToBackup
    endif
 
//choose a file to save backup(s) to; only necessary once per experiment
//use newpath/O saveProcFiles_path to choose a new path
    pathinfo saveProcFiles_path
    if (!V_flag)
        newpath/O saveProcFiles_path
        if (V_flag)
            Print "saveProcFiles(): user aborted selection of a folder for saving procedures; aborting"
        endif
    endif
 
//write each procedure to its save file with fprintf
    Variable saveLen,strIndex,numFullWrites,fileRefNum,writeIndexDelta=numCharsPerWrite-1
    String procWinName,saveStr,fileName
    for (i=0;i<itemsinlist(saveList);i+=1)
        procWinName = stringfromlist(i,saveList)
        saveStr = ProcedureText("" , inf, procWinName)     
        saveLen = strlen(saveStr)
        if (stringmatch(procWinName,"*.ipf"))
            fileName=procWinName[0,strlen(procWinName)-strlen(".ipf")-1]
        else
            fileName=procWinName
        endif
        fileName = fileName+saveAppendStr+saveExtension
       
        numFullWrites=floor(saveLen/numCharsPerWrite)
        Open/Z/P=saveProcFiles_path fileRefNum as fileName
        if (V_flag)
            Print "saveProcs(): failed to identify procedure file save location. Aborting."
            close fileRefNum
            return 0
        endif
        for (j=0;j<numFullWrites;j+=1)
            strIndex=j*numCharsPerWrite
            fprintf fileRefNum,"%s",saveStr[strIndex,strIndex+writeIndexDelta]
        endfor
        strIndex+=numCharsPerWrite
        fprintf fileRefNum,"%s",saveStr[strIndex,inf]
        close fileRefNum
    endfor
end
 
static function AfterCompiledHook()
    saveProcs()
 
    return 0
end
If Igor crashes, please report it to support@wavemetrics.com. Please include your system information (Help->Contact Support is the best way to do this) and if possible instructions for reproducing the crash.

Igor Pro 7.02B01 now generates minidumps after a crash. Sending those to WM can help us figure out what caused the crash in some cases.
Your technique of opening the file and then closing it does, of course, open the file a second time. That's really what is being detected by the external editor code in Igor. It's not surprising that there is a problem.

Perhaps the problem can be resolved using one of the Miscellaneous Settings. Go to Misc->Miscellaneous Settings and choose the Text Editing category. Choose the External Editor tab. I'm thinking that perhaps one of the Reload Automatically settings might help.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com

Forum

Support

Gallery

Igor Pro 8

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More