From c2fc065b338106b770552b231bcd5fbbe99b82de Mon Sep 17 00:00:00 2001 From: Klaas Boesche Date: Wed, 21 May 2014 17:52:04 +0200 Subject: [PATCH] Automatic real nesting for languages with support Removes the placeholder workaround (forceNested) for languages which allow truly nested comments and automatically applies them. Enabled for haskell, racket, scheme, scala, lisp, ocaml, sml. Fixes issue #131 --- plugin/NERD_commenter.vim | 121 ++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 30 deletions(-) diff --git a/plugin/NERD_commenter.vim b/plugin/NERD_commenter.vim index b9068f0..d2b3e34 100644 --- a/plugin/NERD_commenter.vim +++ b/plugin/NERD_commenter.vim @@ -195,7 +195,7 @@ let s:delimiterMap = { \ 'groovy': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, \ 'gsp': { 'left': '<%--', 'right': '--%>', 'leftAlt': ''}, \ 'gtkrc': { 'left': '#' }, - \ 'haskell': { 'left': '{-','right': '-}', 'leftAlt': '--' }, + \ 'haskell': { 'left': '{-','right': '-}', 'nested': 1, 'leftAlt': '--', 'nestedAlt': 1 }, \ 'hb': { 'left': '#' }, \ 'h': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, \ 'haml': { 'left': '-#', 'leftAlt': '/' }, @@ -240,7 +240,7 @@ let s:delimiterMap = { \ 'lilo': { 'left': '#' }, \ 'lilypond': { 'left': '%' }, \ 'liquid': { 'left': '{% comment %}', 'right': '{% endcomment %}' }, - \ 'lisp': { 'left': ';', 'leftAlt': '#|', 'rightAlt': '|#' }, + \ 'lisp': { 'left': ';', 'nested': 1, 'leftAlt': '#|', 'rightAlt': '|#', 'nestedAlt': 1 }, \ 'llvm': { 'left': ';' }, \ 'lotos': { 'left': '(*', 'right': '*)' }, \ 'lout': { 'left': '#' }, @@ -288,7 +288,7 @@ let s:delimiterMap = { \ 'objc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, \ 'objcpp': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, \ 'objj': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, - \ 'ocaml': { 'left': '(*', 'right': '*)' }, + \ 'ocaml': { 'left': '(*', 'right': '*)', 'nested': 1 }, \ 'occam': { 'left': '--' }, \ 'octave': { 'left': '%', 'leftAlt': '#' }, \ 'omlet': { 'left': '(*', 'right': '*)' }, @@ -329,7 +329,7 @@ let s:delimiterMap = { \ 'puppet': { 'left': '#' }, \ 'pyrex': { 'left': '# ', 'leftAlt': '#' }, \ 'python': { 'left': '# ', 'leftAlt': '#' }, - \ 'racket': { 'left': ';', 'leftAlt': '#|', 'rightAlt': '|#' }, + \ 'racket': { 'left': ';', 'nested': 1, 'leftAlt': '#|', 'rightAlt': '|#', 'nestedAlt': 1 }, \ 'radiance': { 'left': '#' }, \ 'ratpoison': { 'left': '#' }, \ 'r': { 'left': '#' }, @@ -349,9 +349,9 @@ let s:delimiterMap = { \ 'samba': { 'left': ';', 'leftAlt': '#' }, \ 'sass': { 'left': '//', 'leftAlt': '/*' }, \ 'sather': { 'left': '--' }, - \ 'scala': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'scala': { 'left': '//', 'nested': 1, 'leftAlt': '/*', 'rightAlt': '*/', 'nestedAlt': 1 }, \ 'scons': { 'left': '#' }, - \ 'scheme': { 'left': ';', 'leftAlt': '#|', 'rightAlt': '|#' }, + \ 'scheme': { 'left': ';', 'nested': 1, 'leftAlt': '#|', 'rightAlt': '|#', 'nestedAlt': 1 }, \ 'scilab': { 'left': '//' }, \ 'scsh': { 'left': ';' }, \ 'scss': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/'}, @@ -373,7 +373,7 @@ let s:delimiterMap = { \ 'smarty': { 'left': '{*', 'right': '*}' }, \ 'smil': { 'left': '' }, \ 'smith': { 'left': ';' }, - \ 'sml': { 'left': '(*', 'right': '*)' }, + \ 'sml': { 'left': '(*', 'right': '*)', 'nested': 1 }, \ 'snippets': { 'left': '#' }, \ 'snnsnet': { 'left': '#' }, \ 'snnspat': { 'left': '#' }, @@ -505,6 +505,11 @@ function s:SetUpForNewFiletype(filetype, forceReset) let b:NERDCommenterDelims[i] = '' endif endfor + for i in ['nested', 'nestedAlt'] + if !has_key(b:NERDCommenterDelims, i) + let b:NERDCommenterDelims[i] = 0 + endif + endfor else let b:NERDCommenterDelims = s:CreateDelimMapFromCms() endif @@ -524,8 +529,10 @@ function s:CreateDelimMapFromCms() return { \ 'left': substitute(&commentstring, '\([^ \t]*\)\s*%s.*', '\1', ''), \ 'right': substitute(&commentstring, '.*%s\s*\(.*\)', '\1', 'g'), + \ 'nested': 0, \ 'leftAlt': '', - \ 'rightAlt': '' } + \ 'rightAlt': '', + \ 'nestedAlt': 0} endfunction " Function: s:SwitchToAlternativeDelimiters(printMsgs) function {{{2 @@ -550,14 +557,18 @@ function s:SwitchToAlternativeDelimiters(printMsgs) "save the current delimiters let tempLeft = s:Left() let tempRight = s:Right() + let tempNested = s:Nested() "swap current delimiters for alternative let b:NERDCommenterDelims['left'] = b:NERDCommenterDelims['leftAlt'] let b:NERDCommenterDelims['right'] = b:NERDCommenterDelims['rightAlt'] + "set information on whether these are nested + let b:NERDCommenterDelims['nested'] = b:NERDCommenterDelims['nestedAlt'] "set the previously current delimiters to be the new alternative ones let b:NERDCommenterDelims['leftAlt'] = tempLeft let b:NERDCommenterDelims['rightAlt'] = tempRight + let b:NERDCommenterDelims['nestedAlt'] = tempNested "tell the user what comment delimiters they are now using if a:printMsgs @@ -755,7 +766,7 @@ function s:CommentLines(forceNested, align, firstLine, lastLine) if s:CanCommentLine(a:forceNested, currentLine) "if the user has specified forceNesting then we check to see if we "need to switch delimiters for place-holders - if a:forceNested && g:NERDUsePlaceHolders + if a:forceNested && g:NERDUsePlaceHolders && !s:Nested() let theLine = s:SwapOuterMultiPartDelimsForPlaceHolders(theLine) endif @@ -801,9 +812,11 @@ function s:CommentLinesMinimal(firstLine, lastLine) throw 'NERDCommenter.Delimiters exception: Minimal comments can only be used for filetypes that have multipart delimiters' endif + let sexyNested = s:SexyNested() + "if we need to use place holders for the comment, make sure they are - "enabled for this filetype - if !g:NERDUsePlaceHolders && s:DoesBlockHaveMultipartDelim(a:firstLine, a:lastLine) + "enabled for this filetype, or the delims allow nesting + if !g:NERDUsePlaceHolders && !sexyNested && s:DoesBlockHaveMultipartDelim(a:firstLine, a:lastLine) throw 'NERDCommenter.Settings exception: Place holders are required but disabled.' endif @@ -813,13 +826,15 @@ function s:CommentLinesMinimal(firstLine, lastLine) "make sure all multipart delimiters on the lines are replaced with "placeholders to prevent illegal syntax - let currentLine = a:firstLine - while(currentLine <= a:lastLine) - let theLine = getline(currentLine) - let theLine = s:ReplaceDelims(left, right, g:NERDLPlace, g:NERDRPlace, theLine) - call setline(currentLine, theLine) - let currentLine = currentLine + 1 - endwhile + if !sexyNested + let currentLine = a:firstLine + while(currentLine <= a:lastLine) + let theLine = getline(currentLine) + let theLine = s:ReplaceDelims(left, right, g:NERDLPlace, g:NERDRPlace, theLine) + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + endif "add the delimiter to the top line let theLine = getline(a:firstLine) @@ -859,7 +874,7 @@ function s:CommentLinesSexy(topline, bottomline) throw 'NERDCommenter.Delimiters exception: cannot perform sexy comments with available delimiters.' endif - "make sure the lines aren't already commented sexually + "make sure the lines aren't already commented sexually or we can nest if !s:CanSexyCommentLines(a:topline, a:bottomline) throw 'NERDCommenter.Nesting exception: cannot nest sexy comments' endif @@ -884,7 +899,9 @@ function s:CommentLinesSexy(topline, bottomline) if lineHasTabs let theLine = s:ConvertLeadingTabsToSpaces(theLine) endif - let theLine = s:SwapOuterMultiPartDelimsForPlaceHolders(theLine) + if !s:SexyNested() + let theLine = s:SwapOuterMultiPartDelimsForPlaceHolders(theLine) + endif let theLine = s:AddLeftDelimAligned(left . spaceString, theLine, leftAlignIndx) if lineHasTabs let theLine = s:ConvertLeadingSpacesToTabs(theLine) @@ -898,7 +915,9 @@ function s:CommentLinesSexy(topline, bottomline) if lineHasTabs let theLine = s:ConvertLeadingTabsToSpaces(theLine) endif - let theLine = s:SwapOuterMultiPartDelimsForPlaceHolders(theLine) + if !s:SexyNested() + let theLine = s:SwapOuterMultiPartDelimsForPlaceHolders(theLine) + endif endif let theLine = s:AddRightDelim(spaceString . right, theLine) if lineHasTabs @@ -943,7 +962,9 @@ function s:CommentLinesSexy(topline, bottomline) let theLine = s:ConvertLeadingTabsToSpaces(theLine) endif - let theLine = s:SwapOuterMultiPartDelimsForPlaceHolders(theLine) + if !s:SexyNested() + let theLine = s:SwapOuterMultiPartDelimsForPlaceHolders(theLine) + endif " add the sexyComMarker let theLine = repeat(' ', leftAlignIndx) . repeat(' ', strlen(left)-strlen(sexyComMarker)) . sexyComMarkerSpaced . strpart(theLine, leftAlignIndx) @@ -985,7 +1006,7 @@ function s:CommentLinesToggle(forceNested, firstLine, lastLine) "if the user has specified forceNesting then we check to see if we "need to switch delimiters for place-holders - if g:NERDUsePlaceHolders + if g:NERDUsePlaceHolders && !s:Nested() let theLine = s:SwapOuterMultiPartDelimsForPlaceHolders(theLine) endif @@ -1305,7 +1326,7 @@ function s:RemoveDelimiters(left, right, line) endif "look for the right delimiter, if we find it, remove it - let rightIndx = s:FindDelimiterIndex(a:right, line) + let rightIndx = s:LastIndexOfDelim(a:right, line) if rightIndx != -1 let line = strpart(line, 0, rightIndx) . strpart(line, rightIndx+lenRight) @@ -1537,8 +1558,8 @@ function s:UncommentLineNormal(line) "get the positions of all delimiter types on the line let indxLeft = s:FindDelimiterIndex(s:Left(), line) let indxLeftAlt = s:FindDelimiterIndex(s:Left({'alt': 1}), line) - let indxRight = s:FindDelimiterIndex(s:Right(), line) - let indxRightAlt = s:FindDelimiterIndex(s:Right({'alt': 1}), line) + let indxRight = s:LastIndexOfDelim(s:Right(), line) + let indxRightAlt = s:LastIndexOfDelim(s:Right({'alt': 1}), line) "get the comment status on the line so we know how it is commented let lineCommentStatus = s:IsCommentedOutermost(s:Left(), s:Right(), s:Left({'alt': 1}), s:Right({'alt': 1}), line) @@ -1671,6 +1692,12 @@ function s:AltMultipart() return b:NERDCommenterDelims['rightAlt'] != '' endfunction +" Function: s:AltNested() {{{2 +" returns 1 if the alternate multipart (if any) delims allow nesting +function s:AltNested() + return b:NERDCommenterDelims['nestedAlt'] +endfunction + " Function: s:CanCommentLine(forceNested, line) {{{2 "This function is used to determine whether the given line can be commented. "It returns 1 if it can be and 0 otherwise @@ -1701,7 +1728,7 @@ function s:CanCommentLine(forceNested, lineNum) endif "if the line is commented but nesting is allowed then return true - if a:forceNested && (!s:Multipart() || g:NERDUsePlaceHolders) + if s:Nested() || (a:forceNested && (!s:Multipart() || g:NERDUsePlaceHolders)) return 1 endif @@ -1723,6 +1750,11 @@ endfunction " Return: 1 if the given lines can be commented sexually, 0 otherwise function s:CanSexyCommentLines(topline, bottomline) " see if the selected regions have any sexy comments + " however, if the language allows nested comments, + " we allow nested sexy comments + if s:SexyNested() + return 1 + endif let currentLine = a:topline while(currentLine <= a:bottomline) if s:IsInSexyComment(currentLine) @@ -2056,6 +2088,29 @@ function s:GetSexyComMarker(space, esc) return sexyComMarker endfunction +" Function: s:SexyNested() {{{2 +" Returns 1 if the sexy delimeters allow nesting +" TODO this is ugly copy&paste from the GetSexyComLeft/Right functions, +" these could all be cleaned up +function s:SexyNested() + let lenLeft = strlen(s:Left()) + let lenLeftAlt = strlen(s:Left({'alt': 1})) + + "assume c style sexy comments if possible + if s:HasCStyleComments() + return (s:Left() == '/*' && s:Nested()) || (s:Left({'alt': 1}) == '/*' && s:AltNested()) + else + "grab the longest left delim that has a right + if s:Multipart() && lenLeft >= lenLeftAlt + return s:Nested() + elseif s:AltMultipart() + return s:AltNested() + else + return 0 + endif + endif +endfunction + " Function: s:GetSexyComLeft(space, esc) {{{2 " Returns the left delimiter for sexy comments for this filetype or -1 if " there is none. C style sexy comments are used if possible @@ -2183,7 +2238,7 @@ endfunction " -left/right: the left and right delimiters to check for function s:IsCommented(left, right, line) "if the line isn't commented return true - if s:FindDelimiterIndex(a:left, a:line) != -1 && (s:FindDelimiterIndex(a:right, a:line) != -1 || !s:Multipart()) + if s:FindDelimiterIndex(a:left, a:line) != -1 && (s:LastIndexOfDelim(a:right, a:line) != -1 || !s:Multipart()) return 1 endif return 0 @@ -2407,7 +2462,7 @@ function s:IsSexyComment(topline, bottomline) endif "if there is a right delimiter on the top line then this isn't a sexy comment - if s:FindDelimiterIndex(right, getline(a:topline)) != -1 + if s:LastIndexOfDelim(right, getline(a:topline)) != -1 return 0 endif @@ -2436,7 +2491,7 @@ function s:IsSexyComment(topline, bottomline) "if there is a right delimiter in an intermediate line then the block isn't "a sexy comment - if s:FindDelimiterIndex(right, theLine) != -1 + if s:LastIndexOfDelim(right, theLine) != -1 return 0 endif @@ -2573,6 +2628,12 @@ function s:NerdEcho(msg, typeOfMsg) endif endfunction +" Function: s:Nested() {{{2 +" returns 1 if the current multipart (if any) delims allow nesting +function s:Nested() + return b:NERDCommenterDelims['nested'] +endfunction + " Function: s:NumberOfLeadingTabs(s) {{{2 " returns the number of leading tabs in the given string function s:NumberOfLeadingTabs(s)