tdf#140004 Toggle comment in the Basic IDE

This patch adds the "toggle comment" functionality to the Basic IDE.
The shortcut Ctrl + Alt + C is used to execute it.

It works similarly to other code editors such as Kate and VSCode.

Change-Id: Ifdae42b3729cc909baf87c729fe8c3cdf6428184
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162005
Reviewed-by: Andreas Heinisch <andreas.heinisch@yahoo.de>
Tested-by: Andreas Heinisch <andreas.heinisch@yahoo.de>
This commit is contained in:
Rafael Lima 2024-01-13 22:26:48 +01:00 committed by Andreas Heinisch
parent 7bcce4180d
commit 22b5007e27
10 changed files with 228 additions and 1 deletions

View file

@ -688,6 +688,12 @@ shell basctl_Shell
ExecMethod = ExecuteDialog;
StateMethod = GetState;
]
SID_TOGGLE_COMMENT
[
StateMethod = GetState;
ExecMethod = ExecuteGlobal;
]
}
interface BasicIDEDocument

View file

@ -1066,6 +1066,12 @@ void ModulWindow::ExecuteGlobal (SfxRequest& rReq)
GetDispatcher()->Execute(SID_GOTOLINE);
}
break;
case SID_TOGGLE_COMMENT:
{
GetEditView()->ToggleComment();
}
break;
}
}

View file

@ -1235,6 +1235,13 @@ void Shell::GetState(SfxItemSet &rSet)
rSet.DisableItem( nWh );
}
break;
case SID_TOGGLE_COMMENT:
{
// Only available in a ModulWindow if the document can be edited
if (pCurWin && (!dynamic_cast<ModulWindow*>(pCurWin.get()) || pCurWin->IsReadOnly()))
rSet.DisableItem(nWh);
}
break;
case SID_GOTOLINE:
{
// if this is not a module window hide the

View file

@ -55,6 +55,7 @@
<menu:menuitem menu:id=".uno:Paste"/>
<menu:menuseparator/>
<menu:menuitem menu:id=".uno:SelectAll"/>
<menu:menuitem menu:id=".uno:ToggleComment"/>
<menu:menuseparator/>
<menu:menuitem menu:id="vnd.sun.star.findbar:FocusToFindbar"/>
<menu:menuitem menu:id=".uno:SearchDialog"/>
@ -177,4 +178,3 @@
</menu:menupopup>
</menu:menu>
</menu:menubar>

View file

@ -670,6 +670,7 @@ class SvxZoomItem;
#define SID_BASICIDE_WATCH TypedWhichId<SfxBoolItem>( SID_BASICIDE_START + 55 )
#define SID_BASICIDE_STACK TypedWhichId<SfxBoolItem>( SID_BASICIDE_START + 56 )
#define SID_BASICIDE_COLOR_SCHEME_DLG ( SID_BASICIDE_START + 57 )
#define SID_TOGGLE_COMMENT ( SID_BASICIDE_START + 58 )
#define SID_OPTIONS_TREEDIALOG ( SID_BASICIDE_START + 862)
#define SID_OPTIONS_SECURITY ( SID_BASICIDE_START + 863)

View file

@ -222,6 +222,9 @@ public:
bool IndentBlock();
bool UnindentBlock();
// Used in the Basic IDE to toggle comment on a block of code
void ToggleComment();
};
#endif

View file

@ -371,6 +371,12 @@ Ctrl+Shift+e aka E_SHIFT_MOD1 under GTK/IBUS is for some emoji thing
</node>
<node oor:name="Modules">
<node oor:name="com.sun.star.script.BasicIDE" oor:op="replace">
<node oor:name="C_MOD1_MOD2" oor:op="replace">
<prop oor:name="Command">
<value xml:lang="x-no-translate">L10N SHORTCUTS - NO TRANSLATE</value>
<value xml:lang="en-US">.uno:ToggleComment</value>
</prop>
</node>
<node oor:name="F5" oor:op="replace">
<prop oor:name="Command">
<value xml:lang="x-no-translate">L10N SHORTCUTS - NO TRANSLATE</value>

View file

@ -18,6 +18,11 @@
<value xml:lang="en-US">Line Numbers</value>
</prop>
</node>
<node oor:name=".uno:ToggleComment" oor:op="replace">
<prop oor:name="Label" oor:type="xs:string">
<value xml:lang="en-US">Toggle Comment</value>
</prop>
</node>
<node oor:name=".uno:InsertFormRadio" oor:op="replace">
<prop oor:name="Label" oor:type="xs:string">
<value xml:lang="en-US">Form Option Button</value>

View file

@ -2393,6 +2393,22 @@ SfxBoolItem ShowLines SID_SHOWLINES
GroupId = SfxGroupId::Macro;
]
SfxVoidItem ToggleComment SID_TOGGLE_COMMENT
()
[
AutoUpdate = TRUE,
FastCall = FALSE,
ReadOnlyDoc = TRUE,
Toggle = FALSE,
Container = FALSE,
RecordAbsolute = FALSE,
RecordPerSet;
AccelConfig = TRUE,
MenuConfig = TRUE,
ToolBoxConfig = TRUE,
GroupId = SfxGroupId::Macro;
]
SfxVoidItem RunMacro SID_RUNMACRO
()

View file

