diff --git a/src/backends/maxima/maximahighlighter.h b/src/backends/maxima/maximahighlighter.h --- a/src/backends/maxima/maximahighlighter.h +++ b/src/backends/maxima/maximahighlighter.h @@ -34,6 +34,8 @@ protected: void highlightBlock(const QString &text) override; + Cantor::DefaultHighlighter::Section findNextCodeSection(const QString& text, Cantor::DefaultHighlighter::Section previous) override; + QString nonSeparatingCharacters() const override; private Q_SLOTS: void addUserVariables(const QStringList variables); diff --git a/src/backends/maxima/maximahighlighter.cpp b/src/backends/maxima/maximahighlighter.cpp --- a/src/backends/maxima/maximahighlighter.cpp +++ b/src/backends/maxima/maximahighlighter.cpp @@ -23,6 +23,10 @@ #include "maximasession.h" #include "maximavariablemodel.h" +#include + +using namespace Cantor; + MaximaHighlighter::MaximaHighlighter(QObject* parent, MaximaSession* session) : Cantor::DefaultHighlighter(parent) { //addRule(QRegExp("\\b[A-Za-z0-9_]+(?=\\()"), functionFormat()); @@ -57,6 +61,8 @@ if (skipHighlighting(text)) return; + DefaultHighlighter::Section::State state = getState(); + //Do some backend independent highlighting (brackets etc.) DefaultHighlighter::highlightBlock(text); @@ -74,32 +80,49 @@ startIndex = 0; } - for (int i = 0; i < text.size(); ++i) { - if (text[i] == QLatin1Char('\\')) { - ++i; // skip the next character - } else if (text[i] == QLatin1Char('"') && commentLevel == 0) { - if (!inString) - startIndex = i; - else - setFormat(startIndex, i - startIndex + 1, stringFormat()); - inString = !inString; - } else if (text.mid(i,2) == QLatin1String("/*") && !inString) { - if (commentLevel == 0) - startIndex = i; - ++commentLevel; - ++i; - } else if (text.mid(i,2) == QLatin1String("*/") && !inString) { - if (commentLevel == 0) { - setFormat(i, 2, errorFormat()); - // undo the --commentLevel below, so we stay at 0 + // TODO: remove this code and MaximaHighlighter::highlightBlock, when DefaultHighlighter starts + // support multiline highlighting? + QVector sections; + if (isPartialHighlighting()) + { + DefaultHighlighter::Section section = {-1, -1, state}; + do + { + section = findNextCodeSection(text, section); + sections.push_back(section); + } + while (section.end != -1); + } + else + sections.push_back(DefaultHighlighter::Section{0, text.size(), DefaultHighlighter::Section::State::Finished}); + + for (DefaultHighlighter::Section section : sections) + for (int i = section.begin; i < section.end; ++i) { + if (text[i] == QLatin1Char('\\')) { + ++i; // skip the next character + } else if (text[i] == QLatin1Char('"') && commentLevel == 0) { + if (!inString) + startIndex = i; + else + setFormat(startIndex, i - startIndex + 1, stringFormat()); + inString = !inString; + } else if (text.mid(i,2) == QLatin1String("/*") && !inString) { + if (commentLevel == 0) + startIndex = i; ++commentLevel; - } else if (commentLevel == 1) { - setFormat(startIndex, i - startIndex + 2, commentFormat()); + ++i; + } else if (text.mid(i,2) == QLatin1String("*/") && !inString) { + if (commentLevel == 0) { + setFormat(i, 2, errorFormat()); + // undo the --commentLevel below, so we stay at 0 + ++commentLevel; + } else if (commentLevel == 1) { + setFormat(startIndex, i - startIndex + 2, commentFormat()); + } + ++i; + --commentLevel; } - ++i; - --commentLevel; } - } if (inString) { setCurrentBlockState(-2); @@ -141,6 +164,50 @@ } } +DefaultHighlighter::Section MaximaHighlighter::findNextCodeSection(const QString& text, DefaultHighlighter::Section previous) +{ + DefaultHighlighter::Section section; + section.state = previous.state; + + if (previous.end >= text.size()) + { + section.begin = -1; + section.end = -1; + if (text.endsWith(QLatin1String("$")) || text.endsWith(QLatin1String(";"))) + section.state = DefaultHighlighter::Section::State::Finished; + } + else + switch(previous.state) + { + case DefaultHighlighter::Section::State::Finished: + { + if (text.contains(QRegExp(QLatin1String("\(\%i[0-9]+\)")))) + { + section.begin = 0; + section.end = text.size(); + section.state = DefaultHighlighter::Section::State::Continue; + } + else + { + section.begin = -1; + section.end = -1; + } + } + break; + + case DefaultHighlighter::Section::State::Continue: + { + section.begin = 0; + section.end = text.size(); + if (text.endsWith(QLatin1String("$")) || text.endsWith(QLatin1String(";"))) + section.state = DefaultHighlighter::Section::State::Finished; + } + break; + } + + return section; +} + QString MaximaHighlighter::nonSeparatingCharacters() const { return QLatin1String("%"); diff --git a/src/backends/octave/octavehighlighter.h b/src/backends/octave/octavehighlighter.h --- a/src/backends/octave/octavehighlighter.h +++ b/src/backends/octave/octavehighlighter.h @@ -40,6 +40,9 @@ void updateVariables(); void sessionStatusChanged(Cantor::Session::Status status); + private: + Cantor::DefaultHighlighter::Section findNextCodeSection(const QString& text, Cantor::DefaultHighlighter::Section previous) override; + private: Cantor::Session* m_session; QStringList m_variables; diff --git a/src/backends/octave/octavehighlighter.cpp b/src/backends/octave/octavehighlighter.cpp --- a/src/backends/octave/octavehighlighter.cpp +++ b/src/backends/octave/octavehighlighter.cpp @@ -93,3 +93,51 @@ removeRule(variable); rehighlight(); } + +DefaultHighlighter::Section OctaveHighlighter::findNextCodeSection(const QString& text, DefaultHighlighter::Section previous) +{ + DefaultHighlighter::Section section; + section.state = previous.state; + + if (previous.end >= text.size()) + { + section.begin = -1; + section.end = -1; + } + else + switch(previous.state) + { + case DefaultHighlighter::Section::State::Finished: + { + int begin = text.indexOf(QLatin1String("Example:"), std::max(previous.end, 0)); + if (begin == -1) + begin = text.indexOf(QLatin1String("for example:"), std::max(previous.end, 0)); + if (begin == -1) + begin = text.indexOf(QLatin1String(" See also:"), std::max(previous.end, 0)); + + if (begin != -1) + { + section.begin = begin; + section.end = text.size(); + section.state = DefaultHighlighter::Section::State::Continue; + } + else + { + section.begin = -1; + section.end = -1; + } + } + break; + + case DefaultHighlighter::Section::State::Continue: + { + section.begin = 0; + section.end = text.size(); + if (text.startsWith(QLatin1String(" See also:"))) + section.state = DefaultHighlighter::Section::State::Finished; + } + break; + } + + return section; +} diff --git a/src/backends/python/pythonhighlighter.cpp b/src/backends/python/pythonhighlighter.cpp --- a/src/backends/python/pythonhighlighter.cpp +++ b/src/backends/python/pythonhighlighter.cpp @@ -44,6 +44,10 @@ // Do some backend independent highlighting (brackets etc.) DefaultHighlighter::highlightBlock(text); + // TODO: Remove, if Python starts support partial highlighting + if (isPartialHighlighting()) + return; + const int IN_MULTILINE_COMMENT = 1; const int IN_SMALL_QUOTE_STRING = 2; const int IN_SINGLE_QUOTE_STRING = 4; diff --git a/src/backends/sage/sageexpression.cpp b/src/backends/sage/sageexpression.cpp --- a/src/backends/sage/sageexpression.cpp +++ b/src/backends/sage/sageexpression.cpp @@ -201,19 +201,9 @@ stripped.chop(1); if (m_isHelpRequest) - { - //Escape whitespace - stripped.replace( QLatin1Char(' '), QLatin1String(" ")); - - //make things quoted in `` `` bold - stripped.replace(QRegExp(QLatin1String("``([^`]*)``")), QLatin1String("\\1")); - result=new Cantor::HelpResult(stripped); - } else - { result=new Cantor::TextResult(stripped); - } if(isLatex) result->setFormat(Cantor::TextResult::LatexFormat); diff --git a/src/backends/sage/sagehighlighter.h b/src/backends/sage/sagehighlighter.h --- a/src/backends/sage/sagehighlighter.h +++ b/src/backends/sage/sagehighlighter.h @@ -35,6 +35,9 @@ public: explicit SageHighlighter( QObject* parent); ~SageHighlighter() override = default; + + private: + Cantor::DefaultHighlighter::Section findNextCodeSection(const QString& text, Cantor::DefaultHighlighter::Section previous) override; }; #endif /* _SAGEHIGHLIGHTER_H */ diff --git a/src/backends/sage/sagehighlighter.cpp b/src/backends/sage/sagehighlighter.cpp --- a/src/backends/sage/sagehighlighter.cpp +++ b/src/backends/sage/sagehighlighter.cpp @@ -21,6 +21,8 @@ #include "sagehighlighter.h" #include "sagekeywords.h" +using namespace Cantor; + SageHighlighter::SageHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent) { addRule(QRegExp(QLatin1String("[A-Za-z0-9_]+(?=\\()")), functionFormat()); @@ -31,3 +33,31 @@ addRule(QRegExp(QLatin1String("#[^\n]*")), commentFormat()); } + +DefaultHighlighter::Section SageHighlighter::findNextCodeSection(const QString& text, DefaultHighlighter::Section previous) +{ + DefaultHighlighter::Section section; + + if (previous.end >= text.size()) + { + section.begin = -1; + section.end = -1; + } + else + { + const int begin = text.indexOf(QLatin1String(" sage: "), std::max(previous.end, 0)); + if (begin != -1) + { + // size of " sage: " == 10 + section.begin = begin + 10; + section.end = text.size(); + } + else + { + section.begin = -1; + section.end = -1; + } + } + + return section; +} diff --git a/src/cantor_part.h b/src/cantor_part.h --- a/src/cantor_part.h +++ b/src/cantor_part.h @@ -37,6 +37,7 @@ class QAction; class KToggleAction; class QProgressDialog; +class QTextDocument; namespace Cantor{ class PanelPluginHandler; @@ -81,7 +82,7 @@ Q_SIGNALS: void setCaption(const QString& caption, const QIcon& icon); - void showHelp(const QString& help); + void showHelp(QTextDocument* doc); protected: /** diff --git a/src/cantor_part.cpp b/src/cantor_part.cpp --- a/src/cantor_part.cpp +++ b/src/cantor_part.cpp @@ -164,7 +164,7 @@ m_worksheetview=new WorksheetView(m_worksheet, widget); m_worksheetview->setEnabled(false); //disable input until the session has successfully logged in and emits the ready signal connect(m_worksheet, SIGNAL(modified()), this, SLOT(setModified())); - connect(m_worksheet, SIGNAL(showHelp(QString)), this, SIGNAL(showHelp(QString))); + connect(m_worksheet, SIGNAL(showHelp(QTextDocument*)), this, SIGNAL(showHelp(QTextDocument*))); connect(m_worksheet, SIGNAL(loaded()), this, SLOT(initialized())); connect(collection, SIGNAL(inserted(QAction*)), m_worksheet, SLOT(registerShortcut(QAction*))); layout->addWidget(m_worksheetview); diff --git a/src/lib/defaulthighlighter.h b/src/lib/defaulthighlighter.h --- a/src/lib/defaulthighlighter.h +++ b/src/lib/defaulthighlighter.h @@ -26,6 +26,7 @@ #include class QGraphicsTextItem; +class QTextDocument; namespace Cantor { @@ -48,14 +49,30 @@ { Q_OBJECT public: + /// Struct for code section with beginning, end and state used to determine next section position + struct Section { + /// Some code sections could continue beyond current text block, which we highlight in DefaultHighlighter::highlightBlock, so for solve this problem, we store section's state + enum State { + /// Section finished, i.e. ends in current text block + Finished, + /// Section unfinished, i.e. doesn't end in current text block, so we need handle this in the next block + Continue + }; + int begin; + int end; + State state; + }; + explicit DefaultHighlighter(QObject* parent); ~DefaultHighlighter() override; /** * Change the item being highlighted. */ void setTextItem(QGraphicsTextItem* item); + void highlightHelpDocument(QTextDocument* doc); + public Q_SLOTS: /** * Called when the cursor moved. Rehighlights accordingly. @@ -149,27 +166,52 @@ void removeRules(const QStringList& conditions); /** - * Highlight pairs added with addPair() + * Highlight pairs added with addPair() in all code sections * @sa addPair */ - void highlightPairs(const QString& text); + void highlightPairs(const QString& text, QVector
sections); /** - * Highlights words added with addRule() + * Highlights words added with addRule() in all code sections * @sa addRule, addRules */ - void highlightWords(const QString& text); + void highlightWords(const QString& text, QVector
sections); /** - * Highlights all matches from regular expressions added with addRule() + * Highlights all matches from regular expressions added with addRule() in all code sections * @sa addRule, addRules */ - void highlightRegExps(const QString& text); + void highlightRegExps(const QString& text, QVector
sections); /** * Returns a string that contains a regular expression that matches for characters thar are allowed inside * words for this backend. For example, maxima or scilab allow % at the beginning of variable names */ virtual QString nonSeparatingCharacters() const; + /** + * This function used for partial highlighting. + * Find a section of code in help result, which we should highlight. + * @param text text in which we are looking for section of code + * @param previous previous section of code + * Must be pure function + */ + virtual Section findNextCodeSection(const QString& text, Section previous); + + /** + * Return true, if we in partial highlighting mode + */ + bool isPartialHighlighting(); + + /** + * Setting partial highlight state + * @sa getState() + */ + void setState(DefaultHighlighter::Section::State state); + + /** + * Getting current partial highlight state + * @sa setState() + */ + DefaultHighlighter::Section::State getState(); private Q_SLOTS: void updateFormats(); diff --git a/src/lib/defaulthighlighter.cpp b/src/lib/defaulthighlighter.cpp --- a/src/lib/defaulthighlighter.cpp +++ b/src/lib/defaulthighlighter.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace Cantor; @@ -73,6 +74,9 @@ int lastBlockNumber; int lastPosition; bool suppressRuleChangedSignal; + bool partialHighlighting; + DefaultHighlighter::Section::State state; + // each two consecutive items build a pair QList pairs; @@ -88,6 +92,8 @@ d->lastBlockNumber=-1; d->lastPosition=-1; d->suppressRuleChangedSignal = false; + d->partialHighlighting = false; + d->state = DefaultHighlighter::Section::State::Finished; addPair(QLatin1Char('('), QLatin1Char(')')); addPair(QLatin1Char('['), QLatin1Char(']')); @@ -118,6 +124,65 @@ d->lastPosition = -1; } +void Cantor::DefaultHighlighter::highlightHelpDocument(QTextDocument* doc) +{ + if (!doc) + return; + + QList > oldFormats, newFormats; + + // TODO: do it here? Or create function for this? + // Store formats of previous document + QTextDocument* oldDocument = this->document(); + if (oldDocument) + { + for (QTextBlock b = oldDocument->firstBlock(); + b.isValid(); b = b.next()) + { + oldFormats.append(b.layout()->additionalFormats()); + } + } + + d->partialHighlighting = true; + d->state = DefaultHighlighter::Section::State::Finished; + setDocument(doc); + rehighlight(); + d->partialHighlighting = false; + + //Store formats of help document + for (QTextBlock b = this->document()->firstBlock(); + b.isValid(); b = b.next()) + { + newFormats.append(b.layout()->additionalFormats()); + } + + setDocument(oldDocument); + + //Restore formats in help document + QTextCursor cursor(doc); + cursor.beginEditBlock(); + for (QTextBlock b = doc->firstBlock(); + b.isValid(); b = b.next()) + { + b.layout()->setAdditionalFormats(newFormats.first()); + newFormats.pop_front(); + } + cursor.endEditBlock(); + + if (oldDocument) + { + QTextCursor cursor(oldDocument); + cursor.beginEditBlock(); + for (QTextBlock b = oldDocument->firstBlock(); + b.isValid(); b = b.next()) + { + b.layout()->setAdditionalFormats(oldFormats.first()); + oldFormats.pop_front(); + } + cursor.endEditBlock(); + } +} + bool DefaultHighlighter::skipHighlighting(const QString& text) { return text.isEmpty(); @@ -132,9 +197,24 @@ if (skipHighlighting(text)) return; - highlightPairs(text); - highlightWords(text); - highlightRegExps(text); + QVector sections; + if (d->partialHighlighting) + { + DefaultHighlighter::Section section = {-1, -1, d->state}; + do + { + section = findNextCodeSection(text, section); + sections.push_back(section); + } + while (section.end != -1); + d->state = section.state; + } + else + sections.push_back(DefaultHighlighter::Section{0, text.size(), DefaultHighlighter::Section::State::Finished}); + + highlightPairs(text, sections); + highlightWords(text, sections); + highlightRegExps(text, sections); } void DefaultHighlighter::addPair(QChar openSymbol, QChar closeSymbol) @@ -144,7 +224,7 @@ d->pairs << openSymbol << closeSymbol; } -void DefaultHighlighter::highlightPairs(const QString& text) +void DefaultHighlighter::highlightPairs(const QString& text, QVector
sections) { //qDebug() << text; const QTextCursor& cursor = d->cursor; @@ -158,102 +238,116 @@ QStack opened; - for (int i = 0; i < text.size(); ++i) { - int idx = d->pairs.indexOf(text[i]); - if (idx == -1) - continue; - if (idx % 2 == 0) { //opener of a pair - opened.push(PairOpener(i, idx)); - } else if (opened.isEmpty()) { //closer with no previous opener - setFormat(i, 1, errorFormat()); - } else if (opened.top().type == idx - 1) { //closer with matched opener - int openPos = opened.pop().position; - if (cursorPos != -1 && - (openPos == cursorPos || openPos == cursorPos - 1 || - i == cursorPos || i == cursorPos - 1)) { - setFormat(openPos, 1, matchingPairFormat()); - setFormat(i, 1, matchingPairFormat()); - } - } else { //closer with mismatching opener - int openPos = opened.pop().position; - setFormat(openPos, 1, mismatchingPairFormat()); - setFormat(i, 1, mismatchingPairFormat()); - } + for (const DefaultHighlighter::Section section :sections) + { + for (int i = section.begin; i < section.end; ++i) + { + int idx = d->pairs.indexOf(text[i]); + if (idx == -1) + continue; + if (idx % 2 == 0) //opener of a pair + opened.push(PairOpener(i, idx)); + else if (opened.isEmpty()) //closer with no previous opener + setFormat(i, 1, errorFormat()); + else if (opened.top().type == idx - 1) //closer with matched opener + { + int openPos = opened.pop().position; + if (cursorPos != -1 && + (openPos == cursorPos || openPos == cursorPos - 1 || i == cursorPos || i == cursorPos - 1)) + { + setFormat(openPos, 1, matchingPairFormat()); + setFormat(i, 1, matchingPairFormat()); + } + } + else //closer with mismatching opener + { + int openPos = opened.pop().position; + setFormat(openPos, 1, mismatchingPairFormat()); + setFormat(i, 1, mismatchingPairFormat()); + } + } } // handled unterminated pairs while (!opened.isEmpty()) { - int position = opened.pop().position; - setFormat(position, 1, errorFormat()); + int position = opened.pop().position; + setFormat(position, 1, errorFormat()); } } -void DefaultHighlighter::highlightWords(const QString& text) +void DefaultHighlighter::highlightWords(const QString& text, QVector
sections) { //qDebug() << "DefaultHighlighter::highlightWords"; - const QStringList& words = text.split(QRegExp(QLatin1String("\\b")), QString::SkipEmptyParts); - int count; - int pos = 0; - - const int n = words.size(); - for (int i = 0; i < n; ++i) + for (const DefaultHighlighter::Section section :sections) { - count = words[i].size(); - QString word = words[i]; - - //kind of a HACK: - //look at previous words, if they end with allowed characters, - //prepend them to the current word. This allows for example - //to highlight words that start with a "Non-word"-character - //e.g. %pi in the scilab backend. - //qDebug() << "nonSeparatingCharacters().isNull(): " << nonSeparatingCharacters().isNull(); - if(!nonSeparatingCharacters().isNull()) + const QStringList& words = text.mid(section.begin, section.end - section.begin).split(QRegExp(QLatin1String("\\b")), QString::SkipEmptyParts); + int count; + int pos = section.begin; + + const int n = words.size(); + for (int i = 0; i < n; ++i) { - for(int j = i - 1; j >= 0; j--) + count = words[i].size(); + QString word = words[i]; + + //kind of a HACK: + //look at previous words, if they end with allowed characters, + //prepend them to the current word. This allows for example + //to highlight words that start with a "Non-word"-character + //e.g. %pi in the scilab backend. + //qDebug() << "nonSeparatingCharacters().isNull(): " << nonSeparatingCharacters().isNull(); + if(!nonSeparatingCharacters().isNull()) { - //qDebug() << "j: " << j << "w: " << words[j]; - const QString& w = words[j]; - const QString exp = QString::fromLatin1("(%1)*$").arg(nonSeparatingCharacters()); - //qDebug() << "exp: " << exp; - int idx = w.indexOf(QRegExp(exp)); - const QString& s = w.mid(idx); - //qDebug() << "s: " << s; - - if(s.size() > 0) + for(int j = i - 1; j >= 0; j--) { - pos -= s.size(); - count += s.size(); - word = s + word; - } else{ - break; + //qDebug() << "j: " << j << "w: " << words[j]; + const QString& w = words[j]; + const QString exp = QString::fromLatin1("(%1)*$").arg(nonSeparatingCharacters()); + //qDebug() << "exp: " << exp; + int idx = w.indexOf(QRegExp(exp)); + const QString& s = w.mid(idx); + //qDebug() << "s: " << s; + + if(s.size() > 0) + { + pos -= s.size(); + count += s.size(); + word = s + word; + } else{ + break; + } } } - } - word = word.trimmed(); + word = word.trimmed(); - //qDebug() << "highlighing: " << word; + // qDebug() << "highlighing: " << word; - if (d->wordRules.contains(word)) - { - setFormat(pos, count, d->wordRules[word]); - } + if (d->wordRules.contains(word)) + { + setFormat(pos, count, d->wordRules[word]); + } - pos += count; + pos += count; + } } } -void DefaultHighlighter::highlightRegExps(const QString& text) +void DefaultHighlighter::highlightRegExps(const QString& text, QVector
sections) { - foreach (const HighlightingRule& rule, d->regExpRules) + for (const DefaultHighlighter::Section section :sections) { - int index = rule.regExp.indexIn(text); - while (index >= 0) { - int length = rule.regExp.matchedLength(); - setFormat(index, length, rule.format); - index = rule.regExp.indexIn(text, index + length); + const QString subtext = text.mid(section.begin, section.end - section.begin); + foreach (const HighlightingRule& rule, d->regExpRules) + { + int index = rule.regExp.indexIn(subtext); + while (index >= 0) { + int length = rule.regExp.matchedLength(); + setFormat(index+section.begin, length, rule.format); + index = rule.regExp.indexIn(subtext, index + length); + } } } } @@ -457,4 +551,24 @@ return QString(); } +DefaultHighlighter::Section DefaultHighlighter::findNextCodeSection(const QString& text, DefaultHighlighter::Section previous) +{ + Q_UNUSED(text); + Q_UNUSED(previous); + return DefaultHighlighter::Section{-1, -1, DefaultHighlighter::Section::State::Finished}; +} + +bool DefaultHighlighter::isPartialHighlighting() +{ + return d->partialHighlighting; +} +DefaultHighlighter::Section::State DefaultHighlighter::getState() +{ + return d->state; +} + +void DefaultHighlighter::setState(DefaultHighlighter::Section::State state) +{ + d->state = state; +} diff --git a/src/lib/helpresult.cpp b/src/lib/helpresult.cpp --- a/src/lib/helpresult.cpp +++ b/src/lib/helpresult.cpp @@ -19,12 +19,12 @@ */ #include "helpresult.h" +#include using namespace Cantor; -HelpResult::HelpResult(const QString& text) : TextResult(text) +HelpResult::HelpResult(const QString& text) : TextResult(text, true) { - } int HelpResult::type() diff --git a/src/lib/textresult.h b/src/lib/textresult.h --- a/src/lib/textresult.h +++ b/src/lib/textresult.h @@ -34,8 +34,8 @@ public: enum { Type=1 }; enum Format { PlainTextFormat, LatexFormat}; - TextResult(const QString& text); - TextResult(const QString& text, const QString& plain); + TextResult(const QString& text, bool noTrim = false); + TextResult(const QString& text, const QString& plain, bool noTrim = false); ~TextResult() override; QString toHtml() override; diff --git a/src/lib/textresult.cpp b/src/lib/textresult.cpp --- a/src/lib/textresult.cpp +++ b/src/lib/textresult.cpp @@ -39,16 +39,16 @@ TextResult::Format format; }; -TextResult::TextResult(const QString& data) : d(new TextResultPrivate) +TextResult::TextResult(const QString& data, bool noTrim) : d(new TextResultPrivate) { - d->data=data.trimmed(); - d->plain=data.trimmed(); + d->data = noTrim ? data : data.trimmed(); + d->plain = noTrim ? data : data.trimmed(); } -TextResult::TextResult(const QString& data, const QString& plain) : d(new TextResultPrivate) +TextResult::TextResult(const QString& data, const QString& plain, bool noTrim) : d(new TextResultPrivate) { - d->data=data.trimmed(); - d->plain=plain.trimmed(); + d->data = noTrim ? data : data.trimmed(); + d->plain = noTrim ? data : plain.trimmed(); } diff --git a/src/panelplugins/helppanel/helppanelplugin.h b/src/panelplugins/helppanel/helppanelplugin.h --- a/src/panelplugins/helppanel/helppanelplugin.h +++ b/src/panelplugins/helppanel/helppanelplugin.h @@ -25,6 +25,7 @@ class KTextEdit; +class QTextDocument; class HelpPanelPlugin : public Cantor::PanelPlugin { @@ -36,8 +37,7 @@ QWidget* widget() override; public Q_SLOTS: - void setHelpHtml(const QString& help); - void showHelp(const QString& help); + void setHelp(QTextDocument*); private: QPointer m_edit; diff --git a/src/panelplugins/helppanel/helppanelplugin.cpp b/src/panelplugins/helppanel/helppanelplugin.cpp --- a/src/panelplugins/helppanel/helppanelplugin.cpp +++ b/src/panelplugins/helppanel/helppanelplugin.cpp @@ -25,6 +25,8 @@ #include #include "cantor_macros.h" +#include + HelpPanelPlugin::HelpPanelPlugin(QObject* parent, QList args) : Cantor::PanelPlugin(parent) { Q_UNUSED(args); @@ -41,34 +43,37 @@ if(m_edit==nullptr) { m_edit=new KTextEdit(parentWidget()); - setHelpHtml(i18n("

