textviewwindowhelper.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. #ifndef TEXTVIEWWINDOWHELPER_H
  2. #define TEXTVIEWWINDOWHELPER_H
  3. #include <QFileInfo>
  4. #include <QTextCursor>
  5. #include <QRegularExpression>
  6. #include <QTextBlock>
  7. #include <QSharedPointer>
  8. #include <vtextedit/texteditorconfig.h>
  9. #include <core/texteditorconfig.h>
  10. #include <core/configmgr.h>
  11. #include <utils/widgetutils.h>
  12. #include <snippet/snippetmgr.h>
  13. #include <search/searchtoken.h>
  14. #include "quickselector.h"
  15. namespace vte
  16. {
  17. class ViConfig;
  18. }
  19. namespace vnotex
  20. {
  21. class TextEditorConfig;
  22. // Abstract some common logics for TextViewWindow and MarkdownViewWindow.
  23. class TextViewWindowHelper
  24. {
  25. public:
  26. TextViewWindowHelper() = delete;
  27. template <typename _ViewWindow>
  28. static void connectEditor(_ViewWindow *p_win)
  29. {
  30. auto editor = p_win->m_editor;
  31. p_win->connect(editor, &vte::VTextEditor::focusIn,
  32. p_win, [p_win]() {
  33. emit p_win->focused(p_win);
  34. });
  35. p_win->connect(editor->getTextEdit(), &vte::VTextEdit::contentsChanged,
  36. p_win, [p_win, editor]() {
  37. if (p_win->m_propogateEditorToBuffer) {
  38. p_win->getBuffer()->setModified(editor->isModified());
  39. p_win->getBuffer()->invalidateContent(
  40. p_win, [p_win](int p_revision) {
  41. p_win->setBufferRevisionAfterInvalidation(p_revision);
  42. });
  43. }
  44. });
  45. }
  46. template <typename _ViewWindow>
  47. static void handleBufferChanged(_ViewWindow *p_win)
  48. {
  49. p_win->m_propogateEditorToBuffer = false;
  50. p_win->syncEditorFromBuffer();
  51. p_win->m_propogateEditorToBuffer = true;
  52. emit p_win->statusChanged();
  53. emit p_win->modeChanged();
  54. }
  55. static QSharedPointer<vte::TextEditorConfig> createTextEditorConfig(const TextEditorConfig &p_config,
  56. const QSharedPointer<vte::ViConfig> &p_viConfig,
  57. const QString &p_themeFile,
  58. const QString &p_syntaxTheme,
  59. LineEndingPolicy p_lineEndingPolicy)
  60. {
  61. auto editorConfig = QSharedPointer<vte::TextEditorConfig>::create();
  62. editorConfig->m_viConfig = p_viConfig;
  63. if (!p_themeFile.isEmpty()) {
  64. editorConfig->m_theme = vte::Theme::createThemeFromFile(p_themeFile);
  65. }
  66. editorConfig->m_syntaxTheme = p_syntaxTheme;
  67. switch (p_config.getLineNumberType()) {
  68. case TextEditorConfig::LineNumberType::Absolute:
  69. editorConfig->m_lineNumberType = vte::VTextEditor::LineNumberType::Absolute;
  70. break;
  71. case TextEditorConfig::LineNumberType::Relative:
  72. editorConfig->m_lineNumberType = vte::VTextEditor::LineNumberType::Relative;
  73. break;
  74. case TextEditorConfig::LineNumberType::None:
  75. editorConfig->m_lineNumberType = vte::VTextEditor::LineNumberType::None;
  76. break;
  77. }
  78. editorConfig->m_textFoldingEnabled = p_config.getTextFoldingEnabled();
  79. switch (p_config.getInputMode()) {
  80. case TextEditorConfig::InputMode::ViMode:
  81. editorConfig->m_inputMode = vte::InputMode::ViMode;
  82. break;
  83. default:
  84. editorConfig->m_inputMode = vte::InputMode::NormalMode;
  85. break;
  86. }
  87. editorConfig->m_scaleFactor = WidgetUtils::calculateScaleFactor();
  88. switch (p_config.getCenterCursor()) {
  89. case TextEditorConfig::CenterCursor::NeverCenter:
  90. editorConfig->m_centerCursor = vte::CenterCursor::NeverCenter;
  91. break;
  92. case TextEditorConfig::CenterCursor::AlwaysCenter:
  93. editorConfig->m_centerCursor = vte::CenterCursor::AlwaysCenter;
  94. break;
  95. case TextEditorConfig::CenterCursor::CenterOnBottom:
  96. editorConfig->m_centerCursor = vte::CenterCursor::CenterOnBottom;
  97. break;
  98. }
  99. switch (p_config.getWrapMode()) {
  100. case TextEditorConfig::WrapMode::NoWrap:
  101. editorConfig->m_wrapMode = vte::WrapMode::NoWrap;
  102. break;
  103. case TextEditorConfig::WrapMode::WordWrap:
  104. editorConfig->m_wrapMode = vte::WrapMode::WordWrap;
  105. break;
  106. case TextEditorConfig::WrapMode::WrapAnywhere:
  107. editorConfig->m_wrapMode = vte::WrapMode::WrapAnywhere;
  108. break;
  109. case TextEditorConfig::WrapMode::WordWrapOrAnywhere:
  110. editorConfig->m_wrapMode = vte::WrapMode::WordWrapOrAnywhere;
  111. break;
  112. }
  113. editorConfig->m_expandTab = p_config.getExpandTabEnabled();
  114. editorConfig->m_tabStopWidth = p_config.getTabStopWidth();
  115. editorConfig->m_highlightWhitespace = p_config.getHighlightWhitespaceEnabled();
  116. switch (p_lineEndingPolicy) {
  117. case LineEndingPolicy::Platform:
  118. editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::Platform;
  119. break;
  120. case LineEndingPolicy::File:
  121. editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::File;
  122. break;
  123. case LineEndingPolicy::LF:
  124. editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::LF;
  125. break;
  126. case LineEndingPolicy::CRLF:
  127. editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::CRLF;
  128. break;
  129. case LineEndingPolicy::CR:
  130. editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::CR;
  131. break;
  132. }
  133. return editorConfig;
  134. }
  135. static vte::FindFlags toEditorFindFlags(FindOptions p_options)
  136. {
  137. vte::FindFlags flags;
  138. if (p_options & FindOption::FindBackward) {
  139. flags |= vte::FindFlag::FindBackward;
  140. }
  141. if (p_options & FindOption::CaseSensitive) {
  142. flags |= vte::FindFlag::CaseSensitive;
  143. }
  144. if (p_options & FindOption::WholeWordOnly) {
  145. flags |= vte::FindFlag::WholeWordOnly;
  146. }
  147. if (p_options & FindOption::RegularExpression) {
  148. flags |= vte::FindFlag::RegularExpression;
  149. }
  150. return flags;
  151. }
  152. template <typename _ViewWindow>
  153. static void handleFindTextChanged(_ViewWindow *p_win, const QString &p_text, FindOptions p_options)
  154. {
  155. if (p_options & FindOption::IncrementalSearch) {
  156. p_win->m_editor->peekText(p_text, toEditorFindFlags(p_options));
  157. }
  158. }
  159. template <typename _ViewWindow>
  160. static void handleFindNext(_ViewWindow *p_win, const QStringList &p_texts, FindOptions p_options)
  161. {
  162. const auto result = p_win->m_editor->findText(p_texts, toEditorFindFlags(p_options));
  163. p_win->showFindResult(p_texts, result.m_totalMatches, result.m_currentMatchIndex);
  164. }
  165. template <typename _ViewWindow>
  166. static void handleReplace(_ViewWindow *p_win, const QString &p_text, FindOptions p_options, const QString &p_replaceText)
  167. {
  168. const auto result = p_win->m_editor->replaceText(p_text, toEditorFindFlags(p_options), p_replaceText);
  169. p_win->showReplaceResult(p_text, result.m_totalMatches);
  170. }
  171. template <typename _ViewWindow>
  172. static void handleReplaceAll(_ViewWindow *p_win, const QString &p_text, FindOptions p_options, const QString &p_replaceText)
  173. {
  174. const auto result = p_win->m_editor->replaceAll(p_text, toEditorFindFlags(p_options), p_replaceText);
  175. p_win->showReplaceResult(p_text, result.m_totalMatches);
  176. }
  177. template <typename _ViewWindow>
  178. static void clearSearchHighlights(_ViewWindow *p_win)
  179. {
  180. p_win->m_editor->clearIncrementalSearchHighlight();
  181. p_win->m_editor->clearSearchHighlight();
  182. }
  183. template <typename _ViewWindow>
  184. static void applySnippet(_ViewWindow *p_win, const QString &p_name)
  185. {
  186. if (p_win->m_editor->isReadOnly() || p_name.isEmpty()) {
  187. qWarning() << "failed to apply snippet" << p_name << "to a read-only buffer";
  188. return;
  189. }
  190. SnippetMgr::getInst().applySnippet(p_name,
  191. p_win->m_editor->getTextEdit(),
  192. SnippetMgr::generateOverrides(p_win->getBuffer()));
  193. p_win->m_editor->enterInsertModeIfApplicable();
  194. p_win->showMessage(vnotex::ViewWindow::tr("Snippet applied: %1").arg(p_name));
  195. }
  196. template <typename _ViewWindow>
  197. static void applySnippet(_ViewWindow *p_win)
  198. {
  199. if (p_win->m_editor->isReadOnly()) {
  200. qWarning() << "failed to apply snippet to a read-only buffer";
  201. return;
  202. }
  203. QString snippetName;
  204. auto textEdit = p_win->m_editor->getTextEdit();
  205. if (!textEdit->hasSelection()) {
  206. // Fetch the snippet symbol containing current cursor.
  207. auto cursor = textEdit->textCursor();
  208. const auto block = cursor.block();
  209. const auto text = block.text();
  210. const int pib = cursor.positionInBlock();
  211. QRegularExpression regExp(SnippetMgr::c_snippetSymbolRegExp);
  212. QRegularExpressionMatch match;
  213. int idx = text.lastIndexOf(regExp, pib, &match);
  214. if (idx >= 0 && (idx + match.capturedLength(0) >= pib)) {
  215. // Found one symbol under current cursor.
  216. snippetName = match.captured(1);
  217. if (!SnippetMgr::getInst().find(snippetName)) {
  218. p_win->showMessage(vnotex::ViewWindow::tr("Snippet (%1) not found").arg(snippetName));
  219. return;
  220. }
  221. // Remove the symbol and apply snippet later.
  222. cursor.setPosition(block.position() + idx);
  223. cursor.setPosition(block.position() + idx + match.capturedLength(0), QTextCursor::KeepAnchor);
  224. cursor.removeSelectedText();
  225. textEdit->setTextCursor(cursor);
  226. }
  227. }
  228. if (snippetName.isEmpty()) {
  229. // Prompt for snippet.
  230. snippetName = promptForSnippet(p_win);
  231. }
  232. if (!snippetName.isEmpty()) {
  233. applySnippet(p_win, snippetName);
  234. }
  235. }
  236. template <typename _ViewWindow>
  237. static QString promptForSnippet(_ViewWindow *p_win)
  238. {
  239. const auto snippets = SnippetMgr::getInst().getSnippets();
  240. if (snippets.isEmpty()) {
  241. p_win->showMessage(vnotex::ViewWindow::tr("Snippet not available"));
  242. return QString();
  243. }
  244. QVector<QuickSelectorItem> items;
  245. for (const auto &snip : snippets) {
  246. items.push_back(QuickSelectorItem(snip->getName(),
  247. snip->getName(),
  248. snip->getDescription(),
  249. snip->getShortcutString()));
  250. }
  251. // Ownership will be transferred to showFloatingWidget().
  252. auto selector = new QuickSelector(vnotex::ViewWindow::tr("Select Snippet"),
  253. items,
  254. true,
  255. p_win);
  256. auto ret = p_win->showFloatingWidget(selector);
  257. return ret.toString();
  258. }
  259. template <typename _ViewWindow>
  260. static QPoint getFloatingWidgetPosition(_ViewWindow *p_win)
  261. {
  262. auto textEdit = p_win->m_editor->getTextEdit();
  263. auto localPos = textEdit->cursorRect().bottomRight();
  264. if (!textEdit->rect().contains(localPos)) {
  265. localPos = QPoint(5, 5);
  266. }
  267. return textEdit->mapToGlobal(localPos);
  268. }
  269. template <typename _ViewWindow>
  270. static void findTextBySearchToken(_ViewWindow *p_win, const QSharedPointer<SearchToken> &p_token, int p_currentMatchLine)
  271. {
  272. const auto patterns = p_token->toPatterns();
  273. p_win->updateLastFindInfo(patterns.first, patterns.second);
  274. const auto result = p_win->m_editor->findText(patterns.first, toEditorFindFlags(patterns.second), 0, -1, p_currentMatchLine);
  275. p_win->showFindResult(patterns.first, result.m_totalMatches, result.m_currentMatchIndex);
  276. }
  277. };
  278. }
  279. #endif