@ -20,6 +20,7 @@
#include <memory>
#include <i18nutil/searchopt.hxx>
#include <o3tl/deleter.hxx>
#include <o3tl/string_view.hxx>
#include <utility>
#include <vcl/textview.hxx>
#include <vcl/texteng.hxx>
@ -2234,5 +2235,181 @@ bool TextView::UnindentBlock()
return ImpIndentBlock( false );
}
void TextView::ToggleComment()
{
/* To determines whether to add or remove comment markers, the rule is:
* - If any of the lines in the selection does not start with a comment character "'"
* or "REM" then the selection is commented
* - Otherwise, the selection is uncommented (i.e. if all of the lines start with a
* comment marker "'" or "REM")
* - Empty lines, or lines with only blank spaces or tabs are ignored
*/
TextEngine* pEngine = GetTextEngine();
TextSelection aSel = GetSelection();
sal_uInt32 nStartPara = aSel.GetStart().GetPara();
sal_uInt32 nEndPara = aSel.GetEnd().GetPara();
// True = Comment character will be added; False = Comment marker will be removed
bool bAddCommentChar = false;
// Indicates whether any change has been made
bool bChanged = false;
// Indicates whether the selection is downwards (normal) or upwards (reversed)
bool bSelReversed = false;
if (nEndPara < nStartPara)
{
std::swap(nStartPara, nEndPara);
bSelReversed = true;
}
for (sal_uInt32 n = nStartPara; n <= nEndPara; n++)
{
OUString sText = pEngine->GetText(n).trim();
// Empty lines or lines with only blank spaces and tabs are ignored
if (sText.isEmpty())
continue;
if (!sText.startsWith("'") && !sText.startsWithIgnoreAsciiCase("REM"))
{
bAddCommentChar = true;
break;
}
// Notice that a REM comment is only actually a comment if:
// a) There is no subsequent character or
// b) The subsequent character is a blank space or a tab
OUString sRest;
if (sText.startsWithIgnoreAsciiCase("REM", &sRest))
{
if (sRest.getLength() > 0 && !sRest.startsWith(" ") && !sRest.startsWith("\t"))
{
bAddCommentChar = true;
break;
}
}
}
if (bAddCommentChar)
{
// For each line, determine the first position where there is a character that is not
// a blank space or a tab; the comment marker will be the smallest such position
size_t nCommentPos = std::string::npos;
for (sal_uInt32 n = nStartPara; n <= nEndPara; n++)
{
OUString sText = pEngine->GetText(n);
std::u16string_view sLine(sText);
sal_uInt32 nCharPos = sLine.find_first_not_of(u" \t");
// Update the position where to place the comment marker
if (nCharPos < nCommentPos)
nCommentPos = nCharPos;
// If the comment position is zero, then there's no more need to keep searching
if (nCommentPos == 0)
break;
}
// Insert the comment marker in all lines (except empty lines)
for (sal_uInt32 n = nStartPara; n <= nEndPara; n++)
{
OUString sText = pEngine->GetText(n);
std::u16string_view sLine(sText);
if (o3tl::trim(sLine).length() > 0)
{
pEngine->ImpInsertText(TextPaM(n, nCommentPos), "' ");
bChanged = true;
}
}
}
else
{
// For each line, find the first comment marker and remove it
for (sal_uInt32 nPara = nStartPara; nPara <= nEndPara; nPara++)
{
OUString sText = pEngine->GetText(nPara);
if (!sText.isEmpty())
{
// Determine the position of the comment marker and check whether it's
// a single quote "'" or a "REM" comment
sal_Int32 nQuotePos = sText.indexOf("'");
sal_Int32 nRemPos = sText.toAsciiUpperCase().indexOf("REM");
// An empty line or a line with only blank spaces or tabs needs to be skipped
if (nQuotePos == -1 && nRemPos == -1)
continue;
// nRemPos only refers to a comment if the subsequent character is a blank space or tab
const sal_Int32 nRemSub = nRemPos + 3;
if (nRemPos != -1 && nRemPos < sText.getLength() - 3 &&
sText.indexOf(" ", nRemSub) != nRemSub &&
sText.indexOf("\t", nRemSub) != nRemSub)
{
nRemPos = -1;
}
// True = comment uses single quote; False = comment uses REM
bool bQuoteComment = true;
// Start and end positions to be removed
sal_Int32 nStartPos = nQuotePos;
sal_Int32 nEndPos = nStartPos + 1;
if (nQuotePos == -1)
bQuoteComment = false;
else if (nRemPos != -1 && nRemPos < nQuotePos)
bQuoteComment = false;
if (!bQuoteComment)
{
nStartPos = nRemPos;
nEndPos = nStartPos + 3;
}
// Check if the next character is a blank space or a tab
if (sText.indexOf(" ", nEndPos) == nEndPos || sText.indexOf("\t", nEndPos) == nEndPos)
nEndPos++;
// Remove the comment marker
pEngine->ImpDeleteText(TextSelection(TextPaM(nPara, nStartPos), TextPaM(nPara, nEndPos)));
bChanged = true;
}
}
}
// Update selection if there was a selection in the first place
if (bChanged)
{
TextPaM aNewStart;
if (!bSelReversed)
aNewStart = TextPaM(nStartPara, std::min(aSel.GetStart().GetIndex(),
pEngine->GetText(nStartPara).getLength()));
else
aNewStart = TextPaM(nStartPara, std::min(aSel.GetEnd().GetIndex(),
pEngine->GetText(nEndPara).getLength()));
if (HasSelection())
{
TextPaM aNewEnd;
if (!bSelReversed)
aNewEnd = TextPaM(nEndPara, pEngine->GetText(nEndPara).getLength());
else
aNewEnd = TextPaM(nEndPara, pEngine->GetText(nStartPara).getLength());
TextSelection aNewSel(aNewStart, aNewEnd);
ImpSetSelection(aNewSel);
}
else
{
TextSelection aNewSel(aNewStart, aNewStart);
ImpSetSelection(aNewSel);
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */