Warning, file /sdk/lokalize/src/syntaxhighlighter.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002   This file is part of Lokalize
0003 
0004   SPDX-FileCopyrightText: 2007-2009 Nick Shaforostoff <shafff@ukr.net>
0005   SPDX-FileCopyrightText: 2018-2019 Simon Depiets <sdepiets@gmail.com>
0006 
0007   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0008 */
0009 
0010 #include "syntaxhighlighter.h"
0011 
0012 #include "lokalize_debug.h"
0013 
0014 #include "project.h"
0015 #include "prefs_lokalize.h"
0016 #include "prefs.h"
0017 
0018 #include <kcolorscheme.h>
0019 
0020 #include <QTextEdit>
0021 #include <QApplication>
0022 #include <QStringBuilder>
0023 
0024 #define STATE_NORMAL 0
0025 #define STATE_TAG 1
0026 
0027 
0028 
0029 #define NUM_OF_RULES 5
0030 
0031 SyntaxHighlighter::SyntaxHighlighter(QTextEdit *parent)
0032     : Sonnet::Highlighter(parent)
0033     , tagBrush(KColorScheme::View, KColorScheme::VisitedText)
0034     , m_approved(true)
0035 //     , fromDocbook(docbook)
0036 {
0037     highlightingRules.reserve(NUM_OF_RULES);
0038     HighlightingRule rule;
0039     //rule.format.setFontItalic(true);
0040 //     tagFormat.setForeground(tagBrush.brush(QApplication::palette()));
0041     setAutomatic(false);
0042 
0043     tagFormat.setForeground(tagBrush.brush(QApplication::palette()));
0044 
0045     //QTextCharFormat format;
0046     //tagFormat.setForeground(Qt::darkBlue);
0047 //     if (!docbook) //support multiline tags
0048 //     {
0049 //         rule.format = tagFormat;
0050 //         rule.pattern = QRegExp("<.+>");
0051 //         rule.pattern.setMinimal(true);
0052 //         highlightingRules.append(rule);
0053 //     }
0054 
0055     //entity
0056     rule.format.setForeground(Qt::darkMagenta);
0057     rule.pattern = QRegExp(QStringLiteral("(&[A-Za-z_:][A-Za-z0-9_\\.:-]*;)"));
0058     highlightingRules.append(rule);
0059 
0060     QString accel = Project::instance()->accel();
0061     if (!accel.isEmpty()) {
0062         rule.format.setForeground(Qt::darkMagenta);
0063         rule.pattern = QRegExp(accel);
0064         highlightingRules.append(rule);
0065     }
0066 
0067     //\n \t \"
0068     rule.format.setForeground(Qt::darkGreen);
0069     rule.pattern = QRegExp(QStringLiteral("(\\\\[abfnrtv'\?\\\\])|(\\\\\\d+)|(\\\\x[\\dabcdef]+)"));
0070     highlightingRules.append(rule);
0071 
0072     //spaces
0073     settingsChanged();
0074     connect(SettingsController::instance(), &SettingsController::generalSettingsChanged, this, &SyntaxHighlighter::settingsChanged);
0075 
0076 }
0077 
0078 void SyntaxHighlighter::settingsChanged()
0079 {
0080     QRegExp re(" +$|^ +|.?" + QChar(0x0000AD) + ".?"); //soft hyphen
0081     if (Settings::highlightSpaces() && highlightingRules.last().pattern != re) {
0082         HighlightingRule rule;
0083         rule.format.clearForeground();
0084 
0085         KColorScheme colorScheme(QPalette::Normal);
0086         //nbsp
0087         //rule.format.setBackground(colorScheme.background(KColorScheme::NegativeBackground));
0088         rule.format.setBackground(colorScheme.foreground(KColorScheme::InactiveText));
0089         rule.format.setFontLetterSpacing(200);
0090 
0091         rule.pattern = QRegExp(QChar(0x00a0U), Qt::CaseSensitive, QRegExp::FixedString);
0092         highlightingRules.append(rule);
0093 
0094         //usual spaces at the end
0095         rule.format.setFontLetterSpacing(100);
0096         rule.format.setBackground(colorScheme.background(KColorScheme::ActiveBackground));
0097         rule.pattern = re;
0098         highlightingRules.append(rule);
0099         rehighlight();
0100     } else if (!Settings::highlightSpaces() && highlightingRules.last().pattern == re) {
0101         highlightingRules.resize(highlightingRules.size() - 2);
0102         rehighlight();
0103     }
0104 }
0105 
0106 /*
0107 void SyntaxHighlighter::setFuzzyState(bool fuzzy)
0108 {
0109     return;
0110     int i=NUM_OF_RULES;
0111     while(--i>=0)
0112         highlightingRules[i].format.setFontItalic(fuzzy);
0113 
0114     tagFormat.setFontItalic(fuzzy);
0115 }*/
0116 
0117 void SyntaxHighlighter::highlightBlock(const QString &text)
0118 {
0119     int currentBlockState = STATE_NORMAL;
0120     QTextCharFormat f;
0121     f.setFontItalic(!m_approved);
0122     setFormat(0, text.length(), f);
0123 
0124     tagFormat.setFontItalic(!m_approved);
0125     //if (fromDocbook)
0126     {
0127         int startIndex = STATE_NORMAL;
0128         if (previousBlockState() != STATE_TAG)
0129             startIndex = text.indexOf('<');
0130 
0131         while (startIndex >= 0) {
0132             int endIndex = text.indexOf('>', startIndex);
0133             int commentLength;
0134             if (endIndex == -1) {
0135                 currentBlockState = STATE_TAG;
0136                 commentLength = text.length() - startIndex;
0137             } else {
0138                 commentLength = endIndex - startIndex
0139                                 + 1/*+ commentEndExpression.matchedLength()*/;
0140             }
0141             setFormat(startIndex, commentLength, tagFormat);
0142             startIndex = text.indexOf('<', startIndex + commentLength);
0143         }
0144     }
0145 
0146     for (const HighlightingRule &rule : qAsConst(highlightingRules)) {
0147         QRegExp expression(rule.pattern);
0148         int index = expression.indexIn(text);
0149         while (index >= 0) {
0150             int length = expression.matchedLength();
0151             QTextCharFormat f = rule.format;
0152             f.setFontItalic(!m_approved);
0153             setFormat(index, length, f);
0154             index = expression.indexIn(text, index + length);
0155         }
0156     }
0157 
0158     if (spellCheckerFound())
0159         Sonnet::Highlighter::highlightBlock(text); // Resets current block state
0160     setCurrentBlockState(currentBlockState);
0161 }
0162 
0163 #if 0
0164 void SyntaxHighlighter::setFormatRetainingUnderlines(int start, int count, QTextCharFormat f)
0165 {
0166     QVector<bool> underLines(count);
0167     for (int i = 0; i < count; ++i)
0168         underLines[i] = format(start + i).fontUnderline();
0169 
0170     setFormat(start, count, f);
0171 
0172     f.setFontUnderline(true);
0173     int prevStart = -1;
0174     bool isPrevUnderLined = false;
0175     for (int i = 0; i < count; ++i) {
0176         if (!underLines.at(i) && prevStart != -1)
0177             setFormat(start + isPrevUnderLined, i - prevStart, f);
0178         else if (underLines.at(i) && !isPrevUnderLined)
0179             prevStart = i;
0180 
0181         isPrevUnderLined = underLines.at(i);
0182     }
0183 }
0184 #endif
0185 
0186 void SyntaxHighlighter::setMisspelled(int start, int count)
0187 {
0188     const Project& project = *Project::instance();
0189 
0190     const QString text = currentBlock().text();
0191     QString word = text.mid(start, count);
0192     if (m_sourceString.contains(word)
0193         && project.targetLangCode().leftRef(2) != project.sourceLangCode().leftRef(2))
0194         return;
0195 
0196     const QString accel = project.accel();
0197 
0198     if (!isWordMisspelled(word.remove(accel)))
0199         return;
0200     count = word.length(); //safety
0201 
0202     bool smthPreceeding = (start > 0) &&
0203                           (accel.endsWith(text.at(start - 1))
0204                            || text.at(start - 1) == QChar(0x0000AD) //soft hyphen
0205                           );
0206 
0207     //HACK. Needs Sonnet API redesign (KDE 5)
0208     if (smthPreceeding) {
0209         qCWarning(LOKALIZE_LOG) << "ampersand is in the way. word len:" << count;
0210         int realStart = text.lastIndexOf(QRegExp("\\b"), start - 2);
0211         if (realStart == -1)
0212             realStart = 0;
0213         QString t = text.mid(realStart, count + start - realStart);
0214         t.remove(accel);
0215         t.remove(QChar(0x0000AD));
0216         if (!isWordMisspelled(t))
0217             return;
0218     }
0219 
0220     bool smthAfter = (start + count + 1 < text.size()) &&
0221                      (accel.startsWith(text.at(start + count))
0222                       || text.at(start + count) == QChar(0x0000AD) //soft hyphen
0223                      );
0224     if (smthAfter) {
0225         qCWarning(LOKALIZE_LOG) << "smthAfter. ampersand is in the way. word len:" << count;
0226         int realEnd = text.indexOf(QRegExp(QStringLiteral("\\b")), start + count + 2);
0227         if (realEnd == -1)
0228             realEnd = text.size();
0229         QString t = text.mid(start, realEnd - start);
0230         t.remove(accel);
0231         t.remove(QChar(0x0000AD));
0232         if (!isWordMisspelled(t))
0233             return;
0234     }
0235 
0236     if (count && format(start) == tagFormat)
0237         return;
0238 
0239     for (int i = 0; i < count; ++i) {
0240         QTextCharFormat f(format(start + i));
0241         f.setFontUnderline(true);
0242         f.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline);
0243         f.setUnderlineColor(Qt::red);
0244         setFormat(start + i, 1, f);
0245     }
0246 }
0247 
0248 void SyntaxHighlighter::unsetMisspelled(int start, int count)
0249 {
0250     for (int i = 0; i < count; ++i) {
0251         QTextCharFormat f(format(start + i));
0252         f.setFontUnderline(false);
0253         setFormat(start + i, 1, f);
0254     }
0255 }
0256 
0257 
0258 
0259