File indexing completed on 2024-04-21 05:49:03
0001 /* 0002 SPDX-FileCopyrightText: 2021 Ilia Kats <ilia-kats@gmx.net> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "completionmodel.h" 0008 #include "completiontable.h" 0009 0010 #include <algorithm> 0011 #include <string> 0012 0013 #include <QIcon> 0014 0015 #include <KTextEditor/Document> 0016 #include <KTextEditor/View> 0017 0018 bool startsWith(const Completion &comp, const std::u16string &prefix) 0019 { 0020 if (prefix.size() <= comp.completion_strlen) 0021 return std::char_traits<char16_t>::compare(prefix.data(), comp.completion, prefix.size()) == 0; 0022 return false; 0023 } 0024 0025 LatexCompletionModel::LatexCompletionModel(QObject *parent) 0026 : KTextEditor::CodeCompletionModel(parent) 0027 { 0028 } 0029 0030 void LatexCompletionModel::completionInvoked(KTextEditor::View *view, 0031 const KTextEditor::Range &range, 0032 KTextEditor::CodeCompletionModel::InvocationType invocationType) 0033 { 0034 Q_UNUSED(invocationType); 0035 beginResetModel(); 0036 m_matches.first = m_matches.second = -1; 0037 auto word = view->document()->text(range).toStdU16String(); 0038 const Completion *beginit = (Completion *)&completiontable; 0039 const Completion *endit = beginit + n_completions; 0040 if (!word.empty() && word[0] == QLatin1Char('\\')) { 0041 auto prefixrangestart = std::lower_bound(beginit, endit, word, [](const Completion &a, const std::u16string &b) -> bool { 0042 return startsWith(a, b) ? false : a.completion < b; 0043 }); 0044 auto prefixrangeend = std::upper_bound(beginit, endit, word, [](const std::u16string &a, const Completion &b) -> bool { 0045 return startsWith(b, a) ? false : a < b.completion; 0046 }); 0047 if (prefixrangestart != endit) { 0048 m_matches.first = prefixrangestart - beginit; 0049 m_matches.second = prefixrangeend - beginit; 0050 } 0051 } 0052 setRowCount(m_matches.second - m_matches.first); 0053 endResetModel(); 0054 } 0055 0056 bool LatexCompletionModel::shouldStartCompletion(KTextEditor::View *view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position) 0057 { 0058 Q_UNUSED(view); 0059 Q_UNUSED(position); 0060 return userInsertion && latexexpr.match(insertedText).hasMatch(); 0061 } 0062 0063 bool LatexCompletionModel::shouldAbortCompletion(KTextEditor::View *view, const KTextEditor::Range &range, const QString ¤tCompletion) 0064 { 0065 if (view->cursorPosition() < range.start() || view->cursorPosition() > range.end()) 0066 return true; 0067 return !latexexpr.match(currentCompletion).hasMatch(); 0068 } 0069 0070 KTextEditor::Range LatexCompletionModel::completionRange(KTextEditor::View *view, const KTextEditor::Cursor &position) 0071 { 0072 auto text = view->document()->line(position.line()); 0073 KTextEditor::Cursor start = position; 0074 int pos = text.left(position.column()).lastIndexOf(latexexpr); 0075 if (pos >= 0) 0076 start.setColumn(pos); 0077 return KTextEditor::Range(start, position); 0078 } 0079 0080 void LatexCompletionModel::executeCompletionItem(KTextEditor::View *view, const KTextEditor::Range &word, const QModelIndex &index) const 0081 { 0082 view->document()->replaceText(word, data(index.sibling(index.row(), Postfix), Qt::DisplayRole).toString()); 0083 } 0084 0085 QVariant LatexCompletionModel::data(const QModelIndex &index, int role) const 0086 { 0087 if (role == UnimportantItemRole) 0088 return false; 0089 else if (role == InheritanceDepth) 0090 return 1; 0091 0092 if (index.isValid() && index.row() < m_matches.second - m_matches.first) { 0093 const Completion &completion = completiontable[m_matches.first + index.row()]; 0094 if (role == IsExpandable) 0095 return true; // if it's not expandable, the description will often be cut off 0096 // because apprarently the ItemSelected role is not taken into account 0097 // when determining the completion widget width. So expanding is 0098 // the only way to make sure that the complete description is available. 0099 else if (role == ItemSelected || role == ExpandingWidget) 0100 return QStringLiteral("<table><tr><td>%1</td><td>%2</td></tr></table>") 0101 .arg(QString::fromUtf16(completion.codepoint), QString::fromUtf16(completion.name)); 0102 else if (role == Qt::DisplayRole) { 0103 if (index.column() == Name) 0104 return QString::fromUtf16(completion.completion); 0105 else if (index.column() == Postfix) 0106 return QString::fromUtf16(completion.chars); 0107 } else if (index.column() == Icon && role == Qt::DecorationRole) { 0108 static const QIcon icon(QIcon::fromTheme(QStringLiteral("texcompiler"))); 0109 return icon; 0110 } 0111 } 0112 return QVariant(); 0113 } 0114 0115 #include "moc_completionmodel.cpp"