Commit 94fdd4d9 authored by Filippo Cucchetto's avatar Filippo Cucchetto

Added plugin for supporting the Nim programming language

The plugin support:
- basic syntax highlighting
- indentation
- project management
- run and build configuration
- debugging
- code style

Change-Id: Idfbb02a11ed570c90149a54b726beb956712e2bd
Reviewed-by: default avatarDavid Schulz <david.schulz@theqtcompany.com>
Reviewed-by: default avatarhjk <hjk@theqtcompany.com>
parent b198d482
{
"version": 1,
"kind": "file",
"id": "Z.NimSource",
"category": "Z.Nim",
"trDescription": "Creates an empty Nim file using UTF-8 charset.",
"trDisplayName": "Nim File",
"trDisplayCategory": "Nim",
"icon": "icon.png",
"enabled": "%{JS: [ %{Plugins} ].indexOf('Nim') >= 0}",
"pages" :
[
{
"trDisplayName": "Location",
"trShortTitle": "Location",
"typeId": "File"
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators" :
[
{
"typeId": "File",
"data":
{
"source": "file.nim",
"target": "%{JS: Util.fileName('%{TargetPath}', 'nim')}",
"openInEditor": true
}
}
]
}
proc mainProc =
echo "Hello World"
if isMainModule:
mainProc()
{
"version": 1,
"kind": "project",
"id": "Z.NimProject",
"category": "I.Projects",
"trDescription": "Creates a simple Nim application.",
"trDisplayName": "Nim Application",
"trDisplayCategory": "Non-Qt Project",
"icon": "icon.png",
"enabled": "%{JS: [ %{Plugins} ].indexOf('Nim') >= 0 }",
"options":
[
{ "key": "ProjectFile", "value": "%{JS: '%{NimProjectFile}'}" },
{ "key": "NimProjectFile", "value": "%{JS: Util.fileName('%{ProjectDirectory}/%{ProjectName}', 'nimproject')}" },
{ "key": "NimFileName", "value": "%{JS: 'main.nim'}" },
{ "key": "IsTopLevelProject", "value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}'}" }
],
"pages":
[
{
"trDisplayName": "Project Location",
"trShortTitle": "Location",
"typeId": "Project"
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"enabled": "%{IsTopLevelProject}",
"data": { "projectFilePath": "%{ProjectFile}" }
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators":
[
{
"typeId": "File",
"data":
[
{
"source": "file.nimproject",
"target": "%{NimProjectFile}",
"openAsProject": true
},
{
"source": "main.nim",
"target": "%{NimFileName}",
"openInEditor": true
},
{
"source": "../git.ignore",
"target": "%{ProjectDirectory}/.gitignore",
"condition": "%{JS: ! %{IsSubproject} && '%{VersionControl}' === 'G.Git'}"
}
]
}
]
}
{
\"Name\" : \"Nim\",
\"Version\" : \"$$QTCREATOR_VERSION\",
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
\"Vendor\" : \"Filippo Cucchetto\",
\"Copyright\" : \"(C) 2015 Filippo Cucchetto\",
\"License\" : [ \"Commercial Usage\",
\"\",
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\",
\"\",
\"GNU Lesser General Public License Usage\",
\"\",
\"Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 or version 3 as published by the Free Software Foundation. Please review the following information to ensure the GNU Lesser General Public License requirements will be met: https://www.gnu.org/licenses/lgpl.html and http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\"
],
\"Category\" : \"Other Languages\",
\"Description\" : \"Plugin for supporting the Nim programming language.\",
\"Url\" : \"http://www.qt.io\",
\"Experimental\" : true,
$$dependencyList
}
<?xml version="1.0"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="text/x-nim-project">
<sub-class-of type="text/plain"/>
<comment>Nim project file</comment>
<glob pattern="*.nimproject"/>
</mime-type>
<mime-type type="text/x-nim">
<sub-class-of type="text/plain"/>
<comment>Nim source file </comment>
<glob pattern="*.nim"/>
</mime-type>
</mime-info>
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "editor/nimeditorfactory.h"
#include "nimconstants.h"
#include "nimplugin.h"
#include "editor/nimindenter.h"
#include "editor/nimhighlighter.h"
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/texteditorconstants.h>
#include <texteditor/textdocument.h>
#include <utils/qtcassert.h>
using namespace TextEditor;
using namespace Utils;
namespace Nim {
NimEditorFactory::NimEditorFactory()
{
setId(Constants::C_NIMEDITOR_ID);
setDisplayName(tr(Nim::Constants::C_EDITOR_DISPLAY_NAME));
addMimeType(QLatin1String(Nim::Constants::C_NIM_MIMETYPE));
setEditorActionHandlers(TextEditorActionHandler::Format
| TextEditorActionHandler::UnCommentSelection
| TextEditorActionHandler::UnCollapseAll);
setEditorWidgetCreator([]{
auto result = new TextEditorWidget();
result->setLanguageSettingsId(Nim::Constants::C_NIMLANGUAGE_ID);
return result;
});
setDocumentCreator([]() {
return new TextDocument(Constants::C_NIMEDITOR_ID);
});
setIndenterCreator([]() {
return new NimIndenter;
});
setSyntaxHighlighterCreator([]() {
return new NimHighlighter;
});
setCommentStyle(CommentDefinition::HashStyle);
setParenthesesMatchingEnabled(true);
setMarksVisible(false);
setCodeFoldingSupported(true);
setMarksVisible(true);
}
Core::IEditor *NimEditorFactory::createEditor()
{
return TextEditorFactory::createEditor();
}
}
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <texteditor/texteditor.h>
namespace Nim {
class NimEditorFactory : public TextEditor::TextEditorFactory
{
Q_OBJECT
public:
NimEditorFactory();
Core::IEditor *createEditor() override;
};
}
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "editor/nimhighlighter.h"
#include "tools/nimlexer.h"
#include <texteditor/textdocument.h>
#include <texteditor/texteditorconstants.h>
#include <utils/qtcassert.h>
namespace Nim {
NimHighlighter::NimHighlighter()
{
initTextFormats();
}
void NimHighlighter::highlightBlock(const QString &text)
{
setCurrentBlockState(highlightLine(text, previousBlockState()));
}
void NimHighlighter::initTextFormats()
{
static QMap<Category, TextEditor::TextStyle> categoryStyle = {
{TextCategory, TextEditor::C_TEXT},
{KeywordCategory, TextEditor::C_KEYWORD},
{CommentCategory, TextEditor::C_COMMENT},
{DocumentationCategory, TextEditor::C_DOXYGEN_COMMENT},
{TypeCategory, TextEditor::C_TYPE},
{StringCategory, TextEditor::C_STRING},
{NumberCategory, TextEditor::C_NUMBER},
{OperatorCategory, TextEditor::C_OPERATOR},
{FunctionCategory, TextEditor::C_FUNCTION},
};
QVector<TextEditor::TextStyle> formats;
for (const auto &category : categoryStyle.keys())
formats << categoryStyle[category];
setTextFormatCategories(formats);
}
NimHighlighter::Category NimHighlighter::categoryForToken(const NimLexer::Token &token,
const QString &tokenValue)
{
switch (token.type) {
case NimLexer::TokenType::Keyword:
return KeywordCategory;
case NimLexer::TokenType::Identifier:
return categoryForIdentifier(token, tokenValue);
case NimLexer::TokenType::Comment:
return CommentCategory;
case NimLexer::TokenType::Documentation:
return DocumentationCategory;
case NimLexer::TokenType::StringLiteral:
return StringCategory;
case NimLexer::TokenType::MultiLineStringLiteral:
return StringCategory;
case NimLexer::TokenType::Operator:
return OperatorCategory;
case NimLexer::TokenType::Number:
return NumberCategory;
default:
return TextCategory;
}
}
NimHighlighter::Category NimHighlighter::categoryForIdentifier(const NimLexer::Token &token,
const QString &tokenValue)
{
Q_UNUSED(token)
QTC_ASSERT(token.type == NimLexer::TokenType::Identifier, return TextCategory);
static QSet<QString> nimBuiltInValues {
"true", "false"
};
static QSet<QString> nimBuiltInFunctions {
"echo", "isMainModule"
};
static QSet<QString> nimBuiltInTypes {
"bool", "cbool", "string",
"cstring", "int", "cint",
"uint", "cuint", "long",
"clong", "double", "cdouble",
"table", "RootObj"
};
if (nimBuiltInFunctions.contains(tokenValue))
return TypeCategory;
if (nimBuiltInValues.contains(tokenValue))
return KeywordCategory;
if (nimBuiltInTypes.contains(tokenValue))
return TypeCategory;
return TextCategory;
}
int NimHighlighter::highlightLine(const QString &text, int initialState)
{
NimLexer lexer(text.constData(),
text.size(),
static_cast<NimLexer::State>(initialState));
NimLexer::Token tk;
while ((tk = lexer.next()).type != NimLexer::TokenType::EndOfText) {
int category = categoryForToken(tk, text.mid(tk.begin, tk.length));
setFormat(tk.begin, tk.length, formatForCategory(category));
}
return lexer.state();
}
}
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "tools/nimlexer.h"
#include <texteditor/syntaxhighlighter.h>
namespace Nim {
class NimHighlighter : public TextEditor::SyntaxHighlighter
{
Q_OBJECT
enum Category {
TextCategory = 0,
KeywordCategory,
CommentCategory,
DocumentationCategory,
TypeCategory,
StringCategory,
NumberCategory,
OperatorCategory,
FunctionCategory
};
public:
NimHighlighter();
protected:
void highlightBlock(const QString &text) override;
private:
void initTextFormats();
Category categoryForToken(const NimLexer::Token &token, const QString &tokenValue);
Category categoryForIdentifier(const NimLexer::Token &token, const QString &tokenValue);
int highlightLine(const QString &text, int initialState);
};
}
/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "editor/nimindenter.h"
#include "tools/nimlexer.h"
#include <texteditor/icodestylepreferences.h>
#include <texteditor/tabsettings.h>
#include <texteditor/simplecodestylepreferences.h>
#include <texteditor/tabsettings.h>
#include <QSet>
#include <QDebug>
namespace Nim {
NimIndenter::NimIndenter()
{}
bool NimIndenter::isElectricCharacter(const QChar &ch) const
{
return NimIndenter::electricCharacters().contains(ch);
}
void NimIndenter::indentBlock(QTextDocument *document,
const QTextBlock &block,
const QChar &typedChar,
const TextEditor::TabSettings &settings)
{
Q_UNUSED(document);
Q_UNUSED(typedChar);
const QString currentLine = block.text();
const QTextBlock previousBlock = block.previous();
const QString previousLine = previousBlock.text();
const int previousState = previousBlock.userState();
if (!previousBlock.isValid()) {
settings.indentLine(block, 0);
return;
}
// Calculate indentation
int indentation = 0;
if (rightTrimmed(currentLine).isEmpty()) {
// Current line is empty so we calculate indentation based on previous line
const int indentationDiff = calculateIndentationDiff(previousLine, previousState, settings.m_indentSize);
indentation = settings.indentationColumn(previousLine) + indentationDiff;
}
else {
// We don't change indentation if the line is already indented.
// This is safer but sub optimal
indentation = settings.indentationColumn(block.text());
}
// Sets indentation
settings.indentLine(block, std::max(0, indentation));
}
const QSet<QChar> &NimIndenter::electricCharacters()
{
static QSet<QChar> result { QLatin1Char(':'), QLatin1Char('=') };
return result;
}
bool NimIndenter::startsBlock(const QString &line, int state) const
{
NimLexer lexer(line.constData(), line.length(), static_cast<NimLexer::State>(state));
// Read until end of line and save the last token
NimLexer::Token previous;
NimLexer::Token current = lexer.next();
while (current.type != NimLexer::TokenType::EndOfText) {
switch (current.type) {
case NimLexer::TokenType::Comment:
case NimLexer::TokenType::Documentation:
break;
default:
previous = current;
break;
}
current = lexer.next();
}
// electric characters start a new block, and are operators
if (previous.type == NimLexer::TokenType::Operator) {
QStringRef ref = line.midRef(previous.begin, previous.length);
return ref.isEmpty() ? false : electricCharacters().contains(ref.at(0));
}
// some keywords starts a new block
if (previous.type == NimLexer::TokenType::Keyword) {
QStringRef ref = line.midRef(previous.begin, previous.length);
return ref == QLatin1String("type")
|| ref == QLatin1String("var")
|| ref == QLatin1String("let")
|| ref == QLatin1String("enum")
|| ref == QLatin1String("object");
}
return false;
}
bool NimIndenter::endsBlock(const QString &line, int state) const
{
NimLexer lexer(line.constData(), line.length(), static_cast<NimLexer::State>(state));
// Read until end of line and save the last tokens
NimLexer::Token previous;
NimLexer::Token current = lexer.next();
while (current.type != NimLexer::TokenType::EndOfText) {
previous = current;
current = lexer.next();
}
// Some keywords end a block
if (previous.type == NimLexer::TokenType::Keyword) {
QStringRef ref = line.midRef(previous.begin, previous.length);
return ref == QLatin1String("return")
|| ref == QLatin1String("break")
|| ref == QLatin1String("continue");
}
return false;
}
int NimIndenter::calculateIndentationDiff(const QString &previousLine, int previousState, int indentSize) const
{
if (previousLine.isEmpty())
return 0;
if (startsBlock(previousLine, previousState))
return indentSize;