Nim: Add code completion with NimSuggest

This patch add basic support for completion by using
the NimSuggest executable.
The user is expected to configure the path to NimSuggest
by using the new tools configuration page in the Nim
settings.
For now we don't support advanced completion of function
parameters. This will be added in further patches.

Change-Id: I5097ee2b3992486071591109c400d9c84da5fc00
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
Filippo Cucchetto
2018-12-23 15:18:06 +01:00
parent 1f974bdbbf
commit 4f6ce9d614
26 changed files with 2108 additions and 22 deletions

View File

@@ -0,0 +1,287 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 "nimcompletionassistprovider.h"
#include "suggest/nimsuggestcache.h"
#include "suggest/nimsuggest.h"
#include <projectexplorer/session.h>
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/iassistprocessor.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <utils/textutils.h>
#include <utils/algorithm.h>
#include <QApplication>
#include <QDebug>
#include <QSet>
#include <QTemporaryFile>
#include <QTextStream>
#include <QTextDocument>
#include <QThread>
using namespace ProjectExplorer;
using namespace TextEditor;
namespace Nim {
bool isIdentifierChar(QChar c)
{
return c.isLetterOrNumber() || c == QLatin1Char('_');
}
bool isActivationChar(QChar c)
{
static const QSet<QChar> chars {QLatin1Char('.'), QLatin1Char('(')};
return chars.contains(c);
}
class NimCompletionAssistProcessor : public QObject, public TextEditor::IAssistProcessor
{
Q_OBJECT
public:
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) final
{
QTC_ASSERT(this->thread() == qApp->thread(), return nullptr);
if (interface->reason() == IdleEditor && !acceptsIdleEditor(interface))
return nullptr;
Suggest::NimSuggest *suggest = nimSuggestInstance(interface);
QTC_ASSERT(suggest, return nullptr);
if (suggest->executablePath().isEmpty() || suggest->projectFile().isEmpty())
return nullptr;
if (!suggest->isReady()) {
m_interface = interface;
QObject::connect(suggest, &Suggest::NimSuggest::readyChanged,
this, &NimCompletionAssistProcessor::onNimSuggestReady);
} else {
doPerform(interface, suggest);
}
m_running = true;
return nullptr;
}
bool running()
{
return m_running;
}
private:
void onNimSuggestReady(bool ready)
{
auto suggest = dynamic_cast<Suggest::NimSuggest *>(sender());
QTC_ASSERT(suggest, return);
QTC_ASSERT(m_interface, return);
if (!ready || !suggest) {
m_running = false;
setAsyncProposalAvailable(nullptr);
} else {
doPerform(m_interface, suggest);
}
}
void doPerform(const TextEditor::AssistInterface *interface,
Suggest::NimSuggest *suggest)
{
int pos = findCompletionPos(interface);
std::unique_ptr<QTemporaryFile> dirtyFile = writeDirtyFile(interface);
QTC_ASSERT(dirtyFile, return);
std::shared_ptr<Suggest::SugRequest> request = sendRequest(interface,
suggest,
dirtyFile->fileName(),
pos);
QTC_ASSERT(request, return);
connect(request.get(), &Suggest::SugRequest::finished, this,
&NimCompletionAssistProcessor::onRequestFinished);
m_pos = pos;
m_dirtyFile = std::move(dirtyFile);
m_request = std::move(request);
}
void onRequestFinished()
{
auto items = Utils::transform<QList>(m_request->lines(), &createProposal);
setAsyncProposalAvailable(new GenericProposal(m_pos, items));
m_running = false;
m_dirtyFile.reset();
m_request.reset();
}
static bool acceptsIdleEditor(const AssistInterface *interface)
{
int pos = interface->position();
QChar c = interface->textDocument()->characterAt(pos - 1);
return isIdentifierChar(c) || isActivationChar(c);
}
static int findCompletionPos(const AssistInterface *interface)
{
int pos = interface->position();
while (isIdentifierChar(interface->textDocument()->characterAt(pos - 1)))
pos--;
return pos;
}
static Suggest::NimSuggest *nimSuggestInstance(const AssistInterface *interface)
{
auto filename = Utils::FileName::fromString(interface->fileName());
return Nim::Suggest::NimSuggestCache::instance().get(filename);
}
static std::shared_ptr<Suggest::SugRequest> sendRequest(const AssistInterface *interface,
Suggest::NimSuggest *suggest,
QString dirtyFile,
int pos)
{
int line = 0, column = 0;
Utils::Text::convertPosition(interface->textDocument(), pos, &line, &column);
QTC_ASSERT(column >= 1, return nullptr);
auto filename = Utils::FileName::fromString(interface->fileName());
return suggest->sug(filename.toString(), line, column - 1, dirtyFile);
}
static std::unique_ptr<QTemporaryFile> writeDirtyFile(const TextEditor::AssistInterface *interface)
{
auto result = std::make_unique<QTemporaryFile>("qtcnim.XXXXXX.nim");
QTC_ASSERT(result->open(), return nullptr);
QTextStream stream(result.get());
stream << interface->textDocument()->toPlainText();
result->close();
return result;
}
static AssistProposalItemInterface *createProposal(const Nim::Suggest::Line &line)
{
auto item = new AssistProposalItem();
item->setIcon(Utils::CodeModelIcon::iconForType(symbolIcon(line.symbol_kind)));
item->setText(line.data.back());
item->setDetail(line.symbol_type);
item->setOrder(symbolOrder(line.symbol_kind));
return item;
}
static int symbolOrder(Suggest::Line::SymbolKind kind)
{
switch (kind) {
case Suggest::Line::SymbolKind::skField:
return 2;
case Suggest::Line::SymbolKind::skVar:
case Suggest::Line::SymbolKind::skLet:
case Suggest::Line::SymbolKind::skEnumField:
case Suggest::Line::SymbolKind::skResult:
case Suggest::Line::SymbolKind::skForVar:
case Suggest::Line::SymbolKind::skParam:
case Suggest::Line::SymbolKind::skLabel:
case Suggest::Line::SymbolKind::skGenericParam:
return 1;
default:
return 0;
}
}
static Utils::CodeModelIcon::Type symbolIcon(Suggest::Line::SymbolKind kind)
{
switch (kind) {
case Suggest::Line::SymbolKind::skField:
return Utils::CodeModelIcon::Property;
case Suggest::Line::SymbolKind::skVar:
case Suggest::Line::SymbolKind::skLet:
case Suggest::Line::SymbolKind::skEnumField:
case Suggest::Line::SymbolKind::skResult:
case Suggest::Line::SymbolKind::skForVar:
case Suggest::Line::SymbolKind::skParam:
case Suggest::Line::SymbolKind::skLabel:
case Suggest::Line::SymbolKind::skGenericParam:
return Utils::CodeModelIcon::VarPublic;
case Suggest::Line::SymbolKind::skProc:
case Suggest::Line::SymbolKind::skFunc:
case Suggest::Line::SymbolKind::skMethod:
case Suggest::Line::SymbolKind::skIterator:
case Suggest::Line::SymbolKind::skConverter:
return Utils::CodeModelIcon::FuncPublic;
case Suggest::Line::SymbolKind::skType:
return Utils::CodeModelIcon::Struct;
case Suggest::Line::SymbolKind::skMacro:
case Suggest::Line::SymbolKind::skTemplate:
return Utils::CodeModelIcon::Macro;
case Suggest::Line::SymbolKind::skModule:
case Suggest::Line::SymbolKind::skPackage:
return Utils::CodeModelIcon::Namespace;
case Suggest::Line::SymbolKind::skUnknown:
case Suggest::Line::SymbolKind::skConditional:
case Suggest::Line::SymbolKind::skDynLib:
case Suggest::Line::SymbolKind::skTemp:
case Suggest::Line::SymbolKind::skConst:
case Suggest::Line::SymbolKind::skStub:
case Suggest::Line::SymbolKind::skAlias:
default:
return Utils::CodeModelIcon::Type::Unknown;
}
}
bool m_running = false;
int m_pos = -1;
std::weak_ptr<Suggest::NimSuggest> m_suggest;
std::shared_ptr<Suggest::SugRequest> m_request;
std::unique_ptr<QTemporaryFile> m_dirtyFile;
const TextEditor::AssistInterface *m_interface = nullptr;
};
TextEditor::IAssistProcessor *NimCompletionAssistProvider::createProcessor() const
{
return new NimCompletionAssistProcessor();
}
int NimCompletionAssistProvider::activationCharSequenceLength() const
{
return 1;
}
bool NimCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const
{
return sequence.size() && isActivationChar(sequence.front());
}
IAssistProvider::RunType NimCompletionAssistProvider::runType() const
{
return RunType::Asynchronous;
}
}
#include "nimcompletionassistprovider.moc"