Cantor

The KDE way to do Mathematics")); + + QTextDocument* doc = new QTextDocument; + doc->setHtml(i18n("

Cantor

The KDE way to do Mathematics")); + setHelp(doc); + m_edit->setTextInteractionFlags(Qt::TextBrowserInteraction); - connect(parent()->parent(), SIGNAL(showHelp(QString)), this, SLOT(setHelpHtml(QString))); - connect(parent()->parent(), SIGNAL(showHelp(QString)), this, SIGNAL(visibilityRequested())); + connect(parent()->parent(), SIGNAL(showHelp(QTextDocument*)), this, SLOT(setHelp(QTextDocument*))); + connect(parent()->parent(), SIGNAL(showHelp(QTextDocument*)), this, SIGNAL(visibilityRequested())); } return m_edit; } -void HelpPanelPlugin::setHelpHtml(const QString& help) +void HelpPanelPlugin::setHelp(QTextDocument* doc) { if(!m_edit) + { + doc->deleteLater(); return; + } - m_edit->setHtml(help); + if (m_edit->document()) + m_edit->document()->deleteLater(); + + m_edit->setDocument(doc); m_edit->selectAll(); m_edit->setFontFamily(QLatin1String("Monospace")); m_edit->moveCursor(QTextCursor::Start); } -void HelpPanelPlugin::showHelp(const QString& help) -{ - if(m_edit) - m_edit->setHtml(help); -} - - K_PLUGIN_FACTORY_WITH_JSON(helppanelplugin, "helppanelplugin.json", registerPlugin();) #include "helppanelplugin.moc" diff --git a/src/worksheet.h b/src/worksheet.h --- a/src/worksheet.h +++ b/src/worksheet.h @@ -205,7 +205,7 @@ Q_SIGNALS: void modified(); void loaded(); - void showHelp(const QString&); + void showHelp(QTextDocument*); void updatePrompt(); void undoAvailable(bool); void redoAvailable(bool); diff --git a/src/worksheet.cpp b/src/worksheet.cpp --- a/src/worksheet.cpp +++ b/src/worksheet.cpp @@ -1165,14 +1165,31 @@ { if(result && result->type()==Cantor::HelpResult::Type) { - QString help = result->toHtml(); + QString help = static_cast(result)->plain(); + //TODO: Do we need this replacing? //Do some basic LaTeX replacing - help.replace(QRegExp(QLatin1String("\\\\code\\{([^\\}]*)\\}")), QLatin1String("\\1")); - help.replace(QRegExp(QLatin1String("\\$([^\\$])\\$")), QLatin1String("\\1")); + // help.replace(QRegExp(QLatin1String("\\\\code\\{([^\\}]*)\\}")), QLatin1String("\\1")); + // help.replace(QRegExp(QLatin1String("\\$([^\\$])\\$")), QLatin1String("\\1")); - emit showHelp(help); - //TODO: break after the first help result found, not clear yet how to handle multiple requests for help within one single command (e.g. ??ev;??int). + QTextDocument* doc = new QTextDocument(); + doc->setPlainText(help); + + // Use new highlighter, because m_highlighter store some worksheet-depend information, + // like variable list, user defined list, etc. + // TODO: For example, MaximaHighlighter get user variable list from parent session, so how handle it? + // create new session for help request? + // or create empty session in Worksheet constructor? + // or pass some flag (withoutState, for example) in syntaxHighlighter function? + // TODO: add variable parsing for highlighting variables defined inside help? + QSyntaxHighlighter* highlighter = m_session->syntaxHighlighter(this); + Cantor::DefaultHighlighter* hl = qobject_cast(highlighter); + if (hl) + hl->highlightHelpDocument(doc); + highlighter->deleteLater(); + + emit showHelp(doc); + break; } }