Improved Single Line Comment

Closes #2031

Improved the Single Line Comment/Uncomment/Toggle behaviour for Lexers
with the following condition:

  Single Line Comment Symbol  :  false
  Stream Comment Symbol Start :  true
  Stream Comment Symbol End   :  true

This includes among others: XML, HTML, CSS, Caml, Pascal, ...

In the current Npp a 'Single Line Comment' will do a 'Block Comment' and
the 'Toggle Single Line Comment' entry does nothing at all for these kind
of Lexers.

This implementation uses the stream comment symbols (start/end) to
accomplish a single line comment,
exactly the same way as the usual Single Line Comment/Uncomment/Toggle
functionality does for Lexers with a single line symbol.
This will add more consistency to the Single Line Comment feature.

NOTE
The selection range behaviour has been revised to be more accurate and for
fixing some 'line leaving' bugs when uncommenting.
This commit is contained in:
A-R-C-A 2016-06-30 01:03:30 +00:00 committed by Don HO
parent 1abac15799
commit 7fcc20f84a

View File

@ -3803,19 +3803,29 @@ static generic_string extractSymbol(TCHAR firstChar, TCHAR secondChar, const TCH
bool Notepad_plus::doBlockComment(comment_mode currCommentMode)
{
Buffer * buf = _pEditView->getCurrentBuffer();
//--FLS: Avoid side-effects (e.g. cursor moves number of comment-characters) when file is read-only.
if (buf->isReadOnly())
return false;
//--LS: BlockToStreamComment:
const TCHAR *commentStart;
const TCHAR *commentEnd;
generic_string symbolStart;
generic_string symbolEnd;
const TCHAR *commentLineSybol;
const TCHAR *commentLineSymbol;
generic_string symbol;
Buffer * buf = _pEditView->getCurrentBuffer();
//--FLS: Avoid side-effects (e.g. cursor moves number of comment-characters) when file is read-only.
if (buf->isReadOnly())
return false;
//Single Line Comment/Uncomment/Toggle can have two modes:
// * a NORMAL MODE which uses a commentLineSymbol to comment/uncomment code per line, and
// * an ADVANCED MODE which uses commentStart and commentEnd symbols to comment/uncomment code per line.
//The NORMAL MODE is used for all Lexers which have a commentLineSymbol defined.
//The ADVANCED MODE is only available for Lexers which do not have a commentLineSymbol but commentStreamSymbols (start/end) defined.
//The ADVANCED MODE behaves the same way as the NORMAL MODE (comment/uncomment every single line in the selection range separately)
//but uses two smbols to accomplish this.
bool isSingleLineAdvancedMode = false;
if (buf->getLangType() == L_USER)
{
UserLangContainer * userLangContainer = NppParameters::getInstance()->getULCFromName(buf->getUserDefineLangName());
@ -3823,7 +3833,7 @@ bool Notepad_plus::doBlockComment(comment_mode currCommentMode)
return false;
symbol = extractSymbol('0', '0', userLangContainer->_keywordLists[SCE_USER_KWLIST_COMMENTS]);
commentLineSybol = symbol.c_str();
commentLineSymbol = symbol.c_str();
//--FLS: BlockToStreamComment: Needed to decide, if stream-comment can be called below!
symbolStart = extractSymbol('0', '3', userLangContainer->_keywordLists[SCE_USER_KWLIST_COMMENTS]);
commentStart = symbolStart.c_str();
@ -3832,24 +3842,35 @@ bool Notepad_plus::doBlockComment(comment_mode currCommentMode)
}
else
{
commentLineSybol = buf->getCommentLineSymbol();
commentLineSymbol = buf->getCommentLineSymbol();
//--FLS: BlockToStreamComment: Needed to decide, if stream-comment can be called below!
commentStart = buf->getCommentStart();
commentEnd = buf->getCommentEnd();
}
if ((!commentLineSybol) || (!commentLineSybol[0]) || (commentLineSybol == NULL))
if ((!commentLineSymbol) || (!commentLineSymbol[0]) || (commentLineSymbol == NULL))
{
//--FLS: BlockToStreamComment: If there is no block-comment symbol, try the stream comment:
if (!(!commentStart || !commentStart[0] || commentStart == NULL || !commentEnd || !commentEnd[0] || commentEnd == NULL))
{
if ((currCommentMode == cm_comment))
if (currCommentMode == cm_comment)
{
return doStreamComment();
//Do an advanced "Single Line Comment" by using stream-comment symbols (start/end) per line in this case.
isSingleLineAdvancedMode = true;
//return doStreamComment(); //Use "Block Comment" for this.
}
else if (currCommentMode == cm_uncomment)
{
//"undoStreamComment()" can be more flexible than "isSingleLineAdvancedMode = true",
//since it can uncomment more embedded levels at once and the commentEnd symbol can be located everywhere.
//But, depending on the selection start/end position, the first/last selected line may not be uncommented properly!
return undoStreamComment();
//isSingleLineAdvancedMode = true;
}
else if (currCommentMode == cm_toggle)
{
//Do an advanced "Toggle Single Line Comment" by using stream-comment symbols (start/end) per line in this case.
isSingleLineAdvancedMode = true;
}
else
return false;
@ -3858,10 +3879,37 @@ bool Notepad_plus::doBlockComment(comment_mode currCommentMode)
return false;
}
generic_string comment(commentLineSybol);
comment += TEXT(" ");
//For Single Line NORMAL MODE
generic_string comment;
size_t comment_length = 0;
//For Single Line ADVANCED MODE
generic_string advCommentStart;
generic_string advCommentEnd;
size_t advCommentStart_length = 0;
size_t advCommentEnd_length = 0;
const TCHAR aSpace[] { TEXT(" ") };
//Only values that have passed through will be assigned, to be sure they are valid!
if (not isSingleLineAdvancedMode)
{
comment = commentLineSymbol;
comment += aSpace;
comment_length = comment.length();
}
else // isSingleLineAdvancedMode
{
advCommentStart = commentStart;
advCommentStart += aSpace;
advCommentEnd = aSpace;
advCommentEnd += commentEnd;
advCommentStart_length = advCommentStart.length();
advCommentEnd_length = advCommentEnd.length();
}
size_t comment_length = comment.length();
size_t selectionStart = _pEditView->execute(SCI_GETSELECTIONSTART);
size_t selectionEnd = _pEditView->execute(SCI_GETSELECTIONEND);
size_t caretPosition = _pEditView->execute(SCI_GETCURRENTPOS);
@ -3870,73 +3918,172 @@ bool Notepad_plus::doBlockComment(comment_mode currCommentMode)
int selStartLine = static_cast<int32_t>(_pEditView->execute(SCI_LINEFROMPOSITION, selectionStart));
int selEndLine = static_cast<int32_t>(_pEditView->execute(SCI_LINEFROMPOSITION, selectionEnd));
int lines = selEndLine - selStartLine;
size_t firstSelLineStart = _pEditView->execute(SCI_POSITIONFROMLINE, selStartLine);
// "caret return" is part of the last selected line
if ((lines > 0) && (selectionEnd == static_cast<size_t>(_pEditView->execute(SCI_POSITIONFROMLINE, selEndLine))))
selEndLine--;
//--FLS: count lines which were un-commented to decide if undoStreamComment() shall be called.
int nUncomments = 0;
//Some Lexers need line-comments at the beginning of a line.
const bool avoidIndent = buf->getLangType() == L_FORTRAN_77;
_pEditView->execute(SCI_BEGINUNDOACTION);
for (int i = selStartLine; i <= selEndLine; ++i)
{
auto lineStart = _pEditView->execute(SCI_POSITIONFROMLINE, i);
auto lineIndent = lineStart;
auto lineEnd = _pEditView->execute(SCI_GETLINEENDPOSITION, i);
size_t lineStart = _pEditView->execute(SCI_POSITIONFROMLINE, i);
size_t lineIndent = _pEditView->execute(SCI_GETLINEINDENTPOSITION, i);
size_t lineEnd = _pEditView->execute(SCI_GETLINEENDPOSITION, i);
size_t linebufferSize = lineEnd - lineStart + 2;
// empty lines are not commented
if (lineIndent == lineEnd)
continue;
if (avoidIndent)
lineIndent = lineStart;
size_t linebufferSize = lineEnd - lineIndent + 2;
TCHAR* linebuf = new TCHAR[linebufferSize];
Lang *lang = _pEditView->getCurrentBuffer()->getCurrentLang();
bool isFortran = lang == NULL?false:lang->_langID == L_FORTRAN_77;
if (!isFortran)
lineIndent = _pEditView->execute(SCI_GETLINEINDENTPOSITION, i);
_pEditView->getGenericText(linebuf, linebufferSize, lineIndent, lineEnd);
generic_string linebufStr = linebuf;
delete [] linebuf;
// empty lines are not commented
if (linebufStr.length() < 1)
continue;
if (currCommentMode != cm_comment)
if (currCommentMode != cm_comment) // uncomment/toggle
{
//--FLS: In order to do get case insensitive comparison use strnicmp() instead case-sensitive comparison.
// Case insensitive comparison is needed e.g. for "REM" and "rem" in Batchfiles.
//if (linebufStr.substr(0, comment_length - 1) == comment.substr(0, comment_length - 1))
if (generic_strnicmp(linebufStr.c_str(), comment.c_str(), comment_length -1) == 0)
if (not isSingleLineAdvancedMode)
{
generic_string commentStr = linebufStr.substr(0, comment_length);
size_t len = (generic_strnicmp(commentStr.c_str(), comment.c_str(), comment_length) == 0) ? comment_length : comment_length - 1;
//--FLS: In order to do get case insensitive comparison use strnicmp() instead case-sensitive comparison.
// Case insensitive comparison is needed e.g. for "REM" and "rem" in Batchfiles.
//if (linebufStr.substr(0, comment_length - 1) == comment.substr(0, comment_length - 1))
if (generic_strnicmp(linebufStr.c_str(), comment.c_str(), comment_length - 1) == 0)
{
size_t len = linebufStr[comment_length - 1] == aSpace[0] ? comment_length : comment_length - 1;
_pEditView->execute(SCI_SETSEL, lineIndent, lineIndent + len);
_pEditView->replaceSelWith("");
_pEditView->execute(SCI_SETSEL, lineIndent, lineIndent + len);
_pEditView->replaceSelWith("");
if (i == selStartLine) // is this the first selected line?
selectionStart -= len;
selectionEnd -= len; // every iteration
++nUncomments;
continue;
// SELECTION RANGE ADJUSTMENT .......................
if (i == selStartLine) // first selected line
{
if (selectionStart > lineIndent + len)
selectionStart -= len;
else if (selectionStart > lineIndent)
selectionStart = lineIndent;
} // ................................................
if (i == selEndLine) // last selected line
{
if (selectionEnd > lineIndent + len)
selectionEnd -= len;
else if (selectionEnd > lineIndent)
{
selectionEnd = lineIndent;
if (lineIndent == lineStart && i != selStartLine)
++selectionEnd; // avoid caret return in this case
}
} // ................................................
else // every iteration except the last selected line
selectionEnd -= len;
// ..................................................
++nUncomments;
continue;
}
}
}
if ((currCommentMode == cm_toggle) || (currCommentMode == cm_comment))
else // isSingleLineAdvancedMode
{
if ((generic_strnicmp(linebufStr.c_str(), advCommentStart.c_str(), advCommentStart_length - 1) == 0) &&
(generic_strnicmp(linebufStr.substr(linebufStr.length() - advCommentEnd_length + 1, advCommentEnd_length - 1).c_str(), advCommentEnd.substr(1, advCommentEnd_length - 1).c_str(), advCommentEnd_length - 1) == 0))
{
size_t startLen = linebufStr[advCommentStart_length - 1] == aSpace[0] ? advCommentStart_length : advCommentStart_length - 1;
size_t endLen = linebufStr[linebufStr.length() - advCommentEnd_length] == aSpace[0] ? advCommentEnd_length : advCommentEnd_length - 1;
_pEditView->execute(SCI_SETSEL, lineIndent, lineIndent + startLen);
_pEditView->replaceSelWith("");
_pEditView->execute(SCI_SETSEL, lineEnd - startLen - endLen, lineEnd - startLen);
_pEditView->replaceSelWith("");
// SELECTION RANGE ADJUSTMENT .......................
if (i == selStartLine) // first selected line
{
if (selectionStart > lineEnd - endLen)
selectionStart = lineEnd - startLen - endLen;
else if (selectionStart > lineIndent + startLen)
selectionStart -= startLen;
else if (selectionStart > lineIndent)
selectionStart = lineIndent;
} // ................................................
if (i == selEndLine) // last selected line
{
if (selectionEnd > lineEnd)
selectionEnd -= (startLen + endLen);
else if (selectionEnd > lineEnd - endLen)
selectionEnd = lineEnd - startLen - endLen;
else if (selectionEnd > lineIndent + startLen)
selectionEnd -= startLen;
else if (selectionEnd > lineIndent)
{
selectionEnd = lineIndent;
if (lineIndent == lineStart && i != selStartLine)
++selectionEnd; // avoid caret return in this case
}
} // ................................................
else // every iteration except the last selected line
selectionEnd -= (startLen + endLen);
// ..................................................
++nUncomments;
continue;
}
}
} // uncomment/toggle
if (currCommentMode != cm_uncomment) // comment/toggle
{
if (i == selStartLine) // is this the first selected line?
selectionStart += comment_length;
selectionEnd += comment_length; // every iteration
_pEditView->insertGenericTextFrom(lineIndent, comment.c_str());
}
}
// after uncommenting selection may promote itself to the lines
// before the first initially selected line;
// another problem - if only comment symbol was selected;
if (selectionStart < firstSelLineStart)
{
if (selectionStart >= selectionEnd - (comment_length - 1))
selectionEnd = firstSelLineStart;
selectionStart = firstSelLineStart;
}
if (not isSingleLineAdvancedMode)
{
_pEditView->insertGenericTextFrom(lineIndent, comment.c_str());
// SELECTION RANGE ADJUSTMENT .......................
if (i == selStartLine) // first selected line
{
if (selectionStart >= lineIndent)
selectionStart += comment_length;
} // ................................................
if (i == selEndLine) // last selected line
{
if (selectionEnd >= lineIndent)
selectionEnd += comment_length;
} // ................................................
else // every iteration except the last selected line
selectionEnd += comment_length;
// ..................................................
}
else // isSingleLineAdvancedMode
{
_pEditView->insertGenericTextFrom(lineIndent, advCommentStart.c_str());
_pEditView->insertGenericTextFrom(lineEnd + advCommentStart_length, advCommentEnd.c_str());
// SELECTION RANGE ADJUSTMENT .......................
if (i == selStartLine) // first selected line
{
if (selectionStart >= lineIndent)
selectionStart += advCommentStart_length;
} // ................................................
if (i == selEndLine) // last selected line
{
if (selectionEnd > lineEnd)
selectionEnd += (advCommentStart_length + advCommentEnd_length);
else if (selectionEnd >= lineIndent)
selectionEnd += advCommentStart_length;
} // ................................................
else // every iteration except the last selected line
selectionEnd += (advCommentStart_length + advCommentEnd_length);
// ..................................................
}
} // comment/toggle
} // for (...)
if (move_caret)
{
// moving caret to the beginning of selected block