View File

@@ -0,0 +1,43 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 <texteditor/codeassist/completionassistprovider.h>
namespace Nim {
class NimCompletionAssistProvider : public TextEditor::CompletionAssistProvider
{
Q_OBJECT
public:
TextEditor::IAssistProcessor *createProcessor() const final;
int activationCharSequenceLength() const final;
bool isActivationCharSequence(const QString &sequence) const final;
RunType runType() const final;
};
}

View File

@@ -26,6 +26,7 @@
#include "nimeditorfactory.h"
#include "nimindenter.h"
#include "nimhighlighter.h"
#include "nimcompletionassistprovider.h"
#include "../nimconstants.h"
#include "../nimplugin.h"
@@ -65,6 +66,7 @@ NimEditorFactory::NimEditorFactory()
setSyntaxHighlighterCreator([]() {
return new NimHighlighter;
});
setCompletionAssistProvider(new NimCompletionAssistProvider());
setCommentDefinition(CommentDefinition::HashStyle);
setParenthesesMatchingEnabled(true);
setCodeFoldingSupported(true);

View File

@@ -11,6 +11,7 @@ INCLUDEPATH += $$PWD
HEADERS += \
nimplugin.h \
nimconstants.h \
editor/nimcompletionassistprovider.h \
editor/nimhighlighter.h \
editor/nimindenter.h \
tools/nimlexer.h \
@@ -29,11 +30,20 @@ HEADERS += \
settings/nimcodestylepreferencesfactory.h \
settings/nimsettings.h \
settings/nimcodestylepreferenceswidget.h \
settings/nimtoolssettingspage.h \
project/nimtoolchain.h \
project/nimtoolchainfactory.h
project/nimtoolchainfactory.h \
suggest/client.h \
suggest/clientrequests.h \
suggest/nimsuggest.h \
suggest/nimsuggestcache.h \
suggest/server.h \
suggest/sexprlexer.h \
suggest/sexprparser.h
SOURCES += \
nimplugin.cpp \
editor/nimcompletionassistprovider.cpp \
editor/nimhighlighter.cpp \
editor/nimindenter.cpp \
tools/nimlexer.cpp \
@@ -51,10 +61,17 @@ SOURCES += \
settings/nimcodestylepreferencesfactory.cpp \
settings/nimsettings.cpp \
settings/nimcodestylepreferenceswidget.cpp \
settings/nimtoolssettingspage.cpp \
project/nimtoolchain.cpp \
project/nimtoolchainfactory.cpp
project/nimtoolchainfactory.cpp \
suggest/client.cpp \
suggest/clientrequests.cpp \
suggest/nimsuggest.cpp \
suggest/nimsuggestcache.cpp \
suggest/server.cpp
FORMS += \
project/nimcompilerbuildstepconfigwidget.ui \
project/nimcompilercleanstepconfigwidget.ui \
settings/nimcodestylepreferenceswidget.ui
settings/nimcodestylepreferenceswidget.ui \
settings/nimtoolssettingswidget.ui

View File

@@ -27,7 +27,8 @@ QtcPlugin {
files: [
"nimeditorfactory.h", "nimeditorfactory.cpp",
"nimhighlighter.h", "nimhighlighter.cpp",
"nimindenter.h", "nimindenter.cpp"
"nimindenter.h", "nimindenter.cpp",
"nimcompletionassistprovider.h", "nimcompletionassistprovider.cpp"
]
}
@@ -57,6 +58,7 @@ QtcPlugin {
"nimcodestylepreferenceswidget.h", "nimcodestylepreferenceswidget.cpp", "nimcodestylepreferenceswidget.ui",
"nimcodestylesettingspage.h", "nimcodestylesettingspage.cpp",
"nimsettings.h", "nimsettings.cpp",
"nimtoolssettingspage.h", "nimtoolssettingspage.cpp", "nimtoolssettingswidget.ui"
]
}
@@ -68,4 +70,18 @@ QtcPlugin {
"sourcecodestream.h"
]
}
Group {
name: "Suggest"
prefix: "suggest/"
files: [
"client.h", "client.cpp",
"clientrequests.h", "clientrequests.cpp",
"nimsuggest.h", "nimsuggest.cpp",
"nimsuggestcache.h", "nimsuggestcache.cpp",
"server.h", "server.cpp",
"sexprlexer.h",
"sexprparser.h",
]
}
}

View File

@@ -77,6 +77,12 @@ const char C_NIMCODESTYLESETTINGSPAGE_ID[] = "Nim.NimCodeStyleSettings";
const char C_NIMCODESTYLESETTINGSPAGE_DISPLAY[] = QT_TRANSLATE_NOOP("NimCodeStyleSettingsPage", "Code Style");
const char C_NIMCODESTYLESETTINGSPAGE_CATEGORY[] = "Z.Nim";
const char C_NIMCODESTYLESETTINGSPAGE_CATEGORY_DISPLAY[] = QT_TRANSLATE_NOOP("NimCodeStyleSettingsPage", "Nim");
const char C_NIMTOOLSSETTINGSPAGE_ID[] = "Nim.NimToolsSettings";
const char C_NIMTOOLSSETTINGSPAGE_DISPLAY[] = QT_TRANSLATE_NOOP("NimToolsSettingsPage", "Tools");
const char C_NIMTOOLSSETTINGSPAGE_CATEGORY[] = "Z.Nim";
const char C_NIMTOOLSSETTINGSPAGE_CATEGORY_DISPLAY[] = QT_TRANSLATE_NOOP("NimToolsSettingsPage", "Nim");
const char C_NIMLANGUAGE_NAME[] = QT_TRANSLATE_NOOP("NimCodeStylePreferencesFactory", "Nim");
const char C_NIMGLOBALCODESTYLE_ID[] = "NimGlobal";
const QString C_NIMSNIPPETSGROUP_ID = QStringLiteral("Nim.NimSnippetsGroup");
@@ -103,5 +109,9 @@ const char C_NIM_SCRIPT_MIMETYPE[] = "text/x-nim-script";
const char C_NIM_MIME_ICON[] = "text-x-nim";
const char C_NIM_PROJECT_MIMETYPE[] = "text/x-nim-project";
const char C_NIM_SETTINGS_GROUP[] = "Nim";
const char C_NIM_SETTINGS_NIMSUGGEST_GROUP[] = "NimSuggest";
const char C_NIM_SETTINGS_COMMAND[] = "Command";
}
}

View File

@@ -36,7 +36,9 @@
#include "project/nimtoolchainfactory.h"
#include "settings/nimcodestylepreferencesfactory.h"
#include "settings/nimcodestylesettingspage.h"
#include "settings/nimtoolssettingspage.h"
#include "settings/nimsettings.h"
#include "suggest/nimsuggestcache.h"
#include <coreplugin/fileiconprovider.h>
#include <projectexplorer/projectmanager.h>
@@ -51,6 +53,15 @@ namespace Nim {
class NimPluginPrivate
{
public:
NimPluginPrivate()
: toolsSettingsPage(&settings)
{
Suggest::NimSuggestCache::instance().setExecutablePath(settings.nimSuggestPath());
QObject::connect(&settings, &NimSettings::nimSuggestPathChanged,
&Suggest::NimSuggestCache::instance(),
&Suggest::NimSuggestCache::setExecutablePath);
}
NimSettings settings;
NimEditorFactory editorFactory;
NimBuildConfigurationFactory buildConfigFactory;
@@ -58,6 +69,7 @@ public:
NimCompilerBuildStepFactory buildStepFactory;
NimCompilerCleanStepFactory cleanStepFactory;
NimCodeStyleSettingsPage codeStyleSettingsPage;
NimToolsSettingsPage toolsSettingsPage;
NimCodeStylePreferencesFactory codeStylePreferencesPage;
NimToolChainFactory toolChainFactory;
};
@@ -89,7 +101,8 @@ void NimPlugin::extensionsInitialized()
{
// Add MIME overlay icons (these icons displayed at Project dock panel)
const QIcon icon = Utils::Icon({{":/nim/images/settingscategory_nim.png",
Utils::Theme::PanelTextColorDark}}, Utils::Icon::Tint).icon();
Utils::Theme::PanelTextColorDark
}}, Utils::Icon::Tint).icon();
if (!icon.isNull()) {
Core::FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIM_MIMETYPE);
Core::FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIM_SCRIPT_MIMETYPE);

View File

