mirror of https://github.com/vitalif/openscad
rewrite large bit of syntax highlighter, add 'manual' test instructions
parent
2834e4265b
commit
3b7bc56bde
|
@ -24,118 +24,174 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// Syntax Highlight code by Chris Olah
|
||||
// based on Syntax Highlight code by Chris Olah
|
||||
|
||||
/* test suite
|
||||
|
||||
1. action: open example001, remove first {, hit f5
|
||||
expected result: red highlight appears on last }, cursor moves there
|
||||
action: replace first {, hit f5
|
||||
expected result: red highlight disappears
|
||||
|
||||
2. action: type a=b
|
||||
expected result: '=' is highlighted as appropriate
|
||||
|
||||
3. action: open example001, put '===' after first ;
|
||||
expected result: red highlight appears in ===
|
||||
action: remove '==='
|
||||
expected result: red highlight disappears
|
||||
|
||||
4. action: open example001, remove last ';' but not trailing whitespace/\n
|
||||
expected result: red highlight appears on last line
|
||||
action: replace last ';'
|
||||
expected result: red highlight disappears
|
||||
|
||||
5. action: open file, type in a multi-line comment
|
||||
expected result: multiline comment should be highlighted appropriately
|
||||
|
||||
6. action: open example001, put a single '=' after first {
|
||||
expected result: red highlight of '=' you added
|
||||
|
||||
*/
|
||||
|
||||
#include "highlighter.h"
|
||||
#include "parsersettings.h" // extern int parser_error_pos;
|
||||
#include <QTextDocument>
|
||||
|
||||
Highlighter::Highlighter(QTextDocument *parent, mode_e mode)
|
||||
#include <iostream>
|
||||
Highlighter::Highlighter(QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent)
|
||||
{
|
||||
this->mode = mode;
|
||||
operators << "!" << "&&" << "||" << "+" << "-" << "*" << "/" << "%" << "!" << "#" << ";";
|
||||
KeyWords << "for" << "intersection_for" << "if" << "assign"
|
||||
<< "module" << "function"
|
||||
<< "$children" << "child" << "$fn" << "$fa" << "$fb" // Lump special variables in here
|
||||
<< "union" << "intersection" << "difference" << "render"; //Lump CSG in here
|
||||
Primitives3D << "cube" << "cylinder" << "sphere" << "polyhedron";
|
||||
Primitives2D << "square" << "polygon" << "circle";
|
||||
Transforms << "scale" << "translate" << "rotate" << "multmatrix" << "color"
|
||||
<< "linear_extrude" << "rotate_extrude"; // Lump extrudes in here.
|
||||
Imports << "include" << "use" << "import_stl";
|
||||
QMap<QString,QStringList> tokentypes;
|
||||
QMap<QString,QTextCharFormat> typeformats;
|
||||
|
||||
//this->OperatorStyle.setForeground
|
||||
KeyWordStyle.setForeground(Qt::darkGreen);
|
||||
TransformStyle.setForeground(Qt::darkGreen);
|
||||
PrimitiveStyle3D.setForeground(Qt::darkBlue);
|
||||
PrimitiveStyle2D.setForeground(Qt::blue);
|
||||
ImportStyle.setForeground(Qt::darkYellow);
|
||||
QuoteStyle.setForeground(Qt::darkMagenta);
|
||||
CommentStyle.setForeground(Qt::darkCyan);
|
||||
ErrorStyle.setForeground(Qt::red);
|
||||
tokentypes["operator"] << "=" << "!" << "&&" << "||" << "+" << "-" << "*" << "/" << "%" << "!" << "#" << ";";
|
||||
typeformats["operator"].setForeground(Qt::blue);
|
||||
|
||||
tokentypes["keyword"] << "for" << "intersection_for" << "if" << "assign" << "module" << "function";
|
||||
typeformats["keyword"].setForeground(Qt::darkGreen);
|
||||
|
||||
tokentypes["prim3d"] << "cube" << "cylinder" << "sphere" << "polyhedron";
|
||||
typeformats["prim3d"].setForeground(Qt::darkBlue);
|
||||
|
||||
tokentypes["prim2d"] << "square" << "polygon" << "circle";
|
||||
typeformats["prim2d"].setForeground(Qt::blue);
|
||||
|
||||
tokentypes["transform"] << "scale" << "translate" << "rotate" << "multmatrix" << "color" << "projection";
|
||||
typeformats["transform"].setForeground(Qt::darkGreen);
|
||||
|
||||
tokentypes["import"] << "include" << "use" << "import_stl";
|
||||
typeformats["import"].setForeground(Qt::darkYellow);
|
||||
|
||||
tokentypes["special"] << "$children" << "child" << "$fn" << "$fa" << "$fb";
|
||||
typeformats["special"].setForeground(Qt::darkGreen);
|
||||
|
||||
tokentypes["csgop"] << "union" << "intersection" << "difference" << "render";
|
||||
typeformats["csgop"].setForeground(Qt::darkGreen);
|
||||
|
||||
tokentypes["extrude"] << "linear_extrude" << "rotate_extrude";
|
||||
typeformats["extrude"].setForeground(Qt::darkGreen);
|
||||
|
||||
// for speed - put all tokens into single QHash, mapped to their format
|
||||
QList<QString>::iterator ki;
|
||||
QList<QString> toktypes = tokentypes.keys();
|
||||
for ( ki=toktypes.begin(); ki!=toktypes.end(); ++ki ) {
|
||||
QString toktype = *ki;
|
||||
QStringList::iterator it;
|
||||
for ( it = tokentypes[toktype].begin(); it < tokentypes[toktype].end(); ++it) {
|
||||
QString token = *it;
|
||||
//std::cout << token.toStdString() << "\n";
|
||||
formatMap[ token ] = typeformats [ toktype ];
|
||||
}
|
||||
}
|
||||
|
||||
quoteFormat.setForeground(Qt::darkMagenta);
|
||||
commentFormat.setForeground(Qt::darkCyan);
|
||||
errorFormat.setBackground(Qt::red);
|
||||
|
||||
// format tweaks
|
||||
formatMap[ "%" ].setFontWeight(QFont::Bold);
|
||||
|
||||
separators << tokentypes["operator"];
|
||||
separators << "(" << ")" << "[" << "]";
|
||||
|
||||
lastErrorBlock = parent->begin();
|
||||
}
|
||||
|
||||
void Highlighter::highlightError(int error_pos)
|
||||
{
|
||||
QTextBlock err_block = document()->findBlock(error_pos);
|
||||
std::cout << "error pos: " << error_pos << " doc len: " << document()->characterCount() << "\n";
|
||||
errorPos = error_pos;
|
||||
errorState = true;
|
||||
//if (errorPos == document()->characterCount()-1) errorPos--;
|
||||
rehighlightBlock( err_block ); // QT 4.6
|
||||
errorState = false;
|
||||
lastErrorBlock = err_block;
|
||||
}
|
||||
|
||||
void Highlighter::unhighlightLastError()
|
||||
{
|
||||
rehighlightBlock( lastErrorBlock );
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
void Highlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
if ( mode == NORMAL_MODE) {
|
||||
state_e state = (state_e) previousBlockState();
|
||||
//Key words and Primitives
|
||||
QStringList::iterator it;
|
||||
std::cout << "block[" << currentBlock().position() << ":"
|
||||
<< currentBlock().length() + currentBlock().position() << "]"
|
||||
<< ", err:" << errorPos << ", text:'" << text.toStdString() << "'\n";
|
||||
|
||||
for (it = KeyWords.begin(); it != KeyWords.end(); ++it){
|
||||
for (int i = 0; i < text.count(*it); ++i){
|
||||
setFormat(text.indexOf(*it),it->size(),KeyWordStyle);
|
||||
}
|
||||
// Split the block into pieces and highlight each as appropriate
|
||||
QString newtext = text;
|
||||
QStringList::iterator sep, token;
|
||||
int tokindex = -1; // deals w duplicate tokens in a single block
|
||||
for ( sep = separators.begin(); sep!=separators.end(); ++sep ) {
|
||||
// so a=b will have '=' highlighted
|
||||
newtext = newtext.replace( *sep, " " + *sep + " ");
|
||||
}
|
||||
for (it = Primitives3D.begin(); it != Primitives3D.end(); ++it){
|
||||
for (int i = 0; i < text.count(*it); ++i){
|
||||
setFormat(text.indexOf(*it),it->size(),PrimitiveStyle3D);
|
||||
}
|
||||
}
|
||||
for (it = Primitives2D.begin(); it != Primitives2D.end(); ++it){
|
||||
for (int i = 0; i < text.count(*it); ++i){
|
||||
setFormat(text.indexOf(*it),it->size(),PrimitiveStyle2D);
|
||||
}
|
||||
}
|
||||
for (it = Transforms.begin(); it != Transforms.end(); ++it){
|
||||
for (int i = 0; i < text.count(*it); ++i){
|
||||
setFormat(text.indexOf(*it),it->size(),TransformStyle);
|
||||
}
|
||||
}
|
||||
for (it = Imports.begin(); it != Imports.end(); ++it){
|
||||
for (int i = 0; i < text.count(*it); ++i){
|
||||
setFormat(text.indexOf(*it),it->size(),ImportStyle);
|
||||
QStringList tokens = newtext.split(QRegExp("\\s"));
|
||||
for ( token = tokens.begin(); token!=tokens.end(); ++token ){
|
||||
if ( formatMap.contains( *token ) ) {
|
||||
tokindex = text.indexOf( *token, tokindex+1 );
|
||||
// Speed note: setFormat() is the big slowdown in all of this code
|
||||
setFormat( tokindex, token->size(), formatMap[ *token ]);
|
||||
// std::cout << "found tok '" << (*token).toStdString() << "' at " << tokindex << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Quoting and Comments.
|
||||
// fixme multiline coments dont work
|
||||
state_e state = (state_e) previousBlockState();
|
||||
for (int n = 0; n < text.size(); ++n){
|
||||
if (state == NORMAL){
|
||||
if (text[n] == '"'){
|
||||
state = QUOTE;
|
||||
setFormat(n,1,QuoteStyle);
|
||||
setFormat(n,1,quoteFormat);
|
||||
} else if (text[n] == '/'){
|
||||
if (text[n+1] == '/'){
|
||||
setFormat(n,text.size(),CommentStyle);
|
||||
setFormat(n,text.size(),commentFormat);
|
||||
break;
|
||||
} else if (text[n+1] == '*'){
|
||||
setFormat(n++,2,CommentStyle);
|
||||
setFormat(n++,2,commentFormat);
|
||||
state = COMMENT;
|
||||
}
|
||||
}
|
||||
} else if (state == QUOTE){
|
||||
setFormat(n,1,QuoteStyle);
|
||||
setFormat(n,1,quoteFormat);
|
||||
if (text[n] == '"' && text[n-1] != '\\')
|
||||
state = NORMAL;
|
||||
} else if (state == COMMENT){
|
||||
setFormat(n,1,CommentStyle);
|
||||
setFormat(n,1,commentFormat);
|
||||
if (text[n] == '*' && text[n+1] == '/'){
|
||||
setFormat(++n,1,CommentStyle);
|
||||
setFormat(++n,1,commentFormat);
|
||||
state = NORMAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // not ErrorMode (syntax highlighting)
|
||||
|
||||
|
||||
// Errors
|
||||
else if (mode == ERROR_MODE) {
|
||||
int n = previousBlockState();
|
||||
if (n < 0)
|
||||
n = 0;
|
||||
int k = n + text.size() + 1;
|
||||
setCurrentBlockState(k);
|
||||
if (parser_error_pos >= n && parser_error_pos < k) {
|
||||
QTextCharFormat style;
|
||||
style.setBackground(Qt::red);
|
||||
setFormat(0, text.size(), style);
|
||||
#if 0
|
||||
style.setBackground(Qt::black);
|
||||
style.setForeground(Qt::white);
|
||||
setFormat(parser_error_pos - n, 1, style);
|
||||
#endif
|
||||
// Highlight an error. Do it last to 'overwrite' other formatting.
|
||||
if (errorState) {
|
||||
setFormat( errorPos - currentBlock().position() - 1, 1, errorFormat);
|
||||
}
|
||||
} // if errormode
|
||||
}
|
||||
|
||||
|
|
|
@ -7,26 +7,17 @@ class Highlighter : public QSyntaxHighlighter
|
|||
{
|
||||
public:
|
||||
enum state_e {NORMAL=-1,QUOTE,COMMENT};
|
||||
enum mode_e {NORMAL_MODE, ERROR_MODE};
|
||||
mode_e mode;
|
||||
|
||||
QStringList operators;
|
||||
QStringList KeyWords;
|
||||
QStringList Primitives3D;
|
||||
QStringList Primitives2D;
|
||||
QStringList Transforms;
|
||||
QStringList Imports;
|
||||
QTextCharFormat ErrorStyle;
|
||||
QTextCharFormat OperatorStyle;
|
||||
QTextCharFormat CommentStyle;
|
||||
QTextCharFormat QuoteStyle;
|
||||
QTextCharFormat KeyWordStyle;
|
||||
QTextCharFormat PrimitiveStyle3D;
|
||||
QTextCharFormat PrimitiveStyle2D;
|
||||
QTextCharFormat TransformStyle;
|
||||
QTextCharFormat ImportStyle;
|
||||
Highlighter(QTextDocument *parent, mode_e mode);
|
||||
QHash<QString, QTextCharFormat> formatMap;
|
||||
QTextCharFormat errorFormat, commentFormat, quoteFormat;
|
||||
Highlighter(QTextDocument *parent);
|
||||
void highlightBlock(const QString &text);
|
||||
void highlightError(int error_pos);
|
||||
void unhighlightLastError();
|
||||
private:
|
||||
QTextBlock lastErrorBlock;
|
||||
int errorPos = -1;
|
||||
bool errorState = false;
|
||||
QStringList separators;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -180,7 +180,7 @@ MainWindow::MainWindow(const QString &filename)
|
|||
fps = 0;
|
||||
fsteps = 1;
|
||||
|
||||
highlighter = new Highlighter(editor->document(), Highlighter::NORMAL_MODE);
|
||||
highlighter = new Highlighter(editor->document());
|
||||
editor->setTabStopWidth(30);
|
||||
editor->setLineWrapping(true); // Not designable
|
||||
|
||||
|
@ -1036,22 +1036,15 @@ bool MainWindow::compileTopLevelDocument(bool reload)
|
|||
QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
|
||||
false);
|
||||
|
||||
// Syntax & Error highlighting
|
||||
|
||||
if (!this->root_module) {
|
||||
if (highlighter->mode==Highlighter::NORMAL_MODE) {
|
||||
delete this->highlighter;
|
||||
highlighter = new Highlighter(editor->document(), Highlighter::ERROR_MODE);
|
||||
}
|
||||
|
||||
if (!animate_panel->isVisible()) {
|
||||
if (!animate_panel->isVisible()) {
|
||||
if (!this->root_module) {
|
||||
QTextCursor cursor = editor->textCursor();
|
||||
cursor.setPosition(parser_error_pos);
|
||||
editor->setTextCursor(cursor);
|
||||
cursor.setPosition( parser_error_pos );
|
||||
editor->setTextCursor( cursor );
|
||||
highlighter->highlightError( parser_error_pos );
|
||||
} else {
|
||||
highlighter->unhighlightLastError();
|
||||
}
|
||||
} else if (highlighter->mode==Highlighter::ERROR_MODE) {
|
||||
delete this->highlighter;
|
||||
this->highlighter = new Highlighter(editor->document(), Highlighter::NORMAL_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue