
Align Comments

chozo
NOTE: From Igor 9 onwards this functionality is available as a standard feature (as Align Comments in the Edit menu), which makes this script unnecessary. You may find it useful if you are working with an earlier version.
Aligns all comments in the selection of a procedure file to the same tab position. The distance is set by the comment furthest to the left or right. Use the function ToTabPos to set a fixed alignment distance in number of tabs.
EDIT: Updated the code to properly honor the first line, even if it is selected only partly, and included the ability to left-align the comments or use a fixed tab number.
NOTES:
- The current font name, font size, and default tab width settings for procedures need to be set to the correct values at the beginning of below code.
- This code will be loaded as an independent module. You'll have to show hidden items in the procedure browser to see the code.
#pragma TextEncoding = "UTF-8" #pragma rtGlobals = 3 // Use modern global access method and strict wave access. #pragma IndependentModule = AlignComments #pragma version = 1.1 // Inspired by the Code Prettify snipped by Tony Withers // https://www.wavemetrics.com/user/chozo // To align comments in the selected code use: // Left-aligned: cmd-7 (Mac), ctrl-7 (Windows) // Right-aligned: cmd-8 (Mac), ctrl-8 (Windows) // Dial in your procedure window settings here: // You can find the default tab size under Procedure->Document Settings... StrConstant FontName = "Courier New" Constant FontSize = 11 Constant DefaultTab = 20 // in points Menu "Edit" "Align Comments in Clipboard Code (Right)", /Q, PutScrapText AlignComments#AlignComments(GetScrapText()) "Align Comments in Selected Code (Left)/7", /Q, AlignCommentsInSelection(1) "Align Comments in Selected Code (Right)/8", /Q, AlignCommentsInSelection(0) End Function AlignCommentsInSelection(Variable LeftOrRight) String strScrap = GetScrapText() DoIgorMenu "Edit" "Copy" PutScrapText AlignComments#AlignComments(GetScrapText(),AligntoLeft=LeftOrRight) DoIgorMenu "Edit" "Paste" PutScrapText strScrap // leave clipboard state unchanged End // For setting a determined tab position of all selected comments. // Execute from the commandline as AlignComments#toTabPos(desiredPos). Function toTabPos(Variable TabPos) // for aligning all comments to a certain tab position DisplayProcedure/W=$StringFromList(0,WinList("*", ";","WIN:128")) // bring to front String strScrap = GetScrapText() DoIgorMenu "Edit" "Copy" PutScrapText AlignComments#AlignComments(GetScrapText(),TabPos=TabPos) DoIgorMenu "Edit" "Paste" PutScrapText strScrap End // The optional variable TabPos lets you choose a fixed offset (in tab widths) for all comments. // For example, TabPos = 30 will position all comments at 30 tabs, if possible. Function/S AlignComments(String strText, [Variable TabPos, Variable AlignToLeft]) Variable LorR = 0 if(!ParamIsDefault(AlignToLeft)) LorR = AlignToLeft endif Variable fsPix = FontSize * ScreenResolution/72 // font size in pix Variable TabPix = floor(DefaultTab * ScreenResolution/72) // tab width in pix Wave/T wText = ListToTextWave(strText, "\r") Variable endsWithReturn = (cmpstr(strText[strlen(strText)-1], "\r") == 0) Variable entries = numpnts(wText) String strEverything = "", strStartOfFirst = "", strEndOfFirst = wText[0] // extract the start of the first line from the whole text strEverything = ProcedureText("",0,StringFromList(0,WinList("*", ";","WIN:128"))) strEverything = ReplaceString(strText,strEverything,"\rCUT\r") // create a break at selected text strEverything = StringFromList(0, strEverything, "\rCUT\r") // only the stuff before the selection if (cmpstr(strEverything[strlen(strEverything)-1], "\r") != 0 && strlen(strEverything)>0) // only if it is not a full line Wave/T wBegin = ListToTextWave(strEverything, "\r") strStartOfFirst = wBegin[numpnts(wBegin)-1] // the extra part of the first line endif if (strlen(strStartOfFirst) > 0) // put the full firt line in here wText[0] = strStartOfFirst + strEndOfFirst // this is needed to calculate the correct line length endif wText += SelectString(p<(entries-1) || endsWithReturn, "", "\r") Make/D/FREE/N=(entries) CodeSize = 0 // saves the code size in 'tab widths' Make/T/FREE/N=(entries) CodeOnly, CommentOnly Variable i,j, bytes = 0, strSize = 0 String strLine = "", strCode = "" for (i = 0; i < entries; i += 1) strLine = wText[i] //bytes = strsearch(strLine, "//",Inf,1) bytes = startOfComment(strLine) // is there a comment? bytes = bytes == strlen(strLine) ? -1 : bytes // no comment found if (i == 0 && strlen(strStartOfFirst) > bytes) // if the full comment in not selected -> skip bytes = -1 endif if(bytes > 1) strCode = strLine[0,bytes-1] if(GrepString(strCode, "\S")) // line contains non-whitespace CodeOnly[i] = strCode // just the code CommentOnly[i] = strLine[bytes,Inf] // just the comment if (cmpstr(strCode[strlen(strCode)-1], " ") == 0) CodeOnly[i] = RemoveEnding(CodeOnly[i] , " ") // get rid of a possible space character in front of the comments CodeOnly[i] += "\t" endif // Below code calculates the line length in units of 'tab length'. // Multiplication by TabPix would give the length in pix. Wave/T wTabList = ListToTextWave(CodeOnly[i], "\t") // split code line by tabs for (j = 0; j < DimSize(wTabList,0); j += 1) strCode = wTabList[j] if (strlen(strCode) > 0) // tab only or code? strSize = FontSizeStringWidth(FontName,fsPix,0,strCode,"") // get the length of all characters if (mod(strSize,TabPix) < 0.05) // does this have roughly the size of a certain no. of tabs? strSize = ceil(strSize/TabPix + 1) // a full tab length is added else strSize = ceil(strSize/TabPix) // this will add a fractional tab length endif CodeSize[i] += strSize else CodeSize[i] += 1 // if it's just a tab then add one tab length endif endfor if (cmpstr(strCode[strlen(strCode)-1], "\t") == 0) // no tab in last entry -> not aligned to the next tab position CodeSize[i] -= 1 endif endif endif endfor wText[0] = strEndOfFirst + "\r" // put only the end line back (in case there is no modification) if (strlen(strStartOfFirst) > 0) // remove the first part of the first line again CodeOnly[0] = ReplaceString(strStartOfFirst,CodeOnly[0],"") endif Variable AlignDistance = 0 // how many tabs define the new alignment if(!ParamIsDefault(TabPos)) AlignDistance = TabPos else Extract/Free CodeSize,SizeVals,CodeSize!=0 AlignDistance = LorR == 1 ? max(WaveMin(SizeVals),AlignDistance) : max(WaveMax(SizeVals),AlignDistance) endif CodeSize = CodeSize[p] > 0 ? AlignDistance - CodeSize[p] : 0 // how many tabs to add or subtract for (i = 0; i < entries; i += 1) String newTabs = "" if(abs(CodeSize[i]) > 0) for (j = 0; j < abs(CodeSize[i]); j += 1) if (CodeSize[i] < 0) CodeOnly[i] = RemoveEnding(CodeOnly[i],"\t") else newTabs += "\t" endif endfor wText[i] = CodeOnly[i] + newTabs + CommentOnly[i] endif endfor wfprintf strText, "%s", wText return strText End // comment search function by Tony Withers Function startOfComment(String strLine) Variable startByte, commentByte, startQuote, endQuote, lineLength lineLength=strlen(strLine) startByte=0 do if(startByte>=(lineLength-1)) return lineLength endif commentByte = strsearch(strLine, "//", startByte) if(commentByte == -1) return lineLength endif // found "//" startQuote=strsearch(strLine, "\"", startByte) if(startQuote==-1 || startQuote>commentByte) // no quotes before //, we're done return commentByte endif // we have a quotation mark before // do endQuote=startQuote do endQuote=strsearch(strLine, "\"", endQuote+1) if(endQuote==-1) return lineLength endif // endQuote is possible end of quote // remove escaped backslashes! strLine[startQuote,endQuote]=ReplaceString("\\\\", strLine[startQuote,endQuote], " ") while(cmpstr(strLine[endQuote-1], "\\")==0) // ignore escaped quotes // found end of quote if(endQuote>commentByte) // commentByte was within commented text startByte=endQuote+1 break // look for another comment mark else startQuote=strsearch(strLine, "\"", endQuote+1) if(startQuote==-1 || startQuote>commentByte) // no quotes before //, we're done return commentByte endif endif // if we get to here we've found another start-of-quoted-text before // while(1) // next quoted text before // while(1) // next comment marker End

