2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2008-12-19 12:20:04 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2008-12-19 12:20:04 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2008-12-19 12:20:04 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2016-01-15 14:57:40 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2008-12-19 12:20:04 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2010-12-17 16:01:08 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2009-03-06 11:22:16 +01:00
|
|
|
//
|
2009-04-16 09:18:09 +02:00
|
|
|
// ATTENTION:
|
|
|
|
|
//
|
|
|
|
|
// 1 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.
|
|
|
|
|
//
|
|
|
|
|
// 2 There are a few auto tests located in ../../../tests/auto/fakevim.
|
|
|
|
|
// Commands that are covered there are marked as "// tested" below.
|
|
|
|
|
//
|
|
|
|
|
// 3 Some conventions:
|
2009-03-06 11:22:16 +01:00
|
|
|
//
|
2009-04-16 09:18:09 +02:00
|
|
|
// Use 1 based line numbers and 0 based column numbers. Even though
|
|
|
|
|
// the 1 based line are not nice it matches vim's and QTextEdit's 'line'
|
|
|
|
|
// concepts.
|
2009-03-06 11:22:16 +01:00
|
|
|
//
|
2009-04-16 09:18:09 +02:00
|
|
|
// Do not pass QTextCursor etc around unless really needed. Convert
|
|
|
|
|
// early to line/column.
|
|
|
|
|
//
|
2010-01-21 17:38:28 +01:00
|
|
|
// A QTextCursor is always between characters, whereas vi's cursor is always
|
|
|
|
|
// over a character. FakeVim interprets the QTextCursor to be over the character
|
|
|
|
|
// to the right of the QTextCursor's position().
|
|
|
|
|
//
|
2010-09-13 14:16:18 +02:00
|
|
|
// A current "region of interest"
|
2010-09-14 14:04:13 +02:00
|
|
|
// spans between anchor(), (i.e. the character below anchor()), and
|
|
|
|
|
// position(). The character below position() is not included
|
2010-01-21 17:38:28 +01:00
|
|
|
// if the last movement command was exclusive (MoveExclusive).
|
2010-05-06 14:08:09 +02:00
|
|
|
//
|
2009-03-06 11:22:16 +01:00
|
|
|
|
2010-04-27 18:02:19 +02:00
|
|
|
#include "fakevimhandler.h"
|
|
|
|
|
|
2013-03-08 17:50:16 +01:00
|
|
|
#include "fakevimactions.h"
|
2014-08-25 17:05:11 +02:00
|
|
|
#include "fakevimtr.h"
|
2013-03-08 17:50:16 +01:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QObject>
|
|
|
|
|
#include <QPointer>
|
|
|
|
|
#include <QProcess>
|
2020-06-11 08:33:47 +02:00
|
|
|
#include <QRegularExpression>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QTextStream>
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
#include <QStack>
|
2008-12-19 16:20:39 +01:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QApplication>
|
2012-07-26 21:46:38 +02:00
|
|
|
#include <QClipboard>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QInputMethodEvent>
|
|
|
|
|
#include <QKeyEvent>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QPlainTextEdit>
|
|
|
|
|
#include <QScrollBar>
|
|
|
|
|
#include <QTextBlock>
|
|
|
|
|
#include <QTextCursor>
|
|
|
|
|
#include <QTextDocumentFragment>
|
|
|
|
|
#include <QTextEdit>
|
2012-08-21 11:39:35 +02:00
|
|
|
#include <QMimeData>
|
2014-04-30 17:32:25 +02:00
|
|
|
#include <QSharedPointer>
|
2016-06-03 13:00:51 +02:00
|
|
|
#include <QDir>
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2010-01-25 10:45:30 +01:00
|
|
|
#include <algorithm>
|
2009-08-11 16:45:29 +02:00
|
|
|
#include <climits>
|
2009-12-11 13:24:53 +01:00
|
|
|
#include <ctype.h>
|
2014-11-15 13:34:32 +01:00
|
|
|
#include <functional>
|
2009-08-11 16:45:29 +02:00
|
|
|
|
2009-03-05 11:06:25 +01:00
|
|
|
//#define DEBUG_KEY 1
|
|
|
|
|
#if DEBUG_KEY
|
|
|
|
|
# define KEY_DEBUG(s) qDebug() << s
|
|
|
|
|
#else
|
|
|
|
|
# define KEY_DEBUG(s)
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-03-06 11:22:16 +01:00
|
|
|
//#define DEBUG_UNDO 1
|
|
|
|
|
#if DEBUG_UNDO
|
2014-04-30 17:32:25 +02:00
|
|
|
# define UNDO_DEBUG(s) qDebug() << "REV" << revision() << s
|
2009-03-06 11:22:16 +01:00
|
|
|
#else
|
|
|
|
|
# define UNDO_DEBUG(s)
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-03-30 12:40:08 +02:00
|
|
|
namespace FakeVim {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2009-03-06 13:22:39 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// FakeVimHandler
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2009-04-14 15:47:25 +02:00
|
|
|
#define StartOfLine QTextCursor::StartOfLine
|
|
|
|
|
#define EndOfLine QTextCursor::EndOfLine
|
|
|
|
|
#define MoveAnchor QTextCursor::MoveAnchor
|
|
|
|
|
#define KeepAnchor QTextCursor::KeepAnchor
|
|
|
|
|
#define Up QTextCursor::Up
|
|
|
|
|
#define Down QTextCursor::Down
|
|
|
|
|
#define Right QTextCursor::Right
|
|
|
|
|
#define Left QTextCursor::Left
|
|
|
|
|
#define EndOfDocument QTextCursor::End
|
|
|
|
|
#define StartOfDocument QTextCursor::Start
|
2013-02-23 12:45:47 +01:00
|
|
|
#define NextBlock QTextCursor::NextBlock
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2012-08-19 19:08:04 +02:00
|
|
|
#define ParagraphSeparator QChar::ParagraphSeparator
|
|
|
|
|
|
2008-12-26 18:29:38 +01:00
|
|
|
#define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
|
|
|
|
|
|
2018-04-11 12:03:04 +02:00
|
|
|
|
|
|
|
|
#ifdef Q_OS_DARWIN
|
|
|
|
|
#define ControlModifier Qt::MetaModifier
|
|
|
|
|
#else
|
|
|
|
|
#define ControlModifier Qt::ControlModifier
|
|
|
|
|
#endif
|
2011-04-07 17:59:12 +02:00
|
|
|
|
2012-08-09 17:32:39 +02:00
|
|
|
/* Clipboard MIME types used by Vim. */
|
2016-03-18 12:10:24 +01:00
|
|
|
static const QString vimMimeText = "_VIM_TEXT";
|
|
|
|
|
static const QString vimMimeTextEncoded = "_VIMENC_TEXT";
|
2012-08-09 17:32:39 +02:00
|
|
|
|
2008-12-19 12:20:04 +01:00
|
|
|
using namespace Qt;
|
|
|
|
|
|
2010-05-06 14:08:09 +02:00
|
|
|
/*! A \e Mode represents one of the basic modes of operation of FakeVim.
|
|
|
|
|
*/
|
2009-04-01 15:11:26 +02:00
|
|
|
|
2008-12-19 16:20:39 +01:00
|
|
|
enum Mode
|
|
|
|
|
{
|
|
|
|
|
InsertMode,
|
2010-05-06 12:10:57 +02:00
|
|
|
ReplaceMode,
|
2008-12-19 16:20:39 +01:00
|
|
|
CommandMode,
|
2010-12-16 12:05:48 +01:00
|
|
|
ExMode
|
2008-12-19 16:20:39 +01:00
|
|
|
};
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
enum BlockInsertMode
|
|
|
|
|
{
|
|
|
|
|
NoneBlockInsertMode,
|
|
|
|
|
AppendBlockInsertMode,
|
|
|
|
|
AppendToEndOfLineBlockInsertMode,
|
|
|
|
|
InsertBlockInsertMode,
|
|
|
|
|
ChangeBlockInsertMode
|
|
|
|
|
};
|
|
|
|
|
|
2010-05-06 14:08:09 +02:00
|
|
|
/*! A \e SubMode is used for things that require one more data item
|
|
|
|
|
and are 'nested' behind a \l Mode.
|
|
|
|
|
*/
|
2008-12-19 16:20:39 +01:00
|
|
|
enum SubMode
|
|
|
|
|
{
|
|
|
|
|
NoSubMode,
|
2010-07-14 13:02:17 +02:00
|
|
|
ChangeSubMode, // Used for c
|
|
|
|
|
DeleteSubMode, // Used for d
|
|
|
|
|
FilterSubMode, // Used for !
|
|
|
|
|
IndentSubMode, // Used for =
|
|
|
|
|
RegisterSubMode, // Used for "
|
|
|
|
|
ShiftLeftSubMode, // Used for <
|
|
|
|
|
ShiftRightSubMode, // Used for >
|
2012-10-13 09:33:30 +02:00
|
|
|
InvertCaseSubMode, // Used for g~
|
|
|
|
|
DownCaseSubMode, // Used for gu
|
|
|
|
|
UpCaseSubMode, // Used for gU
|
2010-07-14 13:02:17 +02:00
|
|
|
WindowSubMode, // Used for Ctrl-w
|
|
|
|
|
YankSubMode, // Used for y
|
|
|
|
|
ZSubMode, // Used for z
|
|
|
|
|
CapitalZSubMode, // Used for Z
|
2013-03-17 12:41:18 +01:00
|
|
|
ReplaceSubMode, // Used for r
|
|
|
|
|
MacroRecordSubMode, // Used for q
|
2014-04-28 13:13:10 +02:00
|
|
|
MacroExecuteSubMode, // Used for @
|
2015-03-28 21:14:37 +01:00
|
|
|
CtrlVSubMode, // Used for Ctrl-v in insert mode
|
|
|
|
|
CtrlRSubMode // Used for Ctrl-r in insert mode
|
2008-12-19 16:20:39 +01:00
|
|
|
};
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2010-05-06 14:08:09 +02:00
|
|
|
/*! A \e SubSubMode is used for things that require one more data item
|
|
|
|
|
and are 'nested' behind a \l SubMode.
|
|
|
|
|
*/
|
2008-12-26 00:18:03 +01:00
|
|
|
enum SubSubMode
|
|
|
|
|
{
|
|
|
|
|
NoSubSubMode,
|
2012-11-29 20:14:56 +01:00
|
|
|
FtSubSubMode, // Used for f, F, t, T.
|
|
|
|
|
MarkSubSubMode, // Used for m.
|
|
|
|
|
BackTickSubSubMode, // Used for `.
|
|
|
|
|
TickSubSubMode, // Used for '.
|
|
|
|
|
TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
|
|
|
|
|
ZSubSubMode, // Used for zj, zk
|
|
|
|
|
OpenSquareSubSubMode, // Used for [{, {(, [z
|
|
|
|
|
CloseSquareSubSubMode, // Used for ]}, ]), ]z
|
2014-04-28 13:13:10 +02:00
|
|
|
SearchSubSubMode,
|
|
|
|
|
CtrlVUnicodeSubSubMode // Used for Ctrl-v based unicode input
|
2008-12-26 00:18:03 +01:00
|
|
|
};
|
|
|
|
|
|
2009-01-06 11:52:05 +01:00
|
|
|
enum VisualMode
|
|
|
|
|
{
|
|
|
|
|
NoVisualMode,
|
|
|
|
|
VisualCharMode,
|
|
|
|
|
VisualLineMode,
|
2010-12-16 12:05:48 +01:00
|
|
|
VisualBlockMode
|
2009-01-06 11:52:05 +01:00
|
|
|
};
|
|
|
|
|
|
2009-01-16 16:57:00 +01:00
|
|
|
enum MoveType
|
|
|
|
|
{
|
|
|
|
|
MoveExclusive,
|
|
|
|
|
MoveInclusive,
|
2010-12-16 12:05:48 +01:00
|
|
|
MoveLineWise
|
2009-01-16 16:57:00 +01:00
|
|
|
};
|
|
|
|
|
|
2010-05-06 14:08:09 +02:00
|
|
|
/*!
|
|
|
|
|
\enum RangeMode
|
|
|
|
|
|
|
|
|
|
The \e RangeMode serves as a means to define how the "Range" between
|
|
|
|
|
the \l cursor and the \l anchor position is to be interpreted.
|
|
|
|
|
|
|
|
|
|
\value RangeCharMode Entered by pressing \key v. The range includes
|
|
|
|
|
all characters between cursor and anchor.
|
|
|
|
|
\value RangeLineMode Entered by pressing \key V. The range includes
|
|
|
|
|
all lines between the line of the cursor and
|
|
|
|
|
the line of the anchor.
|
2014-11-15 13:34:32 +01:00
|
|
|
\value RangeLineModeExclusive Like \l RangeLineMode, but keeps one
|
2010-05-06 14:08:09 +02:00
|
|
|
newline when deleting.
|
|
|
|
|
\value RangeBlockMode Entered by pressing \key Ctrl-v. The range includes
|
|
|
|
|
all characters with line and column coordinates
|
|
|
|
|
between line and columns coordinates of cursor and
|
|
|
|
|
anchor.
|
|
|
|
|
\value RangeBlockAndTailMode Like \l RangeBlockMode, but also includes
|
|
|
|
|
all characters in the affected lines up to the end
|
|
|
|
|
of these lines.
|
|
|
|
|
*/
|
2009-08-11 14:39:44 +02:00
|
|
|
|
|
|
|
|
enum EventResult
|
|
|
|
|
{
|
|
|
|
|
EventHandled,
|
|
|
|
|
EventUnhandled,
|
2012-11-06 17:29:04 +01:00
|
|
|
EventCancelled, // Event is handled but a sub mode was cancelled.
|
2009-08-11 14:39:44 +02:00
|
|
|
EventPassedToCore
|
|
|
|
|
};
|
|
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
struct CursorPosition
|
|
|
|
|
{
|
2018-11-07 23:45:39 +01:00
|
|
|
CursorPosition() = default;
|
2012-10-23 16:35:13 +02:00
|
|
|
CursorPosition(int block, int column) : line(block), column(column) {}
|
|
|
|
|
explicit CursorPosition(const QTextCursor &tc)
|
|
|
|
|
: line(tc.block().blockNumber()), column(tc.positionInBlock()) {}
|
|
|
|
|
CursorPosition(const QTextDocument *document, int position)
|
|
|
|
|
{
|
|
|
|
|
QTextBlock block = document->findBlock(position);
|
|
|
|
|
line = block.blockNumber();
|
|
|
|
|
column = position - block.position();
|
|
|
|
|
}
|
|
|
|
|
bool isValid() const { return line >= 0 && column >= 0; }
|
2012-12-05 18:00:49 +01:00
|
|
|
bool operator>(const CursorPosition &other) const
|
|
|
|
|
{ return line > other.line || column > other.column; }
|
2012-10-23 16:35:13 +02:00
|
|
|
bool operator==(const CursorPosition &other) const
|
|
|
|
|
{ return line == other.line && column == other.column; }
|
|
|
|
|
bool operator!=(const CursorPosition &other) const { return !operator==(other); }
|
|
|
|
|
|
2015-06-18 13:32:25 +02:00
|
|
|
int line = -1; // Line in document (from 0, folded lines included).
|
|
|
|
|
int column = -1; // Position on line.
|
2012-10-23 16:35:13 +02:00
|
|
|
};
|
|
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
QDebug operator<<(QDebug ts, const CursorPosition &pos)
|
|
|
|
|
{
|
|
|
|
|
return ts << "(line: " << pos.line << ", column: " << pos.column << ")";
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
class Mark
|
2012-10-28 14:00:50 +01:00
|
|
|
{
|
2014-02-03 20:47:47 +01:00
|
|
|
public:
|
2012-10-28 14:00:50 +01:00
|
|
|
Mark(const CursorPosition &pos = CursorPosition(), const QString &fileName = QString())
|
2014-02-03 20:47:47 +01:00
|
|
|
: m_position(pos), m_fileName(fileName) {}
|
2012-10-28 14:00:50 +01:00
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
bool isValid() const { return m_position.isValid(); }
|
2012-10-28 14:00:50 +01:00
|
|
|
|
|
|
|
|
bool isLocal(const QString &localFileName) const
|
|
|
|
|
{
|
2014-02-03 20:47:47 +01:00
|
|
|
return m_fileName.isEmpty() || m_fileName == localFileName;
|
2012-10-28 14:00:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
/* Return position of mark within given document.
|
|
|
|
|
* If saved line number is too big, mark position is at the end of document.
|
|
|
|
|
* If line number is in document but column is too big, mark position is at the end of line.
|
|
|
|
|
*/
|
|
|
|
|
CursorPosition position(const QTextDocument *document) const
|
|
|
|
|
{
|
|
|
|
|
QTextBlock block = document->findBlockByNumber(m_position.line);
|
|
|
|
|
CursorPosition pos;
|
|
|
|
|
if (block.isValid()) {
|
|
|
|
|
pos.line = m_position.line;
|
|
|
|
|
pos.column = qMax(0, qMin(m_position.column, block.length() - 2));
|
|
|
|
|
} else if (document->isEmpty()) {
|
|
|
|
|
pos.line = 0;
|
|
|
|
|
pos.column = 0;
|
|
|
|
|
} else {
|
|
|
|
|
pos.line = document->blockCount() - 1;
|
|
|
|
|
pos.column = qMax(0, document->lastBlock().length() - 2);
|
|
|
|
|
}
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString &fileName() const { return m_fileName; }
|
|
|
|
|
|
2014-08-09 15:42:35 +02:00
|
|
|
void setFileName(const QString &fileName) { m_fileName = fileName; }
|
|
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
private:
|
|
|
|
|
CursorPosition m_position;
|
|
|
|
|
QString m_fileName;
|
2012-10-28 14:00:50 +01:00
|
|
|
};
|
2018-11-07 23:45:39 +01:00
|
|
|
using Marks = QHash<QChar, Mark>;
|
2012-10-28 14:00:50 +01:00
|
|
|
|
2012-08-26 18:39:48 +02:00
|
|
|
struct State
|
|
|
|
|
{
|
2018-11-07 23:45:39 +01:00
|
|
|
State() = default;
|
2013-07-20 08:36:05 +02:00
|
|
|
State(int revision, const CursorPosition &position, const Marks &marks,
|
|
|
|
|
VisualMode lastVisualMode, bool lastVisualModeInverted) : revision(revision),
|
2012-12-05 18:00:49 +01:00
|
|
|
position(position), marks(marks), lastVisualMode(lastVisualMode),
|
|
|
|
|
lastVisualModeInverted(lastVisualModeInverted) {}
|
2012-10-28 10:43:22 +01:00
|
|
|
|
2013-04-08 21:25:29 +02:00
|
|
|
bool isValid() const { return position.isValid(); }
|
|
|
|
|
|
2015-06-18 13:32:25 +02:00
|
|
|
int revision = -1;
|
2012-10-23 16:35:13 +02:00
|
|
|
CursorPosition position;
|
2012-08-26 18:39:48 +02:00
|
|
|
Marks marks;
|
2015-06-18 13:32:25 +02:00
|
|
|
VisualMode lastVisualMode = NoVisualMode;
|
|
|
|
|
bool lastVisualModeInverted = false;
|
2012-08-26 18:39:48 +02:00
|
|
|
};
|
|
|
|
|
|
2010-03-09 16:44:36 +01:00
|
|
|
struct Column
|
2009-12-11 13:24:53 +01:00
|
|
|
{
|
2010-03-09 16:44:36 +01:00
|
|
|
Column(int p, int l) : physical(p), logical(l) {}
|
2010-05-06 14:08:09 +02:00
|
|
|
int physical; // Number of characters in the data.
|
|
|
|
|
int logical; // Column on screen.
|
2009-12-11 13:24:53 +01:00
|
|
|
};
|
|
|
|
|
|
2010-07-06 16:17:27 +02:00
|
|
|
QDebug operator<<(QDebug ts, const Column &col)
|
|
|
|
|
{
|
|
|
|
|
return ts << "(p: " << col.physical << ", l: " << col.logical << ")";
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-11 14:39:44 +02:00
|
|
|
struct Register
|
|
|
|
|
{
|
2018-11-07 23:45:39 +01:00
|
|
|
Register() = default;
|
2015-06-18 13:32:25 +02:00
|
|
|
Register(const QString &c) : contents(c) {}
|
2009-08-11 14:39:44 +02:00
|
|
|
Register(const QString &c, RangeMode m) : contents(c), rangemode(m) {}
|
|
|
|
|
QString contents;
|
2015-06-18 13:32:25 +02:00
|
|
|
RangeMode rangemode = RangeCharMode;
|
2009-08-11 14:39:44 +02:00
|
|
|
};
|
|
|
|
|
|
2010-05-20 14:08:11 +02:00
|
|
|
QDebug operator<<(QDebug ts, const Register ®)
|
|
|
|
|
{
|
|
|
|
|
return ts << reg.contents;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 18:24:00 +02:00
|
|
|
struct SearchData
|
|
|
|
|
{
|
|
|
|
|
QString needle;
|
2015-06-18 13:32:25 +02:00
|
|
|
bool forward = true;
|
|
|
|
|
bool highlightMatches = true;
|
2010-05-18 18:24:00 +02:00
|
|
|
};
|
2009-08-11 14:39:44 +02:00
|
|
|
|
2016-06-03 13:00:51 +02:00
|
|
|
static QString replaceTildeWithHome(QString str)
|
|
|
|
|
{
|
|
|
|
|
str.replace("~", QDir::homePath());
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
// If string begins with given prefix remove it with trailing spaces and return true.
|
2016-03-18 12:10:24 +01:00
|
|
|
static bool eatString(const QString &prefix, QString *str)
|
2012-09-01 07:47:25 +02:00
|
|
|
{
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!str->startsWith(prefix))
|
2012-09-01 07:47:25 +02:00
|
|
|
return false;
|
2016-03-18 12:10:24 +01:00
|
|
|
*str = str->mid(prefix.size()).trimmed();
|
2012-09-01 07:47:25 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
static QRegularExpression vimPatternToQtPattern(QString needle, bool ignoreCaseOption, bool smartCaseOption)
|
2012-07-22 14:30:56 +02:00
|
|
|
{
|
2020-06-22 16:49:25 +02:00
|
|
|
/* Transformations (Vim regexp -> QRegularExpression):
|
2012-07-22 14:30:56 +02:00
|
|
|
* \a -> [A-Za-z]
|
|
|
|
|
* \A -> [^A-Za-z]
|
|
|
|
|
* \h -> [A-Za-z_]
|
|
|
|
|
* \H -> [^A-Za-z_]
|
|
|
|
|
* \l -> [a-z]
|
|
|
|
|
* \L -> [^a-z]
|
|
|
|
|
* \o -> [0-7]
|
|
|
|
|
* \O -> [^0-7]
|
|
|
|
|
* \u -> [A-Z]
|
|
|
|
|
* \U -> [^A-Z]
|
|
|
|
|
* \x -> [0-9A-Fa-f]
|
|
|
|
|
* \X -> [^0-9A-Fa-f]
|
|
|
|
|
*
|
|
|
|
|
* \< -> \b
|
|
|
|
|
* \> -> \b
|
|
|
|
|
* [] -> \[\]
|
|
|
|
|
* \= -> ?
|
|
|
|
|
*
|
|
|
|
|
* (...) <-> \(...\)
|
|
|
|
|
* {...} <-> \{...\}
|
|
|
|
|
* | <-> \|
|
|
|
|
|
* ? <-> \?
|
|
|
|
|
* + <-> \+
|
|
|
|
|
* \{...} -> {...}
|
|
|
|
|
*
|
|
|
|
|
* \c - set ignorecase for rest
|
|
|
|
|
* \C - set noignorecase for rest
|
|
|
|
|
*/
|
2013-03-03 16:33:47 +01:00
|
|
|
// FIXME: Option smartcase should be used only if search was typed by user.
|
|
|
|
|
bool ignorecase = ignoreCaseOption
|
2020-06-22 15:40:34 +02:00
|
|
|
&& !(smartCaseOption && needle.contains(QRegularExpression("[A-Z]")));
|
2012-07-22 14:30:56 +02:00
|
|
|
QString pattern;
|
|
|
|
|
pattern.reserve(2 * needle.size());
|
|
|
|
|
|
|
|
|
|
bool escape = false;
|
|
|
|
|
bool brace = false;
|
2012-11-17 21:39:23 +01:00
|
|
|
bool embraced = false;
|
|
|
|
|
bool range = false;
|
2012-07-22 14:30:56 +02:00
|
|
|
bool curly = false;
|
|
|
|
|
foreach (const QChar &c, needle) {
|
|
|
|
|
if (brace) {
|
|
|
|
|
brace = false;
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == ']') {
|
|
|
|
|
pattern.append("\\[\\]");
|
2012-07-22 14:30:56 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2016-03-18 12:10:24 +01:00
|
|
|
pattern.append('[');
|
2012-11-17 21:39:23 +01:00
|
|
|
escape = true;
|
|
|
|
|
embraced = true;
|
2012-07-22 14:30:56 +02:00
|
|
|
}
|
2012-11-17 21:39:23 +01:00
|
|
|
if (embraced) {
|
|
|
|
|
if (range) {
|
|
|
|
|
QChar c2 = pattern[pattern.size() - 2];
|
|
|
|
|
pattern.remove(pattern.size() - 2, 2);
|
2016-03-18 12:10:24 +01:00
|
|
|
pattern.append(c2.toUpper() + '-' + c.toUpper());
|
|
|
|
|
pattern.append(c2.toLower() + '-' + c.toLower());
|
2012-11-17 21:39:23 +01:00
|
|
|
range = false;
|
|
|
|
|
} else if (escape) {
|
|
|
|
|
escape = false;
|
|
|
|
|
pattern.append(c);
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '\\') {
|
2012-11-17 21:39:23 +01:00
|
|
|
escape = true;
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == ']') {
|
|
|
|
|
pattern.append(']');
|
2012-11-17 21:39:23 +01:00
|
|
|
embraced = false;
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '-') {
|
2012-11-17 21:39:23 +01:00
|
|
|
range = ignorecase && pattern[pattern.size() - 1].isLetter();
|
2016-03-18 12:10:24 +01:00
|
|
|
pattern.append('-');
|
2012-11-17 21:39:23 +01:00
|
|
|
} else if (c.isLetter() && ignorecase) {
|
2012-11-20 20:05:51 +02:00
|
|
|
pattern.append(c.toLower()).append(c.toUpper());
|
2012-11-17 21:39:23 +01:00
|
|
|
} else {
|
|
|
|
|
pattern.append(c);
|
|
|
|
|
}
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (QString("(){}+|?").indexOf(c) != -1) {
|
|
|
|
|
if (c == '{') {
|
2012-07-22 14:30:56 +02:00
|
|
|
curly = escape;
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '}' && curly) {
|
2012-07-22 14:30:56 +02:00
|
|
|
curly = false;
|
|
|
|
|
escape = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (escape)
|
|
|
|
|
escape = false;
|
|
|
|
|
else
|
2016-03-18 12:10:24 +01:00
|
|
|
pattern.append('\\');
|
2012-07-22 14:30:56 +02:00
|
|
|
pattern.append(c);
|
|
|
|
|
} else if (escape) {
|
|
|
|
|
// escape expression
|
|
|
|
|
escape = false;
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '<' || c == '>')
|
|
|
|
|
pattern.append("\\b");
|
|
|
|
|
else if (c == 'a')
|
|
|
|
|
pattern.append("[a-zA-Z]");
|
|
|
|
|
else if (c == 'A')
|
|
|
|
|
pattern.append("[^a-zA-Z]");
|
|
|
|
|
else if (c == 'h')
|
|
|
|
|
pattern.append("[A-Za-z_]");
|
|
|
|
|
else if (c == 'H')
|
|
|
|
|
pattern.append("[^A-Za-z_]");
|
|
|
|
|
else if (c == 'c' || c == 'C')
|
|
|
|
|
ignorecase = (c == 'c');
|
|
|
|
|
else if (c == 'l')
|
|
|
|
|
pattern.append("[a-z]");
|
|
|
|
|
else if (c == 'L')
|
|
|
|
|
pattern.append("[^a-z]");
|
|
|
|
|
else if (c == 'o')
|
|
|
|
|
pattern.append("[0-7]");
|
|
|
|
|
else if (c == 'O')
|
|
|
|
|
pattern.append("[^0-7]");
|
|
|
|
|
else if (c == 'u')
|
|
|
|
|
pattern.append("[A-Z]");
|
|
|
|
|
else if (c == 'U')
|
|
|
|
|
pattern.append("[^A-Z]");
|
|
|
|
|
else if (c == 'x')
|
|
|
|
|
pattern.append("[0-9A-Fa-f]");
|
|
|
|
|
else if (c == 'X')
|
|
|
|
|
pattern.append("[^0-9A-Fa-f]");
|
|
|
|
|
else if (c == '=')
|
|
|
|
|
pattern.append("?");
|
|
|
|
|
else {
|
|
|
|
|
pattern.append('\\');
|
|
|
|
|
pattern.append(c);
|
|
|
|
|
}
|
2012-07-22 14:30:56 +02:00
|
|
|
} else {
|
|
|
|
|
// unescaped expression
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '\\')
|
2012-07-22 14:30:56 +02:00
|
|
|
escape = true;
|
2016-03-18 12:10:24 +01:00
|
|
|
else if (c == '[')
|
2012-07-22 14:30:56 +02:00
|
|
|
brace = true;
|
|
|
|
|
else if (c.isLetter() && ignorecase)
|
2016-03-18 12:10:24 +01:00
|
|
|
pattern.append('[').append(c.toLower()).append(c.toUpper()).append(']');
|
2012-07-22 14:30:56 +02:00
|
|
|
else
|
|
|
|
|
pattern.append(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (escape)
|
2016-03-18 12:10:24 +01:00
|
|
|
pattern.append('\\');
|
2012-07-22 14:30:56 +02:00
|
|
|
else if (brace)
|
2016-03-18 12:10:24 +01:00
|
|
|
pattern.append('[');
|
2012-07-22 14:30:56 +02:00
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
return QRegularExpression(pattern);
|
2012-07-22 14:30:56 +02:00
|
|
|
}
|
|
|
|
|
|
2012-11-18 19:58:02 +01:00
|
|
|
static bool afterEndOfLine(const QTextDocument *doc, int position)
|
|
|
|
|
{
|
|
|
|
|
return doc->characterAt(position) == ParagraphSeparator
|
|
|
|
|
&& doc->findBlock(position).length() > 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
static void searchForward(QTextCursor *tc, const QRegularExpression &needleExp, int *repeat)
|
2012-11-18 19:58:02 +01:00
|
|
|
{
|
|
|
|
|
const QTextDocument *doc = tc->document();
|
|
|
|
|
const int startPos = tc->position();
|
|
|
|
|
|
|
|
|
|
// Search from beginning of line so that matched text is the same.
|
|
|
|
|
tc->movePosition(StartOfLine);
|
|
|
|
|
|
|
|
|
|
// forward to current position
|
|
|
|
|
*tc = doc->find(needleExp, *tc);
|
|
|
|
|
while (!tc->isNull() && tc->anchor() < startPos) {
|
|
|
|
|
if (!tc->hasSelection())
|
|
|
|
|
tc->movePosition(Right);
|
|
|
|
|
if (tc->atBlockEnd())
|
2013-02-23 12:45:47 +01:00
|
|
|
tc->movePosition(NextBlock);
|
2012-11-18 19:58:02 +01:00
|
|
|
*tc = doc->find(needleExp, *tc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tc->isNull())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
--*repeat;
|
|
|
|
|
|
|
|
|
|
while (*repeat > 0) {
|
|
|
|
|
if (!tc->hasSelection())
|
|
|
|
|
tc->movePosition(Right);
|
|
|
|
|
if (tc->atBlockEnd())
|
2013-02-23 12:45:47 +01:00
|
|
|
tc->movePosition(NextBlock);
|
2012-11-18 19:58:02 +01:00
|
|
|
*tc = doc->find(needleExp, *tc);
|
|
|
|
|
if (tc->isNull())
|
|
|
|
|
return;
|
|
|
|
|
--*repeat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!tc->isNull() && afterEndOfLine(doc, tc->anchor()))
|
|
|
|
|
tc->movePosition(Left);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
static void searchBackward(QTextCursor *tc, const QRegularExpression &needleExp, int *repeat)
|
2012-11-18 19:58:02 +01:00
|
|
|
{
|
|
|
|
|
// Search from beginning of line so that matched text is the same.
|
|
|
|
|
QTextBlock block = tc->block();
|
|
|
|
|
QString line = block.text();
|
|
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
QRegularExpressionMatch match;
|
|
|
|
|
int i = line.indexOf(needleExp, 0, &match);
|
2012-11-18 19:58:02 +01:00
|
|
|
while (i != -1 && i < tc->positionInBlock()) {
|
|
|
|
|
--*repeat;
|
2020-06-22 16:49:25 +02:00
|
|
|
const int offset = i + qMax(1, match.capturedLength());
|
|
|
|
|
i = line.indexOf(needleExp, offset, &match);
|
2012-11-18 19:58:02 +01:00
|
|
|
if (i == line.size())
|
|
|
|
|
i = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == tc->positionInBlock())
|
|
|
|
|
--*repeat;
|
|
|
|
|
|
|
|
|
|
while (*repeat > 0) {
|
|
|
|
|
block = block.previous();
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
break;
|
|
|
|
|
line = block.text();
|
2020-06-22 16:49:25 +02:00
|
|
|
i = line.indexOf(needleExp, 0, &match);
|
2012-11-18 19:58:02 +01:00
|
|
|
while (i != -1) {
|
|
|
|
|
--*repeat;
|
2020-06-22 16:49:25 +02:00
|
|
|
const int offset = i + qMax(1, match.capturedLength());
|
|
|
|
|
i = line.indexOf(needleExp, offset, &match);
|
2012-11-18 19:58:02 +01:00
|
|
|
if (i == line.size())
|
|
|
|
|
i = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!block.isValid()) {
|
|
|
|
|
*tc = QTextCursor();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
i = line.indexOf(needleExp, 0, &match);
|
2012-11-18 19:58:02 +01:00
|
|
|
while (*repeat < 0) {
|
2020-06-22 16:49:25 +02:00
|
|
|
const int offset = i + qMax(1, match.capturedLength());
|
|
|
|
|
i = line.indexOf(needleExp, offset, &match);
|
2012-11-18 19:58:02 +01:00
|
|
|
++*repeat;
|
|
|
|
|
}
|
|
|
|
|
tc->setPosition(block.position() + i);
|
2020-06-22 16:49:25 +02:00
|
|
|
tc->setPosition(tc->position() + match.capturedLength(), KeepAnchor);
|
2012-11-18 19:58:02 +01:00
|
|
|
}
|
|
|
|
|
|
2013-08-05 19:14:18 +02:00
|
|
|
// Commands [[, []
|
|
|
|
|
static void bracketSearchBackward(QTextCursor *tc, const QString &needleExp, int repeat)
|
|
|
|
|
{
|
2020-06-22 16:49:25 +02:00
|
|
|
const QRegularExpression re(needleExp);
|
2013-08-05 19:14:18 +02:00
|
|
|
QTextCursor tc2 = *tc;
|
|
|
|
|
tc2.setPosition(tc2.position() - 1);
|
|
|
|
|
searchBackward(&tc2, re, &repeat);
|
|
|
|
|
if (repeat <= 1)
|
|
|
|
|
tc->setPosition(tc2.isNull() ? 0 : tc2.position(), KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Commands ][, ]]
|
|
|
|
|
// When ]] is used after an operator, then also stops below a '}' in the first column.
|
|
|
|
|
static void bracketSearchForward(QTextCursor *tc, const QString &needleExp, int repeat,
|
|
|
|
|
bool searchWithCommand)
|
|
|
|
|
{
|
2020-06-22 16:49:25 +02:00
|
|
|
QRegularExpression re(searchWithCommand ? QString("^\\}|^\\{") : needleExp);
|
2013-08-05 19:14:18 +02:00
|
|
|
QTextCursor tc2 = *tc;
|
|
|
|
|
tc2.setPosition(tc2.position() + 1);
|
|
|
|
|
searchForward(&tc2, re, &repeat);
|
|
|
|
|
if (repeat <= 1) {
|
|
|
|
|
if (tc2.isNull()) {
|
|
|
|
|
tc->setPosition(tc->document()->characterCount() - 1, KeepAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
tc->setPosition(tc2.position() - 1, KeepAnchor);
|
|
|
|
|
if (searchWithCommand && tc->document()->characterAt(tc->position()).unicode() == '}') {
|
|
|
|
|
QTextBlock block = tc->block().next();
|
|
|
|
|
if (block.isValid())
|
|
|
|
|
tc->setPosition(block.position(), KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-15 15:15:30 +01:00
|
|
|
static char backslashed(char t)
|
|
|
|
|
{
|
|
|
|
|
switch (t) {
|
|
|
|
|
case 'e': return 27;
|
|
|
|
|
case 't': return '\t';
|
|
|
|
|
case 'r': return '\r';
|
|
|
|
|
case 'n': return '\n';
|
|
|
|
|
case 'b': return 8;
|
|
|
|
|
}
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
static bool substituteText(QString *text,
|
|
|
|
|
const QRegularExpression &pattern,
|
|
|
|
|
const QString &replacement,
|
|
|
|
|
bool global)
|
2012-10-21 17:30:16 +02:00
|
|
|
{
|
|
|
|
|
bool substituted = false;
|
|
|
|
|
int pos = 0;
|
2013-12-01 15:09:04 +01:00
|
|
|
int right = -1;
|
2012-10-21 17:30:16 +02:00
|
|
|
while (true) {
|
2020-06-22 16:49:25 +02:00
|
|
|
const QRegularExpressionMatch match = pattern.match(*text, pos);
|
|
|
|
|
if (!match.hasMatch())
|
2012-10-21 17:30:16 +02:00
|
|
|
break;
|
2013-12-01 15:09:04 +01:00
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
pos = match.capturedStart();
|
|
|
|
|
|
2013-12-01 15:09:04 +01:00
|
|
|
// ensure that substitution is advancing towards end of line
|
|
|
|
|
if (right == text->size() - pos) {
|
|
|
|
|
++pos;
|
|
|
|
|
if (pos == text->size())
|
|
|
|
|
break;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
right = text->size() - pos;
|
|
|
|
|
|
2012-10-21 17:30:16 +02:00
|
|
|
substituted = true;
|
2020-06-22 16:49:25 +02:00
|
|
|
QString matched = text->mid(pos, match.captured(0).size());
|
2012-10-21 17:30:16 +02:00
|
|
|
QString repl;
|
|
|
|
|
bool escape = false;
|
|
|
|
|
// insert captured texts
|
|
|
|
|
for (int i = 0; i < replacement.size(); ++i) {
|
|
|
|
|
const QChar &c = replacement[i];
|
|
|
|
|
if (escape) {
|
|
|
|
|
escape = false;
|
|
|
|
|
if (c.isDigit()) {
|
2020-06-22 16:49:25 +02:00
|
|
|
if (c.digitValue() <= match.lastCapturedIndex())
|
|
|
|
|
repl += match.captured(c.digitValue());
|
2012-10-21 17:30:16 +02:00
|
|
|
} else {
|
2016-03-18 12:10:24 +01:00
|
|
|
repl += backslashed(c.unicode());
|
2012-10-21 17:30:16 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '\\')
|
2012-10-21 17:30:16 +02:00
|
|
|
escape = true;
|
2016-03-18 12:10:24 +01:00
|
|
|
else if (c == '&')
|
2020-06-22 16:49:25 +02:00
|
|
|
repl += match.captured(0);
|
2012-10-21 17:30:16 +02:00
|
|
|
else
|
|
|
|
|
repl += c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
text->replace(pos, matched.size(), repl);
|
2013-12-01 15:09:04 +01:00
|
|
|
pos += (repl.isEmpty() && matched.isEmpty()) ? 1 : repl.size();
|
2012-10-21 17:30:16 +02:00
|
|
|
|
|
|
|
|
if (pos >= text->size() || !global)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return substituted;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
static int findUnescaped(QChar c, const QString &line, int from)
|
|
|
|
|
{
|
|
|
|
|
for (int i = from; i < line.size(); ++i) {
|
2016-03-18 12:10:24 +01:00
|
|
|
if (line.at(i) == c && (i == 0 || line.at(i - 1) != '\\'))
|
2012-10-26 18:34:58 +02:00
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-09 17:32:39 +02:00
|
|
|
static void setClipboardData(const QString &content, RangeMode mode,
|
|
|
|
|
QClipboard::Mode clipboardMode)
|
|
|
|
|
{
|
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
|
char vimRangeMode = mode;
|
|
|
|
|
|
|
|
|
|
QByteArray bytes1;
|
|
|
|
|
bytes1.append(vimRangeMode);
|
|
|
|
|
bytes1.append(content.toUtf8());
|
|
|
|
|
|
|
|
|
|
QByteArray bytes2;
|
|
|
|
|
bytes2.append(vimRangeMode);
|
|
|
|
|
bytes2.append("utf-8");
|
|
|
|
|
bytes2.append('\0');
|
|
|
|
|
bytes2.append(content.toUtf8());
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
auto data = new QMimeData;
|
2012-08-09 17:32:39 +02:00
|
|
|
data->setText(content);
|
|
|
|
|
data->setData(vimMimeText, bytes1);
|
|
|
|
|
data->setData(vimMimeTextEncoded, bytes2);
|
|
|
|
|
clipboard->setMimeData(data, clipboardMode);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-20 19:37:49 +01:00
|
|
|
static QByteArray toLocalEncoding(const QString &text)
|
|
|
|
|
{
|
2018-04-11 12:03:04 +02:00
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
|
return QString(text).replace("\n", "\r\n").toLocal8Bit();
|
|
|
|
|
#else
|
|
|
|
|
return text.toLocal8Bit();
|
|
|
|
|
#endif
|
2013-11-20 19:37:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString fromLocalEncoding(const QByteArray &data)
|
|
|
|
|
{
|
2018-04-11 12:03:04 +02:00
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
|
return QString::fromLocal8Bit(data).replace("\n", "\r\n");
|
|
|
|
|
#else
|
|
|
|
|
return QString::fromLocal8Bit(data);
|
|
|
|
|
#endif
|
2013-11-20 19:37:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString getProcessOutput(const QString &command, const QString &input)
|
|
|
|
|
{
|
|
|
|
|
QProcess proc;
|
2020-04-21 10:46:21 -07:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
|
|
|
|
|
QStringList arguments = QProcess::splitCommand(command);
|
|
|
|
|
QString executable = arguments.takeFirst();
|
|
|
|
|
proc.start(executable, arguments);
|
|
|
|
|
#else
|
2013-11-20 19:37:49 +01:00
|
|
|
proc.start(command);
|
2020-04-21 10:46:21 -07:00
|
|
|
#endif
|
2013-11-20 19:37:49 +01:00
|
|
|
proc.waitForStarted();
|
|
|
|
|
proc.write(toLocalEncoding(input));
|
|
|
|
|
proc.closeWriteChannel();
|
|
|
|
|
|
|
|
|
|
// FIXME: Process should be interruptable by user.
|
|
|
|
|
// Solution is to create a QObject for each process and emit finished state.
|
|
|
|
|
proc.waitForFinished();
|
|
|
|
|
|
|
|
|
|
return fromLocalEncoding(proc.readAllStandardOutput());
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-09 17:42:52 +01:00
|
|
|
static const QMap<QString, int> &vimKeyNames()
|
|
|
|
|
{
|
2015-05-08 14:41:01 +02:00
|
|
|
static const QMap<QString, int> k = {
|
|
|
|
|
// FIXME: Should be value of mapleader.
|
2017-02-22 15:09:35 +01:00
|
|
|
{"LEADER", Key_Backslash},
|
|
|
|
|
|
|
|
|
|
{"SPACE", Key_Space},
|
|
|
|
|
{"TAB", Key_Tab},
|
|
|
|
|
{"NL", Key_Return},
|
|
|
|
|
{"NEWLINE", Key_Return},
|
|
|
|
|
{"LINEFEED", Key_Return},
|
|
|
|
|
{"LF", Key_Return},
|
|
|
|
|
{"CR", Key_Return},
|
|
|
|
|
{"RETURN", Key_Return},
|
|
|
|
|
{"ENTER", Key_Return},
|
|
|
|
|
{"BS", Key_Backspace},
|
|
|
|
|
{"BACKSPACE", Key_Backspace},
|
|
|
|
|
{"ESC", Key_Escape},
|
|
|
|
|
{"BAR", Key_Bar},
|
|
|
|
|
{"BSLASH", Key_Backslash},
|
|
|
|
|
{"DEL", Key_Delete},
|
|
|
|
|
{"DELETE", Key_Delete},
|
|
|
|
|
{"KDEL", Key_Delete},
|
|
|
|
|
{"UP", Key_Up},
|
|
|
|
|
{"DOWN", Key_Down},
|
|
|
|
|
{"LEFT", Key_Left},
|
|
|
|
|
{"RIGHT", Key_Right},
|
|
|
|
|
|
|
|
|
|
{"LT", Key_Less},
|
|
|
|
|
{"GT", Key_Greater},
|
|
|
|
|
|
|
|
|
|
{"F1", Key_F1},
|
|
|
|
|
{"F2", Key_F2},
|
|
|
|
|
{"F3", Key_F3},
|
|
|
|
|
{"F4", Key_F4},
|
|
|
|
|
{"F5", Key_F5},
|
|
|
|
|
{"F6", Key_F6},
|
|
|
|
|
{"F7", Key_F7},
|
|
|
|
|
{"F8", Key_F8},
|
|
|
|
|
{"F9", Key_F9},
|
|
|
|
|
{"F10", Key_F10},
|
|
|
|
|
|
|
|
|
|
{"F11", Key_F11},
|
|
|
|
|
{"F12", Key_F12},
|
|
|
|
|
{"F13", Key_F13},
|
|
|
|
|
{"F14", Key_F14},
|
|
|
|
|
{"F15", Key_F15},
|
|
|
|
|
{"F16", Key_F16},
|
|
|
|
|
{"F17", Key_F17},
|
|
|
|
|
{"F18", Key_F18},
|
|
|
|
|
{"F19", Key_F19},
|
|
|
|
|
{"F20", Key_F20},
|
|
|
|
|
|
|
|
|
|
{"F21", Key_F21},
|
|
|
|
|
{"F22", Key_F22},
|
|
|
|
|
{"F23", Key_F23},
|
|
|
|
|
{"F24", Key_F24},
|
|
|
|
|
{"F25", Key_F25},
|
|
|
|
|
{"F26", Key_F26},
|
|
|
|
|
{"F27", Key_F27},
|
|
|
|
|
{"F28", Key_F28},
|
|
|
|
|
{"F29", Key_F29},
|
|
|
|
|
{"F30", Key_F30},
|
|
|
|
|
|
|
|
|
|
{"F31", Key_F31},
|
|
|
|
|
{"F32", Key_F32},
|
|
|
|
|
{"F33", Key_F33},
|
|
|
|
|
{"F34", Key_F34},
|
|
|
|
|
{"F35", Key_F35},
|
|
|
|
|
|
|
|
|
|
{"INSERT", Key_Insert},
|
|
|
|
|
{"INS", Key_Insert},
|
|
|
|
|
{"KINSERT", Key_Insert},
|
|
|
|
|
{"HOME", Key_Home},
|
|
|
|
|
{"END", Key_End},
|
|
|
|
|
{"PAGEUP", Key_PageUp},
|
|
|
|
|
{"PAGEDOWN", Key_PageDown},
|
|
|
|
|
|
|
|
|
|
{"KPLUS", Key_Plus},
|
|
|
|
|
{"KMINUS", Key_Minus},
|
|
|
|
|
{"KDIVIDE", Key_Slash},
|
|
|
|
|
{"KMULTIPLY", Key_Asterisk},
|
|
|
|
|
{"KENTER", Key_Enter},
|
|
|
|
|
{"KPOINT", Key_Period},
|
|
|
|
|
|
|
|
|
|
{"CAPS", Key_CapsLock},
|
|
|
|
|
{"NUM", Key_NumLock},
|
|
|
|
|
{"SCROLL", Key_ScrollLock},
|
|
|
|
|
{"ALTGR", Key_AltGr}
|
2015-05-08 14:41:01 +02:00
|
|
|
};
|
2012-11-09 17:42:52 +01:00
|
|
|
|
|
|
|
|
return k;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-22 06:16:01 +02:00
|
|
|
static bool isOnlyControlModifier(const Qt::KeyboardModifiers &mods)
|
2013-11-20 19:21:04 +01:00
|
|
|
{
|
2018-04-11 12:03:04 +02:00
|
|
|
return (mods ^ ControlModifier) == Qt::NoModifier;
|
2013-11-20 19:21:04 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-07 13:39:07 +02:00
|
|
|
static bool hasControlModifier(const Qt::KeyboardModifiers &mods)
|
|
|
|
|
{
|
|
|
|
|
return mods.testFlag(ControlModifier);
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-11 14:39:44 +02:00
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
Range::Range(int b, int e, RangeMode m)
|
|
|
|
|
: beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
|
|
|
|
|
{}
|
2009-08-11 14:39:44 +02:00
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
QString Range::toString() const
|
2010-05-12 11:18:18 +02:00
|
|
|
{
|
2016-03-18 12:10:24 +01:00
|
|
|
return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos).arg(rangemode);
|
2010-05-18 14:48:12 +02:00
|
|
|
}
|
|
|
|
|
|
2013-11-20 19:37:49 +01:00
|
|
|
bool Range::isValid() const
|
|
|
|
|
{
|
|
|
|
|
return beginPos >= 0 && endPos >= 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
QDebug operator<<(QDebug ts, const Range &range)
|
|
|
|
|
{
|
|
|
|
|
return ts << '[' << range.beginPos << ',' << range.endPos << ']';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
|
2018-07-12 00:49:23 +02:00
|
|
|
: cmd(c), args(a), range(r)
|
2010-05-18 14:48:12 +02:00
|
|
|
{}
|
2010-05-12 11:18:18 +02:00
|
|
|
|
2010-07-14 18:15:17 +02:00
|
|
|
bool ExCommand::matches(const QString &min, const QString &full) const
|
|
|
|
|
{
|
|
|
|
|
return cmd.startsWith(min) && full.startsWith(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
QDebug operator<<(QDebug ts, const ExCommand &cmd)
|
|
|
|
|
{
|
2010-05-18 14:48:12 +02:00
|
|
|
return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
|
2010-05-12 11:18:18 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-07 19:46:16 +02:00
|
|
|
QDebug operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
|
2009-01-23 16:37:32 +01:00
|
|
|
{
|
2010-02-01 14:00:07 +01:00
|
|
|
foreach (const QTextEdit::ExtraSelection &sel, sels)
|
2009-04-01 15:14:10 +02:00
|
|
|
ts << "SEL: " << sel.cursor.anchor() << sel.cursor.position();
|
2009-01-23 16:37:32 +01:00
|
|
|
return ts;
|
|
|
|
|
}
|
2009-04-01 15:14:10 +02:00
|
|
|
|
2009-06-11 17:09:05 +02:00
|
|
|
QString quoteUnprintable(const QString &ba)
|
2009-01-16 16:15:01 +01:00
|
|
|
{
|
2009-06-11 17:09:05 +02:00
|
|
|
QString res;
|
|
|
|
|
for (int i = 0, n = ba.size(); i != n; ++i) {
|
2010-05-04 17:58:53 +02:00
|
|
|
const QChar c = ba.at(i);
|
|
|
|
|
const int cc = c.unicode();
|
2009-06-11 17:09:05 +02:00
|
|
|
if (c.isPrint())
|
|
|
|
|
res += c;
|
2016-03-18 12:10:24 +01:00
|
|
|
else if (cc == '\n')
|
|
|
|
|
res += "<CR>";
|
2009-06-11 17:09:05 +02:00
|
|
|
else
|
2016-03-18 12:10:24 +01:00
|
|
|
res += QString("\\x%1").arg(c.unicode(), 2, 16, QLatin1Char('0'));
|
2009-06-11 17:09:05 +02:00
|
|
|
}
|
|
|
|
|
return res;
|
2009-01-16 16:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
2010-02-15 17:55:26 +01:00
|
|
|
static bool startsWithWhitespace(const QString &str, int col)
|
|
|
|
|
{
|
2018-04-11 12:03:04 +02:00
|
|
|
if (col > str.size()) {
|
|
|
|
|
qWarning("Wrong column");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2010-02-15 17:55:26 +01:00
|
|
|
for (int i = 0; i < col; ++i) {
|
|
|
|
|
uint u = str.at(i).unicode();
|
2016-03-18 12:10:24 +01:00
|
|
|
if (u != ' ' && u != '\t')
|
2010-02-15 17:55:26 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-07 19:46:16 +02:00
|
|
|
inline QString msgMarkNotSet(const QString &text)
|
2009-10-07 09:25:19 +02:00
|
|
|
{
|
2014-08-25 17:05:11 +02:00
|
|
|
return Tr::tr("Mark \"%1\" not set.").arg(text);
|
2009-10-07 09:25:19 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-24 23:09:11 +03:00
|
|
|
static void initSingleShotTimer(QTimer *timer, int interval, FakeVimHandler::Private *receiver,
|
|
|
|
|
void (FakeVimHandler::Private::*slot)())
|
2014-11-16 10:53:15 +01:00
|
|
|
{
|
|
|
|
|
timer->setSingleShot(true);
|
|
|
|
|
timer->setInterval(interval);
|
2016-05-24 23:09:11 +03:00
|
|
|
QObject::connect(timer, &QTimer::timeout, receiver, slot);
|
2014-11-16 10:53:15 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-26 13:22:06 +01:00
|
|
|
class Input
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2010-03-26 13:22:06 +01:00
|
|
|
public:
|
2010-11-02 12:37:36 +01:00
|
|
|
// Remove some extra "information" on Mac.
|
2013-09-26 22:15:48 +02:00
|
|
|
static Qt::KeyboardModifiers cleanModifier(Qt::KeyboardModifiers m)
|
|
|
|
|
{
|
|
|
|
|
return m & ~Qt::KeypadModifier;
|
|
|
|
|
}
|
2010-11-02 12:37:36 +01:00
|
|
|
|
2018-11-07 23:45:39 +01:00
|
|
|
Input() = default;
|
2010-04-28 14:00:44 +02:00
|
|
|
explicit Input(QChar x)
|
2015-06-18 13:32:25 +02:00
|
|
|
: m_key(x.unicode()), m_xkey(x.unicode()), m_text(x)
|
2012-09-01 07:47:25 +02:00
|
|
|
{
|
|
|
|
|
if (x.isUpper())
|
|
|
|
|
m_modifiers = Qt::ShiftModifier;
|
2012-09-13 18:44:36 +02:00
|
|
|
else if (x.isLower())
|
|
|
|
|
m_key = x.toUpper().unicode();
|
2012-09-01 07:47:25 +02:00
|
|
|
}
|
2010-03-26 13:22:06 +01:00
|
|
|
|
2013-09-26 22:15:48 +02:00
|
|
|
Input(int k, Qt::KeyboardModifiers m, const QString &t = QString())
|
2010-11-02 12:37:36 +01:00
|
|
|
: m_key(k), m_modifiers(cleanModifier(m)), m_text(t)
|
2010-04-28 14:00:44 +02:00
|
|
|
{
|
2013-02-23 11:02:43 +01:00
|
|
|
if (m_text.size() == 1) {
|
|
|
|
|
QChar x = m_text.at(0);
|
|
|
|
|
|
|
|
|
|
// On Mac, QKeyEvent::text() returns non-empty strings for
|
|
|
|
|
// cursor keys. This breaks some of the logic later on
|
|
|
|
|
// relying on text() being empty for "special" keys.
|
|
|
|
|
// FIXME: Check the real conditions.
|
2015-12-16 14:10:32 +01:00
|
|
|
if (x.unicode() < ' ' && x.unicode() != 27)
|
2013-02-23 11:02:43 +01:00
|
|
|
m_text.clear();
|
2013-02-28 15:52:14 +01:00
|
|
|
else if (x.isLetter())
|
2013-02-23 11:02:43 +01:00
|
|
|
m_key = x.toUpper().unicode();
|
|
|
|
|
}
|
2012-10-29 19:51:38 +01:00
|
|
|
|
|
|
|
|
// Set text only if input is ascii key without control modifier.
|
2018-04-11 12:03:04 +02:00
|
|
|
if (m_text.isEmpty() && k >= 0 && k <= 0x7f && (m & ControlModifier) == 0) {
|
2016-03-18 12:10:24 +01:00
|
|
|
QChar c = QChar(k);
|
2014-02-09 13:20:27 +01:00
|
|
|
if (c.isLetter())
|
2016-03-18 12:10:24 +01:00
|
|
|
m_text = isShift() ? c.toUpper() : c;
|
2014-02-09 13:20:27 +01:00
|
|
|
else if (!isShift())
|
|
|
|
|
m_text = c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Normalize <S-TAB>.
|
|
|
|
|
if (m_key == Qt::Key_Backtab) {
|
|
|
|
|
m_key = Qt::Key_Tab;
|
|
|
|
|
m_modifiers |= Qt::ShiftModifier;
|
2012-10-29 19:51:38 +01:00
|
|
|
}
|
|
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
// m_xkey is only a cache.
|
|
|
|
|
m_xkey = (m_text.size() == 1 ? m_text.at(0).unicode() : m_key);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
bool isValid() const
|
|
|
|
|
{
|
|
|
|
|
return m_key != 0 || !m_text.isNull();
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
bool isDigit() const
|
|
|
|
|
{
|
2013-03-17 12:39:57 +01:00
|
|
|
return m_xkey >= '0' && m_xkey <= '9';
|
2010-04-28 14:00:44 +02:00
|
|
|
}
|
2010-05-10 16:41:35 +02:00
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
bool isKey(int c) const
|
|
|
|
|
{
|
|
|
|
|
return !m_modifiers && m_key == c;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-10 16:41:35 +02:00
|
|
|
bool isBackspace() const
|
|
|
|
|
{
|
|
|
|
|
return m_key == Key_Backspace || isControl('h');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isReturn() const
|
|
|
|
|
{
|
2016-03-18 12:10:24 +01:00
|
|
|
return m_key == '\n' || m_key == Key_Return || m_key == Key_Enter;
|
2010-05-10 16:41:35 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-18 18:24:00 +02:00
|
|
|
bool isEscape() const
|
|
|
|
|
{
|
2016-09-30 19:43:50 +02:00
|
|
|
return isKey(Key_Escape) || isShift(Key_Escape) || isKey(27) || isShift(27) || isControl('c')
|
2010-05-18 18:24:00 +02:00
|
|
|
|| isControl(Key_BracketLeft);
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
bool is(int c) const
|
|
|
|
|
{
|
2020-05-07 13:39:07 +02:00
|
|
|
return m_xkey == c && !hasControlModifier(m_modifiers);
|
2010-04-28 14:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
2013-03-21 19:37:32 +01:00
|
|
|
bool isControl() const
|
|
|
|
|
{
|
2013-11-22 06:16:01 +02:00
|
|
|
return isOnlyControlModifier(m_modifiers);
|
2013-03-21 19:37:32 +01:00
|
|
|
}
|
|
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
bool isControl(int c) const
|
|
|
|
|
{
|
2013-03-21 19:37:32 +01:00
|
|
|
return isControl()
|
2010-10-26 10:56:32 +02:00
|
|
|
&& (m_xkey == c || m_xkey + 32 == c || m_xkey + 64 == c || m_xkey + 96 == c);
|
2010-04-28 14:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
2013-03-21 19:37:32 +01:00
|
|
|
bool isShift() const
|
|
|
|
|
{
|
|
|
|
|
return m_modifiers & Qt::ShiftModifier;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
bool isShift(int c) const
|
|
|
|
|
{
|
2013-03-21 19:37:32 +01:00
|
|
|
return isShift() && m_xkey == c;
|
2010-04-28 14:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-29 19:51:38 +01:00
|
|
|
bool operator<(const Input &a) const
|
2010-04-28 14:00:44 +02:00
|
|
|
{
|
2012-10-29 19:51:38 +01:00
|
|
|
if (m_key != a.m_key)
|
|
|
|
|
return m_key < a.m_key;
|
|
|
|
|
// Text for some mapped key cannot be determined (e.g. <C-J>) so if text is not set for
|
|
|
|
|
// one of compared keys ignore it.
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!m_text.isEmpty() && !a.m_text.isEmpty() && m_text != " ")
|
2012-10-29 19:51:38 +01:00
|
|
|
return m_text < a.m_text;
|
|
|
|
|
return m_modifiers < a.m_modifiers;
|
2010-04-28 14:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-29 19:51:38 +01:00
|
|
|
bool operator==(const Input &a) const
|
2011-11-10 16:13:10 +01:00
|
|
|
{
|
2012-10-29 19:51:38 +01:00
|
|
|
return !(*this < a || a < *this);
|
2011-11-10 16:13:10 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-29 19:51:38 +01:00
|
|
|
bool operator!=(const Input &a) const { return !operator==(a); }
|
|
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
QString text() const { return m_text; }
|
|
|
|
|
|
2010-05-11 14:26:37 +02:00
|
|
|
QChar asChar() const
|
|
|
|
|
{
|
|
|
|
|
return (m_text.size() == 1 ? m_text.at(0) : QChar());
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-28 13:13:10 +02:00
|
|
|
int toInt(bool *ok, int base) const
|
|
|
|
|
{
|
|
|
|
|
const int uc = asChar().unicode();
|
|
|
|
|
int res;
|
|
|
|
|
if ('0' <= uc && uc <= '9')
|
|
|
|
|
res = uc -'0';
|
|
|
|
|
else if ('a' <= uc && uc <= 'z')
|
|
|
|
|
res = 10 + uc - 'a';
|
|
|
|
|
else if ('A' <= uc && uc <= 'Z')
|
|
|
|
|
res = 10 + uc - 'A';
|
|
|
|
|
else
|
|
|
|
|
res = base;
|
|
|
|
|
*ok = res < base;
|
|
|
|
|
return *ok ? res : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
int key() const { return m_key; }
|
|
|
|
|
|
2013-09-26 22:15:48 +02:00
|
|
|
Qt::KeyboardModifiers modifiers() const { return m_modifiers; }
|
2013-04-01 12:03:22 +02:00
|
|
|
|
2013-03-17 12:41:18 +01:00
|
|
|
// Return raw character for macro recording or dot command.
|
2010-07-06 09:20:40 +02:00
|
|
|
QChar raw() const
|
|
|
|
|
{
|
|
|
|
|
if (m_key == Key_Tab)
|
2016-03-18 12:10:24 +01:00
|
|
|
return '\t';
|
2010-07-06 09:20:40 +02:00
|
|
|
if (m_key == Key_Return)
|
2016-03-18 12:10:24 +01:00
|
|
|
return '\n';
|
2013-03-17 12:41:18 +01:00
|
|
|
if (m_key == Key_Escape)
|
|
|
|
|
return QChar(27);
|
|
|
|
|
return m_xkey;
|
2010-07-06 09:20:40 +02:00
|
|
|
}
|
|
|
|
|
|
2012-11-09 17:42:52 +01:00
|
|
|
QString toString() const
|
|
|
|
|
{
|
2014-10-26 07:04:34 +01:00
|
|
|
if (!m_text.isEmpty())
|
2016-03-18 12:10:24 +01:00
|
|
|
return QString(m_text).replace("<", "<LT>");
|
2014-10-26 07:04:34 +01:00
|
|
|
|
2012-11-09 17:42:52 +01:00
|
|
|
QString key = vimKeyNames().key(m_key);
|
2013-04-16 21:21:00 +02:00
|
|
|
bool namedKey = !key.isEmpty();
|
2013-03-21 19:37:32 +01:00
|
|
|
|
2013-04-16 21:21:00 +02:00
|
|
|
if (!namedKey) {
|
2013-03-21 19:37:32 +01:00
|
|
|
if (m_xkey == '<')
|
2016-03-18 12:10:24 +01:00
|
|
|
key = "<LT>";
|
2013-08-20 14:37:15 +02:00
|
|
|
else if (m_xkey == '>')
|
2016-03-18 12:10:24 +01:00
|
|
|
key = "<GT>";
|
2013-03-21 19:37:32 +01:00
|
|
|
else
|
|
|
|
|
key = QChar(m_xkey);
|
2013-04-16 21:21:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool shift = isShift();
|
|
|
|
|
bool ctrl = isControl();
|
|
|
|
|
if (shift)
|
2016-03-18 12:10:24 +01:00
|
|
|
key.prepend("S-");
|
2013-04-16 21:21:00 +02:00
|
|
|
if (ctrl)
|
2016-03-18 12:10:24 +01:00
|
|
|
key.prepend("C-");
|
2013-04-16 21:21:00 +02:00
|
|
|
|
|
|
|
|
if (namedKey || shift || ctrl) {
|
2016-03-18 12:10:24 +01:00
|
|
|
key.prepend('<');
|
|
|
|
|
key.append('>');
|
2013-03-21 19:37:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return key;
|
2012-11-09 17:42:52 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-07 17:32:46 +02:00
|
|
|
QDebug dump(QDebug ts) const
|
|
|
|
|
{
|
|
|
|
|
return ts << m_key << '-' << m_modifiers << '-'
|
|
|
|
|
<< quoteUnprintable(m_text);
|
|
|
|
|
}
|
2016-03-18 12:10:24 +01:00
|
|
|
|
2010-04-28 14:00:44 +02:00
|
|
|
private:
|
2015-06-18 13:32:25 +02:00
|
|
|
int m_key = 0;
|
|
|
|
|
int m_xkey = 0;
|
|
|
|
|
Qt::KeyboardModifiers m_modifiers = NoModifier;
|
2010-04-28 14:00:44 +02:00
|
|
|
QString m_text;
|
|
|
|
|
};
|
2010-03-26 13:22:06 +01:00
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
// mapping to <Nop> (do nothing)
|
2013-09-26 22:15:48 +02:00
|
|
|
static const Input Nop(-1, Qt::KeyboardModifiers(-1), QString());
|
2012-09-01 07:47:25 +02:00
|
|
|
|
2014-11-09 18:02:05 +01:00
|
|
|
static SubMode letterCaseModeFromInput(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
if (input.is('~'))
|
|
|
|
|
return InvertCaseSubMode;
|
|
|
|
|
if (input.is('u'))
|
|
|
|
|
return DownCaseSubMode;
|
|
|
|
|
if (input.is('U'))
|
|
|
|
|
return UpCaseSubMode;
|
|
|
|
|
|
|
|
|
|
return NoSubMode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SubMode indentModeFromInput(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
if (input.is('<'))
|
|
|
|
|
return ShiftLeftSubMode;
|
|
|
|
|
if (input.is('>'))
|
|
|
|
|
return ShiftRightSubMode;
|
|
|
|
|
if (input.is('='))
|
|
|
|
|
return IndentSubMode;
|
|
|
|
|
|
|
|
|
|
return NoSubMode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SubMode changeDeleteYankModeFromInput(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
if (input.is('c'))
|
|
|
|
|
return ChangeSubMode;
|
|
|
|
|
if (input.is('d'))
|
|
|
|
|
return DeleteSubMode;
|
|
|
|
|
if (input.is('y'))
|
|
|
|
|
return YankSubMode;
|
|
|
|
|
|
|
|
|
|
return NoSubMode;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-16 14:17:11 +01:00
|
|
|
QString dotCommandFromSubMode(SubMode submode)
|
|
|
|
|
{
|
|
|
|
|
if (submode == ChangeSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QLatin1String("c");
|
2014-11-16 14:17:11 +01:00
|
|
|
if (submode == DeleteSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QLatin1String("d");
|
2014-11-16 14:17:11 +01:00
|
|
|
if (submode == InvertCaseSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QLatin1String("g~");
|
2014-11-16 14:17:11 +01:00
|
|
|
if (submode == DownCaseSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QLatin1String("gu");
|
2014-11-16 14:17:11 +01:00
|
|
|
if (submode == UpCaseSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QLatin1String("gU");
|
2014-11-16 14:17:11 +01:00
|
|
|
if (submode == IndentSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QLatin1String("=");
|
2014-11-16 14:17:11 +01:00
|
|
|
if (submode == ShiftRightSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QLatin1String(">");
|
2014-11-16 14:17:11 +01:00
|
|
|
if (submode == ShiftLeftSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QLatin1String("<");
|
2014-11-16 14:17:11 +01:00
|
|
|
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-07 17:32:46 +02:00
|
|
|
QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
|
|
|
|
|
|
|
|
|
|
class Inputs : public QVector<Input>
|
2010-05-04 11:00:40 +02:00
|
|
|
{
|
2010-05-07 17:32:46 +02:00
|
|
|
public:
|
2018-11-07 23:45:39 +01:00
|
|
|
Inputs() = default;
|
2012-09-01 07:47:25 +02:00
|
|
|
|
|
|
|
|
explicit Inputs(const QString &str, bool noremap = true, bool silent = false)
|
|
|
|
|
: m_noremap(noremap), m_silent(silent)
|
|
|
|
|
{
|
|
|
|
|
parseFrom(str);
|
2013-03-17 12:39:57 +01:00
|
|
|
squeeze();
|
2012-09-01 07:47:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool noremap() const { return m_noremap; }
|
|
|
|
|
|
|
|
|
|
bool silent() const { return m_silent; }
|
|
|
|
|
|
|
|
|
|
private:
|
2010-05-07 17:32:46 +02:00
|
|
|
void parseFrom(const QString &str);
|
2012-09-01 07:47:25 +02:00
|
|
|
|
2015-06-18 13:32:25 +02:00
|
|
|
bool m_noremap = true;
|
|
|
|
|
bool m_silent = false;
|
2010-05-07 17:32:46 +02:00
|
|
|
};
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
static Input parseVimKeyName(const QString &keyName)
|
|
|
|
|
{
|
|
|
|
|
if (keyName.length() == 1)
|
|
|
|
|
return Input(keyName.at(0));
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
const QStringList keys = keyName.split('-');
|
2012-09-01 07:47:25 +02:00
|
|
|
const int len = keys.length();
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if (len == 1 && keys.at(0).toUpper() == "NOP")
|
2012-09-01 07:47:25 +02:00
|
|
|
return Nop;
|
|
|
|
|
|
2013-09-26 22:15:48 +02:00
|
|
|
Qt::KeyboardModifiers mods = NoModifier;
|
2012-09-01 07:47:25 +02:00
|
|
|
for (int i = 0; i < len - 1; ++i) {
|
|
|
|
|
const QString &key = keys[i].toUpper();
|
2016-03-18 12:10:24 +01:00
|
|
|
if (key == "S")
|
2012-09-01 07:47:25 +02:00
|
|
|
mods |= Qt::ShiftModifier;
|
2016-03-18 12:10:24 +01:00
|
|
|
else if (key == "C")
|
2018-04-11 12:03:04 +02:00
|
|
|
mods |= ControlModifier;
|
2012-09-01 07:47:25 +02:00
|
|
|
else
|
|
|
|
|
return Input();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!keys.isEmpty()) {
|
|
|
|
|
const QString key = keys.last();
|
|
|
|
|
if (key.length() == 1) {
|
|
|
|
|
// simple character
|
|
|
|
|
QChar c = key.at(0).toUpper();
|
2012-10-29 19:51:38 +01:00
|
|
|
return Input(c.unicode(), mods);
|
2012-09-01 07:47:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find key name
|
2012-11-09 17:42:52 +01:00
|
|
|
QMap<QString, int>::ConstIterator it = vimKeyNames().constFind(key.toUpper());
|
|
|
|
|
if (it != vimKeyNames().end())
|
2012-10-29 19:51:38 +01:00
|
|
|
return Input(*it, mods);
|
2012-09-01 07:47:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Input();
|
2011-12-08 22:47:29 +01:00
|
|
|
}
|
2010-05-04 11:00:40 +02:00
|
|
|
|
2010-05-07 17:32:46 +02:00
|
|
|
void Inputs::parseFrom(const QString &str)
|
|
|
|
|
{
|
|
|
|
|
const int n = str.size();
|
|
|
|
|
for (int i = 0; i < n; ++i) {
|
2016-03-18 12:10:24 +01:00
|
|
|
const QChar c = str.at(i);
|
2013-02-18 21:52:44 +01:00
|
|
|
if (c == '<') {
|
2016-03-18 12:10:24 +01:00
|
|
|
int j = str.indexOf('>', i);
|
2012-09-01 07:47:25 +02:00
|
|
|
Input input;
|
2012-10-26 18:34:58 +02:00
|
|
|
if (j != -1) {
|
|
|
|
|
const QString key = str.mid(i+1, j - i - 1);
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!key.contains('<'))
|
2012-10-26 18:34:58 +02:00
|
|
|
input = parseVimKeyName(key);
|
|
|
|
|
}
|
2012-09-01 07:47:25 +02:00
|
|
|
if (input.isValid()) {
|
|
|
|
|
append(input);
|
|
|
|
|
i = j;
|
2010-05-07 17:32:46 +02:00
|
|
|
} else {
|
2013-02-18 21:52:44 +01:00
|
|
|
append(Input(c));
|
2010-05-07 17:32:46 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2013-02-18 21:52:44 +01:00
|
|
|
append(Input(c));
|
2010-05-07 17:32:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-03-26 13:22:06 +01:00
|
|
|
|
2012-09-09 10:32:45 +02:00
|
|
|
class History
|
|
|
|
|
{
|
|
|
|
|
public:
|
2015-06-18 13:32:25 +02:00
|
|
|
History() : m_items(QString()) {}
|
2012-09-09 10:32:45 +02:00
|
|
|
void append(const QString &item);
|
2020-06-15 17:47:04 +02:00
|
|
|
const QString &move(QStringView prefix, int skip);
|
2012-09-09 10:32:45 +02:00
|
|
|
const QString ¤t() const { return m_items[m_index]; }
|
|
|
|
|
const QStringList &items() const { return m_items; }
|
|
|
|
|
void restart() { m_index = m_items.size() - 1; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// Last item is always empty or current search prefix.
|
|
|
|
|
QStringList m_items;
|
2015-06-18 13:32:25 +02:00
|
|
|
int m_index = 0;
|
2012-09-09 10:32:45 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void History::append(const QString &item)
|
|
|
|
|
{
|
|
|
|
|
if (item.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
m_items.pop_back();
|
|
|
|
|
m_items.removeAll(item);
|
|
|
|
|
m_items << item << QString();
|
|
|
|
|
restart();
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-15 17:47:04 +02:00
|
|
|
const QString &History::move(QStringView prefix, int skip)
|
2012-09-09 10:32:45 +02:00
|
|
|
{
|
|
|
|
|
if (!current().startsWith(prefix))
|
|
|
|
|
restart();
|
|
|
|
|
|
|
|
|
|
if (m_items.last() != prefix)
|
|
|
|
|
m_items[m_items.size() - 1] = prefix.toString();
|
|
|
|
|
|
|
|
|
|
int i = m_index + skip;
|
|
|
|
|
if (!prefix.isEmpty())
|
2013-02-07 12:55:32 +01:00
|
|
|
for (; i >= 0 && i < m_items.size() && !m_items[i].startsWith(prefix); i += skip)
|
|
|
|
|
;
|
2012-09-09 10:32:45 +02:00
|
|
|
if (i >= 0 && i < m_items.size())
|
|
|
|
|
m_index = i;
|
|
|
|
|
|
|
|
|
|
return current();
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-17 17:44:05 +02:00
|
|
|
// Command line buffer with prompt (i.e. :, / or ? characters), text contents and cursor position.
|
2011-07-15 13:22:38 +02:00
|
|
|
class CommandBuffer
|
|
|
|
|
{
|
|
|
|
|
public:
|
2012-09-09 10:32:45 +02:00
|
|
|
void setPrompt(const QChar &prompt) { m_prompt = prompt; }
|
2012-10-20 18:48:37 +02:00
|
|
|
void setContents(const QString &s) { m_buffer = s; m_anchor = m_pos = s.size(); }
|
2012-09-09 10:32:45 +02:00
|
|
|
|
2012-12-30 15:19:15 +01:00
|
|
|
void setContents(const QString &s, int pos, int anchor = -1)
|
|
|
|
|
{
|
|
|
|
|
m_buffer = s; m_pos = m_userPos = pos; m_anchor = anchor >= 0 ? anchor : pos;
|
|
|
|
|
}
|
2012-09-10 22:10:23 +02:00
|
|
|
|
2020-06-15 17:47:04 +02:00
|
|
|
QStringView userContents() const { return QStringView{m_buffer}.left(m_userPos); }
|
2012-09-09 10:32:45 +02:00
|
|
|
const QChar &prompt() const { return m_prompt; }
|
|
|
|
|
const QString &contents() const { return m_buffer; }
|
2011-07-15 13:22:38 +02:00
|
|
|
bool isEmpty() const { return m_buffer.isEmpty(); }
|
|
|
|
|
int cursorPos() const { return m_pos; }
|
2012-10-20 18:48:37 +02:00
|
|
|
int anchorPos() const { return m_anchor; }
|
2012-12-30 15:19:15 +01:00
|
|
|
bool hasSelection() const { return m_pos != m_anchor; }
|
2011-07-15 13:22:38 +02:00
|
|
|
|
2012-10-20 18:48:37 +02:00
|
|
|
void insertChar(QChar c) { m_buffer.insert(m_pos++, c); m_anchor = m_userPos = m_pos; }
|
2012-09-09 10:32:45 +02:00
|
|
|
void insertText(const QString &s)
|
|
|
|
|
{
|
2012-10-20 18:48:37 +02:00
|
|
|
m_buffer.insert(m_pos, s); m_anchor = m_userPos = m_pos = m_pos + s.size();
|
2012-09-09 10:32:45 +02:00
|
|
|
}
|
2012-10-20 18:48:37 +02:00
|
|
|
void deleteChar() { if (m_pos) m_buffer.remove(--m_pos, 1); m_anchor = m_userPos = m_pos; }
|
2012-09-09 10:32:45 +02:00
|
|
|
|
|
|
|
|
void moveLeft() { if (m_pos) m_userPos = --m_pos; }
|
|
|
|
|
void moveRight() { if (m_pos < m_buffer.size()) m_userPos = ++m_pos; }
|
|
|
|
|
void moveStart() { m_userPos = m_pos = 0; }
|
|
|
|
|
void moveEnd() { m_userPos = m_pos = m_buffer.size(); }
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
void setHistoryAutoSave(bool autoSave) { m_historyAutoSave = autoSave; }
|
2020-06-15 17:47:04 +02:00
|
|
|
bool userContentsValid() const { return m_userPos >= 0 && m_userPos <= m_buffer.size(); }
|
|
|
|
|
void historyDown() { if (userContentsValid()) setContents(m_history.move(userContents(), 1)); }
|
|
|
|
|
void historyUp() { if (userContentsValid()) setContents(m_history.move(userContents(), -1)); }
|
2012-09-09 10:32:45 +02:00
|
|
|
const QStringList &historyItems() const { return m_history.items(); }
|
|
|
|
|
void historyPush(const QString &item = QString())
|
|
|
|
|
{
|
|
|
|
|
m_history.append(item.isNull() ? contents() : item);
|
|
|
|
|
}
|
2011-07-15 13:22:38 +02:00
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
void clear()
|
|
|
|
|
{
|
|
|
|
|
if (m_historyAutoSave)
|
|
|
|
|
historyPush();
|
|
|
|
|
m_buffer.clear();
|
2012-10-20 18:48:37 +02:00
|
|
|
m_anchor = m_userPos = m_pos = 0;
|
2012-09-01 07:47:25 +02:00
|
|
|
}
|
2011-07-15 13:22:38 +02:00
|
|
|
|
|
|
|
|
QString display() const
|
|
|
|
|
{
|
2012-09-09 10:32:45 +02:00
|
|
|
QString msg(m_prompt);
|
2011-07-15 13:22:38 +02:00
|
|
|
for (int i = 0; i != m_buffer.size(); ++i) {
|
|
|
|
|
const QChar c = m_buffer.at(i);
|
|
|
|
|
if (c.unicode() < 32) {
|
2016-03-18 12:10:24 +01:00
|
|
|
msg += '^';
|
2013-02-18 21:52:44 +01:00
|
|
|
msg += QChar(c.unicode() + 64);
|
2011-07-15 13:22:38 +02:00
|
|
|
} else {
|
|
|
|
|
msg += c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-20 18:48:37 +02:00
|
|
|
void deleteSelected()
|
|
|
|
|
{
|
|
|
|
|
if (m_pos < m_anchor) {
|
|
|
|
|
m_buffer.remove(m_pos, m_anchor - m_pos);
|
|
|
|
|
m_anchor = m_pos;
|
|
|
|
|
} else {
|
|
|
|
|
m_buffer.remove(m_anchor, m_pos - m_anchor);
|
|
|
|
|
m_pos = m_anchor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-15 13:22:38 +02:00
|
|
|
bool handleInput(const Input &input)
|
|
|
|
|
{
|
2012-10-20 18:48:37 +02:00
|
|
|
if (input.isShift(Key_Left)) {
|
2011-07-15 13:22:38 +02:00
|
|
|
moveLeft();
|
2012-10-20 18:48:37 +02:00
|
|
|
} else if (input.isShift(Key_Right)) {
|
|
|
|
|
moveRight();
|
|
|
|
|
} else if (input.isShift(Key_Home)) {
|
|
|
|
|
moveStart();
|
|
|
|
|
} else if (input.isShift(Key_End)) {
|
|
|
|
|
moveEnd();
|
|
|
|
|
} else if (input.isKey(Key_Left)) {
|
|
|
|
|
moveLeft();
|
|
|
|
|
m_anchor = m_pos;
|
2011-07-15 13:22:38 +02:00
|
|
|
} else if (input.isKey(Key_Right)) {
|
|
|
|
|
moveRight();
|
2012-10-20 18:48:37 +02:00
|
|
|
m_anchor = m_pos;
|
2011-07-15 13:22:38 +02:00
|
|
|
} else if (input.isKey(Key_Home)) {
|
|
|
|
|
moveStart();
|
2012-10-20 18:48:37 +02:00
|
|
|
m_anchor = m_pos;
|
2011-07-15 13:22:38 +02:00
|
|
|
} else if (input.isKey(Key_End)) {
|
|
|
|
|
moveEnd();
|
2012-10-20 18:48:37 +02:00
|
|
|
m_anchor = m_pos;
|
|
|
|
|
} else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
|
|
|
|
|
historyUp();
|
|
|
|
|
} else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
|
|
|
|
|
historyDown();
|
2011-07-15 13:22:38 +02:00
|
|
|
} else if (input.isKey(Key_Delete)) {
|
2012-12-30 15:19:15 +01:00
|
|
|
if (hasSelection()) {
|
|
|
|
|
deleteSelected();
|
|
|
|
|
} else {
|
2012-10-20 18:48:37 +02:00
|
|
|
if (m_pos < m_buffer.size())
|
|
|
|
|
m_buffer.remove(m_pos, 1);
|
|
|
|
|
else
|
|
|
|
|
deleteChar();
|
|
|
|
|
}
|
2011-07-15 13:22:38 +02:00
|
|
|
} else if (!input.text().isEmpty()) {
|
2012-12-30 15:19:15 +01:00
|
|
|
if (hasSelection())
|
2012-10-20 18:48:37 +02:00
|
|
|
deleteSelected();
|
2011-07-15 13:22:38 +02:00
|
|
|
insertText(input.text());
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QString m_buffer;
|
2012-09-09 10:32:45 +02:00
|
|
|
QChar m_prompt;
|
|
|
|
|
History m_history;
|
2015-06-18 13:32:25 +02:00
|
|
|
int m_pos = 0;
|
|
|
|
|
int m_anchor = 0;
|
|
|
|
|
int m_userPos = 0; // last position of inserted text (for retrieving history items)
|
|
|
|
|
bool m_historyAutoSave = true; // store items to history on clear()?
|
2011-07-15 13:22:38 +02:00
|
|
|
};
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
// Mappings for a specific mode (trie structure)
|
|
|
|
|
class ModeMapping : public QMap<Input, ModeMapping>
|
2010-03-26 13:22:06 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2012-09-01 07:47:25 +02:00
|
|
|
const Inputs &value() const { return m_value; }
|
|
|
|
|
void setValue(const Inputs &value) { m_value = value; }
|
|
|
|
|
private:
|
|
|
|
|
Inputs m_value;
|
|
|
|
|
};
|
2010-03-26 13:22:06 +01:00
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
// Mappings for all modes
|
2018-11-07 23:45:39 +01:00
|
|
|
using Mappings = QHash<char, ModeMapping>;
|
2010-03-26 13:22:06 +01:00
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
// Iterator for mappings
|
|
|
|
|
class MappingsIterator : public QVector<ModeMapping::Iterator>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MappingsIterator(Mappings *mappings, char mode = -1, const Inputs &inputs = Inputs())
|
|
|
|
|
: m_parent(mappings)
|
2010-03-26 13:22:06 +01:00
|
|
|
{
|
2012-09-01 07:47:25 +02:00
|
|
|
reset(mode);
|
|
|
|
|
walk(inputs);
|
2010-03-26 13:22:06 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
// Reset iterator state. Keep previous mode if 0.
|
|
|
|
|
void reset(char mode = 0)
|
2010-03-26 13:22:06 +01:00
|
|
|
{
|
2012-09-01 07:47:25 +02:00
|
|
|
clear();
|
|
|
|
|
m_lastValid = -1;
|
2013-03-17 12:39:57 +01:00
|
|
|
m_currentInputs.clear();
|
2012-09-01 07:47:25 +02:00
|
|
|
if (mode != 0) {
|
|
|
|
|
m_mode = mode;
|
|
|
|
|
if (mode != -1)
|
|
|
|
|
m_modeMapping = m_parent->find(mode);
|
|
|
|
|
}
|
2010-03-26 13:22:06 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
bool isValid() const { return !empty(); }
|
|
|
|
|
|
|
|
|
|
// Return true if mapping can be extended.
|
|
|
|
|
bool canExtend() const { return isValid() && !last()->empty(); }
|
|
|
|
|
|
|
|
|
|
// Return true if this mapping can be used.
|
|
|
|
|
bool isComplete() const { return m_lastValid != -1; }
|
|
|
|
|
|
|
|
|
|
// Return size of current map.
|
|
|
|
|
int mapLength() const { return m_lastValid + 1; }
|
|
|
|
|
|
|
|
|
|
bool walk(const Input &input)
|
2010-03-26 13:22:06 +01:00
|
|
|
{
|
2013-03-17 12:39:57 +01:00
|
|
|
m_currentInputs.append(input);
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
if (m_modeMapping == m_parent->end())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
ModeMapping::Iterator it;
|
|
|
|
|
if (isValid()) {
|
|
|
|
|
it = last()->find(input);
|
|
|
|
|
if (it == last()->end())
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
it = m_modeMapping->find(input);
|
|
|
|
|
if (it == m_modeMapping->end())
|
|
|
|
|
return false;
|
2010-03-26 13:22:06 +01:00
|
|
|
}
|
2012-09-01 07:47:25 +02:00
|
|
|
|
|
|
|
|
if (!it->value().isEmpty())
|
|
|
|
|
m_lastValid = size();
|
|
|
|
|
append(it);
|
|
|
|
|
|
2010-03-26 13:22:06 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
bool walk(const Inputs &inputs)
|
2010-03-26 13:22:06 +01:00
|
|
|
{
|
2020-05-28 07:46:41 +02:00
|
|
|
for (const Input &input : inputs) {
|
2012-09-01 07:47:25 +02:00
|
|
|
if (!walk(input))
|
2010-03-26 13:22:06 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2012-09-01 07:47:25 +02:00
|
|
|
|
|
|
|
|
// Return current mapped value. Iterator must be valid.
|
|
|
|
|
const Inputs &inputs() const
|
|
|
|
|
{
|
|
|
|
|
return at(m_lastValid)->value();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void remove()
|
|
|
|
|
{
|
|
|
|
|
if (isValid()) {
|
|
|
|
|
if (canExtend()) {
|
|
|
|
|
last()->setValue(Inputs());
|
|
|
|
|
} else {
|
|
|
|
|
if (size() > 1) {
|
|
|
|
|
while (last()->empty()) {
|
|
|
|
|
at(size() - 2)->erase(last());
|
|
|
|
|
pop_back();
|
|
|
|
|
if (size() == 1 || !last()->value().isEmpty())
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (last()->empty() && last()->value().isEmpty())
|
|
|
|
|
m_modeMapping->erase(last());
|
|
|
|
|
} else if (last()->empty() && !last()->value().isEmpty()) {
|
|
|
|
|
m_modeMapping->erase(last());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setInputs(const Inputs &key, const Inputs &inputs, bool unique = false)
|
|
|
|
|
{
|
|
|
|
|
ModeMapping *current = &(*m_parent)[m_mode];
|
2020-05-28 07:46:41 +02:00
|
|
|
for (const Input &input : key)
|
2012-09-01 07:47:25 +02:00
|
|
|
current = &(*current)[input];
|
|
|
|
|
if (!unique || current->value().isEmpty())
|
|
|
|
|
current->setValue(inputs);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-17 12:39:57 +01:00
|
|
|
const Inputs ¤tInputs() const { return m_currentInputs; }
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
private:
|
|
|
|
|
Mappings *m_parent;
|
|
|
|
|
Mappings::Iterator m_modeMapping;
|
2015-06-18 13:32:25 +02:00
|
|
|
int m_lastValid = -1;
|
|
|
|
|
char m_mode = 0;
|
2013-03-17 12:39:57 +01:00
|
|
|
Inputs m_currentInputs;
|
2010-03-26 13:22:06 +01:00
|
|
|
};
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
// state of current mapping
|
|
|
|
|
struct MappingState {
|
2018-11-07 23:45:39 +01:00
|
|
|
MappingState() = default;
|
2013-12-29 09:26:15 +01:00
|
|
|
MappingState(bool noremap, bool silent, bool editBlock)
|
|
|
|
|
: noremap(noremap), silent(silent), editBlock(editBlock) {}
|
2015-06-18 13:32:25 +02:00
|
|
|
bool noremap = false;
|
|
|
|
|
bool silent = false;
|
|
|
|
|
bool editBlock = false;
|
2012-09-01 07:47:25 +02:00
|
|
|
};
|
2010-05-05 15:50:05 +02:00
|
|
|
|
2010-03-26 13:22:06 +01:00
|
|
|
class FakeVimHandler::Private : public QObject
|
|
|
|
|
{
|
2008-12-19 12:20:04 +01:00
|
|
|
public:
|
2009-01-23 15:12:04 +01:00
|
|
|
Private(FakeVimHandler *parent, QWidget *widget);
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2009-03-05 11:06:25 +01:00
|
|
|
EventResult handleEvent(QKeyEvent *ev);
|
|
|
|
|
bool wantsOverride(QKeyEvent *ev);
|
2014-12-19 11:31:44 +01:00
|
|
|
bool parseExCommand(QString *line, ExCommand *cmd);
|
2012-10-26 18:34:58 +02:00
|
|
|
bool parseLineRange(QString *line, ExCommand *cmd);
|
|
|
|
|
int parseLineAddress(QString *cmd);
|
|
|
|
|
void parseRangeCount(const QString &line, Range *range) const;
|
2010-03-18 17:45:15 +01:00
|
|
|
void handleCommand(const QString &cmd); // Sets m_tc + handleExCommand
|
2009-01-06 11:52:05 +01:00
|
|
|
void handleExCommand(const QString &cmd);
|
2010-03-18 17:45:15 +01:00
|
|
|
|
2009-03-30 16:54:25 +02:00
|
|
|
void installEventFilter();
|
2013-03-16 13:32:32 +01:00
|
|
|
void removeEventFilter();
|
2010-01-05 15:30:22 +01:00
|
|
|
void passShortcuts(bool enable);
|
2009-01-23 15:12:04 +01:00
|
|
|
void setupWidget();
|
2010-03-26 17:55:43 +01:00
|
|
|
void restoreWidget(int tabSize);
|
2009-01-23 15:12:04 +01:00
|
|
|
|
2009-01-09 17:31:20 +01:00
|
|
|
friend class FakeVimHandler;
|
2008-12-19 12:20:04 +01:00
|
|
|
|
|
|
|
|
void init();
|
2012-11-10 10:36:05 +01:00
|
|
|
void focus();
|
2014-11-16 10:53:15 +01:00
|
|
|
void unfocus();
|
|
|
|
|
void fixExternalCursor(bool focus);
|
|
|
|
|
void fixExternalCursorPosition(bool focus);
|
2012-11-10 10:36:05 +01:00
|
|
|
|
2013-06-06 21:05:32 +02:00
|
|
|
// Call before any FakeVim processing (import cursor position from editor)
|
|
|
|
|
void enterFakeVim();
|
|
|
|
|
// Call after any FakeVim processing
|
|
|
|
|
// (if needUpdate is true, export cursor position to editor and scroll)
|
|
|
|
|
void leaveFakeVim(bool needUpdate = true);
|
2014-12-01 17:28:44 +01:00
|
|
|
void leaveFakeVim(EventResult eventResult);
|
2012-12-07 18:46:30 +01:00
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
EventResult handleKey(const Input &input);
|
|
|
|
|
EventResult handleDefaultKey(const Input &input);
|
2015-03-29 11:36:47 +02:00
|
|
|
bool handleCommandBufferPaste(const Input &input);
|
2013-03-17 12:39:57 +01:00
|
|
|
EventResult handleCurrentMapAsDefault();
|
|
|
|
|
void prependInputs(const QVector<Input> &inputs); // Handle inputs.
|
|
|
|
|
void prependMapping(const Inputs &inputs); // Handle inputs as mapping.
|
|
|
|
|
bool expandCompleteMapping(); // Return false if current mapping is not complete.
|
|
|
|
|
bool extendMapping(const Input &input); // Return false if no suitable mappig found.
|
|
|
|
|
void endMapping();
|
|
|
|
|
bool canHandleMapping();
|
|
|
|
|
void clearPendingInput();
|
|
|
|
|
void waitForMapping();
|
|
|
|
|
EventResult stopWaitForMapping(bool hasInput);
|
2013-07-09 19:39:31 +02:00
|
|
|
EventResult handleInsertOrReplaceMode(const Input &);
|
|
|
|
|
void handleInsertMode(const Input &);
|
|
|
|
|
void handleReplaceMode(const Input &);
|
2014-02-03 20:47:47 +01:00
|
|
|
void finishInsertMode();
|
2012-11-07 17:34:52 +01:00
|
|
|
|
2010-03-26 13:22:06 +01:00
|
|
|
EventResult handleCommandMode(const Input &);
|
2012-11-07 17:34:52 +01:00
|
|
|
|
|
|
|
|
// return true only if input in current mode and sub-mode was correctly handled
|
|
|
|
|
bool handleEscape();
|
|
|
|
|
bool handleNoSubMode(const Input &);
|
2014-10-26 13:49:11 +01:00
|
|
|
bool handleChangeDeleteYankSubModes(const Input &);
|
|
|
|
|
void handleChangeDeleteYankSubModes();
|
2012-11-07 17:34:52 +01:00
|
|
|
bool handleReplaceSubMode(const Input &);
|
|
|
|
|
bool handleFilterSubMode(const Input &);
|
|
|
|
|
bool handleRegisterSubMode(const Input &);
|
|
|
|
|
bool handleShiftSubMode(const Input &);
|
|
|
|
|
bool handleChangeCaseSubMode(const Input &);
|
|
|
|
|
bool handleWindowSubMode(const Input &);
|
|
|
|
|
bool handleZSubMode(const Input &);
|
|
|
|
|
bool handleCapitalZSubMode(const Input &);
|
2013-03-17 12:41:18 +01:00
|
|
|
bool handleMacroRecordSubMode(const Input &);
|
|
|
|
|
bool handleMacroExecuteSubMode(const Input &);
|
2012-11-07 17:34:52 +01:00
|
|
|
|
2013-02-04 20:00:44 +01:00
|
|
|
bool handleCount(const Input &); // Handle count for commands (return false if input isn't count).
|
2012-11-07 17:34:52 +01:00
|
|
|
bool handleMovement(const Input &);
|
|
|
|
|
|
2010-05-05 16:23:39 +02:00
|
|
|
EventResult handleExMode(const Input &);
|
|
|
|
|
EventResult handleSearchSubSubMode(const Input &);
|
2012-11-07 17:34:52 +01:00
|
|
|
bool handleCommandSubSubMode(const Input &);
|
2012-10-13 09:33:30 +02:00
|
|
|
void fixSelection(); // Fix selection according to current range, move and command modes.
|
2013-09-15 16:19:05 +02:00
|
|
|
bool finishSearch();
|
2012-09-28 17:01:24 +02:00
|
|
|
void finishMovement(const QString &dotCommandMovement = QString());
|
2014-11-12 20:18:18 +01:00
|
|
|
|
|
|
|
|
// Returns to insert/replace mode after <C-O> command in insert mode,
|
|
|
|
|
// otherwise returns to command mode.
|
|
|
|
|
void leaveCurrentMode();
|
|
|
|
|
|
|
|
|
|
// Clear data for current (possibly incomplete) command in current mode.
|
|
|
|
|
// I.e. clears count, register, g flag, sub-modes etc.
|
|
|
|
|
void clearCurrentMode();
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
QTextCursor search(const SearchData &sd, int startPos, int count, bool showMessages);
|
2012-09-10 22:10:23 +02:00
|
|
|
void search(const SearchData &sd, bool showMessages = true);
|
2013-09-15 16:19:05 +02:00
|
|
|
bool searchNext(bool forward = true);
|
2010-07-14 13:02:17 +02:00
|
|
|
void searchBalanced(bool forward, QChar needle, QChar other);
|
2009-03-12 18:05:36 +01:00
|
|
|
void highlightMatches(const QString &needle);
|
2010-01-21 17:23:30 +01:00
|
|
|
void stopIncrementalFind();
|
2012-09-10 22:10:23 +02:00
|
|
|
void updateFind(bool isComplete);
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
void resetCount();
|
2013-02-17 13:56:42 +01:00
|
|
|
bool isInputCount(const Input &) const; // Return true if input can be used as count for commands.
|
2013-05-06 20:02:22 +02:00
|
|
|
int mvCount() const { return qMax(1, g.mvcount); }
|
|
|
|
|
int opCount() const { return qMax(1, g.opcount); }
|
2008-12-25 22:41:09 +01:00
|
|
|
int count() const { return mvCount() * opCount(); }
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextBlock block() const { return m_cursor.block(); }
|
2010-09-13 15:23:20 +02:00
|
|
|
int leftDist() const { return position() - block().position(); }
|
2013-05-14 20:35:31 +02:00
|
|
|
int rightDist() const { return block().length() - leftDist() - (isVisualCharMode() ? 0 : 1); }
|
2013-04-08 20:06:17 +02:00
|
|
|
bool atBlockStart() const { return m_cursor.atBlockStart(); }
|
|
|
|
|
bool atBlockEnd() const { return m_cursor.atBlockEnd(); }
|
2010-09-14 14:04:13 +02:00
|
|
|
bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; }
|
2013-05-14 19:09:57 +02:00
|
|
|
bool atDocumentEnd() const { return position() >= lastPositionInDocument(true); }
|
2013-04-08 20:06:17 +02:00
|
|
|
bool atDocumentStart() const { return m_cursor.atStart(); }
|
2012-08-19 19:08:04 +02:00
|
|
|
|
2014-11-09 16:37:23 +01:00
|
|
|
bool atEmptyLine(int pos) const;
|
|
|
|
|
bool atEmptyLine(const QTextCursor &tc) const;
|
|
|
|
|
bool atEmptyLine() const;
|
2012-08-19 19:08:04 +02:00
|
|
|
bool atBoundary(bool end, bool simple, bool onlyWords = false,
|
|
|
|
|
const QTextCursor &tc = QTextCursor()) const;
|
|
|
|
|
bool atWordBoundary(bool end, bool simple, const QTextCursor &tc = QTextCursor()) const;
|
|
|
|
|
bool atWordStart(bool simple, const QTextCursor &tc = QTextCursor()) const;
|
|
|
|
|
bool atWordEnd(bool simple, const QTextCursor &tc = QTextCursor()) const;
|
|
|
|
|
bool isFirstNonBlankOnLine(int pos);
|
2008-12-19 16:20:39 +01:00
|
|
|
|
2012-10-13 09:33:30 +02:00
|
|
|
int lastPositionInDocument(bool ignoreMode = false) const; // Returns last valid position in doc.
|
2012-10-05 17:33:20 +02:00
|
|
|
int firstPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos
|
|
|
|
|
int lastPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos
|
2009-01-06 11:42:44 +01:00
|
|
|
int lineForPosition(int pos) const; // 1 based line, 0 based pos
|
2009-12-09 17:40:00 +01:00
|
|
|
QString lineContents(int line) const; // 1 based line
|
2013-07-09 19:39:31 +02:00
|
|
|
QString textAt(int from, int to) const;
|
2010-05-11 14:26:37 +02:00
|
|
|
void setLineContents(int line, const QString &contents); // 1 based line
|
2012-07-14 16:56:37 +02:00
|
|
|
int blockBoundary(const QString &left, const QString &right,
|
2012-08-14 18:53:33 +02:00
|
|
|
bool end, int count) const; // end or start position of current code block
|
2012-10-05 17:33:20 +02:00
|
|
|
int lineNumber(const QTextBlock &block) const;
|
2008-12-25 19:11:21 +01:00
|
|
|
|
2014-11-09 16:37:23 +01:00
|
|
|
int columnAt(int pos) const;
|
2014-11-20 19:57:16 +01:00
|
|
|
int blockNumberAt(int pos) const;
|
2014-11-09 16:37:23 +01:00
|
|
|
QTextBlock blockAt(int pos) const;
|
2013-05-09 10:04:40 +02:00
|
|
|
QTextBlock nextLine(const QTextBlock &block) const; // following line (respects wrapped parts)
|
|
|
|
|
QTextBlock previousLine(const QTextBlock &block) const; // previous line (respects wrapped parts)
|
|
|
|
|
|
2009-01-06 11:11:31 +01:00
|
|
|
int linesOnScreen() const;
|
2009-12-11 13:24:53 +01:00
|
|
|
int linesInDocument() const;
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
// The following use all zero-based counting.
|
2009-12-11 13:24:53 +01:00
|
|
|
int cursorLineOnScreen() const;
|
2010-07-06 10:12:21 +02:00
|
|
|
int cursorLine() const;
|
2012-10-23 16:35:13 +02:00
|
|
|
int cursorBlockNumber() const; // "." address
|
2010-07-06 10:12:21 +02:00
|
|
|
int physicalCursorColumn() const; // as stored in the data
|
|
|
|
|
int logicalCursorColumn() const; // as visible on screen
|
|
|
|
|
int physicalToLogicalColumn(int physical, const QString &text) const;
|
|
|
|
|
int logicalToPhysicalColumn(int logical, const QString &text) const;
|
2013-04-06 14:51:50 +02:00
|
|
|
int windowScrollOffset() const; // return scrolloffset but max half the current window height
|
2010-07-06 10:12:21 +02:00
|
|
|
Column cursorColumn() const; // as visible on screen
|
2013-04-08 20:06:17 +02:00
|
|
|
void updateFirstVisibleLine();
|
2010-07-06 10:12:21 +02:00
|
|
|
int firstVisibleLine() const;
|
2013-04-08 20:06:17 +02:00
|
|
|
int lastVisibleLine() const;
|
|
|
|
|
int lineOnTop(int count = 1) const; // [count]-th line from top reachable without scrolling
|
|
|
|
|
int lineOnBottom(int count = 1) const; // [count]-th line from bottom reachable without scrolling
|
2010-07-06 10:12:21 +02:00
|
|
|
void scrollToLine(int line);
|
2009-03-20 08:44:52 +01:00
|
|
|
void scrollUp(int count);
|
|
|
|
|
void scrollDown(int count) { scrollUp(-count); }
|
2013-03-03 11:42:24 +01:00
|
|
|
void updateScrollOffset();
|
2012-10-23 16:35:13 +02:00
|
|
|
void alignViewportToCursor(Qt::AlignmentFlag align, int line = -1,
|
|
|
|
|
bool moveToNonBlank = false);
|
2008-12-25 16:27:47 +01:00
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
int lineToBlockNumber(int line) const;
|
|
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
void setCursorPosition(const CursorPosition &p);
|
|
|
|
|
void setCursorPosition(QTextCursor *tc, const CursorPosition &p);
|
2009-07-13 14:04:47 +02:00
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
// Helper functions for indenting/
|
2010-01-06 14:57:46 +01:00
|
|
|
bool isElectricCharacter(QChar c) const;
|
2010-01-06 14:57:46 +01:00
|
|
|
void indentSelectedText(QChar lastTyped = QChar());
|
2010-06-02 15:27:10 +02:00
|
|
|
void indentText(const Range &range, QChar lastTyped = QChar());
|
2009-03-06 13:03:33 +01:00
|
|
|
void shiftRegionLeft(int repeat = 1);
|
|
|
|
|
void shiftRegionRight(int repeat = 1);
|
2008-12-26 10:36:40 +01:00
|
|
|
|
2008-12-19 14:35:57 +01:00
|
|
|
void moveToFirstNonBlankOnLine();
|
2012-08-26 18:39:48 +02:00
|
|
|
void moveToFirstNonBlankOnLine(QTextCursor *tc);
|
2013-10-17 19:01:29 +02:00
|
|
|
void moveToFirstNonBlankOnLineVisually();
|
|
|
|
|
void moveToNonBlankOnLine(QTextCursor *tc);
|
2009-04-03 14:58:41 +02:00
|
|
|
void moveToTargetColumn();
|
2013-10-16 18:54:54 +02:00
|
|
|
void setTargetColumn();
|
2009-01-13 12:35:43 +01:00
|
|
|
void moveToMatchingParanthesis();
|
2012-08-19 19:08:04 +02:00
|
|
|
void moveToBoundary(bool simple, bool forward = true);
|
|
|
|
|
void moveToNextBoundary(bool end, int count, bool simple, bool forward);
|
|
|
|
|
void moveToNextBoundaryStart(int count, bool simple, bool forward = true);
|
|
|
|
|
void moveToNextBoundaryEnd(int count, bool simple, bool forward = true);
|
|
|
|
|
void moveToBoundaryStart(int count, bool simple, bool forward = true);
|
|
|
|
|
void moveToBoundaryEnd(int count, bool simple, bool forward = true);
|
|
|
|
|
void moveToNextWord(bool end, int count, bool simple, bool forward, bool emptyLines);
|
|
|
|
|
void moveToNextWordStart(int count, bool simple, bool forward = true, bool emptyLines = true);
|
|
|
|
|
void moveToNextWordEnd(int count, bool simple, bool forward = true, bool emptyLines = true);
|
|
|
|
|
void moveToWordStart(int count, bool simple, bool forward = true, bool emptyLines = true);
|
|
|
|
|
void moveToWordEnd(int count, bool simple, bool forward = true, bool emptyLines = true);
|
2009-01-16 14:11:44 +01:00
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
// Convenience wrappers to reduce line noise.
|
2009-04-16 09:18:09 +02:00
|
|
|
void moveToStartOfLine();
|
2013-10-17 19:01:29 +02:00
|
|
|
void moveToStartOfLineVisually();
|
2009-04-08 16:05:24 +02:00
|
|
|
void moveToEndOfLine();
|
2013-10-17 19:01:29 +02:00
|
|
|
void moveToEndOfLineVisually();
|
|
|
|
|
void moveToEndOfLineVisually(QTextCursor *tc);
|
2009-07-03 11:22:18 +02:00
|
|
|
void moveBehindEndOfLine();
|
2009-04-08 16:05:24 +02:00
|
|
|
void moveUp(int n = 1) { moveDown(-n); }
|
2010-09-14 16:58:31 +02:00
|
|
|
void moveDown(int n = 1);
|
2013-10-16 18:54:54 +02:00
|
|
|
void moveUpVisually(int n = 1) { moveDownVisually(-n); }
|
|
|
|
|
void moveDownVisually(int n = 1);
|
2014-11-04 18:53:44 +01:00
|
|
|
void moveVertically(int n = 1) {
|
|
|
|
|
if (g.gflag) {
|
|
|
|
|
g.movetype = MoveExclusive;
|
|
|
|
|
moveDownVisually(n);
|
|
|
|
|
} else {
|
|
|
|
|
g.movetype = MoveLineWise;
|
|
|
|
|
moveDown(n);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-25 17:40:06 +02:00
|
|
|
void movePageDown(int count = 1);
|
|
|
|
|
void movePageUp(int count = 1) { movePageDown(-count); }
|
2010-09-14 14:04:13 +02:00
|
|
|
void dump(const char *msg) const {
|
|
|
|
|
qDebug() << msg << "POS: " << anchor() << position()
|
2013-05-06 20:02:22 +02:00
|
|
|
<< "VISUAL: " << g.visualMode;
|
2010-09-14 14:04:13 +02:00
|
|
|
}
|
|
|
|
|
void moveRight(int n = 1) {
|
2013-05-14 20:35:31 +02:00
|
|
|
if (isVisualCharMode()) {
|
|
|
|
|
const QTextBlock currentBlock = block();
|
|
|
|
|
const int max = currentBlock.position() + currentBlock.length() - 1;
|
|
|
|
|
const int pos = position() + n;
|
|
|
|
|
setPosition(qMin(pos, max));
|
|
|
|
|
} else {
|
|
|
|
|
m_cursor.movePosition(Right, KeepAnchor, n);
|
|
|
|
|
}
|
2012-10-05 17:33:20 +02:00
|
|
|
if (atEndOfLine())
|
2018-02-09 18:49:18 +01:00
|
|
|
q->fold(1, false);
|
2014-11-04 18:53:44 +01:00
|
|
|
setTargetColumn();
|
2010-09-14 14:04:13 +02:00
|
|
|
}
|
|
|
|
|
void moveLeft(int n = 1) {
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor.movePosition(Left, KeepAnchor, n);
|
2014-11-04 18:53:44 +01:00
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
void moveToNextCharacter() {
|
|
|
|
|
moveRight();
|
|
|
|
|
if (atEndOfLine())
|
|
|
|
|
moveRight();
|
|
|
|
|
}
|
|
|
|
|
void moveToPreviousCharacter() {
|
|
|
|
|
moveLeft();
|
|
|
|
|
if (atBlockStart())
|
|
|
|
|
moveLeft();
|
2010-09-14 14:04:13 +02:00
|
|
|
}
|
2010-09-13 15:23:20 +02:00
|
|
|
void setAnchor() {
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor.setPosition(position(), MoveAnchor);
|
2010-09-13 15:23:20 +02:00
|
|
|
}
|
|
|
|
|
void setAnchor(int position) {
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor.setPosition(position, KeepAnchor);
|
2010-09-13 15:23:20 +02:00
|
|
|
}
|
|
|
|
|
void setPosition(int position) {
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor.setPosition(position, KeepAnchor);
|
2010-09-13 15:23:20 +02:00
|
|
|
}
|
2010-09-14 14:04:13 +02:00
|
|
|
void setAnchorAndPosition(int anchor, int position) {
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor.setPosition(anchor, MoveAnchor);
|
|
|
|
|
m_cursor.setPosition(position, KeepAnchor);
|
2010-09-13 15:23:20 +02:00
|
|
|
}
|
2014-11-10 18:27:38 +01:00
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
// Set cursor in text editor widget.
|
2014-11-10 18:27:38 +01:00
|
|
|
void commitCursor();
|
2012-11-01 21:25:43 +01:00
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
// Restore cursor from editor widget.
|
2014-11-04 18:53:44 +01:00
|
|
|
// Update selection, record jump and target column if cursor position
|
|
|
|
|
// changes externally (e.g. by code completion).
|
2014-11-10 18:27:38 +01:00
|
|
|
void pullCursor();
|
2014-11-04 18:53:44 +01:00
|
|
|
|
2015-05-16 09:21:36 +02:00
|
|
|
QTextCursor editorCursor() const;
|
|
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
// Values to save when starting FakeVim processing.
|
|
|
|
|
int m_firstVisibleLine;
|
|
|
|
|
QTextCursor m_cursor;
|
2014-11-10 18:27:38 +01:00
|
|
|
bool m_cursorNeedsUpdate;
|
2009-01-16 16:15:01 +01:00
|
|
|
|
2014-11-20 19:57:16 +01:00
|
|
|
bool moveToPreviousParagraph(int count = 1) { return moveToNextParagraph(-count); }
|
|
|
|
|
bool moveToNextParagraph(int count = 1);
|
|
|
|
|
void moveToParagraphStartOrEnd(int direction = 1);
|
2012-11-26 18:50:19 +01:00
|
|
|
|
2014-04-27 09:17:46 +02:00
|
|
|
bool handleFfTt(const QString &key, bool repeats = false);
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
void enterVisualInsertMode(QChar command);
|
2010-05-06 12:10:57 +02:00
|
|
|
void enterReplaceMode();
|
2013-07-09 19:39:31 +02:00
|
|
|
void enterInsertMode();
|
|
|
|
|
void enterInsertOrReplaceMode(Mode mode);
|
2012-11-10 10:36:05 +01:00
|
|
|
void enterCommandMode(Mode returnToMode = CommandMode);
|
2012-10-26 18:34:58 +02:00
|
|
|
void enterExMode(const QString &contents = QString());
|
2012-09-10 22:10:23 +02:00
|
|
|
void showMessage(MessageLevel level, const QString &msg);
|
|
|
|
|
void clearMessage() { showMessage(MessageInfo, QString()); }
|
2009-01-15 17:29:30 +01:00
|
|
|
void notImplementedYet();
|
2009-01-07 17:41:09 +01:00
|
|
|
void updateMiniBuffer();
|
|
|
|
|
void updateSelection();
|
2012-09-27 21:09:13 +02:00
|
|
|
void updateHighlights();
|
2010-09-13 15:23:20 +02:00
|
|
|
void updateCursorShape();
|
2014-11-16 10:53:15 +01:00
|
|
|
void setThinCursor(bool enable = true);
|
|
|
|
|
bool hasThinCursor() const;
|
2009-01-09 12:21:53 +01:00
|
|
|
QWidget *editor() const;
|
2010-09-13 14:16:18 +02:00
|
|
|
QTextDocument *document() const { return EDITOR(document()); }
|
2014-11-17 15:32:00 +01:00
|
|
|
QChar characterAt(int pos) const { return document()->characterAt(pos); }
|
|
|
|
|
QChar characterAtCursor() const { return characterAt(position()); }
|
2012-09-29 19:09:08 +02:00
|
|
|
|
|
|
|
|
void joinPreviousEditBlock();
|
2013-04-07 10:37:34 +02:00
|
|
|
void beginEditBlock(bool largeEditBlock = false);
|
|
|
|
|
void beginLargeEditBlock() { beginEditBlock(true); }
|
2012-09-29 19:09:08 +02:00
|
|
|
void endEditBlock();
|
2014-04-30 17:32:25 +02:00
|
|
|
void breakEditBlock() { m_buffer->breakEditBlock = true; }
|
2013-04-08 21:25:29 +02:00
|
|
|
|
2015-01-05 16:33:47 +01:00
|
|
|
bool canModifyBufferData() const { return m_buffer->currentHandler.data() == this; }
|
2014-11-23 09:56:37 +01:00
|
|
|
|
2016-05-24 23:09:11 +03:00
|
|
|
void onContentsChanged(int position, int charsRemoved, int charsAdded);
|
|
|
|
|
void onCursorPositionChanged();
|
|
|
|
|
void onUndoCommandAdded();
|
2013-04-08 21:25:29 +02:00
|
|
|
|
2016-05-24 23:09:11 +03:00
|
|
|
void onInputTimeout();
|
|
|
|
|
void onFixCursorTimeout();
|
2014-11-16 10:53:15 +01:00
|
|
|
|
2014-10-26 07:38:27 +01:00
|
|
|
bool isCommandLineMode() const { return g.mode == ExMode || g.subsubmode == SearchSubSubMode; }
|
2013-05-06 20:02:22 +02:00
|
|
|
bool isInsertMode() const { return g.mode == InsertMode || g.mode == ReplaceMode; }
|
2013-08-11 17:15:56 +02:00
|
|
|
// Waiting for movement operator.
|
|
|
|
|
bool isOperatorPending() const {
|
|
|
|
|
return g.submode == ChangeSubMode
|
|
|
|
|
|| g.submode == DeleteSubMode
|
|
|
|
|
|| g.submode == FilterSubMode
|
|
|
|
|
|| g.submode == IndentSubMode
|
|
|
|
|
|| g.submode == ShiftLeftSubMode
|
|
|
|
|
|| g.submode == ShiftRightSubMode
|
|
|
|
|
|| g.submode == InvertCaseSubMode
|
|
|
|
|
|| g.submode == DownCaseSubMode
|
|
|
|
|
|| g.submode == UpCaseSubMode
|
|
|
|
|
|| g.submode == YankSubMode; }
|
2009-01-06 13:03:59 +01:00
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
bool isVisualMode() const { return g.visualMode != NoVisualMode; }
|
|
|
|
|
bool isNoVisualMode() const { return g.visualMode == NoVisualMode; }
|
|
|
|
|
bool isVisualCharMode() const { return g.visualMode == VisualCharMode; }
|
|
|
|
|
bool isVisualLineMode() const { return g.visualMode == VisualLineMode; }
|
|
|
|
|
bool isVisualBlockMode() const { return g.visualMode == VisualBlockMode; }
|
2012-09-01 07:47:25 +02:00
|
|
|
char currentModeCode() const;
|
2010-03-09 16:12:08 +01:00
|
|
|
void updateEditor();
|
2009-12-11 13:24:53 +01:00
|
|
|
|
2012-08-19 19:08:04 +02:00
|
|
|
void selectTextObject(bool simple, bool inner);
|
2010-03-18 17:45:15 +01:00
|
|
|
void selectWordTextObject(bool inner);
|
|
|
|
|
void selectWORDTextObject(bool inner);
|
|
|
|
|
void selectSentenceTextObject(bool inner);
|
|
|
|
|
void selectParagraphTextObject(bool inner);
|
2012-11-29 17:11:43 +01:00
|
|
|
bool changeNumberTextObject(int count);
|
2012-11-04 07:33:05 +01:00
|
|
|
// return true only if cursor is in a block delimited with correct characters
|
2016-03-18 12:10:24 +01:00
|
|
|
bool selectBlockTextObject(bool inner, QChar left, QChar right);
|
2012-11-04 07:33:05 +01:00
|
|
|
bool selectQuotedStringTextObject(bool inner, const QString "e);
|
2010-03-18 17:45:15 +01:00
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
void commitInsertState();
|
|
|
|
|
void invalidateInsertState();
|
|
|
|
|
bool isInsertStateValid() const;
|
|
|
|
|
void clearLastInsertion();
|
2012-10-05 17:33:20 +02:00
|
|
|
void ensureCursorVisible();
|
2010-07-06 09:20:40 +02:00
|
|
|
void insertInInsertMode(const QString &text);
|
2010-05-07 19:46:16 +02:00
|
|
|
|
2013-03-17 12:41:18 +01:00
|
|
|
// Macro recording
|
|
|
|
|
bool startRecording(const Input &input);
|
|
|
|
|
void record(const Input &input);
|
|
|
|
|
void stopRecording();
|
2018-10-05 14:00:05 +02:00
|
|
|
bool executeRegister(int reg);
|
2013-03-17 12:41:18 +01:00
|
|
|
|
2014-10-26 14:57:57 +01:00
|
|
|
// Handle current command as synonym
|
|
|
|
|
void handleAs(const QString &command);
|
|
|
|
|
|
2009-01-06 11:52:05 +01:00
|
|
|
public:
|
|
|
|
|
QTextEdit *m_textedit;
|
|
|
|
|
QPlainTextEdit *m_plaintextedit;
|
2009-01-09 12:51:10 +01:00
|
|
|
bool m_wasReadOnly; // saves read-only state of document
|
2009-01-06 11:52:05 +01:00
|
|
|
|
2013-10-14 16:43:34 +02:00
|
|
|
bool m_inFakeVim; // true if currently processing a key press or a command
|
|
|
|
|
|
2008-12-19 12:20:04 +01:00
|
|
|
FakeVimHandler *q;
|
|
|
|
|
int m_register;
|
2014-02-03 20:47:47 +01:00
|
|
|
BlockInsertMode m_visualBlockInsert;
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2010-01-21 17:42:46 +01:00
|
|
|
bool m_anchorPastEnd;
|
|
|
|
|
bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2008-12-28 01:02:54 +01:00
|
|
|
QString m_currentFileName;
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2010-10-26 10:56:32 +02:00
|
|
|
int m_findStartPosition;
|
2012-08-26 18:39:48 +02:00
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
int anchor() const { return m_cursor.anchor(); }
|
|
|
|
|
int position() const { return m_cursor.position(); }
|
2009-08-11 14:39:44 +02:00
|
|
|
|
2014-11-15 13:34:32 +01:00
|
|
|
// Transform text selected by cursor in current visual mode.
|
2018-11-07 23:45:39 +01:00
|
|
|
using Transformation = std::function<QString(const QString &)>;
|
2014-11-15 13:34:32 +01:00
|
|
|
void transformText(const Range &range, QTextCursor &tc, const std::function<void()> &transform) const;
|
|
|
|
|
void transformText(const Range &range, const Transformation &transform);
|
2010-01-21 17:38:26 +01:00
|
|
|
|
2013-04-07 10:40:40 +02:00
|
|
|
void insertText(QTextCursor &tc, const QString &text);
|
2010-05-11 14:26:37 +02:00
|
|
|
void insertText(const Register ®);
|
2009-08-11 14:39:44 +02:00
|
|
|
void removeText(const Range &range);
|
2010-01-21 17:38:26 +01:00
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void invertCase(const Range &range);
|
2010-01-21 17:38:26 +01:00
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void upCase(const Range &range);
|
2010-01-21 17:38:26 +01:00
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void downCase(const Range &range);
|
2009-08-11 14:39:44 +02:00
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void replaceText(const Range &range, const QString &str);
|
2010-03-18 13:15:59 +01:00
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
QString selectText(const Range &range) const;
|
|
|
|
|
void setCurrentRange(const Range &range);
|
2013-05-06 20:02:22 +02:00
|
|
|
Range currentRange() const { return Range(position(), anchor(), g.rangemode); }
|
2009-08-11 14:39:44 +02:00
|
|
|
|
2013-12-01 13:35:37 +01:00
|
|
|
void yankText(const Range &range, int toregister);
|
2009-08-11 14:39:44 +02:00
|
|
|
|
|
|
|
|
void pasteText(bool afterCursor);
|
2009-02-05 17:06:45 +01:00
|
|
|
|
2019-03-22 12:00:39 +01:00
|
|
|
void cutSelectedText(int reg = 0);
|
2014-11-01 17:57:42 +01:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
void joinLines(int count, bool preserveSpace = false);
|
|
|
|
|
|
2013-03-16 13:32:32 +01:00
|
|
|
void insertNewLine();
|
|
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
bool handleInsertInEditor(const Input &input);
|
2014-11-10 18:27:38 +01:00
|
|
|
bool passEventToEditor(QEvent &event, QTextCursor &tc); // Pass event to editor widget without filtering. Returns true if event was processed.
|
2013-04-01 12:03:22 +02:00
|
|
|
|
2009-04-01 15:11:26 +02:00
|
|
|
// undo handling
|
2013-07-20 08:36:05 +02:00
|
|
|
int revision() const { return document()->availableUndoSteps(); }
|
2012-10-28 10:43:22 +01:00
|
|
|
void undoRedo(bool undo);
|
2009-01-06 11:45:56 +01:00
|
|
|
void undo();
|
|
|
|
|
void redo();
|
2013-04-07 10:37:34 +02:00
|
|
|
void pushUndoState(bool overwrite = true);
|
2009-01-06 11:45:56 +01:00
|
|
|
|
2009-01-06 11:43:49 +01:00
|
|
|
// extra data for '.'
|
2013-07-09 19:39:31 +02:00
|
|
|
void replay(const QString &text, int repeat = 1);
|
2010-05-05 15:50:05 +02:00
|
|
|
void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
|
|
|
|
|
void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
|
2012-09-28 17:01:24 +02:00
|
|
|
QString visualDotCommand() const;
|
2009-01-06 11:43:49 +01:00
|
|
|
|
2011-08-02 17:59:05 +02:00
|
|
|
// visual modes
|
|
|
|
|
void toggleVisualMode(VisualMode visualMode);
|
2009-01-06 11:52:05 +01:00
|
|
|
void leaveVisualMode();
|
2014-11-10 18:27:38 +01:00
|
|
|
void saveLastVisualMode();
|
2009-01-06 11:42:44 +01:00
|
|
|
|
2012-10-28 14:00:50 +01:00
|
|
|
// marks
|
|
|
|
|
Mark mark(QChar code) const;
|
|
|
|
|
void setMark(QChar code, CursorPosition position);
|
|
|
|
|
// jump to valid mark return true if mark is valid and local
|
|
|
|
|
bool jumpToMark(QChar mark, bool backTickMode);
|
2012-11-24 08:42:06 +01:00
|
|
|
// update marks on undo/redo
|
|
|
|
|
void updateMarks(const Marks &newMarks);
|
2016-03-18 12:10:24 +01:00
|
|
|
CursorPosition markLessPosition() const { return mark('<').position(document()); }
|
|
|
|
|
CursorPosition markGreaterPosition() const { return mark('>').position(document()); }
|
2008-12-28 03:07:52 +01:00
|
|
|
|
2008-12-27 21:28:22 +01:00
|
|
|
// vi style configuration
|
2009-03-30 12:40:08 +02:00
|
|
|
QVariant config(int code) const { return theFakeVimSetting(code)->value(); }
|
|
|
|
|
bool hasConfig(int code) const { return config(code).toBool(); }
|
2016-03-18 12:10:24 +01:00
|
|
|
bool hasConfig(int code, const QString &value) const
|
|
|
|
|
{ return config(code).toString().contains(value); }
|
2008-12-23 21:34:21 +01:00
|
|
|
|
2010-01-21 17:38:31 +01:00
|
|
|
int m_targetColumn; // -1 if past end of line
|
|
|
|
|
int m_visualTargetColumn; // 'l' can move past eol in visual mode only
|
2013-10-16 18:54:54 +02:00
|
|
|
int m_targetColumnWrapped; // column in current part of wrapped line
|
2009-01-28 19:22:54 +01:00
|
|
|
|
2009-04-03 11:54:29 +02:00
|
|
|
// auto-indent
|
2009-12-11 13:24:53 +01:00
|
|
|
QString tabExpand(int len) const;
|
2010-03-09 16:44:36 +01:00
|
|
|
Column indentation(const QString &line) const;
|
2013-03-08 17:50:16 +01:00
|
|
|
void insertAutomaticIndentation(bool goingDown, bool forceAutoIndent = false);
|
2009-04-03 11:54:29 +02:00
|
|
|
// number of autoindented characters
|
2009-04-03 14:58:41 +02:00
|
|
|
void handleStartOfLine();
|
2009-04-03 11:54:29 +02:00
|
|
|
|
2011-11-12 02:31:52 +01:00
|
|
|
// register handling
|
|
|
|
|
QString registerContents(int reg) const;
|
2012-08-09 17:32:39 +02:00
|
|
|
void setRegister(int reg, const QString &contents, RangeMode mode);
|
2011-11-12 02:31:52 +01:00
|
|
|
RangeMode registerRangeMode(int reg) const;
|
2018-11-07 23:45:39 +01:00
|
|
|
void getRegisterType(int *reg, bool *isClipboard, bool *isSelection, bool *append = nullptr) const;
|
2011-11-12 02:31:52 +01:00
|
|
|
|
2012-12-07 18:46:30 +01:00
|
|
|
void recordJump(int position = -1);
|
2012-08-11 14:31:42 +02:00
|
|
|
void jump(int distance);
|
2009-03-12 18:05:36 +01:00
|
|
|
|
2012-09-27 21:09:13 +02:00
|
|
|
QList<QTextEdit::ExtraSelection> m_extraSelections;
|
2010-05-18 18:24:00 +02:00
|
|
|
QTextCursor m_searchCursor;
|
2012-09-01 10:44:02 +02:00
|
|
|
int m_searchStartPosition;
|
|
|
|
|
int m_searchFromScreenLine;
|
2013-08-06 17:13:53 +02:00
|
|
|
QString m_highlighted; // currently highlighted text
|
2010-03-19 14:31:21 +01:00
|
|
|
|
2012-09-29 19:09:08 +02:00
|
|
|
bool handleExCommandHelper(ExCommand &cmd); // Returns success.
|
2010-05-18 14:48:12 +02:00
|
|
|
bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin?
|
2010-05-11 14:26:37 +02:00
|
|
|
bool handleExBangCommand(const ExCommand &cmd);
|
2012-10-26 18:34:58 +02:00
|
|
|
bool handleExYankDeleteCommand(const ExCommand &cmd);
|
|
|
|
|
bool handleExChangeCommand(const ExCommand &cmd);
|
2012-10-23 16:35:13 +02:00
|
|
|
bool handleExMoveCommand(const ExCommand &cmd);
|
2012-10-26 18:34:58 +02:00
|
|
|
bool handleExJoinCommand(const ExCommand &cmd);
|
2010-05-11 14:26:37 +02:00
|
|
|
bool handleExGotoCommand(const ExCommand &cmd);
|
|
|
|
|
bool handleExHistoryCommand(const ExCommand &cmd);
|
2010-05-20 14:08:11 +02:00
|
|
|
bool handleExRegisterCommand(const ExCommand &cmd);
|
2010-05-11 14:26:37 +02:00
|
|
|
bool handleExMapCommand(const ExCommand &cmd);
|
2010-07-14 16:04:10 +02:00
|
|
|
bool handleExNohlsearchCommand(const ExCommand &cmd);
|
2010-05-11 14:26:37 +02:00
|
|
|
bool handleExNormalCommand(const ExCommand &cmd);
|
|
|
|
|
bool handleExReadCommand(const ExCommand &cmd);
|
2012-10-28 10:43:22 +01:00
|
|
|
bool handleExUndoRedoCommand(const ExCommand &cmd);
|
2010-05-11 14:26:37 +02:00
|
|
|
bool handleExSetCommand(const ExCommand &cmd);
|
2018-03-12 14:13:07 +01:00
|
|
|
bool handleExSortCommand(const ExCommand &cmd);
|
2010-05-18 14:48:12 +02:00
|
|
|
bool handleExShiftCommand(const ExCommand &cmd);
|
2010-05-11 14:26:37 +02:00
|
|
|
bool handleExSourceCommand(const ExCommand &cmd);
|
|
|
|
|
bool handleExSubstituteCommand(const ExCommand &cmd);
|
2017-07-05 15:16:52 +02:00
|
|
|
bool handleExTabNextCommand(const ExCommand &cmd);
|
|
|
|
|
bool handleExTabPreviousCommand(const ExCommand &cmd);
|
2010-05-11 14:26:37 +02:00
|
|
|
bool handleExWriteCommand(const ExCommand &cmd);
|
2011-04-05 16:32:18 +02:00
|
|
|
bool handleExEchoCommand(const ExCommand &cmd);
|
2010-04-16 16:30:45 +02:00
|
|
|
|
2019-03-04 11:48:02 +01:00
|
|
|
void setTabSize(int tabSize);
|
2010-04-28 16:19:51 +02:00
|
|
|
void setupCharClass();
|
|
|
|
|
int charClass(QChar c, bool simple) const;
|
|
|
|
|
signed char m_charClass[256];
|
2014-04-28 13:13:10 +02:00
|
|
|
|
|
|
|
|
int m_ctrlVAccumulator;
|
|
|
|
|
int m_ctrlVLength;
|
|
|
|
|
int m_ctrlVBase;
|
2010-05-05 15:50:05 +02:00
|
|
|
|
2014-11-16 10:53:15 +01:00
|
|
|
QTimer m_fixCursorTimer;
|
|
|
|
|
QTimer m_inputTimer;
|
|
|
|
|
|
2012-12-30 15:19:15 +01:00
|
|
|
void miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos);
|
2012-09-10 22:10:23 +02:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
// Data shared among editors with same document.
|
|
|
|
|
struct BufferData
|
|
|
|
|
{
|
|
|
|
|
QStack<State> undo;
|
|
|
|
|
QStack<State> redo;
|
|
|
|
|
State undoState;
|
2015-06-18 13:32:25 +02:00
|
|
|
int lastRevision = 0;
|
2014-04-30 17:32:25 +02:00
|
|
|
|
2015-06-18 13:32:25 +02:00
|
|
|
int editBlockLevel = 0; // current level of edit blocks
|
|
|
|
|
bool breakEditBlock = false; // if true, joinPreviousEditBlock() starts new edit block
|
2014-04-30 17:32:25 +02:00
|
|
|
|
|
|
|
|
QStack<CursorPosition> jumpListUndo;
|
|
|
|
|
QStack<CursorPosition> jumpListRedo;
|
|
|
|
|
|
2015-06-18 13:32:25 +02:00
|
|
|
VisualMode lastVisualMode = NoVisualMode;
|
|
|
|
|
bool lastVisualModeInverted = false;
|
2014-04-30 17:32:25 +02:00
|
|
|
|
|
|
|
|
Marks marks;
|
|
|
|
|
|
|
|
|
|
// Insert state to get last inserted text.
|
|
|
|
|
struct InsertState {
|
|
|
|
|
int pos1;
|
|
|
|
|
int pos2;
|
|
|
|
|
int backspaces;
|
|
|
|
|
int deletes;
|
|
|
|
|
QSet<int> spaces;
|
|
|
|
|
bool insertingSpaces;
|
|
|
|
|
QString textBeforeCursor;
|
|
|
|
|
bool newLineBefore;
|
|
|
|
|
bool newLineAfter;
|
|
|
|
|
} insertState;
|
|
|
|
|
|
|
|
|
|
QString lastInsertion;
|
2014-11-23 09:56:37 +01:00
|
|
|
|
|
|
|
|
// If there are multiple editors with same document,
|
|
|
|
|
// only the handler with last focused editor can change buffer data.
|
2016-06-17 15:06:59 +03:00
|
|
|
QPointer<FakeVimHandler::Private> currentHandler;
|
2014-04-30 17:32:25 +02:00
|
|
|
};
|
|
|
|
|
|
2018-11-07 23:45:39 +01:00
|
|
|
using BufferDataPtr = QSharedPointer<BufferData>;
|
2014-04-30 17:32:25 +02:00
|
|
|
void pullOrCreateBufferData();
|
|
|
|
|
BufferDataPtr m_buffer;
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
// Data shared among all editors.
|
2010-05-05 15:50:05 +02:00
|
|
|
static struct GlobalData
|
|
|
|
|
{
|
2012-09-10 22:10:23 +02:00
|
|
|
GlobalData()
|
2015-06-18 13:32:25 +02:00
|
|
|
: mappings()
|
2013-05-06 20:02:22 +02:00
|
|
|
, currentMap(&mappings)
|
2010-05-05 16:05:49 +02:00
|
|
|
{
|
2016-03-18 12:10:24 +01:00
|
|
|
commandBuffer.setPrompt(':');
|
2010-05-05 16:05:49 +02:00
|
|
|
}
|
2010-05-05 15:50:05 +02:00
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
// Current state.
|
2015-06-18 13:32:25 +02:00
|
|
|
bool passing = false; // let the core see the next event
|
|
|
|
|
Mode mode = CommandMode;
|
|
|
|
|
SubMode submode = NoSubMode;
|
|
|
|
|
SubSubMode subsubmode = NoSubSubMode;
|
2013-05-06 20:02:22 +02:00
|
|
|
Input subsubdata;
|
2015-06-18 13:32:25 +02:00
|
|
|
VisualMode visualMode = NoVisualMode;
|
2015-03-29 11:36:47 +02:00
|
|
|
Input minibufferData;
|
2013-05-06 20:02:22 +02:00
|
|
|
|
|
|
|
|
// [count] for current command, 0 if no [count] available
|
2015-06-18 13:32:25 +02:00
|
|
|
int mvcount = 0;
|
|
|
|
|
int opcount = 0;
|
2013-05-06 20:02:22 +02:00
|
|
|
|
2015-06-18 13:32:25 +02:00
|
|
|
MoveType movetype = MoveInclusive;
|
|
|
|
|
RangeMode rangemode = RangeCharMode;
|
|
|
|
|
bool gflag = false; // whether current command started with 'g'
|
2013-05-06 20:02:22 +02:00
|
|
|
|
|
|
|
|
// Extra data for ';'.
|
|
|
|
|
Input semicolonType; // 'f', 'F', 't', 'T'
|
|
|
|
|
QString semicolonKey;
|
|
|
|
|
|
2010-05-05 15:50:05 +02:00
|
|
|
// Repetition.
|
|
|
|
|
QString dotCommand;
|
|
|
|
|
|
|
|
|
|
QHash<int, Register> registers;
|
|
|
|
|
|
|
|
|
|
// All mappings.
|
|
|
|
|
Mappings mappings;
|
2012-09-01 07:47:25 +02:00
|
|
|
|
|
|
|
|
// Input.
|
2013-03-17 12:39:57 +01:00
|
|
|
QList<Input> pendingInput;
|
2012-09-01 07:47:25 +02:00
|
|
|
MappingsIterator currentMap;
|
|
|
|
|
QStack<MappingState> mapStates;
|
2015-06-18 13:32:25 +02:00
|
|
|
int mapDepth = 0;
|
2012-09-17 17:44:05 +02:00
|
|
|
|
|
|
|
|
// Command line buffers.
|
|
|
|
|
CommandBuffer commandBuffer;
|
|
|
|
|
CommandBuffer searchBuffer;
|
|
|
|
|
|
|
|
|
|
// Current mini buffer message.
|
|
|
|
|
QString currentMessage;
|
2015-06-18 13:32:25 +02:00
|
|
|
MessageLevel currentMessageLevel = MessageInfo;
|
2012-11-09 17:42:52 +01:00
|
|
|
QString currentCommand;
|
2012-10-03 16:40:05 +02:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
// Search state.
|
2013-08-06 17:13:53 +02:00
|
|
|
QString lastSearch; // last search expression as entered by user
|
|
|
|
|
QString lastNeedle; // last search expression translated with vimPatternToQtPattern()
|
2015-06-18 13:32:25 +02:00
|
|
|
bool lastSearchForward = false; // last search command was '/' or '*'
|
|
|
|
|
bool highlightsCleared = false; // ':nohlsearch' command is active until next search
|
|
|
|
|
bool findPending = false; // currently searching using external tool (until editor is focused again)
|
2012-10-26 18:34:58 +02:00
|
|
|
|
|
|
|
|
// Last substitution command.
|
|
|
|
|
QString lastSubstituteFlags;
|
|
|
|
|
QString lastSubstitutePattern;
|
|
|
|
|
QString lastSubstituteReplacement;
|
2012-10-28 14:00:50 +01:00
|
|
|
|
|
|
|
|
// Global marks.
|
|
|
|
|
Marks marks;
|
2012-11-10 10:36:05 +01:00
|
|
|
|
|
|
|
|
// Return to insert/replace mode after single command (<C-O>).
|
2015-06-18 13:32:25 +02:00
|
|
|
Mode returnToMode = CommandMode;
|
2013-03-17 12:41:18 +01:00
|
|
|
|
2014-10-21 12:53:29 +02:00
|
|
|
// Currently recorded macro
|
2015-06-18 13:32:25 +02:00
|
|
|
bool isRecording = false;
|
2014-10-21 12:53:29 +02:00
|
|
|
QString recorded;
|
2015-06-18 13:32:25 +02:00
|
|
|
int currentRegister = 0;
|
|
|
|
|
int lastExecutedRegister = 0;
|
2010-05-05 15:50:05 +02:00
|
|
|
} g;
|
2008-12-19 12:20:04 +01:00
|
|
|
};
|
|
|
|
|
|
2010-05-05 15:50:05 +02:00
|
|
|
FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g;
|
2009-03-25 15:31:50 +01:00
|
|
|
|
2009-01-23 15:12:04 +01:00
|
|
|
FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
|
|
|
|
q = parent;
|
2009-01-23 15:12:04 +01:00
|
|
|
m_textedit = qobject_cast<QTextEdit *>(widget);
|
|
|
|
|
m_plaintextedit = qobject_cast<QPlainTextEdit *>(widget);
|
2013-10-21 18:26:08 +02:00
|
|
|
|
|
|
|
|
init();
|
|
|
|
|
|
2013-04-08 21:25:29 +02:00
|
|
|
if (editor()) {
|
2016-05-24 23:09:11 +03:00
|
|
|
connect(EDITOR(document()), &QTextDocument::contentsChange,
|
|
|
|
|
this, &Private::onContentsChanged);
|
|
|
|
|
connect(EDITOR(document()), &QTextDocument::undoCommandAdded,
|
|
|
|
|
this, &Private::onUndoCommandAdded);
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->lastRevision = revision();
|
2013-04-08 21:25:29 +02:00
|
|
|
}
|
2009-04-08 16:05:24 +02:00
|
|
|
}
|
2009-01-23 15:12:04 +01:00
|
|
|
|
2009-04-08 16:05:24 +02:00
|
|
|
void FakeVimHandler::Private::init()
|
|
|
|
|
{
|
2014-11-10 18:27:38 +01:00
|
|
|
m_cursor = QTextCursor(document());
|
|
|
|
|
m_cursorNeedsUpdate = true;
|
2013-10-21 18:26:08 +02:00
|
|
|
m_inFakeVim = false;
|
2010-10-26 10:56:32 +02:00
|
|
|
m_findStartPosition = -1;
|
2014-02-03 20:47:47 +01:00
|
|
|
m_visualBlockInsert = NoneBlockInsertMode;
|
2010-09-14 14:04:13 +02:00
|
|
|
m_positionPastEnd = false;
|
|
|
|
|
m_anchorPastEnd = false;
|
2008-12-19 12:20:04 +01:00
|
|
|
m_register = '"';
|
2009-04-03 14:58:41 +02:00
|
|
|
m_targetColumn = 0;
|
2010-01-21 17:38:31 +01:00
|
|
|
m_visualTargetColumn = 0;
|
2013-10-16 18:54:54 +02:00
|
|
|
m_targetColumnWrapped = 0;
|
2012-09-01 10:44:02 +02:00
|
|
|
m_searchStartPosition = 0;
|
|
|
|
|
m_searchFromScreenLine = 0;
|
2013-04-08 20:06:17 +02:00
|
|
|
m_firstVisibleLine = 0;
|
2014-04-28 13:13:10 +02:00
|
|
|
m_ctrlVAccumulator = 0;
|
|
|
|
|
m_ctrlVLength = 0;
|
|
|
|
|
m_ctrlVBase = 0;
|
2010-04-28 16:19:51 +02:00
|
|
|
|
2016-05-24 23:09:11 +03:00
|
|
|
initSingleShotTimer(&m_fixCursorTimer, 0, this, &FakeVimHandler::Private::onFixCursorTimeout);
|
|
|
|
|
initSingleShotTimer(&m_inputTimer, 1000, this, &FakeVimHandler::Private::onInputTimeout);
|
2014-11-16 10:53:15 +01:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
pullOrCreateBufferData();
|
2010-04-28 16:19:51 +02:00
|
|
|
setupCharClass();
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2012-11-10 10:36:05 +01:00
|
|
|
void FakeVimHandler::Private::focus()
|
|
|
|
|
{
|
2014-11-23 09:56:37 +01:00
|
|
|
m_buffer->currentHandler = this;
|
|
|
|
|
|
2013-09-15 16:19:05 +02:00
|
|
|
enterFakeVim();
|
|
|
|
|
|
2012-11-10 10:36:05 +01:00
|
|
|
stopIncrementalFind();
|
2014-11-15 09:19:04 +01:00
|
|
|
if (isCommandLineMode()) {
|
|
|
|
|
if (g.subsubmode == SearchSubSubMode) {
|
|
|
|
|
setPosition(m_searchStartPosition);
|
|
|
|
|
scrollToLine(m_searchFromScreenLine);
|
|
|
|
|
} else {
|
|
|
|
|
leaveVisualMode();
|
|
|
|
|
setPosition(qMin(position(), anchor()));
|
2013-09-15 16:19:05 +02:00
|
|
|
}
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2014-11-15 09:19:04 +01:00
|
|
|
setTargetColumn();
|
|
|
|
|
setAnchor();
|
|
|
|
|
commitCursor();
|
|
|
|
|
} else {
|
|
|
|
|
clearCurrentMode();
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
2014-11-16 10:53:15 +01:00
|
|
|
fixExternalCursor(true);
|
2013-08-06 17:13:53 +02:00
|
|
|
updateHighlights();
|
2013-09-15 16:19:05 +02:00
|
|
|
|
2014-02-01 23:57:21 +01:00
|
|
|
leaveFakeVim(false);
|
2012-11-10 10:36:05 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-16 10:53:15 +01:00
|
|
|
void FakeVimHandler::Private::unfocus()
|
|
|
|
|
{
|
|
|
|
|
fixExternalCursor(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::fixExternalCursor(bool focus)
|
|
|
|
|
{
|
2014-11-20 20:41:32 +01:00
|
|
|
m_fixCursorTimer.stop();
|
|
|
|
|
|
2014-11-16 10:53:15 +01:00
|
|
|
if (isVisualCharMode() && !focus && !hasThinCursor()) {
|
|
|
|
|
// Select the character under thick cursor for external operations with text selection.
|
|
|
|
|
fixExternalCursorPosition(false);
|
|
|
|
|
} else if (isVisualCharMode() && focus && hasThinCursor()) {
|
|
|
|
|
// Fix cursor position if changing its shape.
|
|
|
|
|
// The fix is postponed so context menu action can be finished.
|
|
|
|
|
m_fixCursorTimer.start();
|
|
|
|
|
} else {
|
|
|
|
|
updateCursorShape();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::fixExternalCursorPosition(bool focus)
|
|
|
|
|
{
|
2015-05-16 09:21:36 +02:00
|
|
|
QTextCursor tc = editorCursor();
|
2014-11-16 10:53:15 +01:00
|
|
|
if (tc.anchor() < tc.position()) {
|
|
|
|
|
tc.movePosition(focus ? Left : Right, KeepAnchor);
|
|
|
|
|
EDITOR(setTextCursor(tc));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setThinCursor(!focus);
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-07 18:46:30 +01:00
|
|
|
void FakeVimHandler::Private::enterFakeVim()
|
|
|
|
|
{
|
2018-04-11 12:03:04 +02:00
|
|
|
if (m_inFakeVim) {
|
|
|
|
|
qWarning("enterFakeVim() shouldn't be called recursively!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2012-12-07 18:46:30 +01:00
|
|
|
|
2015-03-24 19:31:28 +01:00
|
|
|
if (!m_buffer->currentHandler)
|
|
|
|
|
m_buffer->currentHandler = this;
|
|
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
pullOrCreateBufferData();
|
|
|
|
|
|
2013-10-14 16:43:34 +02:00
|
|
|
m_inFakeVim = true;
|
2013-04-08 20:06:17 +02:00
|
|
|
|
2013-10-04 10:06:49 +02:00
|
|
|
removeEventFilter();
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
pullCursor();
|
2012-12-07 18:46:30 +01:00
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
updateFirstVisibleLine();
|
2012-12-07 18:46:30 +01:00
|
|
|
}
|
|
|
|
|
|
2013-06-06 21:05:32 +02:00
|
|
|
void FakeVimHandler::Private::leaveFakeVim(bool needUpdate)
|
2012-12-07 18:46:30 +01:00
|
|
|
{
|
2018-04-11 12:03:04 +02:00
|
|
|
if (!m_inFakeVim) {
|
|
|
|
|
qWarning("enterFakeVim() not called before leaveFakeVim()!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-04-08 20:06:17 +02:00
|
|
|
|
2012-12-07 18:46:30 +01:00
|
|
|
// The command might have destroyed the editor.
|
|
|
|
|
if (m_textedit || m_plaintextedit) {
|
|
|
|
|
if (hasConfig(ConfigShowMarks))
|
|
|
|
|
updateSelection();
|
|
|
|
|
|
2014-11-16 10:53:15 +01:00
|
|
|
updateMiniBuffer();
|
|
|
|
|
|
2013-06-06 21:05:32 +02:00
|
|
|
if (needUpdate) {
|
|
|
|
|
// Move cursor line to middle of screen if it's not visible.
|
|
|
|
|
const int line = cursorLine();
|
2013-08-12 19:43:08 +02:00
|
|
|
if (line < firstVisibleLine() || line > firstVisibleLine() + linesOnScreen())
|
2013-06-06 21:05:32 +02:00
|
|
|
scrollToLine(qMax(0, line - linesOnScreen() / 2));
|
|
|
|
|
else
|
|
|
|
|
scrollToLine(firstVisibleLine());
|
|
|
|
|
updateScrollOffset();
|
2014-11-10 18:27:38 +01:00
|
|
|
|
|
|
|
|
commitCursor();
|
2013-06-06 21:05:32 +02:00
|
|
|
}
|
2013-10-04 10:06:49 +02:00
|
|
|
|
|
|
|
|
installEventFilter();
|
2012-12-07 18:46:30 +01:00
|
|
|
}
|
2013-04-08 20:06:17 +02:00
|
|
|
|
2013-10-14 16:43:34 +02:00
|
|
|
m_inFakeVim = false;
|
2012-12-07 18:46:30 +01:00
|
|
|
}
|
|
|
|
|
|
2014-12-01 17:28:44 +01:00
|
|
|
void FakeVimHandler::Private::leaveFakeVim(EventResult eventResult)
|
|
|
|
|
{
|
|
|
|
|
leaveFakeVim(eventResult == EventHandled || eventResult == EventCancelled);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-05 11:06:25 +01:00
|
|
|
bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2009-03-05 11:06:25 +01:00
|
|
|
const int key = ev->key();
|
2013-09-26 22:15:48 +02:00
|
|
|
const Qt::KeyboardModifiers mods = ev->modifiers();
|
2013-05-06 20:02:22 +02:00
|
|
|
KEY_DEBUG("SHORTCUT OVERRIDE" << key << " PASSING: " << g.passing);
|
2009-01-09 12:51:10 +01:00
|
|
|
|
2010-03-18 13:15:59 +01:00
|
|
|
if (key == Key_Escape) {
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.subsubmode == SearchSubSubMode)
|
2010-05-04 17:58:53 +02:00
|
|
|
return true;
|
2011-08-03 11:58:59 +02:00
|
|
|
// Not sure this feels good. People often hit Esc several times.
|
|
|
|
|
if (isNoVisualMode()
|
2013-05-06 20:02:22 +02:00
|
|
|
&& g.mode == CommandMode
|
|
|
|
|
&& g.submode == NoSubMode
|
2012-11-10 10:36:05 +01:00
|
|
|
&& g.currentCommand.isEmpty()
|
|
|
|
|
&& g.returnToMode == CommandMode)
|
2010-03-18 13:15:58 +01:00
|
|
|
return false;
|
|
|
|
|
return true;
|
2009-03-05 11:06:25 +01:00
|
|
|
}
|
|
|
|
|
|
2011-08-03 11:58:59 +02:00
|
|
|
// We are interested in overriding most Ctrl key combinations.
|
2013-11-22 06:16:01 +02:00
|
|
|
if (isOnlyControlModifier(mods)
|
2010-09-24 09:07:08 +02:00
|
|
|
&& !config(ConfigPassControlKey).toBool()
|
2010-03-18 13:15:59 +01:00
|
|
|
&& ((key >= Key_A && key <= Key_Z && key != Key_K)
|
|
|
|
|
|| key == Key_BracketLeft || key == Key_BracketRight)) {
|
2009-10-16 11:30:46 +02:00
|
|
|
// Ctrl-K is special as it is the Core's default notion of Locator
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.passing) {
|
2009-03-05 11:06:25 +01:00
|
|
|
KEY_DEBUG(" PASSING CTRL KEY");
|
|
|
|
|
// We get called twice on the same key
|
2013-05-06 20:02:22 +02:00
|
|
|
//g.passing = false;
|
2009-03-05 11:06:25 +01:00
|
|
|
return false;
|
2009-01-09 17:57:48 +01:00
|
|
|
}
|
2009-03-05 11:06:25 +01:00
|
|
|
KEY_DEBUG(" NOT PASSING CTRL KEY");
|
|
|
|
|
return true;
|
2009-01-09 12:51:10 +01:00
|
|
|
}
|
|
|
|
|
|
2011-08-03 11:58:59 +02:00
|
|
|
// Let other shortcuts trigger.
|
2009-03-05 11:06:25 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
|
|
|
|
|
{
|
2010-04-28 14:00:44 +02:00
|
|
|
const int key = ev->key();
|
2013-09-26 22:15:48 +02:00
|
|
|
const Qt::KeyboardModifiers mods = ev->modifiers();
|
2009-03-05 11:06:25 +01:00
|
|
|
|
2008-12-19 12:20:04 +01:00
|
|
|
if (key == Key_Shift || key == Key_Alt || key == Key_Control
|
2014-05-19 18:33:26 +03:00
|
|
|
|| key == Key_AltGr || key == Key_Meta)
|
2009-03-05 11:06:25 +01:00
|
|
|
{
|
|
|
|
|
KEY_DEBUG("PLAIN MODIFIER");
|
|
|
|
|
return EventUnhandled;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.passing) {
|
2010-01-05 15:30:22 +01:00
|
|
|
passShortcuts(false);
|
2009-03-05 11:06:25 +01:00
|
|
|
KEY_DEBUG("PASSING PLAIN KEY..." << ev->key() << ev->text());
|
2010-04-28 14:00:44 +02:00
|
|
|
//if (input.is(',')) { // use ',,' to leave, too.
|
2009-03-05 11:06:25 +01:00
|
|
|
// qDebug() << "FINISHED...";
|
|
|
|
|
// return EventHandled;
|
|
|
|
|
//}
|
|
|
|
|
KEY_DEBUG(" PASS TO CORE");
|
|
|
|
|
return EventPassedToCore;
|
|
|
|
|
}
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2013-03-08 17:50:16 +01:00
|
|
|
#ifndef FAKEVIM_STANDALONE
|
2011-04-06 14:55:26 +02:00
|
|
|
bool inSnippetMode = false;
|
|
|
|
|
QMetaObject::invokeMethod(editor(),
|
|
|
|
|
"inSnippetMode", Q_ARG(bool *, &inSnippetMode));
|
|
|
|
|
|
|
|
|
|
if (inSnippetMode)
|
|
|
|
|
return EventPassedToCore;
|
2013-03-08 17:50:16 +01:00
|
|
|
#endif
|
2011-04-06 14:55:26 +02:00
|
|
|
|
2008-12-19 12:20:04 +01:00
|
|
|
// Fake "End of line"
|
2013-04-08 20:06:17 +02:00
|
|
|
//m_tc = m_cursor;
|
2008-12-26 18:29:38 +01:00
|
|
|
|
2010-09-13 13:53:18 +02:00
|
|
|
//bool hasBlock = false;
|
2018-02-09 18:49:18 +01:00
|
|
|
//q->requestHasBlockSelection(&hasBlock);
|
2010-09-13 13:53:18 +02:00
|
|
|
//qDebug() << "IMPORT BLOCK 2:" << hasBlock;
|
|
|
|
|
|
|
|
|
|
//if (0 && hasBlock) {
|
|
|
|
|
// (pos > anc) ? --pos : --anc;
|
|
|
|
|
|
2011-04-07 17:59:12 +02:00
|
|
|
//if ((mods & RealControlModifier) != 0) {
|
2010-04-28 14:00:44 +02:00
|
|
|
// if (key >= Key_A && key <= Key_Z)
|
|
|
|
|
// key = shift(key); // make it lower case
|
|
|
|
|
// key = control(key);
|
|
|
|
|
//} else if (key >= Key_A && key <= Key_Z && (mods & Qt::ShiftModifier) == 0) {
|
|
|
|
|
// key = shift(key);
|
|
|
|
|
//}
|
2009-02-09 08:45:02 +01:00
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
//QTC_ASSERT(g.mode == InsertMode || g.mode == ReplaceMode
|
2010-09-14 14:04:13 +02:00
|
|
|
// || !atBlockEnd() || block().length() <= 1,
|
2010-08-11 13:41:54 +02:00
|
|
|
// qDebug() << "Cursor at EOL before key handler");
|
2010-01-21 17:38:28 +01:00
|
|
|
|
2015-12-16 14:10:44 +01:00
|
|
|
const Input input(key, mods, ev->text());
|
|
|
|
|
if (!input.isValid())
|
|
|
|
|
return EventUnhandled;
|
|
|
|
|
|
2012-12-07 18:46:30 +01:00
|
|
|
enterFakeVim();
|
2015-12-16 14:10:44 +01:00
|
|
|
EventResult result = handleKey(input);
|
2014-12-01 17:28:44 +01:00
|
|
|
leaveFakeVim(result);
|
2009-03-05 11:06:25 +01:00
|
|
|
return result;
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 16:54:25 +02:00
|
|
|
void FakeVimHandler::Private::installEventFilter()
|
|
|
|
|
{
|
|
|
|
|
EDITOR(installEventFilter(q));
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-16 13:32:32 +01:00
|
|
|
void FakeVimHandler::Private::removeEventFilter()
|
|
|
|
|
{
|
|
|
|
|
EDITOR(removeEventFilter(q));
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-23 15:12:04 +01:00
|
|
|
void FakeVimHandler::Private::setupWidget()
|
|
|
|
|
{
|
2014-11-18 20:02:22 +01:00
|
|
|
m_cursorNeedsUpdate = true;
|
2016-05-24 23:09:11 +03:00
|
|
|
if (m_textedit) {
|
|
|
|
|
connect(m_textedit, &QTextEdit::cursorPositionChanged,
|
|
|
|
|
this, &FakeVimHandler::Private::onCursorPositionChanged, Qt::UniqueConnection);
|
|
|
|
|
} else {
|
|
|
|
|
connect(m_plaintextedit, &QPlainTextEdit::cursorPositionChanged,
|
|
|
|
|
this, &FakeVimHandler::Private::onCursorPositionChanged, Qt::UniqueConnection);
|
|
|
|
|
}
|
2014-11-18 20:02:22 +01:00
|
|
|
|
2013-10-14 16:43:34 +02:00
|
|
|
enterFakeVim();
|
|
|
|
|
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2009-01-26 10:31:49 +01:00
|
|
|
m_wasReadOnly = EDITOR(isReadOnly());
|
2009-02-05 17:06:45 +01:00
|
|
|
|
2010-03-09 16:12:08 +01:00
|
|
|
updateEditor();
|
2012-12-07 18:46:30 +01:00
|
|
|
|
2013-10-14 16:43:34 +02:00
|
|
|
leaveFakeVim();
|
2010-05-07 19:46:16 +02:00
|
|
|
}
|
2010-03-09 16:12:08 +01:00
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
void FakeVimHandler::Private::commitInsertState()
|
2012-11-24 10:24:48 +01:00
|
|
|
{
|
2013-07-09 19:39:31 +02:00
|
|
|
if (!isInsertStateValid())
|
|
|
|
|
return;
|
2012-11-24 10:24:48 +01:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
QString &lastInsertion = m_buffer->lastInsertion;
|
|
|
|
|
BufferData::InsertState &insertState = m_buffer->insertState;
|
|
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
// Get raw inserted text.
|
2014-04-30 17:32:25 +02:00
|
|
|
lastInsertion = textAt(insertState.pos1, insertState.pos2);
|
2012-11-24 10:24:48 +01:00
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
// Escape special characters and spaces inserted by user (not by auto-indentation).
|
2014-04-30 17:32:25 +02:00
|
|
|
for (int i = lastInsertion.size() - 1; i >= 0; --i) {
|
|
|
|
|
const int pos = insertState.pos1 + i;
|
2016-03-18 12:10:24 +01:00
|
|
|
const QChar c = characterAt(pos);
|
2013-07-09 19:39:31 +02:00
|
|
|
if (c == '<')
|
2016-03-18 12:10:24 +01:00
|
|
|
lastInsertion.replace(i, 1, "<LT>");
|
2014-04-30 17:32:25 +02:00
|
|
|
else if ((c == ' ' || c == '\t') && insertState.spaces.contains(pos))
|
2016-03-18 12:10:24 +01:00
|
|
|
lastInsertion.replace(i, 1, QLatin1String(c == ' ' ? "<SPACE>" : "<TAB>"));
|
2012-11-24 10:24:48 +01:00
|
|
|
}
|
|
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
// Remove unnecessary backspaces.
|
2014-04-30 17:32:25 +02:00
|
|
|
while (insertState.backspaces > 0 && !lastInsertion.isEmpty() && lastInsertion[0].isSpace())
|
|
|
|
|
--insertState.backspaces;
|
2013-07-09 19:39:31 +02:00
|
|
|
|
|
|
|
|
// backspaces in front of inserted text
|
2016-03-18 12:10:24 +01:00
|
|
|
lastInsertion.prepend(QString("<BS>").repeated(insertState.backspaces));
|
2013-07-09 19:39:31 +02:00
|
|
|
// deletes after inserted text
|
2016-03-18 12:10:24 +01:00
|
|
|
lastInsertion.prepend(QString("<DELETE>").repeated(insertState.deletes));
|
2013-07-09 19:39:31 +02:00
|
|
|
|
|
|
|
|
// Remove indentation.
|
2020-06-22 15:40:34 +02:00
|
|
|
lastInsertion.replace(QRegularExpression("(^|\n)[\\t ]+"), "\\1");
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::invalidateInsertState()
|
|
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
BufferData::InsertState &insertState = m_buffer->insertState;
|
|
|
|
|
insertState.pos1 = -1;
|
2014-11-10 18:27:38 +01:00
|
|
|
insertState.pos2 = position();
|
2014-04-30 17:32:25 +02:00
|
|
|
insertState.backspaces = 0;
|
|
|
|
|
insertState.deletes = 0;
|
|
|
|
|
insertState.spaces.clear();
|
|
|
|
|
insertState.insertingSpaces = false;
|
2014-11-10 18:27:38 +01:00
|
|
|
insertState.textBeforeCursor = textAt(block().position(), position());
|
2014-04-30 17:32:25 +02:00
|
|
|
insertState.newLineBefore = false;
|
|
|
|
|
insertState.newLineAfter = false;
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::isInsertStateValid() const
|
|
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
return m_buffer->insertState.pos1 != -1;
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::clearLastInsertion()
|
|
|
|
|
{
|
|
|
|
|
invalidateInsertState();
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->lastInsertion.clear();
|
|
|
|
|
m_buffer->insertState.pos1 = m_buffer->insertState.pos2;
|
2012-11-24 10:24:48 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-05 17:33:20 +02:00
|
|
|
void FakeVimHandler::Private::ensureCursorVisible()
|
|
|
|
|
{
|
|
|
|
|
int pos = position();
|
|
|
|
|
int anc = isVisualMode() ? anchor() : position();
|
|
|
|
|
|
|
|
|
|
// fix selection so it is outside folded block
|
|
|
|
|
int start = qMin(pos, anc);
|
|
|
|
|
int end = qMax(pos, anc) + 1;
|
2014-11-09 16:37:23 +01:00
|
|
|
QTextBlock block = blockAt(start);
|
|
|
|
|
QTextBlock block2 = blockAt(end);
|
2012-10-05 17:33:20 +02:00
|
|
|
if (!block.isVisible() || !block2.isVisible()) {
|
|
|
|
|
// FIXME: Moving cursor left/right or unfolding block immediately after block is folded
|
|
|
|
|
// should restore cursor position inside block.
|
|
|
|
|
// Changing cursor position after folding is not Vim behavior so at least record the jump.
|
|
|
|
|
if (block.isValid() && !block.isVisible())
|
|
|
|
|
recordJump();
|
|
|
|
|
|
|
|
|
|
pos = start;
|
|
|
|
|
while (block.isValid() && !block.isVisible())
|
|
|
|
|
block = block.previous();
|
|
|
|
|
if (block.isValid())
|
|
|
|
|
pos = block.position() + qMin(m_targetColumn, block.length() - 2);
|
|
|
|
|
|
|
|
|
|
if (isVisualMode()) {
|
|
|
|
|
anc = end;
|
|
|
|
|
while (block2.isValid() && !block2.isVisible()) {
|
|
|
|
|
anc = block2.position() + block2.length() - 2;
|
|
|
|
|
block2 = block2.next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setAnchorAndPosition(anc, pos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-09 16:12:08 +01:00
|
|
|
void FakeVimHandler::Private::updateEditor()
|
|
|
|
|
{
|
2019-03-04 11:48:02 +01:00
|
|
|
setTabSize(config(ConfigTabStop).toInt());
|
2010-04-28 16:19:51 +02:00
|
|
|
setupCharClass();
|
2010-03-09 16:12:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-04 11:48:02 +01:00
|
|
|
void FakeVimHandler::Private::setTabSize(int tabSize)
|
|
|
|
|
{
|
|
|
|
|
const int charWidth = QFontMetrics(EDITOR(font())).horizontalAdvance(' ');
|
|
|
|
|
const int width = charWidth * tabSize;
|
|
|
|
|
EDITOR(setTabStopDistance(width));
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-26 17:55:43 +01:00
|
|
|
void FakeVimHandler::Private::restoreWidget(int tabSize)
|
2009-01-23 15:12:04 +01:00
|
|
|
{
|
2009-03-30 16:54:25 +02:00
|
|
|
//EDITOR(removeEventFilter(q));
|
2010-05-04 11:00:40 +02:00
|
|
|
//EDITOR(setReadOnly(m_wasReadOnly));
|
2019-03-04 11:48:02 +01:00
|
|
|
setTabSize(tabSize);
|
2013-05-06 20:02:22 +02:00
|
|
|
g.visualMode = NoVisualMode;
|
2010-05-04 11:00:40 +02:00
|
|
|
// Force "ordinary" cursor.
|
2014-11-16 10:53:15 +01:00
|
|
|
setThinCursor();
|
2009-02-05 17:06:45 +01:00
|
|
|
updateSelection();
|
2012-09-27 21:09:13 +02:00
|
|
|
updateHighlights();
|
2016-05-24 23:09:11 +03:00
|
|
|
if (m_textedit) {
|
|
|
|
|
disconnect(m_textedit, &QTextEdit::cursorPositionChanged,
|
|
|
|
|
this, &FakeVimHandler::Private::onCursorPositionChanged);
|
|
|
|
|
} else {
|
|
|
|
|
disconnect(m_plaintextedit, &QPlainTextEdit::cursorPositionChanged,
|
|
|
|
|
this, &FakeVimHandler::Private::onCursorPositionChanged);
|
|
|
|
|
}
|
2009-01-23 15:12:04 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-26 13:22:06 +01:00
|
|
|
EventResult FakeVimHandler::Private::handleKey(const Input &input)
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2015-03-29 11:36:47 +02:00
|
|
|
KEY_DEBUG("HANDLE INPUT: " << input);
|
2012-09-01 07:47:25 +02:00
|
|
|
|
2012-09-13 18:44:36 +02:00
|
|
|
bool hasInput = input.isValid();
|
|
|
|
|
|
2013-03-17 12:39:57 +01:00
|
|
|
// Waiting on input to complete mapping?
|
|
|
|
|
EventResult r = stopWaitForMapping(hasInput);
|
|
|
|
|
|
2013-03-17 12:41:18 +01:00
|
|
|
if (hasInput) {
|
|
|
|
|
record(input);
|
2012-09-13 18:44:36 +02:00
|
|
|
g.pendingInput.append(input);
|
2013-03-17 12:41:18 +01:00
|
|
|
}
|
2012-09-13 18:44:36 +02:00
|
|
|
|
2013-03-17 12:39:57 +01:00
|
|
|
// Process pending input.
|
|
|
|
|
// Note: Pending input is global state and can be extended by:
|
|
|
|
|
// 1. handling a user input (though handleKey() is not called recursively),
|
|
|
|
|
// 2. expanding a user mapping or
|
|
|
|
|
// 3. executing a register.
|
|
|
|
|
while (!g.pendingInput.isEmpty() && r == EventHandled) {
|
|
|
|
|
const Input in = g.pendingInput.takeFirst();
|
2012-09-01 07:47:25 +02:00
|
|
|
|
|
|
|
|
// invalid input is used to pop mapping state
|
|
|
|
|
if (!in.isValid()) {
|
2013-03-17 12:39:57 +01:00
|
|
|
endMapping();
|
2012-09-01 07:47:25 +02:00
|
|
|
} else {
|
2013-03-17 12:39:57 +01:00
|
|
|
// Handle user mapping.
|
|
|
|
|
if (canHandleMapping()) {
|
|
|
|
|
if (extendMapping(in)) {
|
|
|
|
|
if (!hasInput || !g.currentMap.canExtend())
|
|
|
|
|
expandCompleteMapping();
|
|
|
|
|
} else if (!expandCompleteMapping()) {
|
|
|
|
|
r = handleCurrentMapAsDefault();
|
2012-09-01 07:47:25 +02:00
|
|
|
}
|
2013-03-17 12:39:57 +01:00
|
|
|
} else {
|
|
|
|
|
r = handleDefaultKey(in);
|
2012-11-06 17:29:04 +01:00
|
|
|
}
|
2012-09-01 07:47:25 +02:00
|
|
|
}
|
2010-03-26 13:22:06 +01:00
|
|
|
}
|
2012-09-01 07:47:25 +02:00
|
|
|
|
2013-03-17 12:39:57 +01:00
|
|
|
if (g.currentMap.canExtend()) {
|
|
|
|
|
waitForMapping();
|
|
|
|
|
return EventHandled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r != EventHandled)
|
|
|
|
|
clearPendingInput();
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-29 11:36:47 +02:00
|
|
|
bool FakeVimHandler::Private::handleCommandBufferPaste(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
if (input.isControl('r')
|
|
|
|
|
&& (g.subsubmode == SearchSubSubMode || g.mode == ExMode)) {
|
|
|
|
|
g.minibufferData = input;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (g.minibufferData.isControl('r')) {
|
|
|
|
|
g.minibufferData = Input();
|
|
|
|
|
if (input.isEscape())
|
|
|
|
|
return true;
|
|
|
|
|
CommandBuffer &buffer = (g.subsubmode == SearchSubSubMode)
|
|
|
|
|
? g.searchBuffer : g.commandBuffer;
|
|
|
|
|
if (input.isControl('w')) {
|
|
|
|
|
QTextCursor tc = m_cursor;
|
|
|
|
|
tc.select(QTextCursor::WordUnderCursor);
|
|
|
|
|
QString word = tc.selectedText();
|
|
|
|
|
buffer.insertText(word);
|
|
|
|
|
} else {
|
|
|
|
|
QString r = registerContents(input.asChar().unicode());
|
|
|
|
|
buffer.insertText(r);
|
|
|
|
|
}
|
|
|
|
|
updateMiniBuffer();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
EventResult FakeVimHandler::Private::handleDefaultKey(const Input &input)
|
|
|
|
|
{
|
2015-05-08 09:30:50 +02:00
|
|
|
if (g.passing) {
|
|
|
|
|
passShortcuts(false);
|
|
|
|
|
QKeyEvent event(QEvent::KeyPress, input.key(), input.modifiers(), input.text());
|
|
|
|
|
bool accepted = QApplication::sendEvent(editor()->window(), &event);
|
|
|
|
|
if (accepted || (!m_textedit && !m_plaintextedit))
|
|
|
|
|
return EventHandled;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
if (input == Nop)
|
|
|
|
|
return EventHandled;
|
2013-05-06 20:02:22 +02:00
|
|
|
else if (g.subsubmode == SearchSubSubMode)
|
2012-09-01 07:47:25 +02:00
|
|
|
return handleSearchSubSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
else if (g.mode == CommandMode)
|
2012-09-01 07:47:25 +02:00
|
|
|
return handleCommandMode(input);
|
2013-07-09 19:39:31 +02:00
|
|
|
else if (g.mode == InsertMode || g.mode == ReplaceMode)
|
|
|
|
|
return handleInsertOrReplaceMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
else if (g.mode == ExMode)
|
2012-09-01 07:47:25 +02:00
|
|
|
return handleExMode(input);
|
2009-03-05 11:06:25 +01:00
|
|
|
return EventUnhandled;
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-17 12:39:57 +01:00
|
|
|
EventResult FakeVimHandler::Private::handleCurrentMapAsDefault()
|
2010-03-26 13:22:06 +01:00
|
|
|
{
|
2013-03-17 12:39:57 +01:00
|
|
|
// If mapping has failed take the first input from it and try default command.
|
|
|
|
|
const Inputs &inputs = g.currentMap.currentInputs();
|
2015-12-16 14:10:44 +01:00
|
|
|
if (inputs.isEmpty())
|
|
|
|
|
return EventHandled;
|
2012-09-01 07:47:25 +02:00
|
|
|
|
2013-03-17 12:39:57 +01:00
|
|
|
Input in = inputs.front();
|
|
|
|
|
if (inputs.size() > 1)
|
|
|
|
|
prependInputs(inputs.mid(1));
|
|
|
|
|
g.currentMap.reset();
|
|
|
|
|
|
|
|
|
|
return handleDefaultKey(in);
|
|
|
|
|
}
|
2012-09-01 07:47:25 +02:00
|
|
|
|
2013-03-17 12:39:57 +01:00
|
|
|
void FakeVimHandler::Private::prependInputs(const QVector<Input> &inputs)
|
|
|
|
|
{
|
|
|
|
|
for (int i = inputs.size() - 1; i >= 0; --i)
|
|
|
|
|
g.pendingInput.prepend(inputs[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::prependMapping(const Inputs &inputs)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: Implement Vim option maxmapdepth (default value is 1000).
|
|
|
|
|
if (g.mapDepth >= 1000) {
|
|
|
|
|
const int i = qMax(0, g.pendingInput.lastIndexOf(Input()));
|
2020-05-28 07:46:41 +02:00
|
|
|
const QList<Input> inputs = g.pendingInput.mid(i);
|
2013-03-17 12:39:57 +01:00
|
|
|
clearPendingInput();
|
|
|
|
|
g.pendingInput.append(inputs);
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr("Recursive mapping"));
|
2013-03-17 12:39:57 +01:00
|
|
|
return;
|
2012-09-01 07:47:25 +02:00
|
|
|
}
|
2013-03-17 12:39:57 +01:00
|
|
|
|
|
|
|
|
++g.mapDepth;
|
|
|
|
|
g.pendingInput.prepend(Input());
|
|
|
|
|
prependInputs(inputs);
|
|
|
|
|
g.commandBuffer.setHistoryAutoSave(false);
|
2013-12-29 09:26:15 +01:00
|
|
|
|
|
|
|
|
// start new edit block (undo/redo) only if necessary
|
2014-04-30 17:32:25 +02:00
|
|
|
bool editBlock = m_buffer->editBlockLevel == 0 && !(isInsertMode() && isInsertStateValid());
|
2013-12-29 09:26:15 +01:00
|
|
|
if (editBlock)
|
|
|
|
|
beginLargeEditBlock();
|
|
|
|
|
g.mapStates << MappingState(inputs.noremap(), inputs.silent(), editBlock);
|
2013-03-17 12:39:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::expandCompleteMapping()
|
|
|
|
|
{
|
|
|
|
|
if (!g.currentMap.isComplete())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const Inputs &inputs = g.currentMap.inputs();
|
|
|
|
|
int usedInputs = g.currentMap.mapLength();
|
|
|
|
|
prependInputs(g.currentMap.currentInputs().mid(usedInputs));
|
|
|
|
|
prependMapping(inputs);
|
2012-09-01 07:47:25 +02:00
|
|
|
g.currentMap.reset();
|
2013-03-17 12:39:57 +01:00
|
|
|
|
|
|
|
|
return true;
|
2010-03-26 13:22:06 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-17 12:39:57 +01:00
|
|
|
bool FakeVimHandler::Private::extendMapping(const Input &input)
|
2012-11-06 17:29:04 +01:00
|
|
|
{
|
2013-03-17 12:39:57 +01:00
|
|
|
if (!g.currentMap.isValid())
|
|
|
|
|
g.currentMap.reset(currentModeCode());
|
|
|
|
|
return g.currentMap.walk(input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::endMapping()
|
|
|
|
|
{
|
|
|
|
|
if (!g.currentMap.canExtend())
|
|
|
|
|
--g.mapDepth;
|
2013-03-16 14:28:54 +01:00
|
|
|
if (g.mapStates.isEmpty())
|
2012-11-06 17:29:04 +01:00
|
|
|
return;
|
2013-12-29 09:26:15 +01:00
|
|
|
if (g.mapStates.last().editBlock)
|
|
|
|
|
endEditBlock();
|
2012-11-06 17:29:04 +01:00
|
|
|
g.mapStates.pop_back();
|
2013-03-16 14:28:54 +01:00
|
|
|
if (g.mapStates.isEmpty())
|
2012-11-06 17:29:04 +01:00
|
|
|
g.commandBuffer.setHistoryAutoSave(true);
|
2013-03-17 12:39:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::canHandleMapping()
|
|
|
|
|
{
|
2013-04-02 17:56:12 +02:00
|
|
|
// Don't handle user mapping in sub-modes that cannot be followed by movement and in "noremap".
|
2013-05-06 20:02:22 +02:00
|
|
|
return g.subsubmode == NoSubSubMode
|
|
|
|
|
&& g.submode != RegisterSubMode
|
|
|
|
|
&& g.submode != WindowSubMode
|
|
|
|
|
&& g.submode != ZSubMode
|
|
|
|
|
&& g.submode != CapitalZSubMode
|
|
|
|
|
&& g.submode != ReplaceSubMode
|
|
|
|
|
&& g.submode != MacroRecordSubMode
|
|
|
|
|
&& g.submode != MacroExecuteSubMode
|
2013-03-17 12:39:57 +01:00
|
|
|
&& (g.mapStates.isEmpty() || !g.mapStates.last().noremap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::clearPendingInput()
|
|
|
|
|
{
|
|
|
|
|
// Clear pending input on interrupt or bad mapping.
|
|
|
|
|
g.pendingInput.clear();
|
|
|
|
|
g.mapStates.clear();
|
|
|
|
|
g.mapDepth = 0;
|
|
|
|
|
|
|
|
|
|
// Clear all started edit blocks.
|
2014-04-30 17:32:25 +02:00
|
|
|
while (m_buffer->editBlockLevel > 0)
|
2013-03-17 12:39:57 +01:00
|
|
|
endEditBlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::waitForMapping()
|
|
|
|
|
{
|
|
|
|
|
g.currentCommand.clear();
|
|
|
|
|
foreach (const Input &input, g.currentMap.currentInputs())
|
|
|
|
|
g.currentCommand.append(input.toString());
|
|
|
|
|
|
|
|
|
|
// wait for user to press any key or trigger complete mapping after interval
|
2014-11-16 10:53:15 +01:00
|
|
|
m_inputTimer.start();
|
2013-03-17 12:39:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventResult FakeVimHandler::Private::stopWaitForMapping(bool hasInput)
|
|
|
|
|
{
|
2014-11-20 21:07:14 +01:00
|
|
|
if (!hasInput || m_inputTimer.isActive()) {
|
2014-11-16 10:53:15 +01:00
|
|
|
m_inputTimer.stop();
|
2013-03-17 12:39:57 +01:00
|
|
|
g.currentCommand.clear();
|
|
|
|
|
if (!hasInput && !expandCompleteMapping()) {
|
|
|
|
|
// Cannot complete mapping so handle the first input from it as default command.
|
|
|
|
|
return handleCurrentMapAsDefault();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return EventHandled;
|
2012-11-06 17:29:04 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-21 17:23:30 +01:00
|
|
|
void FakeVimHandler::Private::stopIncrementalFind()
|
|
|
|
|
{
|
2012-10-03 16:40:05 +02:00
|
|
|
if (g.findPending) {
|
|
|
|
|
g.findPending = false;
|
2013-04-08 20:06:17 +02:00
|
|
|
setAnchorAndPosition(m_findStartPosition, m_cursor.selectionStart());
|
2010-10-26 10:56:32 +02:00
|
|
|
finishMovement();
|
|
|
|
|
setAnchor();
|
2010-01-21 17:23:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-10 22:10:23 +02:00
|
|
|
void FakeVimHandler::Private::updateFind(bool isComplete)
|
|
|
|
|
{
|
|
|
|
|
if (!isComplete && !hasConfig(ConfigIncSearch))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
g.currentMessage.clear();
|
|
|
|
|
|
2012-11-10 10:36:05 +01:00
|
|
|
const QString &needle = g.searchBuffer.contents();
|
2013-09-15 16:19:05 +02:00
|
|
|
if (isComplete) {
|
|
|
|
|
setPosition(m_searchStartPosition);
|
|
|
|
|
if (!needle.isEmpty())
|
|
|
|
|
recordJump();
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-10 22:10:23 +02:00
|
|
|
SearchData sd;
|
2012-11-10 10:36:05 +01:00
|
|
|
sd.needle = needle;
|
2012-10-03 16:40:05 +02:00
|
|
|
sd.forward = g.lastSearchForward;
|
2012-09-10 22:10:23 +02:00
|
|
|
sd.highlightMatches = isComplete;
|
|
|
|
|
search(sd, isComplete);
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
void FakeVimHandler::Private::resetCount()
|
|
|
|
|
{
|
|
|
|
|
g.mvcount = 0;
|
|
|
|
|
g.opcount = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-17 13:56:42 +01:00
|
|
|
bool FakeVimHandler::Private::isInputCount(const Input &input) const
|
|
|
|
|
{
|
2013-05-06 20:02:22 +02:00
|
|
|
return input.isDigit() && (!input.is('0') || g.mvcount > 0);
|
2013-02-17 13:56:42 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-09 16:37:23 +01:00
|
|
|
bool FakeVimHandler::Private::atEmptyLine(int pos) const
|
|
|
|
|
{
|
|
|
|
|
return blockAt(pos).length() == 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-19 19:08:04 +02:00
|
|
|
bool FakeVimHandler::Private::atEmptyLine(const QTextCursor &tc) const
|
|
|
|
|
{
|
2014-11-09 16:37:23 +01:00
|
|
|
return atEmptyLine(tc.position());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::atEmptyLine() const
|
|
|
|
|
{
|
|
|
|
|
return atEmptyLine(position());
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::atBoundary(bool end, bool simple, bool onlyWords,
|
|
|
|
|
const QTextCursor &tc) const
|
|
|
|
|
{
|
|
|
|
|
if (tc.isNull())
|
2013-04-08 20:06:17 +02:00
|
|
|
return atBoundary(end, simple, onlyWords, m_cursor);
|
2012-08-19 19:08:04 +02:00
|
|
|
if (atEmptyLine(tc))
|
|
|
|
|
return true;
|
|
|
|
|
int pos = tc.position();
|
2014-11-17 15:32:00 +01:00
|
|
|
QChar c1 = characterAt(pos);
|
|
|
|
|
QChar c2 = characterAt(pos + (end ? 1 : -1));
|
2012-08-19 19:08:04 +02:00
|
|
|
int thisClass = charClass(c1, simple);
|
|
|
|
|
return (!onlyWords || thisClass != 0)
|
|
|
|
|
&& (c2.isNull() || c2 == ParagraphSeparator || thisClass != charClass(c2, simple));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::atWordBoundary(bool end, bool simple, const QTextCursor &tc) const
|
|
|
|
|
{
|
|
|
|
|
return atBoundary(end, simple, true, tc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::atWordStart(bool simple, const QTextCursor &tc) const
|
|
|
|
|
{
|
|
|
|
|
return atWordBoundary(false, simple, tc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::atWordEnd(bool simple, const QTextCursor &tc) const
|
|
|
|
|
{
|
|
|
|
|
return atWordBoundary(true, simple, tc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::isFirstNonBlankOnLine(int pos)
|
|
|
|
|
{
|
2014-11-09 16:37:23 +01:00
|
|
|
for (int i = blockAt(pos).position(); i < pos; ++i) {
|
2012-08-19 19:08:04 +02:00
|
|
|
if (!document()->characterAt(i).isSpace())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-07 10:37:34 +02:00
|
|
|
void FakeVimHandler::Private::pushUndoState(bool overwrite)
|
2009-11-30 13:58:57 +01:00
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->editBlockLevel != 0 && m_buffer->undoState.isValid())
|
2013-04-07 10:37:34 +02:00
|
|
|
return; // No need to save undo state for inner edit blocks.
|
|
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->undoState.isValid() && !overwrite)
|
2013-04-08 21:25:29 +02:00
|
|
|
return;
|
2012-08-26 18:39:48 +02:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
UNDO_DEBUG("PUSH UNDO");
|
2012-08-26 18:39:48 +02:00
|
|
|
int pos = position();
|
2013-04-08 21:25:29 +02:00
|
|
|
if (!isInsertMode()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
if (isVisualMode() || g.submode == DeleteSubMode
|
|
|
|
|
|| (g.submode == ChangeSubMode && g.movetype != MoveLineWise)) {
|
2012-08-26 18:39:48 +02:00
|
|
|
pos = qMin(pos, anchor());
|
|
|
|
|
if (isVisualLineMode())
|
|
|
|
|
pos = firstPositionInLine(lineForPosition(pos));
|
2014-11-16 13:04:03 +01:00
|
|
|
else if (isVisualBlockMode())
|
|
|
|
|
pos = blockAt(pos).position() + qMin(columnAt(anchor()), columnAt(position()));
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.movetype == MoveLineWise && hasConfig(ConfigStartOfLine)) {
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode
|
|
|
|
|
|| g.submode == IndentSubMode) {
|
2012-09-29 15:36:18 +02:00
|
|
|
pos = qMin(pos, anchor());
|
|
|
|
|
}
|
|
|
|
|
tc.setPosition(pos);
|
2012-08-26 18:39:48 +02:00
|
|
|
moveToFirstNonBlankOnLine(&tc);
|
|
|
|
|
pos = qMin(pos, tc.position());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-14 18:51:21 +01:00
|
|
|
CursorPosition lastChangePosition(document(), pos);
|
2016-03-18 12:10:24 +01:00
|
|
|
setMark('.', lastChangePosition);
|
2014-11-14 18:51:21 +01:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->redo.clear();
|
2014-11-14 18:51:21 +01:00
|
|
|
m_buffer->undoState = State(
|
|
|
|
|
revision(), lastChangePosition, m_buffer->marks,
|
|
|
|
|
m_buffer->lastVisualMode, m_buffer->lastVisualModeInverted);
|
2009-11-30 13:58:57 +01:00
|
|
|
}
|
|
|
|
|
|
2009-04-08 16:05:24 +02:00
|
|
|
void FakeVimHandler::Private::moveDown(int n)
|
|
|
|
|
{
|
2013-05-09 10:04:40 +02:00
|
|
|
if (n == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextBlock block = m_cursor.block();
|
2012-10-05 17:33:20 +02:00
|
|
|
const int col = position() - block.position();
|
|
|
|
|
|
2013-05-09 10:04:40 +02:00
|
|
|
int lines = qAbs(n);
|
|
|
|
|
int position = 0;
|
|
|
|
|
while (block.isValid()) {
|
|
|
|
|
position = block.position() + qMax(0, qMin(block.length() - 2, col));
|
|
|
|
|
if (block.isVisible()) {
|
|
|
|
|
--lines;
|
|
|
|
|
if (lines < 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
block = n > 0 ? nextLine(block) : previousLine(block);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setPosition(position);
|
2009-04-09 15:44:51 +02:00
|
|
|
moveToTargetColumn();
|
2013-08-12 19:43:08 +02:00
|
|
|
updateScrollOffset();
|
2009-04-08 16:05:24 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-16 18:54:54 +02:00
|
|
|
void FakeVimHandler::Private::moveDownVisually(int n)
|
|
|
|
|
{
|
|
|
|
|
const QTextCursor::MoveOperation moveOperation = (n > 0) ? Down : Up;
|
|
|
|
|
int count = qAbs(n);
|
|
|
|
|
int oldPos = m_cursor.position();
|
|
|
|
|
|
|
|
|
|
while (count > 0) {
|
|
|
|
|
m_cursor.movePosition(moveOperation, KeepAnchor, 1);
|
|
|
|
|
if (oldPos == m_cursor.position())
|
|
|
|
|
break;
|
|
|
|
|
oldPos = m_cursor.position();
|
|
|
|
|
QTextBlock block = m_cursor.block();
|
|
|
|
|
if (block.isVisible())
|
|
|
|
|
--count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor tc = m_cursor;
|
|
|
|
|
tc.movePosition(StartOfLine);
|
|
|
|
|
const int minPos = tc.position();
|
2013-10-17 19:01:29 +02:00
|
|
|
moveToEndOfLineVisually(&tc);
|
|
|
|
|
const int maxPos = tc.position();
|
2013-10-16 18:54:54 +02:00
|
|
|
|
|
|
|
|
if (m_targetColumn == -1) {
|
|
|
|
|
setPosition(maxPos);
|
|
|
|
|
} else {
|
|
|
|
|
setPosition(qMin(maxPos, minPos + m_targetColumnWrapped));
|
|
|
|
|
const int targetColumn = m_targetColumnWrapped;
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
m_targetColumnWrapped = targetColumn;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-11 18:35:41 +01:00
|
|
|
if (!isInsertMode() && atEndOfLine())
|
|
|
|
|
m_cursor.movePosition(Left, KeepAnchor);
|
|
|
|
|
|
2013-10-16 18:54:54 +02:00
|
|
|
updateScrollOffset();
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-06 14:51:50 +02:00
|
|
|
void FakeVimHandler::Private::movePageDown(int count)
|
|
|
|
|
{
|
|
|
|
|
const int scrollOffset = windowScrollOffset();
|
|
|
|
|
const int screenLines = linesOnScreen();
|
|
|
|
|
const int offset = count > 0 ? scrollOffset - 2 : screenLines - scrollOffset + 2;
|
|
|
|
|
const int value = count * screenLines - cursorLineOnScreen() + offset;
|
|
|
|
|
moveDown(value);
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
scrollToLine(cursorLine());
|
|
|
|
|
else
|
2013-04-08 20:06:17 +02:00
|
|
|
scrollToLine(qMax(0, cursorLine() - screenLines + 1));
|
2013-04-06 14:51:50 +02:00
|
|
|
}
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
void FakeVimHandler::Private::commitCursor()
|
2014-11-04 18:53:44 +01:00
|
|
|
{
|
2014-11-10 18:27:38 +01:00
|
|
|
QTextCursor tc = m_cursor;
|
2014-11-04 18:53:44 +01:00
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
if (isVisualMode()) {
|
|
|
|
|
int pos = tc.position();
|
|
|
|
|
int anc = tc.anchor();
|
2014-11-04 18:53:44 +01:00
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
if (isVisualBlockMode()) {
|
|
|
|
|
const int col1 = columnAt(anc);
|
|
|
|
|
const int col2 = columnAt(pos);
|
|
|
|
|
if (col1 > col2)
|
|
|
|
|
++anc;
|
|
|
|
|
else if (!tc.atBlockEnd())
|
|
|
|
|
++pos;
|
|
|
|
|
// FIXME: After '$' command (i.e. m_visualTargetColumn == -1), end of selected lines
|
|
|
|
|
// should be selected.
|
|
|
|
|
} else if (isVisualLineMode()) {
|
|
|
|
|
const int posLine = lineForPosition(pos);
|
|
|
|
|
const int ancLine = lineForPosition(anc);
|
|
|
|
|
if (anc < pos) {
|
|
|
|
|
pos = lastPositionInLine(posLine);
|
|
|
|
|
anc = firstPositionInLine(ancLine);
|
|
|
|
|
} else {
|
|
|
|
|
pos = firstPositionInLine(posLine);
|
|
|
|
|
anc = lastPositionInLine(ancLine) + 1;
|
|
|
|
|
}
|
|
|
|
|
// putting cursor on folded line will unfold the line, so move the cursor a bit
|
|
|
|
|
if (!blockAt(pos).isVisible())
|
|
|
|
|
++pos;
|
|
|
|
|
} else if (isVisualCharMode()) {
|
|
|
|
|
if (anc > pos)
|
|
|
|
|
++anc;
|
2014-11-16 10:53:15 +01:00
|
|
|
else if (!editor()->hasFocus() || isCommandLineMode())
|
|
|
|
|
m_fixCursorTimer.start();
|
2014-11-10 18:27:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tc.setPosition(anc);
|
|
|
|
|
tc.setPosition(pos, KeepAnchor);
|
|
|
|
|
} else if (g.subsubmode == SearchSubSubMode && !m_searchCursor.isNull()) {
|
|
|
|
|
tc = m_searchCursor;
|
|
|
|
|
} else {
|
|
|
|
|
tc.clearSelection();
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-15 09:19:04 +01:00
|
|
|
updateCursorShape();
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
if (isVisualBlockMode()) {
|
2018-02-09 18:49:18 +01:00
|
|
|
q->requestSetBlockSelection(tc);
|
2014-11-10 18:27:38 +01:00
|
|
|
} else {
|
2018-02-09 18:49:18 +01:00
|
|
|
q->requestDisableBlockSelection();
|
2014-11-10 18:27:38 +01:00
|
|
|
if (editor())
|
|
|
|
|
EDITOR(setTextCursor(tc));
|
2014-11-04 18:53:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
void FakeVimHandler::Private::pullCursor()
|
|
|
|
|
{
|
|
|
|
|
if (!m_cursorNeedsUpdate)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_cursorNeedsUpdate = false;
|
|
|
|
|
|
|
|
|
|
QTextCursor oldCursor = m_cursor;
|
|
|
|
|
|
|
|
|
|
bool visualBlockMode = false;
|
2018-02-09 18:49:18 +01:00
|
|
|
q->requestHasBlockSelection(&visualBlockMode);
|
2014-11-10 18:27:38 +01:00
|
|
|
|
|
|
|
|
if (visualBlockMode)
|
2018-02-09 18:49:18 +01:00
|
|
|
q->requestBlockSelection(&m_cursor);
|
2014-11-10 18:27:38 +01:00
|
|
|
else if (editor())
|
2015-05-16 09:21:36 +02:00
|
|
|
m_cursor = editorCursor();
|
2014-11-10 18:27:38 +01:00
|
|
|
|
|
|
|
|
// Cursor should be always valid.
|
|
|
|
|
if (m_cursor.isNull())
|
|
|
|
|
m_cursor = QTextCursor(document());
|
|
|
|
|
|
|
|
|
|
if (visualBlockMode)
|
|
|
|
|
g.visualMode = VisualBlockMode;
|
|
|
|
|
else if (m_cursor.hasSelection())
|
|
|
|
|
g.visualMode = VisualCharMode;
|
|
|
|
|
else
|
|
|
|
|
g.visualMode = NoVisualMode;
|
|
|
|
|
|
2014-11-16 10:53:15 +01:00
|
|
|
// Keep visually the text selection same.
|
|
|
|
|
// With thick text cursor, the character under cursor is treated as selected.
|
|
|
|
|
if (isVisualCharMode() && hasThinCursor())
|
|
|
|
|
moveLeft();
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
// Cursor position can be after the end of line only in some modes.
|
|
|
|
|
if (atEndOfLine() && !isVisualMode() && !isInsertMode())
|
|
|
|
|
moveLeft();
|
|
|
|
|
|
|
|
|
|
// Record external jump to different line.
|
|
|
|
|
if (lineForPosition(position()) != lineForPosition(oldCursor.position()))
|
|
|
|
|
recordJump(oldCursor.position());
|
|
|
|
|
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 09:21:36 +02:00
|
|
|
QTextCursor FakeVimHandler::Private::editorCursor() const
|
|
|
|
|
{
|
|
|
|
|
QTextCursor tc = EDITOR(textCursor());
|
|
|
|
|
tc.setVisualNavigation(false);
|
|
|
|
|
return tc;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-26 18:50:19 +01:00
|
|
|
bool FakeVimHandler::Private::moveToNextParagraph(int count)
|
|
|
|
|
{
|
|
|
|
|
const bool forward = count > 0;
|
|
|
|
|
int repeat = forward ? count : -count;
|
|
|
|
|
QTextBlock block = this->block();
|
|
|
|
|
|
|
|
|
|
if (block.isValid() && block.length() == 1)
|
|
|
|
|
++repeat;
|
|
|
|
|
|
|
|
|
|
for (; block.isValid(); block = forward ? block.next() : block.previous()) {
|
|
|
|
|
if (block.length() == 1) {
|
|
|
|
|
if (--repeat == 0)
|
|
|
|
|
break;
|
|
|
|
|
while (block.isValid() && block.length() == 1)
|
|
|
|
|
block = forward ? block.next() : block.previous();
|
2014-11-20 19:57:16 +01:00
|
|
|
if (!block.isValid())
|
|
|
|
|
break;
|
2012-11-26 18:50:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-20 19:57:16 +01:00
|
|
|
if (!block.isValid())
|
|
|
|
|
--repeat;
|
|
|
|
|
|
|
|
|
|
if (repeat > 0)
|
2012-11-26 18:50:19 +01:00
|
|
|
return false;
|
|
|
|
|
|
2014-11-20 19:57:16 +01:00
|
|
|
if (block.isValid())
|
|
|
|
|
setPosition(block.position());
|
|
|
|
|
else
|
|
|
|
|
setPosition(forward ? lastPositionInDocument() : 0);
|
2012-11-26 18:50:19 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-20 19:57:16 +01:00
|
|
|
void FakeVimHandler::Private::moveToParagraphStartOrEnd(int direction)
|
|
|
|
|
{
|
|
|
|
|
bool emptyLine = atEmptyLine();
|
|
|
|
|
int oldPos = -1;
|
|
|
|
|
|
|
|
|
|
while (atEmptyLine() == emptyLine && oldPos != position()) {
|
|
|
|
|
oldPos = position();
|
|
|
|
|
moveDown(direction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (oldPos != position())
|
|
|
|
|
moveUp(direction);
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-08 16:05:24 +02:00
|
|
|
void FakeVimHandler::Private::moveToEndOfLine()
|
|
|
|
|
{
|
2012-10-05 17:33:20 +02:00
|
|
|
// Additionally select (in visual mode) or apply current command on hidden lines following
|
|
|
|
|
// the current line.
|
2013-05-06 20:02:22 +02:00
|
|
|
bool onlyVisibleLines = isVisualMode() || g.submode != NoSubMode;
|
2012-10-05 17:33:20 +02:00
|
|
|
const int id = onlyVisibleLines ? lineNumber(block()) : block().blockNumber() + 1;
|
|
|
|
|
setPosition(lastPositionInLine(id, onlyVisibleLines));
|
2013-10-17 19:01:29 +02:00
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToEndOfLineVisually()
|
|
|
|
|
{
|
|
|
|
|
moveToEndOfLineVisually(&m_cursor);
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToEndOfLineVisually(QTextCursor *tc)
|
|
|
|
|
{
|
|
|
|
|
// Moving to end of line ends up on following line if the line is wrapped.
|
|
|
|
|
tc->movePosition(StartOfLine);
|
|
|
|
|
const int minPos = tc->position();
|
|
|
|
|
tc->movePosition(EndOfLine);
|
|
|
|
|
int maxPos = tc->position();
|
|
|
|
|
tc->movePosition(StartOfLine);
|
|
|
|
|
if (minPos != tc->position())
|
|
|
|
|
--maxPos;
|
|
|
|
|
tc->setPosition(maxPos);
|
2009-04-08 16:05:24 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-03 11:22:18 +02:00
|
|
|
void FakeVimHandler::Private::moveBehindEndOfLine()
|
|
|
|
|
{
|
2018-02-09 18:49:18 +01:00
|
|
|
q->fold(1, false);
|
2010-09-13 15:23:20 +02:00
|
|
|
int pos = qMin(block().position() + block().length() - 1,
|
2012-10-13 09:33:30 +02:00
|
|
|
lastPositionInDocument() + 1);
|
2009-07-03 11:22:18 +02:00
|
|
|
setPosition(pos);
|
2014-11-04 18:53:44 +01:00
|
|
|
setTargetColumn();
|
2009-07-03 11:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
2009-04-16 09:18:09 +02:00
|
|
|
void FakeVimHandler::Private::moveToStartOfLine()
|
|
|
|
|
{
|
2010-09-13 15:23:20 +02:00
|
|
|
setPosition(block().position());
|
2013-10-17 19:01:29 +02:00
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToStartOfLineVisually()
|
|
|
|
|
{
|
|
|
|
|
m_cursor.movePosition(StartOfLine, KeepAnchor);
|
|
|
|
|
setTargetColumn();
|
2009-04-16 09:18:09 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-13 09:33:30 +02:00
|
|
|
void FakeVimHandler::Private::fixSelection()
|
|
|
|
|
{
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.rangemode == RangeBlockMode)
|
2012-11-07 17:34:52 +01:00
|
|
|
return;
|
|
|
|
|
|
2013-06-05 14:19:43 +02:00
|
|
|
if (g.movetype == MoveInclusive) {
|
2013-05-14 20:35:31 +02:00
|
|
|
// If position or anchor is after end of non-empty line, include line break in selection.
|
2014-11-17 15:32:00 +01:00
|
|
|
if (characterAtCursor() == ParagraphSeparator) {
|
2014-11-22 11:52:44 +01:00
|
|
|
if (!atEmptyLine() && !atDocumentEnd()) {
|
2013-05-14 20:35:31 +02:00
|
|
|
setPosition(position() + 1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-11-17 15:32:00 +01:00
|
|
|
} else if (characterAt(anchor()) == ParagraphSeparator) {
|
2013-05-14 20:35:31 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
|
|
|
|
tc.setPosition(anchor());
|
|
|
|
|
if (!atEmptyLine(tc)) {
|
|
|
|
|
setAnchorAndPosition(anchor() + 1, position());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-05 19:14:18 +02:00
|
|
|
if (g.movetype == MoveExclusive && g.subsubmode == NoSubSubMode) {
|
2012-10-29 17:36:35 +01:00
|
|
|
if (anchor() < position() && atBlockStart()) {
|
2014-03-04 00:12:11 +01:00
|
|
|
// Exclusive motion ending at the beginning of line
|
2012-10-13 09:33:30 +02:00
|
|
|
// becomes inclusive and end is moved to end of previous line.
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveInclusive;
|
2012-10-13 09:33:30 +02:00
|
|
|
moveToStartOfLine();
|
|
|
|
|
moveLeft();
|
|
|
|
|
|
|
|
|
|
// Exclusive motion ending at the beginning of line and
|
|
|
|
|
// starting at or before first non-blank on a line becomes linewise.
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (anchor() < block().position() && isFirstNonBlankOnLine(anchor()))
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveLineWise;
|
2012-10-13 09:33:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.movetype == MoveLineWise)
|
|
|
|
|
g.rangemode = (g.submode == ChangeSubMode)
|
2012-10-13 09:33:30 +02:00
|
|
|
? RangeLineModeExclusive
|
|
|
|
|
: RangeLineMode;
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.movetype == MoveInclusive) {
|
2012-10-13 09:33:30 +02:00
|
|
|
if (anchor() <= position()) {
|
|
|
|
|
if (!atBlockEnd())
|
|
|
|
|
setPosition(position() + 1); // correction
|
|
|
|
|
|
|
|
|
|
// Omit first character in selection if it's line break on non-empty line.
|
|
|
|
|
int start = anchor();
|
|
|
|
|
int end = position();
|
2012-11-18 19:58:02 +01:00
|
|
|
if (afterEndOfLine(document(), start) && start > 0) {
|
2012-10-13 09:33:30 +02:00
|
|
|
start = qMin(start + 1, end);
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.submode == DeleteSubMode && !atDocumentEnd())
|
2012-10-13 09:33:30 +02:00
|
|
|
setAnchorAndPosition(start, end + 1);
|
|
|
|
|
else
|
|
|
|
|
setAnchorAndPosition(start, end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If more than one line is selected and all are selected completely
|
|
|
|
|
// movement becomes linewise.
|
|
|
|
|
if (start < block().position() && isFirstNonBlankOnLine(start) && atBlockEnd()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.submode != ChangeSubMode) {
|
2012-10-13 09:33:30 +02:00
|
|
|
moveRight();
|
|
|
|
|
if (atEmptyLine())
|
|
|
|
|
moveRight();
|
|
|
|
|
}
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveLineWise;
|
2012-10-13 09:33:30 +02:00
|
|
|
}
|
2013-02-17 14:14:06 +01:00
|
|
|
} else if (!m_anchorPastEnd) {
|
2012-10-13 09:33:30 +02:00
|
|
|
setAnchorAndPosition(anchor() + 1, position());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_positionPastEnd) {
|
|
|
|
|
moveBehindEndOfLine();
|
|
|
|
|
moveRight();
|
2013-05-06 20:02:22 +02:00
|
|
|
setAnchorAndPosition(anchor(), position());
|
2012-10-13 09:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (m_anchorPastEnd) {
|
|
|
|
|
const int pos = position();
|
|
|
|
|
setPosition(anchor());
|
|
|
|
|
moveBehindEndOfLine();
|
|
|
|
|
moveRight();
|
|
|
|
|
setAnchorAndPosition(position(), pos);
|
|
|
|
|
}
|
2012-10-13 09:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
2013-09-15 16:19:05 +02:00
|
|
|
bool FakeVimHandler::Private::finishSearch()
|
|
|
|
|
{
|
|
|
|
|
if (g.lastSearch.isEmpty()
|
|
|
|
|
|| (!g.currentMessage.isEmpty() && g.currentMessageLevel == MessageError)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (g.submode != NoSubMode)
|
|
|
|
|
setAnchorAndPosition(m_searchStartPosition, position());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-28 17:01:24 +02:00
|
|
|
void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2010-09-14 14:04:13 +02:00
|
|
|
//dump("FINISH MOVEMENT");
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.submode == FilterSubMode) {
|
2009-01-16 16:15:01 +01:00
|
|
|
int beginLine = lineForPosition(anchor());
|
|
|
|
|
int endLine = lineForPosition(position());
|
2009-03-06 11:22:16 +01:00
|
|
|
setPosition(qMin(anchor(), position()));
|
2016-03-18 12:10:24 +01:00
|
|
|
enterExMode(QString(".,+%1!").arg(qAbs(endLine - beginLine)));
|
2009-01-08 17:21:51 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.submode == ChangeSubMode
|
|
|
|
|
|| g.submode == DeleteSubMode
|
|
|
|
|
|| g.submode == YankSubMode
|
|
|
|
|
|| g.submode == InvertCaseSubMode
|
|
|
|
|
|| g.submode == DownCaseSubMode
|
2014-11-17 15:32:33 +01:00
|
|
|
|| g.submode == UpCaseSubMode
|
|
|
|
|
|| g.submode == IndentSubMode
|
|
|
|
|
|| g.submode == ShiftLeftSubMode
|
|
|
|
|
|| g.submode == ShiftRightSubMode)
|
|
|
|
|
{
|
2012-10-13 09:33:30 +02:00
|
|
|
fixSelection();
|
2010-01-21 17:38:31 +01:00
|
|
|
|
2014-11-17 15:32:33 +01:00
|
|
|
if (g.submode == ChangeSubMode
|
|
|
|
|
|| g.submode == DeleteSubMode
|
|
|
|
|
|| g.submode == YankSubMode)
|
|
|
|
|
{
|
2010-05-12 11:18:18 +02:00
|
|
|
yankText(currentRange(), m_register);
|
2010-01-21 17:38:27 +01:00
|
|
|
}
|
2010-01-21 17:38:28 +01:00
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.submode == ChangeSubMode) {
|
2013-04-08 20:06:17 +02:00
|
|
|
pushUndoState(false);
|
2013-04-08 21:25:29 +02:00
|
|
|
beginEditBlock();
|
2010-05-12 11:18:18 +02:00
|
|
|
removeText(currentRange());
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.movetype == MoveLineWise)
|
2010-01-21 17:38:28 +01:00
|
|
|
insertAutomaticIndentation(true);
|
2010-01-21 17:38:27 +01:00
|
|
|
endEditBlock();
|
2012-11-10 10:36:05 +01:00
|
|
|
setTargetColumn();
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == DeleteSubMode) {
|
2013-04-08 20:06:17 +02:00
|
|
|
pushUndoState(false);
|
2013-04-08 21:25:29 +02:00
|
|
|
beginEditBlock();
|
2012-11-23 17:59:43 +01:00
|
|
|
const int pos = position();
|
|
|
|
|
// Always delete something (e.g. 'dw' on an empty line deletes the line).
|
2013-05-06 20:02:22 +02:00
|
|
|
if (pos == anchor() && g.movetype == MoveInclusive)
|
2012-11-23 17:59:43 +01:00
|
|
|
removeText(Range(pos, pos + 1));
|
|
|
|
|
else
|
|
|
|
|
removeText(currentRange());
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.movetype == MoveLineWise)
|
2010-01-05 18:42:26 +01:00
|
|
|
handleStartOfLine();
|
2010-01-21 17:38:28 +01:00
|
|
|
endEditBlock();
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == YankSubMode) {
|
2014-04-23 17:34:37 +02:00
|
|
|
bool isVisualModeYank = isVisualMode();
|
|
|
|
|
leaveVisualMode();
|
2013-04-08 20:06:17 +02:00
|
|
|
const QTextCursor tc = m_cursor;
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.rangemode == RangeBlockMode) {
|
2012-11-23 19:35:26 +01:00
|
|
|
const int pos1 = tc.block().position();
|
2014-11-09 16:37:23 +01:00
|
|
|
const int pos2 = blockAt(tc.anchor()).position();
|
2012-11-23 19:35:26 +01:00
|
|
|
const int col = qMin(tc.position() - pos1, tc.anchor() - pos2);
|
|
|
|
|
setPosition(qMin(pos1, pos2) + col);
|
|
|
|
|
} else {
|
|
|
|
|
setPosition(qMin(position(), anchor()));
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.rangemode == RangeLineMode) {
|
2014-04-23 17:34:37 +02:00
|
|
|
if (isVisualModeYank)
|
2012-11-26 18:59:51 +01:00
|
|
|
moveToStartOfLine();
|
2014-11-04 20:21:22 +01:00
|
|
|
else
|
|
|
|
|
moveToTargetColumn();
|
2012-11-26 18:59:51 +01:00
|
|
|
}
|
2012-11-23 19:35:26 +01:00
|
|
|
}
|
2012-12-20 18:12:10 +01:00
|
|
|
setTargetColumn();
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == InvertCaseSubMode
|
|
|
|
|
|| g.submode == UpCaseSubMode
|
|
|
|
|
|| g.submode == DownCaseSubMode) {
|
2013-04-08 21:25:29 +02:00
|
|
|
beginEditBlock();
|
2014-11-16 14:17:11 +01:00
|
|
|
if (g.submode == InvertCaseSubMode)
|
2010-05-12 11:18:18 +02:00
|
|
|
invertCase(currentRange());
|
2014-11-16 14:17:11 +01:00
|
|
|
else if (g.submode == DownCaseSubMode)
|
2010-05-12 11:18:18 +02:00
|
|
|
downCase(currentRange());
|
2014-11-16 14:17:11 +01:00
|
|
|
else if (g.submode == UpCaseSubMode)
|
2012-10-13 09:33:30 +02:00
|
|
|
upCase(currentRange());
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.movetype == MoveLineWise)
|
2010-01-21 17:38:26 +01:00
|
|
|
handleStartOfLine();
|
|
|
|
|
endEditBlock();
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == IndentSubMode
|
|
|
|
|
|| g.submode == ShiftRightSubMode
|
|
|
|
|
|| g.submode == ShiftLeftSubMode) {
|
2009-05-05 09:06:36 +02:00
|
|
|
recordJump();
|
2013-04-08 20:06:17 +02:00
|
|
|
pushUndoState(false);
|
2014-11-16 14:17:11 +01:00
|
|
|
if (g.submode == IndentSubMode)
|
2012-10-13 09:33:30 +02:00
|
|
|
indentSelectedText();
|
2014-11-16 14:17:11 +01:00
|
|
|
else if (g.submode == ShiftRightSubMode)
|
2012-10-13 09:33:30 +02:00
|
|
|
shiftRegionRight(1);
|
2014-11-16 14:17:11 +01:00
|
|
|
else if (g.submode == ShiftLeftSubMode)
|
2012-10-13 09:33:30 +02:00
|
|
|
shiftRegionLeft(1);
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
2009-03-06 11:22:16 +01:00
|
|
|
|
2014-11-16 14:17:11 +01:00
|
|
|
if (!dotCommandMovement.isEmpty()) {
|
|
|
|
|
const QString dotCommand = dotCommandFromSubMode(g.submode);
|
|
|
|
|
if (!dotCommand.isEmpty())
|
|
|
|
|
setDotCommand(dotCommand + dotCommandMovement);
|
|
|
|
|
}
|
2012-10-13 09:33:30 +02:00
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
// Change command continues in insert mode.
|
|
|
|
|
if (g.submode == ChangeSubMode) {
|
2014-11-12 20:18:18 +01:00
|
|
|
clearCurrentMode();
|
2013-07-09 19:39:31 +02:00
|
|
|
enterInsertMode();
|
|
|
|
|
} else {
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
2010-01-21 17:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-12 20:18:18 +01:00
|
|
|
void FakeVimHandler::Private::leaveCurrentMode()
|
2010-01-21 17:38:24 +01:00
|
|
|
{
|
2014-11-12 21:06:36 +01:00
|
|
|
if (isVisualMode())
|
|
|
|
|
enterCommandMode(g.returnToMode);
|
|
|
|
|
else if (g.returnToMode == CommandMode)
|
2013-05-06 20:02:22 +02:00
|
|
|
enterCommandMode();
|
2014-11-12 20:18:18 +01:00
|
|
|
else if (g.returnToMode == InsertMode)
|
|
|
|
|
enterInsertMode();
|
|
|
|
|
else
|
|
|
|
|
enterReplaceMode();
|
|
|
|
|
|
2012-11-24 10:24:48 +01:00
|
|
|
if (isNoVisualMode())
|
|
|
|
|
setAnchor();
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-12 20:18:18 +01:00
|
|
|
void FakeVimHandler::Private::clearCurrentMode()
|
2013-03-16 15:32:16 +01:00
|
|
|
{
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
|
|
|
|
g.movetype = MoveInclusive;
|
|
|
|
|
g.gflag = false;
|
2013-03-16 15:32:16 +01:00
|
|
|
m_register = '"';
|
2013-05-06 20:02:22 +02:00
|
|
|
g.rangemode = RangeCharMode;
|
2013-03-16 15:32:16 +01:00
|
|
|
g.currentCommand.clear();
|
2013-05-06 20:02:22 +02:00
|
|
|
resetCount();
|
2013-03-16 15:32:16 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-06 11:52:05 +01:00
|
|
|
void FakeVimHandler::Private::updateSelection()
|
|
|
|
|
{
|
2012-09-27 21:09:13 +02:00
|
|
|
QList<QTextEdit::ExtraSelection> selections = m_extraSelections;
|
2010-05-10 16:30:46 +02:00
|
|
|
if (hasConfig(ConfigShowMarks)) {
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto it = m_buffer->marks.cbegin(), end = m_buffer->marks.cend(); it != end; ++it) {
|
2010-05-10 16:30:46 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
2013-04-08 20:06:17 +02:00
|
|
|
sel.cursor = m_cursor;
|
2014-02-03 20:47:47 +01:00
|
|
|
setCursorPosition(&sel.cursor, it.value().position(document()));
|
2012-10-23 16:35:13 +02:00
|
|
|
sel.cursor.setPosition(sel.cursor.position(), MoveAnchor);
|
|
|
|
|
sel.cursor.movePosition(Right, KeepAnchor);
|
2013-04-08 20:06:17 +02:00
|
|
|
sel.format = m_cursor.blockCharFormat();
|
2010-05-10 16:30:46 +02:00
|
|
|
sel.format.setForeground(Qt::blue);
|
|
|
|
|
sel.format.setBackground(Qt::green);
|
|
|
|
|
selections.append(sel);
|
|
|
|
|
}
|
2010-05-07 15:17:44 +02:00
|
|
|
}
|
2010-09-14 14:04:13 +02:00
|
|
|
//qDebug() << "SELECTION: " << selections;
|
2018-02-09 18:49:18 +01:00
|
|
|
q->selectionChanged(selections);
|
2009-01-06 11:52:05 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-27 21:09:13 +02:00
|
|
|
void FakeVimHandler::Private::updateHighlights()
|
|
|
|
|
{
|
2013-08-06 17:13:53 +02:00
|
|
|
if (hasConfig(ConfigUseCoreSearch) || !hasConfig(ConfigHlSearch) || g.highlightsCleared) {
|
|
|
|
|
if (m_highlighted.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
m_highlighted.clear();
|
|
|
|
|
} else if (m_highlighted != g.lastNeedle) {
|
|
|
|
|
m_highlighted = g.lastNeedle;
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 18:49:18 +01:00
|
|
|
q->highlightMatches(m_highlighted);
|
2012-09-27 21:09:13 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-27 21:01:05 +01:00
|
|
|
void FakeVimHandler::Private::updateMiniBuffer()
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2010-01-22 13:56:50 +01:00
|
|
|
if (!m_textedit && !m_plaintextedit)
|
|
|
|
|
return;
|
|
|
|
|
|
2008-12-27 21:01:05 +01:00
|
|
|
QString msg;
|
2011-07-15 13:22:38 +02:00
|
|
|
int cursorPos = -1;
|
2012-10-20 18:48:37 +02:00
|
|
|
int anchorPos = -1;
|
2012-09-10 22:10:23 +02:00
|
|
|
MessageLevel messageLevel = MessageMode;
|
2012-09-01 07:47:25 +02:00
|
|
|
|
2013-03-16 14:28:54 +01:00
|
|
|
if (!g.mapStates.isEmpty() && g.mapStates.last().silent && g.currentMessageLevel < MessageInfo)
|
2012-09-17 17:44:05 +02:00
|
|
|
g.currentMessage.clear();
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.passing) {
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "PASSING";
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.subsubmode == SearchSubSubMode) {
|
2012-09-17 17:44:05 +02:00
|
|
|
msg = g.searchBuffer.display();
|
2013-03-16 14:28:54 +01:00
|
|
|
if (g.mapStates.isEmpty()) {
|
2012-09-17 17:44:05 +02:00
|
|
|
cursorPos = g.searchBuffer.cursorPos() + 1;
|
2012-10-20 18:48:37 +02:00
|
|
|
anchorPos = g.searchBuffer.anchorPos() + 1;
|
|
|
|
|
}
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.mode == ExMode) {
|
2012-09-17 17:44:05 +02:00
|
|
|
msg = g.commandBuffer.display();
|
2013-03-16 14:28:54 +01:00
|
|
|
if (g.mapStates.isEmpty()) {
|
2012-09-17 17:44:05 +02:00
|
|
|
cursorPos = g.commandBuffer.cursorPos() + 1;
|
2012-10-20 18:48:37 +02:00
|
|
|
anchorPos = g.commandBuffer.anchorPos() + 1;
|
|
|
|
|
}
|
2012-09-17 17:44:05 +02:00
|
|
|
} else if (!g.currentMessage.isEmpty()) {
|
|
|
|
|
msg = g.currentMessage;
|
|
|
|
|
g.currentMessage.clear();
|
2012-09-10 22:10:23 +02:00
|
|
|
messageLevel = g.currentMessageLevel;
|
2013-03-16 14:28:54 +01:00
|
|
|
} else if (!g.mapStates.isEmpty() && !g.mapStates.last().silent) {
|
2012-09-01 07:47:25 +02:00
|
|
|
// Do not reset previous message when after running a mapped command.
|
|
|
|
|
return;
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && hasConfig(ConfigShowCmd)) {
|
2012-11-09 17:42:52 +01:00
|
|
|
msg = g.currentCommand;
|
|
|
|
|
messageLevel = MessageShowCmd;
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.mode == CommandMode && isVisualMode()) {
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (isVisualCharMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "-- VISUAL --";
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (isVisualLineMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "-- VISUAL LINE --";
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (isVisualBlockMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "VISUAL BLOCK";
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.mode == InsertMode) {
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "-- INSERT --";
|
2015-03-28 21:14:37 +01:00
|
|
|
if (g.submode == CtrlRSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
msg += " ^R";
|
2015-03-28 21:14:37 +01:00
|
|
|
else if (g.submode == CtrlVSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
msg += " ^V";
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.mode == ReplaceMode) {
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "-- REPLACE --";
|
2010-05-18 18:24:00 +02:00
|
|
|
} else {
|
2012-11-10 10:36:05 +01:00
|
|
|
if (g.returnToMode == CommandMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "-- COMMAND --";
|
2012-11-10 10:36:05 +01:00
|
|
|
else if (g.returnToMode == InsertMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "-- (insert) --";
|
2012-11-10 10:36:05 +01:00
|
|
|
else
|
2016-03-18 12:10:24 +01:00
|
|
|
msg = "-- (replace) --";
|
2008-12-27 22:41:47 +01:00
|
|
|
}
|
2009-03-05 11:06:25 +01:00
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if (g.isRecording && msg.startsWith("--"))
|
|
|
|
|
msg.append(' ').append("Recording");
|
2013-03-17 12:41:18 +01:00
|
|
|
|
2018-02-09 18:49:18 +01:00
|
|
|
q->commandBufferChanged(msg, cursorPos, anchorPos, messageLevel);
|
2009-01-07 18:05:45 +01:00
|
|
|
|
|
|
|
|
int linesInDoc = linesInDocument();
|
2010-07-06 10:12:21 +02:00
|
|
|
int l = cursorLine();
|
2009-01-07 18:05:45 +01:00
|
|
|
QString status;
|
2016-03-18 12:10:24 +01:00
|
|
|
const QString pos = QString("%1,%2")
|
2010-07-06 10:12:21 +02:00
|
|
|
.arg(l + 1).arg(physicalCursorColumn() + 1);
|
2008-12-27 21:01:05 +01:00
|
|
|
// FIXME: physical "-" logical
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (linesInDoc != 0)
|
2014-08-25 17:05:11 +02:00
|
|
|
status = Tr::tr("%1%2%").arg(pos, -10).arg(l * 100 / linesInDoc, 4);
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else
|
2014-08-25 17:05:11 +02:00
|
|
|
status = Tr::tr("%1All").arg(pos, -10);
|
2018-02-09 18:49:18 +01:00
|
|
|
q->statusDataChanged(status);
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-10 22:10:23 +02:00
|
|
|
void FakeVimHandler::Private::showMessage(MessageLevel level, const QString &msg)
|
2009-01-08 17:40:27 +01:00
|
|
|
{
|
|
|
|
|
//qDebug() << "MSG: " << msg;
|
2012-09-17 17:44:05 +02:00
|
|
|
g.currentMessage = msg;
|
2012-09-10 22:10:23 +02:00
|
|
|
g.currentMessageLevel = level;
|
2009-01-08 17:40:27 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-15 17:29:30 +01:00
|
|
|
void FakeVimHandler::Private::notImplementedYet()
|
|
|
|
|
{
|
2009-03-20 13:36:54 +01:00
|
|
|
qDebug() << "Not implemented in FakeVim";
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr("Not implemented in FakeVim."));
|
2009-01-15 17:29:30 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-05 15:30:22 +01:00
|
|
|
void FakeVimHandler::Private::passShortcuts(bool enable)
|
|
|
|
|
{
|
2013-05-06 20:02:22 +02:00
|
|
|
g.passing = enable;
|
2010-01-05 15:30:22 +01:00
|
|
|
updateMiniBuffer();
|
|
|
|
|
if (enable)
|
|
|
|
|
QCoreApplication::instance()->installEventFilter(q);
|
|
|
|
|
else
|
|
|
|
|
QCoreApplication::instance()->removeEventFilter(q);
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-07 17:34:52 +01:00
|
|
|
bool FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2012-11-07 17:34:52 +01:00
|
|
|
bool handled = true;
|
2014-11-12 20:18:18 +01:00
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.subsubmode == FtSubSubMode) {
|
|
|
|
|
g.semicolonType = g.subsubdata;
|
|
|
|
|
g.semicolonKey = input.text();
|
2014-11-12 20:18:18 +01:00
|
|
|
handled = handleFfTt(g.semicolonKey);
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = NoSubSubMode;
|
2014-11-12 20:18:18 +01:00
|
|
|
if (handled) {
|
2016-03-18 12:10:24 +01:00
|
|
|
finishMovement(QString("%1%2%3")
|
2010-02-19 13:01:38 +01:00
|
|
|
.arg(count())
|
2013-05-06 20:02:22 +02:00
|
|
|
.arg(g.semicolonType.text())
|
|
|
|
|
.arg(g.semicolonKey));
|
2010-02-19 13:01:38 +01:00
|
|
|
}
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.subsubmode == TextObjectSubSubMode) {
|
2010-04-28 14:00:44 +02:00
|
|
|
if (input.is('w'))
|
2013-05-06 20:02:22 +02:00
|
|
|
selectWordTextObject(g.subsubdata.is('i'));
|
2010-04-28 14:00:44 +02:00
|
|
|
else if (input.is('W'))
|
2013-05-06 20:02:22 +02:00
|
|
|
selectWORDTextObject(g.subsubdata.is('i'));
|
2010-04-28 14:00:44 +02:00
|
|
|
else if (input.is('s'))
|
2013-05-06 20:02:22 +02:00
|
|
|
selectSentenceTextObject(g.subsubdata.is('i'));
|
2010-04-28 14:00:44 +02:00
|
|
|
else if (input.is('p'))
|
2013-05-06 20:02:22 +02:00
|
|
|
selectParagraphTextObject(g.subsubdata.is('i'));
|
2010-04-28 14:00:44 +02:00
|
|
|
else if (input.is('[') || input.is(']'))
|
2014-11-12 20:18:18 +01:00
|
|
|
handled = selectBlockTextObject(g.subsubdata.is('i'), '[', ']');
|
2010-04-28 14:00:44 +02:00
|
|
|
else if (input.is('(') || input.is(')') || input.is('b'))
|
2014-11-12 20:18:18 +01:00
|
|
|
handled = selectBlockTextObject(g.subsubdata.is('i'), '(', ')');
|
2010-04-28 14:00:44 +02:00
|
|
|
else if (input.is('<') || input.is('>'))
|
2014-11-12 20:18:18 +01:00
|
|
|
handled = selectBlockTextObject(g.subsubdata.is('i'), '<', '>');
|
2010-06-03 16:54:45 +02:00
|
|
|
else if (input.is('{') || input.is('}') || input.is('B'))
|
2014-11-12 20:18:18 +01:00
|
|
|
handled = selectBlockTextObject(g.subsubdata.is('i'), '{', '}');
|
2010-04-28 14:00:44 +02:00
|
|
|
else if (input.is('"') || input.is('\'') || input.is('`'))
|
2014-11-12 20:18:18 +01:00
|
|
|
handled = selectQuotedStringTextObject(g.subsubdata.is('i'), input.asChar());
|
2012-11-06 17:29:04 +01:00
|
|
|
else
|
2014-11-12 20:18:18 +01:00
|
|
|
handled = false;
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = NoSubSubMode;
|
2014-11-12 20:18:18 +01:00
|
|
|
if (handled) {
|
2016-03-18 12:10:24 +01:00
|
|
|
finishMovement(QString("%1%2%3")
|
2012-10-13 09:33:30 +02:00
|
|
|
.arg(count())
|
2013-05-06 20:02:22 +02:00
|
|
|
.arg(g.subsubdata.text())
|
2012-10-13 09:33:30 +02:00
|
|
|
.arg(input.text()));
|
|
|
|
|
}
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.subsubmode == MarkSubSubMode) {
|
2013-04-08 20:06:17 +02:00
|
|
|
setMark(input.asChar(), CursorPosition(m_cursor));
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = NoSubSubMode;
|
|
|
|
|
} else if (g.subsubmode == BackTickSubSubMode
|
|
|
|
|
|| g.subsubmode == TickSubSubMode) {
|
2014-11-12 20:18:18 +01:00
|
|
|
handled = jumpToMark(input.asChar(), g.subsubmode == BackTickSubSubMode);
|
|
|
|
|
if (handled)
|
2010-03-19 08:44:57 +01:00
|
|
|
finishMovement();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = NoSubSubMode;
|
|
|
|
|
} else if (g.subsubmode == ZSubSubMode) {
|
2012-11-29 19:04:52 +01:00
|
|
|
handled = false;
|
|
|
|
|
if (input.is('j') || input.is('k')) {
|
|
|
|
|
int pos = position();
|
2018-02-09 18:49:18 +01:00
|
|
|
q->foldGoTo(input.is('j') ? count() : -count(), false);
|
2012-11-29 19:04:52 +01:00
|
|
|
if (pos != position()) {
|
|
|
|
|
handled = true;
|
2016-03-18 12:10:24 +01:00
|
|
|
finishMovement(QString("%1z%2")
|
2012-11-29 19:04:52 +01:00
|
|
|
.arg(count())
|
|
|
|
|
.arg(input.text()));
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-19 18:33:26 +03:00
|
|
|
} else if (g.subsubmode == OpenSquareSubSubMode || g.subsubmode == CloseSquareSubSubMode) {
|
2012-11-29 20:14:56 +01:00
|
|
|
int pos = position();
|
2013-08-05 19:14:18 +02:00
|
|
|
if (input.is('{') && g.subsubmode == OpenSquareSubSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
searchBalanced(false, '{', '}');
|
2013-08-05 19:14:18 +02:00
|
|
|
else if (input.is('}') && g.subsubmode == CloseSquareSubSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
searchBalanced(true, '}', '{');
|
2013-08-05 19:14:18 +02:00
|
|
|
else if (input.is('(') && g.subsubmode == OpenSquareSubSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
searchBalanced(false, '(', ')');
|
2013-08-05 19:14:18 +02:00
|
|
|
else if (input.is(')') && g.subsubmode == CloseSquareSubSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
searchBalanced(true, ')', '(');
|
2013-08-05 19:14:18 +02:00
|
|
|
else if (input.is('[') && g.subsubmode == OpenSquareSubSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
bracketSearchBackward(&m_cursor, "^\\{", count());
|
2013-08-05 19:14:18 +02:00
|
|
|
else if (input.is('[') && g.subsubmode == CloseSquareSubSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
bracketSearchForward(&m_cursor, "^\\}", count(), false);
|
2013-08-05 19:14:18 +02:00
|
|
|
else if (input.is(']') && g.subsubmode == OpenSquareSubSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
bracketSearchBackward(&m_cursor, "^\\}", count());
|
2013-08-05 19:14:18 +02:00
|
|
|
else if (input.is(']') && g.subsubmode == CloseSquareSubSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
bracketSearchForward(&m_cursor, "^\\{", count(), g.submode != NoSubMode);
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (input.is('z'))
|
2018-02-09 18:49:18 +01:00
|
|
|
q->foldGoTo(g.subsubmode == OpenSquareSubSubMode ? -count() : count(), true);
|
2012-11-29 20:14:56 +01:00
|
|
|
handled = pos != position();
|
|
|
|
|
if (handled) {
|
2013-08-06 18:15:05 +02:00
|
|
|
if (lineForPosition(pos) != lineForPosition(position()))
|
|
|
|
|
recordJump(pos);
|
2016-03-18 12:10:24 +01:00
|
|
|
finishMovement(QString("%1%2%3")
|
2012-11-29 20:14:56 +01:00
|
|
|
.arg(count())
|
2013-05-06 20:02:22 +02:00
|
|
|
.arg(g.subsubmode == OpenSquareSubSubMode ? '[' : ']')
|
2012-11-29 20:14:56 +01:00
|
|
|
.arg(input.text()));
|
|
|
|
|
}
|
2010-07-14 13:02:17 +02:00
|
|
|
} else {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = false;
|
2010-07-14 13:02:17 +02:00
|
|
|
}
|
|
|
|
|
return handled;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-04 20:00:44 +01:00
|
|
|
bool FakeVimHandler::Private::handleCount(const Input &input)
|
|
|
|
|
{
|
2013-02-17 13:56:42 +01:00
|
|
|
if (!isInputCount(input))
|
2013-02-04 20:00:44 +01:00
|
|
|
return false;
|
2013-05-06 20:02:22 +02:00
|
|
|
g.mvcount = g.mvcount * 10 + input.text().toInt();
|
2013-02-04 20:00:44 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-07 17:34:52 +01:00
|
|
|
bool FakeVimHandler::Private::handleMovement(const Input &input)
|
2010-03-19 08:44:57 +01:00
|
|
|
{
|
2012-11-07 17:34:52 +01:00
|
|
|
bool handled = true;
|
|
|
|
|
int count = this->count();
|
2010-03-19 08:44:57 +01:00
|
|
|
|
2013-02-04 20:00:44 +01:00
|
|
|
if (handleCount(input)) {
|
|
|
|
|
return true;
|
|
|
|
|
} else if (input.is('0')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
2013-10-17 19:01:29 +02:00
|
|
|
if (g.gflag)
|
|
|
|
|
moveToStartOfLineVisually();
|
|
|
|
|
else
|
|
|
|
|
moveToStartOfLine();
|
2013-02-04 20:00:44 +01:00
|
|
|
count = 1;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('a') || input.is('i')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = TextObjectSubSubMode;
|
|
|
|
|
g.subsubdata = input;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('^') || input.is('_')) {
|
2013-10-17 19:01:29 +02:00
|
|
|
if (g.gflag)
|
|
|
|
|
moveToFirstNonBlankOnLineVisually();
|
|
|
|
|
else
|
|
|
|
|
moveToFirstNonBlankOnLine();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (0 && input.is(',')) {
|
2009-01-26 10:48:37 +01:00
|
|
|
// FIXME: fakevim uses ',' by itself, so it is incompatible
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = FtSubSubMode;
|
2009-01-26 10:48:37 +01:00
|
|
|
// HACK: toggle 'f' <-> 'F', 't' <-> 'T'
|
2013-05-06 20:02:22 +02:00
|
|
|
//g.subsubdata = g.semicolonType ^ 32;
|
2014-04-27 09:17:46 +02:00
|
|
|
handleFfTt(g.semicolonKey, true);
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = NoSubSubMode;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is(';')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = FtSubSubMode;
|
|
|
|
|
g.subsubdata = g.semicolonType;
|
2014-04-27 09:17:46 +02:00
|
|
|
handleFfTt(g.semicolonKey, true);
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = NoSubSubMode;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('/') || input.is('?')) {
|
2012-10-03 16:40:05 +02:00
|
|
|
g.lastSearchForward = input.is('/');
|
2010-04-08 08:34:55 +02:00
|
|
|
if (hasConfig(ConfigUseCoreSearch)) {
|
2009-06-02 11:56:58 +02:00
|
|
|
// re-use the core dialog.
|
2012-10-03 16:40:05 +02:00
|
|
|
g.findPending = true;
|
2010-10-26 10:56:32 +02:00
|
|
|
m_findStartPosition = position();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
2010-12-16 12:05:48 +01:00
|
|
|
setAnchor(); // clear selection: otherwise, search is restricted to selection
|
2018-02-09 18:49:18 +01:00
|
|
|
q->findRequested(!g.lastSearchForward);
|
2009-06-02 11:56:58 +02:00
|
|
|
} else {
|
|
|
|
|
// FIXME: make core find dialog sufficiently flexible to
|
|
|
|
|
// produce the "default vi" behaviour too. For now, roll our own.
|
2012-09-17 17:44:05 +02:00
|
|
|
g.currentMessage.clear();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
|
|
|
|
g.subsubmode = SearchSubSubMode;
|
2016-03-18 12:10:24 +01:00
|
|
|
g.searchBuffer.setPrompt(g.lastSearchForward ? '/' : '?');
|
2012-09-01 10:44:02 +02:00
|
|
|
m_searchStartPosition = position();
|
|
|
|
|
m_searchFromScreenLine = firstVisibleLine();
|
2012-09-27 21:09:13 +02:00
|
|
|
m_searchCursor = QTextCursor();
|
2012-09-17 17:44:05 +02:00
|
|
|
g.searchBuffer.clear();
|
2009-06-02 11:56:58 +02:00
|
|
|
}
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('`')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = BackTickSubSubMode;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('#') || input.is('*')) {
|
2009-01-16 09:56:08 +01:00
|
|
|
// FIXME: That's not proper vim behaviour
|
2010-09-13 15:23:20 +02:00
|
|
|
QString needle;
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2010-09-14 16:58:31 +02:00
|
|
|
tc.select(QTextCursor::WordUnderCursor);
|
2020-06-22 15:40:34 +02:00
|
|
|
needle = QRegularExpression::escape(tc.selection().toPlainText());
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!g.gflag) {
|
|
|
|
|
needle.prepend("\\<");
|
|
|
|
|
needle.append("\\>");
|
|
|
|
|
}
|
2010-09-16 14:32:29 +02:00
|
|
|
setAnchorAndPosition(tc.position(), tc.anchor());
|
2012-09-17 17:44:05 +02:00
|
|
|
g.searchBuffer.historyPush(needle);
|
2012-10-03 16:40:05 +02:00
|
|
|
g.lastSearch = needle;
|
|
|
|
|
g.lastSearchForward = input.is('*');
|
2013-09-15 16:19:05 +02:00
|
|
|
handled = searchNext();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('\'')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = TickSubSubMode;
|
|
|
|
|
if (g.submode != NoSubMode)
|
|
|
|
|
g.movetype = MoveLineWise;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('|')) {
|
2009-01-16 16:15:01 +01:00
|
|
|
moveToStartOfLine();
|
2014-11-11 18:45:32 +01:00
|
|
|
const int column = count - 1;
|
|
|
|
|
moveRight(qMin(column, rightDist() - 1));
|
|
|
|
|
m_targetColumn = column;
|
|
|
|
|
m_visualTargetColumn = column;
|
2014-11-20 19:57:16 +01:00
|
|
|
} else if (input.is('{') || input.is('}')) {
|
|
|
|
|
const int oldPosition = position();
|
|
|
|
|
handled = input.is('}')
|
|
|
|
|
? moveToNextParagraph(count)
|
|
|
|
|
: moveToPreviousParagraph(count);
|
|
|
|
|
if (handled) {
|
|
|
|
|
recordJump(oldPosition);
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
g.movetype = MoveExclusive;
|
|
|
|
|
}
|
2010-05-10 16:41:35 +02:00
|
|
|
} else if (input.isReturn()) {
|
2009-01-16 16:15:01 +01:00
|
|
|
moveToStartOfLine();
|
|
|
|
|
moveDown();
|
2009-01-15 16:57:11 +01:00
|
|
|
moveToFirstNonBlankOnLine();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('-')) {
|
2009-05-29 08:04:13 +10:00
|
|
|
moveToStartOfLine();
|
2012-11-07 17:34:52 +01:00
|
|
|
moveUp(count);
|
2010-01-05 18:42:26 +01:00
|
|
|
moveToFirstNonBlankOnLine();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('+')) {
|
2010-01-05 18:42:26 +01:00
|
|
|
moveToStartOfLine();
|
2012-11-07 17:34:52 +01:00
|
|
|
moveDown(count);
|
2009-05-29 08:04:13 +10:00
|
|
|
moveToFirstNonBlankOnLine();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Home)) {
|
2009-01-16 16:15:01 +01:00
|
|
|
moveToStartOfLine();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('$') || input.isKey(Key_End)) {
|
2013-10-17 19:01:29 +02:00
|
|
|
if (g.gflag) {
|
|
|
|
|
if (count > 1)
|
|
|
|
|
moveDownVisually(count - 1);
|
|
|
|
|
moveToEndOfLineVisually();
|
|
|
|
|
} else {
|
|
|
|
|
if (count > 1)
|
|
|
|
|
moveDown(count - 1);
|
|
|
|
|
moveToEndOfLine();
|
|
|
|
|
}
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = atEmptyLine() ? MoveExclusive : MoveInclusive;
|
|
|
|
|
if (g.submode == NoSubMode)
|
2009-04-03 14:58:41 +02:00
|
|
|
m_targetColumn = -1;
|
2010-01-21 17:38:31 +01:00
|
|
|
if (isVisualMode())
|
|
|
|
|
m_visualTargetColumn = -1;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('%')) {
|
2012-10-23 16:35:13 +02:00
|
|
|
recordJump();
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.mvcount == 0) {
|
2012-10-13 09:33:30 +02:00
|
|
|
moveToMatchingParanthesis();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveInclusive;
|
2012-10-13 09:33:30 +02:00
|
|
|
} else {
|
|
|
|
|
// set cursor position in percentage - formula taken from Vim help
|
2012-11-07 17:34:52 +01:00
|
|
|
setPosition(firstPositionInLine((count * linesInDocument() + 99) / 100));
|
2012-10-13 09:33:30 +02:00
|
|
|
moveToTargetColumn();
|
|
|
|
|
handleStartOfLine();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveLineWise;
|
2012-10-13 09:33:30 +02:00
|
|
|
}
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('b') || input.isShift(Key_Left)) {
|
2012-11-07 17:34:52 +01:00
|
|
|
moveToNextWordStart(count, false, false);
|
2016-09-30 19:58:55 +02:00
|
|
|
} else if (input.is('B') || input.isControl(Key_Left)) {
|
2012-11-07 17:34:52 +01:00
|
|
|
moveToNextWordStart(count, true, false);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (input.is('e') && g.gflag) {
|
2012-11-07 17:34:52 +01:00
|
|
|
moveToNextWordEnd(count, false, false);
|
2016-09-30 19:58:55 +02:00
|
|
|
} else if (input.is('e')) {
|
2012-11-07 17:34:52 +01:00
|
|
|
moveToNextWordEnd(count, false, true, false);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (input.is('E') && g.gflag) {
|
2012-11-07 17:34:52 +01:00
|
|
|
moveToNextWordEnd(count, true, false);
|
|
|
|
|
} else if (input.is('E')) {
|
|
|
|
|
moveToNextWordEnd(count, true, true, false);
|
|
|
|
|
} else if (input.isControl('e')) {
|
|
|
|
|
// FIXME: this should use the "scroll" option, and "count"
|
|
|
|
|
if (cursorLineOnScreen() == 0)
|
|
|
|
|
moveDown(1);
|
|
|
|
|
scrollDown(1);
|
|
|
|
|
} else if (input.is('f')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = FtSubSubMode;
|
|
|
|
|
g.movetype = MoveInclusive;
|
|
|
|
|
g.subsubdata = input;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('F')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = FtSubSubMode;
|
|
|
|
|
g.movetype = MoveExclusive;
|
|
|
|
|
g.subsubdata = input;
|
|
|
|
|
} else if (!g.gflag && input.is('g')) {
|
|
|
|
|
g.gflag = true;
|
2012-11-21 18:01:13 +01:00
|
|
|
return true;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('g') || input.is('G')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
QString dotCommand = QString("%1G").arg(count);
|
2012-11-07 17:34:52 +01:00
|
|
|
recordJump();
|
2013-05-06 20:02:22 +02:00
|
|
|
if (input.is('G') && g.mvcount == 0)
|
2016-03-18 12:10:24 +01:00
|
|
|
dotCommand = "G";
|
2012-11-07 17:34:52 +01:00
|
|
|
int n = (input.is('g')) ? 1 : linesInDocument();
|
2013-05-06 20:02:22 +02:00
|
|
|
n = g.mvcount == 0 ? n : count;
|
|
|
|
|
if (g.submode == NoSubMode || g.submode == ZSubMode
|
|
|
|
|
|| g.submode == CapitalZSubMode || g.submode == RegisterSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
setPosition(firstPositionInLine(n, false));
|
|
|
|
|
handleStartOfLine();
|
2010-03-18 13:15:59 +01:00
|
|
|
} else {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveLineWise;
|
|
|
|
|
g.rangemode = RangeLineMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
setAnchor();
|
|
|
|
|
setPosition(firstPositionInLine(n, false));
|
2010-01-21 17:38:28 +01:00
|
|
|
}
|
2012-11-21 18:01:13 +01:00
|
|
|
setTargetColumn();
|
2013-04-08 20:06:17 +02:00
|
|
|
updateScrollOffset();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('h') || input.isKey(Key_Left) || input.isBackspace()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
2012-11-07 17:34:52 +01:00
|
|
|
int n = qMin(count, leftDist());
|
|
|
|
|
moveLeft(n);
|
|
|
|
|
} else if (input.is('H')) {
|
2013-04-08 20:06:17 +02:00
|
|
|
const CursorPosition pos(lineToBlockNumber(lineOnTop(count)), 0);
|
|
|
|
|
setCursorPosition(&m_cursor, pos);
|
2012-11-07 17:34:52 +01:00
|
|
|
handleStartOfLine();
|
|
|
|
|
} else if (input.is('j') || input.isKey(Key_Down)
|
|
|
|
|
|| input.isControl('j') || input.isControl('n')) {
|
2014-11-04 18:53:44 +01:00
|
|
|
moveVertically(count);
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('k') || input.isKey(Key_Up) || input.isControl('p')) {
|
2014-11-04 18:53:44 +01:00
|
|
|
moveVertically(-count);
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('l') || input.isKey(Key_Right) || input.is(' ')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
|
|
|
|
moveRight(qMax(0, qMin(count, rightDist() - (g.submode == NoSubMode))));
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('L')) {
|
2013-04-08 20:06:17 +02:00
|
|
|
const CursorPosition pos(lineToBlockNumber(lineOnBottom(count)), 0);
|
|
|
|
|
setCursorPosition(&m_cursor, pos);
|
2012-11-07 17:34:52 +01:00
|
|
|
handleStartOfLine();
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.gflag && input.is('m')) {
|
2013-10-25 18:25:59 +02:00
|
|
|
const QPoint pos(EDITOR(viewport()->width()) / 2, EDITOR(cursorRect(m_cursor)).y());
|
|
|
|
|
QTextCursor tc = EDITOR(cursorForPosition(pos));
|
|
|
|
|
if (!tc.isNull()) {
|
|
|
|
|
m_cursor = tc;
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('M')) {
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor = EDITOR(cursorForPosition(QPoint(0, EDITOR(height()) / 2)));
|
2012-11-07 17:34:52 +01:00
|
|
|
handleStartOfLine();
|
|
|
|
|
} else if (input.is('n') || input.is('N')) {
|
|
|
|
|
if (hasConfig(ConfigUseCoreSearch)) {
|
|
|
|
|
bool forward = (input.is('n')) ? g.lastSearchForward : !g.lastSearchForward;
|
|
|
|
|
int pos = position();
|
2018-02-09 18:49:18 +01:00
|
|
|
q->findNextRequested(!forward);
|
2013-04-08 20:06:17 +02:00
|
|
|
if (forward && pos == m_cursor.selectionStart()) {
|
2012-11-07 17:34:52 +01:00
|
|
|
// if cursor is already positioned at the start of a find result, this is returned
|
2018-02-09 18:49:18 +01:00
|
|
|
q->findNextRequested(false);
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
2013-04-08 20:06:17 +02:00
|
|
|
setPosition(m_cursor.selectionStart());
|
2012-11-07 17:34:52 +01:00
|
|
|
} else {
|
2013-09-15 16:19:05 +02:00
|
|
|
handled = searchNext(input.is('n'));
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
|
|
|
|
} else if (input.is('t')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveInclusive;
|
|
|
|
|
g.subsubmode = FtSubSubMode;
|
|
|
|
|
g.subsubdata = input;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('T')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
|
|
|
|
g.subsubmode = FtSubSubMode;
|
|
|
|
|
g.subsubdata = input;
|
2016-09-30 19:58:55 +02:00
|
|
|
} else if (input.is('w') || input.is('W')
|
|
|
|
|
|| input.isShift(Key_Right) || input.isControl(Key_Right)) { // tested
|
2012-11-07 17:34:52 +01:00
|
|
|
// Special case: "cw" and "cW" work the same as "ce" and "cE" if the
|
|
|
|
|
// cursor is on a non-blank - except if the cursor is on the last
|
|
|
|
|
// character of a word: only the current word will be changed
|
2016-09-30 19:58:55 +02:00
|
|
|
bool simple = input.is('W') || input.isControl(Key_Right);
|
2014-11-17 15:32:00 +01:00
|
|
|
if (g.submode == ChangeSubMode && !characterAtCursor().isSpace()) {
|
2012-11-07 17:34:52 +01:00
|
|
|
moveToWordEnd(count, simple, true);
|
|
|
|
|
} else {
|
|
|
|
|
moveToNextWordStart(count, simple, true);
|
2013-05-14 19:09:57 +02:00
|
|
|
// Command 'dw' deletes to the next word on the same line or to end of line.
|
2013-06-05 14:32:48 +02:00
|
|
|
if (g.submode == DeleteSubMode && count == 1) {
|
2014-11-09 16:37:23 +01:00
|
|
|
const QTextBlock currentBlock = blockAt(anchor());
|
2013-05-14 19:09:57 +02:00
|
|
|
setPosition(qMin(position(), currentBlock.position() + currentBlock.length()));
|
|
|
|
|
}
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
2012-11-29 19:04:52 +01:00
|
|
|
} else if (input.is('z')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveLineWise;
|
|
|
|
|
g.subsubmode = ZSubSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('[')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = OpenSquareSubSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is(']')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = CloseSquareSubSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.isKey(Key_PageDown) || input.isControl('f')) {
|
2013-04-06 14:51:50 +02:00
|
|
|
movePageDown(count);
|
2012-11-07 17:34:52 +01:00
|
|
|
handleStartOfLine();
|
|
|
|
|
} else if (input.isKey(Key_PageUp) || input.isControl('b')) {
|
2013-04-06 14:51:50 +02:00
|
|
|
movePageUp(count);
|
2012-11-07 17:34:52 +01:00
|
|
|
handleStartOfLine();
|
|
|
|
|
} else {
|
|
|
|
|
handled = false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
if (handled && g.subsubmode == NoSubSubMode) {
|
|
|
|
|
if (g.submode == NoSubMode) {
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else {
|
|
|
|
|
// finish movement for sub modes
|
|
|
|
|
const QString dotMovement =
|
|
|
|
|
(count > 1 ? QString::number(count) : QString())
|
2016-03-18 12:10:24 +01:00
|
|
|
+ QLatin1String(g.gflag ? "g" : "")
|
2014-11-04 18:53:44 +01:00
|
|
|
+ input.toString();
|
2012-11-07 17:34:52 +01:00
|
|
|
finishMovement(dotMovement);
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return handled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
bool handled = false;
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
bool clearGflag = g.gflag;
|
|
|
|
|
bool clearRegister = g.submode != RegisterSubMode;
|
|
|
|
|
bool clearCount = g.submode != RegisterSubMode && !isInputCount(input);
|
2012-11-07 17:34:52 +01:00
|
|
|
|
|
|
|
|
// Process input for a sub-mode.
|
|
|
|
|
if (input.isEscape()) {
|
|
|
|
|
handled = handleEscape();
|
2019-06-19 12:34:43 +02:00
|
|
|
} else if (m_wasReadOnly) {
|
|
|
|
|
return EventUnhandled;
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.subsubmode != NoSubSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleCommandSubSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == NoSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleNoSubMode(input);
|
2014-10-26 13:49:11 +01:00
|
|
|
} else if (g.submode == ChangeSubMode
|
|
|
|
|
|| g.submode == DeleteSubMode
|
|
|
|
|
|| g.submode == YankSubMode) {
|
|
|
|
|
handled = handleChangeDeleteYankSubModes(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == ReplaceSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleReplaceSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == FilterSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleFilterSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == RegisterSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleRegisterSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == WindowSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleWindowSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == ZSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleZSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == CapitalZSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleCapitalZSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == MacroRecordSubMode) {
|
2013-03-17 12:41:18 +01:00
|
|
|
handled = handleMacroRecordSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == MacroExecuteSubMode) {
|
2013-03-17 12:41:18 +01:00
|
|
|
handled = handleMacroExecuteSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == ShiftLeftSubMode
|
|
|
|
|
|| g.submode == ShiftRightSubMode
|
|
|
|
|
|| g.submode == IndentSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleShiftSubMode(input);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.submode == InvertCaseSubMode
|
|
|
|
|
|| g.submode == DownCaseSubMode
|
|
|
|
|
|| g.submode == UpCaseSubMode) {
|
2012-11-07 17:34:52 +01:00
|
|
|
handled = handleChangeCaseSubMode(input);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-11 17:15:56 +02:00
|
|
|
if (!handled && isOperatorPending())
|
|
|
|
|
handled = handleMovement(input);
|
|
|
|
|
|
2012-11-09 17:42:52 +01:00
|
|
|
// Clear state and display incomplete command if necessary.
|
2012-11-07 17:34:52 +01:00
|
|
|
if (handled) {
|
|
|
|
|
bool noMode =
|
2013-05-06 20:02:22 +02:00
|
|
|
(g.mode == CommandMode && g.submode == NoSubMode && g.subsubmode == NoSubSubMode);
|
|
|
|
|
clearCount = clearCount && noMode && !g.gflag;
|
2012-11-07 17:34:52 +01:00
|
|
|
if (clearCount && clearRegister) {
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else {
|
|
|
|
|
// Use gflag only for next input.
|
|
|
|
|
if (clearGflag)
|
2013-05-06 20:02:22 +02:00
|
|
|
g.gflag = false;
|
2012-11-07 17:34:52 +01:00
|
|
|
// Clear [count] and [register] if its no longer needed.
|
2013-05-06 20:02:22 +02:00
|
|
|
if (clearCount)
|
|
|
|
|
resetCount();
|
2012-11-09 17:42:52 +01:00
|
|
|
// Show or clear current command on minibuffer (showcmd).
|
2013-05-06 20:02:22 +02:00
|
|
|
if (input.isEscape() || g.mode != CommandMode || clearCount)
|
2012-11-09 17:42:52 +01:00
|
|
|
g.currentCommand.clear();
|
|
|
|
|
else
|
|
|
|
|
g.currentCommand.append(input.toString());
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
2014-11-10 18:27:38 +01:00
|
|
|
|
|
|
|
|
saveLastVisualMode();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else {
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2012-11-07 17:34:52 +01:00
|
|
|
//qDebug() << "IGNORED IN COMMAND MODE: " << key << text
|
2013-05-06 20:02:22 +02:00
|
|
|
// << " VISUAL: " << g.visualMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
|
|
|
|
|
// if a key which produces text was pressed, don't mark it as unhandled
|
|
|
|
|
// - otherwise the text would be inserted while being in command mode
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (input.text().isEmpty())
|
2013-06-06 21:05:32 +02:00
|
|
|
handled = false;
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
|
|
|
|
|
2013-02-17 14:14:06 +01:00
|
|
|
m_positionPastEnd = (m_visualTargetColumn == -1) && isVisualMode() && !atEmptyLine();
|
2012-11-07 17:34:52 +01:00
|
|
|
|
|
|
|
|
return handled ? EventHandled : EventCancelled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleEscape()
|
|
|
|
|
{
|
|
|
|
|
if (isVisualMode())
|
|
|
|
|
leaveVisualMode();
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2012-11-07 17:34:52 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
bool handled = true;
|
|
|
|
|
|
2014-11-16 14:17:11 +01:00
|
|
|
const int oldRevision = revision();
|
|
|
|
|
QString dotCommand = visualDotCommand()
|
2016-03-18 12:10:24 +01:00
|
|
|
+ QLatin1String(g.gflag ? "g" : "")
|
2014-11-16 14:17:11 +01:00
|
|
|
+ QString::number(count())
|
|
|
|
|
+ input.toString();
|
|
|
|
|
|
2012-11-07 17:34:52 +01:00
|
|
|
if (input.is('&')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleExCommand(QLatin1String(g.gflag ? "%s//~/&" : "s"));
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is(':')) {
|
|
|
|
|
enterExMode();
|
|
|
|
|
} else if (input.is('!') && isNoVisualMode()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = FilterSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('!') && isVisualMode()) {
|
2016-03-18 12:10:24 +01:00
|
|
|
enterExMode(QString("!"));
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('"')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = RegisterSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is(',')) {
|
|
|
|
|
passShortcuts(true);
|
|
|
|
|
} else if (input.is('.')) {
|
|
|
|
|
//qDebug() << "REPEATING" << quoteUnprintable(g.dotCommand) << count()
|
|
|
|
|
// << input;
|
2014-11-16 14:17:11 +01:00
|
|
|
dotCommand.clear();
|
2012-11-07 17:34:52 +01:00
|
|
|
QString savedCommand = g.dotCommand;
|
|
|
|
|
g.dotCommand.clear();
|
2013-04-08 20:06:17 +02:00
|
|
|
beginLargeEditBlock();
|
2012-11-07 17:34:52 +01:00
|
|
|
replay(savedCommand);
|
2013-04-01 12:03:22 +02:00
|
|
|
endEditBlock();
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2012-11-07 17:34:52 +01:00
|
|
|
g.dotCommand = savedCommand;
|
|
|
|
|
} else if (input.is('<') || input.is('>') || input.is('=')) {
|
2014-11-09 18:02:05 +01:00
|
|
|
g.submode = indentModeFromInput(input);
|
2014-11-09 19:12:23 +01:00
|
|
|
if (isVisualMode()) {
|
2014-11-11 20:13:03 +01:00
|
|
|
leaveVisualMode();
|
|
|
|
|
const int repeat = count();
|
|
|
|
|
if (g.submode == ShiftLeftSubMode)
|
|
|
|
|
shiftRegionLeft(repeat);
|
|
|
|
|
else if (g.submode == ShiftRightSubMode)
|
|
|
|
|
shiftRegionRight(repeat);
|
|
|
|
|
else
|
|
|
|
|
indentSelectedText();
|
|
|
|
|
g.submode = NoSubMode;
|
2014-11-09 19:12:23 +01:00
|
|
|
} else {
|
|
|
|
|
setAnchor();
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
|
|
|
|
} else if ((!isVisualMode() && input.is('a')) || (isVisualMode() && input.is('A'))) {
|
2013-04-25 17:40:06 +02:00
|
|
|
if (isVisualMode()) {
|
2014-11-16 14:17:11 +01:00
|
|
|
if (!isVisualBlockMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
dotCommand = QString::number(count()) + "a";
|
|
|
|
|
enterVisualInsertMode('A');
|
2013-04-25 17:40:06 +02:00
|
|
|
} else {
|
|
|
|
|
moveRight(qMin(rightDist(), 1));
|
2014-02-03 20:47:47 +01:00
|
|
|
breakEditBlock();
|
|
|
|
|
enterInsertMode();
|
2013-04-25 17:40:06 +02:00
|
|
|
}
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('A')) {
|
|
|
|
|
breakEditBlock();
|
|
|
|
|
moveBehindEndOfLine();
|
|
|
|
|
setAnchor();
|
|
|
|
|
enterInsertMode();
|
2012-12-20 18:12:10 +01:00
|
|
|
setTargetColumn();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.isControl('a')) {
|
2014-11-16 14:17:11 +01:00
|
|
|
changeNumberTextObject(count());
|
2014-11-09 18:02:05 +01:00
|
|
|
} else if ((input.is('c') || input.is('d') || input.is('y')) && isNoVisualMode()) {
|
2012-11-07 17:34:52 +01:00
|
|
|
setAnchor();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.opcount = g.mvcount;
|
|
|
|
|
g.mvcount = 0;
|
|
|
|
|
g.rangemode = RangeCharMode;
|
|
|
|
|
g.movetype = MoveExclusive;
|
2014-11-09 18:02:05 +01:00
|
|
|
g.submode = changeDeleteYankModeFromInput(input);
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if ((input.is('c') || input.is('C') || input.is('s') || input.is('R'))
|
|
|
|
|
&& (isVisualCharMode() || isVisualLineMode())) {
|
2013-03-17 14:17:25 +01:00
|
|
|
leaveVisualMode();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = ChangeSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
finishMovement();
|
2014-02-03 20:47:47 +01:00
|
|
|
} else if ((input.is('c') || input.is('s')) && isVisualBlockMode()) {
|
|
|
|
|
resetCount();
|
|
|
|
|
enterVisualInsertMode(input.asChar());
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('C')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleAs("%1c$");
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.isControl('c')) {
|
|
|
|
|
if (isNoVisualMode())
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageInfo, Tr::tr("Type Alt-V, Alt-V to quit FakeVim mode."));
|
2012-11-07 17:34:52 +01:00
|
|
|
else
|
|
|
|
|
leaveVisualMode();
|
|
|
|
|
} else if ((input.is('d') || input.is('x') || input.isKey(Key_Delete))
|
|
|
|
|
&& isVisualMode()) {
|
2014-11-01 17:57:42 +01:00
|
|
|
cutSelectedText();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('D') && isNoVisualMode()) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleAs("%1d$");
|
2014-11-01 17:57:42 +01:00
|
|
|
} else if ((input.is('D') || input.is('X')) && isVisualMode()) {
|
|
|
|
|
if (isVisualCharMode())
|
|
|
|
|
toggleVisualMode(VisualLineMode);
|
2014-11-16 13:04:03 +01:00
|
|
|
if (isVisualBlockMode() && input.is('D'))
|
|
|
|
|
m_visualTargetColumn = -1;
|
2014-11-01 17:57:42 +01:00
|
|
|
cutSelectedText();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isControl('d')) {
|
2013-04-08 20:06:17 +02:00
|
|
|
const int scrollOffset = windowScrollOffset();
|
|
|
|
|
int sline = cursorLine() < scrollOffset ? scrollOffset : cursorLineOnScreen();
|
2009-01-27 14:08:17 +01:00
|
|
|
// FIXME: this should use the "scroll" option, and "count"
|
|
|
|
|
moveDown(linesOnScreen() / 2);
|
2009-04-03 14:58:41 +02:00
|
|
|
handleStartOfLine();
|
2010-07-06 10:12:21 +02:00
|
|
|
scrollToLine(cursorLine() - sline);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (!g.gflag && input.is('g')) {
|
|
|
|
|
g.gflag = true;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (!isVisualMode() && (input.is('i') || input.isKey(Key_Insert))) {
|
2010-05-20 16:32:54 +02:00
|
|
|
breakEditBlock();
|
2009-01-06 13:03:59 +01:00
|
|
|
enterInsertMode();
|
2009-01-23 10:13:12 +01:00
|
|
|
if (atEndOfLine())
|
2009-01-16 17:38:15 +01:00
|
|
|
moveLeft();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('I')) {
|
2010-02-12 15:24:54 +01:00
|
|
|
if (isVisualMode()) {
|
2014-11-16 14:17:11 +01:00
|
|
|
if (!isVisualBlockMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
dotCommand = QString::number(count()) + "i";
|
|
|
|
|
enterVisualInsertMode('I');
|
2010-02-12 15:24:54 +01:00
|
|
|
} else {
|
2014-11-16 14:17:11 +01:00
|
|
|
if (g.gflag)
|
2012-11-07 17:34:52 +01:00
|
|
|
moveToStartOfLine();
|
2014-11-16 14:17:11 +01:00
|
|
|
else
|
2012-11-07 17:34:52 +01:00
|
|
|
moveToFirstNonBlankOnLine();
|
2014-02-03 20:47:47 +01:00
|
|
|
breakEditBlock();
|
|
|
|
|
enterInsertMode();
|
2010-10-26 10:56:32 +02:00
|
|
|
}
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.isControl('i')) {
|
|
|
|
|
jump(count());
|
2012-11-21 18:09:44 +01:00
|
|
|
} else if (input.is('J')) {
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2012-11-21 18:09:44 +01:00
|
|
|
moveBehindEndOfLine();
|
|
|
|
|
beginEditBlock();
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.submode == NoSubMode)
|
|
|
|
|
joinLines(count(), g.gflag);
|
2012-11-21 18:09:44 +01:00
|
|
|
endEditBlock();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.isControl('l')) {
|
|
|
|
|
// screen redraw. should not be needed
|
2013-10-17 17:11:06 +02:00
|
|
|
} else if (!g.gflag && input.is('m')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.subsubmode = MarkSubSubMode;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (isVisualMode() && (input.is('o') || input.is('O'))) {
|
2010-01-21 17:38:25 +01:00
|
|
|
int pos = position();
|
2010-09-14 14:04:13 +02:00
|
|
|
setAnchorAndPosition(pos, anchor());
|
2010-01-21 17:38:31 +01:00
|
|
|
std::swap(m_positionPastEnd, m_anchorPastEnd);
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
if (m_positionPastEnd)
|
|
|
|
|
m_visualTargetColumn = -1;
|
2012-10-05 17:33:20 +02:00
|
|
|
} else if (input.is('o') || input.is('O')) {
|
|
|
|
|
bool insertAfter = input.is('o');
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2013-03-16 13:32:32 +01:00
|
|
|
|
|
|
|
|
// Prepend line only if on the first line and command is 'O'.
|
|
|
|
|
bool appendLine = true;
|
|
|
|
|
if (!insertAfter) {
|
|
|
|
|
if (block().blockNumber() == 0)
|
|
|
|
|
appendLine = false;
|
|
|
|
|
else
|
|
|
|
|
moveUp();
|
2012-10-05 17:33:20 +02:00
|
|
|
}
|
2013-03-16 13:32:32 +01:00
|
|
|
const int line = lineNumber(block());
|
|
|
|
|
|
2012-10-13 09:33:30 +02:00
|
|
|
beginEditBlock();
|
2013-07-09 19:39:31 +02:00
|
|
|
enterInsertMode();
|
|
|
|
|
setPosition(appendLine ? lastPositionInLine(line) : firstPositionInLine(line));
|
|
|
|
|
clearLastInsertion();
|
|
|
|
|
setAnchor();
|
|
|
|
|
insertNewLine();
|
2013-03-16 13:32:32 +01:00
|
|
|
if (appendLine) {
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->insertState.newLineBefore = true;
|
2013-03-16 13:32:32 +01:00
|
|
|
} else {
|
2012-10-13 09:33:30 +02:00
|
|
|
moveUp();
|
2014-11-10 18:27:38 +01:00
|
|
|
m_buffer->insertState.pos1 = position();
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->insertState.newLineAfter = true;
|
2013-03-16 13:32:32 +01:00
|
|
|
}
|
2012-10-13 09:33:30 +02:00
|
|
|
setTargetColumn();
|
2009-08-18 11:32:11 +02:00
|
|
|
endEditBlock();
|
2013-03-16 13:32:32 +01:00
|
|
|
|
|
|
|
|
// Close accidentally opened block.
|
|
|
|
|
if (block().blockNumber() > 0) {
|
|
|
|
|
moveUp();
|
|
|
|
|
if (line != lineNumber(block()))
|
2018-02-09 18:49:18 +01:00
|
|
|
q->fold(1, true);
|
2013-03-16 13:32:32 +01:00
|
|
|
moveDown();
|
|
|
|
|
}
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isControl('o')) {
|
2012-08-11 14:31:42 +02:00
|
|
|
jump(-count());
|
2013-02-28 14:57:17 +01:00
|
|
|
} else if (input.is('p') || input.is('P') || input.isShift(Qt::Key_Insert)) {
|
|
|
|
|
pasteText(!input.is('P'));
|
2009-08-11 14:39:44 +02:00
|
|
|
setTargetColumn();
|
2009-01-22 15:38:03 +01:00
|
|
|
finishMovement();
|
2013-03-17 12:41:18 +01:00
|
|
|
} else if (input.is('q')) {
|
2014-10-21 12:53:29 +02:00
|
|
|
if (g.isRecording) {
|
|
|
|
|
// Stop recording.
|
|
|
|
|
stopRecording();
|
|
|
|
|
} else {
|
2013-03-17 12:41:18 +01:00
|
|
|
// Recording shouldn't work in mapping or while executing register.
|
|
|
|
|
handled = g.mapStates.empty();
|
|
|
|
|
if (handled)
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = MacroRecordSubMode;
|
2013-03-17 12:41:18 +01:00
|
|
|
}
|
2010-05-06 12:10:57 +02:00
|
|
|
} else if (input.is('r')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = ReplaceSubMode;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (!isVisualMode() && input.is('R')) {
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2010-05-20 16:32:54 +02:00
|
|
|
breakEditBlock();
|
2010-05-06 12:10:57 +02:00
|
|
|
enterReplaceMode();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isControl('r')) {
|
2014-11-16 14:17:11 +01:00
|
|
|
dotCommand.clear();
|
2012-08-26 18:39:48 +02:00
|
|
|
int repeat = count();
|
|
|
|
|
while (--repeat >= 0)
|
|
|
|
|
redo();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('s')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleAs("c%1l");
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('S')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleAs("%1cc");
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.gflag && input.is('t')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleExCommand("tabnext");
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.gflag && input.is('T')) {
|
2017-09-08 13:39:37 +02:00
|
|
|
handleExCommand("tabprevious");
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isControl('t')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleExCommand("pop");
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (!g.gflag && input.is('u') && !isVisualMode()) {
|
2014-11-16 14:17:11 +01:00
|
|
|
dotCommand.clear();
|
2012-08-26 18:39:48 +02:00
|
|
|
int repeat = count();
|
|
|
|
|
while (--repeat >= 0)
|
|
|
|
|
undo();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isControl('u')) {
|
2009-01-27 14:08:17 +01:00
|
|
|
int sline = cursorLineOnScreen();
|
|
|
|
|
// FIXME: this should use the "scroll" option, and "count"
|
|
|
|
|
moveUp(linesOnScreen() / 2);
|
2009-04-03 14:58:41 +02:00
|
|
|
handleStartOfLine();
|
2010-07-06 10:12:21 +02:00
|
|
|
scrollToLine(cursorLine() - sline);
|
2013-05-06 20:02:22 +02:00
|
|
|
} else if (g.gflag && input.is('v')) {
|
2014-11-10 18:27:38 +01:00
|
|
|
if (isNoVisualMode()) {
|
2014-02-03 20:47:47 +01:00
|
|
|
CursorPosition from = markLessPosition();
|
|
|
|
|
CursorPosition to = markGreaterPosition();
|
2014-11-10 18:27:38 +01:00
|
|
|
if (m_buffer->lastVisualModeInverted)
|
|
|
|
|
std::swap(from, to);
|
2014-04-30 17:32:25 +02:00
|
|
|
toggleVisualMode(m_buffer->lastVisualMode);
|
2014-11-10 18:27:38 +01:00
|
|
|
setCursorPosition(from);
|
2012-10-23 16:35:13 +02:00
|
|
|
setAnchor();
|
2014-11-10 18:27:38 +01:00
|
|
|
setCursorPosition(to);
|
2013-04-23 20:07:25 +02:00
|
|
|
setTargetColumn();
|
2012-08-11 10:59:29 +02:00
|
|
|
}
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('v')) {
|
2011-08-02 17:59:05 +02:00
|
|
|
toggleVisualMode(VisualCharMode);
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('V')) {
|
2011-08-02 17:59:05 +02:00
|
|
|
toggleVisualMode(VisualLineMode);
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isControl('v')) {
|
2011-08-02 17:59:05 +02:00
|
|
|
toggleVisualMode(VisualBlockMode);
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isControl('w')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = WindowSubMode;
|
2014-11-09 19:12:23 +01:00
|
|
|
} else if (input.is('x') && isNoVisualMode()) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleAs("%1dl");
|
2011-12-27 14:54:49 +01:00
|
|
|
} else if (input.isControl('x')) {
|
2014-11-16 14:17:11 +01:00
|
|
|
changeNumberTextObject(-count());
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('X')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleAs("%1dh");
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('Y') && isNoVisualMode()) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleAs("%1yy");
|
2011-05-13 18:56:25 +02:00
|
|
|
} else if (input.isControl('y')) {
|
|
|
|
|
// FIXME: this should use the "scroll" option, and "count"
|
|
|
|
|
if (cursorLineOnScreen() == linesOnScreen() - 1)
|
|
|
|
|
moveUp(1);
|
|
|
|
|
scrollUp(1);
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('y') && isVisualCharMode()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.rangemode = RangeCharMode;
|
|
|
|
|
g.movetype = MoveInclusive;
|
|
|
|
|
g.submode = YankSubMode;
|
2009-08-10 11:43:35 +02:00
|
|
|
finishMovement();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if ((input.is('y') && isVisualLineMode())
|
|
|
|
|
|| (input.is('Y') && isVisualLineMode())
|
|
|
|
|
|| (input.is('Y') && isVisualCharMode())) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.rangemode = RangeLineMode;
|
|
|
|
|
g.movetype = MoveLineWise;
|
|
|
|
|
g.submode = YankSubMode;
|
2009-01-16 17:38:15 +01:00
|
|
|
finishMovement();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if ((input.is('y') || input.is('Y')) && isVisualBlockMode()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.rangemode = RangeBlockMode;
|
|
|
|
|
g.movetype = MoveInclusive;
|
|
|
|
|
g.submode = YankSubMode;
|
2009-08-11 14:39:44 +02:00
|
|
|
finishMovement();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('z')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = ZSubMode;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.is('Z')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = CapitalZSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if ((input.is('~') || input.is('u') || input.is('U'))) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
2014-11-09 18:02:05 +01:00
|
|
|
g.submode = letterCaseModeFromInput(input);
|
2013-10-04 15:49:37 +02:00
|
|
|
pushUndoState();
|
2012-11-07 17:34:52 +01:00
|
|
|
if (isVisualMode()) {
|
|
|
|
|
leaveVisualMode();
|
|
|
|
|
finishMovement();
|
2014-11-09 18:02:05 +01:00
|
|
|
} else if (g.gflag || (g.submode == InvertCaseSubMode && hasConfig(ConfigTildeOp))) {
|
2012-11-07 17:34:52 +01:00
|
|
|
if (atEndOfLine())
|
|
|
|
|
moveLeft();
|
2010-01-21 17:38:26 +01:00
|
|
|
setAnchor();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else {
|
2016-03-18 12:10:24 +01:00
|
|
|
const QString movementCommand = QString("%1l%1l").arg(count());
|
|
|
|
|
handleAs("g" + input.toString() + movementCommand);
|
2008-12-25 22:22:41 +01:00
|
|
|
}
|
2013-03-17 12:41:18 +01:00
|
|
|
} else if (input.is('@')) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = MacroExecuteSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.isKey(Key_Delete)) {
|
|
|
|
|
setAnchor();
|
|
|
|
|
moveRight(qMin(1, rightDist()));
|
|
|
|
|
removeText(currentRange());
|
2010-01-21 17:38:26 +01:00
|
|
|
if (atEndOfLine())
|
|
|
|
|
moveLeft();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.isControl(Key_BracketRight)) {
|
2016-03-18 12:10:24 +01:00
|
|
|
handleExCommand("tag");
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (handleMovement(input)) {
|
|
|
|
|
// movement handled
|
2014-11-16 14:17:11 +01:00
|
|
|
dotCommand.clear();
|
2012-11-07 17:34:52 +01:00
|
|
|
} else {
|
|
|
|
|
handled = false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-16 14:17:11 +01:00
|
|
|
// Set dot command if the current input changed document or entered insert mode.
|
|
|
|
|
if (handled && !dotCommand.isEmpty() && (oldRevision != revision() || isInsertMode()))
|
|
|
|
|
setDotCommand(dotCommand);
|
|
|
|
|
|
2012-11-07 17:34:52 +01:00
|
|
|
return handled;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-26 13:49:11 +01:00
|
|
|
bool FakeVimHandler::Private::handleChangeDeleteYankSubModes(const Input &input)
|
2012-11-07 17:34:52 +01:00
|
|
|
{
|
2014-11-09 18:02:05 +01:00
|
|
|
if (g.submode != changeDeleteYankModeFromInput(input))
|
|
|
|
|
return false;
|
2012-11-07 17:34:52 +01:00
|
|
|
|
2014-11-09 18:02:05 +01:00
|
|
|
handleChangeDeleteYankSubModes();
|
|
|
|
|
|
|
|
|
|
return true;
|
2014-10-26 13:49:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::handleChangeDeleteYankSubModes()
|
|
|
|
|
{
|
|
|
|
|
g.movetype = MoveLineWise;
|
|
|
|
|
|
2014-11-16 14:17:11 +01:00
|
|
|
const QString dotCommand = dotCommandFromSubMode(g.submode);
|
|
|
|
|
|
|
|
|
|
if (!dotCommand.isEmpty())
|
2014-10-26 13:49:11 +01:00
|
|
|
pushUndoState();
|
|
|
|
|
|
|
|
|
|
const int anc = firstPositionInLine(cursorLine() + 1);
|
|
|
|
|
moveDown(count() - 1);
|
|
|
|
|
const int pos = lastPositionInLine(cursorLine() + 1);
|
|
|
|
|
setAnchorAndPosition(anc, pos);
|
|
|
|
|
|
2014-11-16 14:17:11 +01:00
|
|
|
if (!dotCommand.isEmpty())
|
2016-03-18 12:10:24 +01:00
|
|
|
setDotCommand(QString("%2%1%1").arg(dotCommand), count());
|
2014-10-26 13:49:11 +01:00
|
|
|
|
|
|
|
|
finishMovement();
|
|
|
|
|
|
|
|
|
|
g.submode = NoSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleReplaceSubMode(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
bool handled = true;
|
|
|
|
|
|
2014-11-15 13:34:32 +01:00
|
|
|
const QChar c = input.asChar();
|
2016-03-18 12:10:24 +01:00
|
|
|
setDotCommand(visualDotCommand() + 'r' + c);
|
2012-11-07 17:34:52 +01:00
|
|
|
if (isVisualMode()) {
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2010-01-21 17:38:26 +01:00
|
|
|
leaveVisualMode();
|
2012-11-07 17:34:52 +01:00
|
|
|
Range range = currentRange();
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.rangemode == RangeCharMode)
|
2012-11-07 17:34:52 +01:00
|
|
|
++range.endPos;
|
2014-11-15 13:34:32 +01:00
|
|
|
// Replace each character but preserve lines.
|
|
|
|
|
transformText(range, [&c](const QString &text) {
|
2020-06-22 15:40:34 +02:00
|
|
|
return QString(text).replace(QRegularExpression("[^\\n]"), c);
|
2014-11-15 13:34:32 +01:00
|
|
|
});
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (count() <= rightDist()) {
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2009-01-22 13:44:25 +01:00
|
|
|
setAnchor();
|
2012-11-07 17:34:52 +01:00
|
|
|
moveRight(count());
|
|
|
|
|
Range range = currentRange();
|
|
|
|
|
if (input.isReturn()) {
|
|
|
|
|
beginEditBlock();
|
|
|
|
|
replaceText(range, QString());
|
2016-03-18 12:10:24 +01:00
|
|
|
insertText(QString("\n"));
|
2012-11-07 17:34:52 +01:00
|
|
|
endEditBlock();
|
|
|
|
|
} else {
|
2014-11-15 13:34:32 +01:00
|
|
|
replaceText(range, QString(count(), c));
|
2012-11-07 17:34:52 +01:00
|
|
|
moveRight(count() - 1);
|
|
|
|
|
}
|
|
|
|
|
setTargetColumn();
|
2016-03-18 12:10:24 +01:00
|
|
|
setDotCommand("%1r" + input.text(), count());
|
2012-11-07 17:34:52 +01:00
|
|
|
} else {
|
|
|
|
|
handled = false;
|
|
|
|
|
}
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
finishMovement();
|
2009-11-27 14:48:37 +01:00
|
|
|
|
2012-11-07 17:34:52 +01:00
|
|
|
return handled;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-11 17:15:56 +02:00
|
|
|
bool FakeVimHandler::Private::handleFilterSubMode(const Input &)
|
2012-11-07 17:34:52 +01:00
|
|
|
{
|
2013-08-11 17:15:56 +02:00
|
|
|
return false;
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleRegisterSubMode(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
bool handled = false;
|
|
|
|
|
|
|
|
|
|
QChar reg = input.asChar();
|
2016-03-18 12:10:24 +01:00
|
|
|
if (QString("*+.%#:-\"_").contains(reg) || reg.isLetterOrNumber()) {
|
2012-11-07 17:34:52 +01:00
|
|
|
m_register = reg.unicode();
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
|
|
|
|
|
return handled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleShiftSubMode(const Input &input)
|
|
|
|
|
{
|
2014-11-09 18:02:05 +01:00
|
|
|
if (g.submode != indentModeFromInput(input))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
g.movetype = MoveLineWise;
|
|
|
|
|
pushUndoState();
|
|
|
|
|
moveDown(count() - 1);
|
2016-03-18 12:10:24 +01:00
|
|
|
setDotCommand(QString("%2%1%1").arg(input.asChar()), count());
|
2014-11-09 18:02:05 +01:00
|
|
|
finishMovement();
|
|
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
|
|
|
|
|
return true;
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
2010-01-21 17:38:24 +01:00
|
|
|
|
2012-11-07 17:34:52 +01:00
|
|
|
bool FakeVimHandler::Private::handleChangeCaseSubMode(const Input &input)
|
|
|
|
|
{
|
2014-11-09 18:02:05 +01:00
|
|
|
if (g.submode != letterCaseModeFromInput(input))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!isFirstNonBlankOnLine(position())) {
|
|
|
|
|
moveToStartOfLine();
|
|
|
|
|
moveToFirstNonBlankOnLine();
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
2014-11-09 18:02:05 +01:00
|
|
|
setTargetColumn();
|
|
|
|
|
pushUndoState();
|
|
|
|
|
setAnchor();
|
|
|
|
|
setPosition(lastPositionInLine(cursorLine() + count()) + 1);
|
2016-03-18 12:10:24 +01:00
|
|
|
finishMovement(QString("%1%2").arg(count()).arg(input.raw()));
|
2014-11-09 18:02:05 +01:00
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
|
|
|
|
|
return true;
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleWindowSubMode(const Input &input)
|
|
|
|
|
{
|
2013-02-04 20:00:44 +01:00
|
|
|
if (handleCount(input))
|
|
|
|
|
return true;
|
|
|
|
|
|
2013-09-15 16:19:05 +02:00
|
|
|
leaveVisualMode();
|
2014-11-15 09:19:04 +01:00
|
|
|
leaveCurrentMode();
|
2018-02-09 18:49:18 +01:00
|
|
|
q->windowCommandRequested(input.toString(), count());
|
2013-02-04 20:00:44 +01:00
|
|
|
|
|
|
|
|
return true;
|
2012-11-07 17:34:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleZSubMode(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
bool handled = true;
|
|
|
|
|
bool foldMaybeClosed = false;
|
|
|
|
|
if (input.isReturn() || input.is('t')
|
|
|
|
|
|| input.is('-') || input.is('b')
|
|
|
|
|
|| input.is('.') || input.is('z')) {
|
|
|
|
|
// Cursor line to top/center/bottom of window.
|
|
|
|
|
Qt::AlignmentFlag align;
|
|
|
|
|
if (input.isReturn() || input.is('t'))
|
|
|
|
|
align = Qt::AlignTop;
|
|
|
|
|
else if (input.is('.') || input.is('z'))
|
|
|
|
|
align = Qt::AlignVCenter;
|
2013-03-03 15:57:06 +01:00
|
|
|
else
|
|
|
|
|
align = Qt::AlignBottom;
|
2012-11-07 17:34:52 +01:00
|
|
|
const bool moveToNonBlank = (input.is('.') || input.isReturn() || input.is('-'));
|
2013-05-06 20:02:22 +02:00
|
|
|
const int line = g.mvcount == 0 ? -1 : firstPositionInLine(count());
|
2012-11-07 17:34:52 +01:00
|
|
|
alignViewportToCursor(align, line, moveToNonBlank);
|
|
|
|
|
} else if (input.is('o') || input.is('c')) {
|
|
|
|
|
// Open/close current fold.
|
|
|
|
|
foldMaybeClosed = input.is('c');
|
2018-02-09 18:49:18 +01:00
|
|
|
q->fold(count(), foldMaybeClosed);
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('O') || input.is('C')) {
|
|
|
|
|
// Recursively open/close current fold.
|
|
|
|
|
foldMaybeClosed = input.is('C');
|
2018-02-09 18:49:18 +01:00
|
|
|
q->fold(-1, foldMaybeClosed);
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('a') || input.is('A')) {
|
|
|
|
|
// Toggle current fold.
|
|
|
|
|
foldMaybeClosed = true;
|
2018-02-09 18:49:18 +01:00
|
|
|
q->foldToggle(input.is('a') ? count() : -1);
|
2012-11-07 17:34:52 +01:00
|
|
|
} else if (input.is('R') || input.is('M')) {
|
|
|
|
|
// Open/close all folds in document.
|
|
|
|
|
foldMaybeClosed = input.is('M');
|
2018-02-09 18:49:18 +01:00
|
|
|
q->foldAll(foldMaybeClosed);
|
2012-11-29 19:04:52 +01:00
|
|
|
} else if (input.is('j') || input.is('k')) {
|
2018-02-09 18:49:18 +01:00
|
|
|
q->foldGoTo(input.is('j') ? count() : -count(), false);
|
2012-11-07 17:34:52 +01:00
|
|
|
} else {
|
|
|
|
|
handled = false;
|
|
|
|
|
}
|
|
|
|
|
if (foldMaybeClosed)
|
|
|
|
|
ensureCursorVisible();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2012-11-07 17:34:52 +01:00
|
|
|
return handled;
|
|
|
|
|
}
|
2010-01-21 17:38:31 +01:00
|
|
|
|
2012-11-07 17:34:52 +01:00
|
|
|
bool FakeVimHandler::Private::handleCapitalZSubMode(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
// Recognize ZZ and ZQ as aliases for ":x" and ":q!".
|
|
|
|
|
bool handled = true;
|
|
|
|
|
if (input.is('Z'))
|
2016-03-18 12:10:24 +01:00
|
|
|
handleExCommand("x");
|
2012-11-07 17:34:52 +01:00
|
|
|
else if (input.is('Q'))
|
2016-03-18 12:10:24 +01:00
|
|
|
handleExCommand("q!");
|
2012-11-07 17:34:52 +01:00
|
|
|
else
|
|
|
|
|
handled = false;
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2008-12-23 22:28:46 +01:00
|
|
|
return handled;
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-17 12:41:18 +01:00
|
|
|
bool FakeVimHandler::Private::handleMacroRecordSubMode(const Input &input)
|
|
|
|
|
{
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2013-03-17 12:41:18 +01:00
|
|
|
return startRecording(input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleMacroExecuteSubMode(const Input &input)
|
|
|
|
|
{
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2013-03-17 12:41:18 +01:00
|
|
|
|
|
|
|
|
bool result = true;
|
|
|
|
|
int repeat = count();
|
|
|
|
|
while (result && --repeat >= 0)
|
|
|
|
|
result = executeRegister(input.asChar().unicode());
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
EventResult FakeVimHandler::Private::handleInsertOrReplaceMode(const Input &input)
|
2010-05-06 12:10:57 +02:00
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
if (position() < m_buffer->insertState.pos1 || position() > m_buffer->insertState.pos2) {
|
2013-07-09 19:39:31 +02:00
|
|
|
commitInsertState();
|
|
|
|
|
invalidateInsertState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g.mode == InsertMode)
|
|
|
|
|
handleInsertMode(input);
|
|
|
|
|
else
|
|
|
|
|
handleReplaceMode(input);
|
|
|
|
|
|
2013-09-10 18:39:05 +02:00
|
|
|
if (!m_textedit && !m_plaintextedit)
|
|
|
|
|
return EventHandled;
|
|
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
if (!isInsertMode() || m_buffer->breakEditBlock
|
|
|
|
|
|| position() < m_buffer->insertState.pos1 || position() > m_buffer->insertState.pos2) {
|
2013-07-09 19:39:31 +02:00
|
|
|
commitInsertState();
|
|
|
|
|
invalidateInsertState();
|
|
|
|
|
breakEditBlock();
|
2014-02-03 20:47:47 +01:00
|
|
|
m_visualBlockInsert = NoneBlockInsertMode;
|
2012-11-24 10:24:48 +01:00
|
|
|
}
|
|
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
// We don't want fancy stuff in insert mode.
|
|
|
|
|
return EventHandled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::handleReplaceMode(const Input &input)
|
|
|
|
|
{
|
2010-05-18 18:24:00 +02:00
|
|
|
if (input.isEscape()) {
|
2013-07-09 19:39:31 +02:00
|
|
|
commitInsertState();
|
2010-05-06 12:10:57 +02:00
|
|
|
moveLeft(qMin(1, leftDist()));
|
2012-11-10 10:36:05 +01:00
|
|
|
enterCommandMode();
|
2016-03-18 12:10:24 +01:00
|
|
|
g.dotCommand.append(m_buffer->lastInsertion + "<ESC>");
|
2011-08-03 11:40:45 +02:00
|
|
|
} else if (input.isKey(Key_Left)) {
|
2013-07-09 19:39:31 +02:00
|
|
|
moveLeft();
|
2011-08-03 11:40:45 +02:00
|
|
|
} else if (input.isKey(Key_Right)) {
|
2013-07-09 19:39:31 +02:00
|
|
|
moveRight();
|
2011-08-03 11:40:45 +02:00
|
|
|
} else if (input.isKey(Key_Up)) {
|
2013-07-09 19:39:31 +02:00
|
|
|
moveUp();
|
2011-08-03 11:40:45 +02:00
|
|
|
} else if (input.isKey(Key_Down)) {
|
2013-07-09 19:39:31 +02:00
|
|
|
moveDown();
|
2012-11-10 10:36:05 +01:00
|
|
|
} else if (input.isKey(Key_Insert)) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.mode = InsertMode;
|
2012-11-10 10:36:05 +01:00
|
|
|
} else if (input.isControl('o')) {
|
|
|
|
|
enterCommandMode(ReplaceMode);
|
2010-05-06 12:10:57 +02:00
|
|
|
} else {
|
2010-05-06 16:05:44 +02:00
|
|
|
joinPreviousEditBlock();
|
2010-05-06 12:10:57 +02:00
|
|
|
if (!atEndOfLine()) {
|
|
|
|
|
setAnchor();
|
|
|
|
|
moveRight();
|
2010-05-12 11:18:18 +02:00
|
|
|
removeText(currentRange());
|
2010-05-06 12:10:57 +02:00
|
|
|
}
|
|
|
|
|
const QString text = input.text();
|
2010-09-16 16:56:11 +02:00
|
|
|
setAnchor();
|
2010-05-11 14:26:37 +02:00
|
|
|
insertText(text);
|
2014-11-10 18:27:38 +01:00
|
|
|
setTargetColumn();
|
2010-05-06 12:10:57 +02:00
|
|
|
endEditBlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
void FakeVimHandler::Private::finishInsertMode()
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
bool newLineAfter = m_buffer->insertState.newLineAfter;
|
|
|
|
|
bool newLineBefore = m_buffer->insertState.newLineBefore;
|
2013-07-09 19:39:31 +02:00
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
// Repeat insertion [count] times.
|
|
|
|
|
// One instance was already physically inserted while typing.
|
2014-04-30 17:32:25 +02:00
|
|
|
if (!m_buffer->breakEditBlock && isInsertStateValid()) {
|
2014-02-03 20:47:47 +01:00
|
|
|
commitInsertState();
|
2013-07-09 19:39:31 +02:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
QString text = m_buffer->lastInsertion;
|
2014-02-03 20:47:47 +01:00
|
|
|
const QString dotCommand = g.dotCommand;
|
|
|
|
|
const int repeat = count() - 1;
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->lastInsertion.clear();
|
2014-02-03 20:47:47 +01:00
|
|
|
joinPreviousEditBlock();
|
2013-04-24 20:28:24 +02:00
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
if (newLineAfter) {
|
|
|
|
|
text.chop(1);
|
2016-03-18 12:10:24 +01:00
|
|
|
text.prepend("<END>\n");
|
2014-02-03 20:47:47 +01:00
|
|
|
} else if (newLineBefore) {
|
2016-03-18 12:10:24 +01:00
|
|
|
text.prepend("<END>");
|
2014-02-03 20:47:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
replay(text, repeat);
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if (m_visualBlockInsert != NoneBlockInsertMode && !text.contains('\n')) {
|
2014-02-03 20:47:47 +01:00
|
|
|
const CursorPosition lastAnchor = markLessPosition();
|
|
|
|
|
const CursorPosition lastPosition = markGreaterPosition();
|
2016-03-18 12:10:24 +01:00
|
|
|
const bool change = m_visualBlockInsert == ChangeBlockInsertMode;
|
2014-02-03 20:47:47 +01:00
|
|
|
const int insertColumn = (m_visualBlockInsert == InsertBlockInsertMode || change)
|
|
|
|
|
? qMin(lastPosition.column, lastAnchor.column)
|
|
|
|
|
: qMax(lastPosition.column, lastAnchor.column) + 1;
|
|
|
|
|
|
|
|
|
|
CursorPosition pos(lastAnchor.line, insertColumn);
|
|
|
|
|
|
|
|
|
|
if (change)
|
2014-11-09 16:37:23 +01:00
|
|
|
pos.column = columnAt(m_buffer->insertState.pos1);
|
2014-02-03 20:47:47 +01:00
|
|
|
|
|
|
|
|
// Cursor position after block insert is on the first selected line,
|
|
|
|
|
// last selected column for 's' command, otherwise first selected column.
|
|
|
|
|
const int endColumn = change ? qMax(0, m_cursor.positionInBlock() - 1)
|
|
|
|
|
: qMin(lastPosition.column, lastAnchor.column);
|
|
|
|
|
|
|
|
|
|
while (pos.line < lastPosition.line) {
|
|
|
|
|
++pos.line;
|
|
|
|
|
setCursorPosition(&m_cursor, pos);
|
|
|
|
|
if (m_visualBlockInsert == AppendToEndOfLineBlockInsertMode) {
|
|
|
|
|
moveToEndOfLine();
|
|
|
|
|
} else if (m_visualBlockInsert == AppendBlockInsertMode) {
|
|
|
|
|
// Prepend spaces if necessary.
|
|
|
|
|
int spaces = pos.column - m_cursor.positionInBlock();
|
|
|
|
|
if (spaces > 0) {
|
|
|
|
|
setAnchor();
|
2016-03-18 12:10:24 +01:00
|
|
|
m_cursor.insertText(QString(" ").repeated(spaces));
|
2014-02-03 20:47:47 +01:00
|
|
|
}
|
|
|
|
|
} else if (m_cursor.positionInBlock() != pos.column) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
replay(text, repeat + 1);
|
2010-02-12 15:24:54 +01:00
|
|
|
}
|
2012-12-04 20:37:39 +01:00
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
setCursorPosition(CursorPosition(lastAnchor.line, endColumn));
|
2010-02-12 15:24:54 +01:00
|
|
|
} else {
|
|
|
|
|
moveLeft(qMin(1, leftDist()));
|
|
|
|
|
}
|
2013-04-25 17:40:06 +02:00
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
endEditBlock();
|
|
|
|
|
breakEditBlock();
|
2013-04-25 17:40:06 +02:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->lastInsertion = text;
|
2014-02-03 20:47:47 +01:00
|
|
|
g.dotCommand = dotCommand;
|
|
|
|
|
} else {
|
|
|
|
|
moveLeft(qMin(1, leftDist()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newLineBefore || newLineAfter)
|
2016-03-18 12:10:24 +01:00
|
|
|
m_buffer->lastInsertion.remove(0, m_buffer->lastInsertion.indexOf('\n') + 1);
|
|
|
|
|
g.dotCommand.append(m_buffer->lastInsertion + "<ESC>");
|
2014-02-03 20:47:47 +01:00
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
setTargetColumn();
|
2014-02-03 20:47:47 +01:00
|
|
|
enterCommandMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::handleInsertMode(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
if (input.isEscape()) {
|
2015-03-28 21:14:37 +01:00
|
|
|
if (g.submode == CtrlRSubMode || g.submode == CtrlVSubMode) {
|
|
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
|
|
|
|
updateMiniBuffer();
|
|
|
|
|
} else {
|
|
|
|
|
finishInsertMode();
|
|
|
|
|
}
|
|
|
|
|
} else if (g.submode == CtrlRSubMode) {
|
|
|
|
|
m_cursor.insertText(registerContents(input.asChar().unicode()));
|
|
|
|
|
g.submode = NoSubMode;
|
2014-04-28 13:13:10 +02:00
|
|
|
} else if (g.submode == CtrlVSubMode) {
|
|
|
|
|
if (g.subsubmode == NoSubSubMode) {
|
|
|
|
|
g.subsubmode = CtrlVUnicodeSubSubMode;
|
|
|
|
|
m_ctrlVAccumulator = 0;
|
|
|
|
|
if (input.is('x') || input.is('X')) {
|
|
|
|
|
// ^VXnn or ^Vxnn with 00 <= nn <= FF
|
|
|
|
|
// BMP Unicode codepoints ^Vunnnn with 0000 <= nnnn <= FFFF
|
|
|
|
|
// any Unicode codepoint ^VUnnnnnnnn with 00000000 <= nnnnnnnn <= 7FFFFFFF
|
|
|
|
|
// ^Vnnn with 000 <= nnn <= 255
|
|
|
|
|
// ^VOnnn or ^Vonnn with 000 <= nnn <= 377
|
|
|
|
|
m_ctrlVLength = 2;
|
|
|
|
|
m_ctrlVBase = 16;
|
|
|
|
|
} else if (input.is('O') || input.is('o')) {
|
|
|
|
|
m_ctrlVLength = 3;
|
|
|
|
|
m_ctrlVBase = 8;
|
|
|
|
|
} else if (input.is('u')) {
|
|
|
|
|
m_ctrlVLength = 4;
|
|
|
|
|
m_ctrlVBase = 16;
|
|
|
|
|
} else if (input.is('U')) {
|
|
|
|
|
m_ctrlVLength = 8;
|
|
|
|
|
m_ctrlVBase = 16;
|
|
|
|
|
} else if (input.isDigit()) {
|
|
|
|
|
bool ok;
|
|
|
|
|
m_ctrlVAccumulator = input.toInt(&ok, 10);
|
|
|
|
|
m_ctrlVLength = 2;
|
|
|
|
|
m_ctrlVBase = 10;
|
|
|
|
|
} else {
|
|
|
|
|
insertInInsertMode(input.raw());
|
|
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
bool ok;
|
|
|
|
|
int current = input.toInt(&ok, m_ctrlVBase);
|
|
|
|
|
if (ok)
|
|
|
|
|
m_ctrlVAccumulator = m_ctrlVAccumulator * m_ctrlVBase + current;
|
|
|
|
|
--m_ctrlVLength;
|
|
|
|
|
if (m_ctrlVLength == 0 || !ok) {
|
|
|
|
|
QString s;
|
|
|
|
|
if (QChar::requiresSurrogates(m_ctrlVAccumulator)) {
|
|
|
|
|
s.append(QChar(QChar::highSurrogate(m_ctrlVAccumulator)));
|
|
|
|
|
s.append(QChar(QChar::lowSurrogate(m_ctrlVAccumulator)));
|
|
|
|
|
} else {
|
|
|
|
|
s.append(QChar(m_ctrlVAccumulator));
|
|
|
|
|
}
|
|
|
|
|
insertInInsertMode(s);
|
|
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
|
|
|
|
|
|
|
|
|
// Try again without Ctrl-V interpretation.
|
|
|
|
|
if (!ok)
|
|
|
|
|
handleInsertMode(input);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-11-10 10:36:05 +01:00
|
|
|
} else if (input.isControl('o')) {
|
|
|
|
|
enterCommandMode(InsertMode);
|
2010-07-06 09:20:40 +02:00
|
|
|
} else if (input.isControl('v')) {
|
2014-04-28 13:13:10 +02:00
|
|
|
g.submode = CtrlVSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
2015-03-28 21:14:37 +01:00
|
|
|
updateMiniBuffer();
|
|
|
|
|
} else if (input.isControl('r')) {
|
|
|
|
|
g.submode = CtrlRSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
|
|
|
|
updateMiniBuffer();
|
2010-10-08 12:51:19 +02:00
|
|
|
} else if (input.isControl('w')) {
|
2013-04-08 20:06:17 +02:00
|
|
|
const int blockNumber = m_cursor.blockNumber();
|
2012-11-23 20:25:15 +01:00
|
|
|
const int endPos = position();
|
2013-04-25 17:40:06 +02:00
|
|
|
moveToNextWordStart(1, false, false);
|
2013-04-08 20:06:17 +02:00
|
|
|
if (blockNumber != m_cursor.blockNumber())
|
2012-11-23 20:25:15 +01:00
|
|
|
moveToEndOfLine();
|
|
|
|
|
const int beginPos = position();
|
2010-10-08 12:51:19 +02:00
|
|
|
Range range(beginPos, endPos, RangeCharMode);
|
|
|
|
|
removeText(range);
|
2015-03-22 11:33:10 +01:00
|
|
|
} else if (input.isControl('u')) {
|
|
|
|
|
const int blockNumber = m_cursor.blockNumber();
|
|
|
|
|
const int endPos = position();
|
|
|
|
|
moveToStartOfLine();
|
|
|
|
|
if (blockNumber != m_cursor.blockNumber())
|
|
|
|
|
moveToEndOfLine();
|
|
|
|
|
const int beginPos = position();
|
|
|
|
|
Range range(beginPos, endPos, RangeCharMode);
|
|
|
|
|
removeText(range);
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Insert)) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.mode = ReplaceMode;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Left)) {
|
2013-04-25 17:40:06 +02:00
|
|
|
moveLeft();
|
2016-09-30 19:58:55 +02:00
|
|
|
} else if (input.isShift(Key_Left) || input.isControl(Key_Left)) {
|
2013-04-25 17:40:06 +02:00
|
|
|
moveToNextWordStart(1, false, false);
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Down)) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2013-04-25 17:40:06 +02:00
|
|
|
moveDown();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Up)) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2013-04-25 17:40:06 +02:00
|
|
|
moveUp();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Right)) {
|
2013-04-25 17:40:06 +02:00
|
|
|
moveRight();
|
2016-09-30 19:58:55 +02:00
|
|
|
} else if (input.isShift(Key_Right) || input.isControl(Key_Right)) {
|
2013-04-25 17:40:06 +02:00
|
|
|
moveToNextWordStart(1, false, true);
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Home)) {
|
2010-04-07 18:08:08 +02:00
|
|
|
moveToStartOfLine();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_End)) {
|
2010-04-07 18:08:08 +02:00
|
|
|
moveBehindEndOfLine();
|
2013-01-16 17:49:37 +01:00
|
|
|
m_targetColumn = -1;
|
2012-11-23 20:51:16 +01:00
|
|
|
} else if (input.isReturn() || input.isControl('j') || input.isControl('m')) {
|
2013-07-09 19:39:31 +02:00
|
|
|
if (!input.isReturn() || !handleInsertInEditor(input)) {
|
2013-04-01 12:03:22 +02:00
|
|
|
joinPreviousEditBlock();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.submode = NoSubMode;
|
2013-04-01 12:03:22 +02:00
|
|
|
insertNewLine();
|
|
|
|
|
endEditBlock();
|
|
|
|
|
}
|
2010-05-10 16:41:35 +02:00
|
|
|
} else if (input.isBackspace()) {
|
2015-03-22 11:21:28 +01:00
|
|
|
// pass C-h as backspace, too
|
|
|
|
|
if (!handleInsertInEditor(Input(Qt::Key_Backspace, Qt::NoModifier))) {
|
2013-04-01 12:03:22 +02:00
|
|
|
joinPreviousEditBlock();
|
2014-04-30 17:32:25 +02:00
|
|
|
if (!m_buffer->lastInsertion.isEmpty()
|
2013-04-01 12:03:22 +02:00
|
|
|
|| hasConfig(ConfigBackspace, "start")
|
|
|
|
|
|| hasConfig(ConfigBackspace, "2")) {
|
|
|
|
|
const int line = cursorLine() + 1;
|
|
|
|
|
const Column col = cursorColumn();
|
|
|
|
|
QString data = lineContents(line);
|
|
|
|
|
const Column ind = indentation(data);
|
|
|
|
|
if (col.logical <= ind.logical && col.logical
|
|
|
|
|
&& startsWithWhitespace(data, col.physical)) {
|
|
|
|
|
const int ts = config(ConfigTabStop).toInt();
|
|
|
|
|
const int newl = col.logical - 1 - (col.logical - 1) % ts;
|
|
|
|
|
const QString prefix = tabExpand(newl);
|
|
|
|
|
setLineContents(line, prefix + data.mid(col.physical));
|
|
|
|
|
moveToStartOfLine();
|
|
|
|
|
moveRight(prefix.size());
|
|
|
|
|
} else {
|
|
|
|
|
setAnchor();
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor.deletePreviousChar();
|
2013-04-01 12:03:22 +02:00
|
|
|
}
|
2009-04-03 11:54:29 +02:00
|
|
|
}
|
2013-04-01 12:03:22 +02:00
|
|
|
endEditBlock();
|
2010-02-15 17:55:26 +01:00
|
|
|
}
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Delete)) {
|
2013-07-09 19:39:31 +02:00
|
|
|
if (!handleInsertInEditor(input)) {
|
2013-04-01 12:03:22 +02:00
|
|
|
joinPreviousEditBlock();
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor.deleteChar();
|
2013-04-01 12:03:22 +02:00
|
|
|
endEditBlock();
|
|
|
|
|
}
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_PageDown) || input.isControl('f')) {
|
2013-04-25 17:40:06 +02:00
|
|
|
movePageDown();
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_PageUp) || input.isControl('b')) {
|
2013-04-25 17:40:06 +02:00
|
|
|
movePageUp();
|
2010-07-06 15:12:24 +02:00
|
|
|
} else if (input.isKey(Key_Tab)) {
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->insertState.insertingSpaces = true;
|
2010-07-06 15:12:24 +02:00
|
|
|
if (hasConfig(ConfigExpandTab)) {
|
|
|
|
|
const int ts = config(ConfigTabStop).toInt();
|
|
|
|
|
const int col = logicalCursorColumn();
|
2016-03-18 12:10:24 +01:00
|
|
|
QString str = QString(ts - col % ts, ' ');
|
2010-07-06 15:12:24 +02:00
|
|
|
insertText(str);
|
|
|
|
|
} else {
|
|
|
|
|
insertInInsertMode(input.raw());
|
|
|
|
|
}
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->insertState.insertingSpaces = false;
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isControl('d')) {
|
2010-02-19 13:01:38 +01:00
|
|
|
// remove one level of indentation from the current line
|
|
|
|
|
int shift = config(ConfigShiftWidth).toInt();
|
|
|
|
|
int tab = config(ConfigTabStop).toInt();
|
2010-07-06 10:12:21 +02:00
|
|
|
int line = cursorLine() + 1;
|
2010-02-19 13:01:38 +01:00
|
|
|
int pos = firstPositionInLine(line);
|
|
|
|
|
QString text = lineContents(line);
|
|
|
|
|
int amount = 0;
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (; i < text.size() && amount < shift; ++i) {
|
2016-03-18 12:10:24 +01:00
|
|
|
if (text.at(i) == ' ')
|
2010-02-19 13:01:38 +01:00
|
|
|
++amount;
|
2016-03-18 12:10:24 +01:00
|
|
|
else if (text.at(i) == '\t')
|
2010-02-19 13:01:38 +01:00
|
|
|
amount += tab; // FIXME: take position into consideration
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
removeText(Range(pos, pos+i));
|
2010-12-21 11:36:42 +01:00
|
|
|
} else if (input.isControl('p') || input.isControl('n')) {
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2013-04-25 17:40:06 +02:00
|
|
|
moveToNextWordStart(1, false, false);
|
2010-12-21 11:36:42 +01:00
|
|
|
QString str = selectText(Range(position(), tc.position()));
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor = tc;
|
2018-02-09 18:49:18 +01:00
|
|
|
q->simpleCompletionRequested(str, input.isControl('n'));
|
2013-02-28 14:57:17 +01:00
|
|
|
} else if (input.isShift(Qt::Key_Insert)) {
|
|
|
|
|
// Insert text from clipboard.
|
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
|
const QMimeData *data = clipboard->mimeData();
|
|
|
|
|
if (data && data->hasText())
|
|
|
|
|
insertInInsertMode(data->text());
|
2012-11-24 10:24:48 +01:00
|
|
|
} else {
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->insertState.insertingSpaces = input.isKey(Key_Space);
|
2013-07-09 19:39:31 +02:00
|
|
|
if (!handleInsertInEditor(input)) {
|
|
|
|
|
const QString toInsert = input.text();
|
|
|
|
|
if (toInsert.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
insertInInsertMode(toInsert);
|
2013-04-25 17:40:06 +02:00
|
|
|
}
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->insertState.insertingSpaces = false;
|
2012-11-24 10:24:48 +01:00
|
|
|
}
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2010-07-06 09:20:40 +02:00
|
|
|
void FakeVimHandler::Private::insertInInsertMode(const QString &text)
|
|
|
|
|
{
|
|
|
|
|
joinPreviousEditBlock();
|
|
|
|
|
insertText(text);
|
|
|
|
|
if (hasConfig(ConfigSmartIndent) && isElectricCharacter(text.at(0))) {
|
2010-09-13 15:23:20 +02:00
|
|
|
const QString leftText = block().text()
|
|
|
|
|
.left(position() - 1 - block().position());
|
2010-07-06 09:20:40 +02:00
|
|
|
if (leftText.simplified().isEmpty()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
Range range(position(), position(), g.rangemode);
|
2010-07-06 09:20:40 +02:00
|
|
|
indentText(range, text.at(0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
endEditBlock();
|
2014-04-28 13:13:10 +02:00
|
|
|
g.submode = NoSubMode;
|
2010-07-06 09:20:40 +02:00
|
|
|
}
|
|
|
|
|
|
2013-03-17 12:41:18 +01:00
|
|
|
bool FakeVimHandler::Private::startRecording(const Input &input)
|
|
|
|
|
{
|
|
|
|
|
QChar reg = input.asChar();
|
2016-03-18 12:10:24 +01:00
|
|
|
if (reg == '"' || reg.isLetterOrNumber()) {
|
2013-03-17 12:41:18 +01:00
|
|
|
g.currentRegister = reg.unicode();
|
2014-10-21 12:53:29 +02:00
|
|
|
g.isRecording = true;
|
|
|
|
|
g.recorded.clear();
|
2013-03-17 12:41:18 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::record(const Input &input)
|
|
|
|
|
{
|
2014-10-21 12:53:29 +02:00
|
|
|
if (g.isRecording)
|
|
|
|
|
g.recorded.append(input.toString());
|
2013-03-17 12:41:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::stopRecording()
|
|
|
|
|
{
|
|
|
|
|
// Remove q from end (stop recording command).
|
2014-10-21 12:53:29 +02:00
|
|
|
g.isRecording = false;
|
|
|
|
|
g.recorded.chop(1);
|
|
|
|
|
setRegister(g.currentRegister, g.recorded, g.rangemode);
|
2013-03-17 12:41:18 +01:00
|
|
|
g.currentRegister = 0;
|
2014-10-21 12:53:29 +02:00
|
|
|
g.recorded.clear();
|
2013-03-17 12:41:18 +01:00
|
|
|
}
|
|
|
|
|
|
2014-10-26 14:57:57 +01:00
|
|
|
void FakeVimHandler::Private::handleAs(const QString &command)
|
|
|
|
|
{
|
2016-03-18 12:10:24 +01:00
|
|
|
QString cmd = QString("\"%1").arg(QChar(m_register));
|
2014-11-09 19:12:23 +01:00
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if (command.contains("%1"))
|
2014-11-09 19:12:23 +01:00
|
|
|
cmd.append(command.arg(count()));
|
|
|
|
|
else
|
|
|
|
|
cmd.append(command);
|
|
|
|
|
|
|
|
|
|
leaveVisualMode();
|
2014-10-26 14:57:57 +01:00
|
|
|
beginLargeEditBlock();
|
|
|
|
|
replay(cmd);
|
|
|
|
|
endEditBlock();
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-17 12:41:18 +01:00
|
|
|
bool FakeVimHandler::Private::executeRegister(int reg)
|
|
|
|
|
{
|
|
|
|
|
QChar regChar(reg);
|
|
|
|
|
|
|
|
|
|
// TODO: Prompt for an expression to execute if register is '='.
|
|
|
|
|
if (reg == '@' && g.lastExecutedRegister != 0)
|
|
|
|
|
reg = g.lastExecutedRegister;
|
2016-03-18 12:10:24 +01:00
|
|
|
else if (QString("\".*+").contains(regChar) || regChar.isLetterOrNumber())
|
2013-03-17 12:41:18 +01:00
|
|
|
g.lastExecutedRegister = reg;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// FIXME: In Vim it's possible to interrupt recursive macro with <C-c>.
|
|
|
|
|
// One solution may be to call QApplication::processEvents() and check if <C-c> was
|
|
|
|
|
// used when a mapping is active.
|
|
|
|
|
// According to Vim, register is executed like mapping.
|
2013-03-21 19:37:32 +01:00
|
|
|
prependMapping(Inputs(registerContents(reg), false, false));
|
2013-03-17 12:41:18 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-05 16:23:39 +02:00
|
|
|
EventResult FakeVimHandler::Private::handleExMode(const Input &input)
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2015-03-29 11:36:47 +02:00
|
|
|
// handle C-R, C-R C-W, C-R {register}
|
|
|
|
|
if (handleCommandBufferPaste(input))
|
|
|
|
|
return EventHandled;
|
|
|
|
|
|
2010-05-18 18:24:00 +02:00
|
|
|
if (input.isEscape()) {
|
2012-09-17 17:44:05 +02:00
|
|
|
g.commandBuffer.clear();
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2014-04-28 13:13:10 +02:00
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
} else if (g.submode == CtrlVSubMode) {
|
2012-09-17 17:44:05 +02:00
|
|
|
g.commandBuffer.insertChar(input.raw());
|
2014-04-28 13:13:10 +02:00
|
|
|
g.submode = NoSubMode;
|
2010-07-06 09:20:40 +02:00
|
|
|
} else if (input.isControl('v')) {
|
2014-04-28 13:13:10 +02:00
|
|
|
g.submode = CtrlVSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
2012-09-09 10:32:45 +02:00
|
|
|
return EventHandled;
|
2010-05-10 16:41:35 +02:00
|
|
|
} else if (input.isBackspace()) {
|
2012-11-10 10:36:05 +01:00
|
|
|
if (g.commandBuffer.isEmpty()) {
|
2013-09-15 16:19:05 +02:00
|
|
|
leaveVisualMode();
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2012-12-30 15:19:15 +01:00
|
|
|
} else if (g.commandBuffer.hasSelection()) {
|
|
|
|
|
g.commandBuffer.deleteSelected();
|
2012-11-10 10:36:05 +01:00
|
|
|
} else {
|
2012-09-17 17:44:05 +02:00
|
|
|
g.commandBuffer.deleteChar();
|
2012-11-10 10:36:05 +01:00
|
|
|
}
|
2010-09-20 18:04:35 +02:00
|
|
|
} else if (input.isKey(Key_Tab)) {
|
2012-09-09 10:32:45 +02:00
|
|
|
// FIXME: Complete actual commands.
|
2012-09-17 17:44:05 +02:00
|
|
|
g.commandBuffer.historyUp();
|
2010-05-10 16:41:35 +02:00
|
|
|
} else if (input.isReturn()) {
|
2012-09-10 22:10:23 +02:00
|
|
|
showMessage(MessageCommand, g.commandBuffer.display());
|
|
|
|
|
handleExCommand(g.commandBuffer.contents());
|
|
|
|
|
g.commandBuffer.clear();
|
2012-09-17 17:44:05 +02:00
|
|
|
} else if (!g.commandBuffer.handleInput(input)) {
|
2010-05-05 16:23:39 +02:00
|
|
|
qDebug() << "IGNORED IN EX-MODE: " << input.key() << input.text();
|
|
|
|
|
return EventUnhandled;
|
|
|
|
|
}
|
2014-11-14 18:47:07 +01:00
|
|
|
|
2010-05-05 16:23:39 +02:00
|
|
|
return EventHandled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
|
|
|
|
|
{
|
2012-11-06 17:29:04 +01:00
|
|
|
EventResult handled = EventHandled;
|
|
|
|
|
|
2015-03-29 11:36:47 +02:00
|
|
|
// handle C-R, C-R C-W, C-R {register}
|
|
|
|
|
if (handleCommandBufferPaste(input))
|
|
|
|
|
return handled;
|
|
|
|
|
|
2010-05-18 18:24:00 +02:00
|
|
|
if (input.isEscape()) {
|
2012-09-17 17:44:05 +02:00
|
|
|
g.currentMessage.clear();
|
2013-09-15 16:19:05 +02:00
|
|
|
setPosition(m_searchStartPosition);
|
2012-09-01 10:44:02 +02:00
|
|
|
scrollToLine(m_searchFromScreenLine);
|
2010-05-10 16:41:35 +02:00
|
|
|
} else if (input.isBackspace()) {
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (g.searchBuffer.isEmpty())
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else
|
2012-09-17 17:44:05 +02:00
|
|
|
g.searchBuffer.deleteChar();
|
2010-05-18 18:24:00 +02:00
|
|
|
} else if (input.isReturn()) {
|
2012-09-17 17:44:05 +02:00
|
|
|
const QString &needle = g.searchBuffer.contents();
|
2012-09-30 13:20:48 +02:00
|
|
|
if (!needle.isEmpty())
|
2012-10-03 16:40:05 +02:00
|
|
|
g.lastSearch = needle;
|
2012-09-30 13:20:48 +02:00
|
|
|
else
|
2012-10-03 16:40:05 +02:00
|
|
|
g.searchBuffer.setContents(g.lastSearch);
|
2013-09-15 16:19:05 +02:00
|
|
|
|
|
|
|
|
updateFind(true);
|
|
|
|
|
|
|
|
|
|
if (finishSearch()) {
|
|
|
|
|
if (g.submode != NoSubMode)
|
2016-03-18 12:10:24 +01:00
|
|
|
finishMovement(g.searchBuffer.prompt() + g.lastSearch + '\n');
|
2013-09-15 16:19:05 +02:00
|
|
|
if (g.currentMessage.isEmpty())
|
|
|
|
|
showMessage(MessageCommand, g.searchBuffer.display());
|
2012-09-01 10:44:02 +02:00
|
|
|
} else {
|
2013-06-09 15:23:44 +02:00
|
|
|
handled = EventCancelled; // Not found so cancel mapping if any.
|
2013-09-15 16:19:05 +02:00
|
|
|
}
|
2010-04-28 14:00:44 +02:00
|
|
|
} else if (input.isKey(Key_Tab)) {
|
2012-09-17 17:44:05 +02:00
|
|
|
g.searchBuffer.insertChar(QChar(9));
|
|
|
|
|
} else if (!g.searchBuffer.handleInput(input)) {
|
2012-09-10 22:10:23 +02:00
|
|
|
//qDebug() << "IGNORED IN SEARCH MODE: " << input.key() << input.text();
|
2012-09-09 10:32:45 +02:00
|
|
|
return EventUnhandled;
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
2012-09-10 22:10:23 +02:00
|
|
|
|
2013-09-15 16:19:05 +02:00
|
|
|
if (input.isReturn() || input.isEscape()) {
|
|
|
|
|
g.searchBuffer.clear();
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2013-09-15 16:19:05 +02:00
|
|
|
} else {
|
2012-09-10 22:10:23 +02:00
|
|
|
updateFind(false);
|
2013-09-15 16:19:05 +02:00
|
|
|
}
|
2010-05-18 18:24:00 +02:00
|
|
|
|
2012-11-06 17:29:04 +01:00
|
|
|
return handled;
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
// This uses 0 based line counting (hidden lines included).
|
2012-10-26 18:34:58 +02:00
|
|
|
int FakeVimHandler::Private::parseLineAddress(QString *cmd)
|
2008-12-28 02:15:26 +01:00
|
|
|
{
|
|
|
|
|
//qDebug() << "CMD: " << cmd;
|
2012-10-26 18:34:58 +02:00
|
|
|
if (cmd->isEmpty())
|
2008-12-28 02:15:26 +01:00
|
|
|
return -1;
|
2012-10-23 16:35:13 +02:00
|
|
|
|
|
|
|
|
int result = -1;
|
2012-10-26 18:34:58 +02:00
|
|
|
QChar c = cmd->at(0);
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '.') { // current line
|
2012-10-23 16:35:13 +02:00
|
|
|
result = cursorBlockNumber();
|
2012-10-26 18:34:58 +02:00
|
|
|
cmd->remove(0, 1);
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '$') { // last line
|
2012-10-23 16:35:13 +02:00
|
|
|
result = document()->blockCount() - 1;
|
2012-10-26 18:34:58 +02:00
|
|
|
cmd->remove(0, 1);
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '\'') { // mark
|
2012-10-26 18:34:58 +02:00
|
|
|
cmd->remove(0, 1);
|
|
|
|
|
if (cmd->isEmpty()) {
|
2012-09-10 22:10:23 +02:00
|
|
|
showMessage(MessageError, msgMarkNotSet(QString()));
|
2010-05-11 14:26:37 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
2012-10-26 18:34:58 +02:00
|
|
|
c = cmd->at(0);
|
2012-10-28 14:00:50 +01:00
|
|
|
Mark m = mark(c);
|
|
|
|
|
if (!m.isValid() || !m.isLocal(m_currentFileName)) {
|
2012-10-26 18:34:58 +02:00
|
|
|
showMessage(MessageError, msgMarkNotSet(c));
|
2008-12-29 14:47:42 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2012-10-26 18:34:58 +02:00
|
|
|
cmd->remove(0, 1);
|
2014-02-03 20:47:47 +01:00
|
|
|
result = m.position(document()).line;
|
2012-10-23 16:35:13 +02:00
|
|
|
} else if (c.isDigit()) { // line with given number
|
|
|
|
|
result = 0;
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '-' || c == '+') { // add or subtract from current line number
|
2012-10-23 16:35:13 +02:00
|
|
|
result = cursorBlockNumber();
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '/' || c == '?'
|
|
|
|
|
|| (c == '\\' && cmd->size() > 1 && QString("/?&").contains(cmd->at(1)))) {
|
2012-10-26 18:34:58 +02:00
|
|
|
// search for expression
|
|
|
|
|
SearchData sd;
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '/' || c == '?') {
|
2012-10-26 18:34:58 +02:00
|
|
|
const int end = findUnescaped(c, *cmd, 1);
|
|
|
|
|
if (end == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
sd.needle = cmd->mid(1, end - 1);
|
|
|
|
|
cmd->remove(0, end + 1);
|
|
|
|
|
} else {
|
|
|
|
|
c = cmd->at(1);
|
|
|
|
|
cmd->remove(0, 2);
|
2016-03-18 12:10:24 +01:00
|
|
|
sd.needle = (c == '&') ? g.lastSubstitutePattern : g.lastSearch;
|
2012-10-26 18:34:58 +02:00
|
|
|
}
|
2016-03-18 12:10:24 +01:00
|
|
|
sd.forward = (c != '?');
|
2012-10-26 18:34:58 +02:00
|
|
|
const QTextBlock b = block();
|
|
|
|
|
const int pos = b.position() + (sd.forward ? b.length() - 1 : 0);
|
|
|
|
|
QTextCursor tc = search(sd, pos, 1, true);
|
|
|
|
|
g.lastSearch = sd.needle;
|
|
|
|
|
if (tc.isNull())
|
|
|
|
|
return -1;
|
|
|
|
|
result = tc.block().blockNumber();
|
2012-10-23 16:35:13 +02:00
|
|
|
} else {
|
2012-10-26 18:34:58 +02:00
|
|
|
return cursorBlockNumber();
|
2012-10-23 16:35:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// basic arithmetic ("-3+5" or "++" means "+2" etc.)
|
|
|
|
|
int n = 0;
|
|
|
|
|
bool add = true;
|
|
|
|
|
int i = 0;
|
2012-10-26 18:34:58 +02:00
|
|
|
for (; i < cmd->size(); ++i) {
|
|
|
|
|
c = cmd->at(i);
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '-' || c == '+') {
|
2012-10-23 16:35:13 +02:00
|
|
|
if (n != 0)
|
|
|
|
|
result = result + (add ? n - 1 : -(n - 1));
|
2016-03-18 12:10:24 +01:00
|
|
|
add = c == '+';
|
2012-10-23 16:35:13 +02:00
|
|
|
result = result + (add ? 1 : -1);
|
|
|
|
|
n = 0;
|
|
|
|
|
} else if (c.isDigit()) {
|
|
|
|
|
n = n * 10 + c.digitValue();
|
|
|
|
|
} else if (!c.isSpace()) {
|
|
|
|
|
break;
|
2008-12-28 02:15:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
2012-10-23 16:35:13 +02:00
|
|
|
if (n != 0)
|
|
|
|
|
result = result + (add ? n - 1 : -(n - 1));
|
2012-10-26 18:34:58 +02:00
|
|
|
*cmd = cmd->mid(i).trimmed();
|
2012-10-23 16:35:13 +02:00
|
|
|
|
|
|
|
|
return result;
|
2008-12-28 02:15:26 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void FakeVimHandler::Private::setCurrentRange(const Range &range)
|
2009-01-06 11:33:07 +01:00
|
|
|
{
|
2010-09-14 16:58:31 +02:00
|
|
|
setAnchorAndPosition(range.beginPos, range.endPos);
|
2013-05-06 20:02:22 +02:00
|
|
|
g.rangemode = range.rangemode;
|
2009-01-06 11:33:07 +01:00
|
|
|
}
|
|
|
|
|
|
2014-12-19 11:31:44 +01:00
|
|
|
bool FakeVimHandler::Private::parseExCommand(QString *line, ExCommand *cmd)
|
2011-03-28 13:24:25 +02:00
|
|
|
{
|
2012-10-26 18:34:58 +02:00
|
|
|
*cmd = ExCommand();
|
|
|
|
|
if (line->isEmpty())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// parse range first
|
|
|
|
|
if (!parseLineRange(line, cmd))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// get first command from command line
|
|
|
|
|
QChar close;
|
|
|
|
|
bool subst = false;
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (; i < line->size(); ++i) {
|
|
|
|
|
const QChar &c = line->at(i);
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '\\') {
|
2012-10-26 18:34:58 +02:00
|
|
|
++i; // skip escaped character
|
|
|
|
|
} else if (close.isNull()) {
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '|') {
|
2012-10-26 18:34:58 +02:00
|
|
|
// split on |
|
|
|
|
|
break;
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '/') {
|
|
|
|
|
subst = i > 0 && (line->at(i - 1) == 's');
|
2012-10-26 18:34:58 +02:00
|
|
|
close = c;
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '"' || c == '\'') {
|
2012-10-26 18:34:58 +02:00
|
|
|
close = c;
|
|
|
|
|
}
|
|
|
|
|
} else if (c == close) {
|
|
|
|
|
if (subst)
|
|
|
|
|
subst = false;
|
|
|
|
|
else
|
|
|
|
|
close = QChar();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd->cmd = line->mid(0, i).trimmed();
|
|
|
|
|
|
|
|
|
|
// command arguments starts with first non-letter character
|
2020-06-22 15:40:34 +02:00
|
|
|
cmd->args = cmd->cmd.section(QRegularExpression("(?=[^a-zA-Z])"), 1);
|
2012-10-26 18:34:58 +02:00
|
|
|
if (!cmd->args.isEmpty()) {
|
|
|
|
|
cmd->cmd.chop(cmd->args.size());
|
|
|
|
|
cmd->args = cmd->args.trimmed();
|
|
|
|
|
|
|
|
|
|
// '!' at the end of command
|
2016-03-18 12:10:24 +01:00
|
|
|
cmd->hasBang = cmd->args.startsWith('!');
|
2012-10-26 18:34:58 +02:00
|
|
|
if (cmd->hasBang)
|
|
|
|
|
cmd->args = cmd->args.mid(1).trimmed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove the first command from command line
|
|
|
|
|
line->remove(0, i + 1);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::parseLineRange(QString *line, ExCommand *cmd)
|
|
|
|
|
{
|
2013-11-20 19:37:49 +01:00
|
|
|
// remove leading colons and spaces
|
2020-06-22 15:40:34 +02:00
|
|
|
line->remove(QRegularExpression("^\\s*(:+\\s*)*"));
|
2013-11-20 19:37:49 +01:00
|
|
|
|
|
|
|
|
// special case ':!...' (use invalid range)
|
2016-03-18 12:10:24 +01:00
|
|
|
if (line->startsWith('!')) {
|
2013-11-20 19:37:49 +01:00
|
|
|
cmd->range = Range();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
// FIXME: that seems to be different for %w and %s
|
2016-03-18 12:10:24 +01:00
|
|
|
if (line->startsWith('%'))
|
|
|
|
|
line->replace(0, 1, "1,$");
|
2012-10-26 18:34:58 +02:00
|
|
|
|
|
|
|
|
int beginLine = parseLineAddress(line);
|
|
|
|
|
int endLine;
|
2016-03-18 12:10:24 +01:00
|
|
|
if (line->startsWith(',')) {
|
2012-10-26 18:34:58 +02:00
|
|
|
*line = line->mid(1).trimmed();
|
|
|
|
|
endLine = parseLineAddress(line);
|
|
|
|
|
} else {
|
|
|
|
|
endLine = beginLine;
|
|
|
|
|
}
|
|
|
|
|
if (beginLine == -1 || endLine == -1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const int beginPos = firstPositionInLine(qMin(beginLine, endLine) + 1, false);
|
|
|
|
|
const int endPos = lastPositionInLine(qMax(beginLine, endLine) + 1, false);
|
|
|
|
|
cmd->range = Range(beginPos, endPos, RangeLineMode);
|
|
|
|
|
cmd->count = beginLine;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::parseRangeCount(const QString &line, Range *range) const
|
|
|
|
|
{
|
|
|
|
|
bool ok;
|
|
|
|
|
const int count = qAbs(line.trimmed().toInt(&ok));
|
|
|
|
|
if (ok) {
|
2014-11-09 16:37:23 +01:00
|
|
|
const int beginLine = blockAt(range->endPos).blockNumber() + 1;
|
2012-10-26 18:34:58 +02:00
|
|
|
const int endLine = qMin(beginLine + count - 1, document()->blockCount());
|
|
|
|
|
range->beginPos = firstPositionInLine(beginLine, false);
|
|
|
|
|
range->endPos = lastPositionInLine(endLine, false);
|
|
|
|
|
}
|
2011-03-28 13:24:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-03-18 13:15:59 +01:00
|
|
|
// use handleExCommand for invoking commands that might move the cursor
|
2009-04-06 10:58:48 +02:00
|
|
|
void FakeVimHandler::Private::handleCommand(const QString &cmd)
|
|
|
|
|
{
|
|
|
|
|
handleExCommand(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-11 14:26:37 +02:00
|
|
|
bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2012-10-26 18:34:58 +02:00
|
|
|
// :substitute
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("s", "substitute")
|
|
|
|
|
&& !(cmd.cmd.isEmpty() && !cmd.args.isEmpty() && QString("&~").contains(cmd.args[0]))) {
|
2012-10-26 18:34:58 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-21 17:30:16 +02:00
|
|
|
int count = 1;
|
2012-10-26 18:34:58 +02:00
|
|
|
QString line = cmd.args;
|
2020-06-11 09:34:00 +02:00
|
|
|
const QRegularExpressionMatch match = QRegularExpression("\\d+$").match(line);
|
|
|
|
|
if (match.hasMatch()) {
|
|
|
|
|
count = match.captured().toInt();
|
|
|
|
|
line = line.left(match.capturedStart()).trimmed();
|
2012-10-26 18:34:58 +02:00
|
|
|
}
|
2011-11-28 17:59:30 +01:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
if (cmd.cmd.isEmpty()) {
|
|
|
|
|
// keep previous substitution flags on '&&' and '~&'
|
2016-03-18 12:10:24 +01:00
|
|
|
if (line.size() > 1 && line[1] == '&')
|
2019-01-17 01:38:54 +01:00
|
|
|
g.lastSubstituteFlags += line.midRef(2);
|
2011-11-28 17:59:30 +01:00
|
|
|
else
|
2012-10-26 18:34:58 +02:00
|
|
|
g.lastSubstituteFlags = line.mid(1);
|
2016-03-18 12:10:24 +01:00
|
|
|
if (line[0] == '~')
|
2012-10-26 18:34:58 +02:00
|
|
|
g.lastSubstitutePattern = g.lastSearch;
|
|
|
|
|
} else {
|
|
|
|
|
if (line.isEmpty()) {
|
|
|
|
|
g.lastSubstituteFlags.clear();
|
|
|
|
|
} else {
|
|
|
|
|
// we have /{pattern}/{string}/[flags] now
|
|
|
|
|
const QChar separator = line.at(0);
|
|
|
|
|
int pos1 = findUnescaped(separator, line, 1);
|
|
|
|
|
if (pos1 == -1)
|
|
|
|
|
return false;
|
2013-09-03 18:48:02 +02:00
|
|
|
int pos2 = findUnescaped(separator, line, pos1 + 1);
|
2012-10-26 18:34:58 +02:00
|
|
|
if (pos2 == -1)
|
|
|
|
|
pos2 = line.size();
|
2011-11-28 17:59:30 +01:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
g.lastSubstitutePattern = line.mid(1, pos1 - 1);
|
|
|
|
|
g.lastSubstituteReplacement = line.mid(pos1 + 1, pos2 - pos1 - 1);
|
|
|
|
|
g.lastSubstituteFlags = line.mid(pos2 + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-28 17:59:30 +01:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
count = qMax(1, count);
|
|
|
|
|
QString needle = g.lastSubstitutePattern;
|
2012-10-21 17:30:16 +02:00
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if (g.lastSubstituteFlags.contains('i'))
|
|
|
|
|
needle.prepend("\\c");
|
2011-11-28 17:59:30 +01:00
|
|
|
|
2020-06-22 16:49:25 +02:00
|
|
|
const QRegularExpression pattern = vimPatternToQtPattern(needle,
|
|
|
|
|
hasConfig(ConfigIgnoreCase),
|
|
|
|
|
hasConfig(ConfigSmartCase));
|
2009-12-09 17:40:00 +01:00
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
QTextBlock lastBlock;
|
|
|
|
|
QTextBlock firstBlock;
|
2016-03-18 12:10:24 +01:00
|
|
|
const bool global = g.lastSubstituteFlags.contains('g');
|
2011-11-28 17:59:30 +01:00
|
|
|
for (int a = 0; a != count; ++a) {
|
2014-11-09 16:37:23 +01:00
|
|
|
for (QTextBlock block = blockAt(cmd.range.endPos);
|
2012-10-26 18:34:58 +02:00
|
|
|
block.isValid() && block.position() + block.length() > cmd.range.beginPos;
|
2012-10-23 16:35:13 +02:00
|
|
|
block = block.previous()) {
|
|
|
|
|
QString text = block.text();
|
2012-10-26 18:34:58 +02:00
|
|
|
if (substituteText(&text, pattern, g.lastSubstituteReplacement, global)) {
|
2012-10-23 16:35:13 +02:00
|
|
|
firstBlock = block;
|
|
|
|
|
if (!lastBlock.isValid()) {
|
|
|
|
|
lastBlock = block;
|
2012-09-29 19:09:08 +02:00
|
|
|
beginEditBlock();
|
|
|
|
|
}
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2012-10-23 16:35:13 +02:00
|
|
|
const int pos = block.position();
|
|
|
|
|
const int anchor = pos + block.length() - 1;
|
|
|
|
|
tc.setPosition(anchor);
|
|
|
|
|
tc.setPosition(pos, KeepAnchor);
|
|
|
|
|
tc.insertText(text);
|
2012-10-21 17:30:16 +02:00
|
|
|
}
|
2010-04-16 16:30:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
2012-09-29 19:09:08 +02:00
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
if (lastBlock.isValid()) {
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->undoState.position = CursorPosition(firstBlock.blockNumber(), 0);
|
2012-09-29 19:09:08 +02:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
leaveVisualMode();
|
2012-10-23 16:35:13 +02:00
|
|
|
setPosition(lastBlock.position());
|
2012-10-26 18:34:58 +02:00
|
|
|
setAnchor();
|
2012-09-29 19:09:08 +02:00
|
|
|
moveToFirstNonBlankOnLine();
|
|
|
|
|
|
|
|
|
|
endEditBlock();
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-09 17:40:00 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-05 15:16:52 +02:00
|
|
|
bool FakeVimHandler::Private::handleExTabNextCommand(const ExCommand &cmd)
|
|
|
|
|
{
|
2017-09-08 13:39:37 +02:00
|
|
|
if (!cmd.matches("tabn", "tabnext"))
|
2017-07-05 15:16:52 +02:00
|
|
|
return false;
|
|
|
|
|
|
2018-02-09 18:49:18 +01:00
|
|
|
q->tabNextRequested();
|
2017-07-05 15:16:52 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleExTabPreviousCommand(const ExCommand &cmd)
|
|
|
|
|
{
|
2017-09-08 13:39:37 +02:00
|
|
|
if (!cmd.matches("tabp", "tabprevious"))
|
2017-07-05 15:16:52 +02:00
|
|
|
return false;
|
|
|
|
|
|
2018-02-09 18:49:18 +01:00
|
|
|
q->tabPreviousRequested();
|
2017-07-05 15:16:52 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-11 14:26:37 +02:00
|
|
|
bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
|
2010-03-19 14:31:21 +01:00
|
|
|
{
|
|
|
|
|
QByteArray modes;
|
|
|
|
|
enum Type { Map, Noremap, Unmap } type;
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
QByteArray cmd = cmd0.cmd.toLatin1();
|
2010-03-19 14:31:21 +01:00
|
|
|
|
|
|
|
|
// Strange formatting. But everything else is even uglier.
|
|
|
|
|
if (cmd == "map") { modes = "nvo"; type = Map; } else
|
|
|
|
|
if (cmd == "nm" || cmd == "nmap") { modes = "n"; type = Map; } else
|
|
|
|
|
if (cmd == "vm" || cmd == "vmap") { modes = "v"; type = Map; } else
|
|
|
|
|
if (cmd == "xm" || cmd == "xmap") { modes = "x"; type = Map; } else
|
|
|
|
|
if (cmd == "smap") { modes = "s"; type = Map; } else
|
2013-08-11 17:15:56 +02:00
|
|
|
if (cmd == "omap") { modes = "o"; type = Map; } else
|
2010-03-19 14:31:21 +01:00
|
|
|
if (cmd == "map!") { modes = "ic"; type = Map; } else
|
|
|
|
|
if (cmd == "im" || cmd == "imap") { modes = "i"; type = Map; } else
|
|
|
|
|
if (cmd == "lm" || cmd == "lmap") { modes = "l"; type = Map; } else
|
|
|
|
|
if (cmd == "cm" || cmd == "cmap") { modes = "c"; type = Map; } else
|
|
|
|
|
|
|
|
|
|
if (cmd == "no" || cmd == "noremap") { modes = "nvo"; type = Noremap; } else
|
|
|
|
|
if (cmd == "nn" || cmd == "nnoremap") { modes = "n"; type = Noremap; } else
|
|
|
|
|
if (cmd == "vn" || cmd == "vnoremap") { modes = "v"; type = Noremap; } else
|
|
|
|
|
if (cmd == "xn" || cmd == "xnoremap") { modes = "x"; type = Noremap; } else
|
|
|
|
|
if (cmd == "snor" || cmd == "snoremap") { modes = "s"; type = Noremap; } else
|
|
|
|
|
if (cmd == "ono" || cmd == "onoremap") { modes = "o"; type = Noremap; } else
|
|
|
|
|
if (cmd == "no!" || cmd == "noremap!") { modes = "ic"; type = Noremap; } else
|
|
|
|
|
if (cmd == "ino" || cmd == "inoremap") { modes = "i"; type = Noremap; } else
|
|
|
|
|
if (cmd == "ln" || cmd == "lnoremap") { modes = "l"; type = Noremap; } else
|
|
|
|
|
if (cmd == "cno" || cmd == "cnoremap") { modes = "c"; type = Noremap; } else
|
|
|
|
|
|
|
|
|
|
if (cmd == "unm" || cmd == "unmap") { modes = "nvo"; type = Unmap; } else
|
|
|
|
|
if (cmd == "nun" || cmd == "nunmap") { modes = "n"; type = Unmap; } else
|
|
|
|
|
if (cmd == "vu" || cmd == "vunmap") { modes = "v"; type = Unmap; } else
|
|
|
|
|
if (cmd == "xu" || cmd == "xunmap") { modes = "x"; type = Unmap; } else
|
|
|
|
|
if (cmd == "sunm" || cmd == "sunmap") { modes = "s"; type = Unmap; } else
|
|
|
|
|
if (cmd == "ou" || cmd == "ounmap") { modes = "o"; type = Unmap; } else
|
|
|
|
|
if (cmd == "unm!" || cmd == "unmap!") { modes = "ic"; type = Unmap; } else
|
|
|
|
|
if (cmd == "iu" || cmd == "iunmap") { modes = "i"; type = Unmap; } else
|
|
|
|
|
if (cmd == "lu" || cmd == "lunmap") { modes = "l"; type = Unmap; } else
|
|
|
|
|
if (cmd == "cu" || cmd == "cunmap") { modes = "c"; type = Unmap; }
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
QString args = cmd0.args;
|
|
|
|
|
bool silent = false;
|
|
|
|
|
bool unique = false;
|
|
|
|
|
forever {
|
|
|
|
|
if (eatString("<silent>", &args)) {
|
|
|
|
|
silent = true;
|
|
|
|
|
} else if (eatString("<unique>", &args)) {
|
|
|
|
|
continue;
|
|
|
|
|
} else if (eatString("<special>", &args)) {
|
|
|
|
|
continue;
|
|
|
|
|
} else if (eatString("<buffer>", &args)) {
|
|
|
|
|
notImplementedYet();
|
|
|
|
|
continue;
|
|
|
|
|
} else if (eatString("<script>", &args)) {
|
|
|
|
|
notImplementedYet();
|
|
|
|
|
continue;
|
|
|
|
|
} else if (eatString("<expr>", &args)) {
|
|
|
|
|
notImplementedYet();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 15:40:34 +02:00
|
|
|
const QString lhs = args.section(QRegularExpression("\\s+"), 0, 0);
|
|
|
|
|
const QString rhs = args.section(QRegularExpression("\\s+"), 1);
|
2012-09-01 07:47:25 +02:00
|
|
|
if ((rhs.isNull() && type != Unmap) || (!rhs.isNull() && type == Unmap)) {
|
2010-05-10 09:01:30 +02:00
|
|
|
// FIXME: Dump mappings here.
|
|
|
|
|
//qDebug() << g.mappings;
|
2011-04-05 16:32:18 +02:00
|
|
|
return true;
|
2010-05-10 09:01:30 +02:00
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
Inputs key(lhs);
|
2010-04-16 15:16:48 +02:00
|
|
|
//qDebug() << "MAPPING: " << modes << lhs << rhs;
|
2010-03-19 14:31:21 +01:00
|
|
|
switch (type) {
|
|
|
|
|
case Unmap:
|
|
|
|
|
foreach (char c, modes)
|
2012-09-01 07:47:25 +02:00
|
|
|
MappingsIterator(&g.mappings, c, key).remove();
|
2010-03-19 14:31:21 +01:00
|
|
|
break;
|
2018-01-10 16:51:58 +01:00
|
|
|
case Map: Q_FALLTHROUGH();
|
2010-03-26 13:22:06 +01:00
|
|
|
case Noremap: {
|
2012-09-01 07:47:25 +02:00
|
|
|
Inputs inputs(rhs, type == Noremap, silent);
|
2010-03-19 14:31:21 +01:00
|
|
|
foreach (char c, modes)
|
2012-09-01 07:47:25 +02:00
|
|
|
MappingsIterator(&g.mappings, c).setInputs(key, inputs, unique);
|
2010-03-19 14:31:21 +01:00
|
|
|
break;
|
2010-03-26 13:22:06 +01:00
|
|
|
}
|
2010-03-19 14:31:21 +01:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
bool FakeVimHandler::Private::handleExHistoryCommand(const ExCommand &cmd)
|
2008-12-27 16:42:07 +01:00
|
|
|
{
|
2010-07-14 18:15:17 +02:00
|
|
|
// :his[tory]
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("his", "history"))
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
2009-04-01 15:52:48 +02:00
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
if (cmd.args.isEmpty()) {
|
2010-04-16 16:30:45 +02:00
|
|
|
QString info;
|
2016-03-18 12:10:24 +01:00
|
|
|
info += "# command history\n";
|
2010-04-16 16:30:45 +02:00
|
|
|
int i = 0;
|
2012-09-17 17:44:05 +02:00
|
|
|
foreach (const QString &item, g.commandBuffer.historyItems()) {
|
2010-04-16 16:30:45 +02:00
|
|
|
++i;
|
2016-03-18 12:10:24 +01:00
|
|
|
info += QString("%1 %2\n").arg(i, -8).arg(item);
|
2010-04-16 16:30:45 +02:00
|
|
|
}
|
2018-02-09 18:49:18 +01:00
|
|
|
q->extraInformationChanged(info);
|
2010-04-16 16:30:45 +02:00
|
|
|
} else {
|
|
|
|
|
notImplementedYet();
|
|
|
|
|
}
|
2014-11-14 18:47:07 +01:00
|
|
|
|
2010-04-16 16:30:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2008-12-28 02:15:26 +01:00
|
|
|
|
2010-05-20 14:08:11 +02:00
|
|
|
bool FakeVimHandler::Private::handleExRegisterCommand(const ExCommand &cmd)
|
|
|
|
|
{
|
2010-07-14 18:15:17 +02:00
|
|
|
// :reg[isters] and :di[splay]
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("reg", "registers") && !cmd.matches("di", "display"))
|
2010-05-20 14:08:11 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
QByteArray regs = cmd.args.toLatin1();
|
|
|
|
|
if (regs.isEmpty()) {
|
|
|
|
|
regs = "\"0123456789";
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto it = g.registers.cbegin(), end = g.registers.cend(); it != end; ++it) {
|
2010-05-20 14:08:11 +02:00
|
|
|
if (it.key() > '9')
|
|
|
|
|
regs += char(it.key());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
QString info;
|
2016-03-18 12:10:24 +01:00
|
|
|
info += "--- Registers ---\n";
|
2020-05-28 07:46:41 +02:00
|
|
|
for (char reg : qAsConst(regs)) {
|
2011-11-12 02:31:52 +01:00
|
|
|
QString value = quoteUnprintable(registerContents(reg));
|
2016-03-18 12:10:24 +01:00
|
|
|
info += QString("\"%1 %2\n").arg(reg).arg(value);
|
2010-05-20 14:08:11 +02:00
|
|
|
}
|
2018-02-09 18:49:18 +01:00
|
|
|
q->extraInformationChanged(info);
|
2014-11-14 18:47:07 +01:00
|
|
|
|
2010-05-20 14:08:11 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2010-07-14 18:15:17 +02:00
|
|
|
// :se[t]
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("se", "set"))
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
2009-01-08 13:16:04 +01:00
|
|
|
|
2012-09-10 22:10:23 +02:00
|
|
|
clearMessage();
|
2014-01-20 18:37:00 +01:00
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if (cmd.args.contains('=')) {
|
2010-05-18 14:48:12 +02:00
|
|
|
// Non-boolean config to set.
|
2016-03-18 12:10:24 +01:00
|
|
|
int p = cmd.args.indexOf('=');
|
2012-05-03 10:43:04 +02:00
|
|
|
QString error = theFakeVimSettings()
|
|
|
|
|
->trySetValue(cmd.args.left(p), cmd.args.mid(p + 1));
|
|
|
|
|
if (!error.isEmpty())
|
2012-09-10 22:10:23 +02:00
|
|
|
showMessage(MessageError, error);
|
2010-04-16 16:30:45 +02:00
|
|
|
} else {
|
2014-01-20 18:37:00 +01:00
|
|
|
QString optionName = cmd.args;
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
bool toggleOption = optionName.endsWith('!');
|
|
|
|
|
bool printOption = !toggleOption && optionName.endsWith('?');
|
2014-01-20 18:37:00 +01:00
|
|
|
if (printOption || toggleOption)
|
|
|
|
|
optionName.chop(1);
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
bool negateOption = optionName.startsWith("no");
|
2014-01-20 18:37:00 +01:00
|
|
|
if (negateOption)
|
|
|
|
|
optionName.remove(0, 2);
|
|
|
|
|
|
2016-03-01 23:24:59 +01:00
|
|
|
FakeVimAction *act = theFakeVimSettings()->item(optionName);
|
2014-01-20 18:37:00 +01:00
|
|
|
if (!act) {
|
2016-03-18 12:10:24 +01:00
|
|
|
showMessage(MessageError, Tr::tr("Unknown option:") + ' ' + cmd.args);
|
2014-01-20 18:37:00 +01:00
|
|
|
} else if (act->defaultValue().type() == QVariant::Bool) {
|
|
|
|
|
bool oldValue = act->value().toBool();
|
|
|
|
|
if (printOption) {
|
2016-03-18 12:10:24 +01:00
|
|
|
showMessage(MessageInfo, QLatin1String(oldValue ? "" : "no")
|
2014-01-20 18:37:00 +01:00
|
|
|
+ act->settingsKey().toLower());
|
|
|
|
|
} else if (toggleOption || negateOption == oldValue) {
|
|
|
|
|
act->setValue(!oldValue);
|
|
|
|
|
}
|
|
|
|
|
} else if (negateOption && !printOption) {
|
2016-03-18 12:10:24 +01:00
|
|
|
showMessage(MessageError, Tr::tr("Invalid argument:") + ' ' + cmd.args);
|
2014-01-20 18:37:00 +01:00
|
|
|
} else if (toggleOption) {
|
2016-03-18 12:10:24 +01:00
|
|
|
showMessage(MessageError, Tr::tr("Trailing characters:") + ' ' + cmd.args);
|
2014-01-20 18:37:00 +01:00
|
|
|
} else {
|
2016-03-18 12:10:24 +01:00
|
|
|
showMessage(MessageInfo, act->settingsKey().toLower() + "="
|
2014-01-20 18:37:00 +01:00
|
|
|
+ act->value().toString());
|
|
|
|
|
}
|
2008-12-28 02:15:26 +01:00
|
|
|
}
|
2010-04-16 16:30:45 +02:00
|
|
|
updateEditor();
|
2014-01-20 18:37:00 +01:00
|
|
|
updateHighlights();
|
2010-04-16 16:30:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
bool FakeVimHandler::Private::handleExNormalCommand(const ExCommand &cmd)
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2010-07-14 18:15:17 +02:00
|
|
|
// :norm[al]
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("norm", "normal"))
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
2010-05-04 17:58:53 +02:00
|
|
|
//qDebug() << "REPLAY NORMAL: " << quoteUnprintable(reNormal.cap(3));
|
2012-11-06 17:29:04 +01:00
|
|
|
replay(cmd.args);
|
2010-04-16 16:30:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2008-12-28 02:15:26 +01:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
bool FakeVimHandler::Private::handleExYankDeleteCommand(const ExCommand &cmd)
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2012-10-26 18:34:58 +02:00
|
|
|
// :[range]d[elete] [x] [count]
|
|
|
|
|
// :[range]y[ank] [x] [count]
|
2016-03-18 12:10:24 +01:00
|
|
|
const bool remove = cmd.matches("d", "delete");
|
|
|
|
|
if (!remove && !cmd.matches("y", "yank"))
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
2009-10-16 11:30:46 +02:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
// get register from arguments
|
|
|
|
|
const bool hasRegisterArg = !cmd.args.isEmpty() && !cmd.args.at(0).isDigit();
|
|
|
|
|
const int r = hasRegisterArg ? cmd.args.at(0).unicode() : m_register;
|
|
|
|
|
|
|
|
|
|
// get [count] from arguments
|
|
|
|
|
Range range = cmd.range;
|
|
|
|
|
parseRangeCount(cmd.args.mid(hasRegisterArg ? 1 : 0).trimmed(), &range);
|
|
|
|
|
|
|
|
|
|
yankText(range, r);
|
|
|
|
|
|
|
|
|
|
if (remove) {
|
|
|
|
|
leaveVisualMode();
|
|
|
|
|
setPosition(range.beginPos);
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2012-10-26 18:34:58 +02:00
|
|
|
setCurrentRange(range);
|
|
|
|
|
removeText(currentRange());
|
2010-04-16 16:30:45 +02:00
|
|
|
}
|
2012-10-26 18:34:58 +02:00
|
|
|
|
2010-04-16 16:30:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2008-12-28 02:15:26 +01:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
bool FakeVimHandler::Private::handleExChangeCommand(const ExCommand &cmd)
|
2012-10-23 16:35:13 +02:00
|
|
|
{
|
2012-10-26 18:34:58 +02:00
|
|
|
// :[range]c[hange]
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("c", "change"))
|
2012-10-23 16:35:13 +02:00
|
|
|
return false;
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
Range range = cmd.range;
|
|
|
|
|
range.rangemode = RangeLineModeExclusive;
|
|
|
|
|
removeText(range);
|
2013-03-08 17:50:16 +01:00
|
|
|
insertAutomaticIndentation(true, cmd.hasBang);
|
2012-10-26 18:34:58 +02:00
|
|
|
|
|
|
|
|
// FIXME: In Vim same or less number of lines can be inserted and position after insertion is
|
|
|
|
|
// beginning of last inserted line.
|
|
|
|
|
enterInsertMode();
|
|
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleExMoveCommand(const ExCommand &cmd)
|
|
|
|
|
{
|
|
|
|
|
// :[range]m[ove] {address}
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("m", "move"))
|
2012-10-23 16:35:13 +02:00
|
|
|
return false;
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
QString lineCode = cmd.args;
|
2012-10-23 16:35:13 +02:00
|
|
|
|
2014-11-09 16:37:23 +01:00
|
|
|
const int startLine = blockAt(cmd.range.beginPos).blockNumber();
|
|
|
|
|
const int endLine = blockAt(cmd.range.endPos).blockNumber();
|
2012-10-23 16:35:13 +02:00
|
|
|
const int lines = endLine - startLine + 1;
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
int targetLine = lineCode == "0" ? -1 : parseLineAddress(&lineCode);
|
2012-10-26 18:34:58 +02:00
|
|
|
if (targetLine >= startLine && targetLine < endLine) {
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr("Move lines into themselves."));
|
2012-10-23 16:35:13 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
CursorPosition lastAnchor = markLessPosition();
|
|
|
|
|
CursorPosition lastPosition = markGreaterPosition();
|
2012-10-26 18:34:58 +02:00
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
recordJump();
|
2012-10-26 18:34:58 +02:00
|
|
|
setPosition(cmd.range.beginPos);
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2012-10-23 16:35:13 +02:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
setCurrentRange(cmd.range);
|
|
|
|
|
QString text = selectText(cmd.range);
|
2012-10-23 16:35:13 +02:00
|
|
|
removeText(currentRange());
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
const bool insertAtEnd = targetLine == document()->blockCount();
|
|
|
|
|
if (targetLine >= startLine)
|
|
|
|
|
targetLine -= lines;
|
2012-10-23 16:35:13 +02:00
|
|
|
QTextBlock block = document()->findBlockByNumber(insertAtEnd ? targetLine : targetLine + 1);
|
2012-10-26 18:34:58 +02:00
|
|
|
setPosition(block.position());
|
|
|
|
|
setAnchor();
|
|
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
if (insertAtEnd) {
|
2012-10-26 18:34:58 +02:00
|
|
|
moveBehindEndOfLine();
|
|
|
|
|
text.chop(1);
|
2016-03-18 12:10:24 +01:00
|
|
|
insertText(QString("\n"));
|
2012-10-23 16:35:13 +02:00
|
|
|
}
|
|
|
|
|
insertText(text);
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
if (!insertAtEnd)
|
|
|
|
|
moveUp(1);
|
2012-10-23 16:35:13 +02:00
|
|
|
if (hasConfig(ConfigStartOfLine))
|
|
|
|
|
moveToFirstNonBlankOnLine();
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
if (lastAnchor.line >= startLine && lastAnchor.line <= endLine)
|
|
|
|
|
lastAnchor.line += targetLine - startLine + 1;
|
|
|
|
|
if (lastPosition.line >= startLine && lastPosition.line <= endLine)
|
|
|
|
|
lastPosition.line += targetLine - startLine + 1;
|
2016-03-18 12:10:24 +01:00
|
|
|
setMark('<', lastAnchor);
|
|
|
|
|
setMark('>', lastPosition);
|
2012-10-26 18:34:58 +02:00
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
if (lines > 2)
|
2018-11-07 23:45:39 +01:00
|
|
|
showMessage(MessageInfo, Tr::tr("%n lines moved.", nullptr, lines));
|
2012-10-23 16:35:13 +02:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
bool FakeVimHandler::Private::handleExJoinCommand(const ExCommand &cmd)
|
|
|
|
|
{
|
|
|
|
|
// :[range]j[oin][!] [count]
|
|
|
|
|
// FIXME: Argument [count] can follow immediately.
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("j", "join"))
|
2012-10-26 18:34:58 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// get [count] from arguments
|
|
|
|
|
bool ok;
|
|
|
|
|
int count = cmd.args.toInt(&ok);
|
|
|
|
|
|
|
|
|
|
if (ok) {
|
|
|
|
|
setPosition(cmd.range.endPos);
|
|
|
|
|
} else {
|
|
|
|
|
setPosition(cmd.range.beginPos);
|
2014-11-09 16:37:23 +01:00
|
|
|
const int startLine = blockAt(cmd.range.beginPos).blockNumber();
|
|
|
|
|
const int endLine = blockAt(cmd.range.endPos).blockNumber();
|
2012-10-26 18:34:58 +02:00
|
|
|
count = endLine - startLine + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
moveToStartOfLine();
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2012-10-26 18:34:58 +02:00
|
|
|
joinLines(count, cmd.hasBang);
|
|
|
|
|
|
|
|
|
|
moveToFirstNonBlankOnLine();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-11 14:26:37 +02:00
|
|
|
bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2016-06-03 13:00:51 +02:00
|
|
|
// Note: The cmd.args.isEmpty() case is handled by handleExPluginCommand.
|
2010-05-18 14:48:12 +02:00
|
|
|
// :w, :x, :wq, ...
|
2020-06-22 15:40:34 +02:00
|
|
|
//static QRegularExpression reWrite("^[wx]q?a?!?( (.*))?$");
|
2016-03-18 12:10:24 +01:00
|
|
|
if (cmd.cmd != "w" && cmd.cmd != "x" && cmd.cmd != "wq")
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
int beginLine = lineForPosition(cmd.range.beginPos);
|
|
|
|
|
int endLine = lineForPosition(cmd.range.endPos);
|
|
|
|
|
const bool noArgs = (beginLine == -1);
|
2010-04-16 16:30:45 +02:00
|
|
|
if (beginLine == -1)
|
|
|
|
|
beginLine = 0;
|
|
|
|
|
if (endLine == -1)
|
|
|
|
|
endLine = linesInDocument();
|
|
|
|
|
//qDebug() << "LINES: " << beginLine << endLine;
|
2011-12-27 17:39:56 +01:00
|
|
|
//QString prefix = cmd.args;
|
2010-05-18 14:48:12 +02:00
|
|
|
const bool forced = cmd.hasBang;
|
2016-03-18 12:10:24 +01:00
|
|
|
//const bool quit = prefix.contains('q') || prefix.contains('x');
|
|
|
|
|
//const bool quitAll = quit && prefix.contains('a');
|
2016-06-03 13:00:51 +02:00
|
|
|
QString fileName = replaceTildeWithHome(cmd.args);
|
2010-04-16 16:30:45 +02:00
|
|
|
if (fileName.isEmpty())
|
|
|
|
|
fileName = m_currentFileName;
|
|
|
|
|
QFile file1(fileName);
|
2010-05-18 11:09:38 +02:00
|
|
|
const bool exists = file1.exists();
|
2010-04-16 16:30:45 +02:00
|
|
|
if (exists && !forced && !noArgs) {
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr
|
2010-05-18 11:09:38 +02:00
|
|
|
("File \"%1\" exists (add ! to override)").arg(fileName));
|
2010-04-16 16:30:45 +02:00
|
|
|
} else if (file1.open(QIODevice::ReadWrite)) {
|
2010-05-18 14:48:12 +02:00
|
|
|
// Nobody cared, so act ourselves.
|
2010-04-16 16:30:45 +02:00
|
|
|
file1.close();
|
|
|
|
|
Range range(firstPositionInLine(beginLine),
|
|
|
|
|
firstPositionInLine(endLine), RangeLineMode);
|
2010-05-12 11:18:18 +02:00
|
|
|
QString contents = selectText(range);
|
2010-05-18 14:48:12 +02:00
|
|
|
QFile::remove(fileName);
|
|
|
|
|
QFile file2(fileName);
|
|
|
|
|
if (file2.open(QIODevice::ReadWrite)) {
|
|
|
|
|
QTextStream ts(&file2);
|
|
|
|
|
ts << contents;
|
|
|
|
|
} else {
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr
|
2010-05-18 14:48:12 +02:00
|
|
|
("Cannot open file \"%1\" for writing").arg(fileName));
|
2009-01-09 15:36:02 +01:00
|
|
|
}
|
2010-05-18 11:09:38 +02:00
|
|
|
// Check result by reading back.
|
2010-04-16 16:30:45 +02:00
|
|
|
QFile file3(fileName);
|
|
|
|
|
file3.open(QIODevice::ReadOnly);
|
|
|
|
|
QByteArray ba = file3.readAll();
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageInfo, Tr::tr("\"%1\" %2 %3L, %4C written.")
|
2016-03-18 12:10:24 +01:00
|
|
|
.arg(fileName).arg(exists ? QString(" ") : Tr::tr(" [New] "))
|
2010-04-16 16:30:45 +02:00
|
|
|
.arg(ba.count('\n')).arg(ba.size()));
|
2010-05-18 14:48:12 +02:00
|
|
|
//if (quitAll)
|
|
|
|
|
// passUnknownExCommand(forced ? "qa!" : "qa");
|
|
|
|
|
//else if (quit)
|
|
|
|
|
// passUnknownExCommand(forced ? "q!" : "q");
|
2008-12-28 02:15:26 +01:00
|
|
|
} else {
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr
|
2010-05-18 11:09:38 +02:00
|
|
|
("Cannot open file \"%1\" for reading").arg(fileName));
|
2010-04-16 16:30:45 +02:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
bool FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd)
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2010-07-14 18:15:17 +02:00
|
|
|
// :r[ead]
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.matches("r", "read"))
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
beginEditBlock();
|
2013-03-03 09:15:43 +01:00
|
|
|
|
2010-04-16 16:30:45 +02:00
|
|
|
moveToStartOfLine();
|
|
|
|
|
moveDown();
|
2013-03-03 09:15:43 +01:00
|
|
|
int pos = position();
|
|
|
|
|
|
2016-06-03 13:00:51 +02:00
|
|
|
m_currentFileName = replaceTildeWithHome(cmd.args);
|
2010-04-16 16:30:45 +02:00
|
|
|
QFile file(m_currentFileName);
|
|
|
|
|
file.open(QIODevice::ReadOnly);
|
|
|
|
|
QTextStream ts(&file);
|
|
|
|
|
QString data = ts.readAll();
|
2010-09-14 16:58:31 +02:00
|
|
|
insertText(data);
|
2013-03-03 09:15:43 +01:00
|
|
|
|
|
|
|
|
setAnchorAndPosition(pos, pos);
|
|
|
|
|
|
2010-04-29 15:47:02 +02:00
|
|
|
endEditBlock();
|
2013-03-03 09:15:43 +01:00
|
|
|
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageInfo, Tr::tr("\"%1\" %2L, %3C")
|
2016-03-18 12:10:24 +01:00
|
|
|
.arg(m_currentFileName).arg(data.count('\n')).arg(data.size()));
|
2013-03-03 09:15:43 +01:00
|
|
|
|
2010-04-16 16:30:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-11 14:26:37 +02:00
|
|
|
bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2012-10-26 18:34:58 +02:00
|
|
|
if (!cmd.cmd.isEmpty() || !cmd.hasBang)
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
|
|
|
|
|
2013-11-20 19:37:49 +01:00
|
|
|
bool replaceText = cmd.range.isValid();
|
2016-03-18 12:10:24 +01:00
|
|
|
const QString command = QString(cmd.cmd.mid(1) + ' ' + cmd.args).trimmed();
|
2013-11-20 19:37:49 +01:00
|
|
|
const QString input = replaceText ? selectText(cmd.range) : QString();
|
|
|
|
|
|
|
|
|
|
const QString result = getProcessOutput(command, input);
|
|
|
|
|
|
|
|
|
|
if (replaceText) {
|
|
|
|
|
setCurrentRange(cmd.range);
|
|
|
|
|
int targetPosition = firstPositionInLine(lineForPosition(cmd.range.beginPos));
|
2012-05-08 18:10:05 +02:00
|
|
|
beginEditBlock();
|
|
|
|
|
removeText(currentRange());
|
|
|
|
|
insertText(result);
|
|
|
|
|
setPosition(targetPosition);
|
|
|
|
|
endEditBlock();
|
|
|
|
|
leaveVisualMode();
|
|
|
|
|
//qDebug() << "FILTER: " << command;
|
2018-11-07 23:45:39 +01:00
|
|
|
showMessage(MessageInfo, Tr::tr("%n lines filtered.", nullptr,
|
2016-03-18 12:10:24 +01:00
|
|
|
input.count('\n')));
|
2013-11-20 19:37:49 +01:00
|
|
|
} else if (!result.isEmpty()) {
|
2018-02-09 18:49:18 +01:00
|
|
|
q->extraInformationChanged(result);
|
2012-05-08 18:10:05 +02:00
|
|
|
}
|
2013-11-20 19:37:49 +01:00
|
|
|
|
2010-04-16 16:30:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
bool FakeVimHandler::Private::handleExShiftCommand(const ExCommand &cmd)
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2012-10-26 18:34:58 +02:00
|
|
|
// :[range]{<|>}* [count]
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!cmd.cmd.isEmpty() || (!cmd.args.startsWith('<') && !cmd.args.startsWith('>')))
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
const QChar c = cmd.args.at(0);
|
|
|
|
|
|
|
|
|
|
// get number of repetition
|
|
|
|
|
int repeat = 1;
|
|
|
|
|
int i = 1;
|
|
|
|
|
for (; i < cmd.args.size(); ++i) {
|
|
|
|
|
const QChar c2 = cmd.args.at(i);
|
|
|
|
|
if (c2 == c)
|
|
|
|
|
++repeat;
|
|
|
|
|
else if (!c2.isSpace())
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get [count] from arguments
|
2011-03-28 13:24:25 +02:00
|
|
|
Range range = cmd.range;
|
2012-10-26 18:34:58 +02:00
|
|
|
parseRangeCount(cmd.args.mid(i), &range);
|
|
|
|
|
|
2011-03-28 13:24:25 +02:00
|
|
|
setCurrentRange(range);
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '<')
|
2012-10-26 18:34:58 +02:00
|
|
|
shiftRegionLeft(repeat);
|
2010-05-18 14:48:12 +02:00
|
|
|
else
|
2012-10-26 18:34:58 +02:00
|
|
|
shiftRegionRight(repeat);
|
|
|
|
|
|
2010-04-16 16:30:45 +02:00
|
|
|
leaveVisualMode();
|
2012-10-26 18:34:58 +02:00
|
|
|
|
2010-04-16 16:30:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-12 14:13:07 +01:00
|
|
|
bool FakeVimHandler::Private::handleExSortCommand(const ExCommand &cmd)
|
|
|
|
|
{
|
|
|
|
|
// :[range]sor[t][!] [b][f][i][n][o][r][u][x] [/{pattern}/]
|
|
|
|
|
// FIXME: Only the ! for reverse is implemented.
|
|
|
|
|
if (!cmd.matches("sor", "sort"))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Force operation on full lines, and full document if only
|
|
|
|
|
// one line (the current one...) is specified
|
|
|
|
|
int beginLine = lineForPosition(cmd.range.beginPos);
|
|
|
|
|
int endLine = lineForPosition(cmd.range.endPos);
|
|
|
|
|
if (beginLine == endLine) {
|
|
|
|
|
beginLine = 0;
|
|
|
|
|
endLine = lineForPosition(lastPositionInDocument());
|
|
|
|
|
}
|
|
|
|
|
Range range(firstPositionInLine(beginLine),
|
|
|
|
|
firstPositionInLine(endLine), RangeLineMode);
|
|
|
|
|
|
|
|
|
|
QString input = selectText(range);
|
|
|
|
|
if (input.endsWith('\n')) // It should always...
|
|
|
|
|
input.chop(1);
|
|
|
|
|
|
|
|
|
|
QStringList lines = input.split('\n');
|
|
|
|
|
lines.sort();
|
|
|
|
|
if (cmd.hasBang)
|
|
|
|
|
std::reverse(lines.begin(), lines.end());
|
|
|
|
|
QString res = lines.join('\n') + '\n';
|
|
|
|
|
|
|
|
|
|
replaceText(range, res);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-14 16:04:10 +02:00
|
|
|
bool FakeVimHandler::Private::handleExNohlsearchCommand(const ExCommand &cmd)
|
|
|
|
|
{
|
2013-08-06 17:13:53 +02:00
|
|
|
// :noh, :nohl, ..., :nohlsearch
|
2016-03-18 12:10:24 +01:00
|
|
|
if (cmd.cmd.size() < 3 || !QString("nohlsearch").startsWith(cmd.cmd))
|
2010-07-14 16:04:10 +02:00
|
|
|
return false;
|
|
|
|
|
|
2013-08-06 17:13:53 +02:00
|
|
|
g.highlightsCleared = true;
|
|
|
|
|
updateHighlights();
|
2010-07-14 16:04:10 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-28 10:43:22 +01:00
|
|
|
bool FakeVimHandler::Private::handleExUndoRedoCommand(const ExCommand &cmd)
|
2012-08-26 18:39:48 +02:00
|
|
|
{
|
|
|
|
|
// :undo
|
2012-10-28 10:43:22 +01:00
|
|
|
// :redo
|
2016-03-18 12:10:24 +01:00
|
|
|
bool undo = (cmd.cmd == "u" || cmd.cmd == "un" || cmd.cmd == "undo");
|
|
|
|
|
if (!undo && cmd.cmd != "red" && cmd.cmd != "redo")
|
2012-08-26 18:39:48 +02:00
|
|
|
return false;
|
|
|
|
|
|
2012-10-28 10:43:22 +01:00
|
|
|
undoRedo(undo);
|
2014-11-14 18:47:07 +01:00
|
|
|
|
2012-08-26 18:39:48 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
bool FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd)
|
2010-04-16 16:30:45 +02:00
|
|
|
{
|
2012-10-26 18:34:58 +02:00
|
|
|
// :{address}
|
|
|
|
|
if (!cmd.cmd.isEmpty() || !cmd.args.isEmpty())
|
2010-04-16 16:30:45 +02:00
|
|
|
return false;
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
const int beginLine = lineForPosition(cmd.range.endPos);
|
2010-05-12 11:18:18 +02:00
|
|
|
setPosition(firstPositionInLine(beginLine));
|
2012-09-10 22:10:23 +02:00
|
|
|
clearMessage();
|
2010-04-16 16:30:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
|
2010-04-16 17:47:02 +02:00
|
|
|
{
|
2010-05-18 14:48:12 +02:00
|
|
|
// :source
|
2016-03-18 12:10:24 +01:00
|
|
|
if (cmd.cmd != "so" && cmd.cmd != "source")
|
2010-04-16 17:47:02 +02:00
|
|
|
return false;
|
|
|
|
|
|
2016-06-03 13:00:51 +02:00
|
|
|
QString fileName = replaceTildeWithHome(cmd.args);
|
2010-04-16 17:47:02 +02:00
|
|
|
QFile file(fileName);
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr("Cannot open file %1").arg(fileName));
|
2010-04-16 17:47:02 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool inFunction = false;
|
2012-09-29 19:09:08 +02:00
|
|
|
QByteArray line;
|
|
|
|
|
while (!file.atEnd() || !line.isEmpty()) {
|
|
|
|
|
QByteArray nextline = !file.atEnd() ? file.readLine() : QByteArray();
|
|
|
|
|
|
|
|
|
|
// remove comment
|
|
|
|
|
int i = nextline.lastIndexOf('"');
|
|
|
|
|
if (i != -1)
|
|
|
|
|
nextline = nextline.remove(i, nextline.size() - i);
|
|
|
|
|
|
|
|
|
|
nextline = nextline.trimmed();
|
|
|
|
|
|
|
|
|
|
// multi-line command?
|
|
|
|
|
if (nextline.startsWith('\\')) {
|
|
|
|
|
line += nextline.mid(1);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-16 17:47:02 +02:00
|
|
|
if (line.startsWith("function")) {
|
|
|
|
|
//qDebug() << "IGNORING FUNCTION" << line;
|
2010-04-28 14:00:44 +02:00
|
|
|
inFunction = true;
|
2010-04-16 17:47:02 +02:00
|
|
|
} else if (inFunction && line.startsWith("endfunction")) {
|
|
|
|
|
inFunction = false;
|
|
|
|
|
} else if (!line.isEmpty() && !inFunction) {
|
|
|
|
|
//qDebug() << "EXECUTING: " << line;
|
2011-11-10 15:47:17 +01:00
|
|
|
ExCommand cmd;
|
2012-10-26 18:34:58 +02:00
|
|
|
QString commandLine = QString::fromLocal8Bit(line);
|
2014-12-19 11:31:44 +01:00
|
|
|
while (parseExCommand(&commandLine, &cmd)) {
|
2012-09-29 19:09:08 +02:00
|
|
|
if (!handleExCommandHelper(cmd))
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-04-16 17:47:02 +02:00
|
|
|
}
|
2012-09-29 19:09:08 +02:00
|
|
|
|
|
|
|
|
line = nextline;
|
2010-04-16 17:47:02 +02:00
|
|
|
}
|
|
|
|
|
file.close();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-05 16:32:18 +02:00
|
|
|
bool FakeVimHandler::Private::handleExEchoCommand(const ExCommand &cmd)
|
|
|
|
|
{
|
|
|
|
|
// :echo
|
2016-03-18 12:10:24 +01:00
|
|
|
if (cmd.cmd != "echo")
|
2011-04-05 16:32:18 +02:00
|
|
|
return false;
|
2012-09-10 22:10:23 +02:00
|
|
|
showMessage(MessageInfo, cmd.args);
|
2011-04-05 16:32:18 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-16 16:30:45 +02:00
|
|
|
void FakeVimHandler::Private::handleExCommand(const QString &line0)
|
|
|
|
|
{
|
|
|
|
|
QString line = line0; // Make sure we have a copy to prevent aliasing.
|
2011-10-28 14:16:01 +02:00
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if (line.endsWith('%')) {
|
2011-10-28 14:16:01 +02:00
|
|
|
line.chop(1);
|
|
|
|
|
int percent = line.toInt();
|
|
|
|
|
setPosition(firstPositionInLine(percent * linesInDocument() / 100));
|
2012-09-10 22:10:23 +02:00
|
|
|
clearMessage();
|
2011-10-28 14:16:01 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-29 19:09:08 +02:00
|
|
|
//qDebug() << "CMD: " << cmd;
|
|
|
|
|
|
2012-11-10 10:36:05 +01:00
|
|
|
enterCommandMode(g.returnToMode);
|
2012-10-26 18:34:58 +02:00
|
|
|
|
2012-09-29 19:09:08 +02:00
|
|
|
beginLargeEditBlock();
|
2012-10-26 18:34:58 +02:00
|
|
|
ExCommand cmd;
|
|
|
|
|
QString lastCommand = line;
|
2014-12-19 11:31:44 +01:00
|
|
|
while (parseExCommand(&line, &cmd)) {
|
2012-09-29 19:09:08 +02:00
|
|
|
if (!handleExCommandHelper(cmd)) {
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr("Not an editor command: %1").arg(lastCommand));
|
2012-09-29 19:09:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2012-10-26 18:34:58 +02:00
|
|
|
lastCommand = line;
|
2012-09-29 19:09:08 +02:00
|
|
|
}
|
2013-03-20 13:11:33 +01:00
|
|
|
|
|
|
|
|
// if the last command closed the editor, we would crash here (:vs and then :on)
|
|
|
|
|
if (!(m_textedit || m_plaintextedit))
|
|
|
|
|
return;
|
|
|
|
|
|
2012-09-29 19:09:08 +02:00
|
|
|
endEditBlock();
|
2012-11-10 10:36:05 +01:00
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
if (isVisualMode())
|
|
|
|
|
leaveVisualMode();
|
2014-11-12 20:18:18 +01:00
|
|
|
leaveCurrentMode();
|
2012-09-29 19:09:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::handleExCommandHelper(ExCommand &cmd)
|
|
|
|
|
{
|
2010-05-18 14:48:12 +02:00
|
|
|
return handleExPluginCommand(cmd)
|
|
|
|
|
|| handleExGotoCommand(cmd)
|
2010-05-11 14:26:37 +02:00
|
|
|
|| handleExBangCommand(cmd)
|
|
|
|
|
|| handleExHistoryCommand(cmd)
|
2010-05-20 14:08:11 +02:00
|
|
|
|| handleExRegisterCommand(cmd)
|
2012-10-26 18:34:58 +02:00
|
|
|
|| handleExYankDeleteCommand(cmd)
|
|
|
|
|
|| handleExChangeCommand(cmd)
|
2012-10-23 16:35:13 +02:00
|
|
|
|| handleExMoveCommand(cmd)
|
2012-10-26 18:34:58 +02:00
|
|
|
|| handleExJoinCommand(cmd)
|
2010-05-11 14:26:37 +02:00
|
|
|
|| handleExMapCommand(cmd)
|
2010-07-14 16:04:10 +02:00
|
|
|
|| handleExNohlsearchCommand(cmd)
|
2010-05-11 14:26:37 +02:00
|
|
|
|| handleExNormalCommand(cmd)
|
|
|
|
|
|| handleExReadCommand(cmd)
|
2012-10-28 10:43:22 +01:00
|
|
|
|| handleExUndoRedoCommand(cmd)
|
2010-05-11 14:26:37 +02:00
|
|
|
|| handleExSetCommand(cmd)
|
2010-05-18 14:48:12 +02:00
|
|
|
|| handleExShiftCommand(cmd)
|
2018-03-12 14:13:07 +01:00
|
|
|
|| handleExSortCommand(cmd)
|
2010-05-11 14:26:37 +02:00
|
|
|
|| handleExSourceCommand(cmd)
|
|
|
|
|
|| handleExSubstituteCommand(cmd)
|
2017-07-05 15:16:52 +02:00
|
|
|
|| handleExTabNextCommand(cmd)
|
|
|
|
|
|| handleExTabPreviousCommand(cmd)
|
2011-04-05 16:32:18 +02:00
|
|
|
|| handleExWriteCommand(cmd)
|
|
|
|
|
|| handleExEchoCommand(cmd);
|
2008-12-27 16:42:07 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
bool FakeVimHandler::Private::handleExPluginCommand(const ExCommand &cmd)
|
2009-06-15 15:14:16 +02:00
|
|
|
{
|
2010-05-18 14:48:12 +02:00
|
|
|
bool handled = false;
|
2014-02-19 19:18:14 +01:00
|
|
|
int pos = m_cursor.position();
|
|
|
|
|
commitCursor();
|
2018-02-09 18:49:18 +01:00
|
|
|
q->handleExCommandRequested(&handled, cmd);
|
2010-05-18 14:48:12 +02:00
|
|
|
//qDebug() << "HANDLER REQUEST: " << cmd.cmd << handled;
|
2014-04-23 17:14:21 +02:00
|
|
|
if (handled && (m_textedit || m_plaintextedit)) {
|
2014-02-19 19:18:14 +01:00
|
|
|
pullCursor();
|
|
|
|
|
if (m_cursor.position() != pos)
|
|
|
|
|
recordJump(pos);
|
|
|
|
|
}
|
2010-05-18 14:48:12 +02:00
|
|
|
return handled;
|
2010-01-21 17:23:32 +01:00
|
|
|
}
|
|
|
|
|
|
2010-07-14 13:02:17 +02:00
|
|
|
void FakeVimHandler::Private::searchBalanced(bool forward, QChar needle, QChar other)
|
|
|
|
|
{
|
|
|
|
|
int level = 1;
|
2010-09-13 15:23:20 +02:00
|
|
|
int pos = position();
|
2010-07-14 13:02:17 +02:00
|
|
|
const int npos = forward ? lastPositionInDocument() : 0;
|
|
|
|
|
while (true) {
|
|
|
|
|
if (forward)
|
|
|
|
|
++pos;
|
|
|
|
|
else
|
|
|
|
|
--pos;
|
|
|
|
|
if (pos == npos)
|
|
|
|
|
return;
|
2014-11-17 15:32:00 +01:00
|
|
|
QChar c = characterAt(pos);
|
2010-07-14 13:02:17 +02:00
|
|
|
if (c == other)
|
|
|
|
|
++level;
|
|
|
|
|
else if (c == needle)
|
|
|
|
|
--level;
|
|
|
|
|
if (level == 0) {
|
|
|
|
|
const int oldLine = cursorLine() - cursorLineOnScreen();
|
|
|
|
|
// Making this unconditional feels better, but is not "vim like".
|
|
|
|
|
if (oldLine != cursorLine() - cursorLineOnScreen())
|
|
|
|
|
scrollToLine(cursorLine() - linesOnScreen() / 2);
|
2012-08-11 14:31:42 +02:00
|
|
|
recordJump();
|
2011-11-17 01:10:53 +01:00
|
|
|
setPosition(pos);
|
2010-07-14 13:02:17 +02:00
|
|
|
setTargetColumn();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
QTextCursor FakeVimHandler::Private::search(const SearchData &sd, int startPos, int count,
|
|
|
|
|
bool showMessages)
|
2008-12-19 12:20:04 +01:00
|
|
|
{
|
2020-06-22 16:49:25 +02:00
|
|
|
const QRegularExpression needleExp = vimPatternToQtPattern(sd.needle,
|
|
|
|
|
hasConfig(ConfigIgnoreCase),
|
|
|
|
|
hasConfig(ConfigSmartCase));
|
2012-11-18 19:58:02 +01:00
|
|
|
if (!needleExp.isValid()) {
|
|
|
|
|
if (showMessages) {
|
|
|
|
|
QString error = needleExp.errorString();
|
2014-08-25 17:05:11 +02:00
|
|
|
showMessage(MessageError, Tr::tr("Invalid regular expression: %1").arg(error));
|
2012-11-18 19:58:02 +01:00
|
|
|
}
|
|
|
|
|
if (sd.highlightMatches)
|
|
|
|
|
highlightMatches(QString());
|
|
|
|
|
return QTextCursor();
|
|
|
|
|
}
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
int repeat = count;
|
2012-11-18 19:58:02 +01:00
|
|
|
const int pos = startPos + (sd.forward ? 1 : -1);
|
|
|
|
|
|
|
|
|
|
QTextCursor tc;
|
|
|
|
|
if (pos >= 0 && pos < document()->characterCount()) {
|
|
|
|
|
tc = QTextCursor(document());
|
|
|
|
|
tc.setPosition(pos);
|
|
|
|
|
if (sd.forward && afterEndOfLine(document(), pos))
|
|
|
|
|
tc.movePosition(Right);
|
|
|
|
|
|
|
|
|
|
if (!tc.isNull()) {
|
|
|
|
|
if (sd.forward)
|
|
|
|
|
searchForward(&tc, needleExp, &repeat);
|
|
|
|
|
else
|
|
|
|
|
searchBackward(&tc, needleExp, &repeat);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-09-01 10:44:02 +02:00
|
|
|
|
2010-05-18 18:24:00 +02:00
|
|
|
if (tc.isNull()) {
|
2012-09-01 10:44:02 +02:00
|
|
|
if (hasConfig(ConfigWrapScan)) {
|
2012-11-18 19:58:02 +01:00
|
|
|
tc = QTextCursor(document());
|
|
|
|
|
tc.movePosition(sd.forward ? StartOfDocument : EndOfDocument);
|
|
|
|
|
if (sd.forward)
|
|
|
|
|
searchForward(&tc, needleExp, &repeat);
|
|
|
|
|
else
|
|
|
|
|
searchBackward(&tc, needleExp, &repeat);
|
2012-09-01 10:44:02 +02:00
|
|
|
if (tc.isNull()) {
|
2012-09-10 22:10:23 +02:00
|
|
|
if (showMessages) {
|
|
|
|
|
showMessage(MessageError,
|
2014-08-25 17:05:11 +02:00
|
|
|
Tr::tr("Pattern not found: %1").arg(sd.needle));
|
2012-09-10 22:10:23 +02:00
|
|
|
}
|
|
|
|
|
} else if (showMessages) {
|
2012-09-08 19:21:46 +02:00
|
|
|
QString msg = sd.forward
|
2014-08-25 17:05:11 +02:00
|
|
|
? Tr::tr("Search hit BOTTOM, continuing at TOP.")
|
|
|
|
|
: Tr::tr("Search hit TOP, continuing at BOTTOM.");
|
2012-09-10 22:10:23 +02:00
|
|
|
showMessage(MessageWarning, msg);
|
2010-05-18 18:24:00 +02:00
|
|
|
}
|
2012-09-10 22:10:23 +02:00
|
|
|
} else if (showMessages) {
|
2012-09-01 10:44:02 +02:00
|
|
|
QString msg = sd.forward
|
2014-08-25 17:05:11 +02:00
|
|
|
? Tr::tr("Search hit BOTTOM without match for: %1")
|
|
|
|
|
: Tr::tr("Search hit TOP without match for: %1");
|
2012-09-10 22:10:23 +02:00
|
|
|
showMessage(MessageError, msg.arg(sd.needle));
|
2009-03-20 09:26:34 +01:00
|
|
|
}
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
2010-05-18 18:24:00 +02:00
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
if (sd.highlightMatches)
|
|
|
|
|
highlightMatches(needleExp.pattern());
|
|
|
|
|
|
|
|
|
|
return tc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::search(const SearchData &sd, bool showMessages)
|
|
|
|
|
{
|
|
|
|
|
const int oldLine = cursorLine() - cursorLineOnScreen();
|
|
|
|
|
|
|
|
|
|
QTextCursor tc = search(sd, m_searchStartPosition, count(), showMessages);
|
2012-09-08 19:21:46 +02:00
|
|
|
if (tc.isNull()) {
|
2013-04-08 20:06:17 +02:00
|
|
|
tc = m_cursor;
|
2012-09-08 19:21:46 +02:00
|
|
|
tc.setPosition(m_searchStartPosition);
|
|
|
|
|
}
|
2012-08-11 14:31:42 +02:00
|
|
|
|
2012-09-08 19:21:46 +02:00
|
|
|
if (isVisualMode()) {
|
|
|
|
|
int d = tc.anchor() - tc.position();
|
|
|
|
|
setPosition(tc.position() + d);
|
|
|
|
|
} else {
|
|
|
|
|
// Set Cursor. In contrast to the main editor we have the cursor
|
|
|
|
|
// position before the anchor position.
|
|
|
|
|
setAnchorAndPosition(tc.position(), tc.anchor());
|
|
|
|
|
}
|
2010-05-18 18:24:00 +02:00
|
|
|
|
|
|
|
|
// Making this unconditional feels better, but is not "vim like".
|
2010-07-06 10:12:21 +02:00
|
|
|
if (oldLine != cursorLine() - cursorLineOnScreen())
|
|
|
|
|
scrollToLine(cursorLine() - linesOnScreen() / 2);
|
2010-05-18 18:24:00 +02:00
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
m_searchCursor = m_cursor;
|
2010-09-14 14:04:13 +02:00
|
|
|
|
2010-04-20 15:24:36 +02:00
|
|
|
setTargetColumn();
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2013-09-15 16:19:05 +02:00
|
|
|
bool FakeVimHandler::Private::searchNext(bool forward)
|
2012-09-01 10:44:02 +02:00
|
|
|
{
|
|
|
|
|
SearchData sd;
|
2012-10-03 16:40:05 +02:00
|
|
|
sd.needle = g.lastSearch;
|
|
|
|
|
sd.forward = forward ? g.lastSearchForward : !g.lastSearchForward;
|
2012-09-01 10:44:02 +02:00
|
|
|
sd.highlightMatches = true;
|
|
|
|
|
m_searchStartPosition = position();
|
2012-12-09 22:09:33 +02:00
|
|
|
showMessage(MessageCommand, QLatin1Char(g.lastSearchForward ? '/' : '?') + sd.needle);
|
2012-10-28 14:00:50 +01:00
|
|
|
recordJump();
|
2012-09-01 10:44:02 +02:00
|
|
|
search(sd);
|
2013-09-15 16:19:05 +02:00
|
|
|
return finishSearch();
|
2012-09-01 10:44:02 +02:00
|
|
|
}
|
|
|
|
|
|
2011-05-03 11:20:58 +02:00
|
|
|
void FakeVimHandler::Private::highlightMatches(const QString &needle)
|
2009-03-12 18:05:36 +01:00
|
|
|
{
|
2013-08-06 17:13:53 +02:00
|
|
|
g.lastNeedle = needle;
|
|
|
|
|
g.highlightsCleared = false;
|
2012-09-27 21:09:13 +02:00
|
|
|
updateHighlights();
|
2009-03-12 18:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
2008-12-19 14:35:57 +01:00
|
|
|
void FakeVimHandler::Private::moveToFirstNonBlankOnLine()
|
|
|
|
|
{
|
2014-11-04 18:53:44 +01:00
|
|
|
g.movetype = MoveLineWise;
|
2013-04-08 20:06:17 +02:00
|
|
|
moveToFirstNonBlankOnLine(&m_cursor);
|
2013-10-17 19:01:29 +02:00
|
|
|
setTargetColumn();
|
2012-08-26 18:39:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToFirstNonBlankOnLine(QTextCursor *tc)
|
2013-10-17 19:01:29 +02:00
|
|
|
{
|
|
|
|
|
tc->setPosition(tc->block().position(), KeepAnchor);
|
|
|
|
|
moveToNonBlankOnLine(tc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToFirstNonBlankOnLineVisually()
|
|
|
|
|
{
|
|
|
|
|
moveToStartOfLineVisually();
|
|
|
|
|
moveToNonBlankOnLine(&m_cursor);
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToNonBlankOnLine(QTextCursor *tc)
|
2012-08-26 18:39:48 +02:00
|
|
|
{
|
2013-10-17 19:01:29 +02:00
|
|
|
const QTextBlock block = tc->block();
|
|
|
|
|
const int maxPos = block.position() + block.length() - 1;
|
|
|
|
|
int i = tc->position();
|
2014-11-17 15:32:00 +01:00
|
|
|
while (characterAt(i).isSpace() && i < maxPos)
|
2013-10-17 19:01:29 +02:00
|
|
|
++i;
|
|
|
|
|
tc->setPosition(i, KeepAnchor);
|
2008-12-19 14:35:57 +01:00
|
|
|
}
|
2008-12-19 12:20:04 +01:00
|
|
|
|
2010-01-06 14:57:46 +01:00
|
|
|
void FakeVimHandler::Private::indentSelectedText(QChar typedChar)
|
2008-12-26 10:36:40 +01:00
|
|
|
{
|
2011-12-27 17:39:56 +01:00
|
|
|
beginEditBlock();
|
2010-02-19 13:01:38 +01:00
|
|
|
setTargetColumn();
|
2010-01-06 14:57:46 +01:00
|
|
|
int beginLine = qMin(lineForPosition(position()), lineForPosition(anchor()));
|
|
|
|
|
int endLine = qMax(lineForPosition(position()), lineForPosition(anchor()));
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
Range range(anchor(), position(), g.rangemode);
|
2010-01-06 14:57:46 +01:00
|
|
|
indentText(range, typedChar);
|
|
|
|
|
|
|
|
|
|
setPosition(firstPositionInLine(beginLine));
|
2010-02-19 13:01:38 +01:00
|
|
|
handleStartOfLine();
|
2010-01-06 14:57:46 +01:00
|
|
|
setTargetColumn();
|
2016-03-18 12:10:24 +01:00
|
|
|
setDotCommand("%1==", endLine - beginLine + 1);
|
2011-12-27 17:39:56 +01:00
|
|
|
endEditBlock();
|
2012-10-26 18:34:58 +02:00
|
|
|
|
|
|
|
|
const int lines = endLine - beginLine + 1;
|
|
|
|
|
if (lines > 2)
|
2018-11-07 23:45:39 +01:00
|
|
|
showMessage(MessageInfo, Tr::tr("%n lines indented.", nullptr, lines));
|
2010-01-06 14:57:46 +01:00
|
|
|
}
|
|
|
|
|
|
2010-06-02 15:27:10 +02:00
|
|
|
void FakeVimHandler::Private::indentText(const Range &range, QChar typedChar)
|
2010-01-06 14:57:46 +01:00
|
|
|
{
|
2014-11-09 16:37:23 +01:00
|
|
|
int beginBlock = blockAt(range.beginPos).blockNumber();
|
|
|
|
|
int endBlock = blockAt(range.endPos).blockNumber();
|
2012-10-13 09:33:30 +02:00
|
|
|
if (beginBlock > endBlock)
|
2014-06-16 21:34:13 +04:00
|
|
|
std::swap(beginBlock, endBlock);
|
2013-07-09 19:39:31 +02:00
|
|
|
|
|
|
|
|
// Don't remember current indentation in last text insertion.
|
2014-04-30 17:32:25 +02:00
|
|
|
const QString lastInsertion = m_buffer->lastInsertion;
|
2018-02-09 18:49:18 +01:00
|
|
|
q->indentRegion(beginBlock, endBlock, typedChar);
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->lastInsertion = lastInsertion;
|
2008-12-26 10:36:40 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-06 14:57:46 +01:00
|
|
|
bool FakeVimHandler::Private::isElectricCharacter(QChar c) const
|
|
|
|
|
{
|
|
|
|
|
bool result = false;
|
2018-02-09 18:49:18 +01:00
|
|
|
q->checkForElectricCharacter(&result, c);
|
2010-01-06 14:57:46 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-06 12:12:35 +01:00
|
|
|
void FakeVimHandler::Private::shiftRegionRight(int repeat)
|
2009-03-06 11:22:16 +01:00
|
|
|
{
|
|
|
|
|
int beginLine = lineForPosition(anchor());
|
|
|
|
|
int endLine = lineForPosition(position());
|
2010-04-29 13:30:12 +02:00
|
|
|
int targetPos = anchor();
|
|
|
|
|
if (beginLine > endLine) {
|
2014-06-16 21:34:13 +04:00
|
|
|
std::swap(beginLine, endLine);
|
2010-04-29 13:30:12 +02:00
|
|
|
targetPos = position();
|
|
|
|
|
}
|
|
|
|
|
if (hasConfig(ConfigStartOfLine))
|
|
|
|
|
targetPos = firstPositionInLine(beginLine);
|
|
|
|
|
|
2010-07-06 15:11:35 +02:00
|
|
|
const int sw = config(ConfigShiftWidth).toInt();
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveLineWise;
|
2011-12-27 17:39:56 +01:00
|
|
|
beginEditBlock();
|
2012-10-05 17:33:20 +02:00
|
|
|
QTextBlock block = document()->findBlockByLineNumber(beginLine - 1);
|
|
|
|
|
while (block.isValid() && lineNumber(block) <= endLine) {
|
|
|
|
|
const Column col = indentation(block.text());
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2012-10-05 17:33:20 +02:00
|
|
|
tc.setPosition(block.position());
|
|
|
|
|
if (col.physical > 0)
|
|
|
|
|
tc.setPosition(tc.position() + col.physical, KeepAnchor);
|
|
|
|
|
tc.insertText(tabExpand(col.logical + sw * repeat));
|
|
|
|
|
block = block.next();
|
2009-03-06 11:22:16 +01:00
|
|
|
}
|
2009-11-30 13:58:57 +01:00
|
|
|
endEditBlock();
|
2009-03-06 13:03:33 +01:00
|
|
|
|
2010-04-29 13:30:12 +02:00
|
|
|
setPosition(targetPos);
|
2010-02-19 13:01:38 +01:00
|
|
|
handleStartOfLine();
|
2012-10-26 18:34:58 +02:00
|
|
|
|
|
|
|
|
const int lines = endLine - beginLine + 1;
|
|
|
|
|
if (lines > 2) {
|
|
|
|
|
showMessage(MessageInfo,
|
2018-11-07 23:45:39 +01:00
|
|
|
Tr::tr("%n lines %1ed %2 time.", nullptr, lines)
|
2012-10-26 18:34:58 +02:00
|
|
|
.arg(repeat > 0 ? '>' : '<').arg(qAbs(repeat)));
|
|
|
|
|
}
|
2009-03-06 11:22:16 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-06 12:12:35 +01:00
|
|
|
void FakeVimHandler::Private::shiftRegionLeft(int repeat)
|
2009-03-06 11:22:16 +01:00
|
|
|
{
|
2012-10-05 17:33:20 +02:00
|
|
|
shiftRegionRight(-repeat);
|
2009-03-06 11:22:16 +01:00
|
|
|
}
|
|
|
|
|
|
2009-04-03 14:58:41 +02:00
|
|
|
void FakeVimHandler::Private::moveToTargetColumn()
|
2008-12-24 18:35:53 +01:00
|
|
|
{
|
2010-09-13 15:23:20 +02:00
|
|
|
const QTextBlock &bl = block();
|
2010-07-06 10:12:21 +02:00
|
|
|
//Column column = cursorColumn();
|
|
|
|
|
//int logical = logical
|
2012-11-10 10:36:05 +01:00
|
|
|
const int pos = lastPositionInLine(bl.blockNumber() + 1, false);
|
2010-07-06 10:12:21 +02:00
|
|
|
if (m_targetColumn == -1) {
|
2012-11-10 10:36:05 +01:00
|
|
|
setPosition(pos);
|
2009-04-16 09:18:09 +02:00
|
|
|
return;
|
2010-07-06 10:12:21 +02:00
|
|
|
}
|
2012-11-10 10:36:05 +01:00
|
|
|
const int physical = bl.position() + logicalToPhysicalColumn(m_targetColumn, bl.text());
|
2010-07-06 10:12:21 +02:00
|
|
|
//qDebug() << "CORRECTING COLUMN FROM: " << logical << "TO" << m_targetColumn;
|
2012-11-10 10:36:05 +01:00
|
|
|
setPosition(qMin(pos, physical));
|
2008-12-24 18:35:53 +01:00
|
|
|
}
|
|
|
|
|
|
2013-10-16 18:54:54 +02:00
|
|
|
void FakeVimHandler::Private::setTargetColumn()
|
|
|
|
|
{
|
|
|
|
|
m_targetColumn = logicalCursorColumn();
|
|
|
|
|
m_visualTargetColumn = m_targetColumn;
|
|
|
|
|
|
|
|
|
|
QTextCursor tc = m_cursor;
|
|
|
|
|
tc.movePosition(StartOfLine);
|
|
|
|
|
m_targetColumnWrapped = m_cursor.position() - tc.position();
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-09 16:20:49 +02:00
|
|
|
/* if simple is given:
|
|
|
|
|
* class 0: spaces
|
|
|
|
|
* class 1: non-spaces
|
|
|
|
|
* else
|
|
|
|
|
* class 0: spaces
|
|
|
|
|
* class 1: non-space-or-letter-or-number
|
|
|
|
|
* class 2: letter-or-number
|
|
|
|
|
*/
|
2010-04-28 16:19:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
int FakeVimHandler::Private::charClass(QChar c, bool simple) const
|
2008-12-25 19:11:21 +01:00
|
|
|
{
|
|
|
|
|
if (simple)
|
|
|
|
|
return c.isSpace() ? 0 : 1;
|
2010-04-28 16:19:51 +02:00
|
|
|
// FIXME: This means that only characters < 256 in the
|
|
|
|
|
// ConfigIsKeyword setting are handled properly.
|
|
|
|
|
if (c.unicode() < 256) {
|
2016-03-18 12:10:24 +01:00
|
|
|
//int old = (c.isLetterOrNumber() || c.unicode() == '_') ? 2
|
2010-04-28 16:19:51 +02:00
|
|
|
// : c.isSpace() ? 0 : 1;
|
|
|
|
|
//qDebug() << c.unicode() << old << m_charClass[c.unicode()];
|
|
|
|
|
return m_charClass[c.unicode()];
|
|
|
|
|
}
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c.isLetterOrNumber() || c == '_')
|
2008-12-25 19:11:21 +01:00
|
|
|
return 2;
|
|
|
|
|
return c.isSpace() ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-30 15:19:15 +01:00
|
|
|
void FakeVimHandler::Private::miniBufferTextEdited(const QString &text, int cursorPos,
|
|
|
|
|
int anchorPos)
|
2012-09-10 22:10:23 +02:00
|
|
|
{
|
2014-11-15 09:19:04 +01:00
|
|
|
if (!isCommandLineMode()) {
|
2012-09-10 22:10:23 +02:00
|
|
|
editor()->setFocus();
|
|
|
|
|
} else if (text.isEmpty()) {
|
|
|
|
|
// editing cancelled
|
2013-06-09 15:23:44 +02:00
|
|
|
enterFakeVim();
|
2012-09-10 22:10:23 +02:00
|
|
|
handleDefaultKey(Input(Qt::Key_Escape, Qt::NoModifier, QString()));
|
2013-06-09 15:23:44 +02:00
|
|
|
leaveFakeVim();
|
2012-09-10 22:10:23 +02:00
|
|
|
editor()->setFocus();
|
|
|
|
|
} else {
|
2013-05-06 20:02:22 +02:00
|
|
|
CommandBuffer &cmdBuf = (g.mode == ExMode) ? g.commandBuffer : g.searchBuffer;
|
2012-12-30 15:19:15 +01:00
|
|
|
int pos = qMax(1, cursorPos);
|
|
|
|
|
int anchor = anchorPos == -1 ? pos : qMax(1, anchorPos);
|
|
|
|
|
QString buffer = text;
|
2012-09-10 22:10:23 +02:00
|
|
|
// prepend prompt character if missing
|
2012-12-30 15:19:15 +01:00
|
|
|
if (!buffer.startsWith(cmdBuf.prompt())) {
|
|
|
|
|
buffer.prepend(cmdBuf.prompt());
|
|
|
|
|
++pos;
|
|
|
|
|
++anchor;
|
2012-09-10 22:10:23 +02:00
|
|
|
}
|
2012-12-30 15:19:15 +01:00
|
|
|
// update command/search buffer
|
|
|
|
|
cmdBuf.setContents(buffer.mid(1), pos - 1, anchor - 1);
|
|
|
|
|
if (pos != cursorPos || anchor != anchorPos || buffer != text)
|
2018-02-09 18:49:18 +01:00
|
|
|
q->commandBufferChanged(buffer, pos, anchor, 0);
|
2012-09-10 22:10:23 +02:00
|
|
|
// update search expression
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.subsubmode == SearchSubSubMode) {
|
2012-09-10 22:10:23 +02:00
|
|
|
updateFind(false);
|
2014-11-10 18:27:38 +01:00
|
|
|
commitCursor();
|
2012-09-10 22:10:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
void FakeVimHandler::Private::pullOrCreateBufferData()
|
|
|
|
|
{
|
|
|
|
|
const QVariant data = document()->property("FakeVimSharedData");
|
|
|
|
|
if (data.isValid()) {
|
|
|
|
|
// FakeVimHandler has been already created for this document (e.g. in other split).
|
|
|
|
|
m_buffer = data.value<BufferDataPtr>();
|
|
|
|
|
} else {
|
|
|
|
|
// FakeVimHandler has not been created for this document yet.
|
|
|
|
|
m_buffer = BufferDataPtr(new BufferData);
|
|
|
|
|
document()->setProperty("FakeVimSharedData", QVariant::fromValue(m_buffer));
|
|
|
|
|
}
|
2014-11-23 09:56:37 +01:00
|
|
|
|
|
|
|
|
if (editor()->hasFocus())
|
|
|
|
|
m_buffer->currentHandler = this;
|
2014-04-30 17:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-04-28 16:19:51 +02:00
|
|
|
// Helper to parse a-z,A-Z,48-57,_
|
|
|
|
|
static int someInt(const QString &str)
|
|
|
|
|
{
|
|
|
|
|
if (str.toInt())
|
|
|
|
|
return str.toInt();
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!str.isEmpty())
|
2010-04-28 16:19:51 +02:00
|
|
|
return str.at(0).unicode();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::setupCharClass()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 256; ++i) {
|
2016-03-18 12:10:24 +01:00
|
|
|
const QChar c = QLatin1Char(i);
|
2010-04-28 16:19:51 +02:00
|
|
|
m_charClass[i] = c.isSpace() ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
const QString conf = config(ConfigIsKeyword).toString();
|
2020-05-28 07:46:41 +02:00
|
|
|
for (const QString &part : conf.split(',')) {
|
2016-03-18 12:10:24 +01:00
|
|
|
if (part.contains('-')) {
|
|
|
|
|
const int from = someInt(part.section('-', 0, 0));
|
|
|
|
|
const int to = someInt(part.section('-', 1, 1));
|
2010-04-28 16:19:51 +02:00
|
|
|
for (int i = qMax(0, from); i <= qMin(255, to); ++i)
|
|
|
|
|
m_charClass[i] = 2;
|
|
|
|
|
} else {
|
|
|
|
|
m_charClass[qMin(255, someInt(part))] = 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-19 19:08:04 +02:00
|
|
|
void FakeVimHandler::Private::moveToBoundary(bool simple, bool forward)
|
2008-12-25 19:50:14 +01:00
|
|
|
{
|
2014-11-17 15:32:00 +01:00
|
|
|
QTextCursor tc(document());
|
2012-08-19 19:08:04 +02:00
|
|
|
tc.setPosition(position());
|
|
|
|
|
if (forward ? tc.atBlockEnd() : tc.atBlockStart())
|
|
|
|
|
return;
|
|
|
|
|
|
2014-11-17 15:32:00 +01:00
|
|
|
QChar c = characterAt(tc.position() + (forward ? -1 : 1));
|
2012-08-19 19:08:04 +02:00
|
|
|
int lastClass = tc.atStart() ? -1 : charClass(c, simple);
|
|
|
|
|
QTextCursor::MoveOperation op = forward ? Right : Left;
|
|
|
|
|
while (true) {
|
2014-11-17 15:32:00 +01:00
|
|
|
c = characterAt(tc.position());
|
2008-12-25 19:50:14 +01:00
|
|
|
int thisClass = charClass(c, simple);
|
2012-08-19 19:08:04 +02:00
|
|
|
if (thisClass != lastClass || (forward ? tc.atBlockEnd() : tc.atBlockStart())) {
|
2013-04-08 20:06:17 +02:00
|
|
|
if (tc != m_cursor)
|
2012-08-19 19:08:04 +02:00
|
|
|
tc.movePosition(forward ? Left : Right);
|
2008-12-25 20:12:17 +01:00
|
|
|
break;
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
2008-12-25 19:50:14 +01:00
|
|
|
lastClass = thisClass;
|
2012-08-19 19:08:04 +02:00
|
|
|
tc.movePosition(op);
|
|
|
|
|
}
|
|
|
|
|
setPosition(tc.position());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToNextBoundary(bool end, int count, bool simple, bool forward)
|
|
|
|
|
{
|
|
|
|
|
int repeat = count;
|
|
|
|
|
while (repeat > 0 && !(forward ? atDocumentEnd() : atDocumentStart())) {
|
|
|
|
|
setPosition(position() + (forward ? 1 : -1));
|
|
|
|
|
moveToBoundary(simple, forward);
|
|
|
|
|
if (atBoundary(end, simple))
|
|
|
|
|
--repeat;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToNextBoundaryStart(int count, bool simple, bool forward)
|
|
|
|
|
{
|
|
|
|
|
moveToNextBoundary(false, count, simple, forward);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToNextBoundaryEnd(int count, bool simple, bool forward)
|
|
|
|
|
{
|
|
|
|
|
moveToNextBoundary(true, count, simple, forward);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToBoundaryStart(int count, bool simple, bool forward)
|
|
|
|
|
{
|
|
|
|
|
moveToNextBoundaryStart(atBoundary(false, simple) ? count - 1 : count, simple, forward);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToBoundaryEnd(int count, bool simple, bool forward)
|
|
|
|
|
{
|
|
|
|
|
moveToNextBoundaryEnd(atBoundary(true, simple) ? count - 1 : count, simple, forward);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToNextWord(bool end, int count, bool simple, bool forward, bool emptyLines)
|
|
|
|
|
{
|
|
|
|
|
int repeat = count;
|
|
|
|
|
while (repeat > 0 && !(forward ? atDocumentEnd() : atDocumentStart())) {
|
|
|
|
|
setPosition(position() + (forward ? 1 : -1));
|
|
|
|
|
moveToBoundary(simple, forward);
|
|
|
|
|
if (atWordBoundary(end, simple) && (emptyLines || !atEmptyLine()) )
|
2010-01-21 17:38:27 +01:00
|
|
|
--repeat;
|
2008-12-25 19:50:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-19 19:08:04 +02:00
|
|
|
void FakeVimHandler::Private::moveToNextWordStart(int count, bool simple, bool forward, bool emptyLines)
|
|
|
|
|
{
|
2014-11-04 18:53:44 +01:00
|
|
|
g.movetype = MoveExclusive;
|
2012-08-19 19:08:04 +02:00
|
|
|
moveToNextWord(false, count, simple, forward, emptyLines);
|
2014-11-04 18:53:44 +01:00
|
|
|
setTargetColumn();
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToNextWordEnd(int count, bool simple, bool forward, bool emptyLines)
|
|
|
|
|
{
|
2014-11-04 18:53:44 +01:00
|
|
|
g.movetype = MoveInclusive;
|
2012-08-19 19:08:04 +02:00
|
|
|
moveToNextWord(true, count, simple, forward, emptyLines);
|
2014-11-04 18:53:44 +01:00
|
|
|
setTargetColumn();
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToWordStart(int count, bool simple, bool forward, bool emptyLines)
|
|
|
|
|
{
|
|
|
|
|
moveToNextWordStart(atWordStart(simple) ? count - 1 : count, simple, forward, emptyLines);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::moveToWordEnd(int count, bool simple, bool forward, bool emptyLines)
|
|
|
|
|
{
|
|
|
|
|
moveToNextWordEnd(atWordEnd(simple) ? count - 1 : count, simple, forward, emptyLines);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-27 09:17:46 +02:00
|
|
|
bool FakeVimHandler::Private::handleFfTt(const QString &key, bool repeats)
|
2008-12-25 19:50:14 +01:00
|
|
|
{
|
2010-04-28 14:00:44 +02:00
|
|
|
int key0 = key.size() == 1 ? key.at(0).unicode() : 0;
|
2013-05-06 20:02:22 +02:00
|
|
|
// g.subsubmode \in { 'f', 'F', 't', 'T' }
|
|
|
|
|
bool forward = g.subsubdata.is('f') || g.subsubdata.is('t');
|
2013-08-11 07:46:26 +02:00
|
|
|
bool exclusive = g.subsubdata.is('t') || g.subsubdata.is('T');
|
2008-12-26 00:18:03 +01:00
|
|
|
int repeat = count();
|
2013-08-11 07:46:26 +02:00
|
|
|
int n = block().position() + (forward ? block().length() : - 1);
|
2014-04-27 09:17:46 +02:00
|
|
|
const int d = forward ? 1 : -1;
|
|
|
|
|
// FIXME: This also depends on whether 'cpositions' Vim option contains ';'.
|
|
|
|
|
const int skip = (repeats && repeat == 1 && exclusive) ? d : 0;
|
|
|
|
|
int pos = position() + d + skip;
|
2013-08-11 07:46:26 +02:00
|
|
|
|
2014-04-27 09:17:46 +02:00
|
|
|
for (; repeat > 0 && (forward ? pos < n : pos > n); pos += d) {
|
2014-11-17 15:32:00 +01:00
|
|
|
if (characterAt(pos).unicode() == key0)
|
2008-12-25 19:50:14 +01:00
|
|
|
--repeat;
|
2014-04-27 09:17:46 +02:00
|
|
|
}
|
2013-08-11 07:46:26 +02:00
|
|
|
|
2014-04-27 09:17:46 +02:00
|
|
|
if (repeat == 0) {
|
|
|
|
|
setPosition(pos - d - (exclusive ? d : 0));
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
return true;
|
2008-12-25 19:50:14 +01:00
|
|
|
}
|
2013-08-11 07:46:26 +02:00
|
|
|
|
2010-09-13 15:23:20 +02:00
|
|
|
return false;
|
2008-12-25 19:50:14 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-13 12:35:43 +01:00
|
|
|
void FakeVimHandler::Private::moveToMatchingParanthesis()
|
|
|
|
|
{
|
2009-01-28 22:03:51 +01:00
|
|
|
bool moved = false;
|
|
|
|
|
bool forward = false;
|
|
|
|
|
|
2010-09-27 15:25:18 +02:00
|
|
|
const int anc = anchor();
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2013-11-30 19:53:23 +01:00
|
|
|
|
|
|
|
|
// If no known parenthesis symbol is under cursor find one on the current line after cursor.
|
2016-03-18 12:10:24 +01:00
|
|
|
static const QString parenthesesChars("([{}])");
|
2014-11-17 15:32:00 +01:00
|
|
|
while (!parenthesesChars.contains(characterAt(tc.position())) && !tc.atBlockEnd())
|
2013-11-30 19:53:23 +01:00
|
|
|
tc.setPosition(tc.position() + 1);
|
|
|
|
|
|
|
|
|
|
if (tc.atBlockEnd())
|
|
|
|
|
tc = m_cursor;
|
|
|
|
|
|
2018-02-09 18:49:18 +01:00
|
|
|
q->moveToMatchingParenthesis(&moved, &forward, &tc);
|
2013-11-30 19:53:23 +01:00
|
|
|
if (moved) {
|
|
|
|
|
if (forward)
|
|
|
|
|
tc.movePosition(Left, KeepAnchor, 1);
|
|
|
|
|
setAnchorAndPosition(anc, tc.position());
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
2009-01-13 12:35:43 +01:00
|
|
|
}
|
|
|
|
|
|
2008-12-25 16:27:47 +01:00
|
|
|
int FakeVimHandler::Private::cursorLineOnScreen() const
|
|
|
|
|
{
|
2009-01-06 11:50:30 +01:00
|
|
|
if (!editor())
|
|
|
|
|
return 0;
|
2013-10-25 18:25:59 +02:00
|
|
|
const QRect rect = EDITOR(cursorRect(m_cursor));
|
|
|
|
|
return rect.height() > 0 ? rect.y() / rect.height() : 0;
|
2008-12-25 16:27:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int FakeVimHandler::Private::linesOnScreen() const
|
|
|
|
|
{
|
2009-01-06 11:50:30 +01:00
|
|
|
if (!editor())
|
|
|
|
|
return 1;
|
2013-10-25 18:25:59 +02:00
|
|
|
const int h = EDITOR(cursorRect(m_cursor)).height();
|
|
|
|
|
return h > 0 ? EDITOR(viewport()->height()) / h : 1;
|
2008-12-27 21:01:05 +01:00
|
|
|
}
|
|
|
|
|
|
2010-07-06 10:12:21 +02:00
|
|
|
int FakeVimHandler::Private::cursorLine() const
|
2008-12-25 16:27:47 +01:00
|
|
|
{
|
2012-10-05 17:33:20 +02:00
|
|
|
return lineForPosition(position()) - 1;
|
2008-12-25 16:27:47 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
int FakeVimHandler::Private::cursorBlockNumber() const
|
|
|
|
|
{
|
2014-11-09 16:37:23 +01:00
|
|
|
return blockAt(qMin(anchor(), position())).blockNumber();
|
2012-10-23 16:35:13 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-06 10:12:21 +02:00
|
|
|
int FakeVimHandler::Private::physicalCursorColumn() const
|
2008-12-27 21:01:05 +01:00
|
|
|
{
|
2010-09-13 15:23:20 +02:00
|
|
|
return position() - block().position();
|
2008-12-27 21:01:05 +01:00
|
|
|
}
|
|
|
|
|
|
2010-07-06 10:12:21 +02:00
|
|
|
int FakeVimHandler::Private::physicalToLogicalColumn
|
|
|
|
|
(const int physical, const QString &line) const
|
2010-03-09 16:44:36 +01:00
|
|
|
{
|
|
|
|
|
const int ts = config(ConfigTabStop).toInt();
|
2010-07-06 10:12:21 +02:00
|
|
|
int p = 0;
|
2010-03-09 16:44:36 +01:00
|
|
|
int logical = 0;
|
2010-07-06 10:12:21 +02:00
|
|
|
while (p < physical) {
|
|
|
|
|
QChar c = line.at(p);
|
2016-03-18 12:10:24 +01:00
|
|
|
//if (c == ' ')
|
2010-07-06 10:12:21 +02:00
|
|
|
// ++logical;
|
|
|
|
|
//else
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '\t')
|
2010-03-09 16:44:36 +01:00
|
|
|
logical += ts - logical % ts;
|
|
|
|
|
else
|
2010-07-06 10:12:21 +02:00
|
|
|
++logical;
|
|
|
|
|
//break;
|
|
|
|
|
++p;
|
2010-03-09 16:44:36 +01:00
|
|
|
}
|
|
|
|
|
return logical;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-06 10:12:21 +02:00
|
|
|
int FakeVimHandler::Private::logicalToPhysicalColumn
|
|
|
|
|
(const int logical, const QString &line) const
|
|
|
|
|
{
|
|
|
|
|
const int ts = config(ConfigTabStop).toInt();
|
|
|
|
|
int physical = 0;
|
|
|
|
|
for (int l = 0; l < logical && physical < line.size(); ++physical) {
|
|
|
|
|
QChar c = line.at(physical);
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '\t')
|
2010-07-06 10:12:21 +02:00
|
|
|
l += ts - l % ts;
|
|
|
|
|
else
|
|
|
|
|
++l;
|
|
|
|
|
}
|
|
|
|
|
return physical;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-06 14:51:50 +02:00
|
|
|
int FakeVimHandler::Private::windowScrollOffset() const
|
|
|
|
|
{
|
|
|
|
|
return qMin(theFakeVimSetting(ConfigScrollOff)->value().toInt(), linesOnScreen() / 2);
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-06 10:12:21 +02:00
|
|
|
int FakeVimHandler::Private::logicalCursorColumn() const
|
|
|
|
|
{
|
|
|
|
|
const int physical = physicalCursorColumn();
|
2010-09-13 15:23:20 +02:00
|
|
|
const QString line = block().text();
|
2010-07-06 10:12:21 +02:00
|
|
|
return physicalToLogicalColumn(physical, line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Column FakeVimHandler::Private::cursorColumn() const
|
2010-03-09 16:44:36 +01:00
|
|
|
{
|
2010-07-06 10:12:21 +02:00
|
|
|
return Column(physicalCursorColumn(), logicalCursorColumn());
|
2010-03-09 16:44:36 +01:00
|
|
|
}
|
|
|
|
|
|
2008-12-28 02:15:26 +01:00
|
|
|
int FakeVimHandler::Private::linesInDocument() const
|
|
|
|
|
{
|
2013-04-08 20:06:17 +02:00
|
|
|
if (m_cursor.isNull())
|
2010-01-21 17:38:29 +01:00
|
|
|
return 0;
|
2013-04-06 14:51:50 +02:00
|
|
|
return document()->blockCount();
|
2008-12-28 02:15:26 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
void FakeVimHandler::Private::scrollToLine(int line)
|
2008-12-25 16:27:47 +01:00
|
|
|
{
|
2013-06-06 21:05:32 +02:00
|
|
|
// Don't scroll if the line is already at the top.
|
|
|
|
|
updateFirstVisibleLine();
|
|
|
|
|
if (line == m_firstVisibleLine)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-10-16 18:53:45 +02:00
|
|
|
const QTextCursor tc = m_cursor;
|
|
|
|
|
|
2013-05-12 12:06:40 +02:00
|
|
|
QTextCursor tc2 = tc;
|
|
|
|
|
tc2.setPosition(document()->lastBlock().position());
|
|
|
|
|
EDITOR(setTextCursor(tc2));
|
|
|
|
|
EDITOR(ensureCursorVisible());
|
2013-03-03 11:42:24 +01:00
|
|
|
|
2013-11-29 17:03:57 +01:00
|
|
|
int offset = 0;
|
2013-10-16 18:53:45 +02:00
|
|
|
const QTextBlock block = document()->findBlockByLineNumber(line);
|
2013-11-29 17:03:57 +01:00
|
|
|
if (block.isValid()) {
|
|
|
|
|
const int blockLineCount = block.layout()->lineCount();
|
|
|
|
|
const int lineInBlock = line - block.firstLineNumber();
|
|
|
|
|
if (0 <= lineInBlock && lineInBlock < blockLineCount) {
|
|
|
|
|
QTextLine textLine = block.layout()->lineAt(lineInBlock);
|
|
|
|
|
offset = textLine.textStart();
|
|
|
|
|
} else {
|
|
|
|
|
// QTC_CHECK(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tc2.setPosition(block.position() + offset);
|
2013-05-12 12:06:40 +02:00
|
|
|
EDITOR(setTextCursor(tc2));
|
|
|
|
|
EDITOR(ensureCursorVisible());
|
2013-04-08 20:06:17 +02:00
|
|
|
|
2013-05-12 12:06:40 +02:00
|
|
|
EDITOR(setTextCursor(tc));
|
|
|
|
|
|
|
|
|
|
m_firstVisibleLine = line;
|
2013-03-03 11:42:24 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
void FakeVimHandler::Private::updateFirstVisibleLine()
|
2013-03-03 11:42:24 +01:00
|
|
|
{
|
2013-05-12 12:06:40 +02:00
|
|
|
const QTextCursor tc = EDITOR(cursorForPosition(QPoint(0,0)));
|
2013-10-16 18:53:45 +02:00
|
|
|
m_firstVisibleLine = lineForPosition(tc.position()) - 1;
|
2009-07-13 14:04:47 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-06 10:12:21 +02:00
|
|
|
int FakeVimHandler::Private::firstVisibleLine() const
|
2009-07-13 14:04:47 +02:00
|
|
|
{
|
2013-04-08 20:06:17 +02:00
|
|
|
return m_firstVisibleLine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int FakeVimHandler::Private::lastVisibleLine() const
|
|
|
|
|
{
|
2013-10-16 18:53:45 +02:00
|
|
|
const int line = m_firstVisibleLine + linesOnScreen();
|
|
|
|
|
const QTextBlock block = document()->findBlockByLineNumber(line);
|
|
|
|
|
return block.isValid() ? line : document()->lastBlock().firstLineNumber();
|
2013-04-08 20:06:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int FakeVimHandler::Private::lineOnTop(int count) const
|
|
|
|
|
{
|
|
|
|
|
const int scrollOffset = qMax(count - 1, windowScrollOffset());
|
|
|
|
|
const int line = firstVisibleLine();
|
|
|
|
|
return line == 0 ? count - 1 : scrollOffset + line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int FakeVimHandler::Private::lineOnBottom(int count) const
|
|
|
|
|
{
|
|
|
|
|
const int scrollOffset = qMax(count - 1, windowScrollOffset());
|
|
|
|
|
const int line = lastVisibleLine();
|
|
|
|
|
return line >= document()->lastBlock().firstLineNumber() ? line - count + 1
|
|
|
|
|
: line - scrollOffset - 1;
|
2008-12-25 16:27:47 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-20 08:44:52 +01:00
|
|
|
void FakeVimHandler::Private::scrollUp(int count)
|
|
|
|
|
{
|
2010-07-06 10:12:21 +02:00
|
|
|
scrollToLine(cursorLine() - cursorLineOnScreen() - count);
|
2009-03-20 08:44:52 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-03 11:42:24 +01:00
|
|
|
void FakeVimHandler::Private::updateScrollOffset()
|
|
|
|
|
{
|
|
|
|
|
const int line = cursorLine();
|
2013-04-08 20:06:17 +02:00
|
|
|
if (line < lineOnTop())
|
|
|
|
|
scrollToLine(qMax(0, line - windowScrollOffset()));
|
|
|
|
|
else if (line > lineOnBottom())
|
2013-10-16 18:53:45 +02:00
|
|
|
scrollToLine(firstVisibleLine() + line - lineOnBottom());
|
2013-03-03 11:42:24 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
void FakeVimHandler::Private::alignViewportToCursor(AlignmentFlag align, int line,
|
|
|
|
|
bool moveToNonBlank)
|
|
|
|
|
{
|
|
|
|
|
if (line > 0)
|
|
|
|
|
setPosition(firstPositionInLine(line));
|
|
|
|
|
if (moveToNonBlank)
|
|
|
|
|
moveToFirstNonBlankOnLine();
|
|
|
|
|
|
|
|
|
|
if (align == Qt::AlignTop)
|
|
|
|
|
scrollUp(- cursorLineOnScreen());
|
|
|
|
|
else if (align == Qt::AlignVCenter)
|
|
|
|
|
scrollUp(linesOnScreen() / 2 - cursorLineOnScreen());
|
|
|
|
|
else if (align == Qt::AlignBottom)
|
2013-05-12 10:55:25 +02:00
|
|
|
scrollUp(linesOnScreen() - cursorLineOnScreen() - 1);
|
2012-10-23 16:35:13 +02:00
|
|
|
}
|
|
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
int FakeVimHandler::Private::lineToBlockNumber(int line) const
|
|
|
|
|
{
|
|
|
|
|
return document()->findBlockByLineNumber(line).blockNumber();
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
void FakeVimHandler::Private::setCursorPosition(const CursorPosition &p)
|
|
|
|
|
{
|
|
|
|
|
const int firstLine = firstVisibleLine();
|
2013-04-08 20:06:17 +02:00
|
|
|
const int firstBlock = lineToBlockNumber(firstLine);
|
|
|
|
|
const int lastBlock = lineToBlockNumber(firstLine + linesOnScreen() - 2);
|
2012-10-23 16:35:13 +02:00
|
|
|
bool isLineVisible = firstBlock <= p.line && p.line <= lastBlock;
|
2013-04-08 20:06:17 +02:00
|
|
|
setCursorPosition(&m_cursor, p);
|
2012-10-23 16:35:13 +02:00
|
|
|
if (!isLineVisible)
|
|
|
|
|
alignViewportToCursor(Qt::AlignVCenter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::setCursorPosition(QTextCursor *tc, const CursorPosition &p)
|
|
|
|
|
{
|
|
|
|
|
const int line = qMin(document()->blockCount() - 1, p.line);
|
|
|
|
|
QTextBlock block = document()->findBlockByNumber(line);
|
|
|
|
|
const int column = qMin(p.column, block.length() - 1);
|
|
|
|
|
tc->setPosition(block.position() + column, KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-13 09:33:30 +02:00
|
|
|
int FakeVimHandler::Private::lastPositionInDocument(bool ignoreMode) const
|
2008-12-25 19:11:21 +01:00
|
|
|
{
|
2012-10-13 09:33:30 +02:00
|
|
|
return document()->characterCount()
|
2013-04-08 21:25:29 +02:00
|
|
|
- (ignoreMode || isVisualMode() || isInsertMode() ? 1 : 2);
|
2008-12-25 19:11:21 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
QString FakeVimHandler::Private::selectText(const Range &range) const
|
2009-08-11 14:39:44 +02:00
|
|
|
{
|
|
|
|
|
QString contents;
|
2016-03-18 12:10:24 +01:00
|
|
|
const QString lineEnd = range.rangemode == RangeBlockMode ? QString('\n') : QString();
|
2014-11-15 13:34:32 +01:00
|
|
|
QTextCursor tc = m_cursor;
|
|
|
|
|
transformText(range, tc,
|
|
|
|
|
[&tc, &contents, &lineEnd]() { contents.append(tc.selection().toPlainText() + lineEnd); });
|
2009-08-11 14:39:44 +02:00
|
|
|
return contents;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-12 02:31:52 +01:00
|
|
|
void FakeVimHandler::Private::yankText(const Range &range, int reg)
|
2009-08-11 14:39:44 +02:00
|
|
|
{
|
2013-12-01 13:35:37 +01:00
|
|
|
const QString text = selectText(range);
|
|
|
|
|
setRegister(reg, text, range.rangemode);
|
|
|
|
|
|
|
|
|
|
// If register is not specified or " ...
|
|
|
|
|
if (m_register == '"') {
|
|
|
|
|
// with delete and change commands set register 1 (if text contains more lines) or
|
|
|
|
|
// small delete register -
|
|
|
|
|
if (g.submode == DeleteSubMode || g.submode == ChangeSubMode) {
|
2016-03-18 12:10:24 +01:00
|
|
|
if (text.contains('\n'))
|
2013-12-01 13:35:37 +01:00
|
|
|
setRegister('1', text, range.rangemode);
|
|
|
|
|
else
|
|
|
|
|
setRegister('-', text, range.rangemode);
|
2015-03-22 11:04:41 +01:00
|
|
|
} else {
|
|
|
|
|
// copy to yank register 0 too
|
|
|
|
|
setRegister('0', text, range.rangemode);
|
2013-12-01 13:35:37 +01:00
|
|
|
}
|
2015-03-22 11:12:08 +01:00
|
|
|
} else if (m_register != '_') {
|
|
|
|
|
// Always copy to " register too (except black hole register).
|
2013-12-01 13:35:37 +01:00
|
|
|
setRegister('"', text, range.rangemode);
|
|
|
|
|
}
|
2012-10-23 16:35:13 +02:00
|
|
|
|
2014-11-09 16:37:23 +01:00
|
|
|
const int lines = blockAt(range.endPos).blockNumber()
|
|
|
|
|
- blockAt(range.beginPos).blockNumber() + 1;
|
2012-10-23 16:35:13 +02:00
|
|
|
if (lines > 2)
|
2018-11-07 23:45:39 +01:00
|
|
|
showMessage(MessageInfo, Tr::tr("%n lines yanked.", nullptr, lines));
|
2009-08-11 14:39:44 +02:00
|
|
|
}
|
|
|
|
|
|
2014-11-15 13:34:32 +01:00
|
|
|
void FakeVimHandler::Private::transformText(
|
|
|
|
|
const Range &range, QTextCursor &tc, const std::function<void()> &transform) const
|
2009-01-16 16:15:01 +01:00
|
|
|
{
|
2009-08-11 14:39:44 +02:00
|
|
|
switch (range.rangemode) {
|
|
|
|
|
case RangeCharMode: {
|
2010-05-28 13:09:24 +02:00
|
|
|
// This can span multiple lines.
|
2009-08-11 14:39:44 +02:00
|
|
|
tc.setPosition(range.beginPos, MoveAnchor);
|
|
|
|
|
tc.setPosition(range.endPos, KeepAnchor);
|
2014-11-15 13:34:32 +01:00
|
|
|
transform();
|
|
|
|
|
tc.setPosition(range.beginPos);
|
2012-10-13 09:33:30 +02:00
|
|
|
break;
|
2009-08-11 14:39:44 +02:00
|
|
|
}
|
2010-01-21 17:38:27 +01:00
|
|
|
case RangeLineMode:
|
|
|
|
|
case RangeLineModeExclusive: {
|
2009-08-11 14:39:44 +02:00
|
|
|
tc.setPosition(range.beginPos, MoveAnchor);
|
|
|
|
|
tc.movePosition(StartOfLine, MoveAnchor);
|
|
|
|
|
tc.setPosition(range.endPos, KeepAnchor);
|
|
|
|
|
tc.movePosition(EndOfLine, KeepAnchor);
|
2010-01-21 17:38:27 +01:00
|
|
|
if (range.rangemode != RangeLineModeExclusive) {
|
|
|
|
|
// make sure that complete lines are removed
|
|
|
|
|
// - also at the beginning and at the end of the document
|
|
|
|
|
if (tc.atEnd()) {
|
|
|
|
|
tc.setPosition(range.beginPos, MoveAnchor);
|
|
|
|
|
tc.movePosition(StartOfLine, MoveAnchor);
|
|
|
|
|
if (!tc.atStart()) {
|
|
|
|
|
// also remove first line if it is the only one
|
|
|
|
|
tc.movePosition(Left, MoveAnchor, 1);
|
|
|
|
|
tc.movePosition(EndOfLine, MoveAnchor, 1);
|
|
|
|
|
}
|
|
|
|
|
tc.setPosition(range.endPos, KeepAnchor);
|
|
|
|
|
tc.movePosition(EndOfLine, KeepAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
tc.movePosition(Right, KeepAnchor, 1);
|
2010-01-05 18:42:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
2014-11-15 13:34:32 +01:00
|
|
|
const int posAfter = tc.anchor();
|
|
|
|
|
transform();
|
|
|
|
|
tc.setPosition(posAfter);
|
2012-10-13 09:33:30 +02:00
|
|
|
break;
|
2009-08-11 14:39:44 +02:00
|
|
|
}
|
2010-01-21 17:38:26 +01:00
|
|
|
case RangeBlockAndTailMode:
|
2009-08-11 14:39:44 +02:00
|
|
|
case RangeBlockMode: {
|
2014-11-15 13:34:32 +01:00
|
|
|
int beginColumn = columnAt(range.beginPos);
|
|
|
|
|
int endColumn = columnAt(range.endPos);
|
|
|
|
|
if (endColumn < beginColumn)
|
|
|
|
|
std::swap(beginColumn, endColumn);
|
2009-12-11 18:49:47 +01:00
|
|
|
if (range.rangemode == RangeBlockAndTailMode)
|
|
|
|
|
endColumn = INT_MAX - 1;
|
2014-11-15 13:34:32 +01:00
|
|
|
QTextBlock block = document()->findBlock(range.beginPos);
|
|
|
|
|
const QTextBlock lastBlock = document()->findBlock(range.endPos);
|
|
|
|
|
while (block.isValid() && block.position() <= lastBlock.position()) {
|
2009-08-11 14:39:44 +02:00
|
|
|
int bCol = qMin(beginColumn, block.length() - 1);
|
2009-11-10 10:07:36 +01:00
|
|
|
int eCol = qMin(endColumn + 1, block.length() - 1);
|
2009-08-11 14:39:44 +02:00
|
|
|
tc.setPosition(block.position() + bCol, MoveAnchor);
|
|
|
|
|
tc.setPosition(block.position() + eCol, KeepAnchor);
|
2014-11-15 13:34:32 +01:00
|
|
|
transform();
|
|
|
|
|
block = block.next();
|
2009-08-11 14:39:44 +02:00
|
|
|
}
|
2014-11-15 13:34:32 +01:00
|
|
|
tc.setPosition(range.beginPos);
|
2012-10-13 09:33:30 +02:00
|
|
|
break;
|
2009-08-11 14:39:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
2014-11-15 13:34:32 +01:00
|
|
|
}
|
2012-10-13 09:33:30 +02:00
|
|
|
|
2014-11-15 13:34:32 +01:00
|
|
|
void FakeVimHandler::Private::transformText(const Range &range, const Transformation &transform)
|
|
|
|
|
{
|
|
|
|
|
beginEditBlock();
|
|
|
|
|
transformText(range, m_cursor,
|
|
|
|
|
[this, &transform] { m_cursor.insertText(transform(m_cursor.selection().toPlainText())); });
|
|
|
|
|
endEditBlock();
|
2012-10-13 09:33:30 +02:00
|
|
|
setTargetColumn();
|
2009-08-11 14:39:44 +02:00
|
|
|
}
|
|
|
|
|
|
2013-04-07 10:40:40 +02:00
|
|
|
void FakeVimHandler::Private::insertText(QTextCursor &tc, const QString &text)
|
|
|
|
|
{
|
|
|
|
|
if (hasConfig(ConfigPassKeys)) {
|
|
|
|
|
if (tc.hasSelection() && text.isEmpty()) {
|
|
|
|
|
QKeyEvent event(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier, QString());
|
2014-11-10 18:27:38 +01:00
|
|
|
passEventToEditor(event, tc);
|
2013-04-07 10:40:40 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-28 07:46:41 +02:00
|
|
|
for (QChar c : text) {
|
2013-04-07 10:40:40 +02:00
|
|
|
QKeyEvent event(QEvent::KeyPress, -1, Qt::NoModifier, QString(c));
|
2014-11-10 18:27:38 +01:00
|
|
|
passEventToEditor(event, tc);
|
2013-04-07 10:40:40 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
tc.insertText(text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-11 14:26:37 +02:00
|
|
|
void FakeVimHandler::Private::insertText(const Register ®)
|
|
|
|
|
{
|
2018-04-11 12:03:04 +02:00
|
|
|
if (reg.rangemode != RangeCharMode) {
|
|
|
|
|
qWarning() << "WRONG INSERT MODE: " << reg.rangemode;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-09-16 16:56:11 +02:00
|
|
|
setAnchor();
|
2013-04-08 20:06:17 +02:00
|
|
|
m_cursor.insertText(reg.contents);
|
2010-09-16 15:28:31 +02:00
|
|
|
//dump("AFTER INSERT");
|
2010-05-11 14:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-21 17:38:26 +01:00
|
|
|
void FakeVimHandler::Private::removeText(const Range &range)
|
|
|
|
|
{
|
2014-11-15 13:34:32 +01:00
|
|
|
transformText(range, [](const QString &) { return QString(); });
|
2010-01-21 17:38:26 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void FakeVimHandler::Private::downCase(const Range &range)
|
2010-01-21 17:38:26 +01:00
|
|
|
{
|
2014-11-15 13:34:32 +01:00
|
|
|
transformText(range, [](const QString &text) { return text.toLower(); } );
|
2010-01-21 17:38:26 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void FakeVimHandler::Private::upCase(const Range &range)
|
2010-01-21 17:38:26 +01:00
|
|
|
{
|
2014-11-15 13:34:32 +01:00
|
|
|
transformText(range, [](const QString &text) { return text.toUpper(); } );
|
2010-01-21 17:38:26 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void FakeVimHandler::Private::invertCase(const Range &range)
|
2010-01-21 17:38:26 +01:00
|
|
|
{
|
2014-11-15 13:34:32 +01:00
|
|
|
transformText(range,
|
2014-11-19 12:45:07 +01:00
|
|
|
[] (const QString &text) -> QString {
|
2014-11-15 13:34:32 +01:00
|
|
|
QString result = text;
|
|
|
|
|
for (int i = 0; i < result.length(); ++i) {
|
|
|
|
|
QCharRef c = result[i];
|
|
|
|
|
c = c.isUpper() ? c.toLower() : c.toUpper();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
});
|
2010-01-21 17:38:26 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-12 11:18:18 +02:00
|
|
|
void FakeVimHandler::Private::replaceText(const Range &range, const QString &str)
|
2010-03-18 13:15:59 +01:00
|
|
|
{
|
2014-11-15 13:34:32 +01:00
|
|
|
transformText(range, [&str](const QString &) { return str; } );
|
2010-03-18 13:15:59 +01:00
|
|
|
}
|
|
|
|
|
|
2009-08-11 14:39:44 +02:00
|
|
|
void FakeVimHandler::Private::pasteText(bool afterCursor)
|
|
|
|
|
{
|
2011-11-12 02:31:52 +01:00
|
|
|
const QString text = registerContents(m_register);
|
2012-08-09 13:05:06 +02:00
|
|
|
const RangeMode rangeMode = registerRangeMode(m_register);
|
2011-08-05 08:47:53 +02:00
|
|
|
|
|
|
|
|
beginEditBlock();
|
|
|
|
|
|
2012-08-09 13:05:06 +02:00
|
|
|
// In visual mode paste text only inside selection.
|
|
|
|
|
bool pasteAfter = isVisualMode() ? false : afterCursor;
|
|
|
|
|
|
2014-11-01 17:57:42 +01:00
|
|
|
if (isVisualMode())
|
2019-03-22 12:00:39 +01:00
|
|
|
cutSelectedText('"');
|
2011-08-05 08:47:53 +02:00
|
|
|
|
2012-08-09 13:05:06 +02:00
|
|
|
switch (rangeMode) {
|
2009-08-11 14:39:44 +02:00
|
|
|
case RangeCharMode: {
|
|
|
|
|
m_targetColumn = 0;
|
2012-10-13 09:33:30 +02:00
|
|
|
const int pos = position() + 1;
|
2012-08-09 13:05:06 +02:00
|
|
|
if (pasteAfter && rightDist() > 0)
|
|
|
|
|
moveRight();
|
|
|
|
|
insertText(text.repeated(count()));
|
2016-03-18 12:10:24 +01:00
|
|
|
if (text.contains('\n'))
|
2012-10-13 09:33:30 +02:00
|
|
|
setPosition(pos);
|
|
|
|
|
else
|
|
|
|
|
moveLeft();
|
2009-08-11 14:39:44 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2010-01-21 17:38:27 +01:00
|
|
|
case RangeLineMode:
|
|
|
|
|
case RangeLineModeExclusive: {
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2014-11-01 17:57:42 +01:00
|
|
|
moveToStartOfLine();
|
2012-08-09 13:05:06 +02:00
|
|
|
m_targetColumn = 0;
|
2012-10-13 09:33:30 +02:00
|
|
|
bool lastLine = false;
|
2012-08-09 13:05:06 +02:00
|
|
|
if (pasteAfter) {
|
2012-10-13 09:33:30 +02:00
|
|
|
lastLine = document()->lastBlock() == this->block();
|
2012-08-09 13:05:06 +02:00
|
|
|
if (lastLine) {
|
|
|
|
|
tc.movePosition(EndOfLine, MoveAnchor);
|
|
|
|
|
tc.insertBlock();
|
2011-03-28 13:25:15 +02:00
|
|
|
}
|
2012-08-09 13:05:06 +02:00
|
|
|
moveDown();
|
2009-08-11 14:39:44 +02:00
|
|
|
}
|
2012-11-01 21:25:43 +01:00
|
|
|
const int pos = position();
|
2012-10-13 09:33:30 +02:00
|
|
|
if (lastLine)
|
|
|
|
|
insertText(text.repeated(count()).left(text.size() * count() - 1));
|
|
|
|
|
else
|
|
|
|
|
insertText(text.repeated(count()));
|
2012-08-09 13:05:06 +02:00
|
|
|
setPosition(pos);
|
2009-08-11 14:39:44 +02:00
|
|
|
moveToFirstNonBlankOnLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-12-11 18:59:05 +01:00
|
|
|
case RangeBlockAndTailMode:
|
2009-08-11 14:39:44 +02:00
|
|
|
case RangeBlockMode: {
|
2012-08-09 13:05:06 +02:00
|
|
|
const int pos = position();
|
|
|
|
|
if (pasteAfter && rightDist() > 0)
|
2009-11-10 10:07:36 +01:00
|
|
|
moveRight();
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2012-08-09 13:05:06 +02:00
|
|
|
const int col = tc.columnNumber();
|
|
|
|
|
QTextBlock block = tc.block();
|
2016-03-18 12:10:24 +01:00
|
|
|
const QStringList lines = text.split('\n');
|
2012-08-09 13:05:06 +02:00
|
|
|
for (int i = 0; i < lines.size() - 1; ++i) {
|
|
|
|
|
if (!block.isValid()) {
|
|
|
|
|
tc.movePosition(EndOfDocument);
|
|
|
|
|
tc.insertBlock();
|
|
|
|
|
block = tc.block();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// resize line
|
|
|
|
|
int length = block.length();
|
|
|
|
|
int begin = block.position();
|
|
|
|
|
if (col >= length) {
|
|
|
|
|
tc.setPosition(begin + length - 1);
|
2016-03-18 12:10:24 +01:00
|
|
|
tc.insertText(QString(col - length + 1, ' '));
|
2009-10-16 11:30:46 +02:00
|
|
|
} else {
|
2012-08-09 13:05:06 +02:00
|
|
|
tc.setPosition(begin + col);
|
2009-08-11 14:39:44 +02:00
|
|
|
}
|
2012-08-09 13:05:06 +02:00
|
|
|
|
|
|
|
|
// insert text
|
|
|
|
|
const QString line = lines.at(i).repeated(count());
|
2009-08-11 14:39:44 +02:00
|
|
|
tc.insertText(line);
|
2012-08-09 13:05:06 +02:00
|
|
|
|
|
|
|
|
// next line
|
2009-08-11 14:39:44 +02:00
|
|
|
block = block.next();
|
|
|
|
|
}
|
2012-08-09 13:05:06 +02:00
|
|
|
setPosition(pos);
|
|
|
|
|
if (pasteAfter)
|
|
|
|
|
moveRight();
|
2009-08-11 14:39:44 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-05 08:47:53 +02:00
|
|
|
|
|
|
|
|
endEditBlock();
|
2009-01-16 16:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-22 12:00:39 +01:00
|
|
|
void FakeVimHandler::Private::cutSelectedText(int reg)
|
2014-11-01 17:57:42 +01:00
|
|
|
{
|
2014-11-16 13:04:03 +01:00
|
|
|
pushUndoState();
|
|
|
|
|
|
2014-11-01 17:57:42 +01:00
|
|
|
bool visualMode = isVisualMode();
|
|
|
|
|
leaveVisualMode();
|
|
|
|
|
|
|
|
|
|
Range range = currentRange();
|
|
|
|
|
if (visualMode && g.rangemode == RangeCharMode)
|
|
|
|
|
++range.endPos;
|
|
|
|
|
|
2019-03-22 12:00:39 +01:00
|
|
|
if (!reg)
|
|
|
|
|
reg = m_register;
|
|
|
|
|
|
2014-11-01 17:57:42 +01:00
|
|
|
g.submode = DeleteSubMode;
|
2019-03-22 12:00:39 +01:00
|
|
|
yankText(range, reg);
|
2014-11-01 17:57:42 +01:00
|
|
|
removeText(range);
|
|
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
|
|
|
|
|
if (g.rangemode == RangeLineMode)
|
|
|
|
|
handleStartOfLine();
|
|
|
|
|
else if (g.rangemode == RangeBlockMode)
|
|
|
|
|
setPosition(qMin(position(), anchor()));
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
|
|
|
|
|
{
|
2012-11-23 19:50:24 +01:00
|
|
|
int pos = position();
|
2013-04-08 20:06:17 +02:00
|
|
|
const int blockNumber = m_cursor.blockNumber();
|
2012-11-23 19:50:24 +01:00
|
|
|
for (int i = qMax(count - 2, 0); i >= 0 && blockNumber < document()->blockCount(); --i) {
|
2012-10-26 18:34:58 +02:00
|
|
|
moveBehindEndOfLine();
|
2012-11-23 19:50:24 +01:00
|
|
|
pos = position();
|
2012-10-26 18:34:58 +02:00
|
|
|
setAnchor();
|
|
|
|
|
moveRight();
|
|
|
|
|
if (preserveSpace) {
|
|
|
|
|
removeText(currentRange());
|
|
|
|
|
} else {
|
2016-03-18 12:10:24 +01:00
|
|
|
while (characterAtCursor() == ' ' || characterAtCursor() == '\t')
|
2012-10-26 18:34:58 +02:00
|
|
|
moveRight();
|
2016-03-18 12:10:24 +01:00
|
|
|
m_cursor.insertText(QString(' '));
|
2012-10-26 18:34:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
2012-11-23 19:50:24 +01:00
|
|
|
setPosition(pos);
|
2012-10-26 18:34:58 +02:00
|
|
|
}
|
|
|
|
|
|
2013-03-16 13:32:32 +01:00
|
|
|
void FakeVimHandler::Private::insertNewLine()
|
|
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
if ( m_buffer->editBlockLevel <= 1 && hasConfig(ConfigPassKeys) ) {
|
2016-03-18 12:10:24 +01:00
|
|
|
QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "\n");
|
2014-11-10 18:27:38 +01:00
|
|
|
if (passEventToEditor(event, m_cursor))
|
2013-04-01 12:03:22 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2013-03-16 13:32:32 +01:00
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
insertText(QString("\n"));
|
2013-04-01 12:03:22 +02:00
|
|
|
insertAutomaticIndentation(true);
|
|
|
|
|
}
|
2013-03-16 13:32:32 +01:00
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
bool FakeVimHandler::Private::handleInsertInEditor(const Input &input)
|
2013-04-01 12:03:22 +02:00
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->editBlockLevel > 0 || !hasConfig(ConfigPassKeys))
|
2013-04-01 12:03:22 +02:00
|
|
|
return false;
|
2013-03-16 13:32:32 +01:00
|
|
|
|
2013-04-07 10:37:34 +02:00
|
|
|
joinPreviousEditBlock();
|
|
|
|
|
|
2013-09-26 22:15:48 +02:00
|
|
|
QKeyEvent event(QEvent::KeyPress, input.key(), input.modifiers(), input.text());
|
2013-04-08 20:06:17 +02:00
|
|
|
setAnchor();
|
2014-11-10 18:27:38 +01:00
|
|
|
if (!passEventToEditor(event, m_cursor))
|
2013-09-10 18:39:05 +02:00
|
|
|
return !m_textedit && !m_plaintextedit; // Mark event as handled if it has destroyed editor.
|
2013-04-01 12:03:22 +02:00
|
|
|
|
2013-04-07 10:37:34 +02:00
|
|
|
endEditBlock();
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
setTargetColumn();
|
|
|
|
|
|
2013-04-01 12:03:22 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
bool FakeVimHandler::Private::passEventToEditor(QEvent &event, QTextCursor &tc)
|
2013-04-01 12:03:22 +02:00
|
|
|
{
|
|
|
|
|
removeEventFilter();
|
2018-02-09 18:49:18 +01:00
|
|
|
q->requestDisableBlockSelection();
|
2013-04-01 12:03:22 +02:00
|
|
|
|
2014-11-16 10:53:15 +01:00
|
|
|
setThinCursor();
|
2014-11-10 18:27:38 +01:00
|
|
|
EDITOR(setTextCursor(tc));
|
|
|
|
|
|
2013-04-01 12:03:22 +02:00
|
|
|
bool accepted = QApplication::sendEvent(editor(), &event);
|
2013-09-10 18:39:05 +02:00
|
|
|
if (!m_textedit && !m_plaintextedit)
|
|
|
|
|
return false;
|
2014-11-10 18:27:38 +01:00
|
|
|
|
2013-04-01 12:03:22 +02:00
|
|
|
if (accepted)
|
2015-05-16 09:21:36 +02:00
|
|
|
tc = editorCursor();
|
2013-04-01 12:03:22 +02:00
|
|
|
|
|
|
|
|
return accepted;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-09 17:40:00 +01:00
|
|
|
QString FakeVimHandler::Private::lineContents(int line) const
|
|
|
|
|
{
|
2012-10-05 17:33:20 +02:00
|
|
|
return document()->findBlockByLineNumber(line - 1).text();
|
2009-12-09 17:40:00 +01:00
|
|
|
}
|
|
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
QString FakeVimHandler::Private::textAt(int from, int to) const
|
|
|
|
|
{
|
|
|
|
|
QTextCursor tc(document());
|
|
|
|
|
tc.setPosition(from);
|
|
|
|
|
tc.setPosition(to, KeepAnchor);
|
2016-03-18 12:10:24 +01:00
|
|
|
return tc.selectedText().replace(ParagraphSeparator, '\n');
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-11 14:26:37 +02:00
|
|
|
void FakeVimHandler::Private::setLineContents(int line, const QString &contents)
|
2009-12-09 17:40:00 +01:00
|
|
|
{
|
2012-10-05 17:33:20 +02:00
|
|
|
QTextBlock block = document()->findBlockByLineNumber(line - 1);
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2010-07-06 16:17:27 +02:00
|
|
|
const int begin = block.position();
|
|
|
|
|
const int len = block.length();
|
|
|
|
|
tc.setPosition(begin);
|
|
|
|
|
tc.setPosition(begin + len - 1, KeepAnchor);
|
2009-12-09 17:40:00 +01:00
|
|
|
tc.insertText(contents);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-14 16:56:37 +02:00
|
|
|
int FakeVimHandler::Private::blockBoundary(const QString &left,
|
2012-08-14 18:53:33 +02:00
|
|
|
const QString &right, bool closing, int count) const
|
2012-07-14 16:56:37 +02:00
|
|
|
{
|
|
|
|
|
const QString &begin = closing ? left : right;
|
|
|
|
|
const QString &end = closing ? right : left;
|
|
|
|
|
|
2012-08-14 18:53:33 +02:00
|
|
|
// shift cursor if it is already on opening/closing string
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc1 = m_cursor;
|
2012-07-14 16:56:37 +02:00
|
|
|
int pos = tc1.position();
|
2012-08-14 18:53:33 +02:00
|
|
|
int max = document()->characterCount();
|
2012-07-14 16:56:37 +02:00
|
|
|
int sz = left.size();
|
2012-08-14 18:53:33 +02:00
|
|
|
int from = qMax(pos - sz + 1, 0);
|
|
|
|
|
int to = qMin(pos + sz, max);
|
|
|
|
|
tc1.setPosition(from);
|
|
|
|
|
tc1.setPosition(to, KeepAnchor);
|
|
|
|
|
int i = tc1.selectedText().indexOf(left);
|
|
|
|
|
if (i != -1) {
|
|
|
|
|
// - on opening string:
|
|
|
|
|
tc1.setPosition(from + i + sz);
|
2012-07-14 16:56:37 +02:00
|
|
|
} else {
|
|
|
|
|
sz = right.size();
|
2012-08-14 18:53:33 +02:00
|
|
|
from = qMax(pos - sz + 1, 0);
|
|
|
|
|
to = qMin(pos + sz, max);
|
|
|
|
|
tc1.setPosition(from);
|
|
|
|
|
tc1.setPosition(to, KeepAnchor);
|
|
|
|
|
i = tc1.selectedText().indexOf(right);
|
|
|
|
|
if (i != -1) {
|
|
|
|
|
// - on closing string:
|
|
|
|
|
tc1.setPosition(from + i);
|
2012-07-14 16:56:37 +02:00
|
|
|
} else {
|
2013-04-08 20:06:17 +02:00
|
|
|
tc1 = m_cursor;
|
2012-07-14 16:56:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor tc2 = tc1;
|
|
|
|
|
QTextDocument::FindFlags flags(closing ? 0 : QTextDocument::FindBackward);
|
|
|
|
|
int level = 0;
|
2012-08-14 18:53:33 +02:00
|
|
|
int counter = 0;
|
2012-07-14 16:56:37 +02:00
|
|
|
while (true) {
|
|
|
|
|
tc2 = document()->find(end, tc2, flags);
|
|
|
|
|
if (tc2.isNull())
|
|
|
|
|
return -1;
|
2012-08-14 18:53:33 +02:00
|
|
|
if (!tc1.isNull())
|
|
|
|
|
tc1 = document()->find(begin, tc1, flags);
|
2012-07-14 16:56:37 +02:00
|
|
|
|
|
|
|
|
while (!tc1.isNull() && (closing ? (tc1 < tc2) : (tc2 < tc1))) {
|
|
|
|
|
++level;
|
|
|
|
|
tc1 = document()->find(begin, tc1, flags);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-20 18:53:44 +02:00
|
|
|
while (level > 0
|
|
|
|
|
&& (tc1.isNull() || (closing ? (tc2 < tc1) : (tc1 < tc2)))) {
|
2012-07-14 16:56:37 +02:00
|
|
|
--level;
|
|
|
|
|
tc2 = document()->find(end, tc2, flags);
|
|
|
|
|
if (tc2.isNull())
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (level == 0
|
|
|
|
|
&& (tc1.isNull() || (closing ? (tc2 < tc1) : (tc1 < tc2)))) {
|
2012-08-14 18:53:33 +02:00
|
|
|
++counter;
|
|
|
|
|
if (counter >= count)
|
|
|
|
|
break;
|
2012-07-14 16:56:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tc2.position() - end.size();
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-05 17:33:20 +02:00
|
|
|
int FakeVimHandler::Private::lineNumber(const QTextBlock &block) const
|
|
|
|
|
{
|
|
|
|
|
if (block.isVisible())
|
|
|
|
|
return block.firstLineNumber() + 1;
|
2012-07-14 16:56:37 +02:00
|
|
|
|
2012-10-05 17:33:20 +02:00
|
|
|
// Folded block has line number of the nearest previous visible line.
|
|
|
|
|
QTextBlock block2 = block;
|
|
|
|
|
while (block2.isValid() && !block2.isVisible())
|
|
|
|
|
block2 = block2.previous();
|
|
|
|
|
return block2.firstLineNumber() + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-09 16:37:23 +01:00
|
|
|
int FakeVimHandler::Private::columnAt(int pos) const
|
|
|
|
|
{
|
|
|
|
|
return pos - blockAt(pos).position();
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-20 19:57:16 +01:00
|
|
|
int FakeVimHandler::Private::blockNumberAt(int pos) const
|
|
|
|
|
{
|
|
|
|
|
return blockAt(pos).blockNumber();
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-09 16:37:23 +01:00
|
|
|
QTextBlock FakeVimHandler::Private::blockAt(int pos) const
|
|
|
|
|
{
|
|
|
|
|
return document()->findBlock(pos);
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-09 10:04:40 +02:00
|
|
|
QTextBlock FakeVimHandler::Private::nextLine(const QTextBlock &block) const
|
|
|
|
|
{
|
2014-11-09 16:37:23 +01:00
|
|
|
return blockAt(block.position() + block.length());
|
2013-05-09 10:04:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextBlock FakeVimHandler::Private::previousLine(const QTextBlock &block) const
|
|
|
|
|
{
|
2014-11-09 16:37:23 +01:00
|
|
|
return blockAt(block.position() - 1);
|
2013-05-09 10:04:40 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-05 17:33:20 +02:00
|
|
|
int FakeVimHandler::Private::firstPositionInLine(int line, bool onlyVisibleLines) const
|
2009-01-06 11:33:07 +01:00
|
|
|
{
|
2012-10-05 17:33:20 +02:00
|
|
|
QTextBlock block = onlyVisibleLines ? document()->findBlockByLineNumber(line - 1)
|
|
|
|
|
: document()->findBlockByNumber(line - 1);
|
|
|
|
|
return block.position();
|
2009-01-06 11:33:07 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-05 17:33:20 +02:00
|
|
|
int FakeVimHandler::Private::lastPositionInLine(int line, bool onlyVisibleLines) const
|
2009-02-05 17:06:45 +01:00
|
|
|
{
|
2012-10-13 09:33:30 +02:00
|
|
|
QTextBlock block;
|
2012-10-05 17:33:20 +02:00
|
|
|
if (onlyVisibleLines) {
|
2013-05-09 10:04:40 +02:00
|
|
|
block = document()->findBlockByLineNumber(line - 1);
|
|
|
|
|
// respect folds and wrapped lines
|
|
|
|
|
do {
|
|
|
|
|
block = nextLine(block);
|
|
|
|
|
} while (block.isValid() && !block.isVisible());
|
2012-10-05 17:33:20 +02:00
|
|
|
if (block.isValid()) {
|
|
|
|
|
if (line > 0)
|
|
|
|
|
block = block.previous();
|
|
|
|
|
} else {
|
|
|
|
|
block = document()->lastBlock();
|
|
|
|
|
}
|
2012-10-13 09:33:30 +02:00
|
|
|
} else {
|
|
|
|
|
block = document()->findBlockByNumber(line - 1);
|
2012-10-05 17:33:20 +02:00
|
|
|
}
|
2012-10-23 16:35:13 +02:00
|
|
|
|
|
|
|
|
const int position = block.position() + block.length() - 1;
|
2013-04-08 21:25:29 +02:00
|
|
|
if (block.length() > 1 && !isVisualMode() && !isInsertMode())
|
2012-10-23 16:35:13 +02:00
|
|
|
return position - 1;
|
|
|
|
|
return position;
|
2009-02-05 17:06:45 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-06 11:42:44 +01:00
|
|
|
int FakeVimHandler::Private::lineForPosition(int pos) const
|
|
|
|
|
{
|
2014-11-09 16:37:23 +01:00
|
|
|
const QTextBlock block = blockAt(pos);
|
2013-10-16 18:53:45 +02:00
|
|
|
if (!block.isValid())
|
|
|
|
|
return 0;
|
|
|
|
|
const int positionInBlock = pos - block.position();
|
|
|
|
|
const int lineNumberInBlock = block.layout()->lineForTextPosition(positionInBlock).lineNumber();
|
|
|
|
|
return block.firstLineNumber() + lineNumberInBlock + 1;
|
2009-01-06 11:42:44 +01:00
|
|
|
}
|
|
|
|
|
|
2011-08-02 17:59:05 +02:00
|
|
|
void FakeVimHandler::Private::toggleVisualMode(VisualMode visualMode)
|
2009-01-06 11:42:44 +01:00
|
|
|
{
|
2014-04-23 17:46:17 +02:00
|
|
|
if (visualMode == g.visualMode) {
|
2011-08-02 17:59:05 +02:00
|
|
|
leaveVisualMode();
|
|
|
|
|
} else {
|
|
|
|
|
m_positionPastEnd = false;
|
|
|
|
|
m_anchorPastEnd = false;
|
2013-05-06 20:02:22 +02:00
|
|
|
g.visualMode = visualMode;
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->lastVisualMode = visualMode;
|
2011-08-02 17:59:05 +02:00
|
|
|
}
|
2009-01-06 11:42:44 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-06 11:52:05 +01:00
|
|
|
void FakeVimHandler::Private::leaveVisualMode()
|
2009-01-06 11:42:44 +01:00
|
|
|
{
|
2012-09-09 10:32:45 +02:00
|
|
|
if (!isVisualMode())
|
|
|
|
|
return;
|
|
|
|
|
|
2013-03-17 14:17:25 +01:00
|
|
|
if (isVisualLineMode()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.rangemode = RangeLineMode;
|
|
|
|
|
g.movetype = MoveLineWise;
|
2013-03-17 14:17:25 +01:00
|
|
|
} else if (isVisualCharMode()) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.rangemode = RangeCharMode;
|
|
|
|
|
g.movetype = MoveInclusive;
|
2013-03-17 14:17:25 +01:00
|
|
|
} else if (isVisualBlockMode()) {
|
2014-11-16 13:04:03 +01:00
|
|
|
g.rangemode = m_visualTargetColumn == -1 ? RangeBlockAndTailMode : RangeBlockMode;
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveInclusive;
|
2013-03-17 14:17:25 +01:00
|
|
|
}
|
2010-01-21 17:38:28 +01:00
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
g.visualMode = NoVisualMode;
|
2009-01-06 11:42:44 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
void FakeVimHandler::Private::saveLastVisualMode()
|
|
|
|
|
{
|
|
|
|
|
if (isVisualMode() && g.mode == CommandMode && g.submode == NoSubMode) {
|
2016-03-18 12:10:24 +01:00
|
|
|
setMark('<', markLessPosition());
|
|
|
|
|
setMark('>', markGreaterPosition());
|
2014-11-10 18:27:38 +01:00
|
|
|
m_buffer->lastVisualModeInverted = anchor() > position();
|
|
|
|
|
m_buffer->lastVisualMode = g.visualMode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 11:50:30 +01:00
|
|
|
QWidget *FakeVimHandler::Private::editor() const
|
2009-01-06 11:43:27 +01:00
|
|
|
{
|
|
|
|
|
return m_textedit
|
|
|
|
|
? static_cast<QWidget *>(m_textedit)
|
|
|
|
|
: static_cast<QWidget *>(m_plaintextedit);
|
|
|
|
|
}
|
2009-01-06 11:42:44 +01:00
|
|
|
|
2012-09-29 19:09:08 +02:00
|
|
|
void FakeVimHandler::Private::joinPreviousEditBlock()
|
|
|
|
|
{
|
|
|
|
|
UNDO_DEBUG("JOIN");
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->breakEditBlock) {
|
2012-09-29 19:09:08 +02:00
|
|
|
beginEditBlock();
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc(m_cursor);
|
2013-04-08 18:44:33 +02:00
|
|
|
tc.setPosition(tc.position());
|
2013-04-07 10:37:34 +02:00
|
|
|
tc.beginEditBlock();
|
2016-03-18 12:10:24 +01:00
|
|
|
tc.insertText("X");
|
2013-04-07 10:37:34 +02:00
|
|
|
tc.deletePreviousChar();
|
|
|
|
|
tc.endEditBlock();
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->breakEditBlock = false;
|
2012-11-01 21:25:43 +01:00
|
|
|
} else {
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->editBlockLevel == 0 && !m_buffer->undo.empty())
|
|
|
|
|
m_buffer->undoState = m_buffer->undo.pop();
|
2013-04-08 21:25:29 +02:00
|
|
|
beginEditBlock();
|
2012-11-01 21:25:43 +01:00
|
|
|
}
|
2012-09-29 19:09:08 +02:00
|
|
|
}
|
|
|
|
|
|
2013-04-07 10:37:34 +02:00
|
|
|
void FakeVimHandler::Private::beginEditBlock(bool largeEditBlock)
|
2012-09-29 19:09:08 +02:00
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
UNDO_DEBUG("BEGIN EDIT BLOCK" << m_buffer->editBlockLevel + 1);
|
|
|
|
|
if (!largeEditBlock && !m_buffer->undoState.isValid())
|
2013-04-08 21:25:29 +02:00
|
|
|
pushUndoState(false);
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->editBlockLevel == 0)
|
|
|
|
|
m_buffer->breakEditBlock = true;
|
|
|
|
|
++m_buffer->editBlockLevel;
|
2012-09-29 19:09:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::endEditBlock()
|
|
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
UNDO_DEBUG("END EDIT BLOCK" << m_buffer->editBlockLevel);
|
2018-04-11 12:03:04 +02:00
|
|
|
if (m_buffer->editBlockLevel <= 0) {
|
|
|
|
|
qWarning("beginEditBlock() not called before endEditBlock()!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-04-30 17:32:25 +02:00
|
|
|
--m_buffer->editBlockLevel;
|
|
|
|
|
if (m_buffer->editBlockLevel == 0 && m_buffer->undoState.isValid()) {
|
|
|
|
|
m_buffer->undo.push(m_buffer->undoState);
|
|
|
|
|
m_buffer->undoState = State();
|
2013-04-08 21:25:29 +02:00
|
|
|
}
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->editBlockLevel == 0)
|
|
|
|
|
m_buffer->breakEditBlock = false;
|
2013-04-07 10:37:34 +02:00
|
|
|
}
|
|
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
void FakeVimHandler::Private::onContentsChanged(int position, int charsRemoved, int charsAdded)
|
2013-04-07 10:37:34 +02:00
|
|
|
{
|
2013-07-09 19:39:31 +02:00
|
|
|
// Record inserted and deleted text in insert mode.
|
2014-11-23 09:56:37 +01:00
|
|
|
if (isInsertMode() && (charsAdded > 0 || charsRemoved > 0) && canModifyBufferData()) {
|
2014-04-30 17:32:25 +02:00
|
|
|
BufferData::InsertState &insertState = m_buffer->insertState;
|
2014-11-10 18:27:38 +01:00
|
|
|
const int oldPosition = insertState.pos2;
|
2013-07-09 19:39:31 +02:00
|
|
|
if (!isInsertStateValid()) {
|
2014-11-10 18:27:38 +01:00
|
|
|
insertState.pos1 = oldPosition;
|
2016-03-18 12:10:24 +01:00
|
|
|
g.dotCommand = "i";
|
2013-07-09 19:39:31 +02:00
|
|
|
resetCount();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ignore changes outside inserted text (e.g. renaming other occurrences of a variable).
|
2014-04-30 17:32:25 +02:00
|
|
|
if (position + charsRemoved >= insertState.pos1 && position <= insertState.pos2) {
|
2013-07-09 19:39:31 +02:00
|
|
|
if (charsRemoved > 0) {
|
2016-05-25 19:33:37 +02:00
|
|
|
// Assume that in a manual edit operation a text can be removed only
|
|
|
|
|
// in front of cursor (<DELETE>) or behind it (<BACKSPACE>).
|
|
|
|
|
|
|
|
|
|
// If the recorded amount of backspace/delete keys doesn't correspond with
|
|
|
|
|
// number of removed characters, assume that the document has been changed
|
|
|
|
|
// externally and invalidate current insert state.
|
|
|
|
|
|
|
|
|
|
const bool wholeDocumentChanged =
|
|
|
|
|
charsRemoved > 1
|
|
|
|
|
&& charsAdded > 0
|
|
|
|
|
&& charsAdded + 1 == document()->characterCount();
|
|
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
if (position < insertState.pos1) {
|
2016-05-25 19:33:37 +02:00
|
|
|
// <BACKSPACE>
|
|
|
|
|
const int backspaceCount = insertState.pos1 - position;
|
|
|
|
|
if (backspaceCount != charsRemoved || (oldPosition == charsRemoved && wholeDocumentChanged)) {
|
|
|
|
|
invalidateInsertState();
|
|
|
|
|
} else {
|
|
|
|
|
const QString inserted = textAt(position, oldPosition);
|
|
|
|
|
const QString removed = insertState.textBeforeCursor.right(backspaceCount);
|
|
|
|
|
// Ignore backspaces if same text was just inserted.
|
|
|
|
|
if ( !inserted.endsWith(removed) ) {
|
|
|
|
|
insertState.backspaces += backspaceCount;
|
|
|
|
|
insertState.pos1 = position;
|
|
|
|
|
insertState.pos2 = qMax(position, insertState.pos2 - backspaceCount);
|
|
|
|
|
}
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
2014-04-30 17:32:25 +02:00
|
|
|
} else if (position + charsRemoved > insertState.pos2) {
|
2016-05-25 19:33:37 +02:00
|
|
|
// <DELETE>
|
|
|
|
|
const int deleteCount = position + charsRemoved - insertState.pos2;
|
|
|
|
|
if (deleteCount != charsRemoved || (oldPosition == 0 && wholeDocumentChanged))
|
|
|
|
|
invalidateInsertState();
|
|
|
|
|
else
|
|
|
|
|
insertState.deletes += deleteCount;
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
2014-04-30 17:32:25 +02:00
|
|
|
} else if (charsAdded > 0 && insertState.insertingSpaces) {
|
2013-07-09 19:39:31 +02:00
|
|
|
for (int i = position; i < position + charsAdded; ++i) {
|
2014-11-17 15:32:00 +01:00
|
|
|
const QChar c = characterAt(i);
|
2013-07-09 19:39:31 +02:00
|
|
|
if (c.unicode() == ' ' || c.unicode() == '\t')
|
2014-04-30 17:32:25 +02:00
|
|
|
insertState.spaces.insert(i);
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
const int newPosition = position + charsAdded;
|
|
|
|
|
insertState.pos2 = qMax(insertState.pos2 + charsAdded - charsRemoved, newPosition);
|
|
|
|
|
insertState.textBeforeCursor = textAt(block().position(), newPosition);
|
2013-07-09 19:39:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
2013-08-06 17:13:53 +02:00
|
|
|
|
|
|
|
|
if (!m_highlighted.isEmpty())
|
2018-02-09 18:49:18 +01:00
|
|
|
q->highlightMatches(m_highlighted);
|
2013-04-08 21:25:29 +02:00
|
|
|
}
|
2013-04-07 10:37:34 +02:00
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
void FakeVimHandler::Private::onCursorPositionChanged()
|
|
|
|
|
{
|
2014-11-16 10:53:15 +01:00
|
|
|
if (!m_inFakeVim) {
|
2014-11-10 18:27:38 +01:00
|
|
|
m_cursorNeedsUpdate = true;
|
2014-11-16 10:53:15 +01:00
|
|
|
|
|
|
|
|
// Selecting text with mouse disables the thick cursor so it's more obvious
|
|
|
|
|
// that extra character under cursor is not selected when moving text around or
|
|
|
|
|
// making operations on text outside FakeVim mode.
|
2015-05-16 09:21:36 +02:00
|
|
|
setThinCursor(g.mode == InsertMode || editorCursor().hasSelection());
|
2014-11-16 10:53:15 +01:00
|
|
|
}
|
2014-11-10 18:27:38 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-08 21:25:29 +02:00
|
|
|
void FakeVimHandler::Private::onUndoCommandAdded()
|
|
|
|
|
{
|
2014-11-23 09:56:37 +01:00
|
|
|
if (!canModifyBufferData())
|
|
|
|
|
return;
|
|
|
|
|
|
2013-07-20 08:36:05 +02:00
|
|
|
// Undo commands removed?
|
2014-04-30 17:32:25 +02:00
|
|
|
UNDO_DEBUG("Undo added" << "previous: REV" << m_buffer->lastRevision);
|
|
|
|
|
if (m_buffer->lastRevision >= revision()) {
|
|
|
|
|
UNDO_DEBUG("UNDO REMOVED!");
|
|
|
|
|
const int removed = m_buffer->lastRevision - revision();
|
|
|
|
|
for (int i = m_buffer->undo.size() - 1; i >= 0; --i) {
|
|
|
|
|
if ((m_buffer->undo[i].revision -= removed) < 0) {
|
|
|
|
|
m_buffer->undo.remove(0, i + 1);
|
2013-07-20 08:36:05 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-19 22:07:06 +02:00
|
|
|
}
|
2013-07-20 08:36:05 +02:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->redo.clear();
|
2013-07-20 08:36:05 +02:00
|
|
|
// External change while FakeVim disabled.
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->editBlockLevel == 0 && !m_buffer->undo.isEmpty() && !isInsertMode())
|
|
|
|
|
m_buffer->undo.push(State());
|
2012-09-29 19:09:08 +02:00
|
|
|
}
|
|
|
|
|
|
2014-11-16 10:53:15 +01:00
|
|
|
void FakeVimHandler::Private::onInputTimeout()
|
|
|
|
|
{
|
|
|
|
|
enterFakeVim();
|
|
|
|
|
EventResult result = handleKey(Input());
|
2014-12-01 17:28:44 +01:00
|
|
|
leaveFakeVim(result);
|
2014-11-16 10:53:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::onFixCursorTimeout()
|
|
|
|
|
{
|
|
|
|
|
if (editor())
|
|
|
|
|
fixExternalCursorPosition(editor()->hasFocus() && !isCommandLineMode());
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-01 07:47:25 +02:00
|
|
|
char FakeVimHandler::Private::currentModeCode() const
|
|
|
|
|
{
|
2013-08-11 17:15:56 +02:00
|
|
|
if (g.mode == ExMode)
|
2012-09-29 13:17:21 +02:00
|
|
|
return 'c';
|
|
|
|
|
else if (isVisualMode())
|
2012-09-01 07:47:25 +02:00
|
|
|
return 'v';
|
2013-08-11 17:15:56 +02:00
|
|
|
else if (isOperatorPending())
|
|
|
|
|
return 'o';
|
2013-05-06 20:02:22 +02:00
|
|
|
else if (g.mode == CommandMode)
|
2012-09-01 07:47:25 +02:00
|
|
|
return 'n';
|
2013-08-11 17:15:56 +02:00
|
|
|
else if (g.submode != NoSubMode)
|
|
|
|
|
return ' ';
|
2012-09-01 07:47:25 +02:00
|
|
|
else
|
|
|
|
|
return 'i';
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-28 10:43:22 +01:00
|
|
|
void FakeVimHandler::Private::undoRedo(bool undo)
|
2009-01-06 11:45:56 +01:00
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
UNDO_DEBUG((undo ? "UNDO" : "REDO"));
|
|
|
|
|
|
2010-05-11 14:26:37 +02:00
|
|
|
// FIXME: That's only an approximaxtion. The real solution might
|
|
|
|
|
// be to store marks and old userData with QTextBlock setUserData
|
|
|
|
|
// and retrieve them afterward.
|
2014-04-30 17:32:25 +02:00
|
|
|
QStack<State> &stack = undo ? m_buffer->undo : m_buffer->redo;
|
|
|
|
|
QStack<State> &stack2 = undo ? m_buffer->redo : m_buffer->undo;
|
2012-10-28 10:43:22 +01:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
State state = m_buffer->undoState.isValid() ? m_buffer->undoState
|
2013-07-20 08:36:05 +02:00
|
|
|
: !stack.empty() ? stack.pop() : State();
|
2013-04-07 10:37:34 +02:00
|
|
|
|
2013-04-08 20:06:17 +02:00
|
|
|
CursorPosition lastPos(m_cursor);
|
2013-07-20 08:36:05 +02:00
|
|
|
if (undo ? !document()->isUndoAvailable() : !document()->isRedoAvailable()) {
|
2014-08-25 17:05:11 +02:00
|
|
|
const QString msg = undo ? Tr::tr("Already at oldest change.")
|
|
|
|
|
: Tr::tr("Already at newest change.");
|
2013-07-20 08:36:05 +02:00
|
|
|
showMessage(MessageInfo, msg);
|
2014-04-30 17:32:25 +02:00
|
|
|
UNDO_DEBUG(msg);
|
2013-07-20 08:36:05 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
clearMessage();
|
2013-04-08 21:25:29 +02:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
++m_buffer->editBlockLevel;
|
2013-04-08 21:25:29 +02:00
|
|
|
|
|
|
|
|
// Do undo/redo [count] times to reach previous revision.
|
2013-07-20 08:36:05 +02:00
|
|
|
const int previousRevision = revision();
|
|
|
|
|
if (undo) {
|
|
|
|
|
do {
|
2013-04-07 10:37:34 +02:00
|
|
|
EDITOR(undo());
|
2013-07-20 08:36:05 +02:00
|
|
|
} while (document()->isUndoAvailable() && state.revision >= 0 && state.revision < revision());
|
|
|
|
|
} else {
|
|
|
|
|
do {
|
2013-04-07 10:37:34 +02:00
|
|
|
EDITOR(redo());
|
2013-07-20 08:36:05 +02:00
|
|
|
} while (document()->isRedoAvailable() && state.revision > revision());
|
2013-04-07 10:37:34 +02:00
|
|
|
}
|
2012-08-26 18:39:48 +02:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
--m_buffer->editBlockLevel;
|
2012-08-26 18:39:48 +02:00
|
|
|
|
2013-04-08 21:25:29 +02:00
|
|
|
if (state.isValid()) {
|
2014-04-30 17:32:25 +02:00
|
|
|
Marks marks = m_buffer->marks;
|
2013-04-08 21:25:29 +02:00
|
|
|
marks.swap(state.marks);
|
|
|
|
|
updateMarks(marks);
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->lastVisualMode = state.lastVisualMode;
|
|
|
|
|
m_buffer->lastVisualModeInverted = state.lastVisualModeInverted;
|
2016-03-18 12:10:24 +01:00
|
|
|
setMark('.', state.position);
|
|
|
|
|
setMark('\'', lastPos);
|
|
|
|
|
setMark('`', lastPos);
|
2014-11-14 18:51:21 +01:00
|
|
|
setCursorPosition(state.position);
|
2013-04-08 21:25:29 +02:00
|
|
|
setAnchor();
|
2013-07-20 08:36:05 +02:00
|
|
|
state.revision = previousRevision;
|
2013-04-08 20:06:17 +02:00
|
|
|
} else {
|
|
|
|
|
updateFirstVisibleLine();
|
2014-02-19 19:18:14 +01:00
|
|
|
pullCursor();
|
2012-08-26 18:39:48 +02:00
|
|
|
}
|
2013-04-19 22:07:06 +02:00
|
|
|
stack2.push(state);
|
2009-10-26 12:51:25 +01:00
|
|
|
|
2010-05-17 16:25:58 +02:00
|
|
|
setTargetColumn();
|
2010-02-19 13:01:39 +01:00
|
|
|
if (atEndOfLine())
|
|
|
|
|
moveLeft();
|
2014-04-30 17:32:25 +02:00
|
|
|
|
|
|
|
|
UNDO_DEBUG((undo ? "UNDONE" : "REDONE"));
|
2009-01-06 11:45:56 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-28 10:43:22 +01:00
|
|
|
void FakeVimHandler::Private::undo()
|
2009-01-06 11:45:56 +01:00
|
|
|
{
|
2012-10-28 10:43:22 +01:00
|
|
|
undoRedo(true);
|
|
|
|
|
}
|
2009-10-26 12:51:25 +01:00
|
|
|
|
2012-10-28 10:43:22 +01:00
|
|
|
void FakeVimHandler::Private::redo()
|
|
|
|
|
{
|
|
|
|
|
undoRedo(false);
|
2009-01-16 13:10:42 +01:00
|
|
|
}
|
|
|
|
|
|
2010-09-13 15:23:20 +02:00
|
|
|
void FakeVimHandler::Private::updateCursorShape()
|
2010-05-04 11:00:40 +02:00
|
|
|
{
|
2014-11-16 10:53:15 +01:00
|
|
|
setThinCursor(
|
|
|
|
|
g.mode == InsertMode
|
|
|
|
|
|| isVisualLineMode()
|
|
|
|
|
|| isVisualBlockMode()
|
|
|
|
|
|| isCommandLineMode()
|
|
|
|
|
|| !editor()->hasFocus());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::setThinCursor(bool enable)
|
|
|
|
|
{
|
|
|
|
|
EDITOR(setOverwriteMode(!enable));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::hasThinCursor() const
|
|
|
|
|
{
|
|
|
|
|
return !EDITOR(overwriteMode());
|
2010-05-04 11:00:40 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-06 12:10:57 +02:00
|
|
|
void FakeVimHandler::Private::enterReplaceMode()
|
|
|
|
|
{
|
2013-07-09 19:39:31 +02:00
|
|
|
enterInsertOrReplaceMode(ReplaceMode);
|
2010-05-06 12:10:57 +02:00
|
|
|
}
|
|
|
|
|
|
2009-01-06 13:03:59 +01:00
|
|
|
void FakeVimHandler::Private::enterInsertMode()
|
|
|
|
|
{
|
2013-07-09 19:39:31 +02:00
|
|
|
enterInsertOrReplaceMode(InsertMode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::enterInsertOrReplaceMode(Mode mode)
|
|
|
|
|
{
|
2018-04-11 12:03:04 +02:00
|
|
|
if (mode != InsertMode && mode != ReplaceMode) {
|
|
|
|
|
qWarning("Unexpected mode");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-07-09 19:39:31 +02:00
|
|
|
if (g.mode == mode)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
g.mode = mode;
|
2014-11-12 20:18:18 +01:00
|
|
|
|
|
|
|
|
if (g.returnToMode == mode) {
|
|
|
|
|
// Returning to insert mode after <C-O>.
|
|
|
|
|
clearCurrentMode();
|
|
|
|
|
moveToTargetColumn();
|
|
|
|
|
invalidateInsertState();
|
|
|
|
|
} else {
|
|
|
|
|
// Entering insert mode from command mode.
|
|
|
|
|
if (mode == InsertMode) {
|
|
|
|
|
// m_targetColumn shouldn't be -1 (end of line).
|
|
|
|
|
if (m_targetColumn == -1)
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
|
|
|
|
g.returnToMode = mode;
|
|
|
|
|
clearLastInsertion();
|
|
|
|
|
}
|
2009-01-06 13:03:59 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
void FakeVimHandler::Private::enterVisualInsertMode(QChar command)
|
2012-12-04 20:37:39 +01:00
|
|
|
{
|
2014-02-03 20:47:47 +01:00
|
|
|
if (isVisualBlockMode()) {
|
2016-03-18 12:10:24 +01:00
|
|
|
bool append = command == 'A';
|
|
|
|
|
bool change = command == 's' || command == 'c';
|
2012-12-04 20:37:39 +01:00
|
|
|
|
2013-04-25 17:40:06 +02:00
|
|
|
leaveVisualMode();
|
2012-12-04 20:37:39 +01:00
|
|
|
|
2014-02-03 20:47:47 +01:00
|
|
|
const CursorPosition lastAnchor = markLessPosition();
|
|
|
|
|
const CursorPosition lastPosition = markGreaterPosition();
|
2013-04-25 17:40:06 +02:00
|
|
|
CursorPosition pos(lastAnchor.line,
|
2014-02-03 20:47:47 +01:00
|
|
|
append ? qMax(lastPosition.column, lastAnchor.column) + 1
|
|
|
|
|
: qMin(lastPosition.column, lastAnchor.column));
|
|
|
|
|
|
|
|
|
|
if (append) {
|
|
|
|
|
m_visualBlockInsert = m_visualTargetColumn == -1 ? AppendToEndOfLineBlockInsertMode
|
|
|
|
|
: AppendBlockInsertMode;
|
|
|
|
|
} else if (change) {
|
|
|
|
|
m_visualBlockInsert = ChangeBlockInsertMode;
|
|
|
|
|
beginEditBlock();
|
2014-11-01 17:57:42 +01:00
|
|
|
cutSelectedText();
|
2014-02-03 20:47:47 +01:00
|
|
|
endEditBlock();
|
|
|
|
|
} else {
|
|
|
|
|
m_visualBlockInsert = InsertBlockInsertMode;
|
2013-04-25 17:40:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setCursorPosition(pos);
|
2014-02-03 20:47:47 +01:00
|
|
|
if (m_visualBlockInsert == AppendToEndOfLineBlockInsertMode)
|
|
|
|
|
moveBehindEndOfLine();
|
2013-04-25 17:40:06 +02:00
|
|
|
} else {
|
2014-02-03 20:47:47 +01:00
|
|
|
m_visualBlockInsert = NoneBlockInsertMode;
|
2013-04-25 17:40:06 +02:00
|
|
|
leaveVisualMode();
|
2016-03-18 12:10:24 +01:00
|
|
|
if (command == 'I') {
|
2013-04-25 17:40:06 +02:00
|
|
|
if (lineForPosition(anchor()) <= lineForPosition(position())) {
|
|
|
|
|
setPosition(qMin(anchor(), position()));
|
|
|
|
|
moveToStartOfLine();
|
|
|
|
|
}
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (command == 'A') {
|
2013-04-25 17:40:06 +02:00
|
|
|
if (lineForPosition(anchor()) <= lineForPosition(position())) {
|
|
|
|
|
setPosition(position());
|
|
|
|
|
moveRight(qMin(rightDist(), 1));
|
|
|
|
|
} else {
|
|
|
|
|
setPosition(anchor());
|
|
|
|
|
moveToStartOfLine();
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-12-04 20:37:39 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-25 17:40:06 +02:00
|
|
|
setAnchor();
|
2014-02-03 20:47:47 +01:00
|
|
|
if (m_visualBlockInsert != ChangeBlockInsertMode)
|
|
|
|
|
breakEditBlock();
|
|
|
|
|
enterInsertMode();
|
2012-12-04 20:37:39 +01:00
|
|
|
}
|
|
|
|
|
|
2012-11-10 10:36:05 +01:00
|
|
|
void FakeVimHandler::Private::enterCommandMode(Mode returnToMode)
|
2009-01-06 13:03:59 +01:00
|
|
|
{
|
2014-10-26 07:38:27 +01:00
|
|
|
if (g.isRecording && isCommandLineMode())
|
|
|
|
|
record(Input(Key_Escape, NoModifier));
|
|
|
|
|
|
2014-11-10 18:27:38 +01:00
|
|
|
if (isNoVisualMode()) {
|
|
|
|
|
if (atEndOfLine()) {
|
|
|
|
|
m_cursor.movePosition(Left, KeepAnchor);
|
|
|
|
|
if (m_targetColumn != -1)
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
}
|
|
|
|
|
setAnchor();
|
2014-11-11 19:58:59 +01:00
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:02:22 +02:00
|
|
|
g.mode = CommandMode;
|
2014-11-12 20:18:18 +01:00
|
|
|
clearCurrentMode();
|
2012-11-10 10:36:05 +01:00
|
|
|
g.returnToMode = returnToMode;
|
2014-11-01 17:57:42 +01:00
|
|
|
m_positionPastEnd = false;
|
|
|
|
|
m_anchorPastEnd = false;
|
2009-01-06 13:03:59 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-26 18:34:58 +02:00
|
|
|
void FakeVimHandler::Private::enterExMode(const QString &contents)
|
2009-01-26 12:08:39 +01:00
|
|
|
{
|
2012-10-26 18:34:58 +02:00
|
|
|
g.currentMessage.clear();
|
2013-10-16 20:13:52 +02:00
|
|
|
g.commandBuffer.clear();
|
2012-10-26 18:34:58 +02:00
|
|
|
if (isVisualMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
g.commandBuffer.setContents(QString("'<,'>") + contents, contents.size() + 5);
|
2012-10-26 18:34:58 +02:00
|
|
|
else
|
|
|
|
|
g.commandBuffer.setContents(contents, contents.size());
|
2013-05-06 20:02:22 +02:00
|
|
|
g.mode = ExMode;
|
|
|
|
|
g.submode = NoSubMode;
|
|
|
|
|
g.subsubmode = NoSubSubMode;
|
2014-11-16 10:53:15 +01:00
|
|
|
unfocus();
|
2009-01-26 12:08:39 +01:00
|
|
|
}
|
|
|
|
|
|
2012-12-07 18:46:30 +01:00
|
|
|
void FakeVimHandler::Private::recordJump(int position)
|
2009-01-28 19:22:54 +01:00
|
|
|
{
|
2012-12-07 18:46:30 +01:00
|
|
|
CursorPosition pos = position >= 0 ? CursorPosition(document(), position)
|
2013-04-08 20:06:17 +02:00
|
|
|
: CursorPosition(m_cursor);
|
2016-03-18 12:10:24 +01:00
|
|
|
setMark('\'', pos);
|
|
|
|
|
setMark('`', pos);
|
2014-04-30 17:32:25 +02:00
|
|
|
if (m_buffer->jumpListUndo.isEmpty() || m_buffer->jumpListUndo.top() != pos)
|
|
|
|
|
m_buffer->jumpListUndo.push(pos);
|
|
|
|
|
m_buffer->jumpListRedo.clear();
|
|
|
|
|
UNDO_DEBUG("jumps: " << m_buffer->jumpListUndo);
|
2009-01-28 19:22:54 +01:00
|
|
|
}
|
|
|
|
|
|
2012-08-11 14:31:42 +02:00
|
|
|
void FakeVimHandler::Private::jump(int distance)
|
|
|
|
|
{
|
2014-04-30 17:32:25 +02:00
|
|
|
QStack<CursorPosition> &from = (distance > 0) ? m_buffer->jumpListRedo : m_buffer->jumpListUndo;
|
|
|
|
|
QStack<CursorPosition> &to = (distance > 0) ? m_buffer->jumpListUndo : m_buffer->jumpListRedo;
|
2012-08-11 14:31:42 +02:00
|
|
|
int len = qMin(qAbs(distance), from.size());
|
2013-04-08 20:06:17 +02:00
|
|
|
CursorPosition m(m_cursor);
|
2016-03-18 12:10:24 +01:00
|
|
|
setMark('\'', m);
|
|
|
|
|
setMark('`', m);
|
2012-08-11 14:31:42 +02:00
|
|
|
for (int i = 0; i < len; ++i) {
|
2012-10-23 16:35:13 +02:00
|
|
|
to.push(m);
|
2012-08-11 14:31:42 +02:00
|
|
|
setCursorPosition(from.top());
|
|
|
|
|
from.pop();
|
|
|
|
|
}
|
2014-11-04 18:53:44 +01:00
|
|
|
setTargetColumn();
|
2012-08-11 14:31:42 +02:00
|
|
|
}
|
|
|
|
|
|
2010-03-09 16:44:36 +01:00
|
|
|
Column FakeVimHandler::Private::indentation(const QString &line) const
|
2009-12-11 13:24:53 +01:00
|
|
|
{
|
|
|
|
|
int ts = config(ConfigTabStop).toInt();
|
|
|
|
|
int physical = 0;
|
|
|
|
|
int logical = 0;
|
|
|
|
|
int n = line.size();
|
|
|
|
|
while (physical < n) {
|
|
|
|
|
QChar c = line.at(physical);
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == ' ')
|
2009-12-11 13:24:53 +01:00
|
|
|
++logical;
|
2016-03-18 12:10:24 +01:00
|
|
|
else if (c == '\t')
|
2009-12-11 13:24:53 +01:00
|
|
|
logical += ts - logical % ts;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
++physical;
|
|
|
|
|
}
|
2010-03-09 16:44:36 +01:00
|
|
|
return Column(physical, logical);
|
2009-12-11 13:24:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString FakeVimHandler::Private::tabExpand(int n) const
|
|
|
|
|
{
|
|
|
|
|
int ts = config(ConfigTabStop).toInt();
|
|
|
|
|
if (hasConfig(ConfigExpandTab) || ts < 1)
|
2016-03-18 12:10:24 +01:00
|
|
|
return QString(n, ' ');
|
|
|
|
|
return QString(n / ts, '\t')
|
|
|
|
|
+ QString(n % ts, ' ');
|
2009-12-11 13:24:53 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-08 17:50:16 +01:00
|
|
|
void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool forceAutoIndent)
|
2009-04-03 11:54:29 +02:00
|
|
|
{
|
2013-03-08 17:50:16 +01:00
|
|
|
if (!forceAutoIndent && !hasConfig(ConfigAutoIndent) && !hasConfig(ConfigSmartIndent))
|
2009-04-03 11:54:29 +02:00
|
|
|
return;
|
2010-01-06 14:57:46 +01:00
|
|
|
|
|
|
|
|
if (hasConfig(ConfigSmartIndent)) {
|
2010-09-13 15:23:20 +02:00
|
|
|
QTextBlock bl = block();
|
|
|
|
|
Range range(bl.position(), bl.position());
|
2016-03-18 12:10:24 +01:00
|
|
|
indentText(range, '\n');
|
2010-01-06 14:57:46 +01:00
|
|
|
} else {
|
2010-09-13 15:23:20 +02:00
|
|
|
QTextBlock bl = goingDown ? block().previous() : block().next();
|
|
|
|
|
QString text = bl.text();
|
2010-01-06 14:57:46 +01:00
|
|
|
int pos = 0;
|
|
|
|
|
int n = text.size();
|
|
|
|
|
while (pos < n && text.at(pos).isSpace())
|
|
|
|
|
++pos;
|
|
|
|
|
text.truncate(pos);
|
|
|
|
|
// FIXME: handle 'smartindent' and 'cindent'
|
2010-05-11 14:26:37 +02:00
|
|
|
insertText(text);
|
2010-01-06 14:57:46 +01:00
|
|
|
}
|
2009-04-03 11:54:29 +02:00
|
|
|
}
|
|
|
|
|
|
2009-04-03 14:58:41 +02:00
|
|
|
void FakeVimHandler::Private::handleStartOfLine()
|
|
|
|
|
{
|
|
|
|
|
if (hasConfig(ConfigStartOfLine))
|
|
|
|
|
moveToFirstNonBlankOnLine();
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-09 19:39:31 +02:00
|
|
|
void FakeVimHandler::Private::replay(const QString &command, int repeat)
|
2009-04-06 10:58:48 +02:00
|
|
|
{
|
2013-07-09 19:39:31 +02:00
|
|
|
if (repeat <= 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-05-04 17:58:53 +02:00
|
|
|
//qDebug() << "REPLAY: " << quoteUnprintable(command);
|
2014-11-12 20:18:18 +01:00
|
|
|
clearCurrentMode();
|
2020-05-28 07:46:41 +02:00
|
|
|
const Inputs inputs(command);
|
2013-07-09 19:39:31 +02:00
|
|
|
for (int i = 0; i < repeat; ++i) {
|
2020-05-28 07:46:41 +02:00
|
|
|
for (const Input &in : inputs) {
|
2013-07-09 19:39:31 +02:00
|
|
|
if (handleDefaultKey(in) != EventHandled)
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-06-11 15:41:20 +02:00
|
|
|
}
|
2009-04-06 10:58:48 +02:00
|
|
|
}
|
2009-03-30 16:54:25 +02:00
|
|
|
|
2012-09-28 17:01:24 +02:00
|
|
|
QString FakeVimHandler::Private::visualDotCommand() const
|
|
|
|
|
{
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor start(m_cursor);
|
2012-09-28 17:01:24 +02:00
|
|
|
QTextCursor end(start);
|
|
|
|
|
end.setPosition(end.anchor());
|
|
|
|
|
|
2012-12-04 20:37:39 +01:00
|
|
|
QString command;
|
|
|
|
|
|
2012-09-28 17:01:24 +02:00
|
|
|
if (isVisualCharMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
command = "v";
|
2012-12-04 20:37:39 +01:00
|
|
|
else if (isVisualLineMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
command = "V";
|
2012-12-04 20:37:39 +01:00
|
|
|
else if (isVisualBlockMode())
|
2016-03-18 12:10:24 +01:00
|
|
|
command = "<c-v>";
|
2012-12-04 20:37:39 +01:00
|
|
|
else
|
|
|
|
|
return QString();
|
2012-09-28 17:01:24 +02:00
|
|
|
|
2012-12-04 20:37:39 +01:00
|
|
|
const int down = qAbs(start.blockNumber() - end.blockNumber());
|
|
|
|
|
if (down != 0)
|
2016-03-18 12:10:24 +01:00
|
|
|
command.append(QString("%1j").arg(down));
|
2012-09-28 17:01:24 +02:00
|
|
|
|
2012-12-04 20:37:39 +01:00
|
|
|
const int right = start.positionInBlock() - end.positionInBlock();
|
|
|
|
|
if (right != 0) {
|
|
|
|
|
command.append(QString::number(qAbs(right)));
|
2012-12-09 22:09:33 +02:00
|
|
|
command.append(QLatin1Char(right < 0 && isVisualBlockMode() ? 'h' : 'l'));
|
2012-09-28 17:01:24 +02:00
|
|
|
}
|
|
|
|
|
|
2012-12-04 20:37:39 +01:00
|
|
|
return command;
|
2012-09-28 17:01:24 +02:00
|
|
|
}
|
|
|
|
|
|
2012-08-19 19:08:04 +02:00
|
|
|
void FakeVimHandler::Private::selectTextObject(bool simple, bool inner)
|
2010-03-18 17:45:15 +01:00
|
|
|
{
|
2012-12-02 19:04:55 +01:00
|
|
|
const int position1 = this->position();
|
|
|
|
|
const int anchor1 = this->anchor();
|
|
|
|
|
bool setupAnchor = (position1 == anchor1);
|
|
|
|
|
bool forward = anchor1 <= position1;
|
2012-11-23 17:59:43 +01:00
|
|
|
const int repeat = count();
|
2012-08-19 19:08:04 +02:00
|
|
|
|
|
|
|
|
// set anchor if not already set
|
|
|
|
|
if (setupAnchor) {
|
2012-11-23 17:59:43 +01:00
|
|
|
// Select nothing with 'inner' on empty line.
|
|
|
|
|
if (inner && atEmptyLine() && repeat == 1) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
2012-11-23 17:59:43 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2012-08-19 19:08:04 +02:00
|
|
|
moveToBoundaryStart(1, simple, false);
|
|
|
|
|
setAnchor();
|
2012-12-02 19:04:55 +01:00
|
|
|
} else if (forward) {
|
2014-11-04 18:53:44 +01:00
|
|
|
moveToNextCharacter();
|
2012-12-02 19:04:55 +01:00
|
|
|
} else {
|
2014-11-04 18:53:44 +01:00
|
|
|
moveToPreviousCharacter();
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inner) {
|
|
|
|
|
moveToBoundaryEnd(repeat, simple);
|
|
|
|
|
} else {
|
2012-12-02 19:04:55 +01:00
|
|
|
const int direction = forward ? 1 : -1;
|
2012-08-19 19:08:04 +02:00
|
|
|
for (int i = 0; i < repeat; ++i) {
|
|
|
|
|
// select leading spaces
|
|
|
|
|
bool leadingSpace = characterAtCursor().isSpace();
|
2012-12-02 19:04:55 +01:00
|
|
|
if (leadingSpace) {
|
|
|
|
|
if (forward)
|
|
|
|
|
moveToNextBoundaryStart(1, simple);
|
|
|
|
|
else
|
|
|
|
|
moveToNextBoundaryEnd(1, simple, false);
|
|
|
|
|
}
|
2012-08-19 19:08:04 +02:00
|
|
|
|
|
|
|
|
// select word
|
2012-12-02 19:04:55 +01:00
|
|
|
if (forward)
|
|
|
|
|
moveToWordEnd(1, simple);
|
|
|
|
|
else
|
|
|
|
|
moveToWordStart(1, simple, false);
|
2012-08-19 19:08:04 +02:00
|
|
|
|
|
|
|
|
// select trailing spaces if no leading space
|
2014-11-17 15:32:00 +01:00
|
|
|
QChar afterCursor = characterAt(position() + direction);
|
2013-04-08 20:06:17 +02:00
|
|
|
if (!leadingSpace && afterCursor.isSpace() && afterCursor != ParagraphSeparator
|
2012-08-19 19:08:04 +02:00
|
|
|
&& !atBlockStart()) {
|
2012-12-02 19:04:55 +01:00
|
|
|
if (forward)
|
|
|
|
|
moveToNextBoundaryEnd(1, simple);
|
|
|
|
|
else
|
|
|
|
|
moveToNextBoundaryStart(1, simple, false);
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if there are no trailing spaces in selection select all leading spaces
|
|
|
|
|
// after previous character
|
|
|
|
|
if (setupAnchor && (!characterAtCursor().isSpace() || atBlockEnd())) {
|
|
|
|
|
int min = block().position();
|
|
|
|
|
int pos = anchor();
|
2014-11-17 15:32:00 +01:00
|
|
|
while (pos >= min && characterAt(--pos).isSpace()) {}
|
2012-08-19 19:08:04 +02:00
|
|
|
if (pos >= min)
|
|
|
|
|
setAnchorAndPosition(pos + 1, position());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i + 1 < repeat) {
|
2014-11-04 18:53:44 +01:00
|
|
|
if (forward)
|
|
|
|
|
moveToNextCharacter();
|
|
|
|
|
else
|
|
|
|
|
moveToPreviousCharacter();
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inner) {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveInclusive;
|
2012-08-19 19:08:04 +02:00
|
|
|
} else {
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
2014-11-04 18:53:44 +01:00
|
|
|
if (isNoVisualMode())
|
|
|
|
|
moveToNextCharacter();
|
|
|
|
|
else if (isVisualLineMode())
|
2013-05-06 20:02:22 +02:00
|
|
|
g.visualMode = VisualCharMode;
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-21 11:36:42 +01:00
|
|
|
setTargetColumn();
|
2012-08-19 19:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::selectWordTextObject(bool inner)
|
|
|
|
|
{
|
|
|
|
|
selectTextObject(false, inner);
|
2010-03-18 17:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::selectWORDTextObject(bool inner)
|
|
|
|
|
{
|
2012-08-19 19:08:04 +02:00
|
|
|
selectTextObject(true, inner);
|
2010-03-18 17:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::selectSentenceTextObject(bool inner)
|
|
|
|
|
{
|
2019-07-23 10:58:00 +02:00
|
|
|
Q_UNUSED(inner)
|
2010-03-18 17:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::Private::selectParagraphTextObject(bool inner)
|
|
|
|
|
{
|
2014-11-20 19:57:16 +01:00
|
|
|
const QTextCursor oldCursor = m_cursor;
|
|
|
|
|
const VisualMode oldVisualMode = g.visualMode;
|
|
|
|
|
|
|
|
|
|
const int anchorBlock = blockNumberAt(anchor());
|
|
|
|
|
const int positionBlock = blockNumberAt(position());
|
|
|
|
|
const bool setupAnchor = anchorBlock == positionBlock;
|
|
|
|
|
int repeat = count();
|
|
|
|
|
|
|
|
|
|
// If anchor and position are in the same block,
|
|
|
|
|
// start line selection at beginning of current paragraph.
|
|
|
|
|
if (setupAnchor) {
|
|
|
|
|
moveToParagraphStartOrEnd(-1);
|
|
|
|
|
setAnchor();
|
|
|
|
|
|
|
|
|
|
if (!isVisualLineMode() && isVisualMode())
|
|
|
|
|
toggleVisualMode(VisualLineMode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool forward = anchor() <= position();
|
|
|
|
|
const int d = forward ? 1 : -1;
|
|
|
|
|
|
|
|
|
|
bool startsAtParagraph = !atEmptyLine(position());
|
|
|
|
|
|
|
|
|
|
moveToParagraphStartOrEnd(d);
|
|
|
|
|
|
|
|
|
|
// If selection already changed, decreate count.
|
|
|
|
|
if ((setupAnchor && g.submode != NoSubMode)
|
|
|
|
|
|| oldVisualMode != g.visualMode
|
|
|
|
|
|| m_cursor != oldCursor)
|
|
|
|
|
{
|
|
|
|
|
--repeat;
|
|
|
|
|
if (!inner) {
|
|
|
|
|
moveDown(d);
|
|
|
|
|
moveToParagraphStartOrEnd(d);
|
|
|
|
|
startsAtParagraph = !startsAtParagraph;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (repeat > 0) {
|
|
|
|
|
bool isCountEven = repeat % 2 == 0;
|
|
|
|
|
bool endsOnParagraph =
|
|
|
|
|
inner ? isCountEven == startsAtParagraph : startsAtParagraph;
|
|
|
|
|
|
|
|
|
|
if (inner) {
|
|
|
|
|
repeat = repeat / 2;
|
|
|
|
|
if (!isCountEven || endsOnParagraph)
|
|
|
|
|
++repeat;
|
|
|
|
|
} else {
|
|
|
|
|
if (endsOnParagraph)
|
|
|
|
|
++repeat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!moveToNextParagraph(d * repeat)) {
|
|
|
|
|
m_cursor = oldCursor;
|
|
|
|
|
g.visualMode = oldVisualMode;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (endsOnParagraph && atEmptyLine())
|
|
|
|
|
moveUp(d);
|
|
|
|
|
else
|
|
|
|
|
moveToParagraphStartOrEnd(d);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!inner && setupAnchor && !atEmptyLine() && !atEmptyLine(anchor())) {
|
|
|
|
|
// If position cannot select empty lines, try to select them with anchor.
|
|
|
|
|
setAnchorAndPosition(position(), anchor());
|
|
|
|
|
moveToNextParagraph(-d);
|
|
|
|
|
moveToParagraphStartOrEnd(-d);
|
|
|
|
|
setAnchorAndPosition(position(), anchor());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
recordJump(oldCursor.position());
|
|
|
|
|
setTargetColumn();
|
|
|
|
|
g.movetype = MoveLineWise;
|
2010-03-18 17:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
2012-11-04 07:33:05 +01:00
|
|
|
bool FakeVimHandler::Private::selectBlockTextObject(bool inner,
|
2016-03-18 12:10:24 +01:00
|
|
|
QChar left, QChar right)
|
2010-03-18 17:45:15 +01:00
|
|
|
{
|
2016-03-18 12:10:24 +01:00
|
|
|
int p1 = blockBoundary(left, right, false, count());
|
2012-07-14 16:56:37 +02:00
|
|
|
if (p1 == -1)
|
2012-11-04 07:33:05 +01:00
|
|
|
return false;
|
2012-07-14 16:56:37 +02:00
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
int p2 = blockBoundary(left, right, true, count());
|
2012-07-14 16:56:37 +02:00
|
|
|
if (p2 == -1)
|
2012-11-04 07:33:05 +01:00
|
|
|
return false;
|
2012-07-14 16:56:37 +02:00
|
|
|
|
2014-11-17 15:32:33 +01:00
|
|
|
g.movetype = MoveExclusive;
|
|
|
|
|
|
|
|
|
|
if (inner) {
|
2016-03-18 12:10:24 +01:00
|
|
|
p1 += 1;
|
2014-11-17 15:32:33 +01:00
|
|
|
bool moveStart = characterAt(p1) == ParagraphSeparator;
|
|
|
|
|
bool moveEnd = isFirstNonBlankOnLine(p2);
|
|
|
|
|
if (moveStart)
|
|
|
|
|
++p1;
|
|
|
|
|
if (moveEnd)
|
|
|
|
|
p2 = blockAt(p2).position() - 1;
|
|
|
|
|
if (moveStart && moveEnd)
|
|
|
|
|
g.movetype = MoveLineWise;
|
|
|
|
|
} else {
|
2016-03-18 12:10:24 +01:00
|
|
|
p2 += 1;
|
2014-11-17 15:32:33 +01:00
|
|
|
}
|
2012-07-14 16:56:37 +02:00
|
|
|
|
2012-11-22 17:12:55 +01:00
|
|
|
if (isVisualMode())
|
|
|
|
|
--p2;
|
|
|
|
|
|
2012-07-20 18:53:44 +02:00
|
|
|
setAnchorAndPosition(p1, p2);
|
2012-11-04 07:33:05 +01:00
|
|
|
|
|
|
|
|
return true;
|
2010-03-18 17:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
2012-11-29 17:11:43 +01:00
|
|
|
bool FakeVimHandler::Private::changeNumberTextObject(int count)
|
2011-12-27 14:54:49 +01:00
|
|
|
{
|
2012-11-29 12:29:16 +01:00
|
|
|
const QTextBlock block = this->block();
|
|
|
|
|
const QString lineText = block.text();
|
2013-04-08 20:06:17 +02:00
|
|
|
const int posMin = m_cursor.positionInBlock() + 1;
|
2012-11-29 12:29:16 +01:00
|
|
|
|
|
|
|
|
// find first decimal, hexadecimal or octal number under or after cursor position
|
2020-06-22 15:40:34 +02:00
|
|
|
QRegularExpression re("(0[xX])(0*[0-9a-fA-F]+)|(0)(0*[0-7]+)(?=\\D|$)|(\\d+)");
|
|
|
|
|
QRegularExpressionMatch match;
|
|
|
|
|
QRegularExpressionMatchIterator it = re.globalMatch(lineText);
|
|
|
|
|
while (true) {
|
|
|
|
|
if (!it.hasNext())
|
|
|
|
|
return false;
|
|
|
|
|
match = it.next();
|
|
|
|
|
if (match.capturedEnd() >= posMin)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
int pos = match.capturedStart();
|
|
|
|
|
int len = match.capturedLength();
|
|
|
|
|
QString prefix = match.captured(1) + match.captured(3);
|
2016-03-18 12:10:24 +01:00
|
|
|
bool hex = prefix.length() >= 2 && (prefix[1].toLower() == 'x');
|
2012-11-29 12:29:16 +01:00
|
|
|
bool octal = !hex && !prefix.isEmpty();
|
2020-06-22 15:40:34 +02:00
|
|
|
const QString num = hex ? match.captured(2) : octal ? match.captured(4) : match.captured(5);
|
2012-11-29 12:29:16 +01:00
|
|
|
|
|
|
|
|
// parse value
|
|
|
|
|
bool ok;
|
|
|
|
|
int base = hex ? 16 : octal ? 8 : 10;
|
2013-01-16 15:35:56 +01:00
|
|
|
qlonglong value = 0; // decimal value
|
|
|
|
|
qlonglong uvalue = 0; // hexadecimal or octal value (only unsigned)
|
2012-11-29 12:29:16 +01:00
|
|
|
if (hex || octal)
|
|
|
|
|
uvalue = num.toULongLong(&ok, base);
|
|
|
|
|
else
|
|
|
|
|
value = num.toLongLong(&ok, base);
|
2018-04-11 12:03:04 +02:00
|
|
|
if (!ok) {
|
|
|
|
|
qWarning() << "Cannot parse number:" << num << "base:" << base;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-11-29 12:29:16 +01:00
|
|
|
|
|
|
|
|
// negative decimal number
|
2016-03-18 12:10:24 +01:00
|
|
|
if (!octal && !hex && pos > 0 && lineText[pos - 1] == '-') {
|
2012-11-29 12:29:16 +01:00
|
|
|
value = -value;
|
|
|
|
|
--pos;
|
|
|
|
|
++len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// result to string
|
|
|
|
|
QString repl;
|
|
|
|
|
if (hex || octal)
|
|
|
|
|
repl = QString::number(uvalue + count, base);
|
|
|
|
|
else
|
|
|
|
|
repl = QString::number(value + count, base);
|
|
|
|
|
|
|
|
|
|
// convert hexadecimal number to upper-case if last letter was upper-case
|
|
|
|
|
if (hex) {
|
2020-06-11 08:33:47 +02:00
|
|
|
const int lastLetter = num.lastIndexOf(QRegularExpression("[a-fA-F]"));
|
2012-11-29 12:29:16 +01:00
|
|
|
if (lastLetter != -1 && num[lastLetter].isUpper())
|
|
|
|
|
repl = repl.toUpper();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// preserve leading zeroes
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if ((octal || hex) && repl.size() < num.size())
|
2016-03-18 12:10:24 +01:00
|
|
|
prefix.append(QString("0").repeated(num.size() - repl.size()));
|
2012-11-29 12:29:16 +01:00
|
|
|
repl.prepend(prefix);
|
2011-12-27 14:54:49 +01:00
|
|
|
|
2012-11-29 12:29:16 +01:00
|
|
|
pos += block.position();
|
2013-04-07 10:37:34 +02:00
|
|
|
pushUndoState();
|
2012-11-29 12:29:16 +01:00
|
|
|
setAnchorAndPosition(pos, pos + len);
|
2011-12-27 14:54:49 +01:00
|
|
|
replaceText(currentRange(), repl);
|
2012-11-29 12:29:16 +01:00
|
|
|
setPosition(pos + repl.size() - 1);
|
2012-11-29 17:11:43 +01:00
|
|
|
|
|
|
|
|
return true;
|
2011-12-27 14:54:49 +01:00
|
|
|
}
|
|
|
|
|
|
2012-11-04 07:33:05 +01:00
|
|
|
bool FakeVimHandler::Private::selectQuotedStringTextObject(bool inner,
|
2012-07-19 21:12:13 +02:00
|
|
|
const QString "e)
|
2010-03-18 17:45:15 +01:00
|
|
|
{
|
2013-04-08 20:06:17 +02:00
|
|
|
QTextCursor tc = m_cursor;
|
2012-07-19 21:12:13 +02:00
|
|
|
int sz = quote.size();
|
|
|
|
|
|
|
|
|
|
QTextCursor tc1;
|
|
|
|
|
QTextCursor tc2(document());
|
|
|
|
|
while (tc2 <= tc) {
|
|
|
|
|
tc1 = document()->find(quote, tc2);
|
2014-05-01 08:06:49 +02:00
|
|
|
if (tc1.isNull())
|
2012-11-04 07:33:05 +01:00
|
|
|
return false;
|
2012-07-19 21:12:13 +02:00
|
|
|
tc2 = document()->find(quote, tc1);
|
|
|
|
|
if (tc2.isNull())
|
2012-11-04 07:33:05 +01:00
|
|
|
return false;
|
2012-07-19 21:12:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int p1 = tc1.position();
|
|
|
|
|
int p2 = tc2.position();
|
|
|
|
|
if (inner) {
|
2012-11-04 07:33:05 +01:00
|
|
|
p2 = qMax(p1, p2 - sz);
|
2014-11-17 15:32:00 +01:00
|
|
|
if (characterAt(p1) == ParagraphSeparator)
|
2012-07-19 21:12:13 +02:00
|
|
|
++p1;
|
|
|
|
|
} else {
|
|
|
|
|
p1 -= sz;
|
2012-11-04 07:33:05 +01:00
|
|
|
p2 -= sz - 1;
|
2012-07-19 21:12:13 +02:00
|
|
|
}
|
|
|
|
|
|
2012-11-24 08:26:34 +01:00
|
|
|
if (isVisualMode())
|
|
|
|
|
--p2;
|
|
|
|
|
|
2012-07-19 21:12:13 +02:00
|
|
|
setAnchorAndPosition(p1, p2);
|
2013-05-06 20:02:22 +02:00
|
|
|
g.movetype = MoveExclusive;
|
2012-11-04 07:33:05 +01:00
|
|
|
|
|
|
|
|
return true;
|
2010-03-18 17:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-28 14:00:50 +01:00
|
|
|
Mark FakeVimHandler::Private::mark(QChar code) const
|
2010-05-11 14:26:37 +02:00
|
|
|
{
|
2010-09-14 16:58:31 +02:00
|
|
|
if (isVisualMode()) {
|
2016-03-18 12:10:24 +01:00
|
|
|
if (code == '<')
|
2012-10-23 16:35:13 +02:00
|
|
|
return CursorPosition(document(), qMin(anchor(), position()));
|
2016-03-18 12:10:24 +01:00
|
|
|
if (code == '>')
|
2012-10-23 16:35:13 +02:00
|
|
|
return CursorPosition(document(), qMax(anchor(), position()));
|
2010-09-14 16:58:31 +02:00
|
|
|
}
|
2014-11-14 18:51:21 +01:00
|
|
|
|
2012-10-28 14:00:50 +01:00
|
|
|
if (code.isUpper())
|
|
|
|
|
return g.marks.value(code);
|
|
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
return m_buffer->marks.value(code);
|
2010-05-11 14:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-28 14:00:50 +01:00
|
|
|
void FakeVimHandler::Private::setMark(QChar code, CursorPosition position)
|
2010-05-11 14:26:37 +02:00
|
|
|
{
|
2012-10-28 14:00:50 +01:00
|
|
|
if (code.isUpper())
|
|
|
|
|
g.marks[code] = Mark(position, m_currentFileName);
|
|
|
|
|
else
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->marks[code] = Mark(position);
|
2012-10-28 14:00:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FakeVimHandler::Private::jumpToMark(QChar mark, bool backTickMode)
|
|
|
|
|
{
|
|
|
|
|
Mark m = this->mark(mark);
|
|
|
|
|
if (!m.isValid()) {
|
|
|
|
|
showMessage(MessageError, msgMarkNotSet(mark));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!m.isLocal(m_currentFileName)) {
|
2018-02-09 18:49:18 +01:00
|
|
|
q->requestJumpToGlobalMark(mark, backTickMode, m.fileName());
|
2012-10-28 14:00:50 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if ((mark == '\'' || mark == '`') && !m_buffer->jumpListUndo.isEmpty())
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->jumpListUndo.pop();
|
2012-10-28 14:00:50 +01:00
|
|
|
recordJump();
|
2014-02-03 20:47:47 +01:00
|
|
|
setCursorPosition(m.position(document()));
|
2012-10-28 14:00:50 +01:00
|
|
|
if (!backTickMode)
|
|
|
|
|
moveToFirstNonBlankOnLine();
|
2013-05-06 20:02:22 +02:00
|
|
|
if (g.submode == NoSubMode)
|
2012-10-29 17:36:35 +01:00
|
|
|
setAnchor();
|
2012-10-28 14:00:50 +01:00
|
|
|
setTargetColumn();
|
|
|
|
|
|
|
|
|
|
return true;
|
2010-05-11 14:26:37 +02:00
|
|
|
}
|
2010-03-18 17:45:15 +01:00
|
|
|
|
2012-11-24 08:42:06 +01:00
|
|
|
void FakeVimHandler::Private::updateMarks(const Marks &newMarks)
|
|
|
|
|
{
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto it = newMarks.cbegin(), end = newMarks.cend(); it != end; ++it)
|
2014-04-30 17:32:25 +02:00
|
|
|
m_buffer->marks[it.key()] = it.value();
|
2012-11-24 08:42:06 +01:00
|
|
|
}
|
|
|
|
|
|
2011-11-12 02:31:52 +01:00
|
|
|
RangeMode FakeVimHandler::Private::registerRangeMode(int reg) const
|
|
|
|
|
{
|
2012-08-09 10:58:08 +02:00
|
|
|
bool isClipboard;
|
|
|
|
|
bool isSelection;
|
2014-11-20 19:02:01 +01:00
|
|
|
getRegisterType(®, &isClipboard, &isSelection);
|
2012-08-09 10:58:08 +02:00
|
|
|
|
|
|
|
|
if (isClipboard || isSelection) {
|
2012-08-09 17:32:39 +02:00
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
|
QClipboard::Mode mode = isClipboard ? QClipboard::Clipboard : QClipboard::Selection;
|
|
|
|
|
|
|
|
|
|
// Use range mode from Vim's clipboard data if available.
|
|
|
|
|
const QMimeData *data = clipboard->mimeData(mode);
|
2013-02-28 14:57:17 +01:00
|
|
|
if (data && data->hasFormat(vimMimeText)) {
|
2012-08-09 17:32:39 +02:00
|
|
|
QByteArray bytes = data->data(vimMimeText);
|
|
|
|
|
if (bytes.length() > 0)
|
|
|
|
|
return static_cast<RangeMode>(bytes.at(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If register content is clipboard:
|
|
|
|
|
// - return RangeLineMode if text ends with new line char,
|
|
|
|
|
// - return RangeCharMode otherwise.
|
|
|
|
|
QString text = clipboard->text(mode);
|
2016-03-18 12:10:24 +01:00
|
|
|
return (text.endsWith('\n') || text.endsWith('\r')) ? RangeLineMode : RangeCharMode;
|
2012-08-09 10:58:08 +02:00
|
|
|
}
|
|
|
|
|
|
2011-11-12 02:31:52 +01:00
|
|
|
return g.registers[reg].rangemode;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-09 17:32:39 +02:00
|
|
|
void FakeVimHandler::Private::setRegister(int reg, const QString &contents, RangeMode mode)
|
2011-11-12 02:31:52 +01:00
|
|
|
{
|
2012-08-02 19:35:45 +02:00
|
|
|
bool copyToClipboard;
|
|
|
|
|
bool copyToSelection;
|
2014-11-20 19:02:01 +01:00
|
|
|
bool append;
|
|
|
|
|
getRegisterType(®, ©ToClipboard, ©ToSelection, &append);
|
2012-07-26 21:46:38 +02:00
|
|
|
|
2012-10-23 16:35:13 +02:00
|
|
|
QString contents2 = contents;
|
2014-11-15 13:34:32 +01:00
|
|
|
if ((mode == RangeLineMode || mode == RangeLineModeExclusive)
|
2016-03-18 12:10:24 +01:00
|
|
|
&& !contents2.endsWith('\n'))
|
2014-11-15 13:34:32 +01:00
|
|
|
{
|
2016-03-18 12:10:24 +01:00
|
|
|
contents2.append('\n');
|
2014-11-15 13:34:32 +01:00
|
|
|
}
|
2012-10-23 16:35:13 +02:00
|
|
|
|
2012-07-26 21:46:38 +02:00
|
|
|
if (copyToClipboard || copyToSelection) {
|
2012-08-09 10:58:08 +02:00
|
|
|
if (copyToClipboard)
|
2012-10-23 16:35:13 +02:00
|
|
|
setClipboardData(contents2, mode, QClipboard::Clipboard);
|
2012-08-09 10:58:08 +02:00
|
|
|
if (copyToSelection)
|
2012-10-23 16:35:13 +02:00
|
|
|
setClipboardData(contents2, mode, QClipboard::Selection);
|
2012-07-26 21:46:38 +02:00
|
|
|
} else {
|
2014-11-20 19:02:01 +01:00
|
|
|
if (append)
|
|
|
|
|
g.registers[reg].contents.append(contents2);
|
|
|
|
|
else
|
|
|
|
|
g.registers[reg].contents = contents2;
|
2012-08-09 17:32:39 +02:00
|
|
|
g.registers[reg].rangemode = mode;
|
2012-07-26 21:46:38 +02:00
|
|
|
}
|
2011-11-12 02:31:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString FakeVimHandler::Private::registerContents(int reg) const
|
|
|
|
|
{
|
2012-08-02 19:35:45 +02:00
|
|
|
bool copyFromClipboard;
|
|
|
|
|
bool copyFromSelection;
|
2014-11-20 19:02:01 +01:00
|
|
|
getRegisterType(®, ©FromClipboard, ©FromSelection);
|
2012-07-26 21:46:38 +02:00
|
|
|
|
2012-08-02 19:35:45 +02:00
|
|
|
if (copyFromClipboard || copyFromSelection) {
|
2012-07-26 21:46:38 +02:00
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
2012-08-09 10:58:08 +02:00
|
|
|
if (copyFromClipboard)
|
2012-08-02 19:35:45 +02:00
|
|
|
return clipboard->text(QClipboard::Clipboard);
|
2012-08-09 10:58:08 +02:00
|
|
|
if (copyFromSelection)
|
2012-08-02 19:35:45 +02:00
|
|
|
return clipboard->text(QClipboard::Selection);
|
2012-07-26 21:46:38 +02:00
|
|
|
}
|
|
|
|
|
|
2011-11-12 02:31:52 +01:00
|
|
|
return g.registers[reg].contents;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-20 19:02:01 +01:00
|
|
|
void FakeVimHandler::Private::getRegisterType(int *reg, bool *isClipboard, bool *isSelection, bool *append) const
|
2012-08-02 19:35:45 +02:00
|
|
|
{
|
|
|
|
|
bool clipboard = false;
|
|
|
|
|
bool selection = false;
|
|
|
|
|
|
2014-11-20 19:02:01 +01:00
|
|
|
// If register is uppercase, append content to lower case register on yank/delete.
|
|
|
|
|
const QChar c(*reg);
|
2018-11-07 23:45:39 +01:00
|
|
|
if (append != nullptr)
|
2014-11-20 19:02:01 +01:00
|
|
|
*append = c.isUpper();
|
|
|
|
|
if (c.isUpper())
|
|
|
|
|
*reg = c.toLower().unicode();
|
|
|
|
|
|
2016-03-18 12:10:24 +01:00
|
|
|
if (c == '"') {
|
|
|
|
|
QStringList list = config(ConfigClipboard).toString().split(',');
|
|
|
|
|
clipboard = list.contains("unnamedplus");
|
|
|
|
|
selection = list.contains("unnamed");
|
|
|
|
|
} else if (c == '+') {
|
2012-08-02 19:35:45 +02:00
|
|
|
clipboard = true;
|
2016-03-18 12:10:24 +01:00
|
|
|
} else if (c == '*') {
|
2012-08-02 19:35:45 +02:00
|
|
|
selection = true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-09 10:58:08 +02:00
|
|
|
// selection (primary) is clipboard on systems without selection support
|
|
|
|
|
if (selection && !QApplication::clipboard()->supportsSelection()) {
|
|
|
|
|
clipboard = true;
|
|
|
|
|
selection = false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-07 23:45:39 +01:00
|
|
|
if (isClipboard != nullptr)
|
2012-08-02 19:35:45 +02:00
|
|
|
*isClipboard = clipboard;
|
2018-11-07 23:45:39 +01:00
|
|
|
if (isSelection != nullptr)
|
2012-08-02 19:35:45 +02:00
|
|
|
*isSelection = selection;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-19 12:20:04 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// FakeVimHandler
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2009-01-23 15:12:04 +01:00
|
|
|
FakeVimHandler::FakeVimHandler(QWidget *widget, QObject *parent)
|
|
|
|
|
: QObject(parent), d(new Private(this, widget))
|
2008-12-19 12:20:04 +01:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
FakeVimHandler::~FakeVimHandler()
|
|
|
|
|
{
|
|
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-22 13:56:50 +01:00
|
|
|
// gracefully handle that the parent editor is deleted
|
|
|
|
|
void FakeVimHandler::disconnectFromEditor()
|
|
|
|
|
{
|
2018-11-07 23:45:39 +01:00
|
|
|
d->m_textedit = nullptr;
|
|
|
|
|
d->m_plaintextedit = nullptr;
|
2010-01-22 13:56:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-08-09 15:42:35 +02:00
|
|
|
void FakeVimHandler::updateGlobalMarksFilenames(const QString &oldFileName, const QString &newFileName)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < Private::g.marks.size(); ++i) {
|
|
|
|
|
Mark &mark = Private::g.marks[i];
|
|
|
|
|
if (mark.fileName() == oldFileName)
|
|
|
|
|
mark.setFileName(newFileName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-19 12:20:04 +01:00
|
|
|
bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
|
|
|
|
|
{
|
2013-03-23 19:13:30 +01:00
|
|
|
#ifndef FAKEVIM_STANDALONE
|
|
|
|
|
if (!theFakeVimSetting(ConfigUseFakeVim)->value().toBool())
|
|
|
|
|
return QObject::eventFilter(ob, ev);
|
|
|
|
|
#endif
|
2009-03-30 16:54:25 +02:00
|
|
|
|
2013-03-23 19:13:30 +01:00
|
|
|
if (ev->type() == QEvent::Shortcut) {
|
2010-01-05 15:30:22 +01:00
|
|
|
d->passShortcuts(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-23 19:13:30 +01:00
|
|
|
if (ev->type() == QEvent::KeyPress &&
|
2013-05-06 20:02:22 +02:00
|
|
|
(ob == d->editor()
|
|
|
|
|
|| (Private::g.mode == ExMode || Private::g.subsubmode == SearchSubSubMode))) {
|
2018-11-07 23:45:39 +01:00
|
|
|
auto kev = static_cast<QKeyEvent *>(ev);
|
2010-04-15 13:54:35 +02:00
|
|
|
KEY_DEBUG("KEYPRESS" << kev->key() << kev->text() << QChar(kev->key()));
|
2009-03-05 11:06:25 +01:00
|
|
|
EventResult res = d->handleEvent(kev);
|
2013-05-06 20:02:22 +02:00
|
|
|
//if (Private::g.mode == InsertMode)
|
2018-02-09 18:49:18 +01:00
|
|
|
// completionRequested();
|
2009-03-05 11:06:25 +01:00
|
|
|
// returning false core the app see it
|
2009-03-05 13:51:27 +01:00
|
|
|
//KEY_DEBUG("HANDLED CODE:" << res);
|
2009-03-05 11:06:25 +01:00
|
|
|
//return res != EventPassedToCore;
|
2009-03-05 13:51:27 +01:00
|
|
|
//return true;
|
2012-11-06 17:29:04 +01:00
|
|
|
return res == EventHandled || res == EventCancelled;
|
2009-03-04 16:32:33 +01:00
|
|
|
}
|
2009-01-09 12:21:53 +01:00
|
|
|
|
2015-03-29 11:36:47 +02:00
|
|
|
if (ev->type() == QEvent::ShortcutOverride && (ob == d->editor()
|
|
|
|
|
|| (Private::g.mode == ExMode || Private::g.subsubmode == SearchSubSubMode))) {
|
2018-11-07 23:45:39 +01:00
|
|
|
auto kev = static_cast<QKeyEvent *>(ev);
|
2009-03-05 11:06:25 +01:00
|
|
|
if (d->wantsOverride(kev)) {
|
|
|
|
|
KEY_DEBUG("OVERRIDING SHORTCUT" << kev->key());
|
|
|
|
|
ev->accept(); // accepting means "don't run the shortcuts"
|
2009-01-09 12:21:53 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2009-03-05 11:06:25 +01:00
|
|
|
KEY_DEBUG("NO SHORTCUT OVERRIDE" << kev->key());
|
|
|
|
|
return true;
|
2009-01-09 12:21:53 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-16 10:53:15 +01:00
|
|
|
if (ev->type() == QEvent::FocusOut && ob == d->editor()) {
|
|
|
|
|
d->unfocus();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-23 19:13:30 +01:00
|
|
|
if (ev->type() == QEvent::FocusIn && ob == d->editor())
|
2012-11-10 10:36:05 +01:00
|
|
|
d->focus();
|
2010-01-21 17:23:30 +01:00
|
|
|
|
2009-01-09 12:21:53 +01:00
|
|
|
return QObject::eventFilter(ob, ev);
|
2008-12-19 12:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 16:54:25 +02:00
|
|
|
void FakeVimHandler::installEventFilter()
|
|
|
|
|
{
|
|
|
|
|
d->installEventFilter();
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-23 15:12:04 +01:00
|
|
|
void FakeVimHandler::setupWidget()
|
2008-12-27 16:42:07 +01:00
|
|
|
{
|
2009-01-23 15:12:04 +01:00
|
|
|
d->setupWidget();
|
2008-12-27 16:42:07 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-26 17:55:43 +01:00
|
|
|
void FakeVimHandler::restoreWidget(int tabSize)
|
2008-12-27 16:42:07 +01:00
|
|
|
{
|
2010-03-26 17:55:43 +01:00
|
|
|
d->restoreWidget(tabSize);
|
2008-12-27 16:42:07 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-23 15:12:04 +01:00
|
|
|
void FakeVimHandler::handleCommand(const QString &cmd)
|
2008-12-27 16:42:07 +01:00
|
|
|
{
|
2012-12-07 18:46:30 +01:00
|
|
|
d->enterFakeVim();
|
2009-04-06 10:58:48 +02:00
|
|
|
d->handleCommand(cmd);
|
2012-12-07 18:46:30 +01:00
|
|
|
d->leaveFakeVim();
|
2008-12-27 16:42:07 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-18 15:31:31 +01:00
|
|
|
void FakeVimHandler::handleReplay(const QString &keys)
|
|
|
|
|
{
|
2012-12-07 18:46:30 +01:00
|
|
|
d->enterFakeVim();
|
2012-11-06 17:29:04 +01:00
|
|
|
d->replay(keys);
|
2012-12-07 18:46:30 +01:00
|
|
|
d->leaveFakeVim();
|
2011-02-18 15:31:31 +01:00
|
|
|
}
|
|
|
|
|
|
2011-04-05 16:32:18 +02:00
|
|
|
void FakeVimHandler::handleInput(const QString &keys)
|
|
|
|
|
{
|
2020-05-28 07:46:41 +02:00
|
|
|
const Inputs inputs(keys);
|
2012-12-07 18:46:30 +01:00
|
|
|
d->enterFakeVim();
|
2020-05-28 07:46:41 +02:00
|
|
|
for (const Input &input : inputs)
|
2011-04-05 16:32:18 +02:00
|
|
|
d->handleKey(input);
|
2012-12-07 18:46:30 +01:00
|
|
|
d->leaveFakeVim();
|
2011-04-05 16:32:18 +02:00
|
|
|
}
|
|
|
|
|
|
2016-12-09 10:10:31 +01:00
|
|
|
void FakeVimHandler::enterCommandMode()
|
|
|
|
|
{
|
|
|
|
|
d->enterCommandMode();
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-15 15:27:14 +01:00
|
|
|
void FakeVimHandler::setCurrentFileName(const QString &fileName)
|
|
|
|
|
{
|
2013-05-05 22:43:52 +03:00
|
|
|
d->m_currentFileName = fileName;
|
2009-01-15 15:27:14 +01:00
|
|
|
}
|
2009-01-23 15:12:04 +01:00
|
|
|
|
2010-05-18 14:48:12 +02:00
|
|
|
QString FakeVimHandler::currentFileName() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_currentFileName;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-10 22:10:23 +02:00
|
|
|
void FakeVimHandler::showMessage(MessageLevel level, const QString &msg)
|
2009-06-15 15:14:16 +02:00
|
|
|
{
|
2013-05-05 22:43:52 +03:00
|
|
|
d->showMessage(level, msg);
|
2009-06-15 15:14:16 +02:00
|
|
|
}
|
|
|
|
|
|
2009-01-23 15:12:04 +01:00
|
|
|
QWidget *FakeVimHandler::widget()
|
|
|
|
|
{
|
|
|
|
|
return d->editor();
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-11 13:24:53 +01:00
|
|
|
// Test only
|
|
|
|
|
int FakeVimHandler::physicalIndentation(const QString &line) const
|
|
|
|
|
{
|
2010-03-09 16:44:36 +01:00
|
|
|
Column ind = d->indentation(line);
|
2009-12-11 13:24:53 +01:00
|
|
|
return ind.physical;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int FakeVimHandler::logicalIndentation(const QString &line) const
|
|
|
|
|
{
|
2010-03-09 16:44:36 +01:00
|
|
|
Column ind = d->indentation(line);
|
2009-12-11 13:24:53 +01:00
|
|
|
return ind.logical;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString FakeVimHandler::tabExpand(int n) const
|
|
|
|
|
{
|
|
|
|
|
return d->tabExpand(n);
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-30 15:19:15 +01:00
|
|
|
void FakeVimHandler::miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos)
|
2012-09-10 22:10:23 +02:00
|
|
|
{
|
2012-12-30 15:19:15 +01:00
|
|
|
d->miniBufferTextEdited(text, cursorPos, anchorPos);
|
2012-09-10 22:10:23 +02:00
|
|
|
}
|
|
|
|
|
|
2012-09-28 17:01:24 +02:00
|
|
|
void FakeVimHandler::setTextCursorPosition(int position)
|
|
|
|
|
{
|
|
|
|
|
int pos = qMax(0, qMin(position, d->lastPositionInDocument()));
|
|
|
|
|
if (d->isVisualMode())
|
|
|
|
|
d->setPosition(pos);
|
|
|
|
|
else
|
|
|
|
|
d->setAnchorAndPosition(pos, pos);
|
|
|
|
|
d->setTargetColumn();
|
2013-04-08 20:06:17 +02:00
|
|
|
|
2013-10-14 16:43:34 +02:00
|
|
|
if (!d->m_inFakeVim)
|
2013-04-08 20:06:17 +02:00
|
|
|
d->commitCursor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor FakeVimHandler::textCursor() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_cursor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FakeVimHandler::setTextCursor(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
d->m_cursor = cursor;
|
2012-09-28 17:01:24 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-28 14:00:50 +01:00
|
|
|
bool FakeVimHandler::jumpToLocalMark(QChar mark, bool backTickMode)
|
|
|
|
|
{
|
|
|
|
|
return d->jumpToMark(mark, backTickMode);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 12:40:08 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace FakeVim
|
2010-03-26 13:22:06 +01:00
|
|
|
|
2014-04-30 17:32:25 +02:00
|
|
|
Q_DECLARE_METATYPE(FakeVim::Internal::FakeVimHandler::Private::BufferDataPtr)
|