forked from qt-creator/qt-creator
Haskell: Import from superrepo
Change-Id: I83c6c817c78ccbf1aee65847777f2652e19275a5 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -22,6 +22,7 @@ add_subdirectory(silversearcher)
|
||||
# Level 3: (only depends on Level 2 and below)
|
||||
add_subdirectory(bookmarks)
|
||||
add_subdirectory(cppeditor)
|
||||
add_subdirectory(haskell)
|
||||
add_subdirectory(help)
|
||||
add_subdirectory(resourceeditor)
|
||||
add_subdirectory(nim)
|
||||
|
27
src/plugins/haskell/CMakeLists.txt
Normal file
27
src/plugins/haskell/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
add_qtc_plugin(Haskell
|
||||
PLUGIN_DEPENDS
|
||||
QtCreator::Core QtCreator::TextEditor QtCreator::ProjectExplorer
|
||||
DEPENDS Qt5::Widgets
|
||||
SOURCES
|
||||
haskell.qrc
|
||||
haskell_global.h
|
||||
haskellbuildconfiguration.cpp haskellbuildconfiguration.h
|
||||
haskellconstants.h
|
||||
haskelleditorfactory.cpp haskelleditorfactory.h
|
||||
haskellhighlighter.cpp haskellhighlighter.h
|
||||
haskellmanager.cpp haskellmanager.h
|
||||
haskellplugin.cpp haskellplugin.h
|
||||
haskellproject.cpp haskellproject.h
|
||||
haskellrunconfiguration.cpp haskellrunconfiguration.h
|
||||
haskelltokenizer.cpp haskelltokenizer.h
|
||||
optionspage.cpp optionspage.h
|
||||
stackbuildstep.cpp stackbuildstep.h
|
||||
)
|
||||
|
||||
qtc_add_resources(Haskell haskell_wizards
|
||||
PREFIX "/haskell"
|
||||
BASE share/wizards
|
||||
FILES
|
||||
module/file.hs
|
||||
module/wizard.json
|
||||
)
|
23
src/plugins/haskell/Haskell.json.in
Normal file
23
src/plugins/haskell/Haskell.json.in
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
\"Name\" : \"Haskell\",
|
||||
\"Version\" : \"$$QTCREATOR_VERSION\",
|
||||
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
|
||||
\"DisabledByDefault\" : true,
|
||||
\"Vendor\" : \"Eike Ziller\",
|
||||
\"Copyright\" : \"(C) Eike Ziller\",
|
||||
\"License\" : \"MIT\",
|
||||
\"Description\" : \"Haskell support\",
|
||||
\"Url\" : \"https://haskell.org\",
|
||||
$$dependencyList,
|
||||
|
||||
\"Mimetypes\" : [
|
||||
\"<?xml version=\'1.0\'?>\",
|
||||
\"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\",
|
||||
\" <mime-type type=\'text/x-haskell-project\'>\",
|
||||
\" <sub-class-of type=\'text/plain\'/>\",
|
||||
\" <comment>Haskell Cabal project file</comment>\",
|
||||
\" <glob pattern=\'*.cabal\'/>\",
|
||||
\" </mime-type>\",
|
||||
\"</mime-info>\"
|
||||
]
|
||||
}
|
28
src/plugins/haskell/haskell.qbs
Normal file
28
src/plugins/haskell/haskell.qbs
Normal file
@@ -0,0 +1,28 @@
|
||||
import qbs 1.0
|
||||
|
||||
QtcPlugin {
|
||||
name: "Haskell"
|
||||
|
||||
Depends { name: "Qt.widgets" }
|
||||
Depends { name: "Utils" }
|
||||
|
||||
Depends { name: "Core" }
|
||||
Depends { name: "TextEditor" }
|
||||
Depends { name: "ProjectExplorer" }
|
||||
|
||||
files: [
|
||||
"haskell.qrc",
|
||||
"haskellbuildconfiguration.cpp", "haskellbuildconfiguration.h",
|
||||
"haskellconstants.h",
|
||||
"haskelleditorfactory.cpp", "haskelleditorfactory.h",
|
||||
"haskell_global.h",
|
||||
"haskellhighlighter.cpp", "haskellhighlighter.h",
|
||||
"haskellmanager.cpp", "haskellmanager.h",
|
||||
"haskellplugin.cpp", "haskellplugin.h",
|
||||
"haskellproject.cpp", "haskellproject.h",
|
||||
"haskellrunconfiguration.cpp", "haskellrunconfiguration.h",
|
||||
"haskelltokenizer.cpp", "haskelltokenizer.h",
|
||||
"optionspage.cpp", "optionspage.h",
|
||||
"stackbuildstep.cpp", "stackbuildstep.h"
|
||||
]
|
||||
}
|
5
src/plugins/haskell/haskell.qrc
Normal file
5
src/plugins/haskell/haskell.qrc
Normal file
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/haskell">
|
||||
<file>images/category_haskell.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
34
src/plugins/haskell/haskell_global.h
Normal file
34
src/plugins/haskell/haskell_global.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <QtGlobal>
|
||||
|
||||
#if defined(HASKELL_LIBRARY)
|
||||
# define HASKELLSHARED_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define HASKELLSHARED_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
132
src/plugins/haskell/haskellbuildconfiguration.cpp
Normal file
132
src/plugins/haskell/haskellbuildconfiguration.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskellbuildconfiguration.h"
|
||||
|
||||
#include "haskellconstants.h"
|
||||
|
||||
#include <projectexplorer/buildinfo.h>
|
||||
#include <projectexplorer/buildsteplist.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/target.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/detailswidget.h>
|
||||
#include <utils/mimeutils.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
const char C_HASKELL_BUILDCONFIGURATION_ID[] = "Haskell.BuildConfiguration";
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
HaskellBuildConfigurationFactory::HaskellBuildConfigurationFactory()
|
||||
{
|
||||
registerBuildConfiguration<HaskellBuildConfiguration>(C_HASKELL_BUILDCONFIGURATION_ID);
|
||||
setSupportedProjectType(Constants::C_HASKELL_PROJECT_ID);
|
||||
setSupportedProjectMimeTypeName(Constants::C_HASKELL_PROJECT_MIMETYPE);
|
||||
|
||||
setBuildGenerator([](const Kit *k, const Utils::FilePath &projectPath, bool forSetup) {
|
||||
BuildInfo info;
|
||||
info.typeName = HaskellBuildConfiguration::tr("Release");
|
||||
if (forSetup) {
|
||||
info.displayName = info.typeName;
|
||||
info.buildDirectory = projectPath.parentDir().pathAppended(".stack-work");
|
||||
}
|
||||
info.kitId = k->id();
|
||||
info.buildType = BuildConfiguration::BuildType::Release;
|
||||
return QList<BuildInfo>{info};
|
||||
});
|
||||
}
|
||||
|
||||
HaskellBuildConfiguration::HaskellBuildConfiguration(Target *target, Utils::Id id)
|
||||
: BuildConfiguration(target, id)
|
||||
{
|
||||
setInitializer([this](const BuildInfo &info) {
|
||||
setBuildDirectory(info.buildDirectory);
|
||||
setBuildType(info.buildType);
|
||||
setDisplayName(info.displayName);
|
||||
});
|
||||
appendInitialBuildStep(Constants::C_STACK_BUILD_STEP_ID);
|
||||
}
|
||||
|
||||
NamedWidget *HaskellBuildConfiguration::createConfigWidget()
|
||||
{
|
||||
return new HaskellBuildConfigurationWidget(this);
|
||||
}
|
||||
|
||||
BuildConfiguration::BuildType HaskellBuildConfiguration::buildType() const
|
||||
{
|
||||
return m_buildType;
|
||||
}
|
||||
|
||||
void HaskellBuildConfiguration::setBuildType(BuildConfiguration::BuildType type)
|
||||
{
|
||||
m_buildType = type;
|
||||
}
|
||||
|
||||
HaskellBuildConfigurationWidget::HaskellBuildConfigurationWidget(HaskellBuildConfiguration *bc)
|
||||
: NamedWidget(tr("General"))
|
||||
, m_buildConfiguration(bc)
|
||||
{
|
||||
setLayout(new QVBoxLayout);
|
||||
layout()->setContentsMargins(0, 0, 0, 0);
|
||||
auto box = new Utils::DetailsWidget;
|
||||
box->setState(Utils::DetailsWidget::NoSummary);
|
||||
layout()->addWidget(box);
|
||||
auto details = new QWidget;
|
||||
box->setWidget(details);
|
||||
details->setLayout(new QHBoxLayout);
|
||||
details->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
details->layout()->addWidget(new QLabel(tr("Build directory:")));
|
||||
|
||||
auto buildDirectoryInput = new Utils::PathChooser;
|
||||
buildDirectoryInput->setExpectedKind(Utils::PathChooser::Directory);
|
||||
buildDirectoryInput->setFilePath(m_buildConfiguration->buildDirectory());
|
||||
details->layout()->addWidget(buildDirectoryInput);
|
||||
|
||||
connect(m_buildConfiguration,
|
||||
&BuildConfiguration::buildDirectoryChanged,
|
||||
buildDirectoryInput,
|
||||
[this, buildDirectoryInput] {
|
||||
buildDirectoryInput->setFilePath(m_buildConfiguration->buildDirectory());
|
||||
});
|
||||
connect(buildDirectoryInput,
|
||||
&Utils::PathChooser::textChanged,
|
||||
m_buildConfiguration,
|
||||
[this, buildDirectoryInput](const QString &) {
|
||||
m_buildConfiguration->setBuildDirectory(buildDirectoryInput->rawFilePath());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
67
src/plugins/haskell/haskellbuildconfiguration.h
Normal file
67
src/plugins/haskell/haskellbuildconfiguration.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/namedwidget.h>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class HaskellBuildConfigurationFactory : public ProjectExplorer::BuildConfigurationFactory
|
||||
{
|
||||
public:
|
||||
HaskellBuildConfigurationFactory();
|
||||
};
|
||||
|
||||
class HaskellBuildConfiguration : public ProjectExplorer::BuildConfiguration
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HaskellBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id);
|
||||
|
||||
ProjectExplorer::NamedWidget *createConfigWidget() override;
|
||||
BuildType buildType() const override;
|
||||
void setBuildType(BuildType type);
|
||||
|
||||
private:
|
||||
BuildType m_buildType = BuildType::Release;
|
||||
};
|
||||
|
||||
class HaskellBuildConfigurationWidget : public ProjectExplorer::NamedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HaskellBuildConfigurationWidget(HaskellBuildConfiguration *bc);
|
||||
|
||||
private:
|
||||
HaskellBuildConfiguration *m_buildConfiguration;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
41
src/plugins/haskell/haskellconstants.h
Normal file
41
src/plugins/haskell/haskellconstants.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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
|
||||
|
||||
namespace Haskell {
|
||||
namespace Constants {
|
||||
|
||||
const char C_HASKELLEDITOR_ID[] = "Haskell.HaskellEditor";
|
||||
const char C_HASKELLSNIPPETSGROUP_ID[] = "Haskell";
|
||||
const char C_HASKELL_PROJECT_MIMETYPE[] = "text/x-haskell-project";
|
||||
const char C_HASKELL_PROJECT_ID[] = "Haskell.Project";
|
||||
const char C_HASKELL_RUNCONFIG_ID[] = "Haskell.RunConfiguration";
|
||||
const char C_STACK_BUILD_STEP_ID[] = "Haskell.Stack.Build";
|
||||
const char OPTIONS_GENERAL[] = "Haskell.A.General";
|
||||
const char A_RUN_GHCI[] = "Haskell.RunGHCi";
|
||||
|
||||
} // namespace Haskell
|
||||
} // namespace Constants
|
71
src/plugins/haskell/haskelleditorfactory.cpp
Normal file
71
src/plugins/haskell/haskelleditorfactory.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskelleditorfactory.h"
|
||||
|
||||
#include "haskellconstants.h"
|
||||
#include "haskellhighlighter.h"
|
||||
#include "haskellmanager.h"
|
||||
|
||||
#include <coreplugin/actionmanager/commandbutton.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/texteditoractionhandler.h>
|
||||
#include <texteditor/textindenter.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
static QWidget *createEditorWidget()
|
||||
{
|
||||
auto widget = new TextEditor::TextEditorWidget;
|
||||
auto ghciButton = new Core::CommandButton(Constants::A_RUN_GHCI, widget);
|
||||
ghciButton->setText(HaskellManager::tr("GHCi"));
|
||||
QObject::connect(ghciButton, &QToolButton::clicked, HaskellManager::instance(), [widget] {
|
||||
HaskellManager::openGhci(widget->textDocument()->filePath());
|
||||
});
|
||||
widget->insertExtraToolBarWidget(TextEditor::TextEditorWidget::Left, ghciButton);
|
||||
return widget;
|
||||
}
|
||||
|
||||
HaskellEditorFactory::HaskellEditorFactory()
|
||||
{
|
||||
setId(Constants::C_HASKELLEDITOR_ID);
|
||||
setDisplayName(QCoreApplication::translate("OpenWith::Editors", "Haskell Editor"));
|
||||
addMimeType("text/x-haskell");
|
||||
setEditorActionHandlers(TextEditor::TextEditorActionHandler::UnCommentSelection
|
||||
| TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor);
|
||||
setDocumentCreator([] { return new TextEditor::TextDocument(Constants::C_HASKELLEDITOR_ID); });
|
||||
setIndenterCreator([](QTextDocument *doc) { return new TextEditor::TextIndenter(doc); });
|
||||
setEditorWidgetCreator(createEditorWidget);
|
||||
setCommentDefinition(Utils::CommentDefinition("--", "{-", "-}"));
|
||||
setParenthesesMatchingEnabled(true);
|
||||
setMarksVisible(true);
|
||||
setSyntaxHighlighterCreator([] { return new HaskellHighlighter(); });
|
||||
}
|
||||
|
||||
} // Internal
|
||||
} // Haskell
|
40
src/plugins/haskell/haskelleditorfactory.h
Normal file
40
src/plugins/haskell/haskelleditorfactory.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <texteditor/texteditor.h>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class HaskellEditorFactory : public TextEditor::TextEditorFactory
|
||||
{
|
||||
public:
|
||||
HaskellEditorFactory();
|
||||
};
|
||||
|
||||
} // Internal
|
||||
} // Haskell
|
152
src/plugins/haskell/haskellhighlighter.cpp
Normal file
152
src/plugins/haskell/haskellhighlighter.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskellhighlighter.h"
|
||||
|
||||
#include "haskelltokenizer.h"
|
||||
|
||||
#include <texteditor/fontsettings.h>
|
||||
#include <texteditor/texteditorconstants.h>
|
||||
#include <texteditor/texteditorsettings.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QVector>
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(QSet<QString>, IMPORT_HIGHLIGHTS, ({
|
||||
"qualified",
|
||||
"as",
|
||||
"hiding"
|
||||
}));
|
||||
|
||||
using namespace TextEditor;
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
HaskellHighlighter::HaskellHighlighter()
|
||||
{
|
||||
setDefaultTextFormatCategories();
|
||||
updateFormats(TextEditorSettings::fontSettings());
|
||||
}
|
||||
|
||||
void HaskellHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
const Tokens tokens = HaskellTokenizer::tokenize(text, previousBlockState());
|
||||
setCurrentBlockState(tokens.state);
|
||||
const Token *firstNonWS = 0;
|
||||
const Token *secondNonWS = 0;
|
||||
bool inType = false;
|
||||
bool inImport = false;
|
||||
for (const Token & token : tokens) {
|
||||
switch (token.type) {
|
||||
case TokenType::Variable:
|
||||
if (inType)
|
||||
setTokenFormat(token, C_LOCAL);
|
||||
else if (inImport && IMPORT_HIGHLIGHTS->contains(token.text.toString()))
|
||||
setTokenFormat(token, C_KEYWORD);
|
||||
// else
|
||||
// setTokenFormat(token, C_TEXT);
|
||||
break;
|
||||
case TokenType::Constructor:
|
||||
case TokenType::OperatorConstructor:
|
||||
setTokenFormat(token, C_TYPE);
|
||||
break;
|
||||
case TokenType::Operator:
|
||||
setTokenFormat(token, C_OPERATOR);
|
||||
break;
|
||||
case TokenType::Whitespace:
|
||||
setTokenFormat(token, C_VISUAL_WHITESPACE);
|
||||
break;
|
||||
case TokenType::Keyword:
|
||||
if (token.text == QLatin1String("::") && firstNonWS && !secondNonWS) { // toplevel declaration
|
||||
setFormat(firstNonWS->startCol, firstNonWS->length, m_toplevelDeclFormat);
|
||||
inType = true;
|
||||
} else if (token.text == QLatin1String("import")) {
|
||||
inImport = true;
|
||||
}
|
||||
setTokenFormat(token, C_KEYWORD);
|
||||
break;
|
||||
case TokenType::Integer:
|
||||
case TokenType::Float:
|
||||
setTokenFormat(token, C_NUMBER);
|
||||
break;
|
||||
case TokenType::String:
|
||||
setTokenFormatWithSpaces(text, token, C_STRING);
|
||||
break;
|
||||
case TokenType::Char:
|
||||
setTokenFormatWithSpaces(text, token, C_STRING);
|
||||
break;
|
||||
case TokenType::EscapeSequence:
|
||||
setTokenFormat(token, C_PRIMITIVE_TYPE);
|
||||
break;
|
||||
case TokenType::SingleLineComment:
|
||||
setTokenFormatWithSpaces(text, token, C_COMMENT);
|
||||
break;
|
||||
case TokenType::MultiLineComment:
|
||||
setTokenFormatWithSpaces(text, token, C_COMMENT);
|
||||
break;
|
||||
case TokenType::Special:
|
||||
// setTokenFormat(token, C_TEXT);
|
||||
break;
|
||||
case TokenType::StringError:
|
||||
case TokenType::CharError:
|
||||
case TokenType::Unknown:
|
||||
setTokenFormat(token, C_PARENTHESES_MISMATCH);
|
||||
break;
|
||||
}
|
||||
if (token.type != TokenType::Whitespace) {
|
||||
if (!firstNonWS)
|
||||
firstNonWS = &token;
|
||||
else if (!secondNonWS)
|
||||
secondNonWS = &token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HaskellHighlighter::setFontSettings(const FontSettings &fontSettings)
|
||||
{
|
||||
SyntaxHighlighter::setFontSettings(fontSettings);
|
||||
updateFormats(fontSettings);
|
||||
}
|
||||
|
||||
void HaskellHighlighter::updateFormats(const FontSettings &fontSettings)
|
||||
{
|
||||
m_toplevelDeclFormat = fontSettings.toTextCharFormat(
|
||||
TextStyles::mixinStyle(C_FUNCTION, C_DECLARATION));
|
||||
}
|
||||
|
||||
void HaskellHighlighter::setTokenFormat(const Token &token, TextStyle style)
|
||||
{
|
||||
setFormat(token.startCol, token.length, formatForCategory(style));
|
||||
}
|
||||
|
||||
void HaskellHighlighter::setTokenFormatWithSpaces(const QString &text, const Token &token,
|
||||
TextStyle style)
|
||||
{
|
||||
setFormatWithSpaces(text, token.startCol, token.length, formatForCategory(style));
|
||||
}
|
||||
|
||||
} // Internal
|
||||
} // Haskell
|
58
src/plugins/haskell/haskellhighlighter.h
Normal file
58
src/plugins/haskell/haskellhighlighter.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <texteditor/syntaxhighlighter.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QTextFormat>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class Token;
|
||||
|
||||
class HaskellHighlighter : public TextEditor::SyntaxHighlighter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HaskellHighlighter();
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text) override;
|
||||
|
||||
private:
|
||||
void setFontSettings(const TextEditor::FontSettings &fontSettings) override;
|
||||
void updateFormats(const TextEditor::FontSettings &fontSettings);
|
||||
void setTokenFormat(const Token &token, TextEditor::TextStyle style);
|
||||
void setTokenFormatWithSpaces(const QString &text, const Token &token,
|
||||
TextEditor::TextStyle style);
|
||||
QTextCharFormat m_toplevelDeclFormat;
|
||||
};
|
||||
|
||||
} // Internal
|
||||
} // Haskell
|
141
src/plugins/haskell/haskellmanager.cpp
Normal file
141
src/plugins/haskell/haskellmanager.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskellmanager.h"
|
||||
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/commandline.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/mimeutils.h>
|
||||
#include <utils/processenums.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
static const char kStackExecutableKey[] = "Haskell/StackExecutable";
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class HaskellManagerPrivate
|
||||
{
|
||||
public:
|
||||
FilePath stackExecutable;
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC(HaskellManagerPrivate, m_d)
|
||||
Q_GLOBAL_STATIC(HaskellManager, m_instance)
|
||||
|
||||
HaskellManager *HaskellManager::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
FilePath HaskellManager::findProjectDirectory(const FilePath &filePath)
|
||||
{
|
||||
if (filePath.isEmpty())
|
||||
return {};
|
||||
|
||||
QDir directory(filePath.toFileInfo().isDir() ? filePath.toString()
|
||||
: filePath.parentDir().toString());
|
||||
directory.setNameFilters({"stack.yaml", "*.cabal"});
|
||||
directory.setFilter(QDir::Files | QDir::Readable);
|
||||
do {
|
||||
if (!directory.entryList().isEmpty())
|
||||
return FilePath::fromString(directory.path());
|
||||
} while (!directory.isRoot() && directory.cdUp());
|
||||
return {};
|
||||
}
|
||||
|
||||
FilePath defaultStackExecutable()
|
||||
{
|
||||
// stack from brew or the installer script from https://docs.haskellstack.org
|
||||
// install to /usr/local/bin.
|
||||
if (HostOsInfo::isAnyUnixHost())
|
||||
return FilePath::fromString("/usr/local/bin/stack");
|
||||
return FilePath::fromString("stack");
|
||||
}
|
||||
|
||||
FilePath HaskellManager::stackExecutable()
|
||||
{
|
||||
return m_d->stackExecutable;
|
||||
}
|
||||
|
||||
void HaskellManager::setStackExecutable(const FilePath &filePath)
|
||||
{
|
||||
if (filePath == m_d->stackExecutable)
|
||||
return;
|
||||
m_d->stackExecutable = filePath;
|
||||
emit m_instance->stackExecutableChanged(m_d->stackExecutable);
|
||||
}
|
||||
|
||||
void HaskellManager::openGhci(const FilePath &haskellFile)
|
||||
{
|
||||
const QList<MimeType> mimeTypes = mimeTypesForFileName(haskellFile.toString());
|
||||
const bool isHaskell = Utils::anyOf(mimeTypes, [](const MimeType &mt) {
|
||||
return mt.inherits("text/x-haskell") || mt.inherits("text/x-literate-haskell");
|
||||
});
|
||||
const auto args = QStringList{"ghci"}
|
||||
+ (isHaskell ? QStringList{haskellFile.fileName()} : QStringList());
|
||||
auto p = new QtcProcess(m_instance);
|
||||
p->setTerminalMode(TerminalMode::On);
|
||||
p->setCommand({stackExecutable(), args});
|
||||
p->setWorkingDirectory(haskellFile.absolutePath());
|
||||
connect(p, &QtcProcess::done, p, [p] {
|
||||
if (p->result() != ProcessResult::FinishedWithSuccess) {
|
||||
Core::MessageManager::writeDisrupting(
|
||||
tr("Failed to run GHCi: \"%1\".").arg(p->errorString()));
|
||||
}
|
||||
p->deleteLater();
|
||||
});
|
||||
p->start();
|
||||
}
|
||||
|
||||
void HaskellManager::readSettings(QSettings *settings)
|
||||
{
|
||||
m_d->stackExecutable = FilePath::fromString(
|
||||
settings->value(kStackExecutableKey,
|
||||
defaultStackExecutable().toString()).toString());
|
||||
emit m_instance->stackExecutableChanged(m_d->stackExecutable);
|
||||
}
|
||||
|
||||
void HaskellManager::writeSettings(QSettings *settings)
|
||||
{
|
||||
if (m_d->stackExecutable == defaultStackExecutable())
|
||||
settings->remove(kStackExecutableKey);
|
||||
else
|
||||
settings->setValue(kStackExecutableKey, m_d->stackExecutable.toString());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
58
src/plugins/haskell/haskellmanager.h
Normal file
58
src/plugins/haskell/haskellmanager.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <QObject>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSettings;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class HaskellManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static HaskellManager *instance();
|
||||
|
||||
static Utils::FilePath findProjectDirectory(const Utils::FilePath &filePath);
|
||||
static Utils::FilePath stackExecutable();
|
||||
static void setStackExecutable(const Utils::FilePath &filePath);
|
||||
static void openGhci(const Utils::FilePath &haskellFile);
|
||||
static void readSettings(QSettings *settings);
|
||||
static void writeSettings(QSettings *settings);
|
||||
|
||||
signals:
|
||||
void stackExecutableChanged(const Utils::FilePath &filePath);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
100
src/plugins/haskell/haskellplugin.cpp
Normal file
100
src/plugins/haskell/haskellplugin.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskellplugin.h"
|
||||
|
||||
#include "haskellbuildconfiguration.h"
|
||||
#include "haskellconstants.h"
|
||||
#include "haskelleditorfactory.h"
|
||||
#include "haskellmanager.h"
|
||||
#include "haskellproject.h"
|
||||
#include "haskellrunconfiguration.h"
|
||||
#include "optionspage.h"
|
||||
#include "stackbuildstep.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/projectmanager.h>
|
||||
#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
|
||||
#include <texteditor/snippets/snippetprovider.h>
|
||||
|
||||
#include <QAction>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class HaskellPluginPrivate
|
||||
{
|
||||
public:
|
||||
HaskellEditorFactory editorFactory;
|
||||
OptionsPage optionsPage;
|
||||
HaskellBuildConfigurationFactory buildConfigFactory;
|
||||
StackBuildStepFactory stackBuildStepFactory;
|
||||
HaskellRunConfigurationFactory runConfigFactory;
|
||||
ProjectExplorer::SimpleTargetRunnerFactory runWorkerFactory{{Constants::C_HASKELL_RUNCONFIG_ID}};
|
||||
};
|
||||
|
||||
HaskellPlugin::~HaskellPlugin()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
static void registerGhciAction()
|
||||
{
|
||||
QAction *action = new QAction(HaskellManager::tr("Run GHCi"), HaskellManager::instance());
|
||||
Core::ActionManager::registerAction(action, Constants::A_RUN_GHCI);
|
||||
QObject::connect(action, &QAction::triggered, HaskellManager::instance(), [] {
|
||||
if (Core::IDocument *doc = Core::EditorManager::currentDocument())
|
||||
HaskellManager::openGhci(doc->filePath());
|
||||
});
|
||||
}
|
||||
|
||||
bool HaskellPlugin::initialize(const QStringList &arguments, QString *errorString)
|
||||
{
|
||||
Q_UNUSED(arguments)
|
||||
Q_UNUSED(errorString)
|
||||
|
||||
d = new HaskellPluginPrivate;
|
||||
|
||||
ProjectExplorer::ProjectManager::registerProjectType<HaskellProject>(
|
||||
Constants::C_HASKELL_PROJECT_MIMETYPE);
|
||||
TextEditor::SnippetProvider::registerGroup(Constants::C_HASKELLSNIPPETSGROUP_ID,
|
||||
tr("Haskell", "SnippetProvider"));
|
||||
|
||||
connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, [] {
|
||||
HaskellManager::writeSettings(Core::ICore::settings());
|
||||
});
|
||||
|
||||
registerGhciAction();
|
||||
|
||||
HaskellManager::readSettings(Core::ICore::settings());
|
||||
|
||||
ProjectExplorer::JsonWizardFactory::addWizardPath(":/haskell/share/wizards/");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
52
src/plugins/haskell/haskellplugin.h
Normal file
52
src/plugins/haskell/haskellplugin.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskell_global.h"
|
||||
|
||||
#include <extensionsystem/iplugin.h>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class HaskellPlugin : public ExtensionSystem::IPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Haskell.json")
|
||||
|
||||
public:
|
||||
HaskellPlugin() = default;
|
||||
~HaskellPlugin() final;
|
||||
|
||||
private:
|
||||
bool initialize(const QStringList &arguments, QString *errorString) final;
|
||||
void extensionsInitialized() final {}
|
||||
|
||||
class HaskellPluginPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
135
src/plugins/haskell/haskellproject.cpp
Normal file
135
src/plugins/haskell/haskellproject.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskellproject.h"
|
||||
|
||||
#include "haskellconstants.h"
|
||||
|
||||
#include <coreplugin/iversioncontrol.h>
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
|
||||
#include <projectexplorer/buildtargetinfo.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/runextensions.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
static QVector<QString> parseExecutableNames(const FilePath &projectFilePath)
|
||||
{
|
||||
static const QString EXECUTABLE = "executable";
|
||||
static const int EXECUTABLE_LEN = EXECUTABLE.length();
|
||||
QVector<QString> result;
|
||||
QFile file(projectFilePath.toString());
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
QTextStream stream(&file);
|
||||
while (!stream.atEnd()) {
|
||||
const QString line = stream.readLine().trimmed();
|
||||
if (line.length() > EXECUTABLE_LEN && line.startsWith(EXECUTABLE)
|
||||
&& line.at(EXECUTABLE_LEN).isSpace())
|
||||
result.append(line.mid(EXECUTABLE_LEN + 1).trimmed());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
HaskellProject::HaskellProject(const Utils::FilePath &fileName)
|
||||
: Project(Constants::C_HASKELL_PROJECT_MIMETYPE, fileName)
|
||||
{
|
||||
setId(Constants::C_HASKELL_PROJECT_ID);
|
||||
setDisplayName(fileName.toFileInfo().completeBaseName());
|
||||
setBuildSystemCreator([](Target *t) { return new HaskellBuildSystem(t); });
|
||||
}
|
||||
|
||||
bool HaskellProject::isHaskellProject(Project *project)
|
||||
{
|
||||
return project && project->id() == Constants::C_HASKELL_PROJECT_ID;
|
||||
}
|
||||
|
||||
HaskellBuildSystem::HaskellBuildSystem(Target *t)
|
||||
: BuildSystem(t)
|
||||
{
|
||||
connect(&m_scanner, &TreeScanner::finished, this, [this] {
|
||||
auto root = std::make_unique<ProjectNode>(projectDirectory());
|
||||
root->setDisplayName(target()->project()->displayName());
|
||||
std::vector<std::unique_ptr<FileNode>> nodePtrs
|
||||
= Utils::transform<std::vector>(m_scanner.release().allFiles, [](FileNode *fn) {
|
||||
return std::unique_ptr<FileNode>(fn);
|
||||
});
|
||||
root->addNestedNodes(std::move(nodePtrs));
|
||||
setRootProjectNode(std::move(root));
|
||||
|
||||
updateApplicationTargets();
|
||||
|
||||
m_parseGuard.markAsSuccess();
|
||||
m_parseGuard = {};
|
||||
|
||||
emitBuildSystemUpdated();
|
||||
});
|
||||
|
||||
connect(target()->project(),
|
||||
&Project::projectFileIsDirty,
|
||||
this,
|
||||
&BuildSystem::requestDelayedParse);
|
||||
|
||||
requestDelayedParse();
|
||||
}
|
||||
|
||||
void HaskellBuildSystem::triggerParsing()
|
||||
{
|
||||
m_parseGuard = guardParsingRun();
|
||||
m_scanner.asyncScanForFiles(target()->project()->projectDirectory());
|
||||
}
|
||||
|
||||
void HaskellBuildSystem::updateApplicationTargets()
|
||||
{
|
||||
const QVector<QString> executables = parseExecutableNames(projectFilePath());
|
||||
const Utils::FilePath projFilePath = projectFilePath();
|
||||
const QList<BuildTargetInfo> appTargets
|
||||
= Utils::transform<QList>(executables, [projFilePath](const QString &executable) {
|
||||
BuildTargetInfo bti;
|
||||
bti.displayName = executable;
|
||||
bti.buildKey = executable;
|
||||
bti.targetFilePath = FilePath::fromString(executable);
|
||||
bti.projectFilePath = projFilePath;
|
||||
bti.isQtcRunnable = true;
|
||||
return bti;
|
||||
});
|
||||
setApplicationTargets(appTargets);
|
||||
target()->updateDefaultRunConfigurations();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
66
src/plugins/haskell/haskellproject.h
Normal file
66
src/plugins/haskell/haskellproject.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <projectexplorer/buildsystem.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectnodes.h>
|
||||
#include <projectexplorer/treescanner.h>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class HaskellProject : public ProjectExplorer::Project
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HaskellProject(const Utils::FilePath &fileName);
|
||||
|
||||
static bool isHaskellProject(Project *project);
|
||||
};
|
||||
|
||||
class HaskellBuildSystem : public ProjectExplorer::BuildSystem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HaskellBuildSystem(ProjectExplorer::Target *t);
|
||||
|
||||
void triggerParsing() override;
|
||||
QString name() const final { return QLatin1String("haskell"); }
|
||||
|
||||
private:
|
||||
void updateApplicationTargets();
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
ParseGuard m_parseGuard;
|
||||
ProjectExplorer::TreeScanner m_scanner;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
97
src/plugins/haskell/haskellrunconfiguration.cpp
Normal file
97
src/plugins/haskell/haskellrunconfiguration.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskellrunconfiguration.h"
|
||||
|
||||
#include "haskellconstants.h"
|
||||
#include "haskellmanager.h"
|
||||
#include "haskellproject.h"
|
||||
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/localenvironmentaspect.h>
|
||||
#include <projectexplorer/runconfigurationaspects.h>
|
||||
#include <projectexplorer/runcontrol.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
HaskellRunConfigurationFactory::HaskellRunConfigurationFactory()
|
||||
{
|
||||
registerRunConfiguration<HaskellRunConfiguration>(Constants::C_HASKELL_RUNCONFIG_ID);
|
||||
addSupportedProjectType(Constants::C_HASKELL_PROJECT_ID);
|
||||
addSupportedTargetDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE);
|
||||
}
|
||||
|
||||
HaskellExecutableAspect::HaskellExecutableAspect()
|
||||
{
|
||||
setSettingsKey("Haskell.Executable");
|
||||
setLabelText(tr("Executable"));
|
||||
}
|
||||
|
||||
HaskellRunConfiguration::HaskellRunConfiguration(Target *target, Utils::Id id)
|
||||
: RunConfiguration(target, id)
|
||||
{
|
||||
auto envAspect = addAspect<LocalEnvironmentAspect>(target);
|
||||
|
||||
addAspect<HaskellExecutableAspect>();
|
||||
addAspect<ArgumentsAspect>(macroExpander());
|
||||
|
||||
auto workingDirAspect = addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
|
||||
workingDirAspect->setDefaultWorkingDirectory(target->project()->projectDirectory());
|
||||
workingDirAspect->setVisible(false);
|
||||
|
||||
addAspect<TerminalAspect>();
|
||||
|
||||
setUpdater([this] { aspect<HaskellExecutableAspect>()->setValue(buildTargetInfo().buildKey); });
|
||||
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
|
||||
update();
|
||||
}
|
||||
|
||||
Runnable HaskellRunConfiguration::runnable() const
|
||||
{
|
||||
const Utils::FilePath projectDirectory = target()->project()->projectDirectory();
|
||||
Runnable r;
|
||||
QStringList args;
|
||||
if (BuildConfiguration *buildConfiguration = target()->activeBuildConfiguration()) {
|
||||
args << "--work-dir"
|
||||
<< QDir(projectDirectory.toString()).relativeFilePath(
|
||||
buildConfiguration->buildDirectory().toString());
|
||||
}
|
||||
args << "exec" << aspect<HaskellExecutableAspect>()->value();
|
||||
const QString arguments = aspect<ArgumentsAspect>()->arguments();
|
||||
if (!arguments.isEmpty())
|
||||
args << "--" << arguments;
|
||||
|
||||
r.workingDirectory = projectDirectory;
|
||||
r.environment = aspect<LocalEnvironmentAspect>()->environment();
|
||||
r.command = {r.environment.searchInPath(HaskellManager::stackExecutable().toString()), args};
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
62
src/plugins/haskell/haskellrunconfiguration.h
Normal file
62
src/plugins/haskell/haskellrunconfiguration.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/runconfigurationaspects.h>
|
||||
#include <projectexplorer/runcontrol.h>
|
||||
#include <utils/aspects.h>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class HaskellRunConfiguration : public ProjectExplorer::RunConfiguration
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HaskellRunConfiguration(ProjectExplorer::Target *target, Utils::Id id);
|
||||
|
||||
private:
|
||||
ProjectExplorer::Runnable runnable() const final;
|
||||
};
|
||||
|
||||
class HaskellRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory
|
||||
{
|
||||
public:
|
||||
HaskellRunConfigurationFactory();
|
||||
};
|
||||
|
||||
class HaskellExecutableAspect : public Utils::StringAspect
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HaskellExecutableAspect();
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
643
src/plugins/haskell/haskelltokenizer.cpp
Normal file
643
src/plugins/haskell/haskelltokenizer.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "haskelltokenizer.h"
|
||||
|
||||
#include <QSet>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(QSet<QString>, RESERVED_OP, ({
|
||||
"..",
|
||||
":",
|
||||
"::",
|
||||
"=",
|
||||
"\\",
|
||||
"|",
|
||||
"<-",
|
||||
"->",
|
||||
"@",
|
||||
"~",
|
||||
"=>",
|
||||
|
||||
// Arrows GHC extension
|
||||
"-<",
|
||||
"-<<",
|
||||
">-",
|
||||
">>-",
|
||||
"(|",
|
||||
"|)"
|
||||
}));
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(QSet<QString>, RESERVED_ID, ({
|
||||
"case",
|
||||
"class",
|
||||
"data",
|
||||
"default",
|
||||
"deriving",
|
||||
"do",
|
||||
"else",
|
||||
"foreign",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"infix",
|
||||
"infixl",
|
||||
"infixr",
|
||||
"instance",
|
||||
"let",
|
||||
"module",
|
||||
"newtype",
|
||||
"of",
|
||||
"then",
|
||||
"type",
|
||||
"where",
|
||||
"_",
|
||||
|
||||
// from GHC extensions
|
||||
"family",
|
||||
"forall",
|
||||
"mdo",
|
||||
"proc",
|
||||
"rec"
|
||||
}));
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(QSet<QChar>, SPECIAL, ({
|
||||
'(',
|
||||
')',
|
||||
',',
|
||||
';',
|
||||
'[',
|
||||
']',
|
||||
'`',
|
||||
'{',
|
||||
'}',
|
||||
}));
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(QSet<QChar>, CHAR_ESCAPES,
|
||||
({'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '&'}));
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(QVector<QString>, ASCII_ESCAPES, ({
|
||||
"NUL",
|
||||
"SOH", // must be before "SO" to match
|
||||
"STX",
|
||||
"ETX",
|
||||
"EOT",
|
||||
"ENQ",
|
||||
"ACK",
|
||||
"BEL",
|
||||
"BS",
|
||||
"HT",
|
||||
"LF",
|
||||
"VT",
|
||||
"FF",
|
||||
"CR",
|
||||
"SO",
|
||||
"SI",
|
||||
"DLE",
|
||||
"DC1",
|
||||
"DC2",
|
||||
"DC3",
|
||||
"DC4",
|
||||
"NAK",
|
||||
"SYN",
|
||||
"ETB",
|
||||
"CAN",
|
||||
"EM",
|
||||
"SUB",
|
||||
"ESC",
|
||||
"FS",
|
||||
"GS",
|
||||
"RS",
|
||||
"US",
|
||||
"SP",
|
||||
"DEL"
|
||||
}));
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
Token token(TokenType type, std::shared_ptr<QString> line, int start, int end)
|
||||
{
|
||||
return {type, start, end - start, QStringView(*line).mid(start, end - start), line};
|
||||
}
|
||||
|
||||
Tokens::Tokens(std::shared_ptr<QString> source)
|
||||
: source(source)
|
||||
{
|
||||
}
|
||||
|
||||
Token Tokens::tokenAtColumn(int col) const
|
||||
{
|
||||
auto it = std::upper_bound(begin(), end(), col, [](int c, const Token &i) {
|
||||
return c < i.startCol;
|
||||
});
|
||||
if (it == begin())
|
||||
return Token();
|
||||
--it;
|
||||
if (it->startCol + it->length > col)
|
||||
return *it;
|
||||
return Token();
|
||||
}
|
||||
|
||||
static int grab(const QString &line, int begin,
|
||||
const std::function<bool(const QChar&)> &test)
|
||||
{
|
||||
const int length = line.length();
|
||||
int current = begin;
|
||||
while (current < length && test(line.at(current)))
|
||||
++current;
|
||||
return current - begin;
|
||||
};
|
||||
|
||||
static bool isIdentifierChar(const QChar &c)
|
||||
{
|
||||
return c.isLetterOrNumber() || c == '\'' || c == '_';
|
||||
}
|
||||
|
||||
static bool isVariableIdentifierStart(const QChar &c)
|
||||
{
|
||||
return c == '_' || c.isLower();
|
||||
}
|
||||
|
||||
static bool isAscSymbol(const QChar &c)
|
||||
{
|
||||
return c == '!'
|
||||
|| c == '#'
|
||||
|| c == '$'
|
||||
|| c == '%'
|
||||
|| c == '&'
|
||||
|| c == '*'
|
||||
|| c == '+'
|
||||
|| c == '.'
|
||||
|| c == '/'
|
||||
|| c == '<'
|
||||
|| c == '='
|
||||
|| c == '>'
|
||||
|| c == '?'
|
||||
|| c == '@'
|
||||
|| c == '\\'
|
||||
|| c == '^'
|
||||
|| c == '|'
|
||||
|| c == '-'
|
||||
|| c == '~'
|
||||
|| c == ':';
|
||||
}
|
||||
|
||||
static bool isSymbol(const QChar &c)
|
||||
{
|
||||
return isAscSymbol(c)
|
||||
|| ((c.isSymbol() || c.isPunct()) && c != '_' && c != '"' && c != '\''
|
||||
&& !SPECIAL->contains(c));
|
||||
}
|
||||
|
||||
static bool isDigit(const QChar &c)
|
||||
{
|
||||
return c.isDigit();
|
||||
}
|
||||
|
||||
static bool isOctit(const QChar &c)
|
||||
{
|
||||
return c >= '0' && c <= '7';
|
||||
}
|
||||
|
||||
static bool isHexit(const QChar &c)
|
||||
{
|
||||
return c.isDigit()
|
||||
|| (c >= 'A' && c <= 'F')
|
||||
|| (c >= 'a' && c <= 'f');
|
||||
}
|
||||
|
||||
static bool isCntrl(const QChar &c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z')
|
||||
|| c == '@'
|
||||
|| c == '['
|
||||
|| c == '\\'
|
||||
|| c == ']'
|
||||
|| c == '^'
|
||||
|| c == '_';
|
||||
}
|
||||
|
||||
static QVector<Token> getSpace(std::shared_ptr<QString> line, int start)
|
||||
{
|
||||
const auto lineEnd = line->cend();
|
||||
const auto tokenStart = line->cbegin() + start;
|
||||
auto current = tokenStart;
|
||||
while (current != lineEnd && (*current).isSpace())
|
||||
++current;
|
||||
const int length = int(std::distance(tokenStart, current));
|
||||
if (current > tokenStart)
|
||||
return {{TokenType::Whitespace, start, length, QStringView(*line).mid(start, length), line}};
|
||||
return {};
|
||||
}
|
||||
|
||||
static QVector<Token> getNumber(std::shared_ptr<QString> line, int start)
|
||||
{
|
||||
const QChar &startC = line->at(start);
|
||||
if (!startC.isDigit())
|
||||
return {};
|
||||
const int length = line->length();
|
||||
int current = start + 1;
|
||||
TokenType type = TokenType::Integer;
|
||||
if (current < length) {
|
||||
if (startC == '0') {
|
||||
// check for octal or hexadecimal
|
||||
const QChar &secondC = line->at(current);
|
||||
if (secondC == 'o' || secondC == 'O') {
|
||||
const int numLen = grab(*line, current + 1, isOctit);
|
||||
if (numLen > 0)
|
||||
return {token(TokenType::Integer, line, start, current + numLen + 1)};
|
||||
} else if (secondC == 'x' || secondC == 'X') {
|
||||
const int numLen = grab(*line, current + 1, isHexit);
|
||||
if (numLen > 0)
|
||||
return {token(TokenType::Integer, line, start, current + numLen + 1)};
|
||||
}
|
||||
}
|
||||
// starts with decimal
|
||||
const int numLen = grab(*line, start, isDigit);
|
||||
current = start + numLen;
|
||||
// check for floating point
|
||||
if (current < length && line->at(current) == '.') {
|
||||
const int numLen = grab(*line, current + 1, isDigit);
|
||||
if (numLen > 0) {
|
||||
current += numLen + 1;
|
||||
type = TokenType::Float;
|
||||
}
|
||||
}
|
||||
// check for exponent
|
||||
if (current + 1 < length /*for at least 'e' and digit*/
|
||||
&& (line->at(current) == 'e' || line->at(current) == 'E')) {
|
||||
int expEnd = current + 1;
|
||||
if (line->at(expEnd) == '+' || line->at(expEnd) == '-')
|
||||
++expEnd;
|
||||
const int numLen = grab(*line, expEnd, isDigit);
|
||||
if (numLen > 0) {
|
||||
current = expEnd + numLen;
|
||||
type = TokenType::Float;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {token(type, line, start, current)};
|
||||
}
|
||||
|
||||
static QVector<Token> getIdOrOpOrSingleLineComment(std::shared_ptr<QString> line, int start)
|
||||
{
|
||||
const int length = line->length();
|
||||
if (start >= length)
|
||||
return {};
|
||||
int current = start;
|
||||
// check for {conid.}conid
|
||||
int conidEnd = start;
|
||||
bool canOnlyBeConstructor = false;
|
||||
while (current < length && line->at(current).isUpper()) {
|
||||
current += grab(*line, current, isIdentifierChar);
|
||||
conidEnd = current;
|
||||
// it is definitely a constructor id if it is not followed by a '.'
|
||||
canOnlyBeConstructor = current >= length || line->at(current) != '.';
|
||||
// otherwise it might be a module id, and we skip the dot to check for qualified thing
|
||||
if (!canOnlyBeConstructor)
|
||||
++current;
|
||||
}
|
||||
if (canOnlyBeConstructor)
|
||||
return {token(TokenType::Constructor, line, start, conidEnd)};
|
||||
|
||||
// check for variable or reserved id
|
||||
if (current < length && isVariableIdentifierStart(line->at(current))) {
|
||||
const int varLen = grab(*line, current, isIdentifierChar);
|
||||
// check for reserved id
|
||||
if (RESERVED_ID->contains(line->mid(current, varLen))) {
|
||||
QVector<Token> result;
|
||||
// possibly add constructor + op '.'
|
||||
if (conidEnd > start) {
|
||||
result.append(token(TokenType::Constructor, line, start, conidEnd));
|
||||
result.append(token(TokenType::Operator, line, conidEnd, current));
|
||||
}
|
||||
result.append(token(TokenType::Keyword, line, current, current + varLen));
|
||||
return result;
|
||||
}
|
||||
return {token(TokenType::Variable, line, start, current + varLen)};
|
||||
}
|
||||
// check for operator
|
||||
if (current < length && isSymbol(line->at(current))) {
|
||||
const int opLen = grab(*line, current, isSymbol);
|
||||
// check for reserved op
|
||||
if (RESERVED_OP->contains(line->mid(current, opLen))) {
|
||||
// because of the case of F... (constructor + op '...') etc
|
||||
// we only add conid if we have one, handling the rest in next iteration
|
||||
if (conidEnd > start)
|
||||
return {token(TokenType::Constructor, line, start, conidEnd)};
|
||||
return {token(TokenType::Keyword, line, start, current + opLen)};
|
||||
}
|
||||
// check for single line comment
|
||||
if (opLen >= 2 && std::all_of(line->begin() + current, line->begin() + current + opLen,
|
||||
[](const QChar c) { return c == '-'; })) {
|
||||
QVector<Token> result;
|
||||
// possibly add constructor + op '.'
|
||||
if (conidEnd > start) {
|
||||
result.append(token(TokenType::Constructor, line, start, conidEnd));
|
||||
result.append(token(TokenType::Operator, line, conidEnd, current));
|
||||
}
|
||||
// rest is comment
|
||||
result.append(token(TokenType::SingleLineComment, line, current, length));
|
||||
return result;
|
||||
}
|
||||
// check for (qualified?) operator constructor
|
||||
if (line->at(current) == ':')
|
||||
return {token(TokenType::OperatorConstructor, line, start, current + opLen)};
|
||||
return {token(TokenType::Operator, line, start, current + opLen)};
|
||||
}
|
||||
// Foo.Blah.
|
||||
if (conidEnd > start)
|
||||
return {token(TokenType::Constructor, line, start, conidEnd)};
|
||||
return {};
|
||||
}
|
||||
|
||||
static int getEscape(const QString &line, int start)
|
||||
{
|
||||
if (CHAR_ESCAPES->contains(line.at(start)))
|
||||
return 1;
|
||||
|
||||
// decimal
|
||||
if (line.at(start).isDigit())
|
||||
return grab(line, start + 1, isDigit) + 1;
|
||||
// octal
|
||||
if (line.at(start) == 'o') {
|
||||
const int count = grab(line, start + 1, isOctit);
|
||||
if (count < 1) // no octal number after 'o'
|
||||
return 0;
|
||||
return count + 1;
|
||||
}
|
||||
// hexadecimal
|
||||
if (line.at(start) == 'x') {
|
||||
const int count = grab(line, start + 1, isHexit);
|
||||
if (count < 1) // no octal number after 'o'
|
||||
return 0;
|
||||
return count + 1;
|
||||
}
|
||||
// ascii cntrl
|
||||
if (line.at(start) == '^') {
|
||||
const int count = grab(line, start + 1, isCntrl);
|
||||
if (count < 1) // no octal number after 'o'
|
||||
return 0;
|
||||
return count + 1;
|
||||
}
|
||||
const QStringView s = QStringView(line).mid(start);
|
||||
for (const QString &esc : *ASCII_ESCAPES) {
|
||||
if (s.startsWith(esc))
|
||||
return esc.length();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QVector<Token> getString(std::shared_ptr<QString> line, int start, bool *inStringGap/*in-out*/)
|
||||
{
|
||||
// Haskell has the specialty of using \<whitespace>\ within strings for multiline strings
|
||||
const int length = line->length();
|
||||
if (start >= length)
|
||||
return {};
|
||||
QVector<Token> result;
|
||||
int tokenStart = start;
|
||||
int current = tokenStart;
|
||||
bool inString = *inStringGap;
|
||||
do {
|
||||
const QChar c = line->at(current);
|
||||
if (*inStringGap && !c.isSpace() && c != '\\') {
|
||||
// invalid non-whitespace in string gap
|
||||
// add previous string as token, this is at least a whitespace
|
||||
result.append(token(TokenType::String, line, tokenStart, current));
|
||||
// then add wrong non-whitespace
|
||||
tokenStart = current;
|
||||
do { ++current; } while (current < length && !line->at(current).isSpace());
|
||||
result.append(token(TokenType::StringError, line, tokenStart, current));
|
||||
tokenStart = current;
|
||||
} else if (c == '"') {
|
||||
inString = !inString;
|
||||
++current;
|
||||
} else if (inString) {
|
||||
if (c == '\\') {
|
||||
++current;
|
||||
if (*inStringGap) {
|
||||
// ending string gap
|
||||
*inStringGap = false;
|
||||
} else if (current >= length || line->at(current).isSpace()) {
|
||||
// starting string gap
|
||||
*inStringGap = true;
|
||||
current = std::min(current + 1, length);
|
||||
} else { // there is at least one character after current
|
||||
const int escapeLength = getEscape(*line, current);
|
||||
if (escapeLength > 0) {
|
||||
// valid escape
|
||||
// add previous string as token without backslash, if necessary
|
||||
if (tokenStart < current - 1/*backslash*/)
|
||||
result.append(token(TokenType::String, line, tokenStart, current - 1));
|
||||
tokenStart = current - 1; // backslash
|
||||
current += escapeLength;
|
||||
result.append(token(TokenType::EscapeSequence, line, tokenStart, current));
|
||||
tokenStart = current;
|
||||
} else { // invalid escape sequence
|
||||
// add previous string as token, this is at least backslash
|
||||
result.append(token(TokenType::String, line, tokenStart, current));
|
||||
result.append(token(TokenType::StringError, line, current, current + 1));
|
||||
++current;
|
||||
tokenStart = current;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++current;
|
||||
}
|
||||
}
|
||||
} while (current < length && inString);
|
||||
if (current > tokenStart)
|
||||
result.append(token(TokenType::String, line, tokenStart, current));
|
||||
if (inString && !*inStringGap) { // unterminated string
|
||||
// mark last character of last token as Unknown as an error hint
|
||||
if (!result.isEmpty()) { // should actually never be different
|
||||
Token &lastRef = result.last();
|
||||
if (lastRef.length == 1) {
|
||||
lastRef.type = TokenType::StringError;
|
||||
} else {
|
||||
--lastRef.length;
|
||||
lastRef.text = QStringView(*line).mid(lastRef.startCol, lastRef.length);
|
||||
result.append(token(TokenType::StringError, line, current - 1, current));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static QVector<Token> getMultiLineComment(std::shared_ptr<QString> line, int start,
|
||||
int *commentLevel/*in_out*/)
|
||||
{
|
||||
// Haskell multiline comments can be nested {- foo {- bar -} blah -}
|
||||
const int length = line->length();
|
||||
int current = start;
|
||||
do {
|
||||
const QStringView test = QStringView(*line).mid(current, 2);
|
||||
if (test == QLatin1String("{-")) {
|
||||
++(*commentLevel);
|
||||
current += 2;
|
||||
} else if (test == QLatin1String("-}") && *commentLevel > 0) {
|
||||
--(*commentLevel);
|
||||
current += 2;
|
||||
} else if (*commentLevel > 0) {
|
||||
++current;
|
||||
}
|
||||
} while (current < length && *commentLevel > 0);
|
||||
if (current > start) {
|
||||
return {token(TokenType::MultiLineComment, line, start, current)};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static QVector<Token> getChar(std::shared_ptr<QString> line, int start)
|
||||
{
|
||||
if (line->at(start) != '\'')
|
||||
return {};
|
||||
QVector<Token> result;
|
||||
const int length = line->length();
|
||||
int tokenStart = start;
|
||||
int current = tokenStart + 1;
|
||||
bool inChar = true;
|
||||
int count = 0;
|
||||
while (current < length && inChar) {
|
||||
if (line->at(current) == '\'') {
|
||||
inChar = false;
|
||||
++current;
|
||||
} else if (count == 1) {
|
||||
// we already have one character, so start Unknown token
|
||||
if (current > tokenStart)
|
||||
result.append(token(TokenType::Char, line, tokenStart, current));
|
||||
tokenStart = current;
|
||||
++count;
|
||||
++current;
|
||||
} else if (count > 1) {
|
||||
++count;
|
||||
++current;
|
||||
} else if (line->at(current) == '\\') {
|
||||
if (current + 1 < length) {
|
||||
++current;
|
||||
++count;
|
||||
const int escapeLength = getEscape(*line, current);
|
||||
if (line->at(current) != '&' && escapeLength > 0) { // no & escape for chars
|
||||
// valid escape
|
||||
// add previous string as token without backslash, if necessary
|
||||
if (tokenStart < current - 1/*backslash*/)
|
||||
result.append(token(TokenType::Char, line, tokenStart, current - 1));
|
||||
tokenStart = current - 1; // backslash
|
||||
current += escapeLength;
|
||||
result.append(token(TokenType::EscapeSequence, line, tokenStart, current));
|
||||
tokenStart = current;
|
||||
} else { // invalid escape sequence
|
||||
// add previous string as token, this is at least backslash
|
||||
result.append(token(TokenType::Char, line, tokenStart, current));
|
||||
result.append(token(TokenType::CharError, line, current, current + 1));
|
||||
++current;
|
||||
tokenStart = current;
|
||||
}
|
||||
} else {
|
||||
++current;
|
||||
}
|
||||
} else {
|
||||
++count;
|
||||
++current;
|
||||
}
|
||||
}
|
||||
if (count > 1 && inChar) {
|
||||
// too long and unterminated, just add Unknown token till end
|
||||
result.append(token(TokenType::CharError, line, tokenStart, current));
|
||||
} else if (count > 1) {
|
||||
// too long but terminated, add Unknown up to ending quote, then quote
|
||||
result.append(token(TokenType::CharError, line, tokenStart, current - 1));
|
||||
result.append(token(TokenType::Char, line, current - 1, current));
|
||||
} else if (inChar || count < 1) {
|
||||
// unterminated, or no character inside, mark last character as error
|
||||
if (current > tokenStart + 1)
|
||||
result.append(token(TokenType::Char, line, tokenStart, current - 1));
|
||||
result.append(token(TokenType::CharError, line, current - 1, current));
|
||||
} else {
|
||||
result.append(token(TokenType::Char, line, tokenStart, current));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static QVector<Token> getSpecial(std::shared_ptr<QString> line, int start)
|
||||
{
|
||||
if (SPECIAL->contains(line->at(start)))
|
||||
return {{TokenType::Special, start, 1, QStringView(*line).mid(start, 1), line}};
|
||||
return {};
|
||||
}
|
||||
|
||||
Tokens HaskellTokenizer::tokenize(const QString &line, int startState)
|
||||
{
|
||||
Tokens result(std::make_shared<QString>(line));
|
||||
const int length = result.source->length();
|
||||
bool inStringGap = startState == int(Tokens::State::StringGap);
|
||||
int multiLineCommentLevel = std::max(startState - int(Tokens::State::MultiLineCommentGuard), 0);
|
||||
int currentStart = 0;
|
||||
QVector<Token> tokens;
|
||||
while (currentStart < length) {
|
||||
if (multiLineCommentLevel <= 0 &&
|
||||
!(tokens = getString(result.source, currentStart, &inStringGap)).isEmpty()) {
|
||||
result.append(tokens);
|
||||
} else if (!(tokens = getMultiLineComment(result.source, currentStart,
|
||||
&multiLineCommentLevel)).isEmpty()) {
|
||||
result.append(tokens);
|
||||
} else if (!(tokens = getChar(result.source, currentStart)).isEmpty()) {
|
||||
result.append(tokens);
|
||||
} else if (!(tokens = getSpace(result.source, currentStart)).isEmpty()) {
|
||||
result.append(tokens);
|
||||
} else if (!(tokens = getNumber(result.source, currentStart)).isEmpty()) {
|
||||
result.append(tokens);
|
||||
} else if (!(tokens = getIdOrOpOrSingleLineComment(result.source, currentStart)).isEmpty()) {
|
||||
result.append(tokens);
|
||||
} else if (!(tokens = getSpecial(result.source, currentStart)).isEmpty()) {
|
||||
result.append(tokens);
|
||||
} else {
|
||||
tokens = {{TokenType::Unknown,
|
||||
currentStart,
|
||||
1,
|
||||
QStringView(*result.source).mid(currentStart, 1),
|
||||
result.source}};
|
||||
result.append(tokens);
|
||||
}
|
||||
currentStart += std::accumulate(tokens.cbegin(), tokens.cend(), 0,
|
||||
[](int s, const Token &t) { return s + t.length; });
|
||||
}
|
||||
if (inStringGap)
|
||||
result.state = int(Tokens::State::StringGap);
|
||||
else if (multiLineCommentLevel > 0)
|
||||
result.state = int(Tokens::State::MultiLineCommentGuard) + multiLineCommentLevel;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Token::isValid() const
|
||||
{
|
||||
return type != TokenType::Unknown;
|
||||
}
|
||||
|
||||
} // Internal
|
||||
} // Haskell
|
93
src/plugins/haskell/haskelltokenizer.h
Normal file
93
src/plugins/haskell/haskelltokenizer.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <QChar>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
enum class TokenType {
|
||||
Variable,
|
||||
Constructor,
|
||||
Operator,
|
||||
OperatorConstructor,
|
||||
Whitespace,
|
||||
String,
|
||||
StringError,
|
||||
Char,
|
||||
CharError,
|
||||
EscapeSequence,
|
||||
Integer,
|
||||
Float,
|
||||
Keyword,
|
||||
Special,
|
||||
SingleLineComment,
|
||||
MultiLineComment,
|
||||
Unknown
|
||||
};
|
||||
|
||||
class Token {
|
||||
public:
|
||||
bool isValid() const;
|
||||
|
||||
TokenType type = TokenType::Unknown;
|
||||
int startCol = -1;
|
||||
int length = -1;
|
||||
QStringView text;
|
||||
|
||||
std::shared_ptr<QString> source; // keep the string ref alive
|
||||
};
|
||||
|
||||
class Tokens : public QVector<Token>
|
||||
{
|
||||
public:
|
||||
enum class State {
|
||||
None = -1,
|
||||
StringGap = 0, // gap == two backslashes enclosing only whitespace
|
||||
MultiLineCommentGuard // nothing may follow that
|
||||
};
|
||||
|
||||
Tokens(std::shared_ptr<QString> source);
|
||||
|
||||
Token tokenAtColumn(int col) const;
|
||||
|
||||
std::shared_ptr<QString> source;
|
||||
int state = int(State::None);
|
||||
};
|
||||
|
||||
class HaskellTokenizer
|
||||
{
|
||||
public:
|
||||
static Tokens tokenize(const QString &line, int startState);
|
||||
};
|
||||
|
||||
} // Internal
|
||||
} // Haskell
|
BIN
src/plugins/haskell/images/category_haskell.png
Normal file
BIN
src/plugins/haskell/images/category_haskell.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
84
src/plugins/haskell/optionspage.cpp
Normal file
84
src/plugins/haskell/optionspage.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "optionspage.h"
|
||||
|
||||
#include "haskellconstants.h"
|
||||
#include "haskellmanager.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
OptionsPage::OptionsPage()
|
||||
{
|
||||
setId(Constants::OPTIONS_GENERAL);
|
||||
setDisplayName(tr("General"));
|
||||
setCategory("J.Z.Haskell");
|
||||
setDisplayCategory(tr("Haskell"));
|
||||
setCategoryIcon(Utils::Icon(":/haskell/images/category_haskell.png"));
|
||||
}
|
||||
|
||||
QWidget *OptionsPage::widget()
|
||||
{
|
||||
using namespace Utils;
|
||||
if (!m_widget) {
|
||||
m_widget = new QWidget;
|
||||
auto topLayout = new QVBoxLayout;
|
||||
m_widget->setLayout(topLayout);
|
||||
auto generalBox = new QGroupBox(tr("General"));
|
||||
topLayout->addWidget(generalBox);
|
||||
topLayout->addStretch(10);
|
||||
auto boxLayout = new QHBoxLayout;
|
||||
generalBox->setLayout(boxLayout);
|
||||
boxLayout->addWidget(new QLabel(tr("Stack executable:")));
|
||||
m_stackPath = new PathChooser();
|
||||
m_stackPath->setExpectedKind(PathChooser::ExistingCommand);
|
||||
m_stackPath->setPromptDialogTitle(tr("Choose Stack Executable"));
|
||||
m_stackPath->setFilePath(HaskellManager::stackExecutable());
|
||||
m_stackPath->setCommandVersionArguments({"--version"});
|
||||
boxLayout->addWidget(m_stackPath);
|
||||
}
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
void OptionsPage::apply()
|
||||
{
|
||||
if (!m_widget)
|
||||
return;
|
||||
HaskellManager::setStackExecutable(m_stackPath->rawFilePath());
|
||||
}
|
||||
|
||||
void OptionsPage::finish()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
53
src/plugins/haskell/optionspage.h
Normal file
53
src/plugins/haskell/optionspage.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <coreplugin/dialogs/ioptionspage.h>
|
||||
#include <utils/pathchooser.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class OptionsPage : public Core::IOptionsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OptionsPage();
|
||||
|
||||
QWidget *widget() override;
|
||||
void apply() override;
|
||||
void finish() override;
|
||||
|
||||
private:
|
||||
QPointer<QWidget> m_widget;
|
||||
QPointer<Utils::PathChooser> m_stackPath;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
1
src/plugins/haskell/share/wizards/module/file.hs
Normal file
1
src/plugins/haskell/share/wizards/module/file.hs
Normal file
@@ -0,0 +1 @@
|
||||
module %{BaseName} where
|
42
src/plugins/haskell/share/wizards/module/wizard.json
Normal file
42
src/plugins/haskell/share/wizards/module/wizard.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"version": 1,
|
||||
"supportedProjectTypes": [ ],
|
||||
"id": "C.Module",
|
||||
"category": "S.Haskell",
|
||||
"trDescription": "Creates a Haskell module.",
|
||||
"trDisplayName": "Module",
|
||||
"trDisplayCategory": "Haskell",
|
||||
"iconText": "hs",
|
||||
"enabled": "%{JS: value('Plugins').indexOf('Haskell') >= 0}",
|
||||
|
||||
"options": [
|
||||
{ "key": "FileName", "value": "%{JS: Util.fileName(value('TargetPath'), 'hs')}" },
|
||||
{ "key": "BaseName", "value": "%{JS: Util.baseName(value('FileName'))}" }
|
||||
],
|
||||
|
||||
"pages" :
|
||||
[
|
||||
{
|
||||
"trDisplayName": "Location",
|
||||
"trShortTitle": "Location",
|
||||
"typeId": "File"
|
||||
},
|
||||
{
|
||||
"trDisplayName": "Project Management",
|
||||
"trShortTitle": "Summary",
|
||||
"typeId": "Summary"
|
||||
}
|
||||
],
|
||||
"generators" :
|
||||
[
|
||||
{
|
||||
"typeId": "File",
|
||||
"data":
|
||||
{
|
||||
"source": "file.hs",
|
||||
"target": "%{FileName}",
|
||||
"openInEditor": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
77
src/plugins/haskell/stackbuildstep.cpp
Normal file
77
src/plugins/haskell/stackbuildstep.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "stackbuildstep.h"
|
||||
|
||||
#include "haskellconstants.h"
|
||||
#include "haskellmanager.h"
|
||||
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/processparameters.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
StackBuildStep::StackBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id)
|
||||
: AbstractProcessStep(bsl, id)
|
||||
{
|
||||
setDefaultDisplayName(trDisplayName());
|
||||
}
|
||||
|
||||
QWidget *StackBuildStep::createConfigWidget()
|
||||
{
|
||||
return new QWidget;
|
||||
}
|
||||
|
||||
QString StackBuildStep::trDisplayName()
|
||||
{
|
||||
return tr("Stack Build");
|
||||
}
|
||||
|
||||
bool StackBuildStep::init()
|
||||
{
|
||||
if (AbstractProcessStep::init()) {
|
||||
const auto projectDir = QDir(project()->projectDirectory().toString());
|
||||
processParameters()->setCommandLine(
|
||||
{HaskellManager::stackExecutable(),
|
||||
{"build", "--work-dir", projectDir.relativeFilePath(buildDirectory().toString())}});
|
||||
processParameters()->setEnvironment(buildEnvironment());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
StackBuildStepFactory::StackBuildStepFactory()
|
||||
{
|
||||
registerStep<StackBuildStep>(Constants::C_STACK_BUILD_STEP_ID);
|
||||
setDisplayName(StackBuildStep::StackBuildStep::trDisplayName());
|
||||
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
55
src/plugins/haskell/stackbuildstep.h
Normal file
55
src/plugins/haskell/stackbuildstep.h
Normal file
@@ -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 <projectexplorer/abstractprocessstep.h>
|
||||
|
||||
namespace Haskell {
|
||||
namespace Internal {
|
||||
|
||||
class StackBuildStep : public ProjectExplorer::AbstractProcessStep
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StackBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
|
||||
|
||||
QWidget *createConfigWidget() override;
|
||||
|
||||
static QString trDisplayName();
|
||||
|
||||
protected:
|
||||
bool init() override;
|
||||
};
|
||||
|
||||
class StackBuildStepFactory : public ProjectExplorer::BuildStepFactory
|
||||
{
|
||||
public:
|
||||
StackBuildStepFactory();
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Haskell
|
@@ -9,6 +9,7 @@ add_subdirectory(environment)
|
||||
add_subdirectory(extensionsystem)
|
||||
add_subdirectory(externaltool)
|
||||
add_subdirectory(filesearch)
|
||||
add_subdirectory(haskell)
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(languageserverprotocol)
|
||||
add_subdirectory(mapreduce)
|
||||
|
8
tests/auto/haskell/CMakeLists.txt
Normal file
8
tests/auto/haskell/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
add_qtc_test(tst_tokenizer
|
||||
DEPENDS Qt::Core Qt::Test
|
||||
INCLUDES ../../../src/plugins/haskell
|
||||
SOURCES
|
||||
tst_tokenizer.cpp
|
||||
../../../src/plugins/haskell/haskelltokenizer.cpp
|
||||
../../../src/plugins/haskell/haskelltokenizer.h
|
||||
)
|
730
tests/auto/haskell/tst_tokenizer.cpp
Normal file
730
tests/auto/haskell/tst_tokenizer.cpp
Normal file
@@ -0,0 +1,730 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <haskelltokenizer.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtTest>
|
||||
|
||||
using namespace Haskell::Internal;
|
||||
|
||||
const QSet<char> escapes{'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '&'};
|
||||
|
||||
struct TokenInfo
|
||||
{
|
||||
TokenType type;
|
||||
int column;
|
||||
QString text;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(TokenInfo)
|
||||
|
||||
bool operator==(const TokenInfo &info, const Token &token)
|
||||
{
|
||||
return info.type == token.type
|
||||
&& info.column == token.startCol
|
||||
&& info.text.length() == token.length
|
||||
&& info.text == token.text.toString();
|
||||
}
|
||||
|
||||
bool operator==(const Token &token, const TokenInfo &info)
|
||||
{
|
||||
return info == token;
|
||||
}
|
||||
|
||||
class tst_Tokenizer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void singleLineComment_data();
|
||||
void singleLineComment();
|
||||
|
||||
void multiLineComment_data();
|
||||
void multiLineComment();
|
||||
|
||||
void string_data();
|
||||
void string();
|
||||
|
||||
void character_data();
|
||||
void character();
|
||||
|
||||
void number_data();
|
||||
void number();
|
||||
|
||||
void keyword_data();
|
||||
void keyword();
|
||||
|
||||
void variable_data();
|
||||
void variable();
|
||||
|
||||
void constructor_data();
|
||||
void constructor();
|
||||
|
||||
void op_data();
|
||||
void op();
|
||||
|
||||
private:
|
||||
void setupData();
|
||||
void addRow(const char *name,
|
||||
const QString &input,
|
||||
const QList<TokenInfo> &tokens,
|
||||
Tokens::State startState = Tokens::State::None,
|
||||
Tokens::State endState = Tokens::State::None);
|
||||
void checkData();
|
||||
};
|
||||
|
||||
void tst_Tokenizer::setupData()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QList<TokenInfo>>("output");
|
||||
QTest::addColumn<int>("startState");
|
||||
QTest::addColumn<int>("endState");
|
||||
}
|
||||
|
||||
void tst_Tokenizer::addRow(const char *name,
|
||||
const QString &input,
|
||||
const QList<TokenInfo> &tokens,
|
||||
Tokens::State startState,
|
||||
Tokens::State endState)
|
||||
{
|
||||
QTest::newRow(name) << input << tokens << int(startState) << int(endState);
|
||||
}
|
||||
|
||||
void tst_Tokenizer::checkData()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QList<TokenInfo>, output);
|
||||
QFETCH(int, startState);
|
||||
QFETCH(int, endState);
|
||||
const Tokens tokens = HaskellTokenizer::tokenize(input, startState);
|
||||
QCOMPARE(tokens.length(), output.length());
|
||||
QCOMPARE(tokens.state, endState);
|
||||
for (int i = 0; i < tokens.length(); ++i) {
|
||||
const Token t = tokens.at(i);
|
||||
const TokenInfo ti = output.at(i);
|
||||
QVERIFY2(t == ti, QString("Token at index %1 does not match, {%2, %3, \"%4\"} != {%5, %6, \"%7\"}")
|
||||
.arg(i)
|
||||
.arg(int(t.type)).arg(t.startCol).arg(t.text.toString())
|
||||
.arg(int(ti.type)).arg(ti.column).arg(ti.text)
|
||||
.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Tokenizer::singleLineComment_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("simple", " -- foo", {
|
||||
{TokenType::Whitespace, 0, " "},
|
||||
{TokenType::SingleLineComment, 1, "-- foo"}
|
||||
});
|
||||
addRow("dash, id", "--foo", {
|
||||
{TokenType::SingleLineComment, 0, "--foo"}
|
||||
});
|
||||
addRow("dash, space, op", "-- |foo", {
|
||||
{TokenType::SingleLineComment, 0, "-- |foo"}
|
||||
});
|
||||
addRow("multi-dash, space", "---- foo", {
|
||||
{TokenType::SingleLineComment, 0, "---- foo"}
|
||||
});
|
||||
addRow("dash, op", "--| foo", {
|
||||
{TokenType::Operator, 0, "--|"},
|
||||
{TokenType::Whitespace, 3, " "},
|
||||
{TokenType::Variable, 4, "foo"}
|
||||
});
|
||||
addRow("dash, special", "--(foo", {
|
||||
{TokenType::SingleLineComment, 0, "--(foo"}
|
||||
});
|
||||
addRow("not a qualified varsym", "F.-- foo", {
|
||||
{TokenType::Constructor, 0, "F"},
|
||||
{TokenType::Operator, 1, "."},
|
||||
{TokenType::SingleLineComment, 2, "-- foo"}
|
||||
});
|
||||
}
|
||||
|
||||
void tst_Tokenizer::singleLineComment()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
void tst_Tokenizer::multiLineComment_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("trailing dashes", "{---foo -}", {
|
||||
{TokenType::MultiLineComment, 0, "{---foo -}"}
|
||||
});
|
||||
addRow("multiline", "{- foo", {
|
||||
{TokenType::MultiLineComment, 0, "{- foo"}
|
||||
},
|
||||
Tokens::State::None,
|
||||
Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 1));
|
||||
addRow("multiline2", "bar -}", {
|
||||
{TokenType::MultiLineComment, 0, "bar -}"}
|
||||
},
|
||||
Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 1),
|
||||
Tokens::State::None);
|
||||
addRow("nested", "{- fo{-o", {
|
||||
{TokenType::MultiLineComment, 0, "{- fo{-o"}
|
||||
},
|
||||
Tokens::State::None,
|
||||
Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 2));
|
||||
addRow("nested2", "bar -}", {
|
||||
{TokenType::MultiLineComment, 0, "bar -}"}
|
||||
},
|
||||
Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 2),
|
||||
Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 1));
|
||||
addRow("nested3", "bar -}", {
|
||||
{TokenType::MultiLineComment, 0, "bar -}"}
|
||||
},
|
||||
Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 1),
|
||||
Tokens::State::None);
|
||||
}
|
||||
|
||||
void tst_Tokenizer::multiLineComment()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
void tst_Tokenizer::string_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("simple", "\"foo\"", {
|
||||
{TokenType::String, 0, "\"foo\""}
|
||||
});
|
||||
|
||||
addRow("unterminated", "\"", {
|
||||
{TokenType::StringError, 0, "\""}
|
||||
});
|
||||
addRow("unterminated2", "\"foo", {
|
||||
{TokenType::String, 0, "\"fo"},
|
||||
{TokenType::StringError, 3, "o"}
|
||||
});
|
||||
addRow("unterminated with escape", "\"\\\\", {
|
||||
{TokenType::String, 0, "\""},
|
||||
{TokenType::EscapeSequence, 1, "\\"},
|
||||
{TokenType::StringError, 2, "\\"}
|
||||
});
|
||||
|
||||
// gaps
|
||||
addRow("gap", "\" \\ \\\"", {
|
||||
{TokenType::String, 0, "\" \\ \\\""}
|
||||
});
|
||||
addRow("gap over endline", "\"foo\\", {
|
||||
{TokenType::String, 0, "\"foo\\"}
|
||||
},
|
||||
Tokens::State::None, Tokens::State::StringGap);
|
||||
addRow("gap over endline2", "\\foo\"", {
|
||||
{TokenType::String, 0, "\\foo\""}
|
||||
},
|
||||
Tokens::State::StringGap, Tokens::State::None);
|
||||
addRow("gap error", "\"\\ ab \\\"", {
|
||||
{TokenType::String, 0, "\"\\ "},
|
||||
{TokenType::StringError, 3, "ab"},
|
||||
{TokenType::String, 5, " \\\""}
|
||||
});
|
||||
addRow("gap error with quote", "\"\\ \"", {
|
||||
{TokenType::String, 0, "\"\\ "},
|
||||
{TokenType::StringError, 3, "\""}
|
||||
},
|
||||
Tokens::State::None, Tokens::State::StringGap);
|
||||
|
||||
// char escapes (including wrong ones)
|
||||
for (char c = '!'; c <= '~'; ++c) {
|
||||
// skip uppercase and '^', since these can be part of ascii escapes
|
||||
// and 'o' and 'x' since they start octal and hex escapes
|
||||
// and digits as part of decimal escape
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '^' || c == 'o' || c == 'x')
|
||||
continue;
|
||||
const QChar qc(c);
|
||||
const QByteArray name = QString("charesc '%1'").arg(qc).toUtf8();
|
||||
const QString input = QString("\"\\%1\"").arg(qc);
|
||||
if (escapes.contains(c)) {
|
||||
addRow(name.constData(), input, {
|
||||
{TokenType::String, 0, "\""},
|
||||
{TokenType::EscapeSequence, 1, QLatin1String("\\") + qc},
|
||||
{TokenType::String, 3, "\""}
|
||||
});
|
||||
} else {
|
||||
addRow(name.constData(), input, {
|
||||
{TokenType::String, 0, "\"\\"},
|
||||
{TokenType::StringError, 2, qc},
|
||||
{TokenType::String, 3, "\""}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addRow("decimal escape", "\"\\1234a\"", {
|
||||
{TokenType::String, 0, "\""},
|
||||
{TokenType::EscapeSequence, 1, "\\1234"},
|
||||
{TokenType::String, 6, "a\""}
|
||||
});
|
||||
|
||||
addRow("octal escape", "\"\\o0678a\"", {
|
||||
{TokenType::String, 0, "\""},
|
||||
{TokenType::EscapeSequence, 1, "\\o067"},
|
||||
{TokenType::String, 6, "8a\""}
|
||||
});
|
||||
addRow("octal escape error", "\"\\o8a\"", {
|
||||
{TokenType::String, 0, "\"\\"},
|
||||
{TokenType::StringError, 2, "o"},
|
||||
{TokenType::String, 3, "8a\""}
|
||||
});
|
||||
|
||||
addRow("hexadecimal escape", "\"\\x0678Abg\"", {
|
||||
{TokenType::String, 0, "\""},
|
||||
{TokenType::EscapeSequence, 1, "\\x0678Ab"},
|
||||
{TokenType::String, 9, "g\""}
|
||||
});
|
||||
addRow("hexadecimal escape error", "\"\\xg\"", {
|
||||
{TokenType::String, 0, "\"\\"},
|
||||
{TokenType::StringError, 2, "x"},
|
||||
{TokenType::String, 3, "g\""}
|
||||
});
|
||||
|
||||
// ascii cntrl escapes (including wrong ones)
|
||||
for (char c = '!'; c <= '~'; ++c) {
|
||||
if (c == '"') // is special because it also ends the string
|
||||
continue;
|
||||
const QChar qc(c);
|
||||
const QByteArray name = QString("ascii cntrl '^%1'").arg(qc).toUtf8();
|
||||
const QString input = QString("\"\\^%1\"").arg(qc);
|
||||
if ((qc >= 'A' && qc <= 'Z') || qc == '@' || qc == '[' || qc == '\\' || qc == ']'
|
||||
|| qc == '^' || qc == '_') {
|
||||
addRow(name.constData(), input, {
|
||||
{TokenType::String, 0, "\""},
|
||||
{TokenType::EscapeSequence, 1, QLatin1String("\\^") + qc},
|
||||
{TokenType::String, 4, "\""}
|
||||
});
|
||||
} else {
|
||||
addRow(name.constData(), input, {
|
||||
{TokenType::String, 0, "\"\\"},
|
||||
{TokenType::StringError, 2, "^"},
|
||||
{TokenType::String, 3, QString(qc) + "\""}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addRow("ascii escape SOH", "\"\\SOHN\"", {
|
||||
{TokenType::String, 0, "\""},
|
||||
{TokenType::EscapeSequence, 1, "\\SOH"},
|
||||
{TokenType::String, 5, "N\""}
|
||||
});
|
||||
addRow("ascii escape SO", "\"\\SON\"", {
|
||||
{TokenType::String, 0, "\""},
|
||||
{TokenType::EscapeSequence, 1, "\\SO"},
|
||||
{TokenType::String, 4, "N\""}
|
||||
});
|
||||
addRow("ascii escape error", "\"\\TON\"", {
|
||||
{TokenType::String, 0, "\"\\"},
|
||||
{TokenType::StringError, 2, "T"},
|
||||
{TokenType::String, 3, "ON\""}
|
||||
});
|
||||
addRow("ascii escape error 2", "\"\\STO\"", {
|
||||
{TokenType::String, 0, "\"\\"},
|
||||
{TokenType::StringError, 2, "S"},
|
||||
{TokenType::String, 3, "TO\""}
|
||||
});
|
||||
}
|
||||
|
||||
void tst_Tokenizer::string()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
void tst_Tokenizer::character_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("simple", "'a'", {
|
||||
{TokenType::Char, 0, "'a'"}
|
||||
});
|
||||
addRow("too many", "'abc'", {
|
||||
{TokenType::Char, 0, "'a"},
|
||||
{TokenType::CharError, 2, "bc"},
|
||||
{TokenType::Char, 4, "'"}
|
||||
});
|
||||
addRow("too few", "''", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::CharError, 1, "'"}
|
||||
});
|
||||
addRow("only quote", "'", {
|
||||
{TokenType::CharError, 0, "'"}
|
||||
});
|
||||
addRow("unterminated", "'a", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::CharError, 1, "a"}
|
||||
});
|
||||
addRow("unterminated too many", "'abc", {
|
||||
{TokenType::Char, 0, "'a"},
|
||||
{TokenType::CharError, 2, "bc"}
|
||||
});
|
||||
addRow("unterminated backslash", "'\\", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::CharError, 1, "\\"}
|
||||
});
|
||||
|
||||
// char escapes (including wrong ones)
|
||||
for (char c = '!'; c <= '~'; ++c) {
|
||||
// skip uppercase and '^', since these can be part of ascii escapes
|
||||
// and 'o' and 'x' since they start octal and hex escapes
|
||||
// and digits as part of decimal escape
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '^' || c == 'o' || c == 'x')
|
||||
continue;
|
||||
const QChar qc(c);
|
||||
const QByteArray name = QString("charesc '%1'").arg(qc).toUtf8();
|
||||
const QString input = QString("'\\%1'").arg(qc);
|
||||
if (c != '&' && escapes.contains(c)) {
|
||||
addRow(name.constData(), input, {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::EscapeSequence, 1, QLatin1String("\\") + qc},
|
||||
{TokenType::Char, 3, "'"}
|
||||
});
|
||||
} else {
|
||||
addRow(name.constData(), input, {
|
||||
{TokenType::Char, 0, "'\\"},
|
||||
{TokenType::CharError, 2, qc},
|
||||
{TokenType::Char, 3, "'"}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addRow("decimal escape", "'\\1234'", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::EscapeSequence, 1, "\\1234"},
|
||||
{TokenType::Char, 6, "'"}
|
||||
});
|
||||
addRow("decimal escape too long", "'\\1234a'", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::EscapeSequence, 1, "\\1234"},
|
||||
{TokenType::CharError, 6, "a"},
|
||||
{TokenType::Char, 7, "'"}
|
||||
});
|
||||
|
||||
addRow("octal escape", "'\\o067'", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::EscapeSequence, 1, "\\o067"},
|
||||
{TokenType::Char, 6, "'"}
|
||||
});
|
||||
addRow("octal escape error", "'\\o8'", {
|
||||
{TokenType::Char, 0, "'\\"},
|
||||
{TokenType::CharError, 2, "o"},
|
||||
{TokenType::CharError, 3, "8"},
|
||||
{TokenType::Char, 4, "'"}
|
||||
});
|
||||
|
||||
addRow("hexadecimal escape", "'\\x0678Ab'", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::EscapeSequence, 1, "\\x0678Ab"},
|
||||
{TokenType::Char, 9, "'"}
|
||||
});
|
||||
addRow("hexadecimal escape error", "'\\xg'", {
|
||||
{TokenType::Char, 0, "'\\"},
|
||||
{TokenType::CharError, 2, "x"},
|
||||
{TokenType::CharError, 3, "g"},
|
||||
{TokenType::Char, 4, "'"}
|
||||
});
|
||||
|
||||
// ascii cntrl escapes (including wrong ones)
|
||||
for (char c = '!'; c <= '~'; ++c) {
|
||||
if (c == '\'') // is special because it also ends the string
|
||||
continue;
|
||||
const QChar qc(c);
|
||||
const QByteArray name = QString("ascii cntrl '^%1'").arg(qc).toUtf8();
|
||||
const QString input = QString("'\\^%1'").arg(qc);
|
||||
if ((qc >= 'A' && qc <= 'Z') || qc == '@' || qc == '[' || qc == '\\' || qc == ']'
|
||||
|| qc == '^' || qc == '_') {
|
||||
addRow(name.constData(), input, {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::EscapeSequence, 1, QLatin1String("\\^") + qc},
|
||||
{TokenType::Char, 4, "'"}
|
||||
});
|
||||
} else {
|
||||
addRow(name.constData(), input, {
|
||||
{TokenType::Char, 0, "'\\"},
|
||||
{TokenType::CharError, 2, "^"},
|
||||
{TokenType::CharError, 3, qc},
|
||||
{TokenType::Char, 4, "'"}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addRow("ascii escape SOH", "'\\SOH'", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::EscapeSequence, 1, "\\SOH"},
|
||||
{TokenType::Char, 5, "'"}
|
||||
});
|
||||
addRow("ascii escape SO, too long", "'\\SON'", {
|
||||
{TokenType::Char, 0, "'"},
|
||||
{TokenType::EscapeSequence, 1, "\\SO"},
|
||||
{TokenType::CharError, 4, "N"},
|
||||
{TokenType::Char, 5, "'"}
|
||||
});
|
||||
addRow("ascii escape error", "'\\TON'", {
|
||||
{TokenType::Char, 0, "'\\"},
|
||||
{TokenType::CharError, 2, "T"},
|
||||
{TokenType::CharError, 3, "ON"},
|
||||
{TokenType::Char, 5, "'"}
|
||||
});
|
||||
}
|
||||
|
||||
void tst_Tokenizer::character()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
void tst_Tokenizer::number_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("decimal", "012345", {
|
||||
{TokenType::Integer, 0, "012345"}
|
||||
});
|
||||
addRow("single digit decimal", "0", {
|
||||
{TokenType::Integer, 0, "0"}
|
||||
});
|
||||
addRow("octal", "0o1234", {
|
||||
{TokenType::Integer, 0, "0o1234"}
|
||||
});
|
||||
// this is a bit weird, but correct: octal 1 followed by decimal 8
|
||||
addRow("number after octal", "0O18", {
|
||||
{TokenType::Integer, 0, "0O1"},
|
||||
{TokenType::Integer, 3, "8"}
|
||||
});
|
||||
addRow("not octal", "0o9", {
|
||||
{TokenType::Integer, 0, "0"},
|
||||
{TokenType::Variable, 1, "o9"},
|
||||
});
|
||||
addRow("hexadecimal", "0x9fA", {
|
||||
{TokenType::Integer, 0, "0x9fA"}
|
||||
});
|
||||
// hex number followed by identifier 'g'
|
||||
addRow("hexadecimal", "0X9fg", {
|
||||
{TokenType::Integer, 0, "0X9f"},
|
||||
{TokenType::Variable, 4, "g"}
|
||||
});
|
||||
|
||||
// 0 followed by identifier
|
||||
addRow("decimal followed by identifier", "0z6", {
|
||||
{TokenType::Integer, 0, "0"},
|
||||
{TokenType::Variable, 1, "z6"}
|
||||
});
|
||||
|
||||
addRow("float", "0123.45", {
|
||||
{TokenType::Float, 0, "0123.45"}
|
||||
});
|
||||
addRow("decimal + operator '.'", "0123.", {
|
||||
{TokenType::Integer, 0, "0123"},
|
||||
{TokenType::Operator, 4, "."}
|
||||
});
|
||||
addRow("operator '.' + decimal", ".0123", {
|
||||
{TokenType::Operator, 0, "."},
|
||||
{TokenType::Integer, 1, "0123"}
|
||||
});
|
||||
addRow("without '.', with exp 'e'", "0123e45", {
|
||||
{TokenType::Float, 0, "0123e45"}
|
||||
});
|
||||
addRow("without '.', with exp 'E'", "0123E45", {
|
||||
{TokenType::Float, 0, "0123E45"}
|
||||
});
|
||||
addRow("without '.', with '+'", "0123e+45", {
|
||||
{TokenType::Float, 0, "0123e+45"}
|
||||
});
|
||||
addRow("without '.', with '-'", "0123e-45", {
|
||||
{TokenType::Float, 0, "0123e-45"}
|
||||
});
|
||||
addRow("without '.', with '+', missing decimal", "0123e+", {
|
||||
{TokenType::Integer, 0, "0123"},
|
||||
{TokenType::Variable, 4, "e"},
|
||||
{TokenType::Operator, 5, "+"}
|
||||
});
|
||||
addRow("without '.', missing decimal", "0123e", {
|
||||
{TokenType::Integer, 0, "0123"},
|
||||
{TokenType::Variable, 4, "e"}
|
||||
});
|
||||
addRow("exp 'e'", "01.23e45", {
|
||||
{TokenType::Float, 0, "01.23e45"}
|
||||
});
|
||||
addRow("exp 'E'", "01.23E45", {
|
||||
{TokenType::Float, 0, "01.23E45"}
|
||||
});
|
||||
addRow("with '+'", "01.23e+45", {
|
||||
{TokenType::Float, 0, "01.23e+45"}
|
||||
});
|
||||
addRow("with '-'", "01.23e-45", {
|
||||
{TokenType::Float, 0, "01.23e-45"}
|
||||
});
|
||||
addRow("with '+', missing decimal", "01.23e+", {
|
||||
{TokenType::Float, 0, "01.23"},
|
||||
{TokenType::Variable, 5, "e"},
|
||||
{TokenType::Operator, 6, "+"}
|
||||
});
|
||||
addRow("missing decimal", "01.23e", {
|
||||
{TokenType::Float, 0, "01.23"},
|
||||
{TokenType::Variable, 5, "e"}
|
||||
});
|
||||
}
|
||||
|
||||
void tst_Tokenizer::number()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
void tst_Tokenizer::keyword_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("data", "data", {
|
||||
{TokenType::Keyword, 0, "data"}
|
||||
});
|
||||
addRow("not a qualified varid", "Foo.case", {
|
||||
{TokenType::Constructor, 0, "Foo"},
|
||||
{TokenType::Operator, 3, "."},
|
||||
{TokenType::Keyword, 4, "case"}
|
||||
});
|
||||
addRow(":", ":", {
|
||||
{TokenType::Keyword, 0, ":"}
|
||||
});
|
||||
addRow("->", "->", {
|
||||
{TokenType::Keyword, 0, "->"}
|
||||
});
|
||||
addRow("not a qualified varsym", "Foo...", {
|
||||
{TokenType::Constructor, 0, "Foo"},
|
||||
{TokenType::Operator, 3, "..."}
|
||||
});
|
||||
}
|
||||
|
||||
void tst_Tokenizer::keyword()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
void tst_Tokenizer::variable_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("simple", "fOo_1'", {
|
||||
{TokenType::Variable, 0, "fOo_1'"}
|
||||
});
|
||||
addRow("start with '_'", "_1", {
|
||||
{TokenType::Variable, 0, "_1"}
|
||||
});
|
||||
addRow("not a keyword", "cases", {
|
||||
{TokenType::Variable, 0, "cases"}
|
||||
});
|
||||
addRow("not a keyword 2", "qualified", {
|
||||
{TokenType::Variable, 0, "qualified"}
|
||||
});
|
||||
addRow("not a keyword 3", "as", {
|
||||
{TokenType::Variable, 0, "as"}
|
||||
});
|
||||
addRow("not a keyword 4", "hiding", {
|
||||
{TokenType::Variable, 0, "hiding"}
|
||||
});
|
||||
addRow(".variable", ".foo", {
|
||||
{TokenType::Operator, 0, "."},
|
||||
{TokenType::Variable, 1, "foo"}
|
||||
});
|
||||
addRow("variable.", "foo.", {
|
||||
{TokenType::Variable, 0, "foo"},
|
||||
{TokenType::Operator, 3, "."}
|
||||
});
|
||||
addRow("variable.variable", "blah.foo", {
|
||||
{TokenType::Variable, 0, "blah"},
|
||||
{TokenType::Operator, 4, "."},
|
||||
{TokenType::Variable, 5, "foo"}
|
||||
});
|
||||
addRow("qualified", "Blah.foo", {
|
||||
{TokenType::Variable, 0, "Blah.foo"}
|
||||
});
|
||||
addRow("qualified2", "Goo.Blah.foo", {
|
||||
{TokenType::Variable, 0, "Goo.Blah.foo"}
|
||||
});
|
||||
addRow("variable + op '..'", "foo..", {
|
||||
{TokenType::Variable, 0, "foo"},
|
||||
{TokenType::Keyword, 3, ".."}
|
||||
});
|
||||
addRow("variable + op '...'", "foo...", {
|
||||
{TokenType::Variable, 0, "foo"},
|
||||
{TokenType::Operator, 3, "..."}
|
||||
});
|
||||
}
|
||||
|
||||
void tst_Tokenizer::variable()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
void tst_Tokenizer::constructor_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("simple", "Foo", {
|
||||
{TokenType::Constructor, 0, "Foo"}
|
||||
});
|
||||
addRow("qualified", "Foo.Bar", {
|
||||
{TokenType::Constructor, 0, "Foo.Bar"}
|
||||
});
|
||||
addRow("followed by op '.'", "Foo.Bar.", {
|
||||
{TokenType::Constructor, 0, "Foo.Bar"},
|
||||
{TokenType::Operator, 7, "."}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void tst_Tokenizer::constructor()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
void tst_Tokenizer::op_data()
|
||||
{
|
||||
setupData();
|
||||
|
||||
addRow("simple", "+-=", {
|
||||
{TokenType::Operator, 0, "+-="}
|
||||
});
|
||||
addRow("qualified", "Foo.+-=", {
|
||||
{TokenType::Operator, 0, "Foo.+-="}
|
||||
});
|
||||
addRow("qualified '.'", "Foo..", {
|
||||
{TokenType::Operator, 0, "Foo.."}
|
||||
});
|
||||
addRow("constructor plus op", "Foo+", {
|
||||
{TokenType::Constructor, 0, "Foo"},
|
||||
{TokenType::Operator, 3, "+"}
|
||||
});
|
||||
}
|
||||
|
||||
void tst_Tokenizer::op()
|
||||
{
|
||||
checkData();
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_Tokenizer)
|
||||
|
||||
#include "tst_tokenizer.moc"
|
1
tests/manual/haskell/simple/README.md
Normal file
1
tests/manual/haskell/simple/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# simple test project
|
2
tests/manual/haskell/simple/Setup.hs
Normal file
2
tests/manual/haskell/simple/Setup.hs
Normal file
@@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
14
tests/manual/haskell/simple/simple.cabal
Normal file
14
tests/manual/haskell/simple/simple.cabal
Normal file
@@ -0,0 +1,14 @@
|
||||
name: simple
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
category: Test
|
||||
build-type: Simple
|
||||
cabal-version: >=1.10
|
||||
extra-source-files: README.md
|
||||
|
||||
executable simple
|
||||
hs-source-dirs: src
|
||||
main-is: Main.hs
|
||||
default-language: Haskell2010
|
||||
build-depends: base >= 4.7 && < 5
|
5
tests/manual/haskell/simple/src/Main.hs
Normal file
5
tests/manual/haskell/simple/src/Main.hs
Normal file
@@ -0,0 +1,5 @@
|
||||
module Main where
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "hello world"
|
66
tests/manual/haskell/simple/stack.yaml
Normal file
66
tests/manual/haskell/simple/stack.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# resolver: lts-3.5
|
||||
# resolver: nightly-2015-09-21
|
||||
# resolver: ghc-7.10.2
|
||||
# resolver: ghcjs-0.1.0_ghc-7.10.2
|
||||
# resolver:
|
||||
# name: custom-snapshot
|
||||
# location: "./custom-snapshot.yaml"
|
||||
resolver: lts-7.24
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# - location:
|
||||
# git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
# extra-dep: true
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
#
|
||||
# A package marked 'extra-dep: true' will only be built if demanded by a
|
||||
# non-dependency (i.e. a user package), and its test suites and benchmarks
|
||||
# will not be run. This is useful for tweaking upstream packages.
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver
|
||||
# (e.g., acme-missiles-0.3)
|
||||
# extra-deps: []
|
||||
|
||||
# Override default flag values for local packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=1.6"
|
||||
#
|
||||
# Override the architecture used by stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
Reference in New Issue
Block a user