15#include "katedocument.h"
17#include "kateabstractinputmode.h"
18#include "kateautoindent.h"
19#include "katebuffer.h"
20#include "katecompletionwidget.h"
21#include "kateconfig.h"
22#include "katedialogs.h"
23#include "kateglobal.h"
24#include "katehighlight.h"
25#include "kateindentdetecter.h"
26#include "katemodemanager.h"
27#include "katepartdebug.h"
28#include "kateplaintextsearch.h"
29#include "kateregexpsearch.h"
30#include "katerenderer.h"
31#include "katescriptmanager.h"
32#include "kateswapfile.h"
33#include "katesyntaxmanager.h"
34#include "katetemplatehandler.h"
35#include "kateundomanager.h"
36#include "katevariableexpansionmanager.h"
38#include "printing/kateprinter.h"
39#include "spellcheck/ontheflycheck.h"
40#include "spellcheck/prefixstore.h"
41#include "spellcheck/spellcheck.h"
46#include "editorconfig.h"
49#include <KTextEditor/Attribute>
50#include <KTextEditor/DocumentCursor>
51#include <ktexteditor/message.h>
53#include <KConfigGroup>
56#include <KIO/FileCopyJob>
57#include <KIO/JobUiDelegate>
62#include <KNetworkMounts>
63#include <KParts/OpenUrlArguments>
64#include <KStringHandler>
65#include <KToggleAction>
66#include <KXMLGUIFactory>
68#include <QApplication>
70#include <QCryptographicHash>
74#include <QMimeDatabase>
76#include <QRegularExpression>
77#include <QStandardPaths>
78#include <QTemporaryFile>
86#define EDIT_DEBUG qCDebug(LOG_KTE)
93template<
class C,
class E>
94static int indexOf(
const std::initializer_list<C> &list,
const E &entry)
100template<
class C,
class E>
101static bool contains(
const std::initializer_list<C> &list,
const E &entry)
103 return indexOf(list, entry) >= 0;
106static inline QChar matchingStartBracket(
const QChar c)
119static inline QChar matchingEndBracket(
const QChar c,
bool withQuotes =
true)
136static inline QChar matchingBracket(
const QChar c)
138 QChar bracket = matchingStartBracket(c);
140 bracket = matchingEndBracket(c,
false);
145static inline bool isStartBracket(
const QChar c)
147 return !matchingEndBracket(c,
false).
isNull();
150static inline bool isEndBracket(
const QChar c)
152 return !matchingStartBracket(c).
isNull();
155static inline bool isBracket(
const QChar c)
157 return isStartBracket(c) || isEndBracket(c);
164KTextEditor::DocumentPrivate::DocumentPrivate(
const KPluginMetaData &data,
bool bSingleViewMode,
bool bReadOnly,
QWidget *parentWidget,
QObject *parent)
166 , m_bSingleViewMode(bSingleViewMode)
167 , m_bReadOnly(bReadOnly)
177 m_docName(QStringLiteral(
"need init"))
180 m_fileType(QStringLiteral(
"Normal"))
183 m_config(new KateDocumentConfig(this))
187 const auto &aboutData = EditorPrivate::self()->aboutData();
188 setComponentName(aboutData.componentName(), aboutData.displayName());
192 setProgressInfoEnabled(
false);
198 m_buffer->setHighlight(0);
201 m_swapfile = (config()->swapFileMode() == KateDocumentConfig::DisableSwapFile) ?
nullptr : new Kate::SwapFile(this);
207 connect(KateHlManager::self(), &KateHlManager::changed,
this, &KTextEditor::DocumentPrivate::internalHlChanged);
217 m_modOnHdTimer.setSingleShot(
true);
218 m_modOnHdTimer.setInterval(200);
219 connect(&m_modOnHdTimer, &
QTimer::timeout,
this, &KTextEditor::DocumentPrivate::slotDelayedHandleModOnHd);
223 m_autoReloadMode->setWhatsThis(
i18n(
"Automatic reload the document when it was changed on disk"));
226 m_autoReloadThrottle.setSingleShot(
true);
228 m_autoReloadThrottle.setInterval(QStandardPaths::isTestModeEnabled() ? 50 : 3000);
245 if (m_bSingleViewMode && parentWidget) {
247 insertChildClient(view);
255 onTheFlySpellCheckingEnabled(config()->onTheFlySpellCheck());
260 m_autoSaveTimer.setSingleShot(
true);
262 if (isModified() && url().isLocalFile()) {
271KTextEditor::DocumentPrivate::~DocumentPrivate()
278 delete m_modOnHdHandler;
284 delete m_onTheFlyChecker;
285 m_onTheFlyChecker =
nullptr;
287 clearDictionaryRanges();
295 deactivateDirWatch();
306 for (
auto &
mark : std::as_const(m_marks)) {
321 if (m_editingStackPosition != m_editingStack.size() - 1) {
322 m_editingStack.resize(m_editingStackPosition);
326 std::shared_ptr<KTextEditor::MovingCursor> mc;
329 if (!m_editingStack.isEmpty() && cursor.
line() == m_editingStack.top()->line()) {
330 mc = m_editingStack.pop();
335 const int editingStackSizeLimit = 32;
336 if (m_editingStack.size() >= editingStackSizeLimit) {
338 m_editingStack.removeFirst();
340 mc = m_editingStack.takeFirst();
346 mc->setPosition(cursor);
348 mc = std::shared_ptr<KTextEditor::MovingCursor>(newMovingCursor(cursor));
352 m_editingStack.push(mc);
353 m_editingStackPosition = m_editingStack.size() - 1;
358 if (m_editingStack.isEmpty()) {
361 auto targetPos = m_editingStack.at(m_editingStackPosition)->toCursor();
362 if (targetPos == currentCursor) {
363 if (nextOrPrev == Previous) {
364 m_editingStackPosition--;
366 m_editingStackPosition++;
368 m_editingStackPosition = qBound(0, m_editingStackPosition, m_editingStack.size() - 1);
370 return m_editingStack.at(m_editingStackPosition)->toCursor();
375 m_editingStack.clear();
376 m_editingStackPosition = -1;
383 if (!singleViewMode()) {
394 insertChildClient(view);
404 KTextEditor::ViewPrivate *newView =
new KTextEditor::ViewPrivate(
this, parent, mainWindow);
406 if (m_fileChangedDialogsActivated) {
410 Q_EMIT viewCreated(
this, newView);
413 const auto keys = m_messageHash.keys();
415 if (!message->view()) {
416 newView->postMessage(message, m_messageHash[message]);
425 const int col1 = toVirtualColumn(range.
start());
426 const int col2 = toVirtualColumn(range.
end());
427 return KTextEditor::Range(line, fromVirtualColumn(line, col1), line, fromVirtualColumn(line, col2));
434 return editSessionNumber > 0;
439 return m_buffer->text();
445 qCWarning(LOG_KTE) <<
"Text requested for invalid range" << range;
459 for (
int i = range.
start().
line(); (i <= range.
end().
line()) && (i < m_buffer->lines()); ++i) {
464 }
else if (i == range.
end().
line()) {
486 return textLine.
at(position.
column());
491 return text(wordRangeAt(cursor));
497 const int line = cursor.
line();
501 const int lineLenth = textLine.
length();
502 if (cursor.
column() > lineLenth) {
512 while (end < lineLenth && highlight()->isInWord(textLine.
at(end), textLine.
attribute(end))) {
521 const int ln = cursor.
line();
522 const int col = cursor.
column();
524 if (ln < 0 || col < 0 || ln >= lines() || col > lineLength(ln)) {
529 Q_ASSERT(str.
length() >= col);
532 const int len = lineLength(ln);
533 if (col == 0 || col == len) {
546 qCWarning(LOG_KTE) <<
"Text requested for invalid range" << range;
555 Q_ASSERT(range.
start() <= range.
end());
560 for (
int i = range.
start().
line(); (i <= range.
end().
line()) && (i < m_buffer->lines()); ++i) {
565 }
else if (i == range.
end().
line()) {
568 ret << textLine.
text();
586bool KTextEditor::DocumentPrivate::setText(
const QString &s)
588 if (!isReadWrite()) {
592 std::vector<KTextEditor::Mark> msave;
593 msave.reserve(m_marks.size());
594 std::transform(m_marks.cbegin(), m_marks.cend(), std::back_inserter(msave), [](
KTextEditor::Mark *mark) {
598 for (
auto v : std::as_const(m_views)) {
599 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
true);
612 for (
auto v : std::as_const(m_views)) {
613 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
false);
617 setMark(mark.line, mark.type);
623bool KTextEditor::DocumentPrivate::setText(
const QStringList &text)
625 if (!isReadWrite()) {
629 std::vector<KTextEditor::Mark> msave;
630 msave.reserve(m_marks.size());
631 std::transform(m_marks.cbegin(), m_marks.cend(), std::back_inserter(msave), [](
KTextEditor::Mark *mark) {
635 for (
auto v : std::as_const(m_views)) {
636 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
true);
649 for (
auto v : std::as_const(m_views)) {
650 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
false);
654 setMark(mark.line, mark.type);
660bool KTextEditor::DocumentPrivate::clear()
662 if (!isReadWrite()) {
666 for (
auto view : std::as_const(m_views)) {
667 static_cast<ViewPrivate *
>(view)->
clear();
668 static_cast<ViewPrivate *
>(view)->tagAll();
674 Q_EMIT aboutToInvalidateMovingInterfaceContent(
this);
675 m_buffer->invalidateRanges();
677 Q_EMIT aboutToRemoveText(documentRange());
679 return editRemoveLines(0, lastLine());
684 if (!isReadWrite()) {
698 auto insertStart = position;
699 int currentLine = position.
line();
700 int currentLineStart = 0;
701 const int totalLength = text.
length();
702 int insertColumn = position.
column();
705 if (position.
line() >= lines()) {
707 while (line <= position.
line()) {
708 editInsertLine(line,
QString(),
false);
711 if (insertStart == position) {
712 insertStart = m_editLastChangeStartCursor;
720 int positionColumnExpanded = insertColumn;
721 const int tabWidth = config()->tabWidth();
723 if (currentLine < lines()) {
724 positionColumnExpanded = plainKateTextLine(currentLine).toVirtualColumn(insertColumn, tabWidth);
730 for (; pos < totalLength; pos++) {
731 const QChar &ch = text.
at(pos);
735 if (currentLineStart < pos) {
736 editInsertText(currentLine, insertColumn, text.
mid(currentLineStart, pos - currentLineStart), notify);
737 endCol = insertColumn + (pos - currentLineStart);
742 const auto wrapColumn = insertColumn + pos - currentLineStart;
743 const auto currentLineLength = lineLength(currentLine);
744 if (wrapColumn > currentLineLength) {
745 editInsertText(currentLine, currentLineLength,
QString(wrapColumn - currentLineLength,
QLatin1Char(
' ')), notify);
749 editWrapLine(currentLine, wrapColumn,
true,
nullptr, notify);
757 auto l = currentLine < lines();
758 if (currentLine == lastLine() + 1) {
759 editInsertLine(currentLine,
QString(), notify);
762 insertColumn = positionColumnExpanded;
764 insertColumn = plainKateTextLine(currentLine).fromVirtualColumn(insertColumn, tabWidth);
768 currentLineStart = pos + 1;
773 if (currentLineStart < pos) {
774 editInsertText(currentLine, insertColumn, text.
mid(currentLineStart, pos - currentLineStart), notify);
775 endCol = insertColumn + (pos - currentLineStart);
780 Q_EMIT textInsertedRange(
this, insertedRange);
788 if (!isReadWrite()) {
800 if (!isReadWrite()) {
812 Q_EMIT aboutToRemoveText(range);
818 if (range.
end().
line() > lastLine()) {
829 if (to <= lastLine()) {
830 editRemoveText(to, 0, range.
end().
column());
839 editRemoveLines(from + 1, to - 1);
843 editRemoveText(from, range.
start().
column(), m_buffer->plainLine(from).length() - range.
start().
column());
844 editUnWrapLine(from);
849 int startLine = qMax(0, range.
start().
line());
850 int vc1 = toVirtualColumn(range.
start());
851 int vc2 = toVirtualColumn(range.
end());
852 for (
int line = qMin(range.
end().
line(), lastLine()); line >= startLine; --line) {
853 int col1 = fromVirtualColumn(line, vc1);
854 int col2 = fromVirtualColumn(line, vc2);
855 editRemoveText(line, qMin(col1, col2), qAbs(col2 - col1));
863bool KTextEditor::DocumentPrivate::insertLine(
int l,
const QString &str)
865 if (!isReadWrite()) {
869 if (l < 0 || l > lines()) {
873 return editInsertLine(l, str);
876bool KTextEditor::DocumentPrivate::insertLines(
int line,
const QStringList &text)
878 if (!isReadWrite()) {
882 if (line < 0 || line > lines()) {
887 for (
const QString &
string : text) {
888 success &= editInsertLine(line++,
string);
894bool KTextEditor::DocumentPrivate::removeLine(
int line)
896 if (!isReadWrite()) {
900 if (line < 0 || line > lastLine()) {
904 return editRemoveLine(line);
910 for (
int i = 0; i < m_buffer->lines(); ++i) {
911 l += m_buffer->lineLength(i);
918 return m_buffer->lines();
923 return m_buffer->lineLength(line);
928 return m_buffer->cursorToOffset(c);
933 return m_buffer->offsetToCursor(offset);
938 if (line < 0 || line >= lines()) {
943 return l.markedAsModified();
948 if (line < 0 || line >= lines()) {
953 return l.markedAsSavedOnDisk();
958 if (line < 0 || line >= lines()) {
963 return l.markedAsModified() || l.markedAsSavedOnDisk();
975 if (editSessionNumber > 1) {
979 editIsRunning =
true;
984 m_undoManager->editStart();
986 for (
auto view : std::as_const(m_views)) {
987 static_cast<ViewPrivate *
>(view)->editStart();
990 m_buffer->editStart();
999 if (editSessionNumber == 0) {
1005 if (m_buffer->editChanged() && (editSessionNumber == 1)) {
1006 if (m_undoManager->isActive() && config()->wordWrap()) {
1007 wrapText(m_buffer->editTagStart(), m_buffer->editTagEnd());
1011 editSessionNumber--;
1013 if (editSessionNumber > 0) {
1019 m_buffer->editEnd();
1021 m_undoManager->editEnd();
1024 for (
auto view : std::as_const(m_views)) {
1025 static_cast<ViewPrivate *
>(view)->editEnd(m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
1028 if (m_buffer->editChanged()) {
1030 Q_EMIT textChanged(
this);
1036 if (m_editLastChangeStartCursor.isValid()) {
1037 saveEditingPositions(m_editLastChangeStartCursor);
1040 if (config()->autoSave() && config()->autoSaveInterval() > 0) {
1041 m_autoSaveTimer.start();
1044 editIsRunning =
false;
1048void KTextEditor::DocumentPrivate::pushEditState()
1050 editStateStack.push(editSessionNumber);
1053void KTextEditor::DocumentPrivate::popEditState()
1055 if (editStateStack.isEmpty()) {
1059 int count = editStateStack.pop() - editSessionNumber;
1070void KTextEditor::DocumentPrivate::inputMethodStart()
1072 m_undoManager->inputMethodStart();
1075void KTextEditor::DocumentPrivate::inputMethodEnd()
1077 m_undoManager->inputMethodEnd();
1082 if (startLine < 0 || endLine < 0) {
1086 if (!isReadWrite()) {
1090 int col = config()->wordWrapAt();
1098 for (
int line = startLine; (line <= endLine) && (line < lines()); line++) {
1104 bool nextlValid = line + 1 < lines();
1109 int eolPosition = l.
length() - 1;
1115 for (; z2 < l.
length(); z2++) {
1117 if (t.
at(z2) == tabChar) {
1118 x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
1128 const int colInChars = qMin(z2, l.
length() - 1);
1129 int searchStart = colInChars;
1133 if (searchStart == eolPosition && t.
at(searchStart).
isSpace()) {
1145 for (z = searchStart; z >= 0; z--) {
1149 if ((nw < 0) && highlight()->canBreakAt(t.
at(z), l.
attribute(z))) {
1165 if ((nw >= 0) && nw < colInChars) {
1168 z = (nw >= 0) ? nw : colInChars;
1172 editWrapLine(line, z,
true);
1173 editMarkLineAutoWrapped(line + 1,
true);
1178 editInsertText(line + 1, 0, QStringLiteral(
" "));
1181 bool newLineAdded =
false;
1182 editWrapLine(line, z,
false, &newLineAdded);
1184 editMarkLineAutoWrapped(line + 1,
true);
1198 if (first == last) {
1199 return wrapText(first, last);
1202 if (first < 0 || last < first) {
1206 if (last >= lines() || first > last) {
1210 if (!isReadWrite()) {
1217 std::unique_ptr<KTextEditor::MovingRange> range(newMovingRange(
KTextEditor::Range(first, 0, last, 0)));
1218 std::unique_ptr<KTextEditor::MovingCursor> curr(newMovingCursor(
KTextEditor::Cursor(range->start())));
1221 for (
int line = first; line <= range->end().line(); ++line) {
1223 if (plainKateTextLine(first).firstChar() < 0) {
1226 curr->setPosition(curr->line() + 1, 0);
1231 if (plainKateTextLine(line).firstChar() < 0) {
1232 curr->setPosition(line, 0);
1233 joinLines(first, line - 1);
1236 wrapText(first, first);
1238 first = curr->line() + 1;
1244 bool needWrap = (curr->line() != range->end().line());
1245 if (needWrap && plainKateTextLine(first).firstChar() != -1) {
1246 joinLines(first, range->end().line());
1249 wrapText(first, first);
1260 EDIT_DEBUG <<
"editInsertText" << line << col << s;
1262 if (line < 0 || col < 0) {
1271 if (!isReadWrite()) {
1275 auto l = plainKateTextLine(line);
1276 int length = l.length();
1285 if (col2 > length) {
1290 m_undoManager->slotTextInserted(line, col2, s2, l);
1296 m_buffer->insertText(m_editLastChangeStartCursor, s2);
1309 EDIT_DEBUG <<
"editRemoveText" << line << col << len;
1311 if (line < 0 || line >= lines() || col < 0 || len < 0) {
1315 if (!isReadWrite()) {
1332 len = qMin(len, l.
text().
size() - col);
1338 m_undoManager->slotTextRemoved(line, col, oldText, l);
1356 EDIT_DEBUG <<
"editMarkLineAutoWrapped" << line << autowrapped;
1358 if (line < 0 || line >= lines()) {
1362 if (!isReadWrite()) {
1368 m_undoManager->slotMarkLineAutoWrapped(line, autowrapped);
1372 m_buffer->setLineMetaData(line, l);
1382 EDIT_DEBUG <<
"editWrapLine" << line << col << newLine;
1384 if (line < 0 || line >= lines() || col < 0) {
1388 if (!isReadWrite()) {
1392 const auto tl = plainKateTextLine(line);
1396 const bool nextLineValid = lineLength(line + 1) >= 0;
1398 m_undoManager->slotLineWrapped(line, col, tl.length() - col, (!nextLineValid || newLine), tl);
1400 if (!nextLineValid || newLine) {
1404 for (
const auto &mark : std::as_const(m_marks)) {
1405 if (mark->line >= line) {
1406 if ((col == 0) || (mark->line > line)) {
1412 for (
const auto &mark : list) {
1413 m_marks.take(mark->line);
1416 for (
const auto &mark : list) {
1418 m_marks.insert(mark->line, mark);
1421 if (!list.
empty()) {
1422 Q_EMIT marksChanged(
this);
1427 (*newLineAdded) =
true;
1431 m_buffer->unwrapLine(line + 2);
1435 (*newLineAdded) =
false;
1454 EDIT_DEBUG <<
"editUnWrapLine" << line << removeLine << length;
1456 if (line < 0 || line >= lines() || line + 1 >= lines() || length < 0) {
1460 if (!isReadWrite()) {
1470 m_undoManager->slotLineUnWrapped(line, col, length, removeLine, tl, nextLine);
1473 m_buffer->unwrapLine(line + 1);
1476 m_buffer->unwrapLine(line + 1);
1480 for (
const auto &mark : std::as_const(m_marks)) {
1481 if (mark->line >= line + 1) {
1485 if (mark->line == line + 1) {
1486 auto m = m_marks.take(line);
1488 mark->type |= m->type;
1494 for (
const auto &mark : list) {
1495 m_marks.take(mark->line);
1498 for (
const auto &mark : list) {
1500 m_marks.insert(mark->line, mark);
1504 Q_EMIT marksChanged(
this);
1510 Q_EMIT textRemoved(
this,
KTextEditor::Range(line, col, line + 1, 0), QStringLiteral(
"\n"));
1520 EDIT_DEBUG <<
"editInsertLine" << line << s;
1526 if (!isReadWrite()) {
1530 if (line > lines()) {
1536 m_undoManager->slotLineInserted(line, s);
1550 for (
const auto &mark : std::as_const(m_marks)) {
1551 if (mark->line >= line) {
1556 for (
const auto &mark : list) {
1557 m_marks.take(mark->line);
1560 for (
const auto &mark : list) {
1562 m_marks.insert(mark->line, mark);
1566 Q_EMIT marksChanged(
this);
1572 int prevLineLength = lineLength(line - 1);
1579 m_editLastChangeStartCursor = rangeInserted.
start();
1582 Q_EMIT textInsertedRange(
this, rangeInserted);
1592 return editRemoveLines(line, line);
1595bool KTextEditor::DocumentPrivate::editRemoveLines(
int from,
int to)
1598 EDIT_DEBUG <<
"editRemoveLines" << from << to;
1600 if (to < from || from < 0 || to > lastLine()) {
1604 if (!isReadWrite()) {
1609 return editRemoveText(0, 0, lineLength(0));
1616 for (
int line = to; line >= from; --line) {
1619 m_undoManager->slotLineRemoved(line, l.
text(), l);
1625 for (
int line = to; line >= from; --line) {
1627 if (line + 1 < m_buffer->lines()) {
1628 m_buffer->unwrapLine(line + 1);
1630 m_buffer->unwrapLine(line);
1638 int line = mark->line;
1641 }
else if (line >= from) {
1646 for (
int line : rmark) {
1647 delete m_marks.take(line);
1650 for (
auto mark : list) {
1651 m_marks.take(mark->line);
1654 for (
auto mark : list) {
1655 mark->line -= to - from + 1;
1656 m_marks.insert(mark->line, mark);
1660 Q_EMIT marksChanged(
this);
1665 if (to == lastLine() + to - from + 1) {
1668 int prevLineLength = lineLength(from - 1);
1674 m_editLastChangeStartCursor = rangeRemoved.start();
1685uint KTextEditor::DocumentPrivate::undoCount()
const
1687 return m_undoManager->undoCount();
1690uint KTextEditor::DocumentPrivate::redoCount()
const
1692 return m_undoManager->redoCount();
1695void KTextEditor::DocumentPrivate::undo()
1697 for (
auto v : std::as_const(m_views)) {
1698 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
true);
1701 m_undoManager->undo();
1703 for (
auto v : std::as_const(m_views)) {
1704 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
false);
1708void KTextEditor::DocumentPrivate::redo()
1710 for (
auto v : std::as_const(m_views)) {
1711 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
true);
1714 m_undoManager->redo();
1716 for (
auto v : std::as_const(m_views)) {
1717 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
false);
1740 return searcher.search(pattern, range, backwards, patternOptions);
1743 if (escapeSequences) {
1763QWidget *KTextEditor::DocumentPrivate::dialogParent()
1780QUrl KTextEditor::DocumentPrivate::getSaveFileUrl(
const QString &dialogTitle)
1783 QUrl startUrl = url();
1793 const auto views = mainWindow->views();
1794 for (
auto view : views) {
1810 return updateFileType(name);
1829 for (KateFileType *type : modeList) {
1838 int mode = KateHlManager::self()->nameFind(name);
1842 m_buffer->setHighlight(mode);
1848 return highlight()->name();
1853 const auto modeList = KateHlManager::self()->modeList();
1856 for (
const auto &hl : modeList) {
1864 return KateHlManager::self()->modeList().at(index).section();
1872void KTextEditor::DocumentPrivate::bufferHlChanged()
1878 m_indenter->checkRequiredStyle();
1880 Q_EMIT highlightingModeChanged(
this);
1885 m_hlSetByUser =
true;
1890 m_bomSetByUser =
true;
1897 if (!flags.
contains(QStringLiteral(
"SkipEncoding"))) {
1900 if (!tmpenc.
isEmpty() && (tmpenc != encoding())) {
1901 setEncoding(tmpenc);
1905 if (!flags.
contains(QStringLiteral(
"SkipUrl"))) {
1920 if (!flags.
contains(QStringLiteral(
"SkipMode")) && kconfig.
hasKey(
"Mode Set By User")) {
1921 updateFileType(kconfig.
readEntry(
"Mode"),
true );
1924 if (!flags.
contains(QStringLiteral(
"SkipHighlighting"))) {
1926 if (kconfig.
hasKey(
"Highlighting Set By User")) {
1927 const int mode = KateHlManager::self()->nameFind(kconfig.
readEntry(
"Highlighting"));
1928 m_hlSetByUser =
true;
1931 m_buffer->setHighlight(mode);
1938 if (!userSetIndentMode.
isEmpty()) {
1939 config()->setIndentationMode(userSetIndentMode);
1944 for (
int i = 0; i < marks.
count(); i++) {
1954 if (this->url().isLocalFile()) {
1955 const QString path = this->url().toLocalFile();
1961 if (!flags.
contains(QStringLiteral(
"SkipUrl"))) {
1963 kconfig.
writeEntry(
"URL", this->url().toString());
1967 if (encoding() !=
QLatin1String(
"UTF-8") && !m_buffer->brokenEncoding() && !flags.
contains(QStringLiteral(
"SkipEncoding"))) {
1973 if (m_fileTypeSetByUser && !flags.
contains(QStringLiteral(
"SkipMode"))) {
1974 kconfig.
writeEntry(
"Mode Set By User",
true);
1978 if (m_hlSetByUser && !flags.
contains(QStringLiteral(
"SkipHighlighting"))) {
1980 kconfig.
writeEntry(
"Highlighting", highlight()->name());
1983 kconfig.
writeEntry(
"Highlighting Set By User", m_hlSetByUser);
1987 if (m_indenterSetByUser) {
1988 kconfig.
writeEntry(
"Indentation Mode", config()->indentationMode());
1993 for (
const auto &mark : std::as_const(m_marks)) {
2016void KTextEditor::DocumentPrivate::setMark(
int line, uint markType)
2019 addMark(line, markType);
2022void KTextEditor::DocumentPrivate::clearMark(
int line)
2024 if (line < 0 || line > lastLine()) {
2028 if (
auto mark = m_marks.take(line)) {
2029 Q_EMIT markChanged(
this, *mark, MarkRemoved);
2030 Q_EMIT marksChanged(
this);
2037void KTextEditor::DocumentPrivate::addMark(
int line, uint markType)
2041 if (line < 0 || line > lastLine()) {
2045 if (markType == 0) {
2049 if ((mark = m_marks.value(line))) {
2051 markType &= ~mark->type;
2053 if (markType == 0) {
2058 mark->
type |= markType;
2062 mark->
type = markType;
2063 m_marks.insert(line, mark);
2069 temp.
type = markType;
2070 Q_EMIT markChanged(
this, temp, MarkAdded);
2072 Q_EMIT marksChanged(
this);
2077void KTextEditor::DocumentPrivate::removeMark(
int line, uint markType)
2079 if (line < 0 || line > lastLine()) {
2083 auto it = m_marks.find(line);
2084 if (it == m_marks.end()) {
2090 markType &= mark->
type;
2092 if (markType == 0) {
2097 mark->
type &= ~markType;
2102 temp.
type = markType;
2103 Q_EMIT markChanged(
this, temp, MarkRemoved);
2105 if (mark->
type == 0) {
2110 Q_EMIT marksChanged(
this);
2120void KTextEditor::DocumentPrivate::requestMarkTooltip(
int line,
QPoint position)
2127 bool handled =
false;
2128 Q_EMIT markToolTipRequested(
this, *mark, position, handled);
2133 bool handled =
false;
2138 Q_EMIT markClicked(
this, *mark, handled);
2146 bool handled =
false;
2149 Q_EMIT markContextMenuRequested(
this,
KTextEditor::Mark{.line = line, .type = 0}, position, handled);
2151 Q_EMIT markContextMenuRequested(
this, *mark, position, handled);
2166 for (
const auto &m : marksCopy) {
2167 Q_EMIT markChanged(
this, *m, MarkRemoved);
2172 Q_EMIT marksChanged(
this);
2178 m_markDescriptions.insert(type, description);
2184 if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
2185 return KateRendererConfig::global()->lineMarkerColor(type);
2193 return m_markDescriptions.value(type,
QString());
2196void KTextEditor::DocumentPrivate::setEditableMarks(uint markMask)
2198 m_editableMarks = markMask;
2203 return m_editableMarks;
2209 m_markIcons.insert(markType, icon);
2214 return m_markIcons.value(markType,
QIcon());
2218bool KTextEditor::DocumentPrivate::print()
2220 return KatePrinter::print(
this);
2223void KTextEditor::DocumentPrivate::printPreview()
2225 KatePrinter::printPreview(
this);
2232 if (!m_modOnHd && url().isLocalFile()) {
2240 for (
int i = 0; (i < lines()) && (buf.
size() <= 4096); ++i) {
2241 buf.
append(line(i).toUtf8());
2246 if (!url().path().isEmpty()) {
2256void KTextEditor::DocumentPrivate::showAndSetOpeningErrorAccess()
2259 i18n(
"The file %1 could not be loaded, as it was not possible to read from it.<br />Check if you have read access to this file.",
2262 message->setWordWrap(
true);
2264 i18nc(
"translators: you can also translate 'Try Again' with 'Reload'",
"Try Again"),
2269 closeAction->
setToolTip(
i18nc(
"Close the message being displayed",
"Close message"));
2272 message->addAction(tryAgainAction);
2273 message->addAction(closeAction);
2276 postMessage(message);
2279 m_openingError =
true;
2283void KTextEditor::DocumentPrivate::openWithLineLengthLimitOverride()
2286 const int longestLine = m_buffer->longestLineLoaded();
2287 int newLimit = pow(2, ceil(log2(longestLine)));
2288 if (newLimit <= longestLine) {
2293 config()->setLineLengthLimit(newLimit);
2298 if (!m_openingError) {
2300 m_readWriteStateBeforeLoading =
true;
2306 return config()->lineLengthLimit();
2313 Q_EMIT aboutToInvalidateMovingInterfaceContent(
this);
2316 m_openingError =
false;
2322 QString currentEncoding = encoding();
2327 QString mimeType = arguments().mimeType();
2329 if (pos != -1 && !(m_reloading && m_userSetEncodingForNextReload)) {
2330 setEncoding(mimeType.
mid(pos + 1));
2341 if (m_reloading && m_userSetEncodingForNextReload && (currentEncoding != encoding())) {
2342 setEncoding(currentEncoding);
2345 bool success = m_buffer->openFile(localFilePath(), (m_reloading && m_userSetEncodingForNextReload));
2358 for (
auto view : std::as_const(m_views)) {
2361 static_cast<ViewPrivate *
>(view)->updateView(
true);
2365 Q_EMIT textChanged(
this);
2366 Q_EMIT loaded(
this);
2373 m_modOnHdReason = OnDiskUnmodified;
2374 m_prevModOnHdReason = OnDiskUnmodified;
2375 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2380 if (!isEmpty() && config()->autoDetectIndent() && !config()->isSet(KateDocumentConfig::IndentationWidth)
2381 && !config()->isSet(KateDocumentConfig::ReplaceTabsWithSpaces)) {
2383 auto result = detecter.detect(config()->indentationWidth(), config()->replaceTabsDyn());
2384 config()->setIndentationWidth(result.indentWidth);
2385 config()->setReplaceTabsDyn(result.indentUsingSpaces);
2392 showAndSetOpeningErrorAccess();
2396 if (m_buffer->brokenEncoding()) {
2398 setReadWrite(
false);
2399 m_readWriteStateBeforeLoading =
false;
2401 i18n(
"The file %1 was opened with %2 encoding but contained invalid characters.<br />"
2402 "It is set to read-only mode, as saving might destroy its content.<br />"
2403 "Either reopen the file with the correct encoding chosen or enable the read-write mode again in the tools menu to be able to edit it.",
2405 m_buffer->textCodec()),
2407 message->setWordWrap(
true);
2408 postMessage(message);
2411 m_openingError =
true;
2415 if (m_buffer->tooLongLinesWrapped()) {
2417 setReadWrite(
false);
2418 m_readWriteStateBeforeLoading =
false;
2420 new KTextEditor::Message(
i18n(
"The file %1 was opened and contained lines longer than the configured Line Length Limit (%2 characters).<br />"
2421 "The longest of those lines was %3 characters long<br/>"
2422 "Those lines were wrapped and the document is set to read-only mode, as saving will modify its content.",
2424 config()->lineLengthLimit(),
2425 m_buffer->longestLineLoaded()),
2427 QAction *increaseAndReload =
new QAction(
i18n(
"Temporarily raise limit and reload file"), message);
2428 connect(increaseAndReload, &
QAction::triggered,
this, &KTextEditor::DocumentPrivate::openWithLineLengthLimitOverride);
2429 message->addAction(increaseAndReload,
true);
2430 message->addAction(
new QAction(
i18n(
"Close"), message),
true);
2431 message->setWordWrap(
true);
2432 postMessage(message);
2435 m_openingError =
true;
2447 delete m_modOnHdHandler;
2450 if (!url().isEmpty()) {
2451 if (m_fileChangedDialogsActivated && m_modOnHd) {
2454 if (!isModified()) {
2457 str +
i18n(
"Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."),
2458 i18n(
"Trying to Save Unmodified File"),
2468 "Do you really want to save this file? Both your open file and the file on disk were changed. There could be some data lost."),
2469 i18n(
"Possible Data Loss"),
2481 if (!m_buffer->canEncode()
2483 i18n(
"The selected encoding cannot encode every Unicode character in this document. Do you really want to save "
2484 "it? There could be some data lost."),
2485 i18n(
"Possible Data Loss"),
2493 if (!createBackupFile()) {
2498 QString oldPath = m_dirWatchFile;
2501 if (oldPath != localFilePath()) {
2504 if (url().isLocalFile()) {
2511 const bool variablesWereRead = readVariables();
2517 if (!variablesWereRead) {
2518 for (
auto *view : std::as_const(m_views)) {
2519 auto v =
static_cast<ViewPrivate *
>(view);
2520 if (v->isVisible()) {
2521 const auto range = v->visibleRange();
2523 bool repaint =
false;
2525 if (isLineModified(i)) {
2532 v->updateView(
true);
2539 deactivateDirWatch();
2544 removeTrailingSpacesAndAddNewLineAtEof();
2549 if (!m_buffer->saveFile(localFilePath())) {
2551 activateDirWatch(oldPath);
2553 i18n(
"The document could not be saved, as it was not possible to write to %1.\nCheck that you have write access to this file or "
2554 "that enough disk space is available.\nThe original file may be lost or damaged. "
2555 "Don't quit the application until the file is successfully written.",
2571 m_modOnHdReason = OnDiskUnmodified;
2572 m_prevModOnHdReason = OnDiskUnmodified;
2573 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2578 m_undoManager->undoSafePoint();
2579 m_undoManager->updateLineModifications();
2587bool KTextEditor::DocumentPrivate::createBackupFile()
2590 const bool backupLocalFiles = config()->backupOnSaveLocal();
2591 const bool backupRemoteFiles = config()->backupOnSaveRemote();
2595 if (!backupLocalFiles && !backupRemoteFiles) {
2602 bool needBackup = backupLocalFiles && backupRemoteFiles;
2604 bool slowOrRemoteFile = !u.isLocalFile();
2605 if (!slowOrRemoteFile) {
2609 slowOrRemoteFile = (mountPoint && mountPoint->probablySlow());
2611 needBackup = (!slowOrRemoteFile && backupLocalFiles) || (slowOrRemoteFile && backupRemoteFiles);
2622 if (backupPrefix.isEmpty() && backupSuffix.isEmpty()) {
2629 u.setPath(backupPrefix + u.fileName() + backupSuffix);
2632 const QString fileName = u.fileName();
2634 u.setPath(u.path() + backupPrefix + fileName + backupSuffix);
2637 qCDebug(LOG_KTE) <<
"backup src file name: " << url();
2638 qCDebug(LOG_KTE) <<
"backup dst file name: " << u;
2641 bool backupSuccess =
false;
2644 if (u.isLocalFile()) {
2647 QFile backupFile(u.toLocalFile());
2648 if (backupFile.exists()) {
2649 backupFile.remove();
2652 backupSuccess =
QFile::copy(url().toLocalFile(), u.toLocalFile());
2654 backupSuccess =
true;
2660 if (statJob->
exec()) {
2665 backupSuccess = job->
exec();
2667 backupSuccess =
true;
2674 i18n(
"For file %1 no backup copy could be created before saving."
2675 " If an error occurs while saving, you might lose the data of this file."
2676 " A reason could be that the media you write to is full or the directory of the file is read-only for you.",
2678 i18n(
"Failed to create backup copy."),
2681 QStringLiteral(
"Backup Failed Warning"))
2689void KTextEditor::DocumentPrivate::readDirConfig(KTextEditor::ViewPrivate *v)
2699 while (!seenDirectories.
contains(
dir.absolutePath())) {
2701 seenDirectories.
insert(
dir.absolutePath());
2709 QString line = stream.readLine();
2710 while ((linesRead < 32) && !line.
isNull()) {
2711 readVariableLine(line, v);
2713 line = stream.readLine();
2727#if EDITORCONFIG_FOUND
2729 if (!v && config()->value(KateDocumentConfig::UseEditorConfig).toBool()) {
2733 EditorConfig editorConfig(
this);
2734 editorConfig.parse();
2739void KTextEditor::DocumentPrivate::activateDirWatch(
const QString &useFileName)
2741 QString fileToUse = useFileName;
2743 fileToUse = localFilePath();
2757 if (fileToUse == m_dirWatchFile) {
2762 deactivateDirWatch();
2765 if (url().isLocalFile() && !fileToUse.
isEmpty()) {
2767 m_dirWatchFile = fileToUse;
2771void KTextEditor::DocumentPrivate::deactivateDirWatch()
2773 if (!m_dirWatchFile.isEmpty()) {
2777 m_dirWatchFile.clear();
2780bool KTextEditor::DocumentPrivate::openUrl(
const QUrl &url)
2784 m_fileTypeSetByUser =
false;
2791bool KTextEditor::DocumentPrivate::closeUrl()
2796 if (!m_reloading && !url().isEmpty()) {
2797 if (m_fileChangedDialogsActivated && m_modOnHd) {
2799 delete m_modOnHdHandler;
2801 QWidget *parentWidget(dialogParent());
2804 +
i18n(
"Do you really want to continue to close this file? Data loss may occur."),
2805 i18n(
"Possible Data Loss"),
2808 QStringLiteral(
"kate_close_modonhd_%1").arg(m_modOnHdReason))
2811 m_reloading =
false;
2822 m_reloading =
false;
2828 Q_EMIT aboutToClose(
this);
2832 if (!m_messageHash.isEmpty()) {
2833 const auto keys = m_messageHash.keys();
2840 Q_EMIT aboutToInvalidateMovingInterfaceContent(
this);
2843 deactivateDirWatch();
2851 m_modOnHdReason = OnDiskUnmodified;
2852 m_prevModOnHdReason = OnDiskUnmodified;
2853 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2863 m_undoManager->clearUndo();
2864 m_undoManager->clearRedo();
2870 m_buffer->setHighlight(0);
2873 for (
auto view : std::as_const(m_views)) {
2874 static_cast<ViewPrivate *
>(view)->clearSelection();
2875 static_cast<ViewPrivate *
>(view)->
clear();
2880 m_swapfile->fileClosed();
2889 return m_swapfile && m_swapfile->shouldRecover();
2894 if (isDataRecoveryAvailable()) {
2895 m_swapfile->recover();
2901 if (isDataRecoveryAvailable()) {
2902 m_swapfile->discard();
2906void KTextEditor::DocumentPrivate::setReadWrite(
bool rw)
2908 if (isReadWrite() == rw) {
2914 for (
auto v : std::as_const(m_views)) {
2915 auto view =
static_cast<ViewPrivate *
>(v);
2916 view->slotUpdateUndo();
2917 view->slotReadWriteChanged();
2920 Q_EMIT readWriteChanged(
this);
2925 if (isModified() != m) {
2928 for (
auto view : std::as_const(m_views)) {
2929 static_cast<ViewPrivate *
>(view)->slotUpdateUndo();
2932 Q_EMIT modifiedChanged(
this);
2935 m_undoManager->setModified(m);
2941void KTextEditor::DocumentPrivate::makeAttribs(
bool needInvalidate)
2943 for (
auto view : std::as_const(m_views)) {
2944 static_cast<ViewPrivate *
>(view)->renderer()->updateAttributes();
2947 if (needInvalidate) {
2948 m_buffer->invalidateHighlighting();
2951 for (
auto v : std::as_const(m_views)) {
2952 auto view =
static_cast<ViewPrivate *
>(v);
2954 view->updateView(
true);
2959void KTextEditor::DocumentPrivate::internalHlChanged()
2966 Q_ASSERT(!m_views.contains(view));
2967 m_views.append(view);
2968 auto *v =
static_cast<KTextEditor::ViewPrivate *
>(view);
2971 if (!m_fileType.isEmpty()) {
2980 setActiveView(view);
2985 Q_ASSERT(m_views.contains(view));
2986 m_views.removeAll(view);
2988 if (activeView() == view) {
2989 setActiveView(
nullptr);
2995 if (m_activeView == view) {
2999 m_activeView =
static_cast<KTextEditor::ViewPrivate *
>(view);
3002bool KTextEditor::DocumentPrivate::ownedView(KTextEditor::ViewPrivate *view)
3005 return (m_views.contains(view));
3008int KTextEditor::DocumentPrivate::toVirtualColumn(
int line,
int column)
const
3016 return toVirtualColumn(cursor.
line(), cursor.
column());
3019int KTextEditor::DocumentPrivate::fromVirtualColumn(
int line,
int column)
const
3025int KTextEditor::DocumentPrivate::fromVirtualColumn(
const KTextEditor::Cursor cursor)
const
3027 return fromVirtualColumn(cursor.
line(), cursor.
column());
3036 bool skipAutobrace = closingBracket ==
QLatin1Char(
'\'');
3037 if (highlight() && skipAutobrace) {
3039 skipAutobrace = highlight()->spellCheckingRequiredForLocation(
this, pos - Cursor{0, 1});
3042 if (!skipAutobrace && (closingBracket ==
QLatin1Char(
'\''))) {
3048 skipAutobrace = (count % 2 == 0) ?
true : false;
3050 if (!skipAutobrace && (closingBracket ==
QLatin1Char(
'\"'))) {
3055 skipAutobrace = (count % 2 == 0) ?
true : false;
3057 return skipAutobrace;
3068 QChar closingBracket;
3069 if (view->config()->autoBrackets()) {
3071 const QChar typedChar = chars.
at(0);
3072 const QChar openBracket = matchingStartBracket(typedChar);
3073 if (!openBracket.
isNull()) {
3075 if ((characterAt(curPos) == typedChar) && findMatchingBracket(curPos, 123 ).isValid()) {
3077 view->cursorRight();
3083 if (chars.
size() == 1) {
3085 closingBracket = matchingEndBracket(typedChar);
3088 if (m_currentAutobraceClosingChar == typedChar && m_currentAutobraceRange) {
3090 m_currentAutobraceRange.reset(
nullptr);
3091 view->cursorRight();
3098 if (view->selection() && closingBracket.
isNull() && view->config()->encloseSelectionInChars()) {
3099 const QChar typedChar = chars.
at(0);
3100 if (view->config()->charsToEncloseSelection().
contains(typedChar)) {
3109 if (view->selection() && !closingBracket.
isNull()) {
3110 std::unique_ptr<KTextEditor::MovingRange> selectionRange(newMovingRange(view->selectionRange()));
3111 const int startLine = qMax(0, selectionRange->start().line());
3112 const int endLine = qMin(selectionRange->end().line(), lastLine());
3113 const bool blockMode = view->blockSelection() && (startLine != endLine);
3115 if (selectionRange->start().column() > selectionRange->end().column()) {
3121 const int startColumn = qMin(selectionRange->start().column(), selectionRange->end().column());
3122 const int endColumn = qMax(selectionRange->start().column(), selectionRange->end().column());
3124 for (
int line = startLine; line <= endLine; ++line) {
3126 insertText(r.
end(),
QString(closingBracket));
3127 view->slotTextInserted(view, r.
end(),
QString(closingBracket));
3128 insertText(r.
start(), chars);
3129 view->slotTextInserted(view, r.
start(), chars);
3133 for (
const auto &cursor : view->secondaryCursors()) {
3134 if (!cursor.range) {
3137 const auto &currSelectionRange = cursor.range;
3138 auto expandBehaviour = currSelectionRange->insertBehaviors();
3140 insertText(currSelectionRange->end(),
QString(closingBracket));
3141 insertText(currSelectionRange->start(), chars);
3142 currSelectionRange->setInsertBehaviors(expandBehaviour);
3143 cursor.pos->setPosition(currSelectionRange->end());
3144 auto mutableCursor =
const_cast<KTextEditor::ViewPrivate::SecondaryCursor *
>(&cursor);
3145 mutableCursor->anchor = currSelectionRange->start().toCursor();
3149 insertText(selectionRange->end(),
QString(closingBracket));
3150 view->slotTextInserted(view, selectionRange->end(),
QString(closingBracket));
3151 insertText(selectionRange->start(), chars);
3152 view->slotTextInserted(view, selectionRange->start(), chars);
3156 view->setSelection(selectionRange->toRange());
3157 view->setCursorPosition(selectionRange->end());
3164 if (!view->config()->persistentSelection() && view->selection()) {
3165 view->removeSelectedText();
3170 const bool multiLineBlockMode = view->blockSelection() && view->selection();
3171 if (view->currentInputMode()->overwrite()) {
3174 const int startLine = multiLineBlockMode ? qMax(0, selectionRange.
start().
line()) : view->cursorPosition().
line();
3175 const int endLine = multiLineBlockMode ? qMin(selectionRange.
end().
line(), lastLine()) : startLine;
3176 const int virtualColumn = toVirtualColumn(multiLineBlockMode ? selectionRange.
end() : view->cursorPosition());
3178 for (
int line = endLine; line >= startLine; --line) {
3180 const int column = fromVirtualColumn(line, virtualColumn);
3184 if (oldCur.
column() < lineLength(line)) {
3186 view->currentInputMode()->overwrittenChar(removed);
3193 chars = eventuallyReplaceTabs(view->cursorPosition(), chars);
3195 if (multiLineBlockMode) {
3197 const int startLine = qMax(0, selectionRange.
start().
line());
3198 const int endLine = qMin(selectionRange.
end().
line(), lastLine());
3199 const int column = toVirtualColumn(selectionRange.
end());
3200 for (
int line = endLine; line >= startLine; --line) {
3201 editInsertText(line, fromVirtualColumn(line, column), chars);
3203 int newSelectionColumn = toVirtualColumn(view->cursorPosition());
3206 view->setSelection(selectionRange);
3211 view->completionWidget()->setIgnoreBufferSignals(
true);
3212 const auto &sc = view->secondaryCursors();
3214 const bool hasClosingBracket = !closingBracket.
isNull();
3215 const QString closingChar = closingBracket;
3217 std::vector<std::pair<Kate::TextCursor *, KTextEditor::Cursor>> freezedCursors;
3218 for (
auto it = sc.begin(); it != sc.end(); ++it) {
3219 auto pos = it->cursor();
3220 if (it != sc.begin() && pos == std::prev(it)->cursor()) {
3221 freezedCursors.push_back({std::prev(it)->pos.get(), std::prev(it)->cursor()});
3224 lastInsertionCursor = pos;
3225 insertText(pos, chars);
3228 if (it->cursor() == view->cursorPosition()) {
3229 freezedCursors.push_back({it->pos.get(), it->cursor()});
3232 const auto nextChar = view->document()->
text({pos, pos +
Cursor{0, 1}}).trimmed();
3233 if (hasClosingBracket && !skipAutoBrace(closingBracket, pos) && (nextChar.isEmpty() || !nextChar.at(0).isLetterOrNumber())) {
3234 insertText(it->cursor(), closingChar);
3235 it->pos->setPosition(pos);
3239 view->completionWidget()->setIgnoreBufferSignals(
false);
3241 insertText(view->cursorPosition(), chars);
3243 for (
auto &freezed : freezedCursors) {
3244 freezed.first->setPosition(freezed.second);
3252 if (!closingBracket.
isNull() && !skipAutoBrace(closingBracket, view->cursorPosition())) {
3254 const auto cursorPos = view->cursorPosition();
3255 const auto nextChar = view->document()->
text({cursorPos, cursorPos +
Cursor{0, 1}}).trimmed();
3256 if (nextChar.isEmpty() || !nextChar.at(0).isLetterOrNumber()) {
3257 insertText(view->cursorPosition(),
QString(closingBracket));
3258 const auto insertedAt(view->cursorPosition());
3259 view->setCursorPosition(cursorPos);
3264 chars.
append(closingBracket);
3266 m_currentAutobraceClosingChar = closingBracket;
3273 const auto &secondaryCursors = view->secondaryCursors();
3274 for (
const auto &c : secondaryCursors) {
3275 m_indenter->userTypedChar(view, c.cursor(), chars.
isEmpty() ?
QChar() : chars.
at(chars.
length() - 1));
3283 view->slotTextInserted(view, oldCur, chars);
3288 if (m_currentAutobraceRange && !m_currentAutobraceRange->toRange().contains(newPos)) {
3289 m_currentAutobraceRange.reset();
3293void KTextEditor::DocumentPrivate::newLine(KTextEditor::ViewPrivate *v, KTextEditor::DocumentPrivate::NewLineIndent indent, NewLinePos newLinePos)
3297 if (!v->config()->persistentSelection() && v->selection()) {
3298 v->removeSelectedText();
3299 v->clearSelection();
3303 if (c.line() > lastLine()) {
3304 c.setLine(lastLine());
3313 int len = lineLength(ln);
3315 if (c.column() > len) {
3320 editWrapLine(c.line(), c.column());
3323 m_buffer->updateHighlighting();
3330 bool moveCursorToTop =
false;
3331 if (newLinePos == Above) {
3332 if (pos.
line() <= 0) {
3335 moveCursorToTop =
true;
3340 }
else if (newLinePos == Below) {
3341 int lastCol = lineLength(pos.
line());
3344 return std::pair{pos, moveCursorToTop};
3348 const auto &secondaryCursors = v->secondaryCursors();
3349 if (!secondaryCursors.empty()) {
3352 for (
const auto &c : secondaryCursors) {
3353 const auto [newPos, moveCursorToTop] = adjustCusorPos(c.cursor());
3354 c.pos->setPosition(newPos);
3355 insertNewLine(c.cursor());
3356 if (moveCursorToTop) {
3357 c.pos->setPosition({0, 0});
3360 if (indent == KTextEditor::DocumentPrivate::Indent) {
3364 v->setCursorPosition(c.cursor());
3365 m_indenter->userTypedChar(v, c.cursor(),
QLatin1Char(
'\n'));
3367 c.pos->setPosition(v->cursorPosition());
3371 v->setCursorPosition(savedPrimary.toCursor());
3374 const auto [newPos, moveCursorToTop] = adjustCusorPos(v->cursorPosition());
3375 v->setCursorPosition(newPos);
3376 insertNewLine(v->cursorPosition());
3377 if (moveCursorToTop) {
3378 v->setCursorPosition({0, 0});
3381 if (indent == KTextEditor::DocumentPrivate::Indent) {
3382 m_indenter->userTypedChar(v, v->cursorPosition(),
QLatin1Char(
'\n'));
3391 if (textLine.
length() < 2) {
3395 uint col = cursor.
column();
3401 if ((textLine.
length() - col) < 2) {
3405 uint line = cursor.
line();
3416 editRemoveText(line, col, 2);
3417 editInsertText(line, col, s);
3424 Q_ASSERT(!firstWord.
overlaps(secondWord));
3432 const QString tempString = text(secondWord);
3435 replaceText(secondWord, text(firstWord));
3436 replaceText(firstWord, tempString);
3442 int col = qMax(c.
column(), 0);
3443 int line = qMax(c.
line(), 0);
3444 if ((col == 0) && (line == 0)) {
3447 if (line >= lines()) {
3454 bool useNextBlock =
false;
3455 if (config()->backspaceIndents()) {
3462 if (pos < 0 || pos >= (
int)colX) {
3464 if ((
int)col > textLine.
length()) {
3470 useNextBlock =
true;
3473 if (!config()->backspaceIndents() || useNextBlock) {
3476 if (!view->config()->backspaceRemoveComposed()) {
3477 beginCursor.setColumn(col - 1);
3479 if (!isValidTextPosition(beginCursor)) {
3481 beginCursor.setColumn(col - 2);
3484 if (
auto l = view->textLayout(c)) {
3485 beginCursor.setColumn(l->previousCursorPosition(c.
column()));
3500 if (config()->wordWrap() && textLine.
endsWith(QStringLiteral(
" "))) {
3513void KTextEditor::DocumentPrivate::backspace(KTextEditor::ViewPrivate *view)
3515 if (!view->config()->persistentSelection() && view->hasSelections()) {
3519 if (view->blockSelection() && view->selection() && range.
start().
column() > 0 && toVirtualColumn(range.
start()) == toVirtualColumn(range.
end())) {
3522 view->setSelection(range);
3524 view->removeSelectedText();
3525 view->ensureUniqueCursors();
3533 const auto &multiCursors = view->secondaryCursors();
3534 view->completionWidget()->setIgnoreBufferSignals(
true);
3535 for (
const auto &c : multiCursors) {
3536 const auto newPos = backspaceAtCursor(view, c.cursor());
3541 view->completionWidget()->setIgnoreBufferSignals(
false);
3544 auto newPos = backspaceAtCursor(view, view->cursorPosition());
3546 view->setCursorPosition(newPos);
3549 view->ensureUniqueCursors();
3554 if (m_currentAutobraceRange) {
3555 const auto r = m_currentAutobraceRange->toRange();
3556 if (r.columnWidth() == 1 && view->cursorPosition() == r.
start()) {
3558 del(view, view->cursorPosition());
3559 m_currentAutobraceRange.reset();
3564void KTextEditor::DocumentPrivate::del(KTextEditor::ViewPrivate *view,
const KTextEditor::Cursor c)
3566 if (!view->config()->persistentSelection() && view->selection()) {
3569 if (view->blockSelection() && toVirtualColumn(range.
start()) == toVirtualColumn(range.
end())) {
3572 view->setSelection(range);
3574 view->removeSelectedText();
3579 if (c.
column() < m_buffer->lineLength(c.
line())) {
3582 }
else if (c.
line() < lastLine()) {
3587bool KTextEditor::DocumentPrivate::multiPaste(KTextEditor::ViewPrivate *view,
const QStringList &texts)
3589 if (texts.
isEmpty() || view->isMulticursorNotAllowed() || view->secondaryCursors().size() + 1 != (
size_t)texts.
size()) {
3593 m_undoManager->undoSafePoint();
3596 if (view->selection()) {
3597 view->removeSelectedText();
3600 auto plainSecondaryCursors = view->plainSecondaryCursors();
3601 KTextEditor::ViewPrivate::PlainSecondaryCursor primary;
3602 primary.pos = view->cursorPosition();
3603 primary.range = view->selectionRange();
3604 plainSecondaryCursors.append(primary);
3605 std::sort(plainSecondaryCursors.begin(), plainSecondaryCursors.end());
3609 for (
int i = texts.
size() - 1; i >= 0; --i) {
3611 text.
replace(re, QStringLiteral(
"\n"));
3614 insertText(pos, text,
false);
3622void KTextEditor::DocumentPrivate::paste(KTextEditor::ViewPrivate *view,
const QString &text)
3635 const bool isSingleLine = lines == 0;
3637 m_undoManager->undoSafePoint();
3643 bool skipIndentOnPaste =
false;
3645 const int length = lineLength(pos.
line());
3647 skipIndentOnPaste = length > 0;
3650 if (!view->config()->persistentSelection() && view->selection()) {
3651 pos = view->selectionRange().
start();
3652 if (view->blockSelection()) {
3653 pos = rangeOnLine(view->selectionRange(), pos.
line()).
start();
3660 view->removeSelectedText();
3663 if (config()->ovr()) {
3666 if (!view->blockSelection()) {
3667 int endColumn = (pasteLines.count() == 1 ? pos.
column() : 0) + pasteLines.last().length();
3670 int maxi = qMin(pos.
line() + pasteLines.count(), this->lines());
3672 for (
int i = pos.
line(); i < maxi; ++i) {
3673 int pasteLength = pasteLines.at(i - pos.
line()).length();
3679 insertText(pos, s, view->blockSelection());
3686 if (view->blockSelection()) {
3687 view->setCursorPositionInternal(pos);
3690 if (config()->indentPastedText()) {
3692 if (!skipIndentOnPaste) {
3693 m_indenter->indent(view, range);
3697 if (!view->blockSelection()) {
3698 Q_EMIT charactersSemiInteractivelyInserted(pos, s);
3700 m_undoManager->undoSafePoint();
3705 if (!isReadWrite()) {
3710 m_indenter->changeIndent(range, change);
3714void KTextEditor::DocumentPrivate::align(KTextEditor::ViewPrivate *view,
KTextEditor::Range range)
3716 m_indenter->indent(view, range);
3723 if (lines.
size() < 2) {
3729 int selectionStartColumn = range.
start().
column();
3731 for (
const auto &line : lines) {
3733 if (!
match.hasMatch()) {
3734 patternStartColumns.
append(-1);
3735 }
else if (
match.lastCapturedIndex() == 0) {
3736 patternStartColumns.
append(
match.capturedStart(0) + (blockwise ? selectionStartColumn : 0));
3738 patternStartColumns.
append(
match.capturedStart(1) + (blockwise ? selectionStartColumn : 0));
3741 if (!blockwise && patternStartColumns[0] != -1) {
3742 patternStartColumns[0] += selectionStartColumn;
3745 int maxColumn = *std::max_element(patternStartColumns.
cbegin(), patternStartColumns.
cend());
3748 for (
int i = 0; i < lines.size(); ++i) {
3749 if (patternStartColumns[i] != -1) {
3756void KTextEditor::DocumentPrivate::insertTab(KTextEditor::ViewPrivate *view,
const KTextEditor::Cursor)
3758 if (!isReadWrite()) {
3762 int lineLen = line(view->cursorPosition().
line()).length();
3767 if (!view->config()->persistentSelection() && view->selection()) {
3768 view->removeSelectedText();
3769 }
else if (view->currentInputMode()->overwrite() && c.
column() < lineLen) {
3774 view->currentInputMode()->overwrittenChar(removed);
3778 c = view->cursorPosition();
3779 editInsertText(c.
line(), c.
column(), QStringLiteral(
"\t"));
3788bool KTextEditor::DocumentPrivate::removeStringFromBeginning(
int line,
const QString &str)
3812bool KTextEditor::DocumentPrivate::removeStringFromEnd(
int line,
const QString &str)
3817 bool there = textline.
endsWith(str);
3839 const bool replacetabs = config()->replaceTabsDyn();
3843 const int indentWidth = config()->indentationWidth();
3846 int column = cursorPos.
column();
3852 for (
const QChar ch : str) {
3853 if (ch == tabChar) {
3856 int spacesToInsert = indentWidth - (column % indentWidth);
3858 column += spacesToInsert;
3871void KTextEditor::DocumentPrivate::addStartLineCommentToSingleLine(
int line,
int attrib)
3873 const QString commentLineMark = highlight()->getCommentSingleLineStart(attrib) +
QLatin1Char(
' ');
3876 if (highlight()->getCommentSingleLinePosition(attrib) == KSyntaxHighlighting::CommentPosition::AfterWhitespace) {
3887bool KTextEditor::DocumentPrivate::removeStartLineCommentFromSingleLine(
int line,
int attrib)
3889 const QString shortCommentMark = highlight()->getCommentSingleLineStart(attrib);
3895 bool removed = (removeStringFromBeginning(line, longCommentMark) || removeStringFromBeginning(line, shortCommentMark));
3906void KTextEditor::DocumentPrivate::addStartStopCommentToSingleLine(
int line,
int attrib)
3908 const QString startCommentMark = highlight()->getCommentStart(attrib) +
QLatin1Char(
' ');
3909 const QString stopCommentMark =
QLatin1Char(
' ') + highlight()->getCommentEnd(attrib);
3917 const int col = m_buffer->lineLength(line);
3929bool KTextEditor::DocumentPrivate::removeStartStopCommentFromSingleLine(
int line,
int attrib)
3931 const QString shortStartCommentMark = highlight()->getCommentStart(attrib);
3933 const QString shortStopCommentMark = highlight()->getCommentEnd(attrib);
3939 const bool removedStart = (removeStringFromBeginning(line, longStartCommentMark) || removeStringFromBeginning(line, shortStartCommentMark));
3942 const bool removedStop = removedStart && (removeStringFromEnd(line, longStopCommentMark) || removeStringFromEnd(line, shortStopCommentMark));
3946 return (removedStart || removedStop);
3953void KTextEditor::DocumentPrivate::addStartStopCommentToSelection(
KTextEditor::Range selection,
bool blockSelection,
int attrib)
3955 const QString startComment = highlight()->getCommentStart(attrib);
3956 const QString endComment = highlight()->getCommentEnd(attrib);
3966 if (!blockSelection) {
3967 insertText(range.
end(), endComment);
3968 insertText(range.
start(), startComment);
3970 for (
int line = range.
start().
line(); line <= range.
end().
line(); line++) {
3972 insertText(subRange.
end(), endComment);
3973 insertText(subRange.
start(), startComment);
3984void KTextEditor::DocumentPrivate::addStartLineCommentToSelection(
KTextEditor::Range selection,
int attrib)
3987 int el = selection.
end().
line();
3990 if ((selection.
end().
column() == 0) && (el > 0)) {
3994 if (sl < 0 || el < 0 || sl >= lines() || el >= lines()) {
4000 const QString commentLineMark = highlight()->getCommentSingleLineStart(attrib) +
QLatin1Char(
' ');
4003 if (highlight()->getCommentSingleLinePosition(attrib) == KSyntaxHighlighting::CommentPosition::AfterWhitespace) {
4011 col = std::numeric_limits<int>::max();
4013 for (
int l = el; l >= sl; l--) {
4014 const auto line = plainKateTextLine(l);
4015 if (line.length() == 0) {
4018 col = qMin(col, qMax(0, line.firstChar()));
4025 if (col == std::numeric_limits<int>::max()) {
4032 for (
int l = el; l >= sl; l--) {
4039bool KTextEditor::DocumentPrivate::nextNonSpaceCharPos(
int &line,
int &col)
4041 for (; line >= 0 && line < m_buffer->lines(); line++) {
4055bool KTextEditor::DocumentPrivate::previousNonSpaceCharPos(
int &line,
int &col)
4057 while (line >= 0 && line < m_buffer->lines()) {
4079bool KTextEditor::DocumentPrivate::removeStartStopCommentFromSelection(
KTextEditor::Range selection,
int attrib)
4081 const QString startComment = highlight()->getCommentStart(attrib);
4082 const QString endComment = highlight()->getCommentEnd(attrib);
4084 int sl = qMax<int>(0, selection.
start().
line());
4085 int el = qMin<int>(selection.
end().
line(), lastLine());
4092 }
else if (el > 0) {
4094 ec = m_buffer->lineLength(el) - 1;
4097 const int startCommentLen = startComment.
length();
4098 const int endCommentLen = endComment.
length();
4102 bool remove = nextNonSpaceCharPos(sl, sc) && m_buffer->plainLine(sl).matchesAt(sc, startComment) && previousNonSpaceCharPos(el, ec)
4103 && ((ec - endCommentLen + 1) >= 0) && m_buffer->plainLine(el).matchesAt(ec - endCommentLen + 1, endComment);
4120 const QString startComment = highlight()->getCommentStart(attrib);
4121 const QString endComment = highlight()->getCommentEnd(attrib);
4122 const int startCommentLen = startComment.
length();
4123 const int endCommentLen = endComment.
length();
4125 const bool remove = m_buffer->plainLine(
start.line()).matchesAt(
start.column(), startComment)
4126 && m_buffer->plainLine(
end.line()).matchesAt(
end.column() - endCommentLen, endComment);
4140bool KTextEditor::DocumentPrivate::removeStartLineCommentFromSelection(
KTextEditor::Range selection,
int attrib,
bool toggleComment)
4142 const QString shortCommentMark = highlight()->getCommentSingleLineStart(attrib);
4145 const int startLine = selection.
start().
line();
4146 int endLine = selection.
end().
line();
4148 if ((selection.
end().
column() == 0) && (endLine > 0)) {
4152 bool removed =
false;
4158 if (toggleComment) {
4159 bool allLinesAreCommented =
true;
4160 for (
int line = endLine; line >= startLine; line--) {
4161 const auto ln = m_buffer->plainLine(line);
4162 const QString &text = ln.text();
4169 textView = textView.trimmed();
4170 if (!textView.startsWith(shortCommentMark) && !textView.startsWith(longCommentMark)) {
4171 allLinesAreCommented =
false;
4175 if (!allLinesAreCommented) {
4183 for (
int z = endLine; z >= startLine; z--) {
4185 removed = (removeStringFromBeginning(z, longCommentMark) || removeStringFromBeginning(z, shortCommentMark) || removed);
4196 const bool hasSelection = !selection.
isEmpty();
4197 int selectionCol = 0;
4202 const int line = c.
line();
4204 int startAttrib = 0;
4207 if (selectionCol < ln.
length()) {
4208 startAttrib = ln.
attribute(selectionCol);
4213 bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart(startAttrib).isEmpty());
4214 bool hasStartStopCommentMark = (!(highlight()->getCommentStart(startAttrib).isEmpty()) && !(highlight()->getCommentEnd(startAttrib).isEmpty()));
4216 if (changeType == Comment) {
4217 if (!hasSelection) {
4218 if (hasStartLineCommentMark) {
4219 addStartLineCommentToSingleLine(line, startAttrib);
4220 }
else if (hasStartStopCommentMark) {
4221 addStartStopCommentToSingleLine(line, startAttrib);
4232 if (hasStartStopCommentMark
4233 && (!hasStartLineCommentMark
4236 addStartStopCommentToSelection(selection, blockSelect, startAttrib);
4237 }
else if (hasStartLineCommentMark) {
4238 addStartLineCommentToSelection(selection, startAttrib);
4242 bool removed =
false;
4243 const bool toggleComment = changeType == ToggleComment;
4244 if (!hasSelection) {
4245 removed = (hasStartLineCommentMark && removeStartLineCommentFromSingleLine(line, startAttrib))
4246 || (hasStartStopCommentMark && removeStartStopCommentFromSingleLine(line, startAttrib));
4249 removed = (hasStartStopCommentMark && removeStartStopCommentFromSelection(selection, startAttrib))
4250 || (hasStartLineCommentMark && removeStartLineCommentFromSelection(selection, startAttrib, toggleComment));
4254 if (!removed && toggleComment) {
4255 commentSelection(selection, c, blockSelect, Comment);
4264void KTextEditor::DocumentPrivate::comment(KTextEditor::ViewPrivate *v, uint line, uint column, CommentType change)
4267 const bool skipWordWrap = wordWrap();
4274 if (v->selection()) {
4275 const auto &cursors = v->secondaryCursors();
4276 for (
const auto &c : cursors) {
4280 commentSelection(c.range->toRange(), c.cursor(),
false, change);
4283 commentSelection(v->selectionRange(), c, v->blockSelection(), change);
4285 const auto &cursors = v->secondaryCursors();
4286 for (
const auto &c : cursors) {
4287 commentSelection({}, c.cursor(),
false, change);
4299void KTextEditor::DocumentPrivate::transformCursorOrRange(KTextEditor::ViewPrivate *v,
4302 KTextEditor::DocumentPrivate::TextTransform t)
4304 if (v->selection()) {
4316 if (range.
start().
line() == selection.
end().
line() || v->blockSelection()) {
4321 int swapCol =
start;
4331 if (t == Uppercase) {
4334 }
else if (t == Lowercase) {
4347 && !highlight()->isInWord(l.
at(range.
start().
column() - 1)))
4348 || (p && !highlight()->isInWord(s.
at(p - 1)))) {
4357 insertText(range.
start(), s);
4392 insertText(cursor, s);
4402 if (v->selection()) {
4403 const auto &cursors = v->secondaryCursors();
4404 for (
const auto &c : cursors) {
4408 auto pos = c.pos->toCursor();
4409 transformCursorOrRange(v, c.anchor, c.range->toRange(), t);
4413 const auto selRange = v->selectionRange();
4414 transformCursorOrRange(v, c, v->selectionRange(), t);
4415 v->setSelection(selRange);
4416 v->setCursorPosition(c);
4418 const auto &secondaryCursors = v->secondaryCursors();
4419 for (
const auto &c : secondaryCursors) {
4420 transformCursorOrRange(v, c.cursor(), {}, t);
4422 transformCursorOrRange(v, c, {}, t);
4433 while (first < last) {
4434 if (line >= lines() || line + 1 >= lines()) {
4450 editRemoveText(line + 1, 0, pos);
4453 editInsertText(line + 1, 0, QStringLiteral(
" "));
4457 editRemoveText(line + 1, 0, tl.
length());
4460 editUnWrapLine(line);
4468 for (
auto view : std::as_const(m_views)) {
4469 static_cast<ViewPrivate *
>(view)->tagLines(lineRange,
true);
4473void KTextEditor::DocumentPrivate::tagLine(
int line)
4475 tagLines({line, line});
4478void KTextEditor::DocumentPrivate::repaintViews(
bool paintOnlyDirty)
4480 for (
auto view : std::as_const(m_views)) {
4481 static_cast<ViewPrivate *
>(view)->repaintText(paintOnlyDirty);
4494 if (maxLines < 0 ||
start.line() < 0 ||
start.line() >= lines()) {
4504 if (config()->ovr()) {
4505 if (isBracket(right)) {
4510 }
else if (isBracket(right)) {
4512 }
else if (isBracket(left)) {
4519 const QChar opposite = matchingBracket(bracket);
4524 const int searchDir = isStartBracket(bracket) ? 1 : -1;
4527 const int minLine = qMax(range.
start().
line() - maxLines, 0);
4528 const int maxLine = qMin(range.
start().
line() + maxLines, documentEnd().line());
4533 int validAttr = kateTextLine(cursor.
line()).attribute(cursor.
column());
4535 while (cursor.
line() >= minLine && cursor.
line() <= maxLine) {
4536 if (!cursor.move(searchDir)) {
4544 if (c == opposite) {
4546 if (searchDir > 0) {
4547 range.
setEnd(cursor.toCursor());
4554 }
else if (c == bracket) {
4570void KTextEditor::DocumentPrivate::updateDocName()
4573 if (!url().isEmpty() && (m_docName == removeNewLines(url().fileName()) || m_docName.
startsWith(removeNewLines(url().fileName()) +
QLatin1String(
" (")))) {
4579 std::vector<KTextEditor::DocumentPrivate *> docsWithSameName;
4584 if ((doc !=
this) && (doc->url().fileName() == url().fileName())) {
4585 if (doc->m_docNameNumber > count) {
4586 count = doc->m_docNameNumber;
4588 docsWithSameName.push_back(doc);
4592 m_docNameNumber = count + 1;
4595 m_docName = removeNewLines(url().fileName());
4597 m_isUntitled = m_docName.isEmpty();
4599 if (!m_isUntitled && !docsWithSameName.empty()) {
4600 docsWithSameName.push_back(
this);
4601 uniquifyDocNames(docsWithSameName);
4606 m_docName =
i18n(
"Untitled");
4609 if (m_docNameNumber > 0) {
4614 if (oldName != m_docName) {
4615 Q_EMIT documentNameChanged(
this);
4633 int lastSlash = url.lastIndexOf(
QLatin1Char(
'/'));
4634 if (lastSlash == -1) {
4638 int fileNameStart = lastSlash;
4641 lastSlash = url.lastIndexOf(
QLatin1Char(
'/'), lastSlash);
4642 if (lastSlash == -1) {
4645 return url.mid(lastSlash, fileNameStart);
4650 urlv = urlv.
mid(lastSlash);
4652 for (
size_t i = 0; i < urls.size(); ++i) {
4653 if (urls[i] == url) {
4657 if (urls[i].endsWith(urlv)) {
4658 lastSlash = url.lastIndexOf(
QLatin1Char(
'/'), lastSlash - 1);
4659 if (lastSlash <= 0) {
4661 return url.mid(0, fileNameStart);
4664 urlv = urlView.
mid(lastSlash);
4669 return url.mid(lastSlash + 1, fileNameStart - (lastSlash + 1));
4672void KTextEditor::DocumentPrivate::uniquifyDocNames(
const std::vector<KTextEditor::DocumentPrivate *> &docs)
4674 std::vector<QString> paths;
4675 paths.reserve(docs.size());
4677 return d->url().toString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile);
4680 for (
const auto doc : docs) {
4681 const QString prefix = shortestPrefix(paths, doc);
4683 const QString oldName = doc->m_docName;
4686 doc->m_docName = fileName + QStringLiteral(
" - ") + prefix;
4688 doc->m_docName = fileName;
4691 if (doc->m_docName != oldName) {
4699 if (url().isEmpty() || !m_modOnHd) {
4703 if (!isModified() && isAutoReload()) {
4704 onModOnHdAutoReload();
4708 if (!m_fileChangedDialogsActivated || m_modOnHdHandler) {
4713 if (m_modOnHdReason == m_prevModOnHdReason) {
4716 m_prevModOnHdReason = m_modOnHdReason;
4718 m_modOnHdHandler =
new KateModOnHdPrompt(
this, m_modOnHdReason, reasonedMOHString());
4719 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::saveAsTriggered,
this, &DocumentPrivate::onModOnHdSaveAs);
4720 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::closeTriggered,
this, &DocumentPrivate::onModOnHdClose);
4721 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::reloadTriggered,
this, &DocumentPrivate::onModOnHdReload);
4722 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::autoReloadTriggered,
this, &DocumentPrivate::onModOnHdAutoReload);
4723 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::ignoreTriggered,
this, &DocumentPrivate::onModOnHdIgnore);
4726void KTextEditor::DocumentPrivate::onModOnHdSaveAs()
4729 const QUrl res = getSaveFileUrl(
i18n(
"Save File"));
4735 delete m_modOnHdHandler;
4736 m_prevModOnHdReason = OnDiskUnmodified;
4737 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4744void KTextEditor::DocumentPrivate::onModOnHdClose()
4750 m_fileChangedDialogsActivated =
false;
4755 m_fileChangedDialogsActivated =
true;
4760void KTextEditor::DocumentPrivate::onModOnHdReload()
4763 m_prevModOnHdReason = OnDiskUnmodified;
4764 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4770 m_undoManager->clearUndo();
4771 m_undoManager->clearRedo();
4774 delete m_modOnHdHandler;
4777void KTextEditor::DocumentPrivate::autoReloadToggled(
bool b)
4779 m_autoReloadMode->setChecked(b);
4783 disconnect(&m_modOnHdTimer, &
QTimer::timeout,
this, &DocumentPrivate::onModOnHdAutoReload);
4787bool KTextEditor::DocumentPrivate::isAutoReload()
4789 return m_autoReloadMode->isChecked();
4792void KTextEditor::DocumentPrivate::delayAutoReload()
4794 if (isAutoReload()) {
4795 m_autoReloadThrottle.start();
4799void KTextEditor::DocumentPrivate::onModOnHdAutoReload()
4801 if (m_modOnHdHandler) {
4802 delete m_modOnHdHandler;
4803 autoReloadToggled(
true);
4806 if (!isAutoReload()) {
4810 if (m_modOnHd && !m_reloading && !m_autoReloadThrottle.isActive()) {
4812 m_prevModOnHdReason = OnDiskUnmodified;
4813 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4817 m_undoManager->clearUndo();
4818 m_undoManager->clearRedo();
4821 m_autoReloadThrottle.start();
4825void KTextEditor::DocumentPrivate::onModOnHdIgnore()
4828 delete m_modOnHdHandler;
4833 m_modOnHdReason = reason;
4834 m_modOnHd = (reason != OnDiskUnmodified);
4835 Q_EMIT modifiedOnDisk(
this, (reason != OnDiskUnmodified), reason);
4838class KateDocumentTmpMark
4847 m_fileChangedDialogsActivated = on;
4852 if (url().isEmpty()) {
4864 m_undoManager->clearUndo();
4865 m_undoManager->clearRedo();
4870 delete m_modOnHdHandler;
4872 Q_EMIT aboutToReload(
this);
4876 std::transform(m_marks.cbegin(), m_marks.cend(), std::back_inserter(tmp), [
this](
KTextEditor::Mark *mark) {
4877 return KateDocumentTmpMark{.line = line(mark->line), .mark = *mark};
4881 const QString oldMode = mode();
4882 const bool modeByUser = m_fileTypeSetByUser;
4883 const QString oldHlMode = highlightingMode();
4884 const bool hlByUser = m_hlSetByUser;
4886 m_storedVariables.clear();
4890 std::transform(m_views.cbegin(), m_views.cend(), std::back_inserter(cursorPositions), [](
KTextEditor::View *v) {
4891 return std::pair<KTextEditor::ViewPrivate *, KTextEditor::Cursor>(static_cast<ViewPrivate *>(v), v->cursorPosition());
4896 for (
auto *view : m_views) {
4897 static_cast<ViewPrivate *
>(view)->clearSecondaryCursors();
4900 static_cast<ViewPrivate *
>(view)->clearFoldingState();
4905 KTextEditor::DocumentPrivate::openUrl(url());
4908 m_userSetEncodingForNextReload =
false;
4911 for (
auto v : std::as_const(m_views)) {
4913 auto it = std::find_if(cursorPositions.
cbegin(), cursorPositions.
cend(), [v](
const std::pair<KTextEditor::ViewPrivate *, KTextEditor::Cursor> &p) {
4914 return p.first == v;
4916 v->setCursorPosition(it->second);
4922 const int lines = this->lines();
4923 for (
const auto &tmpMark : tmp) {
4924 if (tmpMark.mark.line < lines) {
4925 if (tmpMark.line == line(tmpMark.mark.line)) {
4926 setMark(tmpMark.mark.line, tmpMark.mark.type);
4933 updateFileType(oldMode,
true);
4936 setHighlightingMode(oldHlMode);
4939 Q_EMIT reloaded(
this);
4944bool KTextEditor::DocumentPrivate::documentSave()
4946 if (!url().
isValid() || !isReadWrite()) {
4947 return documentSaveAs();
4953bool KTextEditor::DocumentPrivate::documentSaveAs()
4955 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save File"));
4963bool KTextEditor::DocumentPrivate::documentSaveAsWithEncoding(
const QString &encoding)
4965 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save File"));
4970 setEncoding(encoding);
4974void KTextEditor::DocumentPrivate::documentSaveCopyAs()
4976 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save Copy of File"));
4982 if (!file->
open()) {
4986 if (!m_buffer->saveFile(file->
fileName())) {
4988 i18n(
"The document could not be saved, as it was not possible to write to %1.\n\nCheck that you have write access to this file or "
4989 "that enough disk space is available.",
4997 const auto url = this->url();
4999 if (
auto sj = qobject_cast<KIO::StatJob *>(j)) {
5010void KTextEditor::DocumentPrivate::setWordWrap(
bool on)
5012 config()->setWordWrap(on);
5015bool KTextEditor::DocumentPrivate::wordWrap()
const
5017 return config()->wordWrap();
5020void KTextEditor::DocumentPrivate::setWordWrapAt(uint col)
5022 config()->setWordWrapAt(col);
5025unsigned int KTextEditor::DocumentPrivate::wordWrapAt()
const
5027 return config()->wordWrapAt();
5030void KTextEditor::DocumentPrivate::setPageUpDownMovesCursor(
bool on)
5032 config()->setPageUpDownMovesCursor(on);
5035bool KTextEditor::DocumentPrivate::pageUpDownMovesCursor()
const
5037 return config()->pageUpDownMovesCursor();
5043 return m_config->setEncoding(e);
5048 return m_config->encoding();
5051void KTextEditor::DocumentPrivate::updateConfig()
5053 m_undoManager->updateConfig();
5056 m_indenter->setMode(m_config->indentationMode());
5057 m_indenter->updateConfig();
5060 m_buffer->setTabWidth(config()->tabWidth());
5063 for (
auto view : std::as_const(m_views)) {
5064 static_cast<ViewPrivate *
>(view)->updateDocumentConfig();
5068 if (m_onTheFlyChecker) {
5069 m_onTheFlyChecker->updateConfig();
5072 if (config()->autoSave()) {
5073 int interval = config()->autoSaveInterval();
5074 if (interval == 0) {
5075 m_autoSaveTimer.stop();
5077 m_autoSaveTimer.setInterval(interval * 1000);
5079 m_autoSaveTimer.start();
5084 Q_EMIT configChanged(
this);
5094bool KTextEditor::DocumentPrivate::readVariables(KTextEditor::ViewPrivate *view)
5096 const bool hasVariableline = [
this] {
5099 for (
int i = qMax(10, lines() - 10); i < lines(); ++i) {
5100 if (line(i).contains(s)) {
5105 for (
int i = 0; i < qMin(9, lines()); ++i) {
5106 if (line(i).contains(s)) {
5112 if (!hasVariableline) {
5117 m_config->configStart();
5118 for (
auto view : std::as_const(m_views)) {
5119 auto v =
static_cast<ViewPrivate *
>(view);
5130 for (
int i = 0; i < qMin(9, lines()); ++i) {
5131 readVariableLine(line(i), view);
5134 for (
int i = qMax(10, lines() - 10); i < lines(); i++) {
5135 readVariableLine(line(i), view);
5140 m_config->configEnd();
5141 for (
auto view : std::as_const(m_views)) {
5142 auto v =
static_cast<ViewPrivate *
>(view);
5154void KTextEditor::DocumentPrivate::readVariableLine(
const QString &t, KTextEditor::ViewPrivate *view)
5157 static const QRegularExpression kvLineWildcard(QStringLiteral(
"kate-wildcard\\((.*)\\):(.*)"));
5158 static const QRegularExpression kvLineMime(QStringLiteral(
"kate-mimetype\\((.*)\\):(.*)"));
5171 auto match = kvLine.match(t);
5172 if (
match.hasMatch()) {
5173 s =
match.captured(1);
5176 }
else if ((match = kvLineWildcard.match(t)).hasMatch()) {
5182 for (
const QString &pattern : wildcards) {
5189 found = wildcard.match(matchPath ? pathOfFile : nameOfFile).hasMatch();
5200 s =
match.captured(2);
5203 }
else if ((match = kvLineMime.match(t)).hasMatch()) {
5211 s =
match.captured(2);
5219 static const auto vvl = {
5243 int spaceIndent = -1;
5244 bool replaceTabsSet =
false;
5249 while ((match = kvVar.match(s, startPos)).hasMatch()) {
5250 startPos =
match.capturedEnd(0);
5251 var =
match.captured(1);
5252 val =
match.captured(2).trimmed();
5258 if (contains(vvl, var)) {
5260 setViewVariable(var, val, {&v, 1});
5264 if (var ==
QLatin1String(
"word-wrap") && checkBoolValue(val, &state)) {
5269 else if (var ==
QLatin1String(
"backspace-indents") && checkBoolValue(val, &state)) {
5270 m_config->setBackspaceIndents(state);
5271 }
else if (var ==
QLatin1String(
"indent-pasted-text") && checkBoolValue(val, &state)) {
5272 m_config->setIndentPastedText(state);
5273 }
else if (var ==
QLatin1String(
"replace-tabs") && checkBoolValue(val, &state)) {
5274 m_config->setReplaceTabsDyn(state);
5275 replaceTabsSet =
true;
5276 }
else if (var ==
QLatin1String(
"remove-trailing-space") && checkBoolValue(val, &state)) {
5277 qCWarning(LOG_KTE) <<
i18n(
5278 "Using deprecated modeline 'remove-trailing-space'. "
5279 "Please replace with 'remove-trailing-spaces modified;', see "
5280 "https://docs.kde.org/?application=katepart&branch=stable5&path=config-variables.html#variable-remove-trailing-spaces");
5281 m_config->setRemoveSpaces(state ? 1 : 0);
5282 }
else if (var ==
QLatin1String(
"replace-trailing-space-save") && checkBoolValue(val, &state)) {
5283 qCWarning(LOG_KTE) <<
i18n(
5284 "Using deprecated modeline 'replace-trailing-space-save'. "
5285 "Please replace with 'remove-trailing-spaces all;', see "
5286 "https://docs.kde.org/?application=katepart&branch=stable5&path=config-variables.html#variable-remove-trailing-spaces");
5287 m_config->setRemoveSpaces(state ? 2 : 0);
5288 }
else if (var ==
QLatin1String(
"overwrite-mode") && checkBoolValue(val, &state)) {
5289 m_config->setOvr(state);
5290 }
else if (var ==
QLatin1String(
"keep-extra-spaces") && checkBoolValue(val, &state)) {
5291 m_config->setKeepExtraSpaces(state);
5292 }
else if (var ==
QLatin1String(
"tab-indents") && checkBoolValue(val, &state)) {
5293 m_config->setTabIndents(state);
5294 }
else if (var ==
QLatin1String(
"show-tabs") && checkBoolValue(val, &state)) {
5295 m_config->setShowTabs(state);
5296 }
else if (var ==
QLatin1String(
"show-trailing-spaces") && checkBoolValue(val, &state)) {
5297 m_config->setShowSpaces(state ? KateDocumentConfig::Trailing : KateDocumentConfig::None);
5298 }
else if (var ==
QLatin1String(
"space-indent") && checkBoolValue(val, &state)) {
5300 spaceIndent = state;
5301 }
else if (var ==
QLatin1String(
"smart-home") && checkBoolValue(val, &state)) {
5302 m_config->setSmartHome(state);
5303 }
else if (var ==
QLatin1String(
"newline-at-eof") && checkBoolValue(val, &state)) {
5304 m_config->setNewLineAtEof(state);
5308 else if (var ==
QLatin1String(
"tab-width") && checkIntValue(val, &n)) {
5309 m_config->setTabWidth(n);
5310 }
else if (var ==
QLatin1String(
"indent-width") && checkIntValue(val, &n)) {
5311 m_config->setIndentationWidth(n);
5313 m_config->setIndentationMode(val);
5314 }
else if (var ==
QLatin1String(
"word-wrap-column") && checkIntValue(val, &n) && n > 0) {
5315 m_config->setWordWrapAt(n);
5321 if ((n = indexOf(l, val.
toLower())) != -1) {
5324 m_config->setEol(n);
5325 m_config->setAllowEolDetection(
false);
5328 if (checkBoolValue(val, &state)) {
5329 m_config->setBom(state);
5331 }
else if (var ==
QLatin1String(
"remove-trailing-spaces")) {
5334 m_config->setRemoveSpaces(1);
5336 m_config->setRemoveSpaces(2);
5338 m_config->setRemoveSpaces(0);
5341 setHighlightingMode(val);
5347 setDefaultDictionary(val);
5348 }
else if (var ==
QLatin1String(
"automatic-spell-checking") && checkBoolValue(val, &state)) {
5349 onTheFlySpellCheckingEnabled(state);
5353 else if (contains(vvl, var)) {
5354 setViewVariable(var, val, m_views);
5356 m_storedVariables[var] = val;
5368 if (!replaceTabsSet && spaceIndent >= 0) {
5369 m_config->setReplaceTabsDyn(spaceIndent > 0);
5373void KTextEditor::DocumentPrivate::setViewVariable(
const QString &var,
const QString &val, std::span<KTextEditor::View *> views)
5378 for (
auto view : views) {
5379 auto v =
static_cast<ViewPrivate *
>(view);
5382 if (checkBoolValue(val, &state)) {
5385 if (v->config()->
setValue(var, help)) {
5386 }
else if (v->rendererConfig()->
setValue(var, help)) {
5388 }
else if (var ==
QLatin1String(
"dynamic-word-wrap") && checkBoolValue(val, &state)) {
5389 v->config()->setDynWordWrap(state);
5390 }
else if (var ==
QLatin1String(
"block-selection") && checkBoolValue(val, &state)) {
5391 v->setBlockSelection(state);
5394 }
else if (var ==
QLatin1String(
"icon-bar-color") && checkColorValue(val, c)) {
5395 v->rendererConfig()->setIconBarColor(c);
5398 else if (var ==
QLatin1String(
"background-color") && checkColorValue(val, c)) {
5399 v->rendererConfig()->setBackgroundColor(c);
5400 }
else if (var ==
QLatin1String(
"selection-color") && checkColorValue(val, c)) {
5401 v->rendererConfig()->setSelectionColor(c);
5402 }
else if (var ==
QLatin1String(
"current-line-color") && checkColorValue(val, c)) {
5403 v->rendererConfig()->setHighlightedLineColor(c);
5404 }
else if (var ==
QLatin1String(
"bracket-highlight-color") && checkColorValue(val, c)) {
5405 v->rendererConfig()->setHighlightedBracketColor(c);
5406 }
else if (var ==
QLatin1String(
"word-wrap-marker-color") && checkColorValue(val, c)) {
5407 v->rendererConfig()->setWordWrapMarkerColor(c);
5413 _f.setFixedPitch(
QFont(val).fixedPitch());
5418 v->rendererConfig()->setFont(_f);
5420 v->rendererConfig()->setSchema(val);
5425bool KTextEditor::DocumentPrivate::checkBoolValue(
QString val,
bool *result)
5429 if (contains(trueValues, val)) {
5435 if (contains(falseValues, val)) {
5442bool KTextEditor::DocumentPrivate::checkIntValue(
const QString &val,
int *result)
5445 *result = val.
toInt(&ret);
5449bool KTextEditor::DocumentPrivate::checkColorValue(
const QString &val,
QColor &c)
5458 auto it = m_storedVariables.find(name);
5459 if (it == m_storedVariables.end()) {
5467 QString s = QStringLiteral(
"kate: ");
5471 readVariableLine(s);
5476void KTextEditor::DocumentPrivate::slotModOnHdDirty(
const QString &path)
5478 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified)) {
5480 m_modOnHdReason = OnDiskModified;
5482 if (!m_modOnHdTimer.isActive()) {
5483 m_modOnHdTimer.start();
5488void KTextEditor::DocumentPrivate::slotModOnHdCreated(
const QString &path)
5490 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated)) {
5492 m_modOnHdReason = OnDiskCreated;
5494 if (!m_modOnHdTimer.isActive()) {
5495 m_modOnHdTimer.start();
5500void KTextEditor::DocumentPrivate::slotModOnHdDeleted(
const QString &path)
5502 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted)) {
5504 m_modOnHdReason = OnDiskDeleted;
5506 if (!m_modOnHdTimer.isActive()) {
5507 m_modOnHdTimer.start();
5512void KTextEditor::DocumentPrivate::slotDelayedHandleModOnHd()
5516 if (!oldDigest.
isEmpty() && !url().isEmpty() && url().isLocalFile()) {
5518 if (m_modOnHdReason != OnDiskDeleted && m_modOnHdReason != OnDiskCreated && createDigest() && oldDigest == checksum()) {
5520 m_modOnHdReason = OnDiskUnmodified;
5521 m_prevModOnHdReason = OnDiskUnmodified;
5528 if (m_modOnHd && !isModified() &&
QFile::exists(url().toLocalFile())
5529 && config()->value(KateDocumentConfig::AutoReloadIfStateIsInVersionControl).toBool()) {
5536 git.
start(fullGitPath, args);
5543 m_modOnHdReason = OnDiskUnmodified;
5544 m_prevModOnHdReason = OnDiskUnmodified;
5554 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
5559 return m_buffer->digest();
5562bool KTextEditor::DocumentPrivate::createDigest()
5566 if (url().isLocalFile()) {
5567 QFile f(url().toLocalFile());
5571 const QString header = QStringLiteral(
"blob %1").
arg(f.size());
5574 digest = crypto.result();
5579 m_buffer->setDigest(digest);
5583QString KTextEditor::DocumentPrivate::reasonedMOHString()
const
5588 switch (m_modOnHdReason) {
5589 case OnDiskModified:
5590 return i18n(
"The file '%1' was modified on disk.", str);
5593 return i18n(
"The file '%1' was created on disk.", str);
5596 return i18n(
"The file '%1' was deleted or moved on disk.", str);
5605void KTextEditor::DocumentPrivate::removeTrailingSpacesAndAddNewLineAtEof()
5608 const int remove = config()->removeSpaces();
5609 const bool newLineAtEof = config()->newLineAtEof();
5610 if (remove == 0 && !newLineAtEof) {
5615 const bool wordWrapEnabled = config()->wordWrap();
5616 if (wordWrapEnabled) {
5623 const int lines = this->lines();
5625 for (
int line = 0; line < lines; ++line) {
5631 if (remove == 2 || textline.markedAsModified() || textline.markedAsSavedOnDisk()) {
5632 const int p = textline.
lastChar() + 1;
5633 const int l = textline.
length() - p;
5635 editRemoveText(line, p, l);
5644 Q_ASSERT(lines > 0);
5645 const auto length = lineLength(lines - 1);
5649 const auto oldEndOfDocumentCursor = documentEnd();
5650 std::vector<KTextEditor::ViewPrivate *> viewsToRestoreCursors;
5651 for (
auto view : std::as_const(m_views)) {
5652 auto v =
static_cast<ViewPrivate *
>(view);
5653 if (v->cursorPosition() == oldEndOfDocumentCursor) {
5654 viewsToRestoreCursors.push_back(v);
5659 editWrapLine(lines - 1, length);
5662 for (
auto v : viewsToRestoreCursors) {
5663 v->setCursorPosition(oldEndOfDocumentCursor);
5671 if (wordWrapEnabled) {
5679 const int lines = this->lines();
5680 for (
int line = 0; line < lines; ++line) {
5682 const int p = textline.
lastChar() + 1;
5683 const int l = textline.
length() - p;
5685 editRemoveText(line, p, l);
5693 if (user || !m_fileTypeSetByUser) {
5699 if (fileType.name.
isEmpty()) {
5704 m_fileTypeSetByUser = user;
5706 m_fileType = newType;
5708 m_config->configStart();
5713 if ((user || !m_hlSetByUser) && !fileType.hl.
isEmpty()) {
5714 int hl(KateHlManager::self()->nameFind(fileType.hl));
5717 m_buffer->setHighlight(hl);
5724 if (!m_indenterSetByUser && !fileType.indenter.
isEmpty()) {
5725 config()->setIndentationMode(fileType.indenter);
5729 for (
auto view : std::as_const(m_views)) {
5730 auto v =
static_cast<ViewPrivate *
>(view);
5735 bool bom_settings =
false;
5736 if (m_bomSetByUser) {
5737 bom_settings = m_config->bom();
5739 readVariableLine(fileType.varLine);
5740 if (m_bomSetByUser) {
5741 m_config->setBom(bom_settings);
5743 m_config->configEnd();
5744 for (
auto view : std::as_const(m_views)) {
5745 auto v =
static_cast<ViewPrivate *
>(view);
5752 Q_EMIT modeChanged(
this);
5756void KTextEditor::DocumentPrivate::slotQueryClose_save(
bool *handled,
bool *abortClosing)
5759 *abortClosing =
true;
5760 if (url().isEmpty()) {
5761 const QUrl res = getSaveFileUrl(
i18n(
"Save File"));
5763 *abortClosing =
true;
5767 *abortClosing =
false;
5770 *abortClosing =
false;
5780 return m_config->configKeys();
5786 return m_config->
value(key);
5792 m_config->setValue(key, value);
5806 bool changed = removeText(range, block);
5807 changed |= insertText(range.
start(), s, block);
5812KateHighlighting *KTextEditor::DocumentPrivate::highlight()
const
5814 return m_buffer->highlight();
5819 m_buffer->ensureHighlighted(i);
5820 return m_buffer->plainLine(i);
5825 return m_buffer->plainLine(i);
5828bool KTextEditor::DocumentPrivate::isEditRunning()
const
5830 return editIsRunning;
5833void KTextEditor::DocumentPrivate::setUndoMergeAllEdits(
bool merge)
5835 if (merge && m_undoMergeAllEdits) {
5840 m_undoManager->undoSafePoint();
5841 m_undoManager->setAllowComplexMerge(merge);
5842 m_undoMergeAllEdits =
merge;
5855 return new Kate::TextRange(m_buffer, range, insertBehaviors, emptyBehavior);
5860 return m_buffer->history().revision();
5865 return m_buffer->history().lastSavedRevision();
5870 m_buffer->history().lockRevision(revision);
5875 m_buffer->history().unlockRevision(revision);
5881 qint64 fromRevision,
5884 m_buffer->history().transformCursor(line, column, insertBehavior, fromRevision, toRevision);
5889 qint64 fromRevision,
5892 int line = cursor.
line();
5893 int column = cursor.
column();
5894 m_buffer->history().transformCursor(line, column, insertBehavior, fromRevision, toRevision);
5901 qint64 fromRevision,
5904 m_buffer->history().transformRange(range, insertBehaviors, emptyBehavior, fromRevision, toRevision);
5913 m_annotationModel = model;
5914 Q_EMIT annotationModelChanged(oldmodel, m_annotationModel);
5919 return m_annotationModel;
5924bool KTextEditor::DocumentPrivate::queryClose()
5926 if (!isModified() || (isEmpty() && url().isEmpty())) {
5930 QString docName = documentName();
5933 i18n(
"The document \"%1\" has been modified.\n"
5934 "Do you want to save your changes or discard them?",
5936 i18n(
"Close Document"),
5940 bool abortClose =
false;
5941 bool handled =
false;
5945 sigQueryClose(&handled, &abortClose);
5947 if (url().isEmpty()) {
5948 const QUrl url = getSaveFileUrl(
i18n(
"Save File"));
5957 }
else if (abortClose) {
5960 return waitSaveComplete();
5968void KTextEditor::DocumentPrivate::slotStarted(
KIO::Job *job)
5971 if (m_documentState == DocumentIdle) {
5972 m_documentState = DocumentLoading;
5980 if (m_documentState == DocumentLoading) {
5982 m_readWriteStateBeforeLoading = isReadWrite();
5987 setReadWrite(
false);
5997void KTextEditor::DocumentPrivate::slotCompleted()
6001 if (m_documentState == DocumentLoading) {
6002 setReadWrite(m_readWriteStateBeforeLoading);
6003 delete m_loadingMessage;
6004 m_reloading =
false;
6008 if (m_documentState == DocumentSaving || m_documentState == DocumentSavingAs) {
6009 Q_EMIT documentSavedOrUploaded(
this, m_documentState == DocumentSavingAs);
6013 m_documentState = DocumentIdle;
6016void KTextEditor::DocumentPrivate::slotCanceled()
6020 if (m_documentState == DocumentLoading) {
6021 setReadWrite(m_readWriteStateBeforeLoading);
6022 delete m_loadingMessage;
6023 m_reloading =
false;
6025 if (!m_openingError) {
6026 showAndSetOpeningErrorAccess();
6033 m_documentState = DocumentIdle;
6036void KTextEditor::DocumentPrivate::slotTriggerLoadingMessage()
6040 if (m_documentState != DocumentLoading) {
6045 delete m_loadingMessage;
6054 m_loadingMessage->addAction(cancel);
6058 postMessage(m_loadingMessage);
6061void KTextEditor::DocumentPrivate::slotAbortLoading()
6064 if (!m_loadingJob) {
6070 m_loadingJob->kill(KJob::EmitResult);
6071 m_loadingJob =
nullptr;
6074void KTextEditor::DocumentPrivate::slotUrlChanged(
const QUrl &url)
6078 Q_EMIT documentUrlChanged(
this);
6081bool KTextEditor::DocumentPrivate::save()
6085 if ((m_documentState != DocumentIdle) && (m_documentState != DocumentPreSavingAs)) {
6090 if (m_documentState == DocumentIdle) {
6091 m_documentState = DocumentSaving;
6093 m_documentState = DocumentSavingAs;
6097 Q_EMIT aboutToSave(
this);
6103bool KTextEditor::DocumentPrivate::saveAs(
const QUrl &url)
6114 if (m_documentState != DocumentIdle) {
6119 m_documentState = DocumentPreSavingAs;
6125QString KTextEditor::DocumentPrivate::defaultDictionary()
const
6127 return m_defaultDictionary;
6132 return m_dictionaryRanges;
6135void KTextEditor::DocumentPrivate::clearDictionaryRanges()
6137 for (
auto i = m_dictionaryRanges.cbegin(); i != m_dictionaryRanges.cend(); ++i) {
6140 m_dictionaryRanges.clear();
6141 if (m_onTheFlyChecker) {
6142 m_onTheFlyChecker->refreshSpellCheck();
6144 Q_EMIT dictionaryRangesPresent(
false);
6151 setDictionary(newDictionary, rangeOnLine(range, i));
6154 setDictionary(newDictionary, range);
6157 Q_EMIT dictionaryRangesPresent(!m_dictionaryRanges.isEmpty());
6163 if (!newDictionaryRange.
isValid() || newDictionaryRange.
isEmpty()) {
6168 for (
auto i = m_dictionaryRanges.begin(); i != m_dictionaryRanges.end();) {
6169 qCDebug(LOG_KTE) <<
"new iteration" << newDictionaryRange;
6170 if (newDictionaryRange.
isEmpty()) {
6173 QPair<KTextEditor::MovingRange *, QString> pair = *i;
6174 QString dictionarySet = pair.second;
6176 qCDebug(LOG_KTE) << *dictionaryRange << dictionarySet;
6177 if (dictionaryRange->
contains(newDictionaryRange) && newDictionary == dictionarySet) {
6178 qCDebug(LOG_KTE) <<
"dictionaryRange contains newDictionaryRange";
6181 if (newDictionaryRange.
contains(*dictionaryRange)) {
6182 delete dictionaryRange;
6183 i = m_dictionaryRanges.erase(i);
6184 qCDebug(LOG_KTE) <<
"newDictionaryRange contains dictionaryRange";
6190 if (dictionarySet == newDictionary) {
6193 Q_ASSERT(remainingRanges.
size() == 1);
6194 newDictionaryRange = remainingRanges.
first();
6196 qCDebug(LOG_KTE) <<
"dictionarySet == newDictionary";
6200 for (
auto j = remainingRanges.
begin(); j != remainingRanges.
end(); ++j) {
6203 newRanges.
push_back({remainingRange, dictionarySet});
6205 i = m_dictionaryRanges.erase(i);
6206 delete dictionaryRange;
6211 m_dictionaryRanges += newRanges;
6216 m_dictionaryRanges.push_back({newDictionaryMovingRange, newDictionary});
6218 if (m_onTheFlyChecker && !newDictionaryRange.
isEmpty()) {
6219 m_onTheFlyChecker->refreshSpellCheck(newDictionaryRange);
6223void KTextEditor::DocumentPrivate::setDefaultDictionary(
const QString &dict)
6225 if (m_defaultDictionary == dict) {
6229 m_defaultDictionary = dict;
6231 if (m_onTheFlyChecker) {
6232 m_onTheFlyChecker->updateConfig();
6233 refreshOnTheFlyCheck();
6235 Q_EMIT defaultDictionaryChanged(
this);
6238void KTextEditor::DocumentPrivate::onTheFlySpellCheckingEnabled(
bool enable)
6240 if (isOnTheFlySpellCheckingEnabled() == enable) {
6245 Q_ASSERT(m_onTheFlyChecker ==
nullptr);
6246 m_onTheFlyChecker =
new KateOnTheFlyChecker(
this);
6248 delete m_onTheFlyChecker;
6249 m_onTheFlyChecker =
nullptr;
6252 for (
auto view : std::as_const(m_views)) {
6253 static_cast<ViewPrivate *
>(view)->reflectOnTheFlySpellCheckStatus(enable);
6257bool KTextEditor::DocumentPrivate::isOnTheFlySpellCheckingEnabled()
const
6259 return m_onTheFlyChecker !=
nullptr;
6264 if (!m_onTheFlyChecker) {
6267 return m_onTheFlyChecker->dictionaryForMisspelledRange(range);
6271void KTextEditor::DocumentPrivate::clearMisspellingForWord(
const QString &word)
6273 if (m_onTheFlyChecker) {
6274 m_onTheFlyChecker->clearMisspellingForWord(word);
6280 if (m_onTheFlyChecker) {
6281 m_onTheFlyChecker->refreshSpellCheck(range);
6287 deleteDictionaryRange(movingRange);
6292 deleteDictionaryRange(movingRange);
6297 qCDebug(LOG_KTE) <<
"deleting" << movingRange;
6299 auto finder = [=](
const QPair<KTextEditor::MovingRange *, QString> &item) ->
bool {
6300 return item.first == movingRange;
6303 auto it = std::find_if(m_dictionaryRanges.begin(), m_dictionaryRanges.end(), finder);
6305 if (it != m_dictionaryRanges.end()) {
6306 m_dictionaryRanges.erase(it);
6310 Q_ASSERT(std::find_if(m_dictionaryRanges.begin(), m_dictionaryRanges.end(), finder) == m_dictionaryRanges.end());
6313bool KTextEditor::DocumentPrivate::containsCharacterEncoding(
KTextEditor::Range range)
6315 KateHighlighting *highlighting = highlight();
6317 const int rangeStartLine = range.
start().
line();
6318 const int rangeStartColumn = range.
start().
column();
6319 const int rangeEndLine = range.
end().
line();
6320 const int rangeEndColumn = range.
end().
column();
6322 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6324 const int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6325 const int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.length();
6326 for (
int col = startColumn; col < endColumn; ++col) {
6328 const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr);
6338int KTextEditor::DocumentPrivate::computePositionWrtOffsets(
const OffsetList &offsetList,
int pos)
6340 int previousOffset = 0;
6341 for (
auto i = offsetList.cbegin(); i != offsetList.cend(); ++i) {
6342 if (i->first > pos) {
6345 previousOffset = i->second;
6347 return pos + previousOffset;
6356 int decToEncCurrentOffset = 0;
6357 int encToDecCurrentOffset = 0;
6361 KateHighlighting *highlighting = highlight();
6364 const int rangeStartLine = range.
start().
line();
6365 const int rangeStartColumn = range.
start().
column();
6366 const int rangeEndLine = range.
end().
line();
6367 const int rangeEndColumn = range.
end().
column();
6369 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6370 textLine = kateTextLine(line);
6371 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6372 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.
length();
6373 for (
int col = startColumn; col < endColumn;) {
6375 const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr);
6378 if (!matchingPrefix.
isEmpty()) {
6380 const QChar &c = characterEncodingsHash.
value(matchingPrefix);
6381 const bool isNullChar = c.
isNull();
6385 i += matchingPrefix.
length();
6386 col += matchingPrefix.
length();
6388 decToEncCurrentOffset = decToEncCurrentOffset - (isNullChar ? 0 : 1) + matchingPrefix.
length();
6389 encToDecCurrentOffset = encToDecCurrentOffset - matchingPrefix.
length() + (isNullChar ? 0 : 1);
6390 newI += (isNullChar ? 0 : 1);
6391 decToEncOffsetList.
push_back(QPair<int, int>(newI, decToEncCurrentOffset));
6392 encToDecOffsetList.
push_back(QPair<int, int>(i, encToDecCurrentOffset));
6402 if (previous < range.
end()) {
6408void KTextEditor::DocumentPrivate::replaceCharactersByEncoding(
KTextEditor::Range range)
6410 KateHighlighting *highlighting = highlight();
6413 const int rangeStartLine = range.
start().
line();
6414 const int rangeStartColumn = range.
start().
column();
6415 const int rangeEndLine = range.
end().
line();
6416 const int rangeEndColumn = range.
end().
column();
6418 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6419 textLine = kateTextLine(line);
6420 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6421 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.length();
6422 for (
int col = startColumn; col < endColumn;) {
6424 const QHash<QChar, QString> &reverseCharacterEncodingsHash = highlighting->getReverseCharacterEncodings(attr);
6425 auto it = reverseCharacterEncodingsHash.
find(textLine.
at(col));
6426 if (it != reverseCharacterEncodingsHash.
end()) {
6428 col += (*it).length();
6442 return highlight()->getEmbeddedHighlightingModes();
6447 return highlight()->higlightingModeForLocation(
this, position);
6462 if (line < 0 || line >= lines() || column < 0) {
6463 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6471 if (column < tl.
length()) {
6473 }
else if (column == tl.
length()) {
6477 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6480 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6483 return highlight()->defaultStyleForAttribute(attribute);
6486bool KTextEditor::DocumentPrivate::isComment(
int line,
int column)
6488 return defStyleNum(line, column) == KSyntaxHighlighting::Theme::TextStyle::Comment;
6493 const int offset = down ? 1 : -1;
6494 const int lineCount = lines();
6495 while (startLine >= 0 && startLine < lineCount) {
6497 if (tl.markedAsModified() || tl.markedAsSavedOnDisk()) {
6500 startLine += offset;
6509 delete m_activeTemplateHandler.data();
6510 m_activeTemplateHandler = handler;
6523 qCWarning(LOG_KTE) <<
"trying to post a message to a view of another document:" << message->
text();
6533 closeAction->
setToolTip(
i18nc(
"Close the message being displayed",
"Close message"));
6539 const auto messageActions = message->
actions();
6540 managedMessageActions.
reserve(messageActions.size());
6541 for (
QAction *action : messageActions) {
6542 action->setParent(
nullptr);
6543 managedMessageActions.
append(std::shared_ptr<QAction>(action));
6545 m_messageHash.insert(message, managedMessageActions);
6548 if (KTextEditor::ViewPrivate *view = qobject_cast<KTextEditor::ViewPrivate *>(message->
view())) {
6549 view->postMessage(message, managedMessageActions);
6551 for (
auto view : std::as_const(m_views)) {
6552 static_cast<ViewPrivate *
>(view)->postMessage(message, managedMessageActions);
6557 connect(message, &
Message::closed,
this, &DocumentPrivate::messageDestroyed);
6565 Q_ASSERT(m_messageHash.contains(message));
6566 m_messageHash.remove(message);
6570#include "moc_katedocument.cpp"
bool hasKey(const char *key) const
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
void deleteGroup(const QString &group, WriteConfigFlags flags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
void addFile(const QString &file)
void removeFile(const QString &file)
void deleted(const QString &path)
void dirty(const QString &path)
void created(const QString &path)
mode_t permissions() const
const UDSEntry & statResult() const
virtual Q_SCRIPTABLE void start()=0
Ptr findByDevice(const QString &device) const
static List currentMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
static KNetworkMounts * self()
MediumSideEffectsOptimizations
virtual QWidget * widget()
void setAutoDeletePart(bool autoDeletePart)
void setAutoDeleteWidget(bool autoDeleteWidget)
virtual bool openUrl(const QUrl &url)
void urlChanged(const QUrl &url)
void canceled(const QString &errMsg)
void started(KIO::Job *job)
virtual void setReadWrite(bool readwrite=true)
virtual bool saveAs(const QUrl &url)
void sigQueryClose(bool *handled, bool *abortClosing)
An model for providing line annotation information.
KTextEditor::MainWindow * activeMainWindow()
Accessor to the active main window.
The Cursor represents a position in a Document.
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
void setColumn(int column) noexcept
Set the cursor column to column.
void setPosition(Cursor position) noexcept
Set the current cursor position to position.
constexpr bool isValid() const noexcept
Returns whether the current position of this cursor is a valid position (line + column must both be >...
static constexpr Cursor start() noexcept
Returns a cursor representing the start of any document - i.e., line 0, column 0.
void setLine(int line) noexcept
Set the cursor line to line.
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
static constexpr Cursor invalid() noexcept
Returns an invalid cursor.
A Cursor which is bound to a specific Document.
Backend of KTextEditor::Document related public KTextEditor interfaces.
bool setEncoding(const QString &e) override
Set the encoding for this document.
bool saveFile() override
save the file obtained by the kparts framework the framework abstracts the uploading of remote files
void transformRange(KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision=-1) override
Transform a range from one revision to an other.
void lockRevision(qint64 revision) override
Lock a revision, this will keep it around until released again.
void recoverData() override
If recover data is available, calling recoverData() will trigger the recovery of the data.
QString highlightingModeSection(int index) const override
Returns the name of the section for a highlight given its index in the highlight list (as returned by...
bool handleMarkContextMenu(int line, QPoint position)
Returns true if the context-menu event should not further be processed.
void readSessionConfig(const KConfigGroup &config, const QSet< QString > &flags=QSet< QString >()) override
Read session settings from the given config.
QStringList highlightingModes() const override
Return a list of the names of all possible modes.
bool documentReload() override
Reloads the current document from disk if possible.
void setModifiedOnDisk(ModifiedOnDiskReason reason) override
Set the document's modified-on-disk state to reason.
KTextEditor::Cursor documentEnd() const override
End position of the document.
void clearMarks() override
QStringList configKeys() const override
Get a list of all available keys.
bool postMessage(KTextEditor::Message *message) override
Post message to the Document and its Views.
int lineLengthLimit() const
reads the line length limit from config, if it is not overridden
virtual void slotModifiedOnDisk(KTextEditor::View *v=nullptr)
Ask the user what to do, if the file has been modified on disk.
void joinLines(uint first, uint last)
Unwrap a range of lines.
KTextEditor::MovingRange * newMovingRange(KTextEditor::Range range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors=KTextEditor::MovingRange::DoNotExpand, KTextEditor::MovingRange::EmptyBehavior emptyBehavior=KTextEditor::MovingRange::AllowEmpty) override
Create a new moving range for this document.
virtual QString variable(const QString &name) const
Returns the value for the variable name.
bool setHighlightingMode(const QString &name) override
Set the current mode of the document by giving its name.
QStringList textLines(KTextEditor::Range range, bool block=false) const override
Get the document content within the given range.
void clearEditingPosStack()
Removes all the elements in m_editingStack of the respective document.
void transform(KTextEditor::ViewPrivate *view, KTextEditor::Cursor, TextTransform)
Handling uppercase, lowercase and capitalize for the view.
void rangeInvalid(KTextEditor::MovingRange *movingRange) override
The range is now invalid (ie.
void discardDataRecovery() override
If recover data is available, calling discardDataRecovery() will discard the recover data and the rec...
bool updateFileType(const QString &newType, bool user=false)
bool isLineModified(int line) const override
Check whether line currently contains unsaved data.
bool setMode(const QString &name) override
Set the current mode of the document by giving its name.
qsizetype totalCharacters() const override
Get the count of characters in the document.
QString highlightingMode() const override
Return the name of the currently used mode.
QString line(int line) const override
Get a single text line.
KTextEditor::AnnotationModel * annotationModel() const override
returns the currently set AnnotationModel or 0 if there's none set
QString markDescription(Document::MarkTypes) const override
Get the mark's description to text.
qint64 revision() const override
Current revision.
QString mimeType() override
Tries to detect mime-type based on file name and content of buffer.
KTextEditor::MovingCursor * newMovingCursor(KTextEditor::Cursor position, KTextEditor::MovingCursor::InsertBehavior insertBehavior=KTextEditor::MovingCursor::MoveOnInsert) override
Create a new moving cursor for this document.
QChar characterAt(KTextEditor::Cursor position) const override
Get the character at text position cursor.
qsizetype cursorToOffset(KTextEditor::Cursor c) const override
Retrives the offset for the given cursor position NOTE: It will return -1 if the cursor was invalid o...
QStringList modes() const override
Return a list of the names of all possible modes.
void setAnnotationModel(KTextEditor::AnnotationModel *model) override
Sets a new AnnotationModel for this document to provide annotation information for each line.
bool editUnWrapLine(int line, bool removeLine=true, int length=0)
Unwrap line.
bool editInsertText(int line, int col, const QString &s, bool notify=true)
Add a string in the given line/column.
KTextEditor::Cursor offsetToCursor(qsizetype offset) const override
Retrives the cursor position for given offset NOTE: It will return an invalid cursor(-1,...
bool openFile() override
open the file obtained by the kparts framework the framework abstracts the loading of remote files
bool isLineSaved(int line) const override
Check whether line currently contains only saved text.
QWidget * widget() override
void writeSessionConfig(KConfigGroup &config, const QSet< QString > &flags=QSet< QString >()) override
Write session settings to the config.
QByteArray checksum() const override
Returns a git compatible sha1 checksum of this document on disk.
bool isLineTouched(int line) const override
Check whether line was touched since the file was opened.
void rangeEmpty(KTextEditor::MovingRange *movingRange) override
The range is now empty (ie.
QString text() const override
Get the document content.
int lines() const override
Get the count of lines of the document.
bool handleMarkClick(int line)
Returns true if the click on the mark should not be further processed.
KTextEditor::View * createView(QWidget *parent, KTextEditor::MainWindow *mainWindow=nullptr) override
Create a new view attached to parent.
bool isDataRecoveryAvailable() const override
Returns whether a recovery is available for the current document.
void bomSetByUser()
Set that the BOM marker is forced via the tool menu.
bool editStart()
Enclose editor actions with editStart() and editEnd() to group them.
KTextEditor::Cursor lastEditingPosition(EditingPositionKind nextOrPrevious, KTextEditor::Cursor)
Returns the next or previous position cursor in this document from the stack depending on the argumen...
void setConfigValue(const QString &key, const QVariant &value) override
Set a the key's value to value.
KSyntaxHighlighting::Theme::TextStyle defStyleNum(int line, int column)
void setModifiedOnDiskWarning(bool on) override
Control, whether the editor should show a warning dialog whenever a file was modified on disk.
QIcon markIcon(Document::MarkTypes markType) const override
Get the mark's icon.
KSyntaxHighlighting::Theme::TextStyle defaultStyleAt(KTextEditor::Cursor position) const override
Get the default style of the character located at position.
bool editWrapLine(int line, int col, bool newLine=true, bool *newLineAdded=nullptr, bool notify=true)
Wrap line.
void typeChars(KTextEditor::ViewPrivate *view, QString chars)
Type chars in a view.
QString highlightingModeAt(KTextEditor::Cursor position) override
Get the highlight mode used at a given position in the document.
QVariant configValue(const QString &key) override
Get a value for the key.
bool wrapText(int startLine, int endLine)
Warp a line.
bool editRemoveText(int line, int col, int len)
Remove a string in the given line/column.
qint64 lastSavedRevision() const override
Last revision the buffer got successful saved.
void setDontChangeHlOnSave()
allow to mark, that we changed hl on user wish and should not reset it atm used for the user visible ...
QString decodeCharacters(KTextEditor::Range range, KTextEditor::DocumentPrivate::OffsetList &decToEncOffsetList, KTextEditor::DocumentPrivate::OffsetList &encToDecOffsetList)
The first OffsetList is from decoded to encoded, and the second OffsetList from encoded to decoded.
void saveEditingPositions(const KTextEditor::Cursor cursor)
Saves the editing positions into the stack.
uint editableMarks() const override
Get, which marks can be toggled by the user.
Kate::TextLine plainKateTextLine(int i)
Return line lineno.
uint mark(int line) override
Get all marks set on the line.
void unlockRevision(qint64 revision) override
Release a revision.
QString mode() const override
Return the name of the currently used mode.
bool isValidTextPosition(KTextEditor::Cursor cursor) const override
Get whether cursor is a valid text position.
void removeAllTrailingSpaces()
This function doesn't check for config and is available for use all the time via an action.
void transformCursor(KTextEditor::Cursor &cursor, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision=-1) override
Transform a cursor from one revision to an other.
QString modeSection(int index) const override
Returns the name of the section for a mode given its index in the highlight list (as returned by mode...
bool editMarkLineAutoWrapped(int line, bool autowrapped)
Mark line as autowrapped.
bool editInsertLine(int line, const QString &s, bool notify=true)
Insert a string at the given line.
QStringList embeddedHighlightingModes() const override
Get all available highlighting modes for the current document.
bool editRemoveLine(int line)
Remove a line.
bool isEditingTransactionRunning() const override
Check whether an editing transaction is currently running.
Kate::TextLine kateTextLine(int i)
Same as plainKateTextLine(), except that it is made sure the line is highlighted.
QString encoding() const override
Get the current chosen encoding.
bool editEnd()
End a editor operation.
virtual void setVariable(const QString &name, const QString &value)
Set the variable name to value.
int findTouchedLine(int startLine, bool down)
Find the next modified/saved line, starting at startLine.
QString wordAt(KTextEditor::Cursor cursor) const override
Get the word at the text position cursor.
int lineLength(int line) const override
Get the length of a given line in characters.
KTextEditor::Range wordRangeAt(KTextEditor::Cursor cursor) const override
Get the text range for the word located under the text position cursor.
const QHash< int, KTextEditor::Mark * > & marks() override
Get a hash holding all marks in the document.
void removeView(KTextEditor::View *)
removes the view from the list of views.
bool wrapParagraph(int first, int last)
Wrap lines touched by the selection with respect of existing paragraphs.
A KParts derived class representing a text document.
void documentNameChanged(KTextEditor::Document *document)
This signal is emitted whenever the document name changes.
MarkTypes
Predefined mark types.
virtual QString text() const =0
Get the document content.
void aboutToInvalidateMovingInterfaceContent(KTextEditor::Document *document)
This signal is emitted before the ranges of a document are invalidated and the revisions are deleted ...
static int reservedMarkersCount()
Get the number of predefined mark types we have so far.
void aboutToClose(KTextEditor::Document *document)
Warn anyone listening that the current document is about to close.
ModifiedOnDiskReason
Reasons why a document is modified on disk.
KateModeManager * modeManager()
global mode manager used to manage the modes centrally
void deregisterDocument(KTextEditor::DocumentPrivate *doc)
unregister document at the factory
KTextEditor::Application * application() const override
Current hosting application, if any set.
KDirWatch * dirWatch()
global dirwatch
void registerDocument(KTextEditor::DocumentPrivate *doc)
register document at the factory this allows us to loop over all docs for example on config changes
static KTextEditor::EditorPrivate * self()
Kate Part Internal stuff ;)
QList< KTextEditor::Document * > documents() override
Returns a list of all documents of this editor.
KateVariableExpansionManager * variableExpansionManager()
Returns the variable expansion manager.
static Editor * instance()
Accessor to get the Editor instance.
An object representing lines from a start line to an end line.
This class allows the application that embeds the KTextEditor component to allow it to access parts o...
QWidget * window()
Get the toplevel widget.
uint type
The mark types in the line, combined with logical OR.
int line
The line that contains the mark.
This class holds a Message to display in Views.
@ TopInView
show message as view overlay in the top right corner.
KTextEditor::View * view() const
This function returns the view you set by setView().
int autoHide() const
Returns the auto hide time in milliseconds.
void addAction(QAction *action, bool closeOnTrigger=true)
Adds an action to the message.
QString text() const
Returns the text set in the constructor.
void closed(KTextEditor::Message *message)
This signal is emitted before the message is deleted.
@ Error
error message type
@ Warning
warning message type
void setDocument(KTextEditor::Document *document)
Set the document pointer to document.
QList< QAction * > actions() const
Accessor to all actions, mainly used in the internal implementation to add the actions into the gui.
A Cursor which is bound to a specific Document, and maintains its position.
InsertBehavior
Insert behavior of this cursor, should it stay if text is insert at its position or should it move.
@ MoveOnInsert
move on insert
A range that is bound to a specific Document, and maintains its position.
EmptyBehavior
Behavior of range if it becomes empty.
const Range toRange() const
Convert this clever range into a dumb one.
virtual void setFeedback(MovingRangeFeedback *feedback)=0
Sets the currently active MovingRangeFeedback for this range.
bool contains(const Range &range) const
Check whether the this range wholly encompasses range.
@ DoNotExpand
Don't expand to encapsulate new characters in either direction. This is the default.
@ ExpandRight
Expand to encapsulate new characters to the right of the range.
@ ExpandLeft
Expand to encapsulate new characters to the left of the range.
An object representing a section of text, from one Cursor to another.
constexpr Cursor end() const noexcept
Get the end position of this range.
constexpr Cursor start() const noexcept
Get the start position of this range.
void setEnd(Cursor end) noexcept
Set the end cursor to end.
constexpr bool isEmpty() const noexcept
Returns true if this range contains no characters, ie.
void setRange(Range range) noexcept
Set the start and end cursors to range.start() and range.end() respectively.
constexpr bool overlaps(Range range) const noexcept
Check whether the this range overlaps with range.
constexpr int columnWidth() const noexcept
Returns the number of columns separating the start() and end() positions.
constexpr bool onSingleLine() const noexcept
Check whether this range is wholly contained within one line, ie.
static constexpr Range invalid() noexcept
Returns an invalid range.
constexpr bool isValid() const noexcept
Validity check.
constexpr bool contains(Range range) const noexcept
Check whether the this range wholly encompasses range.
constexpr Range intersect(Range range) const noexcept
Intersects this range with another, returning the shared area of the two ranges.
void setBothLines(int line) noexcept
Convenience function.
constexpr int numberOfLines() const noexcept
Returns the number of lines separating the start() and end() positions.
void setStart(Cursor start) noexcept
Set the start cursor to start.
A text widget with KXMLGUIClient that represents a Document.
virtual QMenu * defaultContextMenu(QMenu *menu=nullptr) const =0
Populate menu with default text editor actions.
virtual bool setCursorPosition(Cursor position)=0
Set the view's new cursor to position.
virtual Document * document() const =0
Get the view's document, that means the view is a view of the returned document.
void focusIn(KTextEditor::View *view)
This signal is emitted whenever the view gets the focus.
void cursorPositionChanged(KTextEditor::View *view, KTextEditor::Cursor newPosition)
This signal is emitted whenever the view's cursor position changed.
virtual void setContextMenu(QMenu *menu)=0
Set a context menu for this view to menu.
Provides Auto-Indent functionality for katepart.
The KateBuffer class maintains a collections of lines.
void tagLines(KTextEditor::LineRange lineRange)
Emitted when the highlighting of a certain range has changed.
void configEnd()
End a config change transaction, update the concerned KateDocumentConfig/KateDocumentConfig/KateDocum...
void configStart()
Start some config changes.
bool setValue(const int key, const QVariant &value)
Set a config value.
File indentation detecter.
This dialog will prompt the user for what do with a file that is modified on disk.
Object to help to search for plain text.
This class can be used to efficiently search for occurrences of strings in a given string.
QString findPrefix(const QString &s, int start=0) const
Returns the shortest prefix of the given string that is contained in this prefix store starting at po...
Object to help to search for regexp.
static QString escapePlaintext(const QString &text)
Returns a modified version of text where escape sequences are resolved, e.g.
const QFont & currentFont() const
Access currently used font.
Inserts a template and offers advanced snippet features, like navigation and mirroring.
KateUndoManager implements a document's history.
Class for tracking editing actions.
Class representing a 'clever' text cursor.
Class representing a single text line.
int attribute(int pos) const
Gets the attribute at the given position use KRenderer::attributes to get the KTextAttribute for this...
const QString & text() const
Accessor to the text contained in this line.
bool endsWith(const QString &match) const
Returns true, if the line ends with match, otherwise returns false.
void setAutoWrapped(bool wrapped)
set auto-wrapped property
const QList< Attribute > & attributesList() const
Accessor to attributes.
int virtualLength(int tabWidth) const
Returns the text length with each tab expanded into tabWidth characters.
QString string(int column, int length) const
Returns the substring with length beginning at the given column.
int previousNonSpaceChar(int pos) const
Find the position of the previous char that is not a space.
int length() const
Returns the line's length.
bool isAutoWrapped() const
Returns true, if the line was automagically wrapped, otherwise returns false.
bool startsWith(const QString &match) const
Returns true, if the line starts with match, otherwise returns false.
int lastChar() const
Returns the position of the last non-whitespace character.
QChar at(int column) const
Returns the character at the given column.
int firstChar() const
Returns the position of the first non-whitespace character.
int toVirtualColumn(int column, int tabWidth) const
Returns the column with each tab expanded into tabWidth characters.
bool matchesAt(int column, const QString &match) const
Returns true, if match equals to the text at position column, otherwise returns false.
int nextNonSpaceChar(int pos) const
Find the position of the next char that is not a space.
int fromVirtualColumn(int column, int tabWidth) const
Returns the "real" column where each tab only counts one character.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
bool remove(const QString &column, const QVariant &value)
KCALUTILS_EXPORT QString mimeType()
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT FileCopyJob * file_copy(const QUrl &src, const QUrl &dest, int permissions=-1, JobFlags flags=DefaultFlags)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
ButtonCode warningTwoActionsCancel(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const KGuiItem &cancelAction=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
QStringView merge(QStringView lhs, QStringView rhs)
bool isValid(QStringView ifopt)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QAction * saveAs(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & end()
const QList< QKeySequence > & save()
const QList< QKeySequence > & help()
KCOREADDONS_EXPORT QString csqueeze(const QString &str, int maxlen=40)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
@ CaseInsensitive
Ignores cases, e.g. "a" matches "A".
@ Regex
Treats the pattern as a regular expression.
@ Backwards
Searches in backward direction.
@ EscapeSequences
Plaintext mode: Processes escape sequences.
@ WholeWords
Plaintext mode: Whole words only, e.g. not "amp" in "example".
void triggered(bool checked)
QByteArray & append(QByteArrayView data)
bool isEmpty() const const
qsizetype size() const const
QByteArray toHex(char separator) const const
bool isLowSurrogate(char32_t ucs4)
bool isNull() const const
bool isSpace(char32_t ucs4)
char32_t mirroredChar(char32_t ucs4)
char toLatin1() const const
char32_t toUpper(char32_t ucs4)
QColor fromString(QAnyStringView name)
bool isValid() const const
bool copy(const QString &fileName, const QString &newName)
bool exists() const const
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QString canonicalFilePath() const const
bool isSymLink() const const
bool testFlag(Enum flag) const const
iterator find(const Key &key)
T value(const Key &key) const const
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype count() const const
bool isEmpty() const const
void prepend(parameter_type value)
void push_back(parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
QString toLower(const QString &str) const const
QString toUpper(const QString &str) const const
QMimeType mimeTypeForData(QIODevice *device) const const
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const const
QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const const
bool disconnect(const QMetaObject::Connection &connection)
void setParent(QObject *parent)
int exitCode() const const
void setWorkingDirectory(const QString &dir)
void start(OpenMode mode)
bool waitForFinished(int msecs)
bool waitForStarted(int msecs)
UnanchoredWildcardConversion
QString wildcardToRegularExpression(QStringView pattern, WildcardConversionOptions options)
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
QString findExecutable(const QString &executableName, const QStringList &paths)
qsizetype count() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString repeated(qsizetype times) const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
void reserve(qsizetype size)
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
QString toLower() const const
QString toUpper() const const
QString trimmed() const const
QString join(QChar separator) const const
QStringView mid(qsizetype start, qsizetype length) const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
virtual QString fileName() const const override
int nextCursorPosition(int oldPos, CursorMode mode) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl adjusted(FormattingOptions options) const const
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isLocalFile() const const
bool isValid() const const
QString path(ComponentFormattingOptions options) const const
QString toString(FormattingOptions options) const const
const_iterator cbegin() const const
const_iterator cend() const const
void reserve(qsizetype size)