forked from qt-creator/qt-creator
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>
319 lines
12 KiB
C++
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
|