@@ -47,6 +47,9 @@
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <QFileInfo>
#include <QQueue>
@@ -68,7 +71,8 @@ NimProject::NimProject(const FileName &fileName) : Project(Constants::C_NIM_MIME
connect(&m_projectScanTimer, &QTimer::timeout, this, &NimProject::collectProjectFiles);
connect(this, &Project::settingsLoaded, this, &NimProject::collectProjectFiles);
connect(&m_futureWatcher, &QFutureWatcher<QList<FileNode *>>::finished, this, &NimProject::updateProject);
connect(&m_futureWatcher, &QFutureWatcher<QList<FileNode *>>::finished, this,
&NimProject::updateProject);
}
void NimProject::scheduleProjectScan()
@@ -86,7 +90,9 @@ void NimProject::scheduleProjectScan()
bool NimProject::addFiles(const QStringList &filePaths)
{
m_excludedFiles = Utils::filtered(m_excludedFiles, [&](const QString &f) { return !filePaths.contains(f); });
m_excludedFiles = Utils::filtered(m_excludedFiles, [&](const QString & f) {
return !filePaths.contains(f);
});
scheduleProjectScan();
return true;
}
@@ -112,8 +118,9 @@ void NimProject::collectProjectFiles()
m_lastProjectScan.start();
QTC_ASSERT(!m_futureWatcher.future().isRunning(), return);
FileName prjDir = projectDirectory();
QFuture<QList<ProjectExplorer::FileNode *>> future = Utils::runAsync([prjDir, excluded = m_excludedFiles] {
return FileNode::scanForFiles(prjDir, [excluded](const FileName &fn) -> FileNode * {
QFuture<QList<ProjectExplorer::FileNode *>> future = Utils::runAsync([prjDir,
excluded = m_excludedFiles] {
return FileNode::scanForFiles(prjDir, [excluded](const FileName & fn) -> FileNode * {
const QString fileName = fn.fileName();
if (excluded.contains(fn.toString())
|| fileName.endsWith(".nimproject", HostOsInfo::fileNameCaseSensitivity())
@@ -132,7 +139,9 @@ void NimProject::updateProject()
auto newRoot = std::make_unique<NimProjectNode>(*this, projectDirectory());
for (FileNode *node : m_futureWatcher.future().result())
QList<FileNode *> files = m_futureWatcher.future().result();
for (FileNode *node : files)
newRoot->addNestedNode(std::unique_ptr<FileNode>(node));
newRoot->setDisplayName(displayName());
@@ -144,7 +153,8 @@ void NimProject::updateProject()
QList<Task> NimProject::projectIssues(const Kit *k) const
{
QList<Task> result = Project::projectIssues(k);
auto tc = dynamic_cast<NimToolChain*>(ToolChainKitInformation::toolChain(k, Constants::C_NIMLANGUAGE_ID));
auto tc = dynamic_cast<NimToolChain *>(ToolChainKitInformation::toolChain(k,
Constants::C_NIMLANGUAGE_ID));
if (!tc) {
result.append(createProjectTask(Task::TaskType::Error, tr("No Nim compiler set.")));
return result;
@@ -157,7 +167,9 @@ QList<Task> NimProject::projectIssues(const Kit *k) const
FileNameList NimProject::nimFiles() const
{
return files([](const ProjectExplorer::Node *n) { return AllFiles(n) && n->filePath().endsWith(".nim"); });
return files([](const ProjectExplorer::Node *n) {
return AllFiles(n) && n->filePath().endsWith(".nim");
});
}
QVariantMap NimProject::toMap() const

View File

@@ -57,7 +57,6 @@ private:
void collectProjectFiles();
void updateProject();
QStringList m_files;
QStringList m_excludedFiles;
QFutureWatcher<QList<ProjectExplorer::FileNode *>> m_futureWatcher;
QElapsedTimer m_lastProjectScan;

View File

@@ -39,10 +39,50 @@ using namespace TextEditor;
namespace Nim {
SimpleCodeStylePreferences *m_globalCodeStyle = nullptr;
static SimpleCodeStylePreferences *m_globalCodeStyle = nullptr;
NimSettings::NimSettings(QObject *parent)
: QObject(parent)
{
InitializeCodeStyleSettings();
InitializeNimSuggestSettings();
}
NimSettings::~NimSettings()
{
TerminateCodeStyleSettings();
}
QString NimSettings::nimSuggestPath() const
{
return m_nimSuggestPath;
}
void NimSettings::setNimSuggestPath(const QString &path)
{
if (m_nimSuggestPath == path)
return;
m_nimSuggestPath = path;
emit nimSuggestPathChanged(path);
}
void NimSettings::save()
{
QSettings *s = Core::ICore::settings();
s->beginGroup(Constants::C_NIM_SETTINGS_GROUP);
s->beginGroup(Constants::C_NIM_SETTINGS_NIMSUGGEST_GROUP);
s->setValue(QString::fromStdString(Constants::C_NIM_SETTINGS_COMMAND), nimSuggestPath());
s->endGroup();
s->endGroup();
s->sync();
}
SimpleCodeStylePreferences *NimSettings::globalCodeStyle()
{
return m_globalCodeStyle;
}
void NimSettings::InitializeCodeStyleSettings()
{
// code style factory
auto factory = new NimCodeStylePreferencesFactory();
@@ -63,12 +103,14 @@ NimSettings::NimSettings(QObject *parent)
nimCodeStyle->setId("nim");
nimCodeStyle->setDisplayName(tr("Nim"));
nimCodeStyle->setReadOnly(true);
TabSettings nimTabSettings;
nimTabSettings.m_tabPolicy = TabSettings::SpacesOnlyTabPolicy;
nimTabSettings.m_tabSize = 2;
nimTabSettings.m_indentSize = 2;
nimTabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithIndent;
nimCodeStyle->setTabSettings(nimTabSettings);
pool->addCodeStyle(nimCodeStyle);
m_globalCodeStyle->setCurrentDelegate(nimCodeStyle);
@@ -79,11 +121,24 @@ NimSettings::NimSettings(QObject *parent)
QSettings *s = Core::ICore::settings();
m_globalCodeStyle->fromSettings(QLatin1String(Nim::Constants::C_NIMLANGUAGE_ID), s);
TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_MIMETYPE, Nim::Constants::C_NIMLANGUAGE_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_SCRIPT_MIMETYPE, Nim::Constants::C_NIMLANGUAGE_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_MIMETYPE,
Nim::Constants::C_NIMLANGUAGE_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_SCRIPT_MIMETYPE,
Nim::Constants::C_NIMLANGUAGE_ID);
}
NimSettings::~NimSettings()
void NimSettings::InitializeNimSuggestSettings()
{
QSettings *s = Core::ICore::settings();
s->beginGroup(Constants::C_NIM_SETTINGS_GROUP);
s->beginGroup(Constants::C_NIM_SETTINGS_NIMSUGGEST_GROUP);
setNimSuggestPath(s->value(QString::fromStdString(Constants::C_NIM_SETTINGS_COMMAND),
QString()).toString());
s->endGroup();
s->endGroup();
}
void NimSettings::TerminateCodeStyleSettings()
{
TextEditorSettings::unregisterCodeStyle(Nim::Constants::C_NIMLANGUAGE_ID);
TextEditorSettings::unregisterCodeStylePool(Nim::Constants::C_NIMLANGUAGE_ID);
@@ -93,9 +148,4 @@ NimSettings::~NimSettings()
m_globalCodeStyle = nullptr;
}
SimpleCodeStylePreferences *NimSettings::globalCodeStyle()
{
return m_globalCodeStyle;
}
} // namespace Nim

View File

@@ -39,7 +39,24 @@ public:
NimSettings(QObject *parent = nullptr);
~NimSettings() override;
QString nimSuggestPath() const;
void setNimSuggestPath(const QString &path);
void save();
static TextEditor::SimpleCodeStylePreferences *globalCodeStyle();
signals:
void nimSuggestPathChanged(QString path);
private:
void InitializeCodeStyleSettings();
void TerminateCodeStyleSettings();
void InitializeNimSuggestSettings();
void TerminateNimSuggestSettings();
QString m_nimSuggestPath;
};
} // namespace Nim

View File

@@ -0,0 +1,93 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 "nimtoolssettingspage.h"
#include "nimconstants.h"
#include "nimsettings.h"
#include "ui_nimtoolssettingswidget.h"
#include <coreplugin/icore.h>
namespace Nim {
NimToolsSettingsWidget::NimToolsSettingsWidget(NimSettings *settings, QWidget *parent)
: QWidget(parent)
, ui(new Ui::NimToolsSettingsWidget)
, m_settings(settings)
{
ui->setupUi(this);
ui->pathWidget->setExpectedKind(Utils::PathChooser::ExistingCommand);
}
NimToolsSettingsWidget::~NimToolsSettingsWidget()
{
delete ui;
}
QString NimToolsSettingsWidget::command() const
{
return ui->pathWidget->path();
}
void NimToolsSettingsWidget::setCommand(const QString &filename)
{
ui->pathWidget->setPath(filename);
}
NimToolsSettingsPage::NimToolsSettingsPage(NimSettings *settings, QWidget *parent)
: Core::IOptionsPage(parent)
, m_settings(settings)
{
setId(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_ID);
setDisplayName(tr(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_DISPLAY));
setCategory(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_CATEGORY);
setDisplayCategory(tr("Nim"));
setCategoryIcon(Utils::Icon({{":/nim/images/settingscategory_nim.png",
Utils::Theme::PanelTextColorDark
}}, Utils::Icon::Tint));
}
NimToolsSettingsPage::~NimToolsSettingsPage() = default;
QWidget *NimToolsSettingsPage::widget()
{
if (!m_widget)
m_widget.reset(new NimToolsSettingsWidget(m_settings));
m_widget->setCommand(m_settings->nimSuggestPath());
return m_widget.get();
}
void NimToolsSettingsPage::apply()
{
m_settings->setNimSuggestPath(m_widget->command());
m_settings->save();
}
void NimToolsSettingsPage::finish()
{
m_widget.reset();
}
}

View File

@@ -0,0 +1,76 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 <coreplugin/dialogs/ioptionspage.h>
#include <utils/fileutils.h>
#include <QWidget>
#include <memory>
namespace Nim {
class NimSettings;
namespace Ui { class NimToolsSettingsWidget; }
class NimToolsSettingsWidget : public QWidget
{
Q_OBJECT
public:
explicit NimToolsSettingsWidget(NimSettings *settings, QWidget *parent = nullptr);
~NimToolsSettingsWidget();
QString command() const;
void setCommand(const QString &filename);
private:
Ui::NimToolsSettingsWidget *ui;
NimSettings *m_settings = nullptr;
};
class NimToolsSettingsPage : public Core::IOptionsPage
{
Q_OBJECT
public:
NimToolsSettingsPage(NimSettings *settings, QWidget *parent = nullptr);
~NimToolsSettingsPage();
QWidget *widget() final;
void apply() final;
void finish() final;
private:
std::unique_ptr<NimToolsSettingsWidget> m_widget;
NimSettings *m_settings = nullptr;
};
}

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Nim::NimToolsSettingsWidget</class>
<widget class="QWidget" name="Nim::NimToolsSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Nimsuggest</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="pathLabel">
<property name="text">
<string>Path</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Utils::PathChooser" name="pathWidget" native="true"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Utils::PathChooser</class>
<extends>QWidget</extends>
<header location="global">utils/pathchooser.h</header>
<container>1</container>
<slots>
<signal>editingFinished()</signal>
<signal>browsingFinished()</signal>
</slots>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,164 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 "client.h"
#include "sexprparser.h"
namespace Nim {
namespace Suggest {
NimSuggestClient::NimSuggestClient(QObject *parent) : QObject(parent)
{
connect(&m_socket, &QTcpSocket::readyRead, this, &NimSuggestClient::onReadyRead);
connect(&m_socket, &QTcpSocket::connected, this, &NimSuggestClient::connected);
connect(&m_socket, &QTcpSocket::disconnected, this, &NimSuggestClient::disconnected);
}
bool NimSuggestClient::connectToServer(quint16 port)
{
m_port = port;
m_socket.connectToHost("localhost", m_port);
return true;
}
bool NimSuggestClient::disconnectFromServer()
{
m_socket.disconnectFromHost();
clear();
return true;
}
std::shared_ptr<SugRequest> NimSuggestClient::sug(const QString &nimFile,
int line, int column,
const QString &dirtyFile)
{
if (!m_socket.isOpen())
return nullptr;
auto result = std::make_shared<SugRequest>(m_lastMessageId++);
m_requests.emplace(result->id(), result);
QByteArray body = QString(R"((call %1 sug ("%2" %3 %4 "%5"))\n)")
.arg(result->id())
.arg(nimFile)
.arg(line).arg(column)
.arg(dirtyFile)
.toUtf8();
QByteArray length = QString::number(body.size(), 16).rightJustified(6, '0').toUtf8();
QByteArray message = length + body;
m_socket.write(message);
m_socket.waitForBytesWritten();
return result;
}
void NimSuggestClient::clear()
{
for (const auto &pair : m_requests) {
if (auto req = pair.second.lock()) {
emit req->finished();
}
}
m_lines.clear();
m_readBuffer.clear();
m_requests.clear();
m_lastMessageId = 0;
}
void NimSuggestClient::onDisconnectedFromServer()
{
clear();
}
void NimSuggestClient::onReadyRead()
{
std::array<char, 2048> buffer;
for (;;) {
qint64 num_read = m_socket.read(buffer.data(), buffer.size());
m_readBuffer.insert(m_readBuffer.end(), buffer.begin(), buffer.begin() + num_read);
if (num_read <= 0) {
break;
}
}
while (m_readBuffer.size() >= 6) {
const size_t payload_size = QByteArray::fromRawData(m_readBuffer.data(), 6).toUInt(nullptr, 16);
if (payload_size <= m_readBuffer.size() - 6) {
parsePayload(m_readBuffer.data() + 6, payload_size);
m_readBuffer.erase(m_readBuffer.begin(), m_readBuffer.begin() + 6u + payload_size);
} else {
break;
}
}
}
void NimSuggestClient::parsePayload(const char *payload, std::size_t size)
{
SExprParser::Node root_ast;
SExprParser parser(payload, size);
if (!parser.parse(root_ast))
return;
if (root_ast.nodes.at(0).value != "return")
return;
const quint64 uid = std::stoull(root_ast.nodes.at(1).value, nullptr, 0);
auto it = m_requests.find(uid);
if (it == m_requests.end())
return;
auto req = std::dynamic_pointer_cast<SugRequest>((*it).second.lock());
if (!req)
return;
std::vector<Line> suggestions;
suggestions.reserve(root_ast.nodes.at(2).nodes.size());
for (const SExprParser::Node &row : root_ast.nodes.at(2).nodes) {
Line line;
if (!Line::fromString(line.line_type, row.nodes.at(0).value))
continue;
if (!Line::fromString(line.symbol_kind, row.nodes.at(1).value))
continue;
line.data.reserve(row.nodes.at(2).nodes.size());
for (const SExprParser::Node &field : row.nodes.at(2).nodes)
line.data.push_back(QString::fromStdString(field.value));
line.abs_path = QString::fromStdString(row.nodes.at(3).value);
line.symbol_type = QString::fromStdString(row.nodes.at(4).value);
line.row = std::stoi(row.nodes.at(5).value);
line.column = std::stoi(row.nodes.at(6).value);
line.doc = QString::fromStdString(row.nodes.at(7).value);
suggestions.push_back(std::move(line));
}
req->setFinished(std::move(suggestions));
m_requests.erase(it);
}
} // namespace Suggest
} // namespace Nim

View File

@@ -0,0 +1,75 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 <QObject>
#include <QString>
#include <QTcpSocket>
#include <memory>
#include <unordered_map>
#include <vector>
#include "clientrequests.h"
namespace Nim {
namespace Suggest {
class NimSuggestClient : public QObject
{
Q_OBJECT
public:
NimSuggestClient(QObject *parent = nullptr);
bool connectToServer(quint16 port);
bool disconnectFromServer();
std::shared_ptr<SugRequest> sug(const QString &nimFile,
int line, int column,
const QString &dirtyFile);
signals:
void connected();
void disconnected();
private:
void clear();
void onDisconnectedFromServer();
void onReadyRead();
void parsePayload(const char *payload, std::size_t size);
QTcpSocket m_socket;
quint16 m_port;
std::unordered_map<quint64, std::weak_ptr<BaseNimSuggestClientRequest>> m_requests;
std::vector<QString> m_lines;
std::vector<char> m_readBuffer;
quint64 m_lastMessageId = 0;
};
} // namespace Suggest
} // namespace Nim

View File

@@ -0,0 +1,68 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 "clientrequests.h"
#include <QDebug>
#include <QMetaEnum>
namespace Nim {
namespace Suggest {
bool Line::fromString(Line::LineType &type, const std::string &str)
{
static const auto metaobject = QMetaEnum::fromType<LineType>();
bool result = false;
type = static_cast<LineType>(metaobject.keyToValue(str.c_str(), &result));
return result;
}
bool Line::fromString(Line::SymbolKind &type, const std::string &str)
{
static const auto metaobject = QMetaEnum::fromType<SymbolKind>();
bool result = false;
type = static_cast<SymbolKind>(metaobject.keyToValue(str.c_str(), &result));
return result;
}
BaseNimSuggestClientRequest::BaseNimSuggestClientRequest(quint64 id)
: m_id(id)
{}
quint64 BaseNimSuggestClientRequest::id() const
{
return m_id;
}
} // namespace Suggest
} // namespace Nim
QDebug operator<<(QDebug debug, const Nim::Suggest::Line &c)
{
QDebugStateSaver saver(debug);
debug.space() << c.line_type << c.symbol_kind << c.symbol_type << c.data << c.row << c.column <<
c.abs_path;
return debug;
}

View File

@@ -0,0 +1,141 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 <QObject>
namespace Nim {
namespace Suggest {
class Line
{
Q_GADGET
public:
enum class LineType : int {
sug,
con,
def,
use,
dus,
chk,
mod,
highlight,
outline,
known
};
Q_ENUM(LineType)
enum class SymbolKind : int {
skUnknown,
skConditional,
skDynLib,
skParam,
skGenericParam,
skTemp,
skModule,
skType,
skVar,
skLet,
skConst,
skResult,
skProc,
skFunc,
skMethod,
skIterator,
skConverter,
skMacro,
skTemplate,
skField,
skEnumField,
skForVar,
skLabel,
skStub,
skPackage,
skAlias
};
Q_ENUM(SymbolKind)
static bool fromString(LineType &type, const std::string &str);
static bool fromString(SymbolKind &type, const std::string &str);
LineType line_type;
SymbolKind symbol_kind;
QString abs_path;
QString symbol_type;
std::vector<QString> data;
int row;
int column;
QString doc;
};
class BaseNimSuggestClientRequest : public QObject
{
Q_OBJECT
public:
BaseNimSuggestClientRequest(quint64 id);
quint64 id() const;
signals:
void finished();
private:
const quint64 m_id;
};
class SugRequest : public BaseNimSuggestClientRequest
{
public:
using BaseNimSuggestClientRequest::BaseNimSuggestClientRequest;
std::vector<Line> &lines()
{
return m_lines;
}
const std::vector<Line> &lines() const
{
return m_lines;
}
private:
friend class NimSuggestClient;
void setFinished(std::vector<Line> lines)
{
m_lines = std::move(lines);
emit finished();
}
std::vector<Line> m_lines;
};
} // namespace Suggest
} // namespace Nim
QDebug operator<<(QDebug debug, const Nim::Suggest::Line &c);

View File

@@ -0,0 +1,175 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 "nimsuggest.h"
namespace Nim {
namespace Suggest {
NimSuggest::NimSuggest(QObject *parent)
: QObject(parent)
{
connect(&m_server, &NimSuggestServer::started, this, &NimSuggest::onServerStarted);
connect(&m_server, &NimSuggestServer::crashed, this, &NimSuggest::onServerCrashed);
connect(&m_server, &NimSuggestServer::finished, this, &NimSuggest::onServerFinished);
connect(&m_client, &NimSuggestClient::disconnected, this, &NimSuggest::onClientDisconnected);
connect(&m_client, &NimSuggestClient::connected, this, &NimSuggest::onClientConnected);
}
QString NimSuggest::projectFile() const
{
return m_projectFile;
}
void NimSuggest::setProjectFile(const QString &file)
{
if (m_projectFile == file)
return;
auto old = m_projectFile;
m_projectFile = file;
emit projectFileChanged(file);
restart();
}
QString NimSuggest::executablePath() const
{
return m_executablePath;
}
void NimSuggest::setExecutablePath(const QString &path)
{
if (m_executablePath == path)
return;
auto old = m_executablePath;
m_executablePath = path;
emit executablePathChanged(path);
restart();
}
bool NimSuggest::isReady() const
{
return m_ready;
}
std::shared_ptr<SugRequest> NimSuggest::sug(const QString &filename, int line, int column,
const QString &dirtyFilename)
{
return m_ready ? m_client.sug(filename, line, column, dirtyFilename) : nullptr;
}
void NimSuggest::restart()
{
disconnectClient();
setClientReady(false);
stopServer();
setServerReady(false);
startServer();
}
void NimSuggest::setReady(bool ready)
{
if (m_ready == ready)
return;
m_ready = ready;
emit readyChanged(ready);
}
void NimSuggest::setServerReady(bool ready)
{
if (m_serverReady == ready)
return;
m_serverReady = ready;
setReady(m_clientReady && m_serverReady);
}
void NimSuggest::setClientReady(bool ready)
{
if (m_clientReady == ready)
return;
m_clientReady = ready;
setReady(m_clientReady && m_serverReady);
}
void NimSuggest::connectClient()
{
m_client.connectToServer(m_server.port());
}
void NimSuggest::disconnectClient()
{
m_client.disconnectFromServer();
}
void NimSuggest::stopServer()
{
m_server.kill();
}
void NimSuggest::startServer()
{
if (!m_projectFile.isEmpty() && !m_executablePath.isEmpty()) {
m_server.start(m_executablePath, m_projectFile);
}
}
void NimSuggest::onServerStarted()
{
setServerReady(true);
connectClient();
}
void NimSuggest::onServerCrashed()
{
setServerReady(false);
disconnectClient();
restart();
}
void NimSuggest::onServerFinished()
{
onServerCrashed();
}
void NimSuggest::onClientConnected()
{
setClientReady(true);
}
void NimSuggest::onClientDisconnected()
{
setClientReady(false);
if (isServerReady())
connectClient();
}
} // namespace Suggest
} // namespace Nim

View File

@@ -0,0 +1,91 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 "client.h"
#include "server.h"
namespace Nim {
namespace Suggest {
class NimSuggest : public QObject
{
Q_OBJECT
public:
NimSuggest(QObject *parent = nullptr);
QString projectFile() const;
void setProjectFile(const QString &file);
QString executablePath() const;
void setExecutablePath(const QString &path);
bool isReady() const;
std::shared_ptr<SugRequest> sug(const QString &filename, int line, int column,
const QString &dirtyFilename);
signals:
void readyChanged(bool ready);
void projectFileChanged(const QString &projectFile);
void executablePathChanged(const QString &executablePath);
private:
void restart();
void setReady(bool ready);
bool isServerReady() const { return m_serverReady; }
void setServerReady(bool ready);
bool isClientReady() const { return m_clientReady; }
void setClientReady(bool ready);
void connectClient();
void disconnectClient();
void stopServer();
void startServer();
void onServerStarted();
void onServerCrashed();
void onServerFinished();
void onClientConnected();
void onClientDisconnected();
bool m_ready = false;
bool m_clientReady = false;
bool m_serverReady = false;
QString m_projectFile;
QString m_executablePath;
NimSuggestServer m_server;
NimSuggestClient m_client;
};
} // namespace Suggest
} // namespace Nim

View File

@@ -0,0 +1,98 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 "nimsuggestcache.h"
#include "nimconstants.h"
#include "nimsuggest.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
namespace Nim {
namespace Suggest {
NimSuggestCache &NimSuggestCache::instance()
{
static NimSuggestCache instance;
return instance;
}
NimSuggestCache::~NimSuggestCache() = default;
NimSuggest *NimSuggestCache::get(const Utils::FileName &filename)
{
auto it = m_nimSuggestInstances.find(filename);
if (it == m_nimSuggestInstances.end()) {
auto instance = std::make_unique<Suggest::NimSuggest>(this);
instance->setProjectFile(filename.toString());
instance->setExecutablePath(m_executablePath);
it = m_nimSuggestInstances.emplace(filename, std::move(instance)).first;
}
return it->second.get();
}
NimSuggestCache::NimSuggestCache()
{
Core::EditorManager *editorManager = Core::EditorManager::instance();
connect(editorManager, &Core::EditorManager::editorOpened,
this, &NimSuggestCache::onEditorOpened);
connect(editorManager, &Core::EditorManager::editorAboutToClose,
this, &NimSuggestCache::onEditorClosed);
}
QString NimSuggestCache::executablePath() const
{
return m_executablePath;
}
void NimSuggestCache::setExecutablePath(const QString &path)
{
if (m_executablePath == path)
return;
m_executablePath = path;
for (const auto &pair : m_nimSuggestInstances) {
pair.second->setExecutablePath(path);
}
}
void Nim::Suggest::NimSuggestCache::onEditorOpened(Core::IEditor *editor)
{
if (editor->document()->mimeType() == Constants::C_NIM_MIMETYPE) {
get(editor->document()->filePath());
}
}
void Nim::Suggest::NimSuggestCache::onEditorClosed(Core::IEditor *editor)
{
auto it = m_nimSuggestInstances.find(editor->document()->filePath());
if (it != m_nimSuggestInstances.end())
m_nimSuggestInstances.erase(it);
}
}
}

View File

@@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 <utils/fileutils.h>
#include <QObject>
#include <unordered_map>
namespace Core { class IEditor; }
namespace Nim {
namespace Suggest {
class NimSuggest;
class NimSuggestCache : public QObject
{
Q_OBJECT
public:
static NimSuggestCache &instance();
NimSuggest *get(const Utils::FileName &filename);
QString executablePath() const;
void setExecutablePath(const QString &path);
private:
NimSuggestCache();
~NimSuggestCache();
void onEditorOpened(Core::IEditor *editor);
void onEditorClosed(Core::IEditor *editor);
std::unordered_map<Utils::FileName, std::unique_ptr<Suggest::NimSuggest>> m_nimSuggestInstances;
QString m_executablePath;
};
} // namespace Suggest
} // namespace Nim

View File

@@ -0,0 +1,125 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 "server.h"
namespace Nim {
namespace Suggest {
NimSuggestServer::NimSuggestServer(QObject *parent) : QObject(parent)
{
connect(&m_process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>
(&QProcess::finished), this, &NimSuggestServer::onFinished);
connect(&m_process, &QProcess::started, this, &NimSuggestServer::onStarted);
connect(&m_process, &QProcess::readyReadStandardOutput, this,
&NimSuggestServer::onStandardOutputAvailable);
}
NimSuggestServer::~NimSuggestServer()
{
kill();
}
QString NimSuggestServer::executablePath() const
{
return m_executablePath;
}
bool NimSuggestServer::start(const QString &executablePath,
const QString &projectFilePath)
{
if (!QFile::exists(executablePath)) {
qWarning() << "NimSuggest executable path" << executablePath << "does not exist";
return false;
}
if (!QFile::exists(projectFilePath)) {
qWarning() << "Project file" << projectFilePath << "doesn't exist";
return false;
}
m_port = 0;
m_executablePath = executablePath;
m_projectFilePath = projectFilePath;
m_process.start(executablePath, {"--epc", m_projectFilePath});
return true;
}
void NimSuggestServer::kill()
{
disconnect(&m_process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>
(&QProcess::finished), this, &NimSuggestServer::onFinished);
m_process.kill();
m_process.waitForFinished();
clearState();
}
quint16 NimSuggestServer::port() const
{
return m_port;
}
QString NimSuggestServer::projectFilePath() const
{
return m_projectFilePath;
}
void NimSuggestServer::onStarted()
{
m_started = true;
}
void NimSuggestServer::onStandardOutputAvailable()
{
if (m_started && !m_portAvailable) {
auto output = QString::fromUtf8(m_process.readAllStandardOutput());
m_port = static_cast<uint16_t>(output.toUInt());
m_portAvailable = true;
emit started();
} else {
qDebug() << m_process.readAllStandardOutput();
}
}
void NimSuggestServer::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
clearState();
Q_UNUSED(exitCode);
if (exitStatus == QProcess::ExitStatus::CrashExit)
emit crashed();
else
emit finished();
}
void NimSuggestServer::clearState()
{
m_started = false;
m_portAvailable = false;
m_port = 0;
}
} // namespace Suggest
} // namespace Nim

View File

@@ -0,0 +1,80 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 <QDebug>
#include <QFile>
#include <QObject>
#include <QProcess>
namespace Nim {
namespace Suggest {
class NimSuggestServer : public QObject
{
Q_OBJECT
public:
NimSuggestServer(QObject *parent = nullptr);
~NimSuggestServer();
bool start(const QString &executablePath, const QString &projectFilePath);
void kill();
quint16 port() const;
QString executablePath() const;
QString projectFilePath() const;
signals:
void started();
void finished();
void crashed();
private:
void onStarted();
void onStandardOutputAvailable();
void onFinished(int exitCode, QProcess::ExitStatus exitStatus);
void clearState();
bool m_started = false;
bool m_portAvailable = false;
QProcess m_process;
quint16 m_port = 0;
QString m_projectFilePath;
QString m_executablePath;
};
} // namespace Suggest
} // namespace Nim

View File

@@ -0,0 +1,181 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 <cstdint>
#include <cstring>
#include <string>
#include <vector>
namespace Nim {
struct SExprLexer {
enum Result {
Finished,
TokenAvailable,
Error
};
enum TokenType : std::size_t {
STRING,
NUMBER,
IDENTIFIER,
OPEN_BRACE,
CLOSE_BRACE,
};
struct Token {
TokenType type;
std::size_t start;
std::size_t end;
};
SExprLexer(const char *data, std::size_t length)
: m_data(data)
, m_dataLength(length)
, m_pos(0)
{}
SExprLexer(const char *data)
: SExprLexer(data, ::strlen(data))
{}
SExprLexer(const std::string &data)
: SExprLexer(data.c_str(), data.length())
{}
const char *data() const
{
return m_data;
}
std::size_t dataLength() const
{
return m_dataLength;
}
static size_t tokenLength(Token &token)
{
return token.end - token.start + 1;
}
std::string tokenValue(Token &token) const
{
return std::string(m_data + token.start, tokenLength(token));
}
Result next(Token &token)
{
while (m_pos < m_dataLength) {
if (m_data[m_pos] == '(') {
token = Token{OPEN_BRACE, m_pos, m_pos + 1};
m_pos++;
return TokenAvailable;
} else if (m_data[m_pos] == ')') {
token = Token{CLOSE_BRACE, m_pos, m_pos + 1};
m_pos++;
return TokenAvailable;
} else if (m_data[m_pos] == '"') {
Token token_tmp {STRING, m_pos, m_pos};
char previous = '"';
m_pos++;
while (true) {
if (m_pos >= m_dataLength)
return Error;
if (m_data[m_pos] == '"' && previous != '\\')
break;
previous = m_data[m_pos];
m_pos++;
}
token_tmp.end = m_pos;
token = std::move(token_tmp);
m_pos++;
return TokenAvailable;
} else if (std::isdigit(m_data[m_pos])) {
bool decimal_separator_found = false;
token = {NUMBER, m_pos, m_pos};
m_pos++;
while (true) {
if (m_pos >= m_dataLength)
break;
if (m_data[m_pos] == ',' || m_data[m_pos] == '.') {
if (decimal_separator_found)
return Error;
decimal_separator_found = true;
} else if (!std::isdigit(m_data[m_pos]))
break;
m_pos++;
}
token.end = m_pos - 1;
return TokenAvailable;
} else if (!std::isspace(m_data[m_pos])) {
token = {IDENTIFIER, m_pos, m_pos};
m_pos++;
while (true) {
if (m_pos >= m_dataLength)
break;
if (std::isspace(m_data[m_pos]) || m_data[m_pos] == '(' || m_data[m_pos] == ')')
break;
m_pos++;
}
token.end = m_pos - 1;
return TokenAvailable;
} else {
m_pos++;
}
}
return Finished;
}
static std::vector<Token> parse(const std::string &data)
{
return parse(data.data(), data.size());
}
static std::vector<Token> parse(const char *data)
{
return parse(data, ::strlen(data));
}
static std::vector<Token> parse(const char *data, std::size_t data_size)
{
std::vector<Token> result;
SExprLexer lexer(data, data_size);
Token token {};
while (lexer.next(token)) {
result.push_back(token);
token = {};
}
return result;
}
private:
const char *m_data = nullptr;
std::size_t m_dataLength = 0;
std::size_t m_pos = 0;
};
} // namespace Nim

View File

@@ -0,0 +1,118 @@
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://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 <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include "sexprlexer.h"
namespace Nim {
struct SExprParser {
enum NodeType : uint8_t {
ATOM_STRING = 1,
ATOM_NUMBER = 2,
ATOM_IDENTIFIER = 4,
LIST = 8
};
struct Node {
NodeType kind;
std::size_t from;
std::size_t end;
std::vector<Node> nodes;
std::string value;
};
SExprParser(const std::string &str)
: SExprParser(str.data(), str.length())
{}
SExprParser(const char *data)
: SExprParser(data, ::strlen(data))
{}
SExprParser(const char *data, std::size_t length)
: m_lexer(SExprLexer(data, length))
{}
bool parse(Node &node)
{
SExprLexer::Token token;
if (m_lexer.next(token) != SExprLexer::Result::TokenAvailable
|| token.type != SExprLexer::OPEN_BRACE)
return false;
node = Node {LIST, token.start, token.start, {}, {}};
return parseList(node);
}
private:
bool parseList(Node &node)
{
while (true) {
SExprLexer::Token token;
if (m_lexer.next(token) != SExprLexer::Result::TokenAvailable)
return false;
switch (token.type) {
case SExprLexer::STRING: {
std::string value = m_lexer.tokenValue(token);
value.pop_back();
value.erase(value.begin());
node.nodes.emplace_back(Node{ATOM_STRING, token.start, token.end, {}, std::move(value)});
break;
}
case SExprLexer::NUMBER: {
node.nodes.emplace_back(Node{ATOM_NUMBER, token.start, token.end, {}, m_lexer.tokenValue(token)});
break;
}
case SExprLexer::IDENTIFIER: {
node.nodes.emplace_back(Node{ATOM_IDENTIFIER, token.start, token.end, {}, m_lexer.tokenValue(token)});
break;
}
case SExprLexer::OPEN_BRACE: {
Node list {LIST, token.start, token.start, {}, {}};
if (!parseList(list)) {
return false;
}
node.nodes.emplace_back(std::move(list));
break;
}
case SExprLexer::CLOSE_BRACE: {
node.end = token.end;
return true;
}
}
}
}
SExprLexer m_lexer;
};
} // namespace Nim