CMakePM: Add hover help for CMakeEditor

Fixes: QTCREATORBUG-25780
Change-Id: I954023f701e6c1c6ca5e25190b37f8b9a8becfb5
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Cristian Adam
2023-09-18 22:08:23 +02:00
parent 05614ab740
commit cd94514dbb
2 changed files with 95 additions and 9 deletions

View File

@@ -9,6 +9,7 @@
#include "cmakefilecompletionassist.h" #include "cmakefilecompletionassist.h"
#include "cmakeindenter.h" #include "cmakeindenter.h"
#include "cmakekitaspect.h" #include "cmakekitaspect.h"
#include "cmakeproject.h"
#include "cmakeprojectconstants.h" #include "cmakeprojectconstants.h"
#include "3rdparty/cmake/cmListFileCache.h" #include "3rdparty/cmake/cmListFileCache.h"
@@ -19,11 +20,14 @@
#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h> #include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h> #include <projectexplorer/projecttree.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <texteditor/basehoverhandler.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditoractionhandler.h>
#include <utils/textutils.h> #include <utils/textutils.h>
#include <utils/tooltip/tooltip.h>
#include <QTextDocument> #include <QTextDocument>
@@ -31,6 +35,7 @@
using namespace Core; using namespace Core;
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Utils;
using namespace TextEditor; using namespace TextEditor;
namespace CMakeProjectManager::Internal { namespace CMakeProjectManager::Internal {
@@ -308,6 +313,83 @@ static TextDocument *createCMakeDocument()
return doc; return doc;
} }
//
// CMakeHoverHandler
//
class CMakeHoverHandler : public TextEditor::BaseHoverHandler
{
mutable CMakeKeywords m_keywords;
QString m_helpToolTip;
public:
const CMakeKeywords &keywords() const;
void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
int pos,
ReportPriority report) final;
void operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) final;
};
const CMakeKeywords &CMakeHoverHandler::keywords() const
{
if (m_keywords.functions.isEmpty()) {
CMakeTool *tool = nullptr;
if (auto bs = ProjectTree::currentBuildSystem())
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
if (!tool)
tool = CMakeToolManager::defaultCMakeTool();
if (tool)
m_keywords = tool->keywords();
}
return m_keywords;
}
QString readFirstParagraphs(const QString &element, const FilePath &helpFile);
void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
int pos,
ReportPriority report)
{
const QScopeGuard cleanup([this, report] { report(priority()); });
QTextCursor cursor = editorWidget->textCursor();
cursor.setPosition(pos);
const QString word = Utils::Text::wordUnderCursor(cursor);
FilePath helpFile;
for (const auto &map : {keywords().functions,
keywords().variables,
keywords().directoryProperties,
keywords().sourceProperties,
keywords().targetProperties,
keywords().testProperties,
keywords().properties,
keywords().includeStandardModules,
keywords().findModules,
keywords().policies}) {
if (map.contains(word)) {
helpFile = map.value(word);
break;
}
}
m_helpToolTip.clear();
if (!helpFile.isEmpty())
m_helpToolTip = readFirstParagraphs(word, helpFile);
setPriority(m_helpToolTip.isEmpty() ? Priority_Tooltip : Priority_None);
}
void CMakeHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
{
if (!m_helpToolTip.isEmpty())
Utils::ToolTip::show(point, m_helpToolTip, Qt::MarkdownText, editorWidget);
else
Utils::ToolTip::hide();
}
// //
// CMakeEditorFactory // CMakeEditorFactory
// //
@@ -334,6 +416,8 @@ CMakeEditorFactory::CMakeEditorFactory()
| TextEditorActionHandler::FollowSymbolUnderCursor | TextEditorActionHandler::FollowSymbolUnderCursor
| TextEditorActionHandler::Format); | TextEditorActionHandler::Format);
addHoverHandler(new CMakeHoverHandler);
ActionContainer *contextMenu = ActionManager::createMenu(Constants::M_CONTEXT); ActionContainer *contextMenu = ActionManager::createMenu(Constants::M_CONTEXT);
contextMenu->addAction(ActionManager::command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR)); contextMenu->addAction(ActionManager::command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR));
contextMenu->addSeparator(Context(Constants::CMAKE_EDITOR_ID)); contextMenu->addSeparator(Context(Constants::CMAKE_EDITOR_ID));

View File

@@ -155,14 +155,21 @@ QList<AssistProposalItemInterface *> generateList(const T &words, const QIcon &i
QString readFirstParagraphs(const QString &element, const FilePath &helpFile) QString readFirstParagraphs(const QString &element, const FilePath &helpFile)
{ {
static QMap<FilePath, QString> map;
if (map.contains(helpFile))
return map.value(helpFile);
auto content = helpFile.fileContents(1024).value_or(QByteArray()); auto content = helpFile.fileContents(1024).value_or(QByteArray());
return QString("```\n%1\n```").arg(QString::fromUtf8(content.left(content.lastIndexOf("\n")))); const QString firstParagraphs
= QString("```\n%1\n```").arg(QString::fromUtf8(content.left(content.lastIndexOf("\n"))));
map[helpFile] = firstParagraphs;
return firstParagraphs;
} }
QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath> &words, QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath> &words,
const QIcon &icon) const QIcon &icon)
{ {
static QMap<FilePath, QString> map;
struct MarkDownAssitProposalItem : public AssistProposalItem struct MarkDownAssitProposalItem : public AssistProposalItem
{ {
Qt::TextFormat detailFormat() const { return Qt::MarkdownText; } Qt::TextFormat detailFormat() const { return Qt::MarkdownText; }
@@ -172,13 +179,8 @@ QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath>
for (const auto &[text, helpFile] : words.asKeyValueRange()) { for (const auto &[text, helpFile] : words.asKeyValueRange()) {
MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem(); MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem();
item->setText(text); item->setText(text);
if (!helpFile.isEmpty())
if (!helpFile.isEmpty()) { item->setDetail(readFirstParagraphs(text, helpFile));
if (!map.contains(helpFile))
map[helpFile] = readFirstParagraphs(text, helpFile);
item->setDetail(map.value(helpFile));
}
item->setIcon(icon); item->setIcon(icon);
list << item; list << item;
}; };