aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Ziller <git@eikeziller.de>2017-09-25 11:57:34 +0200
committerEike Ziller <git@eikeziller.de>2017-10-01 20:11:08 +0200
commit3f1f50c44847a0e76a95507c9566a35dfe809a78 (patch)
treeb9bc03a93a0743d7f7b5568f3e0c04b74c30b08e
parent233678df4307588d6c748fbf4463674fd4a58268 (diff)
Support "Follow Symbol Under Cursor"
-rw-r--r--plugins/haskell/followsymbol.cpp138
-rw-r--r--plugins/haskell/followsymbol.h94
-rw-r--r--plugins/haskell/haskell.pro8
-rw-r--r--plugins/haskell/haskelleditorfactory.cpp5
-rw-r--r--plugins/haskell/haskelleditorwidget.cpp86
-rw-r--r--plugins/haskell/haskelleditorwidget.h55
-rw-r--r--plugins/haskell/haskellhoverhandler.cpp38
-rw-r--r--plugins/haskell/haskellmanager.cpp6
-rw-r--r--plugins/haskell/haskellmanager.h2
9 files changed, 403 insertions, 29 deletions
diff --git a/plugins/haskell/followsymbol.cpp b/plugins/haskell/followsymbol.cpp
new file mode 100644
index 0000000..1f06467
--- /dev/null
+++ b/plugins/haskell/followsymbol.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "followsymbol.h"
+
+#include "haskelleditorwidget.h"
+#include "haskellmanager.h"
+#include "haskelltokenizer.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <texteditor/codeassist/assistinterface.h>
+#include <texteditor/codeassist/assistproposalitem.h>
+#include <texteditor/codeassist/genericproposal.h>
+#include <texteditor/codeassist/genericproposalmodel.h>
+
+#include <utils/qtcassert.h>
+
+using namespace TextEditor;
+using namespace Utils;
+
+namespace Haskell {
+namespace Internal {
+
+IAssistProvider::RunType FollowSymbolAssistProvider::runType() const
+{
+ return AsynchronousWithThread;
+}
+
+IAssistProcessor *FollowSymbolAssistProvider::createProcessor() const
+{
+ return new FollowSymbolAssistProcessor(m_inNextSplit);
+}
+
+void FollowSymbolAssistProvider::setOpenInNextSplit(bool inNextSplit)
+{
+ m_inNextSplit = inNextSplit;
+}
+
+FollowSymbolAssistProcessor::FollowSymbolAssistProcessor(bool inNextSplit)
+ : m_inNextSplit(inNextSplit)
+{
+}
+
+IAssistProposal *FollowSymbolAssistProcessor::immediateProposal(const AssistInterface *interface)
+{
+ int line, column;
+ const optional<Token> symbol
+ = HaskellEditorWidget::symbolAt(interface->textDocument(), interface->position(),
+ &line, &column);
+ QTC_ASSERT(symbol, return nullptr); // should have been checked before
+ const auto filePath = FileName::fromString(interface->fileName());
+ m_ghcmod = HaskellManager::ghcModForFile(filePath);
+ m_symbolFuture = m_ghcmod->findSymbol(filePath, symbol->text.toString());
+
+ auto item = new AssistProposalItem();
+ item->setText(HaskellManager::trLookingUp(symbol->text.toString()));
+ item->setData(QString());
+ item->setOrder(-1000);
+
+ auto proposal = new GenericProposal(interface->position(), {item});
+ proposal->setFragile(true);
+ return proposal;
+}
+
+IAssistProposal *FollowSymbolAssistProcessor::perform(const AssistInterface *interface)
+{
+ const int position = interface->position();
+ delete interface;
+ const optional<SymbolInfo> info = m_symbolFuture.result();
+ auto item = new FollowSymbolAssistProposalItem(m_ghcmod->basePath(), info, m_inNextSplit);
+ return new InstantProposal(position, {item});
+}
+
+FollowSymbolAssistProposalItem::FollowSymbolAssistProposalItem(const FileName &basePath,
+ const optional<SymbolInfo> &info,
+ bool inNextSplit)
+ : m_basePath(basePath),
+ m_inNextSplit(inNextSplit)
+{
+ if (info && !info->file.isEmpty()) {
+ m_info = info;
+ setText(m_basePath.toString() + '/' + m_info->file.toString());
+ }
+}
+
+void FollowSymbolAssistProposalItem::apply(TextDocumentManipulatorInterface &, int) const
+{
+ if (m_info)
+ Core::EditorManager::openEditorAt(m_basePath.toString() + '/' + m_info->file.toString(),
+ m_info->line, m_info->col - 1, Core::Id(),
+ m_inNextSplit ? Core::EditorManager::OpenInOtherSplit
+ : Core::EditorManager::NoFlags);
+}
+
+void InstantActivationProposalWidget::showProposal(const QString &prefix)
+{
+ if (model() && model()->size() == 1) {
+ emit proposalItemActivated(model()->proposalItem(0));
+ deleteLater();
+ return;
+ }
+ GenericProposalWidget::showProposal(prefix);
+}
+
+InstantProposal::InstantProposal(int cursorPos, const QList<AssistProposalItemInterface *> &items)
+ : GenericProposal(cursorPos, items)
+{
+}
+
+IAssistProposalWidget *InstantProposal::createWidget() const
+{
+ return new InstantActivationProposalWidget();
+}
+
+} // namespace Internal
+} // namespace Haskell
diff --git a/plugins/haskell/followsymbol.h b/plugins/haskell/followsymbol.h
new file mode 100644
index 0000000..2e8f454
--- /dev/null
+++ b/plugins/haskell/followsymbol.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ghcmod.h"
+
+#include <texteditor/codeassist/assistproposalitem.h>
+#include <texteditor/codeassist/genericproposal.h>
+#include <texteditor/codeassist/genericproposalwidget.h>
+#include <texteditor/codeassist/iassistprocessor.h>
+#include <texteditor/codeassist/iassistprovider.h>
+
+namespace Haskell {
+namespace Internal {
+
+class FollowSymbolAssistProposalItem : public TextEditor::AssistProposalItem
+{
+public:
+ FollowSymbolAssistProposalItem(const Utils::FileName &basePath,
+ const Utils::optional<SymbolInfo> &info,
+ bool inNextSplit);
+
+ void apply(TextEditor::TextDocumentManipulatorInterface &, int) const override;
+
+private:
+ Utils::FileName m_basePath;
+ Utils::optional<SymbolInfo> m_info;
+ bool m_inNextSplit;
+};
+
+class InstantActivationProposalWidget : public TextEditor::GenericProposalWidget
+{
+protected:
+ void showProposal(const QString &prefix) override;
+};
+
+class InstantProposal : public TextEditor::GenericProposal
+{
+public:
+ InstantProposal(int cursorPos, const QList<TextEditor::AssistProposalItemInterface *> &items);
+
+ TextEditor::IAssistProposalWidget *createWidget() const override;
+};
+
+class FollowSymbolAssistProvider : public TextEditor::IAssistProvider
+{
+public:
+ RunType runType() const override;
+ TextEditor::IAssistProcessor *createProcessor() const override;
+ void setOpenInNextSplit(bool inNextSplit);
+
+private:
+ bool m_inNextSplit = false;
+};
+
+class FollowSymbolAssistProcessor : public TextEditor::IAssistProcessor
+{
+public:
+ FollowSymbolAssistProcessor(bool inNextSplit);
+
+ TextEditor::IAssistProposal *immediateProposal(const TextEditor::AssistInterface *interface) override;
+ TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
+
+private:
+ std::shared_ptr<AsyncGhcMod> m_ghcmod;
+ QFuture<Utils::optional<SymbolInfo>> m_symbolFuture;
+ bool m_inNextSplit;
+};
+
+} // namespace Internal
+} // namespace Haskell
diff --git a/plugins/haskell/haskell.pro b/plugins/haskell/haskell.pro
index 8f5bbc5..2c9739a 100644
--- a/plugins/haskell/haskell.pro
+++ b/plugins/haskell/haskell.pro
@@ -13,7 +13,9 @@ SOURCES += \
haskellmanager.cpp \
haskelldocument.cpp \
optionspage.cpp \
- filecache.cpp
+ filecache.cpp \
+ haskelleditorwidget.cpp \
+ followsymbol.cpp
HEADERS += \
haskell_global.h \
@@ -28,7 +30,9 @@ HEADERS += \
haskellmanager.h \
haskelldocument.h \
optionspage.h \
- filecache.h
+ filecache.h \
+ haskelleditorwidget.h \
+ followsymbol.h
## uncomment to build plugin into user config directory
## <localappdata>/plugins/<ideversion>
diff --git a/plugins/haskell/haskelleditorfactory.cpp b/plugins/haskell/haskelleditorfactory.cpp
index e1d245f..8e5d837 100644
--- a/plugins/haskell/haskelleditorfactory.cpp
+++ b/plugins/haskell/haskelleditorfactory.cpp
@@ -28,6 +28,7 @@
#include "haskellcompletionassist.h"
#include "haskellconstants.h"
#include "haskelldocument.h"
+#include "haskelleditorwidget.h"
#include "haskellhighlighter.h"
#include "haskellhoverhandler.h"
@@ -44,9 +45,11 @@ HaskellEditorFactory::HaskellEditorFactory()
setId(Constants::C_HASKELLEDITOR_ID);
setDisplayName(QCoreApplication::translate("OpenWith::Editors", "Haskell Editor"));
addMimeType("text/x-haskell");
- setEditorActionHandlers(TextEditor::TextEditorActionHandler::UnCommentSelection);
+ setEditorActionHandlers(TextEditor::TextEditorActionHandler::UnCommentSelection
+ | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor);
addHoverHandler(new HaskellHoverHandler);
setDocumentCreator([] { return new HaskellDocument(); });
+ setEditorWidgetCreator([] { return new HaskellEditorWidget; });
setCommentDefinition(Utils::CommentDefinition("--", "{-", "-}"));
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
diff --git a/plugins/haskell/haskelleditorwidget.cpp b/plugins/haskell/haskelleditorwidget.cpp
new file mode 100644
index 0000000..785f4e0
--- /dev/null
+++ b/plugins/haskell/haskelleditorwidget.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "haskelleditorwidget.h"
+
+#include "haskelltokenizer.h"
+
+#include <utils/textutils.h>
+
+#include <QTextBlock>
+
+using namespace TextEditor;
+
+namespace Haskell {
+namespace Internal {
+
+HaskellEditorWidget::HaskellEditorWidget(QWidget *parent)
+ : TextEditorWidget(parent)
+{
+}
+
+Utils::optional<Token> HaskellEditorWidget::symbolAt(QTextDocument *doc, int position,
+ int *line, int *column)
+{
+ Utils::Text::convertPosition(doc, position, line, column);
+ if (*line < 0 || *column < 0)
+ return Utils::nullopt;
+ const QTextBlock block = doc->findBlockByNumber(*line - 1);
+ if (block.text().isEmpty())
+ return Utils::nullopt;
+ const int startState = block.previous().isValid() ? block.previous().userState() : -1;
+ const Tokens tokens = HaskellTokenizer::tokenize(block.text(), startState);
+ const Token token = tokens.tokenAtColumn(*column);
+ if (token.isValid()
+ && (token.type == TokenType::Variable
+ || token.type == TokenType::Operator
+ || token.type == TokenType::Constructor
+ || token.type == TokenType::OperatorConstructor)) {
+ return token;
+ }
+ return Utils::nullopt;
+}
+
+TextEditorWidget::Link HaskellEditorWidget::findLinkAt(const QTextCursor &cursor,
+ bool resolveTarget, bool inNextSplit)
+{
+ int line, column;
+ const Utils::optional<Token> symbol = symbolAt(document(), cursor.position(), &line, &column);
+ if (symbol) {
+ const QTextBlock block = document()->findBlockByNumber(line - 1);
+ Link link;
+ link.linkTextStart = block.position() + symbol->startCol;
+ link.linkTextEnd = link.linkTextStart + symbol->length;
+ if (resolveTarget) {
+ m_followSymbolAssistProvider.setOpenInNextSplit(inNextSplit);
+ invokeAssist(FollowSymbol, &m_followSymbolAssistProvider);
+ }
+ return link;
+ }
+ return Link();
+}
+
+} // namespace Internal
+} // namespace Haskell
diff --git a/plugins/haskell/haskelleditorwidget.h b/plugins/haskell/haskelleditorwidget.h
new file mode 100644
index 0000000..f8b2102
--- /dev/null
+++ b/plugins/haskell/haskelleditorwidget.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "followsymbol.h"
+
+#include <texteditor/texteditor.h>
+#include <utils/optional.h>
+
+namespace Haskell {
+namespace Internal {
+
+class Token;
+
+class HaskellEditorWidget : public TextEditor::TextEditorWidget
+{
+public:
+ HaskellEditorWidget(QWidget *parent = 0);
+
+ static Utils::optional<Token> symbolAt(QTextDocument *doc, int position,
+ int *line, int *column);
+
+protected:
+ Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true,
+ bool inNextSplit = false) override;
+
+private:
+ FollowSymbolAssistProvider m_followSymbolAssistProvider;
+};
+
+} // namespace Internal
+} // namespace Haskell
diff --git a/plugins/haskell/haskellhoverhandler.cpp b/plugins/haskell/haskellhoverhandler.cpp
index 5e03e32..f418170 100644
--- a/plugins/haskell/haskellhoverhandler.cpp
+++ b/plugins/haskell/haskellhoverhandler.cpp
@@ -26,6 +26,8 @@
#include "haskellhoverhandler.h"
#include "haskelldocument.h"
+#include "haskelleditorwidget.h"
+#include "haskellmanager.h"
#include "haskelltokenizer.h"
#include <texteditor/textdocument.h>
@@ -35,11 +37,6 @@
#include <utils/synchronousprocess.h>
#include <utils/tooltip/tooltip.h>
-#include <QTextBlock>
-#include <QTextDocument>
-
-#include <functional>
-
using namespace Utils;
static QString toCode(const QString &s)
@@ -77,27 +74,16 @@ void HaskellHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidg
{
cancel();
m_name.clear();
- editorWidget->convertPosition(pos, &m_line, &m_col);
- if (m_line < 0 || m_col < 0)
- return;
- QTextBlock block = editorWidget->document()->findBlockByNumber(m_line - 1);
- if (block.text().isEmpty())
- return;
- m_filePath = editorWidget->textDocument()->filePath();
- const int startState = block.previous().isValid() ? block.previous().userState() : -1;
- const Tokens tokens = HaskellTokenizer::tokenize(block.text(), startState);
- const Token token = tokens.tokenAtColumn(m_col);
- if (token.isValid()
- && (token.type == TokenType::Variable
- || token.type == TokenType::Operator
- || token.type == TokenType::Constructor
- || token.type == TokenType::OperatorConstructor)) {
- m_name = token.text.toString();
- }
- if (m_name.isEmpty())
- setPriority(-1);
- else
+ m_filePath.clear();
+ const Utils::optional<Token> token = HaskellEditorWidget::symbolAt(editorWidget->document(),
+ pos, &m_line, &m_col);
+ if (token) {
+ m_filePath = editorWidget->textDocument()->filePath();
+ m_name = token->text.toString();
setPriority(Priority_Tooltip);
+ } else {
+ setPriority(-1);
+ }
}
static void tryShowToolTip(const QPointer<QWidget> &widget, const QPoint &point,
@@ -125,7 +111,7 @@ void HaskellHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWid
Utils::ToolTip::hide();
return;
}
- Utils::ToolTip::show(point, tr("Looking up \"%1\"").arg(m_name), editorWidget);
+ Utils::ToolTip::show(point, HaskellManager::trLookingUp(m_name), editorWidget);
QPointer<QWidget> widget = editorWidget;
std::shared_ptr<AsyncGhcMod> ghcmod;
diff --git a/plugins/haskell/haskellmanager.cpp b/plugins/haskell/haskellmanager.cpp
index 0867edf..a0ab153 100644
--- a/plugins/haskell/haskellmanager.cpp
+++ b/plugins/haskell/haskellmanager.cpp
@@ -29,6 +29,7 @@
#include <utils/hostosinfo.h>
+#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QSettings>
@@ -126,5 +127,10 @@ void HaskellManager::writeSettings(QSettings *settings)
settings->setValue(kStackExecutableKey, m_d->stackExecutable.toString());
}
+QString HaskellManager::trLookingUp(const QString &name)
+{
+ return QCoreApplication::translate("HaskellManager", "Looking up \"%1\"...").arg(name);
+}
+
} // namespace Internal
} // namespace Haskell
diff --git a/plugins/haskell/haskellmanager.h b/plugins/haskell/haskellmanager.h
index 87c08e8..aea4106 100644
--- a/plugins/haskell/haskellmanager.h
+++ b/plugins/haskell/haskellmanager.h
@@ -52,6 +52,8 @@ public:
static void readSettings(QSettings *settings);
static void writeSettings(QSettings *settings);
+ static QString trLookingUp(const QString &name);
+
signals:
void stackExecutableChanged(const Utils::FileName &filePath);
};