- enhancement: Highlighter for makefiles

This commit is contained in:
Roy Qu 2022-12-06 22:51:59 +08:00
parent 650e3b407c
commit 8af963579a
10 changed files with 826 additions and 31 deletions

View File

@ -1,3 +1,7 @@
Red Panda C++ Version 2.6
- enhancement: Highlighter for makefiles
Red Panda C++ Version 2.5
- enhancement: New color scheme Monokai (contributed by 小龙Dev(XiaoLoong@github))

View File

@ -21,6 +21,8 @@
#include "qsynedit/highlighter/cpp.h"
#include "qsynedit/highlighter/asm.h"
#include "qsynedit/highlighter/glsl.h"
#include "qsynedit/highlighter/makefilehighlighter.h"
#include "qsynedit/Constants.h"
#include "colorscheme.h"
@ -38,6 +40,8 @@ QSynedit::PHighlighter HighlighterManager::getHighlighter(QSynedit::HighlighterL
return getCppHighlighter();
case QSynedit::HighlighterLanguage::Asssembly:
return getAsmHighlighter();
case QSynedit::HighlighterLanguage::Makefile:
return getMakefileHighlighter();
case QSynedit::HighlighterLanguage::GLSL:
return getGLSLHighlighter();
default:
@ -49,6 +53,7 @@ QSynedit::PHighlighter HighlighterManager::getHighlighter(const QString &filenam
{
QFileInfo info(filename);
QString suffix = info.suffix();
QString basename = info.baseName();
if (suffix.isEmpty() || suffix == "c" || suffix == "cpp" || suffix == "cxx"
|| suffix == "cc" || suffix == "h" || suffix == "hpp"
|| suffix == "hxx" || suffix == "hh" || suffix == "C"
@ -59,7 +64,8 @@ QSynedit::PHighlighter HighlighterManager::getHighlighter(const QString &filenam
return getGLSLHighlighter();
} else if (suffix == "s" || suffix == "asm") {
return getAsmHighlighter();
}
} else if (basename.compare("makefile", Qt::CaseInsensitive)==0)
return getMakefileHighlighter();
return QSynedit::PHighlighter();
}
@ -67,14 +73,7 @@ QSynedit::PHighlighter HighlighterManager::copyHighlighter(QSynedit::PHighlighte
{
if (!highlighter)
return QSynedit::PHighlighter();
if (highlighter->language() == QSynedit::HighlighterLanguage::Cpp)
return getCppHighlighter();
else if (highlighter->language() == QSynedit::HighlighterLanguage::Asssembly)
return getAsmHighlighter();
else if (highlighter->language() == QSynedit::HighlighterLanguage::GLSL)
return getGLSLHighlighter();
//todo
return QSynedit::PHighlighter();
return getHighlighter(highlighter->language());
}
QSynedit::PHighlighter HighlighterManager::getCppHighlighter()
@ -95,26 +94,29 @@ QSynedit::PHighlighter HighlighterManager::getGLSLHighlighter()
return highlighter;
}
QSynedit::PHighlighter HighlighterManager::getMakefileHighlighter()
{
std::shared_ptr<QSynedit::MakefileHighlighter> highlighter=std::make_shared<QSynedit::MakefileHighlighter>();
return highlighter;
}
void HighlighterManager::applyColorScheme(QSynedit::PHighlighter highlighter, const QString &schemeName)
{
if (!highlighter)
return;
if ( (highlighter->language() == QSynedit::HighlighterLanguage::Cpp)
|| (highlighter->language() == QSynedit::HighlighterLanguage::Asssembly)
) {
for (QString name: highlighter->attributes().keys()) {
PColorSchemeItem item = pColorManager->getItem(schemeName,name);
if (item) {
QSynedit::PHighlighterAttribute attr = highlighter->attributes()[name];
attr->setBackground(item->background());
attr->setForeground(item->foreground());
QSynedit::FontStyles styles = QSynedit::FontStyle::fsNone;
styles.setFlag(QSynedit::FontStyle::fsBold, item->bold());
styles.setFlag(QSynedit::FontStyle::fsItalic, item->italic());
styles.setFlag(QSynedit::FontStyle::fsUnderline, item->underlined());
styles.setFlag(QSynedit::FontStyle::fsStrikeOut, item->strikeout());
attr->setStyles(styles);
}
for (QString name: highlighter->attributes().keys()) {
PColorSchemeItem item = pColorManager->getItem(schemeName,name);
if (item) {
QSynedit::PHighlighterAttribute attr = highlighter->attributes()[name];
attr->setBackground(item->background());
attr->setForeground(item->foreground());
QSynedit::FontStyles styles = QSynedit::FontStyle::fsNone;
styles.setFlag(QSynedit::FontStyle::fsBold, item->bold());
styles.setFlag(QSynedit::FontStyle::fsItalic, item->italic());
styles.setFlag(QSynedit::FontStyle::fsUnderline, item->underlined());
styles.setFlag(QSynedit::FontStyle::fsStrikeOut, item->strikeout());
attr->setStyles(styles);
}
}
}

View File

@ -29,6 +29,7 @@ public:
QSynedit::PHighlighter getCppHighlighter();
QSynedit::PHighlighter getAsmHighlighter();
QSynedit::PHighlighter getGLSLHighlighter();
QSynedit::PHighlighter getMakefileHighlighter();
void applyColorScheme(QSynedit::PHighlighter highlighter, const QString& schemeName);
};

View File

@ -467,7 +467,7 @@ RESOURCES += \
RC_ICONS = images/devcpp.ico images/associations/c.ico images/associations/cpp.ico images/associations/dev.ico images/associations/c.ico images/associations/cpp.ico images/associations/h.ico images/associations/hpp.ico
## fixed lrelease.prf
# fixed lrelease.prf
qtPrepareTool(QMAKE_LRELEASE, lrelease)
isEmpty(LRELEASE_DIR): LRELEASE_DIR = .qm

View File

@ -52,8 +52,6 @@
#include "project.h"
#include <qt_utils/charsetinfo.h>
using namespace std;
SaveException::SaveException(const QString& reason) {
mReason = reason;
mReasonBuffer = mReason.toLocal8Bit();
@ -912,7 +910,7 @@ void Editor::onGetEditingAreas(int Line, QSynedit::EditingAreaList &areaList)
{
areaList.clear();
if (mTabStopBegin>=0 && mTabStopY == Line) {
QSynedit::PEditingArea p = make_shared<QSynedit::EditingArea>();
QSynedit::PEditingArea p = std::make_shared<QSynedit::EditingArea>();
p->type = QSynedit::EditingAreaType::eatRectangleBorder;
// int spaceCount = leftSpaces(mLineBeforeTabStop);
// int spaceBefore = mLineBeforeTabStop.length()-TrimLeft(mLineBeforeTabStop).length();

View File

@ -1331,8 +1331,8 @@ void MainWindow::setProjectCurrentFile(const QString &filename)
if (!unit)
return;
QModelIndex index = mProject->model()->getNodeIndex(unit->node().get());
index = mProjectProxyModel->mapFromSource(index);
if (index.isValid()) {
index = mProjectProxyModel->mapFromSource(index);
ui->projectView->expand(index);
ui->projectView->setCurrentIndex(index);
}

View File

@ -39,7 +39,8 @@ SOURCES += qsynedit/CodeFolding.cpp \
qsynedit/Search.cpp \
qsynedit/SearchBase.cpp \
qsynedit/SearchRegex.cpp \
qsynedit/Types.cpp
qsynedit/Types.cpp \
qsynedit/highlighter/makefilehighlighter.cpp
HEADERS += qsynedit/Search.h \
qsynedit/SearchBase.h \
@ -62,5 +63,6 @@ HEADERS += qsynedit/Search.h \
qsynedit/highlighter/cpp.h \
qsynedit/highlighter/customhighlighterv1.h \
qsynedit/highlighter/glsl.h \
qsynedit/highlighter/makefilehighlighter.h
INCLUDEPATH += ../redpanda_qt_utils

View File

@ -80,6 +80,7 @@ enum class HighlighterLanguage {
Asssembly,
Cpp,
GLSL,
Makefile,
Custom
};

View File

@ -0,0 +1,641 @@
/*
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "makefilehighlighter.h"
#include "../Constants.h"
//#include <QDebug>
namespace QSynedit {
const QSet<QString> MakefileHighlighter::Directives {
"abspath",
"addprefix",
"addsuffix",
"and",
"AR",
"ARFLAGS",
"AS",
"ASFLAGS",
"basename",
"bindir",
"call",
"CC",
"CFLAGS",
"CO",
"COFLAGS",
"COMSPEC",
"CPP",
"CPPFLAGS",
"CTANGLE",
"CURDIR",
"CWEAVE",
"CXX",
"CXXFLAGS",
"define",
"DESTDIR",
"dir",
"else",
"endef",
"endif",
"error",
"eval",
"exec_prefix",
"export",
"FC",
"FFLAGS",
"file",
"filter",
"filter-out",
"findstring",
"firstword",
"flavor",
"foreach",
"GET",
"GFLAGS",
"gmk-eval",
"gmk-expand",
"gmk_add_function",
"gmk_alloc",
"gmk_eval",
"gmk_expand",
"gmk_free",
"gmk_func_ptr",
"GNUmakefile",
"GPATH",
"guile",
"if",
"if",
"ifdef",
"ifeq",
"ifndef",
"ifneq",
"include",
"info",
"intcmp",
"join",
"lastword",
"LDFLAGS",
"LDLIBS",
"let",
"LEX",
"LFLAGS",
"libexecdir",
"LINT",
"LINTFLAGS",
"load",
"LOADLIBES",
"M2C",
"MAKE",
"MAKE",
"MAKECMDGOALS",
"Makefile",
"makefile",
"MAKEFILES",
"MAKEFILES",
"MAKEFILE_LIST",
"MAKEFLAGS",
"MAKEINFO",
"MAKELEVEL",
"MAKELEVEL",
"MAKEOVERRIDES",
"MAKESHELL"
"MAKE_HOST",
"MAKE_RESTARTS"
"MAKE_TERMERR"
"MAKE_TERMOUT"
"MAKE_VERSION",
"MFLAGS",
"notdir",
"or",
"origin",
"OUTPUT_OPTION",
"override",
"patsubst",
"patsubst",
"PC",
"PFLAGS",
"prefix",
"private",
"realpath",
"RFLAGS",
"RM",
"sbindir",
"SHELL",
"shell",
"sort",
"strip",
"subst",
"subst",
"suffix",
"SUFFIXES",
"TANGLE",
"TEX",
"TEXI2DVI",
"undefine",
"unexport",
"value",
"VPATH",
"VPATH",
"vpath",
"vpath",
"warning",
"WEAVE",
"wildcard",
"wildcard",
"word",
"wordlist",
"words",
"YACC",
"YFLAGS",
};
MakefileHighlighter::MakefileHighlighter()
{
mTargetAttribute = std::make_shared<HighlighterAttribute>(SYNS_AttrClass, TokenType::Identifier);
addAttribute(mTargetAttribute);
mCommandAttribute = std::make_shared<HighlighterAttribute>(SYNS_AttrGlobalVariable, TokenType::Identifier);
addAttribute(mCommandAttribute);
mCommandParamAttribute = std::make_shared<HighlighterAttribute>(SYNS_AttrPreprocessor, TokenType::Identifier);
addAttribute(mCommandParamAttribute);
mNumberAttribute = std::make_shared<HighlighterAttribute>(SYNS_AttrNumber, TokenType::Number);
addAttribute(mNumberAttribute);
mVariableAttribute = std::make_shared<HighlighterAttribute>(SYNS_AttrLocalVariable, TokenType::Identifier);
addAttribute(mVariableAttribute);
mExpressionAttribute = std::make_shared<HighlighterAttribute>(SYNS_AttrFunction, TokenType::Identifier);
addAttribute(mExpressionAttribute);
}
void MakefileHighlighter::procSpace()
{
mTokenID = TokenId::Space;
while (mLine[mRun]!=0 && mLine[mRun]<=32)
mRun++;
}
void MakefileHighlighter::procNumber()
{
while (isNumberChar(mLine[mRun]))
mRun++;
mTokenID = TokenId::Number;
}
void MakefileHighlighter::procNull()
{
mTokenID = TokenId::Null;
mState = RangeState::Unknown;
}
void MakefileHighlighter::procString(bool inExpression )
{
mState = RangeState::String;
mTokenID = TokenId::String;
while (mLine[mRun] != 0) {
if (mLine[mRun] == '\"') {
mRun++;
popState();
break;
} else if (!inExpression && mLine[mRun] == '$') {
break;
} else if (isSpaceChar(mLine[mRun])) {
break;
} else
mRun++;
}
}
void MakefileHighlighter::procStringStart()
{
mRun++;
pushState();
procString(mState!=RangeState::BraceExpression
&& mState!=RangeState::ParenthesisExpression);
}
void MakefileHighlighter::procExpressionStart(ExpressionStartType type)
{
mRun+=2; //skip '$(' or '${'
pushState();
switch(type) {
case ExpressionStartType::Brace:
mState = RangeState::BraceExpression;
break;
case ExpressionStartType::Parenthesis:
mState = RangeState::ParenthesisExpression;
break;
}
mTokenID = TokenId::Expression;
}
void MakefileHighlighter::procExpressionEnd()
{
mTokenID = TokenId::Expression;
mRun+=1;
popState();
}
void MakefileHighlighter::procSymbol()
{
mTokenID = TokenId::Symbol;
mRun+=1;
}
void MakefileHighlighter::procVariableExpression()
{
mRun+=1; //skip $
while (isIdentStartChar(mLine[mRun]))
mRun++;
mTokenID = TokenId::Variable;
}
void MakefileHighlighter::procAutoVariable()
{
mRun+=1; //skip $
switch(mLine[mRun].unicode()) {
case '@':
case '%':
case '<':
case '?':
case '^':
case '+':
case '|':
case '*':
case '$':
mRun+=1;
mTokenID=TokenId::Expression;
break;
default:
mTokenID=TokenId::Symbol;
break;
}
}
void MakefileHighlighter::procAssignment()
{
mTokenID = TokenId::Symbol;
mRun++;
mState = RangeState::Assignment;
}
void MakefileHighlighter::procDollar()
{
if (mLine[mRun+1]=='(') {
procExpressionStart(ExpressionStartType::Parenthesis);
} else if (mLine[mRun+1]=='{') {
procExpressionStart(ExpressionStartType::Brace);
} else if (isIdentStartChar(mLine[mRun+1])) {
procVariableExpression();
} else {
procAutoVariable();
}
}
void MakefileHighlighter::procComment()
{
mRun++; //skip #
mRun = mLineString.length();
mTokenID = TokenId::Comment;
}
void MakefileHighlighter::procIdentifier()
{
int start = mRun;
while (isIdentChar(mLine[mRun])) {
mRun++;
}
QString s = mLineString.mid(start,mRun-start).toLower();
if (Directives.contains(s)) {
mTokenID = TokenId::Directive;
} else {
switch(mState) {
case RangeState::Assignment:
case RangeState::BraceExpression:
case RangeState::ParenthesisExpression:
mTokenID = TokenId::Variable;
break;
case RangeState::CommandParameters:
mTokenID = TokenId::CommandParam;
break;
case RangeState::Command:
mTokenID = TokenId::Command;
mState = RangeState::CommandParameters;
break;
case RangeState::Prequisitions:
case RangeState::Unknown:
mTokenID = TokenId::Target;
break;
case RangeState::String:
mTokenID = TokenId::String;
break;
}
}
}
void MakefileHighlighter::pushState()
{
mStates.push_back(mState);
}
void MakefileHighlighter::popState()
{
if (!mStates.empty()) {
mState = mStates.back();
mStates.pop_back();
}
}
bool MakefileHighlighter::isIdentChar(const QChar &ch) const
{
if (ch == '_' || ch =='-') {
return true;
}
if ((ch>='0') && (ch <= '9')) {
return true;
}
if ((ch>='a') && (ch <= 'z')) {
return true;
}
if ((ch>='A') && (ch <= 'Z')) {
return true;
}
return false;
}
bool MakefileHighlighter::eol() const
{
return mTokenID == TokenId::Null;
}
QString MakefileHighlighter::languageName()
{
return "makefile";
}
HighlighterLanguage MakefileHighlighter::language()
{
return HighlighterLanguage::Makefile;
}
QString MakefileHighlighter::getToken() const
{
return mLineString.mid(mTokenPos,mRun-mTokenPos);
}
const PHighlighterAttribute &MakefileHighlighter::getTokenAttribute() const
{
/*
Directive,
Unknown
*/
switch(mTokenID) {
case TokenId::Comment:
return mCommentAttribute;
case TokenId::Target:
return mTargetAttribute;
case TokenId::Command:
return mCommandAttribute;
case TokenId::CommandParam:
return mCommandParamAttribute;
case TokenId::Number:
return mNumberAttribute;
case TokenId::Space:
return mWhitespaceAttribute;
case TokenId::String:
return mStringAttribute;
case TokenId::Identifier:
return mIdentifierAttribute;
case TokenId::Variable:
return mVariableAttribute;
case TokenId::Expression:
return mExpressionAttribute;
case TokenId::Symbol:
return mSymbolAttribute;
case TokenId::Directive:
return mKeywordAttribute;
default:
return mSymbolAttribute;
}
}
int MakefileHighlighter::getTokenPos()
{
return mTokenPos;
}
void MakefileHighlighter::next()
{
mTokenPos = mRun;
if (mLine[mRun].unicode()==0) {
procNull();
return;
} else if (mRun==0 && mLine[mRun]=='\t') {
mState = RangeState::Command;
procSpace();
return;
} else if (isSpaceChar(mLine[mRun].unicode())) {
procSpace();
return;
}
switch(mState) {
case RangeState::String:
if (mLine[mRun] == '$')
procDollar();
else
procString(false);
break;
case RangeState::Command:
case RangeState::CommandParameters:
case RangeState::Prequisitions:
case RangeState::Assignment:
switch(mLine[mRun].unicode()) {
case '$':
procDollar();
break;
case '\"':
procStringStart();
break;
case '#':
procComment();
break;
default:
if (mLine[mRun]>='0' && mLine[mRun]<='9') {
procNumber();
} else if (isIdentStartChar(mLine[mRun])) {
procIdentifier();
} else {
procSymbol();
}
}
break;
case RangeState::ParenthesisExpression:
case RangeState::BraceExpression:
switch(mLine[mRun].unicode()) {
case '$':
procDollar();
break;
case ')':
if (mState == RangeState::ParenthesisExpression)
procExpressionEnd();
else
procSymbol();
break;
case '}':
if (mState == RangeState::BraceExpression)
procExpressionEnd();
else
procSymbol();
break;
case '#':
procComment();
break;
case '@':
case '+':
case '*':
case '%':
case '^':
case '<':
case '?':
if (mLine[mRun]=='D' || mLine[mRun]=='F') {
//auto variable
mRun+=2;
mTokenID = TokenId::Variable;
} else
procSymbol();
break;
default:
if (mLine[mRun]>='0' && mLine[mRun]<='9') {
procNumber();
} else if (isIdentStartChar(mLine[mRun])) {
procIdentifier();
} else {
procSymbol();
}
}
break;
case RangeState::Unknown:
switch(mLine[mRun].unicode()) {
case '$':
procDollar();
break;
case '#':
procComment();
break;
case '\"':
procStringStart();
break;
case '?':
case '+':
if (mLine[mRun+1]=='=') {
mRun++;
procAssignment();
} else {
procSymbol();
}
break;
case ':':
if (mLine[mRun+1]=='=') {
mRun++;
procAssignment();
} else {
mRun++;
mTokenID = TokenId::Target;
mState = RangeState::Prequisitions;
}
break;
case '=':
procAssignment();
break;
default:
if (mLine[mRun]>='0' && mLine[mRun]<='9') {
procNumber();
} else if (isIdentStartChar(mLine[mRun])) {
procIdentifier();
} else {
procSymbol();
}
}
}
}
void MakefileHighlighter::setLine(const QString &newLine, int lineNumber)
{
mLineString = newLine;
mLine = mLineString.data();
mLineNumber = lineNumber;
mRun = 0;
next();
}
bool MakefileHighlighter::getTokenFinished() const
{
return true;
}
bool MakefileHighlighter::isLastLineCommentNotFinished(int /*state*/) const
{
return false;
}
bool MakefileHighlighter::isLastLineStringNotFinished(int /*state*/) const
{
return false;
}
HighlighterState MakefileHighlighter::getState() const
{
HighlighterState state;
state.state = (int)mState;
return state;
}
void MakefileHighlighter::setState(const HighlighterState & rangeState)
{
mState = (RangeState)rangeState.state;
mStates.clear();
}
void MakefileHighlighter::resetState()
{
mState = RangeState::Unknown;
mStates.clear();
}
QSet<QString> MakefileHighlighter::keywords() const
{
return Directives;
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MAKEFILEHIGHLIGHTER_H
#define MAKEFILEHIGHLIGHTER_H
#include "base.h"
#include <QVector>
namespace QSynedit {
class MakefileHighlighter : public Highlighter
{
enum class TokenId {
Null,
Comment,
Target,
Command,
CommandParam,
Number,
Space,
String,
Identifier,
Variable,
Expression,
Symbol,
Directive
};
enum RangeState {
Unknown, String,
/* Targets, */
Prequisitions,
Command,
CommandParameters,
ParenthesisExpression,
BraceExpression,
Assignment,
};
enum ExpressionStartType {
Parenthesis,
Brace
};
public:
explicit MakefileHighlighter();
static const QSet<QString> Directives;
private:
QChar* mLine;
QString mLineString;
int mLineNumber;
int mRun;
int mStringLen;
int mTokenPos;
QVector<RangeState> mStates;
RangeState mState;
TokenId mTokenID;
PHighlighterAttribute mTargetAttribute;
PHighlighterAttribute mCommandAttribute;
PHighlighterAttribute mCommandParamAttribute;
PHighlighterAttribute mNumberAttribute;
PHighlighterAttribute mVariableAttribute;
PHighlighterAttribute mExpressionAttribute;
private:
void procSpace();
void procNumber();
void procNull();
void procString(bool inExpression );
void procStringStart();
void procExpressionStart(ExpressionStartType type);
void procExpressionEnd();
void procSymbol();
void procVariableExpression();
void procAutoVariable();
void procAssignment();
void procDollar();
void procComment();
void procIdentifier();
void pushState();
void popState();
bool isIdentStartChar(const QChar& ch) {
if (ch == '_') {
return true;
}
if ((ch>='a') && (ch <= 'z')) {
return true;
}
if ((ch>='A') && (ch <= 'Z')) {
return true;
}
return false;
}
bool isNumberChar(const QChar& ch) {
return (ch>='0') && (ch<='9');
}
// SynHighlighter interface
public:
bool eol() const override;
QString languageName() override;
HighlighterLanguage language() override;
QString getToken() const override;
const PHighlighterAttribute &getTokenAttribute() const override;
int getTokenPos() override;
void next() override;
void setLine(const QString &newLine, int lineNumber) override;
// SynHighlighter interface
public:
bool getTokenFinished() const override;
bool isLastLineCommentNotFinished(int state) const override;
bool isLastLineStringNotFinished(int state) const override;
HighlighterState getState() const override;
void setState(const HighlighterState& rangeState) override;
void resetState() override;
bool isIdentChar(const QChar& ch) const override;
public:
QSet<QString> keywords() const override;
};
}
#endif // MAKEFILEHIGHLIGHTER_H