Ryan McFadden added a nice syntax-highlighter to musredit (Qt6).
Build and Deploy Documentation / build-and-deploy (push) Successful in 32s

Merge branch 'highlight' into root6
This commit is contained in:
2026-06-24 07:26:06 +02:00
4 changed files with 602 additions and 0 deletions
+1
View File
@@ -42,6 +42,7 @@ set(musredit_src
PGetPlotBlockDialog.cpp
PMsr2DataDialog.cpp
PChangeDefaultPathsDialog.cpp
PMsrHighlighter.cpp
PMusrEditAbout.cpp
main.cpp
)
@@ -0,0 +1,471 @@
/**
* @file PMsrHighlighter.cpp
* @brief Implementation of the MsrHighlighter class for musredit.
*
* This file contains the implementation of a custom syntax highlighter for
* (.msr) files, to be used by musredit. It identifies, for example, blocks,
* comments, and predefined keywords.
*
* @author Ryan M. L. McFadden (rmlm@triumf.ca)
* @date June 2026
*/
/***************************************************************************
* Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "PMsrHighlighter.h"
/**
* @brief Initializes a new instance of the syntax highlighting compiler engine.
*
* Sets up structural text formatting rules (fonts, colors, and styles) and
* compiles matching regex patterns for blocks, keywords, and comments before
* appending them to the main lookup vector.
*/
PMsrHighlighter::PMsrHighlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent) {
// =========================================================================
// TABLEAU COLOURS
// =========================================================================
// Individual QColor definitions grouped by color pairs (Dark / Light)
const QColor tabBlue("#1F77B4");
const QColor tabBlueLight("#AEC7E8");
const QColor tabOrange("#FF7F0E");
const QColor tabOrangeLight("#FFBB78");
const QColor tabGreen("#2CA02C");
const QColor tabGreenLight("#98DF8A");
const QColor tabRed("#D62728");
const QColor tabRedLight("#FF9896");
const QColor tabPurple("#9467BD");
const QColor tabPurpleLight("#C5B0D5");
const QColor tabBrown("#8C564B");
const QColor tabBrownLight("#C49C94");
const QColor tabPink("#E377C2");
const QColor tabPinkLight("#F7B6D2");
const QColor tabGray("#7F7F7F");
const QColor tabGrayLight("#C7C7C7");
const QColor tabOlive("#BCBD22");
const QColor tabOliveLight("#DBDB8D");
const QColor tabCyan("#17BECF");
const QColor tabCyanLight("#9EDAE5");
// =========================================================================
// KEYWORD STYLING
// =========================================================================
// Define the colour/styling for each keyword type.
blockFormat.setForeground(tabOlive); // BLOCK keywords
theoryFormat.setForeground(tabGreen); // THEORY keywords
functionsFormat.setForeground(tabRed); // FUNCTION keywords
parFormat.setForeground(tabPink); // parX keywords
mapFormat.setForeground(tabOrange); // mapX keywords
funFormat.setForeground(tabPurple); // funX keywords
runFormat.setForeground(tabGreen); // RUN/GLOBAL keywords
commandsFormat.setForeground(tabGreen); // COMMANDS keywords
fourierFormat.setForeground(tabGreen); // FOURIER keywords
plotFormat.setForeground(tabGreen); // PLOT keywords
optionsFormat.setForeground(tabRed); // keywords input options
infoFormat.setForeground(tabGray); // auto-printed information
boolFormat.setForeground(tabRed); // boolean strings
nullFormat.setForeground(tabRed); // null strings
dateFormat.setForeground(tabPink); // date & time strings
numberFormat.setForeground(tabBlue); // integer & float strings
commentFormat.setForeground(tabGray); // comments
commentFormat.setFontItalic(true);
titleFormat.setForeground(Qt::black); // title
// titleFormat.setBackground(tabGrayLight);
// =========================================================================
// BLOCK HEADERS
// =========================================================================
QStringList block_keywords = {"FITPARAMETER", "THEORY", "FUNCTIONS",
"GLOBAL", "RUN", "COMMANDS",
"FOURIER", "PLOT", "STATISTIC"};
addRule(block_keywords, blockFormat);
// =========================================================================
// THEORY KEYWORDS
// =========================================================================
QStringList theory_keywords_long = {
"const", "asymmetry", "simplExpo", "generExpo",
"simpleGss", "statGssKT", "statGssKTLF", "dynGssKTLF",
"statExpKT", "statExpKTLF", "dynExpKTLF", "dynGLKT_F_ZF",
"dynGLKT_F_LF", "dynGLKT_LF", "combiLGKT", "strKT",
"spinGlass", "rdAnisoHf", "TFieldCos", "internFld",
"Bessel", "internbsl", "internFldGK", "internFldLL",
"F_mu_F", "abragam", "skewedGss", "staticNKZF",
"staticNKTF", "dynamicNKZF", "dynamicNKTF", "muMinusExpTF",
"polynom"};
QStringList theory_keywords_short = {"c",
"a",
"se",
"ge",
"stg",
"sgktlf",
"dgktlf",
"sekt",
"sektlf",
"dektlf",
"dglktfzf",
"dglktflf",
"dglktlf",
"lgkt",
"skt",
"spg",
"rahf",
"tf",
"ifld",
"b",
"ib",
"ifgk",
"ifll",
"fmuf",
"ab"
"skg",
"snkzf",
"snktf",
"snkzf",
"dnkzf",
"dnktf",
"mmsetf",
"p"};
// combine both theory keyword lists
QStringList theory_keywords = theory_keywords_long + theory_keywords_short;
addRule(theory_keywords, theoryFormat);
// =========================================================================
// FUNCTIONS KEYWORDS
// =========================================================================
QStringList functions_keywords = {
"gamma_mu", "pi", "B", "b", "En", "en", "T\\d+",
/* "+", "-", "*", "/", */
};
addRule(functions_keywords, functionsFormat);
// =========================================================================
// funX KEYWORDS
// =========================================================================
QStringList fun_keywords = {"fun\\d+"};
addRule(fun_keywords, funFormat);
// =========================================================================
// parX KEYWORDS
// =========================================================================
QStringList par_keywords = {"par\\d+"};
addRule(par_keywords, parFormat);
// =========================================================================
// mapX KEYWORDS
// =========================================================================
QStringList map_keywords = {"map\\d+"};
addRule(map_keywords, mapFormat);
// =========================================================================
// RUN KEYWORDS
// =========================================================================
QStringList run_keywords = {
"fittype", "data", "t0", "addt0",
"deadtime-cor", "fit", "packing", "rrf_freq",
"rrf_packing", "rrf_phase", "alpha", "beta",
"norm", "backgr.fit", "lifetime", "lifetimecorrection",
"map", "forward", "backward", "backgr.fix",
"background", "xy-data"};
addRule(run_keywords, runFormat);
// =========================================================================
// FOURIER KEYWORDS
// =========================================================================
QStringList fourier_keywords = {"units",
"fourier_power",
"dc-corrected",
"apodization",
"plot",
"phase",
"range_for_phase_correction",
"range"};
addRule(fourier_keywords, fourierFormat);
// =========================================================================
// PLOT KEYWORDS
// =========================================================================
QStringList plot_keywords = {"lifetimecorrection",
"runs",
"range",
"sub_ranges",
"use_fit_ranges",
"view_packing",
"logx",
"logy",
"rrf_packing value",
"rrf_freq value unit",
"rrf_phase value"};
addRule(plot_keywords, plotFormat);
// =========================================================================
// UNITS OPTIONS
// =========================================================================
QStringList units_options = {"Gauss", "Tesla", "MHz", "Mc/s"};
// =========================================================================
// APODIZATION OPTIONS
// =========================================================================
QStringList apodization_options = {"NONE", "WEAK", "MEDIUM", "STRONG"};
// =========================================================================
// RRF OPTIONS
// =========================================================================
QStringList rrf_options = {"MHz", "Mc", "T"};
// =========================================================================
// FOURIER OPTIONS
// =========================================================================
QStringList fourier_options = {"REAL", "IMAG", "REAL_AND_IMAG",
"POWER", "PHASE", "PHASE_OPT_REAL"};
// =========================================================================
// COMMANDS OPTIONS
// =========================================================================
QStringList commands_options = {"RESET"};
// =========================================================================
// ALL OPTIONS
// =========================================================================
QStringList all_options = units_options + apodization_options + rrf_options +
fourier_options + commands_options;
addRule(all_options, optionsFormat);
// =========================================================================
// FIT INFO
// =========================================================================
QStringList fit_info = {
"(single histogram fit)", "(single histogram RRF fit)",
"(asymmetry fit)", "(asymmetry RRF fit)",
"(mu minus fit)", "(beta-NMR fit)",
"(non muSR fit)"};
// =========================================================================
// PLOT INFO
// =========================================================================
QStringList plot_info = {"(single histo plot)", "(single histo RRF plot)",
"(asymmetry plot)", "(asymmetry RRF plot)",
"(mu minus plot)", "(beta-NMR asymmetry plot)",
"(non muSR plot)"};
// =========================================================================
// RUN INFO
// =========================================================================
QStringList run_info = {"(name beamline institute data-file-format)"};
// =========================================================================
// THEORY INFO
// =========================================================================
QStringList theory_info = {"(N0 tau A lambda phase nu tshift)",
"(N0 tau A lambda phase nu)",
"(damping hopping-rate tshift)",
"(damping hopping-rate)",
"(damping_D0 R_b nu_c tshift)",
"(damping_D0 R_b nu_c)",
"(damping_D0 R_b tshift)",
"(damping_D0 R_b)",
"(fraction frequency sigma lambda beta tshift)",
"(fraction frequency sigma lambda beta)",
"(fraction phase frequency Trate Lrate tshift)",
"(fraction phase frequency Trate Lrate)",
"(frequency damping hopping-rate tshift)",
"(frequency damping hopping-rate)",
"(frequency damping tshift)",
"(frequency damping)",
"(frequency rate tshift)",
"(frequency rate)",
"(frequency tshift)",
"(frequency)",
"(lorentzRate gaussRate tshift)",
"(lorentzRate gaussRate)",
"(phase frequency damping_D0 R_b nu_c tshift)",
"(phase frequency damping_D0 R_b nu_c)",
"(phase frequency damping_D0 R_b tshift)",
"(phase frequency damping_D0 R_b)",
"(phase frequency rate_m rate_p tshift)",
"(phase frequency rate_m rate_p)",
"(phase frequency tshift)",
"(phase frequency)",
"(rate beta tshift)",
"(rate beta)",
"(rate exponent tshift)",
"(rate exponent)",
"(rate hopprate order tshift)",
"(rate hopprate order)",
"(rate hopprate tshift)",
"(rate hopprate)",
"(rate tshift)",
"(rate)",
"(tshift p0 p1 ... pn)"};
// =========================================================================
// ALL INFO
// =========================================================================
QStringList all_info = fit_info + run_info + plot_info + theory_info;
addRule(all_info, infoFormat);
// =========================================================================
// COMMANDS BLOCK KEYWORDS
// =========================================================================
QStringList commands_keywords = {
"STRATEGY", "MIGRAD", "SIMPLEX", "MINIMIZE",
"MINOS", "HESSE", "SAVE", "SET BATCH",
"END RETURN", "MAX_LIKELIHOOD", "PRINT_LEVEL", "SCAN",
"CONTOURS", "MNPLOT", "FIX", "RELEASE",
"FIT_RANGE", "SCALE_N0_BKG", "SECTOR", "OpenMP",
"CUDA", "OpenCL-CPU", "OpenCL-GPU"};
addRule(commands_keywords, commandsFormat);
// =========================================================================
// BOOLEANS
// =========================================================================
QStringList bool_keywords = {"true", "false"};
addRule(bool_keywords, boolFormat);
// =========================================================================
// NULLS
// =========================================================================
QStringList null_keywords = {"none"};
addRule(null_keywords, nullFormat);
// =========================================================================
// NUMBERS
// =========================================================================
// Distinguish integer/decimal numbers from raw text.
HighlightingRule number_rule;
QRegularExpression number_regex("\\b-?\\d*\\.?\\d+\\b");
number_rule.pattern = number_regex;
number_rule.format = numberFormat;
highlightingRules.append(number_rule);
// =========================================================================
// DATES
// =========================================================================
// Distinguish date/time strings from raw text.
HighlightingRule date_rule;
// date regex (for dates of the form: YYYY-MM-DD HH:mm:ss)
QRegularExpression date_regex("\\b\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3["
"01]) (0\\d|1\\d|2[0-3]):[0-5]\\d:[0-5]\\d\\b");
date_rule.pattern = date_regex;
date_rule.format = dateFormat;
highlightingRules.append(date_rule);
// =========================================================================
// INLINE AND LINE COMMENTS
// =========================================================================
// Musrfit treats lines beginning with '#' as ignored comments.
// This matches a literal '#' symbol and captures everything remaining on that
// line. Styled gray and italicized to quickly indicate non-executable text
// annotations.
HighlightingRule comment_rule;
QRegularExpression comment_regex("#.*");
comment_rule.pattern = comment_regex;
comment_rule.format = commentFormat;
highlightingRules.append(comment_rule);
}
/**
* @brief Triggers formatting checks against text segments whenever they refresh
* on screen.
*
* Loops through the rules sequentially. If a match occurs, the method
* calculates the starting point index and length, passing this information back
* to the Qt layout interface engine.
*/
void PMsrHighlighter::addRule(const QStringList &keywords,
const QTextCharFormat formatting) {
// Instantiate the HighlighingRule struct.
HighlightingRule rule;
// loop through every keyword
for (const QString &keyword : keywords) {
// Wrap each keyword string into a strict word-boundary (\b) regular
// expression. This ensures keywords matching inside longer variable titles
// do not falsely trigger formatting.
QRegularExpression regex(QString("\\b%1\\b").arg(keyword));
// apply the formatting to the rule struct
rule.pattern = regex;
rule.format = formatting;
// add the rule to the main list
this->highlightingRules.append(rule);
}
}
/**
* @brief Triggers formatting checks against text segments whenever they refresh
* on screen.
*
* Loops through the rules sequentially. If a match occurs, the method
* calculates the starting point index and length, passing this information back
* to the Qt layout interface engine.
*/
void PMsrHighlighter::highlightBlock(const QString &text) {
// Sequentially process each entry stored inside the rules vector array.
for (const HighlightingRule &rule : highlightingRules) {
// Execute a global search through the single block string provided.
QRegularExpressionMatchIterator matchIterator =
rule.pattern.globalMatch(text);
// Loop over every match location instance discovered within this single
// text string.
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
// Extract the position details of the substring match sequence.
int index = match.capturedStart();
int length = match.capturedLength();
// Core Qt framework utility method which maps styles directly onto
// coordinates of the running open source buffer segment.
setFormat(index, length, rule.format);
}
}
// Treat highlighting of the TITLE block separately so that it take precedence
// over all other rules.
if (currentBlock().blockNumber() == 0) {
// applies the title formatting for the entirety of the first line
setFormat(0, text.length(), titleFormat);
}
}
+125
View File
@@ -0,0 +1,125 @@
/**
* @file PMsrHighlighter.h
* @brief Definition of the MsrHighlighter class for musredit.
*
* This file contains the definition of a custom syntax highlighter for
* (.msr) files, to be used by musredit. It identifies, for example, blocks,
* comments, and predefined keywords.
*
* @author Ryan M. L. McFadden (rmlm@triumf.ca)
* @date June 2026
*/
/***************************************************************************
* Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef _PMSRHIGHLIGHTER_H_
#define _PMSRHIGHLIGHTER_H_
#include <QColor>
#include <QRegularExpression>
#include <QSyntaxHighlighter>
#include <QTextCharFormat>
#include <QVector>
/**
* @class MsrHighlighter
* @brief Custom syntax highlighter for .msr (musrfit configuration) file
* syntax.
*
* This class inherits from QSyntaxHighlighter to intercept a QTextDocument's
* layout. It applies custom fonts, weights, and colors to specific structural
* components of the text document via regular expression rules.
*/
class PMsrHighlighter : public QSyntaxHighlighter {
Q_OBJECT
public:
/**
* @brief Constructs a PMsrHighlighter object and attaches it to a target
* document.
*
* @param parent The parent QTextDocument that this engine will dynamically
* format.
*/
PMsrHighlighter(QTextDocument *parent = nullptr);
protected:
/**
* @brief Automatically called by the Qt rendering engine to format lines of
* text.
*
* Iterates through the registered regular expression rule vectors and
* overrides the text layout style wherever a character sequence matches a
* rule.
*
* @param text A single block (typically a line) of text provided by the
* editor.
*/
void highlightBlock(const QString &text) override;
private:
/**
* @struct HighlightingRule
* @brief Container grouping a lookup pattern with its visual theme mapping.
*/
struct HighlightingRule {
QRegularExpression
pattern; ///< The compiled regular expression pattern to search for.
QTextCharFormat
format; ///< The text style (color, font, weight) applied upon match.
};
/**
* @brief A dynamic array tracking all active syntax parsing rules.
*/
QVector<HighlightingRule> highlightingRules;
/**
* @brief Helper function for passing formatting information to the
* highlightingRules dynamic array.
*/
void addRule(const QStringList &keywords, const QTextCharFormat formatting);
QTextCharFormat blockFormat; ///< Formatting profile for BLOCK keywords.
QTextCharFormat titleFormat; ///< Formatting profile for TITLE line.
QTextCharFormat theoryFormat; ///< Formatting profile for THEORY keywords.
QTextCharFormat
functionsFormat; ///< Formatting profile for FUNCTION keywords.
QTextCharFormat parFormat; ///< Formatting profile for parX keywords.
QTextCharFormat mapFormat; ///< Formatting profile mapX keywords.
QTextCharFormat funFormat; ///< Formatting profile funX keywords.
QTextCharFormat runFormat; ///< Formatting profile RUN/GLOBAL keywords.
QTextCharFormat commandsFormat; ///< Formatting profile COMMANDS keywords.
QTextCharFormat fourierFormat; ///< Formatting profile FOURIER keywords.
QTextCharFormat plotFormat; ///< Formatting profile PLOT keywords.
QTextCharFormat
optionsFormat; ///< Formatting profile for keyword input options.
QTextCharFormat
infoFormat; ///< Formatting profile for auto-printed information.
QTextCharFormat boolFormat; ///< Formatting profile for boolean strings.
QTextCharFormat nullFormat; ///< Formatting profile for null strings.
QTextCharFormat dateFormat; ///< Formatting profile for date & time strings
QTextCharFormat
numberFormat; ///< Formatting profile for integer & float strings.
QTextCharFormat commentFormat; ///< Formatting profile for comments.
};
#endif // _PMSRHIGHLIGHTER_H_
@@ -66,6 +66,7 @@
#include "PGetNonMusrRunBlockDialog.h"
#include "PGetFourierBlockDialog.h"
#include "PGetPlotBlockDialog.h"
#include "PMsrHighlighter.h"
//----------------------------------------------------------------------------------------------------
/**
@@ -101,6 +102,10 @@ PSubTextEdit::PSubTextEdit(PAdmin *admin, QWidget *parent) :
QPlainTextEdit(parent),
fAdmin(admin)
{
// Pass the current text document to the highlighter. This ensures that every
// time a new .msr file or tab is spawned, the document registers the parser
// formatting.
PMsrHighlighter *highlight = new PMsrHighlighter(this->document());
}
//----------------------------------------------------------------------------------------------------