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