1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "profilehoverhandler.h"
27 #include "profilecompletionassist.h"
28 #include "qmakeprojectmanagerconstants.h"
29
30 #include <coreplugin/helpmanager.h>
31 #include <texteditor/texteditor.h>
32 #include <utils/htmldocextractor.h>
33 #include <utils/executeondestruction.h>
34
35 #include <QTextBlock>
36 #include <QUrl>
37
38 using namespace Core;
39
40 namespace QmakeProjectManager {
41 namespace Internal {
42
ProFileHoverHandler()43 ProFileHoverHandler::ProFileHoverHandler()
44 : m_keywords(qmakeKeywords())
45 {
46 }
47
identifyMatch(TextEditor::TextEditorWidget * editorWidget,int pos,ReportPriority report)48 void ProFileHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
49 int pos,
50 ReportPriority report)
51 {
52 Utils::ExecuteOnDestruction reportPriority([this, report](){ report(priority()); });
53
54 m_docFragment.clear();
55 m_manualKind = UnknownManual;
56 if (!editorWidget->extraSelectionTooltip(pos).isEmpty()) {
57 setToolTip(editorWidget->extraSelectionTooltip(pos));
58 } else {
59 QTextDocument *document = editorWidget->document();
60 QTextBlock block = document->findBlock(pos);
61 identifyQMakeKeyword(block.text(), pos - block.position());
62
63 if (m_manualKind != UnknownManual) {
64 QUrl url(QString::fromLatin1("qthelp://org.qt-project.qmake/qmake/qmake-%1-reference.html#%2")
65 .arg(manualName()).arg(m_docFragment));
66 setLastHelpItemIdentified(
67 Core::HelpItem(url, m_docFragment, Core::HelpItem::QMakeVariableOfFunction));
68 } else {
69 // General qmake manual will be shown outside any function or variable
70 setLastHelpItemIdentified("qmake");
71 }
72 }
73 }
74
identifyQMakeKeyword(const QString & text,int pos)75 void ProFileHoverHandler::identifyQMakeKeyword(const QString &text, int pos)
76 {
77 if (text.isEmpty())
78 return;
79
80 QString buf;
81
82 for (int i = 0; i < text.length(); ++i) {
83 const QChar c = text.at(i);
84 bool checkBuffer = false;
85 if (c.isLetter() || c == QLatin1Char('_') || c == QLatin1Char('.') || c.isDigit()) {
86 buf += c;
87 if (i == text.length() - 1)
88 checkBuffer = true;
89 } else {
90 checkBuffer = true;
91 }
92 if (checkBuffer) {
93 if (!buf.isEmpty()) {
94 if ((i >= pos) && (i - buf.size() <= pos)) {
95 if (m_keywords.isFunction(buf))
96 identifyDocFragment(FunctionManual, buf);
97 else if (m_keywords.isVariable(buf))
98 identifyDocFragment(VariableManual, buf);
99 break;
100 }
101 buf.clear();
102 } else {
103 if (i >= pos)
104 break; // we are after the tooltip pos
105 }
106 if (c == QLatin1Char('#'))
107 break; // comment start
108 }
109 }
110 }
111
manualName() const112 QString ProFileHoverHandler::manualName() const
113 {
114 if (m_manualKind == FunctionManual)
115 return QLatin1String("function");
116 else if (m_manualKind == VariableManual)
117 return QLatin1String("variable");
118 return QString();
119 }
120
identifyDocFragment(ProFileHoverHandler::ManualKind manualKind,const QString & keyword)121 void ProFileHoverHandler::identifyDocFragment(ProFileHoverHandler::ManualKind manualKind,
122 const QString &keyword)
123 {
124 m_manualKind = manualKind;
125 m_docFragment = keyword.toLower();
126 // Special case: _PRO_FILE_ and _PRO_FILE_PWD_ ids
127 // don't have starting and ending '_'.
128 if (m_docFragment.startsWith(QLatin1Char('_')))
129 m_docFragment = m_docFragment.mid(1);
130 if (m_docFragment.endsWith(QLatin1Char('_')))
131 m_docFragment = m_docFragment.left(m_docFragment.size() - 1);
132 m_docFragment.replace(QLatin1Char('.'), QLatin1Char('-'));
133 m_docFragment.replace(QLatin1Char('_'), QLatin1Char('-'));
134
135 if (m_manualKind == FunctionManual) {
136 QUrl url(QString::fromLatin1("qthelp://org.qt-project.qmake/qmake/qmake-%1-reference.html").arg(manualName()));
137 const QByteArray html = Core::HelpManager::fileData(url);
138
139 Utils::HtmlDocExtractor htmlExtractor;
140 htmlExtractor.setMode(Utils::HtmlDocExtractor::FirstParagraph);
141
142 // Document fragment of qmake function is retrieved from docs.
143 // E.g. in case of the keyword "find" the document fragment
144 // parsed from docs is "find-variablename-substr".
145 m_docFragment = htmlExtractor.getQMakeFunctionId(QString::fromUtf8(html), m_docFragment);
146 }
147 }
148
149 } // namespace Internal
150 } // namespace QmakeProjectManager
151