Files
qt-creator/src/plugins/fakevim/fakevimactions.cpp
Christian Stenger e47dd1d765 FakeVim: Restrict tabstop size
The default minimum of QSpinBox is 0 which is not allowed
for vim's tabstop in general.
It has to be some positive integer.
Beside disallowing the unsupported value it also fixes
some crash which could happen if using the zero as
tabstop width. (div by zero)

Fixes: QTCREATORBUG-28082
Change-Id: I8913b1c28b8ec239ecdf2306fe8861669ccb9d56
Reviewed-by: hjk <hjk@qt.io>
2024-08-01 10:57:51 +00:00

319 lines
12 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "fakevimactions.h"
#include "fakevimhandler.h"
#include "fakevimtr.h"
// Please do not add any direct dependencies to other Qt Creator code here.
// Instead emit signals and let the FakeVimPlugin channel the information to
// Qt Creator. The idea is to keep this file here in a "clean" state that
// allows easy reuse with any QTextEdit or QPlainTextEdit derived class.
#ifndef FAKEVIM_STANDALONE
#include <texteditor/icodestylepreferences.h>
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/typingsettings.h>
#endif
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <QDebug>
using namespace Utils;
namespace FakeVim::Internal {
#ifdef FAKEVIM_STANDALONE
void FvBaseAspect::setSettingsKey(const Key &group, const Key &key)
{
m_settingsGroup = group;
m_settingsKey = key;
}
Key FvBaseAspect::settingsKey() const
{
return m_settingsKey;
}
// unused but kept for compile
void setAutoApply(bool ) {}
#endif
FakeVimSettings &settings()
{
static FakeVimSettings theSettings;
return theSettings;
}
FakeVimSettings::FakeVimSettings()
{
setAutoApply(false);
setup(&useFakeVim, false, "UseFakeVim", {}, Tr::tr("Use FakeVim"));
// Specific FakeVim settings
setup(&readVimRc, false, "ReadVimRc", {}, Tr::tr("Read .vimrc from location:"));
setup(&vimRcPath, QString(), "VimRcPath", {}, {}); // Tr::tr("Path to .vimrc")
setup(&showMarks, false, "ShowMarks", "sm", Tr::tr("Show position of text marks"));
setup(&passControlKey, false, "PassControlKey", "pck", Tr::tr("Pass control keys"));
setup(&passKeys, true, "PassKeys", "pk", Tr::tr("Pass keys in insert mode"));
// Emulated Vsetting
setup(&startOfLine, true, "StartOfLine", "sol", Tr::tr("Start of line"));
setup(&tabStop, 8, "TabStop", "ts", Tr::tr("Tabulator size:"));
setup(&smartTab, false, "SmartTab", "sta", Tr::tr("Smart tabulators"));
setup(&hlSearch, true, "HlSearch", "hls", Tr::tr("Highlight search results"));
setup(&shiftWidth, 8, "ShiftWidth", "sw", Tr::tr("Shift width:"));
setup(&expandTab, false, "ExpandTab", "et", Tr::tr("Expand tabulators"));
setup(&autoIndent, false, "AutoIndent", "ai", Tr::tr("Automatic indentation"));
setup(&smartIndent, false, "SmartIndent", "si", Tr::tr("Smart indentation"));
setup(&incSearch, true, "IncSearch", "is", Tr::tr("Incremental search"));
setup(&useCoreSearch, false, "UseCoreSearch", "ucs", Tr::tr("Use search dialog"));
setup(&smartCase, false, "SmartCase", "scs", Tr::tr("Use smartcase"));
setup(&ignoreCase, false, "IgnoreCase", "ic", Tr::tr("Use ignorecase"));
setup(&wrapScan, true, "WrapScan", "ws", Tr::tr("Use wrapscan"));
setup(&tildeOp, false, "TildeOp", "top", Tr::tr("Use tildeop"));
setup(&showCmd, true, "ShowCmd", "sc", Tr::tr("Show partial command"));
setup(&relativeNumber, false, "RelativeNumber", "rnu", Tr::tr("Show line numbers relative to cursor"));
setup(&blinkingCursor, false, "BlinkingCursor", "bc", Tr::tr("Blinking cursor"));
setup(&systemEncoding, false, "SystemEncoding", {}, Tr::tr("Use system encoding for :source"));
setup(&scrollOff, 0, "ScrollOff", "so", Tr::tr("Scroll offset:"));
setup(&backspace, "indent,eol,start",
"Backspace", "bs", Tr::tr("Backspace:"));
setup(&isKeyword, "@,48-57,_,192-255,a-z,A-Z",
"IsKeyword", "isk", Tr::tr("Keyword characters:"));
setup(&clipboard, {}, "Clipboard", "cb", Tr::tr(""));
setup(&formatOptions, {}, "formatoptions", "fo", Tr::tr(""));
// Emulated plugins
setup(&emulateVimCommentary, false, "commentary", {}, "vim-commentary");
setup(&emulateReplaceWithRegister, false, "ReplaceWithRegister", {}, "ReplaceWithRegister");
setup(&emulateExchange, false, "exchange", {}, "vim-exchange");
setup(&emulateArgTextObj, false, "argtextobj", {}, "argtextobj.vim");
setup(&emulateSurround, false, "surround", {}, "vim-surround");
// Some polish
useFakeVim.setDisplayName(Tr::tr("Use Vim-style Editing"));
relativeNumber.setToolTip(Tr::tr("Displays line numbers relative to the line containing "
"text cursor."));
passControlKey.setToolTip(Tr::tr("Does not interpret key sequences like Ctrl-S in FakeVim "
"but handles them as regular shortcuts. This gives easier access to core functionality "
"at the price of losing some features of FakeVim."));
passKeys.setToolTip(Tr::tr("Does not interpret some key presses in insert mode so that "
"code can be properly completed and expanded."));
tabStop.setToolTip(Tr::tr("Vim tabstop option."));
#ifndef FAKEVIM_STANDALONE
tabStop.setRange(1, 99);
backspace.setDisplayStyle(FvStringAspect::LineEditDisplay);
isKeyword.setDisplayStyle(FvStringAspect::LineEditDisplay);
const QString vimrcDefault = QLatin1String(HostOsInfo::isAnyUnixHost()
? "$HOME/.vimrc" : "%USERPROFILE%\\_vimrc");
vimRcPath.setExpectedKind(PathChooser::File);
vimRcPath.setToolTip(Tr::tr("Keep empty to use the default path, i.e. "
"%USERPROFILE%\\_vimrc on Windows, ~/.vimrc otherwise."));
vimRcPath.setPlaceHolderText(Tr::tr("Default: %1").arg(vimrcDefault));
setLayouter([this] {
using namespace Layouting;
using namespace TextEditor;
Row bools {
Column {
autoIndent,
smartIndent,
expandTab,
smartTab,
hlSearch,
showCmd,
startOfLine,
passKeys,
blinkingCursor,
If { HostOsInfo::isWindowsHost(), { systemEncoding } }
},
Column {
incSearch,
useCoreSearch,
ignoreCase,
smartCase,
wrapScan,
showMarks,
passControlKey,
relativeNumber,
tildeOp
}
};
Row ints { shiftWidth, tabStop, scrollOff, st };
Column strings {
backspace,
isKeyword,
Row {readVimRc, vimRcPath}
};
return Column {
useFakeVim,
Group {
title(Tr::tr("Vim Behavior")),
Column {
bools,
ints,
strings
}
},
Group {
title(Tr::tr("Plugin Emulation")),
Column {
emulateVimCommentary,
emulateReplaceWithRegister,
emulateArgTextObj,
emulateExchange,
emulateSurround
}
},
Row {
PushButton {
text(Tr::tr("Copy Text Editor Settings")),
onClicked([this] {
TabSettings ts = TextEditorSettings::codeStyle()->tabSettings();
TypingSettings tps = globalTypingSettings();
expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
tabStop.setValue(ts.m_tabSize);
shiftWidth.setValue(ts.m_indentSize);
smartTab.setValue(tps.m_smartBackspaceBehavior
== TypingSettings::BackspaceFollowsPreviousIndents);
autoIndent.setValue(true);
smartIndent.setValue(tps.m_autoIndent);
incSearch.setValue(true);
}, this),
},
PushButton {
text(Tr::tr("Set Qt Style")),
onClicked([this] {
expandTab.setVolatileValue(true);
tabStop.setVolatileValue(4);
shiftWidth.setVolatileValue(4);
smartTab.setVolatileValue(true);
autoIndent.setVolatileValue(true);
smartIndent.setVolatileValue(true);
incSearch.setVolatileValue(true);
backspace.setVolatileValue(QString("indent,eol,start"));
passKeys.setVolatileValue(true);
}, this),
},
PushButton {
text(Tr::tr("Set Plain Style")),
onClicked([this] {
expandTab.setVolatileValue(false);
tabStop.setVolatileValue(8);
shiftWidth.setVolatileValue(8);
smartTab.setVolatileValue(false);
autoIndent.setVolatileValue(false);
smartIndent.setVolatileValue(false);
incSearch.setVolatileValue(false);
backspace.setVolatileValue(QString());
passKeys.setVolatileValue(false);
}, this),
},
st
},
st
};
});
readSettings();
vimRcPath.setEnabler(&readVimRc);
#endif
}
FakeVimSettings::~FakeVimSettings() = default;
FvBaseAspect *FakeVimSettings::item(const Utils::Key &name)
{
return m_nameToAspect.value(name, nullptr);
}
QString FakeVimSettings::trySetValue(const QString &name, const QString &value)
{
FvBaseAspect *aspect = m_nameToAspect.value(keyFromString(name), nullptr);
if (!aspect)
return Tr::tr("Unknown option: %1").arg(name);
if (aspect == &tabStop || aspect == &shiftWidth) {
if (value.toInt() <= 0)
return Tr::tr("Argument must be positive: %1=%2")
.arg(name).arg(value);
}
aspect->setVariantValue(value);
return QString();
}
void FakeVimSettings::setup(FvBaseAspect *aspect,
const QVariant &value,
const Utils::Key &settingsKey,
const Utils::Key &shortName,
const QString &labelText)
{
aspect->setSettingsKey("FakeVim", settingsKey);
aspect->setDefaultVariantValue(value);
#ifndef FAKEVIM_STANDALONE
aspect->setLabelText(labelText);
aspect->setAutoApply(false);
registerAspect(aspect);
if (auto boolAspect = dynamic_cast<FvBoolAspect *>(aspect))
boolAspect->setLabelPlacement(FvBoolAspect::LabelPlacement::AtCheckBox);
#else
Q_UNUSED(labelText)
#endif
const Key longName = settingsKey.toByteArray().toLower();
if (!longName.isEmpty()) {
m_nameToAspect[longName] = aspect;
m_aspectToName[aspect] = longName;
}
if (!shortName.isEmpty())
m_nameToAspect[shortName] = aspect;
}
#ifndef FAKEVIM_STANDALONE
class FakeVimSettingsPage final : public Core::IOptionsPage
{
public:
FakeVimSettingsPage()
{
const char SETTINGS_CATEGORY[] = "D.FakeVim";
const char SETTINGS_ID[] = "A.FakeVim.General";
setId(SETTINGS_ID);
setDisplayName(Tr::tr("General"));
setCategory(SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("FakeVim"));
setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png");
setSettingsProvider([] { return &settings(); });
}
};
const FakeVimSettingsPage settingsPage;
#endif
} // FakeVim::Internal