Forum

Support

Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
tricky!
See also the related snippet for comment wrapping. It doesn't help with what you're trying to do, but it does attempt to deal with indentation for long comments.
You will likely want to put your snippet into an independent module for compatibility with auto-compile.
June 23, 2020 at 11:58 pm - Permalink
In reply to tricky! See also the… by tony
Yes, thanks for the comment, I will do that. I am also planning to upgrade the code to optionally align to the rightmost or leftmost comment. Do you have any idea how to improve the following points:
Btw, are you interested / do you plan to wrap all these snippets for procedure windows into a full tool set at some point?
June 24, 2020 at 01:49 am - Permalink
In reply to Yes, thanks for the comment,… by chozo
OK, here is the version as independent module. There are two menu entries now for aligning all selected comments to the leftmost (if possible) or rightmost comment. Also I have added a command-line function to set a fixed tab position. I have added the new version (v0.8) above.
June 24, 2020 at 06:06 am - Permalink
In reply to Yes, thanks for the comment,… by chozo
Procedure windows cannot, for the most part, be controlled programatically. So as far as I know there is no way to modify the selection.
But you can use the ProcedureText function to get all of the text of a procedure window, and then parse off the first line of the text.
June 24, 2020 at 06:51 am - Permalink
In reply to Yes, thanks for the comment,… by chozo
The sorts of things you're looking for are easy to do with notebooks, but I don't know any way to do that with procedure windows, which makes sense because users were probably never expected to adjust the behaviour of that window. If we could use hook functions with procedure windows that would be great for adding contextual menus, but I can also see that it might make sense to put in requests for new features for Igor 9 (or 10?) rather than trying to 'roll our own'. Something that I would like is an operation to query the current state of ALL of the user-editable settings for Igor.
You could try using ProcedureText() to grab the whole procedure and then looking for a unique match of selected text within the procedure text... I just checked and it looks like the current text of uncompiled and unsaved ipf files is retrieved by ProcedureText(), so there may be some mileage in that.
I do maintain a TextEditTools.ipf procedure file for myself, but it doesn't contain much, just the snippets mentioned above and a transpose paste function. If the collection of user contributions grows then it might make sense to put together a compilation of utility text-editing code.
June 24, 2020 at 09:00 am - Permalink
Tony and Adam,
Thank you very much for the input! I have modified the code again to grab the first line from ProcedureText(). Works great so far. I guess nothing can be done about having to dial in the font settings for now. I have attached the new version above.
June 24, 2020 at 08:43 pm - Permalink
looking at the file AlignComments_v10.ipf:
you need to remove the prepended piece of the first line before pasting the adjusted text over the selection.
Also, you might worry about comment markers buried inside quotation marks, for instance in a line like
bytes = strsearch(strLine, "//",Inf,1)
Here is an (untested) attempt to find the start of comments that doesn't get caught out by lines like
print "this is \"not a // comment\"" // and // this " is
June 25, 2020 at 06:28 am - Permalink
Tony, thank you for your help with this. Indeed, properly selecting whole lines screwed up the code insertion thanks to the added handling of the first line in the last version. Oops. There were other hiccups as well.
I was searching for the start of a comment from the right. But as you have mentioned, two // in a comment or // in code without any comment would have been aligned as well. I have included your comment search function now. I hope that is OK.
I have added a new version (this time in the first post).
June 25, 2020 at 09:47 pm - Permalink
great. let me know if you find any problems with the comment search function. i updated the prettify code to use the more robust comment searching, though it's not critical to get it right for that purpose.
June 26, 2020 at 12:07 am - Permalink
Aligning comments is a nice feature!
But do we really want to align code with tabs even when we are indenting with tabs?I don't think this will work.
Consider the following example
If the comments ought to be right aligned they should be that for every tab width. As IMHO the tab width is settable by the user and that should not obstruct the code.
If I now align with tabs manually in IP
which looks different here in the forum as a tab consists of 8 spaces here vs 4 in IP. You need to copy and paste to IP to see that it is correctly aligned with a tab width of 4.
July 7, 2020 at 02:48 pm - Permalink
I am actually not sure I understand. Is this a problem with the snippet? Yes, aligning comments with tabs is futile if you are looking at the code in another software, as tab widths change widely. The above snippet only aligns the comments so that they look 'good' within Igor. Unless we get the feature that comments are aligned/shown in a separate space next to the code, no matter the number of tabs before them, there is really no other solution than to align the stuff for one program and stick with it (or use the align functionality within each of these programs if available).
By the way, an additional complication arises with the forum, since tabs are replaced with a number of space characters. I don't know a way to get the original code with tabs out of a post.
July 9, 2020 at 07:55 pm - Permalink