diff --git a/doc/downloads/index.js b/doc/downloads/index.js index 8ff5ca6aa5..f2f6b0a492 100644 --- a/doc/downloads/index.js +++ b/doc/downloads/index.js @@ -37,9 +37,7 @@ var jsonData = { "versions": [ } ], "changes": [ - { "change": "General: can now reload a file using Ctrl+R (see issue #2286)." }, - { "change": "Simulation Experiment view: can now customise the font size of the different bits of text in a graph panel (see issue #2271). Fixed a small GUI glitch on macOS (see issue #2277). Fixed an issue with reloading a SED-ML file / COMBINE archive potentially crashing OpenCOR (see issue #2290)." }, - { "change": "Third-party libraries: upgraded LLVM+Clang to version 9.0.1 (see issue #2269). Upgraded the SUNDIALS library to version 5.1.0 (see issue #2272). Upgraded libgit2 to version 0.28.4 (see issue #2280). Upgraded Mesa to version 19.3.2 (see issue #2281)." } + { "change": "CellML Text view: greatly reduced the time and memory required to do syntax highlighting (see issue #2284)." } ] }, { "major": 0, "minor": 5, "patch": 0, "day": 15, "month": 10, "year": 2016, "type": 0, "license": 1, diff --git a/doc/downloads/previousSnapshots.js b/doc/downloads/previousSnapshots.js index c00d4df4e9..e0ec998dbe 100644 --- a/doc/downloads/previousSnapshots.js +++ b/doc/downloads/previousSnapshots.js @@ -1,4 +1,28 @@ var jsonData = { "versions": [ + { "major": 0, "minor": 0, "patch": 0, "day": 20, "month": 1, "year": 2020, "type": 2, "license": 2, + "platforms": [ + { "name": "Windows", "supported": "Windows 10", + "files": [ + { "extension": ".exe" }, { "extension": ".zip" } + ] + }, + { "name": "Linux", "supported": "Ubuntu 18.04 LTS (Bionic Beaver) and later", + "files": [ + { "extension": ".tar.gz" } + ] + }, + { "name": "macOS", "supported": "macOS 10.12 (Sierra) and later", + "files": [ + { "extension": ".pkg" }, { "extension": ".zip" } + ] + } + ], + "changes": [ + { "change": "General: can now reload a file using Ctrl+R (see issue #2286)." }, + { "change": "Simulation Experiment view: can now customise the font size of the different bits of text in a graph panel (see issue #2271). Fixed a small GUI glitch on macOS (see issue #2277). Fixed an issue with reloading a SED-ML file / COMBINE archive potentially crashing OpenCOR (see issue #2290)." }, + { "change": "Third-party libraries: upgraded LLVM+Clang to version 9.0.1 (see issue #2269). Upgraded the SUNDIALS library to version 5.1.0 (see issue #2272). Upgraded libgit2 to version 0.28.4 (see issue #2280). Upgraded Mesa to version 19.3.2 (see issue #2281)." } + ] + }, { "major": 0, "minor": 0, "patch": 0, "day": 13, "month": 12, "year": 2019, "type": 2, "license": 2, "platforms": [ { "name": "Windows", "supported": "Windows 7 and later", diff --git a/doc/whatIsNew.js b/doc/whatIsNew.js index 281a6a1e86..8f15ca16db 100644 --- a/doc/whatIsNew.js +++ b/doc/whatIsNew.js @@ -26,6 +26,7 @@ var jsonData = { "versions": [ "entries": [ { "type": "subCategory", "name": "CellML Text view", "entries": [ + { "type": "improved", "description": "Rendering of the GUI." }, { "type": "fixed", "description": "Reading a CellML file that has no XML processing instruction." } ] } diff --git a/src/plugins/editing/CellMLTextView/src/cellmltextviewlexer.cpp b/src/plugins/editing/CellMLTextView/src/cellmltextviewlexer.cpp index 9a9a10fbaf..408df7d392 100644 --- a/src/plugins/editing/CellMLTextView/src/cellmltextviewlexer.cpp +++ b/src/plugins/editing/CellMLTextView/src/cellmltextviewlexer.cpp @@ -130,50 +130,72 @@ QFont CellmlTextViewLexer::font(int pStyle) const //============================================================================== -void CellmlTextViewLexer::styleText(int pBytesStart, int pBytesEnd) +static const int StyleChunk = 32768; + +//============================================================================== + +void CellmlTextViewLexer::styleText(int pStart, int pEnd) { +#ifdef QT_DEBUG // Make sure that we have an editor if (editor() == nullptr) { - return; + qFatal("FATAL ERROR | %s:%d: styling can only be performed with an editor.", __FILE__, __LINE__); } +#endif - // Retrieve the text to style + // Keep track of some information - auto data = new char[pBytesEnd-pBytesStart+1] {}; + mFullText = editor()->text().toUtf8(); + mEolString = qobject_cast(editor())->eolString(); - editor()->SendScintilla(QsciScintilla::SCI_GETTEXTRANGE, - pBytesStart, pBytesEnd, data); + // Style the text in small chunks (to reduce memory usage, which can quickly + // become ridiculous the first time we are styling a big CellML file) - QString text = QString(data); + int start = pStart; + int end; - delete[] data; + forever { + // Use a default style for our chunk of text + // Note: this is so that validString() can work properly... - // Use a default style for our text - // Note: this is so that validString() can work properly... + end = qMin(start+StyleChunk, pEnd); - startStyling(pBytesStart); - setStyling(pBytesEnd-pBytesStart, int(Style::Default)); + applyStyle(start, end, Style::Default); - // Effectively style our text + // Retrieve the chunk of text to style and style it - mFullText = editor()->text(); - mFullTextUtf8 = mFullText.toUtf8(); + auto data = new char[end-start+1] {}; - mEolString = qobject_cast(editor())->eolString(); + editor()->SendScintilla(QsciScintilla::SCI_GETTEXTRANGE, + start, end, data); - styleText(pBytesStart, pBytesEnd, text, false); + styleText(start, end, data, false); + + delete[] data; #ifdef QT_DEBUG - // Make sure that the end position of the last bit of text that we styled is - // pBytesEnd - // Note: we need to ensure that it is the case, so that validString() can - // take advantage of the style of a given character in the editor... + // Make sure that the end position of the last bit of chunk of text that + // we styled is end + // Note: we need to ensure that it is the case, so that validString() + // can take advantage of the style of a given character in the + // editor... + + if (editor()->SendScintilla(QsciScintillaBase::SCI_GETENDSTYLED) != end) { + qFatal("FATAL ERROR | %s:%d: the styling of the text must be incremental.", __FILE__, __LINE__); + } +#endif + + // Was it our last chunk of text? + + if (end == pEnd) { + break; + } + + // Get ready for the next chunk of text - if (editor()->SendScintilla(QsciScintillaBase::SCI_GETENDSTYLED) != pBytesEnd) { - qFatal("FATAL ERROR | %s:%d: the styling of the text must be incremental.", __FILE__, __LINE__); + start = end; } -#endif // Let people know that we are done with our styling @@ -205,66 +227,92 @@ static const int StringLength = 1; //============================================================================== -void CellmlTextViewLexer::styleText(int pBytesStart, int pBytesEnd, - const QString &pText, bool pParameterBlock) +void CellmlTextViewLexer::applyStyle(int pStart, int pEnd, Style pStyle) +{ + // Apply the given style to the given chunk of text + + startStyling(pStart); + setStyling(pEnd-pStart, int(pStyle)); +} + +//============================================================================== + +void CellmlTextViewLexer::styleText(int pStart, int pEnd, + const QByteArray &pText, + bool pParameterBlock) { // Make sure that we are given some text to style - if (pBytesStart == pBytesEnd) { + if (pStart == pEnd) { return; } // Check whether a /* XXX */ comment or a parameter block started before or // at the beginning of the given text - int start = fullTextPosition(pBytesStart); - int multilineCommentStartPosition = findString(StartMultilineCommentString, start, int(Style::MultilineComment), false); - int parameterBlockStartPosition = findString(StartParameterBlockString, start, int(Style::ParameterBlock), false); + int multilineCommentStartPosition = findString(StartMultilineCommentString, pStart, Style::MultilineComment, false); + int parameterBlockStartPosition = findString(StartParameterBlockString, pStart, Style::ParameterBlock, false); - multilineCommentStartPosition = (multilineCommentStartPosition == -1)?INT_MAX:multilineCommentStartPosition; - parameterBlockStartPosition = (parameterBlockStartPosition == -1)?INT_MAX:parameterBlockStartPosition; + multilineCommentStartPosition = (multilineCommentStartPosition == -1)? + INT_MAX: + multilineCommentStartPosition; + parameterBlockStartPosition = (parameterBlockStartPosition == -1)? + INT_MAX: + parameterBlockStartPosition; - int multilineCommentEndPosition = (multilineCommentStartPosition == INT_MAX)?INT_MAX:findString(EndMultilineCommentString, multilineCommentStartPosition, int(Style::MultilineComment)); - int parameterBlockEndPosition = (parameterBlockStartPosition == INT_MAX)?INT_MAX:findString(EndParameterBlockString, parameterBlockStartPosition, int(Style::ParameterBlock)); + int multilineCommentEndPosition = (multilineCommentStartPosition == INT_MAX)? + INT_MAX: + findString(EndMultilineCommentString, multilineCommentStartPosition, Style::MultilineComment); - multilineCommentEndPosition = (multilineCommentEndPosition == -1)?INT_MAX:multilineCommentEndPosition; - parameterBlockEndPosition = (parameterBlockEndPosition == -1)?INT_MAX:parameterBlockEndPosition; + multilineCommentEndPosition = (multilineCommentEndPosition == -1)? + INT_MAX: + multilineCommentEndPosition; if ( (multilineCommentStartPosition != INT_MAX) && ( (parameterBlockStartPosition == INT_MAX) || ( (multilineCommentStartPosition > parameterBlockStartPosition) - && (multilineCommentEndPosition > start)))) { + && (multilineCommentEndPosition > pStart)))) { // There is a previous /* XXX */ comment to style styleTextPreviousMultilineComment(multilineCommentStartPosition, - pBytesStart, pBytesEnd, + pStart, pEnd, pText, pParameterBlock); - } else if ( (parameterBlockStartPosition != INT_MAX) - && ( (multilineCommentStartPosition == INT_MAX) - || ( (parameterBlockStartPosition > multilineCommentStartPosition) - && (parameterBlockEndPosition > start)))) { - // There is a previous parameter block to style - - styleTextPreviousParameterBlock(parameterBlockStartPosition, - pBytesStart, pBytesEnd, - pText, pParameterBlock); } else { - // Style the current text + int parameterBlockEndPosition = (parameterBlockStartPosition == INT_MAX)? + INT_MAX: + findString(EndParameterBlockString, parameterBlockStartPosition, Style::ParameterBlock); + + parameterBlockEndPosition = (parameterBlockEndPosition == -1)? + INT_MAX: + parameterBlockEndPosition; + + if ( (parameterBlockStartPosition != INT_MAX) + && ( (multilineCommentStartPosition == INT_MAX) + || ( (parameterBlockStartPosition > multilineCommentStartPosition) + && (parameterBlockEndPosition > pStart)))) { + // There is a previous parameter block to style + + styleTextPreviousParameterBlock(parameterBlockStartPosition, + pStart, pEnd, + pText, pParameterBlock); + } else { + // Style the current text - styleTextCurrent(pBytesStart, pBytesEnd, pText, - (parameterBlockStartPosition < start) && (parameterBlockEndPosition > start)); + styleTextCurrent(pStart, pEnd, pText, + (parameterBlockStartPosition < pStart) && (parameterBlockEndPosition > pStart)); + } } } //============================================================================== -void CellmlTextViewLexer::styleTextCurrent(int pBytesStart, int pBytesEnd, - const QString &pText, +void CellmlTextViewLexer::styleTextCurrent(int pStart, int pEnd, + const QByteArray &pText, bool pParameterBlock) { // Make sure that we are given some text to style - if (pBytesStart == pBytesEnd) { + if (pStart == pEnd) { return; } @@ -274,28 +322,30 @@ void CellmlTextViewLexer::styleTextCurrent(int pBytesStart, int pBytesEnd, int stringPosition = pText.indexOf(StringString); int singleLineCommentPosition = pText.indexOf(SingleLineCommentString); int multilineCommentStartPosition = pText.indexOf(StartMultilineCommentString); - int parameterBlockStartPosition = pText.indexOf(StartParameterBlockString); - stringPosition = (stringPosition == -1)?INT_MAX:stringPosition; - singleLineCommentPosition = (singleLineCommentPosition == -1)?INT_MAX:singleLineCommentPosition; - multilineCommentStartPosition = (multilineCommentStartPosition == -1)?INT_MAX:multilineCommentStartPosition; - parameterBlockStartPosition = (parameterBlockStartPosition == -1)?INT_MAX:parameterBlockStartPosition; + stringPosition = (stringPosition == -1)? + INT_MAX: + stringPosition; + singleLineCommentPosition = (singleLineCommentPosition == -1)? + INT_MAX: + singleLineCommentPosition; + multilineCommentStartPosition = (multilineCommentStartPosition == -1)? + INT_MAX: + multilineCommentStartPosition; if ( (stringPosition != INT_MAX) && (stringPosition < singleLineCommentPosition) && (stringPosition < multilineCommentStartPosition)) { // There is a string to style - styleTextString(stringPosition, pBytesStart, pBytesEnd, pText, pParameterBlock); + styleTextString(stringPosition, pStart, pEnd, pText, pParameterBlock); } else if ( (singleLineCommentPosition != INT_MAX) && (singleLineCommentPosition < stringPosition) && (singleLineCommentPosition < multilineCommentStartPosition)) { // There is a // comment to style, so first style everything that is // before it - int singleLineCommentBytesPosition = fullTextBytesPosition(fullTextPosition(pBytesStart)+singleLineCommentPosition); - - styleTextCurrent(pBytesStart, singleLineCommentBytesPosition, + styleTextCurrent(pStart, pStart+singleLineCommentPosition, pText.left(singleLineCommentPosition), pParameterBlock); @@ -303,18 +353,14 @@ void CellmlTextViewLexer::styleTextCurrent(int pBytesStart, int pBytesEnd, // line, if any int eolPosition = pText.indexOf(mEolString, singleLineCommentPosition+SingleLineCommentLength); - int eolBytesPosition = (eolPosition == -1)?-1:textBytesPosition(pText, eolPosition); - int end = (eolBytesPosition == -1)?pBytesEnd:pBytesStart+eolBytesPosition+mEolString.length(); + int end = (eolPosition == -1)?pEnd:pStart+eolPosition+mEolString.length(); - startStyling(singleLineCommentBytesPosition); - setStyling(end-singleLineCommentBytesPosition, int(Style::SingleLineComment)); + applyStyle(pStart+singleLineCommentPosition, end, Style::SingleLineComment); // Now, style everything that is after the // comment, if anything - if (eolBytesPosition != -1) { - styleText(end, pBytesEnd, - pText.right(fullTextLength(end, pBytesEnd)), - pParameterBlock); + if (eolPosition != -1) { + styleText(end, pEnd, pText.right(pEnd-end), pParameterBlock); } } else if ( (multilineCommentStartPosition != INT_MAX) && (multilineCommentStartPosition < stringPosition) @@ -322,9 +368,7 @@ void CellmlTextViewLexer::styleTextCurrent(int pBytesStart, int pBytesEnd, // There is a /* XXX */ comment to style, so first style everything that // is before it - int multilineCommentStartBytesPosition = fullTextBytesPosition(fullTextPosition(pBytesStart)+multilineCommentStartPosition); - - styleTextCurrent(pBytesStart, multilineCommentStartBytesPosition, + styleTextCurrent(pStart, pStart+multilineCommentStartPosition, pText.left(multilineCommentStartPosition), pParameterBlock); @@ -337,178 +381,188 @@ void CellmlTextViewLexer::styleTextCurrent(int pBytesStart, int pBytesEnd, // we need to finish styling it (which is effectively done in // styleTextPreviousMultilineComment())... - int start = fullTextPosition(pBytesStart); - int multilineCommentParameterBlockStartPosition = findString(StartParameterBlockString, start+multilineCommentStartPosition, int(Style::ParameterBlock), false); - int multilineCommentParameterBlockEndPosition = findString(EndParameterBlockString, multilineCommentParameterBlockStartPosition, int(Style::ParameterBlock)); + int multilineCommentParameterBlockStartPosition = findString(StartParameterBlockString, pStart+multilineCommentStartPosition, Style::ParameterBlock, false); + int multilineCommentParameterBlockEndPosition = findString(EndParameterBlockString, multilineCommentParameterBlockStartPosition, Style::ParameterBlock); - multilineCommentParameterBlockStartPosition = (multilineCommentParameterBlockStartPosition == -1)?INT_MAX:multilineCommentParameterBlockStartPosition; - multilineCommentParameterBlockEndPosition = (multilineCommentParameterBlockEndPosition == -1)?INT_MAX:multilineCommentParameterBlockEndPosition; + multilineCommentParameterBlockStartPosition = (multilineCommentParameterBlockStartPosition == -1)? + INT_MAX: + multilineCommentParameterBlockStartPosition; + multilineCommentParameterBlockEndPosition = (multilineCommentParameterBlockEndPosition == -1)? + INT_MAX: + multilineCommentParameterBlockEndPosition; - int absoluteMultilineCommentStartPosition = start+multilineCommentStartPosition; + int absoluteMultilineCommentStartPosition = pStart+multilineCommentStartPosition; - styleText(multilineCommentStartBytesPosition, pBytesEnd, - pText.right(fullTextLength(multilineCommentStartBytesPosition, pBytesEnd)), + styleText(pStart+multilineCommentStartPosition, pEnd, + pText.right(pEnd-pStart-multilineCommentStartPosition), (multilineCommentParameterBlockStartPosition < absoluteMultilineCommentStartPosition) && (absoluteMultilineCommentStartPosition < multilineCommentParameterBlockEndPosition)); - } else if ( (parameterBlockStartPosition != INT_MAX) - && (parameterBlockStartPosition < stringPosition) - && (parameterBlockStartPosition < singleLineCommentPosition) - && (parameterBlockStartPosition < multilineCommentStartPosition)) { - // There is a parameter block, so first style everything that is before - // it + } else { + int parameterBlockStartPosition = pText.indexOf(StartParameterBlockString); + + parameterBlockStartPosition = (parameterBlockStartPosition == -1)? + INT_MAX: + parameterBlockStartPosition; + + if ( (parameterBlockStartPosition != INT_MAX) + && (parameterBlockStartPosition < stringPosition) + && (parameterBlockStartPosition < singleLineCommentPosition) + && (parameterBlockStartPosition < multilineCommentStartPosition)) { + // There is a parameter block, so first style everything that is + // before it + + styleTextCurrent(pStart, pStart+parameterBlockStartPosition, + pText.left(parameterBlockStartPosition), + pParameterBlock); + + // Now style everything from the parameter block onwards + // Note: to style everything from the parameter block onwards means + // that we will find that a parameter block starts at the + // beginning of the 'new' given text... + + styleText(pStart+parameterBlockStartPosition, pEnd, + pText.right(pEnd-pStart-parameterBlockStartPosition), + pParameterBlock); + } else { + // Style the given text as a parameter block, if needed - int parameterBlockStartBytesPosition = fullTextBytesPosition(fullTextPosition(pBytesStart)+parameterBlockStartPosition); + if (pParameterBlock) { + applyStyle(pStart, pEnd, Style::ParameterBlock); + } - styleTextCurrent(pBytesStart, parameterBlockStartBytesPosition, - pText.left(parameterBlockStartPosition), - pParameterBlock); + // Check whether the given text contains some keywords from various + // categories - // Now style everything from the parameter block onwards - // Note: to style everything from the parameter block onwards means that - // we will find that a parameter block starts at the beginning of - // the 'new' given text... + static const QRegularExpression KeywordsRegEx = QRegularExpression( + "\\b(" + // CellML Text keywords - styleText(parameterBlockStartBytesPosition, pBytesEnd, - pText.right(fullTextLength(parameterBlockStartBytesPosition, pBytesEnd)), - pParameterBlock); - } else { - // Style the given text as a parameter block, if needed + "and|as|between|case|comp|def|endcomp|enddef|endsel|for|" + "group|import|incl|map|model|otherwise|sel|unit|using|var|" + "vars|" - if (pParameterBlock) { - startStyling(pBytesStart); - setStyling(pBytesEnd-pBytesStart, int(Style::ParameterBlock)); - } + // MathML arithmetic operators - // Check whether the given text contains some keywords from various - // categories + "abs|ceil|exp|fact|floor|ln|log|pow|root|sqr|sqrt|" - static const QRegularExpression KeywordsRegEx = QRegularExpression( - "\\b(" - // CellML Text keywords + // MathML logical operators - "and|as|between|case|comp|def|endcomp|enddef|endsel|for|group|" - "import|incl|map|model|otherwise|sel|unit|using|var|vars|" + "and|or|xor|not|" - // MathML arithmetic operators + // MathML calculus elements - "abs|ceil|exp|fact|floor|ln|log|pow|root|sqr|sqrt|" + "ode|" - // MathML logical operators + // MathML min/max operators - "and|or|xor|not|" + "min|max|" - // MathML calculus elements + // MathML gcd/lcm operators - "ode|" + "gcd|lcm|" - // MathML min/max operators + // MathML trigonometric operators - "min|max|" + "sin|cos|tan|sec|csc|cot|sinh|cosh|tanh|sech|csch|coth|" + "asin|acos|atan|asec|acsc|acot|asinh|acosh|atanh|asech|" + "acsch|acoth|" - // MathML gcd/lcm operators + // MathML constants - "gcd|lcm|" + "true|false|nan|pi|inf|e|" - // MathML trigonometric operators + // Extra operators - "sin|cos|tan|sec|csc|cot|sinh|cosh|tanh|sech|csch|coth|asin|" - "acos|atan|asec|acsc|acot|asinh|acosh|atanh|asech|acsch|acoth|" + "rem" + ")\\b"); - // MathML constants + static const QRegularExpression CellmlKeywordsRegEx = QRegularExpression( + "\\b(" + // Miscellaneous - "true|false|nan|pi|inf|e|" + "base|encapsulation|containment" + ")\\b"); - // Extra operators + static const QRegularExpression ParameterKeywordsRegEx = QRegularExpression( + "\\b(" + // Unit keywords - "rem" - ")\\b"); + "pref|expo|mult|off|" - static const QRegularExpression CellmlKeywordsRegEx = QRegularExpression( - "\\b(" - // Miscellaneous + // Variable keywords - "base|encapsulation|containment" - ")\\b"); + "init|pub|priv" + ")\\b"); - static const QRegularExpression ParameterKeywordsRegEx = QRegularExpression( - "\\b(" - // Unit keywords + static const QRegularExpression ParameterCellmlKeywordsRegEx = QRegularExpression( + "\\b(" + // Unit prefixes - "pref|expo|mult|off|" + "yotta|zetta|exa|peta|tera|giga|mega|kilo|hecto|deka|deci|" + "centi|milli|micro|nano|pico|femto|atto|zepto|yocto|" - // Variable keywords + // Public/private interfaces - "init|pub|priv" - ")\\b"); + "in|out|none" + ")\\b"); - static const QRegularExpression ParameterCellmlKeywordsRegEx = QRegularExpression( - "\\b(" - // Unit prefixes + static const QRegularExpression SiUnitKeywordsRegEx = QRegularExpression( + "\\b(" + // Standard units - "yotta|zetta|exa|peta|tera|giga|mega|kilo|hecto|deka|deci|" - "centi|milli|micro|nano|pico|femto|atto|zepto|yocto|" + "ampere|becquerel|candela|celsius|coulomb|dimensionless|" + "farad|gram|gray|henry|hertz|joule|katal|kelvin|kilogram|" + "liter|litre|lumen|lux|meter|metre|mole|newton|ohm|pascal|" + "radian|second|siemens|sievert|steradian|tesla|volt|watt|" + "weber" + ")\\b"); + + if (pParameterBlock) { + styleTextRegEx(pStart, pText, ParameterKeywordsRegEx, Style::ParameterKeyword); + styleTextRegEx(pStart, pText, ParameterCellmlKeywordsRegEx, Style::ParameterCellmlKeyword); + } else { + styleTextRegEx(pStart, pText, KeywordsRegEx, Style::Keyword); + styleTextRegEx(pStart, pText, CellmlKeywordsRegEx, Style::CellmlKeyword); + } - // Public/private interfaces + styleTextRegEx(pStart, pText, SiUnitKeywordsRegEx, + pParameterBlock? + Style::ParameterCellmlKeyword: + Style::CellmlKeyword); - "in|out|none" - ")\\b"); + // Check whether the given text contains some numbers - static const QRegularExpression SiUnitKeywordsRegEx = QRegularExpression( - "\\b(" - // Standard units + styleTextNumber(pStart, pText, pParameterBlock? + Style::ParameterNumber: + Style::Number); - "ampere|becquerel|candela|celsius|coulomb|dimensionless|farad|" - "gram|gray|henry|hertz|joule|katal|kelvin|kilogram|liter|litre|" - "lumen|lux|meter|metre|mole|newton|ohm|pascal|radian|second|" - "siemens|sievert|steradian|tesla|volt|watt|weber" - ")\\b"); + // Let QScintilla know that we are done with the styling of the + // given text + // Note: indeed, QScintilla uses the end position of the last bit of + // text that has been styled to determine the starting + // position of the next bit of text that needs to be styled + // (see QsciLexerCustom::handleStyleNeeded()). Now, depending + // on whether keywords and/or numbers have been styled, the + // end position may or not be pEnd. So, here, we make sure + // that it is, so that QScintilla can work as it should... - if (pParameterBlock) { - styleTextRegEx(pBytesStart, pText, ParameterKeywordsRegEx, int(Style::ParameterKeyword)); - styleTextRegEx(pBytesStart, pText, ParameterCellmlKeywordsRegEx, int(Style::ParameterCellmlKeyword)); - } else { - styleTextRegEx(pBytesStart, pText, KeywordsRegEx, int(Style::Keyword)); - styleTextRegEx(pBytesStart, pText, CellmlKeywordsRegEx, int(Style::CellmlKeyword)); + startStyling(pEnd); } - - styleTextRegEx(pBytesStart, pText, SiUnitKeywordsRegEx, - pParameterBlock? - int(Style::ParameterCellmlKeyword): - int(Style::CellmlKeyword)); - - // Check whether the given text contains some numbers - - styleTextNumberRegEx(pBytesStart, pText, - pParameterBlock? - int(Style::ParameterNumber): - int(Style::Number)); - - // Let QScintilla know that we are done with the styling of the given - // text - // Note: indeed, QScintilla uses the end position of the last bit of - // text that has been styled to determine the starting position of - // the next bit of text that needs to be styled (see - // QsciLexerCustom::handleStyleNeeded()). Now, depending on - // whether keywords and/or numbers have been styled, the end - // position may or not be pBytesEnd. So, here, we make sure that - // it is, so that QScintilla can work as it should... - - startStyling(pBytesEnd); } } //============================================================================== void CellmlTextViewLexer::styleTextPreviousMultilineComment(int pPosition, - int pBytesStart, - int pBytesEnd, - const QString &pText, + int pStart, + int pEnd, + const QByteArray &pText, bool pParameterBlock) { // A /* XXX */ comment started before or at the beginning of the given text, // so now look for where it ends - int multilineCommentEndPosition = findString(EndMultilineCommentString, pPosition, int(Style::MultilineComment)); + int multilineCommentEndPosition = findString(EndMultilineCommentString, pPosition, Style::MultilineComment); if (multilineCommentEndPosition == -1) { multilineCommentEndPosition = mFullText.length(); @@ -517,55 +571,47 @@ void CellmlTextViewLexer::styleTextPreviousMultilineComment(int pPosition, // Check whether the beginning of the given text is within the /* XXX */ // comment - int start = fullTextPosition(pBytesStart); - - if ((pPosition <= start) && (start <= multilineCommentEndPosition)) { + if ((pPosition <= pStart) && (pStart <= multilineCommentEndPosition)) { // The beginning of the given text is within a /* XXX */ comment, so // style it - int realBytesEnd = fullTextBytesPosition(multilineCommentEndPosition)+EndMultilineCommentLength; - int bytesEnd = qMin(pBytesEnd, realBytesEnd); + int realEnd = multilineCommentEndPosition+EndMultilineCommentLength; + int end = qMin(pEnd, realEnd); - startStyling(pBytesStart); - setStyling(bytesEnd-pBytesStart, int(Style::MultilineComment)); + applyStyle(pStart, end, Style::MultilineComment); // Now, style everything that is behind the /* XXX */ comment, if // anything - if (bytesEnd != pBytesEnd) { + if (end != pEnd) { if (pParameterBlock) { // Our /* XXX */ comment is within a parameter block, so finish // styling our parameter block - styleTextPreviousParameterBlock(fullTextPosition(bytesEnd), bytesEnd, pBytesEnd, - pText.right(fullTextLength(bytesEnd, pBytesEnd)), - false); + styleTextPreviousParameterBlock(end, end, pEnd, pText.right(pEnd-end), false); } else { - styleText(bytesEnd, pBytesEnd, - pText.right(fullTextLength(bytesEnd, pBytesEnd)), - pParameterBlock); + styleText(end, pEnd, pText.right(pEnd-end), pParameterBlock); } } } else { // The beginning of the given text is not within a /* XXX */ comment, so // style it - styleTextCurrent(pBytesStart, pBytesEnd, pText, pParameterBlock); + styleTextCurrent(pStart, pEnd, pText, pParameterBlock); } } //============================================================================== void CellmlTextViewLexer::styleTextPreviousParameterBlock(int pPosition, - int pBytesStart, - int pBytesEnd, - const QString &pText, + int pStart, int pEnd, + const QByteArray &pText, bool pParameterBlock) { // A parameter block started before or at the beginning of the given text, // so now look for where it ends - int parameterBlockEndPosition = findString(EndParameterBlockString, pPosition, int(Style::ParameterBlock)); + int parameterBlockEndPosition = findString(EndParameterBlockString, pPosition, Style::ParameterBlock); if (parameterBlockEndPosition == -1) { parameterBlockEndPosition = mFullText.length(); @@ -573,69 +619,60 @@ void CellmlTextViewLexer::styleTextPreviousParameterBlock(int pPosition, // Check whether the beginning of the given text is within a parameter block - int start = fullTextPosition(pBytesStart); - - if ((pPosition <= start) && (start <= parameterBlockEndPosition)) { + if ((pPosition <= pStart) && (pStart <= parameterBlockEndPosition)) { // The beginning of the given text is within a parameter block, so style // it as such - int realBytesEnd = fullTextBytesPosition(parameterBlockEndPosition)+EndParameterBlockLength; - int bytesEnd = qMin(pBytesEnd, realBytesEnd); - bool hasStart = (pPosition == start) + int realEnd = parameterBlockEndPosition+EndParameterBlockLength; + int end = qMin(pEnd, realEnd); + bool hasStart = (pPosition == pStart) && (mFullText.mid(pPosition, StartParameterBlockLength) == StartParameterBlockString); - bool hasEnd = bytesEnd == realBytesEnd; + bool hasEnd = end == realEnd; // If needed, style the start of the parameter block if (hasStart) { - startStyling(pBytesStart); - setStyling(StartParameterBlockLength, int(Style::ParameterBlock)); + applyStyle(pStart, pStart+StartParameterBlockLength, Style::ParameterBlock); } // Now style the contents of the parameter block itself - int newBytesStart = pBytesStart+(hasStart?StartParameterBlockLength:0); - int newBytesEnd = bytesEnd-(hasEnd?EndParameterBlockLength:0); + int newStart = pStart+(hasStart?StartParameterBlockLength:0); + int newEnd = end-(hasEnd?EndParameterBlockLength:0); - styleTextCurrent(newBytesStart, newBytesEnd, - pText.mid(fullTextLength(pBytesStart, newBytesStart), - fullTextLength(newBytesStart, newBytesEnd)), - true); + styleTextCurrent(newStart, newEnd, pText.mid(newStart-pStart, newEnd-newStart), true); // If needed, style the end of the parameter block, as well as what is // behind it if (hasEnd) { - startStyling(bytesEnd-EndParameterBlockLength); - setStyling(EndParameterBlockLength, int(Style::ParameterBlock)); + applyStyle(end-EndParameterBlockLength, end, Style::ParameterBlock); - styleText(bytesEnd, pBytesEnd, - pText.right(fullTextLength(bytesEnd, pBytesEnd)), - pParameterBlock); + styleText(end, pEnd, pText.right(pEnd-end), pParameterBlock); } } else { // The beginning of the given text is not within a parameter block, so // style it - styleTextCurrent(pBytesStart, pBytesEnd, pText, pParameterBlock); + styleTextCurrent(pStart, pEnd, pText, pParameterBlock); } } //============================================================================== -void CellmlTextViewLexer::styleTextString(int pPosition, int pBytesStart, - int pBytesEnd, const QString &pText, +void CellmlTextViewLexer::styleTextString(int pPosition, int pStart, int pEnd, + const QByteArray &pText, bool pParameterBlock) { // There is a string to style, so first style everything that is before it - int bytesPosition = fullTextBytesPosition(fullTextPosition(pBytesStart)+pPosition); + int position = pStart+pPosition; - styleTextCurrent(pBytesStart, bytesPosition, pText.left(pPosition), pParameterBlock); + styleTextCurrent(pStart, position, pText.left(pPosition), pParameterBlock); // Now, check where the string ends, if anywhere - int nextBytesStart = -1; + int nextStart = -1; int stringEndPosition = pText.indexOf(StringString, pPosition+StringLength); if (stringEndPosition != -1) { @@ -644,18 +681,17 @@ void CellmlTextViewLexer::styleTextString(int pPosition, int pBytesStart, int stringStartLineNumber, stringStartColumnNumber; int stringEndLineNumber, stringEndColumnNumber; - int stringEndBytesPosition = textBytesPosition(pText, stringEndPosition); - editor()->lineIndexFromPosition(bytesPosition, + editor()->lineIndexFromPosition(position, &stringStartLineNumber, &stringStartColumnNumber); - editor()->lineIndexFromPosition(pBytesStart+stringEndBytesPosition, + editor()->lineIndexFromPosition(pStart+stringEndPosition, &stringEndLineNumber, &stringEndColumnNumber); if (stringStartLineNumber == stringEndLineNumber) { // The string starts and ends on the same line, so get ready to // style everything that is after the string - nextBytesStart = pBytesStart+stringEndBytesPosition+StringLength; + nextStart = pStart+stringEndPosition+StringLength; } else { // The string starts and ends on a different line, so consider that // we couldn't find the end of the string @@ -670,60 +706,53 @@ void CellmlTextViewLexer::styleTextString(int pPosition, int pBytesStart, // end of the line, if anything, on which the string started int eolPosition = pText.indexOf(mEolString, pPosition+StringLength); - int eolBytesPosition = (eolPosition == -1)?-1:textBytesPosition(pText, eolPosition); - if (eolBytesPosition != -1) { - nextBytesStart = pBytesStart+eolBytesPosition+mEolString.length(); + if (eolPosition != -1) { + nextStart = pStart+eolPosition+mEolString.length(); } } // Style the string itself - startStyling(bytesPosition); - setStyling(((nextBytesStart == -1)?pBytesEnd:nextBytesStart)-bytesPosition, + applyStyle(position, + (nextStart == -1)? + pEnd: + nextStart, pParameterBlock? - int(Style::ParameterString): - int(Style::String)); + Style::ParameterString: + Style::String); // Style whatever is after the string - if (nextBytesStart != -1) { - styleText(nextBytesStart, pBytesEnd, - pText.right(fullTextLength(nextBytesStart, pBytesEnd)), - pParameterBlock); + if (nextStart != -1) { + styleText(nextStart, pEnd, pText.right(pEnd-nextStart), pParameterBlock); } } //============================================================================== -void CellmlTextViewLexer::styleTextRegEx(int pBytesStart, - const QString &pText, +void CellmlTextViewLexer::styleTextRegEx(int pStart, const QByteArray &pText, const QRegularExpression &pRegEx, - int pRegExStyle) + Style pStyle) { // Style the given text using the given regular expression QRegularExpressionMatchIterator regExMatchIter = pRegEx.globalMatch(pText); - QRegularExpressionMatch regExMatch; while (regExMatchIter.hasNext()) { - regExMatch = regExMatchIter.next(); - // We have a match, so style it - int matchBytesStart = textBytesPosition(pText, regExMatch.capturedStart()); + QRegularExpressionMatch regExMatch = regExMatchIter.next(); + int capturedStart = regExMatch.capturedStart(); - startStyling(pBytesStart+matchBytesStart); - setStyling(textBytesPosition(pText, regExMatch.capturedStart()+regExMatch.capturedLength())-matchBytesStart, - pRegExStyle); + applyStyle(pStart+capturedStart, pStart+capturedStart+regExMatch.capturedLength(), pStyle); } } //============================================================================== -void CellmlTextViewLexer::styleTextNumberRegEx(int pBytesStart, - const QString &pText, - int pRegExStyle) +void CellmlTextViewLexer::styleTextNumber(int pStart, const QByteArray &pText, + Style pStyle) { // Style the given text using the number regular expression @@ -733,52 +762,53 @@ void CellmlTextViewLexer::styleTextNumberRegEx(int pBytesStart, // want to be able to catch "123e")... QRegularExpressionMatchIterator regExMatchIter = NumberRegEx.globalMatch(pText); - QRegularExpressionMatch regExMatch; while (regExMatchIter.hasNext()) { - regExMatch = regExMatchIter.next(); - // We have a match, so style it, but only if: // - The character in front of the match is not in [0-9a-zA-Z_] and is // part of the ASCII table // - The character following the match is not in [a-zA-Z_.] and is part // of the ASCII table - int prevCharPos = fullTextPosition(pBytesStart)+regExMatch.capturedStart()-1; - int nextCharPos = prevCharPos+regExMatch.capturedLength()+1; - - ushort prevChar = ((prevCharPos >= 0)?mFullText[prevCharPos]:QChar()).unicode(); - ushort nextChar = ((nextCharPos < mFullText.length())?mFullText[nextCharPos]:QChar()).unicode(); - - if (( (prevChar < 48) || ((prevChar > 57) && (prevChar < 65)) + QRegularExpressionMatch regExMatch = regExMatchIter.next(); + int capturedStart = regExMatch.capturedStart(); + int capturedLength = regExMatch.capturedLength(); + int prevCharPos = pStart+capturedStart-1; + int nextCharPos = prevCharPos+capturedLength+1; + + char prevChar = (prevCharPos >= 0)? + mFullText[prevCharPos]: + 0; + char nextChar = (nextCharPos < mFullText.length())? + mFullText[nextCharPos]: + 0; + + if ( ( (prevChar < 48) + || ((prevChar > 57) && (prevChar < 65)) || ((prevChar > 90) && (prevChar < 95)) - || (prevChar == 96) || ((prevChar > 122) && (prevChar < 128))) - && ( (nextChar < 46) || ((nextChar > 46) && (nextChar < 65)) + || (prevChar == 96) + || (prevChar > 122)) + && ( (nextChar < 46) + || ((nextChar > 46) && (nextChar < 65)) || ((nextChar > 90) && (nextChar < 95)) - || (nextChar == 96) || ((nextChar > 122) && (nextChar < 128)))) { - int matchBytesStart = textBytesPosition(pText, regExMatch.capturedStart()); - - startStyling(pBytesStart+matchBytesStart); - setStyling(textBytesPosition(pText, regExMatch.capturedStart()+regExMatch.capturedLength())-matchBytesStart, - pRegExStyle); + || (nextChar == 96) + || (nextChar > 122))) { + applyStyle(pStart+capturedStart, pStart+capturedStart+capturedLength, pStyle); } } } //============================================================================== -bool CellmlTextViewLexer::validString(int pFrom, int pTo, int pStyle) const +bool CellmlTextViewLexer::validString(int pFrom, int pTo, Style pStyle) const { // Check whether the string, which range is given, is valid, i.e. is either // of the default or given style - qint64 style; - for (int i = pFrom; i < pTo; ++i) { - style = editor()->SendScintilla(QsciScintilla::SCI_GETSTYLEAT, - fullTextBytesPosition(i)); + Style style = Style(editor()->SendScintilla(QsciScintilla::SCI_GETSTYLEAT, i)); - if ((style != int(Style::Default)) && (style != pStyle)) { + if ((style != Style::Default) && (style != pStyle)) { return false; } } @@ -788,8 +818,8 @@ bool CellmlTextViewLexer::validString(int pFrom, int pTo, int pStyle) const //============================================================================== -int CellmlTextViewLexer::findString(const QString &pString, int pFrom, - int pStyle, bool pForward) +int CellmlTextViewLexer::findString(const QByteArray &pString, int pFrom, + Style pStyle, bool pForward) { // Find forward/backward the given string starting from the given position @@ -807,45 +837,6 @@ int CellmlTextViewLexer::findString(const QString &pString, int pFrom, //============================================================================== -int CellmlTextViewLexer::fullTextPosition(int pBytesPosition) const -{ - // Return the corresponding position within mFullText of the given - // byte-based position within mFullTextUtf8 - - return QString(mFullTextUtf8.left(pBytesPosition)).length(); -} - -//============================================================================== - -int CellmlTextViewLexer::fullTextLength(int pBytesStart, int pBytesEnd) const -{ - // Return the corresponding length within mFullText of the given byte-based - // length of a substring within mFullTextUtf8 - - return fullTextPosition(pBytesEnd)-fullTextPosition(pBytesStart); -} - -//============================================================================== - -int CellmlTextViewLexer::fullTextBytesPosition(int pPosition) const -{ - // Return the byte-based value of the given position within mFullText - - return mFullText.left(pPosition).toUtf8().length(); -} - -//============================================================================== - -int CellmlTextViewLexer::textBytesPosition(const QString &pText, - int pPosition) const -{ - // Return the byte-based value of the given position within the given text - - return pText.left(pPosition).toUtf8().length(); -} - -//============================================================================== - } // namespace CellMLTextView } // namespace OpenCOR diff --git a/src/plugins/editing/CellMLTextView/src/cellmltextviewlexer.h b/src/plugins/editing/CellMLTextView/src/cellmltextviewlexer.h index 1b4d683721..c46d28049f 100644 --- a/src/plugins/editing/CellMLTextView/src/cellmltextviewlexer.h +++ b/src/plugins/editing/CellMLTextView/src/cellmltextviewlexer.h @@ -69,41 +69,34 @@ class CellmlTextViewLexer : public QsciLexerCustom QColor color(int pStyle) const override; QFont font(int pStyle) const override; - void styleText(int pBytesStart, int pBytesEnd) override; + void styleText(int pStart, int pEnd) override; private: - QString mFullText; - QByteArray mFullTextUtf8; + QByteArray mFullText; + QByteArray mEolString; - QString mEolString; - - void styleText(int pBytesStart, int pBytesEnd, const QString &pText, + void applyStyle(int pStart, int pEnd, Style pStyle); + void styleText(int pStart, int pEnd, const QByteArray &pText, bool pParameterBlock); - void styleTextCurrent(int pBytesStart, int pBytesEnd, const QString &pText, + void styleTextCurrent(int pStart, int pEnd, const QByteArray &pText, bool pParameterBlock); - void styleTextPreviousMultilineComment(int pPosition, int pBytesStart, - int pBytesEnd, const QString &pText, + void styleTextPreviousMultilineComment(int pPosition, int pStart, int pEnd, + const QByteArray &pText, bool pParameterBlock); - void styleTextPreviousParameterBlock(int pPosition, int pBytesStart, - int pBytesEnd, const QString &pText, + void styleTextPreviousParameterBlock(int pPosition, int pStart, int pEnd, + const QByteArray &pText, bool pParameterBlock); - void styleTextString(int pPosition, int pBytesStart, int pBytesEnd, - const QString &pText, bool pParameterBlock); - void styleTextRegEx(int pBytesStart, const QString &pText, - const QRegularExpression &pRegEx, int pRegExStyle); - void styleTextNumberRegEx(int pBytesStart, const QString &pText, - int pRegExStyle); - - bool validString(int pFrom, int pTo, int pStyle) const; - int findString(const QString &pString, int pFrom, int pStyle, + void styleTextString(int pPosition, int pStart, int pEnd, + const QByteArray &pText, bool pParameterBlock); + void styleTextRegEx(int pStart, const QByteArray &pText, + const QRegularExpression &pRegEx, Style pStyle); + void styleTextNumber(int pStart, const QByteArray &pText, + Style pStyle); + + bool validString(int pFrom, int pTo, Style pStyle) const; + int findString(const QByteArray &pString, int pFrom, Style pStyle, bool pForward = true); - int fullTextPosition(int pBytesPosition) const; - int fullTextLength(int pBytesStart, int pBytesEnd) const; - - int fullTextBytesPosition(int pPosition) const; - int textBytesPosition(const QString &pText, int pPosition) const; - signals: void done(); }; diff --git a/src/plugins/widget/QScintillaWidget/src/qscintillawidget.cpp b/src/plugins/widget/QScintillaWidget/src/qscintillawidget.cpp index 9ffbf75853..6e7941666b 100644 --- a/src/plugins/widget/QScintillaWidget/src/qscintillawidget.cpp +++ b/src/plugins/widget/QScintillaWidget/src/qscintillawidget.cpp @@ -405,7 +405,7 @@ QLabel * QScintillaWidget::editingModeWidget() const //============================================================================== -QString QScintillaWidget::eolString() const +QByteArray QScintillaWidget::eolString() const { // Return the end of line we use diff --git a/src/plugins/widget/QScintillaWidget/src/qscintillawidget.h b/src/plugins/widget/QScintillaWidget/src/qscintillawidget.h index 4f71fd637c..92784b396b 100644 --- a/src/plugins/widget/QScintillaWidget/src/qscintillawidget.h +++ b/src/plugins/widget/QScintillaWidget/src/qscintillawidget.h @@ -96,7 +96,7 @@ class QSCINTILLAWIDGET_EXPORT QScintillaWidget : public QsciScintilla QLabel * cursorPositionWidget() const; QLabel * editingModeWidget() const; - QString eolString() const; + QByteArray eolString() const; QColor backgroundColor(int pStyle); void setBackgroundColor(int pStyle, const QColor &pBackgroundColor);