commit 9e2c8796a3761177a76200ee7029e34743316e37
Author: Juergen Spitzmueller <[email protected]>
Date:   Fri Oct 10 11:49:28 2025 +0200

    Bring InsetMathRef on par with InsetRef (#13179)
    
    File format change.
---
 development/FORMAT              |   5 +
 lib/lyx2lyx/lyx_2_6.py          | 338 +++++++++++++++++++++++++++++++++-
 src/frontends/qt/GuiRef.cpp     |  13 +-
 src/mathed/InsetMathCommand.cpp |   4 +-
 src/mathed/InsetMathCommand.h   |   2 +-
 src/mathed/InsetMathRef.cpp     | 389 +++++++++++++++++++++++++++++++---------
 src/mathed/InsetMathRef.h       |   4 +
 src/mathed/MathFactory.cpp      |  17 +-
 src/mathed/MathParser.cpp       |  11 +-
 src/version.h                   |   4 +-
 10 files changed, 680 insertions(+), 107 deletions(-)

diff --git a/development/FORMAT b/development/FORMAT
index 504628ea42..1900f2fce4 100644
--- a/development/FORMAT
+++ b/development/FORMAT
@@ -7,6 +7,11 @@ changes happened in particular if possible. A good example 
would be
 
 -----------------------
 
+2025-10-10 Jürgen Spitzmüller <[email protected]>
+       * Format incremented to 645: Support options and params
+         in InsetMathRef. Syntax:
+         \cmd[options]{labels}[features]
+
 2025-10-05 Jürgen Spitzmüller <[email protected]>
        * Format incremented to 644:
         - Support formatted cross references for examples
diff --git a/lib/lyx2lyx/lyx_2_6.py b/lib/lyx2lyx/lyx_2_6.py
index 8f184b8ee4..cc94ab147f 100644
--- a/lib/lyx2lyx/lyx_2_6.py
+++ b/lib/lyx2lyx/lyx_2_6.py
@@ -237,17 +237,353 @@ def convert_refname(document):
         i = j
 
 
+def revert_mathref(document):
+    "Revert advanced formatted refs to LaTeX"
+
+    package = "refstyle"
+    i = find_token(document.header, "\\crossref_package", 0)
+    if i == -1:
+        document.warning("Missing \\crossref_package header!")
+    else:
+        package = get_value(document.header, "\\crossref_package", i)
+
+    regexp = 
re.compile(r"(.*\\)(ref|pageref|cpageref|vref|vpageref|formatted|prettyref|eqref|nameref|labelonly)(\[|\{)(.*)")
+    # \cmd[opt]{arg}[opt]
+    reg_opt1 = 
re.compile(r"(.*\\)(ref|pageref|cpageref|vref|vpageref|formatted|prettyref|eqref|nameref|labelonly)(\[[^\]]+\])(\{[^\}]+\})(\[[^\]]+\])(.*)")
+    # \cmd[opt]{arg}
+    reg_opt2 = 
re.compile(r"(.*\\)(ref|pageref|cpageref|vref|vpageref|formatted|prettyref|eqref|nameref|labelonly)(\[[^\]]\])(\{[^\}]+\})(.*)")
+    # \cmd{arg}[opt]
+    reg_opt3 = 
re.compile(r"(.*\\)(ref|pageref|cpageref|vref|vpageref|formatted|prettyref|eqref|nameref|labelonly)(\{[^\}]+\})(\[[^\]]+\])(.*)")
+    need_zref_clever = False
+    need_zref_vario = False
+    need_cleveref = False
+    need_refstyle = False
+    need_varioref = False
+    i = 0
+    while True:
+        i = find_token(document.body, "\\begin_inset Formula", i)
+        if i == -1:
+            break
+        j = find_end_of_inset(document.body, i)
+        if j == -1:
+            document.warning("Can't find end of inset at line %d of body!" % i)
+            i += 1
+            continue
+        k = find_re(document.body, regexp, i, j)
+        if k == -1:
+            i = j
+            continue
+        
+        pretext = ""
+        posttext = ""
+        cmd = ""
+        opt = ""
+        arg = ""
+        features = []
+        l = i
+        while True:
+            m = reg_opt1.match(document.body[k])
+            if m:
+                pretext = m.group(1)
+                cmd = m.group(2)
+                opt = m.group(3)
+                arg = m.group(4)
+                features = m.group(5)[1:-1].split(",")
+                posttext = m.group(6)
+            else:
+                m = reg_opt2.match(document.body[k])
+                if m:
+                    pretext = m.group(1)
+                    cmd = m.group(2)
+                    opt = m.group(3)
+                    arg = m.group(4)
+                    posttext = m.group(5)
+                else:
+                    m = reg_opt3.match(document.body[k])
+                    if m:
+                        pretext = m.group(1)
+                        cmd = m.group(2)
+                        arg = m.group(3)
+                        features = m.group(4)[1:-1].split(",")
+                        posttext = m.group(5)
+                    else:
+                        l += 1
+                        k = find_re(document.body, regexp, l, j)
+                        if k == -1 or l == j:
+                            i = j
+                            break
+
+            arguments = arg[1:-1].split(",")
+            
+            use_nolink = "nolink" in features and find_token(document.header, 
"\\use_hyperref true", 0) != -1
+
+            # labelonly
+            if cmd == "labelonly":
+                # strip prefixes if requested
+                if "noprefix" in features:
+                    npargs = []
+                    for argu in arguments:
+                        if ":" in argu:
+                            npargs.append(argu.split(":", 1)[1])
+                        else:
+                            npargs.append(argu)
+                    arg = "{" + ",".join(npargs) + "}"
+                document.body[k] = pretext + cmd + arg + posttext
+                continue
+
+            use_range = False
+            if len(arguments) == 2 and (cmd == "vref" or cmd == "vpageref") 
and package != "zref":
+                use_range = True
+            elif len(arguments) != 2 or "range" not in features:
+                use_range = False
+            elif cmd == "vref" or cmd == "vpageref" or cmd == "cpageref" or 
(cmd == "formatted" and  "prettyref" not in package):
+                use_range = True
+            
+            if cmd == "vref" or cmd == "vpageref":
+                if not use_range and "nolink" not in features and (package != 
"zref" or ("caps" not in features and opt == "")):
+                    document.body[k] = pretext + cmd + arg + posttext
+                    continue
+                if package == "zref":
+                    cmd = "z" + cmd
+                    need_zref_vario = True
+                else:
+                    need_varioref = True
+                if use_range:
+                    cmd += "range"
+                if use_nolink:
+                    cmd += "*"
+                realopt = []
+                if package == "zref":
+                    realopt = opt[1:-1].split(",")
+                    if "caps" in features:
+                        realopt.append("S")
+                    realopt = list(filter(None, realopt))
+                    if len(realopt) > 0:
+                        cmd += "[" + ",".join(realopt) + "]"
+                first = True
+                cmd += "{"
+                for argu in arguments:
+                    if not first:
+                        if use_range:
+                            cmd += "}{"
+                        else:
+                            cmd += ","
+                    first = False
+                    cmd += argu
+                cmd += "}"
+                document.body[k] = pretext + cmd + posttext
+                continue
+
+            # cpageref
+            if cmd == "cpageref":
+                if package == "cleveref":
+                    if not use_range and "caps" not in features:
+                        # just remove the opts
+                        document.body[k] = pretext + cmd + arg + posttext
+                        continue
+                    need_cleveref = True
+                    if "caps" in features:
+                        cmd = cmd.title()
+                    first = True
+                    cmd += "{"
+                    for argu in arguments:
+                        if not first:
+                            if use_range:
+                                cmd += "}{"
+                            else:
+                                cmd += ","
+                            first = False
+                        cmd += argu
+                    cmd += "}"
+                    document.body[k] = pretext + cmd + posttext
+                    continue
+                if package == "zref":
+                    if not use_range and "caps" not in features and not 
use_nolink:
+                        # just remove the opts
+                        document.body[k] = pretext + cmd + arg + posttext
+                        continue
+                    need_zref_clever = True
+                    cmd = "z" + cmd
+                    if use_nolink:
+                        cmd += "*"
+                    realopt = opt[1:-1].split(",")
+                    if "caps" in features:
+                        realopt.append("S")
+                    if use_range:
+                        realopt.append("range")
+                    realopt = list(filter(None, realopt))
+                    if len(realopt) > 0:
+                        cmd += "[" + ",".join(realopt) + "]"
+                    first = True
+                    cmd += "{"
+                    for argu in arguments:
+                        if not first:
+                            if use_range:
+                                cmd += "}{"
+                            else:
+                                cmd += ","
+                            first = False
+                        cmd += argu
+                    cmd += "}"
+                    document.body[k] = pretext + cmd + posttext
+                    continue
+                else:
+                    if "nolink" in features:
+                        cmd = "pageref*"
+                    document.body[k] = pretext + cmd + arg + posttext
+                    continue
+
+            if len(arguments) > 1 and package == "cleveref":
+                if cmd == "ref":
+                    need_cleveref = True
+                    document.body[k] = pretext + "labelcref" + arg + posttext
+                    continue
+                if cmd == "pageref":
+                    need_cleveref = True
+                    document.body[k] = pretext + "labelcpageref" + arg + 
posttext
+                    continue
+
+            if len(arguments) > 1 and package == "zref":
+                if cmd == "ref" or cmd == "pageref":
+                    need_zref_clever = True
+                    cmd = "zcref"
+                    if use_nolink:
+                        cmd += "*"
+                    realopt = opt[1:-1].split(",")
+                    if "caps" in features:
+                        realopt.append("noname")
+                        if cmd == "pageref":
+                            realopt.append("page")
+                    realopt = list(filter(None, realopt))
+                    if len(realopt) > 0:
+                        cmd += "[" + ",".join(realopt) + "]"
+                    document.body[k] = pretext + cmd + arg + posttext
+                    continue
+
+            # formatted
+            if cmd == "formatted":
+                # prettyref
+                if "prettyref" in package:
+                    cmd = "prettyref"
+                # refstyle
+                if package == "refstyle":
+                    if len(arguments) == 1 and not "plural" in features and 
not "caps" in features:
+                        document.body[k] = pretext + cmd + arg + posttext
+                        continue
+                    need_refstyle = True
+                    npargs = []
+                    prfx = ""
+                    for argu in arguments:
+                        document.warning("argu: %s" % argu)
+                        if ":" in argu:
+                            npargs.append(argu.split(":", 1)[1])
+                            if prfx == "":
+                                prfx = argu.split(":", 1)[0]
+                        else:
+                            npargs.append(argu)
+                    cmd = prfx + "ref"
+                    if "caps" in features:
+                        cmd = cmd.title()
+                    if "plural" in features:
+                        cmd += "[s]"
+                    arg = "{" + ",".join(npargs) + "}"
+                    document.body[k] = pretext + cmd + arg + posttext
+                    continue
+                # cleveref
+                if package == "cleveref":
+                    if len(arguments) == 1 and not "caps" in features:
+                        document.body[k] = pretext + cmd + arg + posttext
+                        continue
+                    need_cleveref = True
+                    if "caps" in features:
+                        cmd = "Cref"
+                    else:
+                        cmd = "cref"
+                    if use_range:
+                        cmd += "range"
+                    if use_nolink:
+                        cmd += "*"
+                    document.body[k] = pretext + cmd + arg + posttext
+                    continue
+                # zref
+                if package == "zref":
+                    if len(arguments) == 1 and not "caps" in features:
+                        document.body[k] = pretext + cmd + arg + posttext
+                        continue
+                    need_zref_clever = True
+                    cmd = "zcref"
+                    realopt = opt[1:-1].split(",")
+                    if "caps" in features:
+                        realopt.append("S")
+                    if use_range:
+                        realopt.append("range") 
+                    if use_nolink:
+                        cmd += "*"
+                    realopt = list(filter(None, realopt))
+                    if len(realopt) > 0:
+                        cmd += "[" + ",".join(realopt) + "]"
+                    document.body[k] = pretext + cmd + arg + posttext
+                    continue
+
+            # Rest (\ref, \eqref, \prettyref
+            if len(arguments) > 1:
+                cmds = []
+                first = True
+                for argu in arguments:
+                    if first:
+                        cmds.append(cmd + "{" + argu + "}")
+                        first = False
+                    else:
+                        cmds.append("\\" + cmd + "{" + argu + "}")
+                cmd = ", ".join(cmds)
+                document.body[k] = pretext + cmd + posttext
+                continue
+            # remove the opts
+            document.body[k] = pretext + cmd + arg + posttext
+            continue
+
+        i = j
+
+    # preamble
+    if need_zref_clever:
+        add_to_preamble(
+            document,
+            ["\\usepackage{zref-clever}"]
+        )
+    if need_zref_vario:
+        add_to_preamble(
+            document,
+            ["\\usepackage{zref-vario}"]
+        )
+    if need_cleveref:
+        add_to_preamble(
+            document,
+            ["\\usepackage{cleveref}"]
+        )
+    if need_varioref:
+        add_to_preamble(
+            document,
+            ["\\usepackage{varioref}"]
+        )
+    if need_refstyle:
+        add_to_preamble(
+            document,
+            ["\\usepackage{refstyle}"]
+        )
+
 ##
 # Conversion hub
 #
 
 supported_versions = ["2.6.0", "2.6"]
 convert = [
-    [644, [convert_refname]]
+    [644, [convert_refname]],
+    [645, []]
 ]
 
 
 revert = [
+    [644, [revert_mathref]],
     [643, [revert_ling_xrefs]]
 ]
 
diff --git a/src/frontends/qt/GuiRef.cpp b/src/frontends/qt/GuiRef.cpp
index 63b7332ce0..ffaa916470 100644
--- a/src/frontends/qt/GuiRef.cpp
+++ b/src/frontends/qt/GuiRef.cpp
@@ -176,18 +176,17 @@ void GuiRef::enableBoxes()
        bool const allow_caps = use_refstyle || use_cleveref || use_zref;
        bool const allow_nohyper = !isLabelOnly && (!isFormatted || 
use_cleveref || use_zref)
                        && (reftype != "cpageref" || use_zref);
-       bool const intext = bufferview()->cursor().inTexted();
-       pluralCB->setEnabled(intext && isFormatted && allow_plural);
-       capsCB->setEnabled(intext && (isFormatted || zref_clever || reftype == 
"cpageref")
+       pluralCB->setEnabled(isFormatted && allow_plural);
+       capsCB->setEnabled((isFormatted || zref_clever || reftype == "cpageref")
                           && allow_caps);
-       noprefixCB->setEnabled(intext && isLabelOnly);
+       noprefixCB->setEnabled(isLabelOnly);
        // disabling of hyperlinks not supported by formatted references
-       nolinkCB->setEnabled(hyper_on && intext && allow_nohyper);
+       nolinkCB->setEnabled(hyper_on && allow_nohyper);
        // options only supported by zref currently
        refOptionsLE->setEnabled(use_zref && (isFormatted || zref_clever));
        refOptionsLA->setEnabled(use_zref && (isFormatted || zref_clever));
        bool const allow_range_list_switch = selectedLV->topLevelItemCount() == 
2
-               && (isFormatted || reftype == "cpageref") && !use_prettyref && 
intext;
+               && (isFormatted || reftype == "cpageref") && !use_prettyref;
        if (reftype == "vref" || reftype == "vpageref")
                rangeListCO->setCurrentIndex(rangeListCO->findData("range"));
        rangeListCO->setEnabled(allow_range_list_switch);
@@ -572,8 +571,6 @@ void GuiRef::updateContents()
 
        typeCO->clear();
 
-       // FIXME Bring InsetMathRef on par with InsetRef
-       // (see #11104)
        bool const have_cpageref =
                        buffer().params().xref_package == "cleveref"
                        || buffer().params().xref_package == "zref";
diff --git a/src/mathed/InsetMathCommand.cpp b/src/mathed/InsetMathCommand.cpp
index dcebf2d9a5..e1e7a2ff54 100644
--- a/src/mathed/InsetMathCommand.cpp
+++ b/src/mathed/InsetMathCommand.cpp
@@ -21,8 +21,8 @@ using namespace std;
 namespace lyx {
 
 
-InsetMathCommand::InsetMathCommand(Buffer * buf, docstring const & name, bool 
needs_math_mode)
-       : InsetMathNest(buf, 2), name_(name), needs_math_mode_(needs_math_mode),
+InsetMathCommand::InsetMathCommand(Buffer * buf, docstring const & name, bool 
needs_math_mode, idx_type ncells)
+       : InsetMathNest(buf, ncells), name_(name), 
needs_math_mode_(needs_math_mode),
          set_label_(false)
 {
        lock_ = true;
diff --git a/src/mathed/InsetMathCommand.h b/src/mathed/InsetMathCommand.h
index 2a0c0dfa25..b93de4390c 100644
--- a/src/mathed/InsetMathCommand.h
+++ b/src/mathed/InsetMathCommand.h
@@ -26,7 +26,7 @@ class InsetMathCommand : public InsetMathNest {
 public:
        ///
        explicit InsetMathCommand(Buffer * buf, docstring const & name,
-               bool needs_math_mode = true);
+               bool needs_math_mode = true, idx_type ncells = 2);
        ///
        marker_type marker(BufferView const *) const override { return 
marker_type::NO_MARKER; }
        ///
diff --git a/src/mathed/InsetMathRef.cpp b/src/mathed/InsetMathRef.cpp
index edf10365ea..9d7a05a848 100644
--- a/src/mathed/InsetMathRef.cpp
+++ b/src/mathed/InsetMathRef.cpp
@@ -25,6 +25,8 @@
 #include "MathStream.h"
 #include "MathSupport.h"
 #include "ParIterator.h"
+#include "PDFOptions.h"
+#include "frontends/alert.h"
 #include "xml.h"
 
 #include "insets/InsetCommand.h"
@@ -37,16 +39,17 @@
 #include <ostream>
 
 using namespace std;
+using namespace lyx::support;
 
 namespace lyx {
 
 InsetMathRef::InsetMathRef(Buffer * buf)
-       : InsetMathCommand(buf, from_ascii("ref"), false)
+       : InsetMathCommand(buf, from_ascii("ref"), false, 3)
 {}
 
 
 InsetMathRef::InsetMathRef(Buffer * buf, docstring const & data)
-       : InsetMathCommand(buf, data, false)
+       : InsetMathCommand(buf, data, false, 3)
 {}
 
 
@@ -262,6 +265,18 @@ string const InsetMathRef::createDialogStr(string const & 
type) const
        InsetCommandParams icp(REF_CODE, (type.empty()
                        ?  to_ascii(commandname()) : type));
        icp["reference"] = asString(cell(0));
+       if (!cell(1).empty())
+               icp["options"] = asString(cell(1));
+       if (hasFeature("plural"))
+               icp["plural"] = from_ascii("true");
+       if (hasFeature("caps"))
+               icp["caps"] = from_ascii("true");
+       if (hasFeature("noprefix"))
+               icp["noprefix"] = from_ascii("true");
+       if (hasFeature("nolink"))
+               icp["nolink"] = from_ascii("true");
+       if (hasFeature("range"))
+               icp["tuple"] = from_ascii("range");
        return InsetCommand::params2string(icp);
 }
 
@@ -272,10 +287,33 @@ docstring const InsetMathRef::getTarget() const
 }
 
 
+bool InsetMathRef::hasFeature(string const & string) const
+{
+       vector<docstring> const features = 
getVectorFromString(asString(cell(2)));
+       for (auto const & f : features) {
+               if (from_ascii(string) == f)
+                       return true;
+       }
+       return false;
+}
+
+
 void InsetMathRef::changeTarget(docstring const & target)
 {
        InsetCommandParams icp(REF_CODE, to_ascii(commandname()));
        icp["reference"] = target;
+       if (!cell(1).empty())
+               icp["options"] = asString(cell(1));
+       if (hasFeature("plural"))
+               icp["plural"] = from_ascii("true");
+       if (hasFeature("caps"))
+               icp["caps"] = from_ascii("true");
+       if (hasFeature("noprefix"))
+               icp["noprefix"] = from_ascii("true");
+       if (hasFeature("nolink"))
+               icp["nolink"] = from_ascii("true");
+       if (hasFeature("range"))
+               icp["tuple"] = from_ascii("range");
        MathData md(buffer_);
        Buffer & buf = buffer();
        if (createInsetMath_fromDialogStr(
@@ -287,6 +325,21 @@ void InsetMathRef::changeTarget(docstring const & target)
 }
 
 
+bool InsetMathRef::useRange() const
+{
+       docstring const & cmd = commandname();
+       vector<docstring> const refs = getVectorFromString(asString(cell(0)));
+       if (refs.size() == 2 && (cmd == "vref" || cmd == "vpageref")
+           && buffer().masterParams().xref_package != "zref")
+               return true;
+       if (refs.size() != 2 || !hasFeature("range"))
+               return false;
+       return cmd == "vref" || cmd == "vpageref"
+               || (cmd == "formatted" && 
!prefixIs(buffer().masterParams().xref_package, "prettyref"))
+               || (cmd == "cpageref");
+}
+
+
 void InsetMathRef::writeMath(TeXMathStream & os) const
 {
        docstring const & cmd = commandname();
@@ -295,112 +348,270 @@ void InsetMathRef::writeMath(TeXMathStream & os) const
                LYXERR0("Unassigned buffer_ in InsetMathRef::write!");
                LYXERR0("LaTeX output may be wrong!");
        }
-       // are we writing to the LyX file?
        if (!os.latex()) {
-               // if so, then this is easy
-               InsetMathCommand::writeMath(os);
+               // we are writing to the LyX file
+               ModeSpecifier specifier(os, currentMode(), lockedMode(), 
asciiOnly());
+               MathEnsurer ensurer(os, false);
+               os << '\\' << cmd;
+               if (!cell(1).empty())
+                       os << '[' << cell(1) << ']';
+               os << '{' << cell(0) << '}';
+               if (!cell(2).empty())
+                       os << '[' << cell(2) << ']';
                return;
        }
+       bool const use_prettyref =
+               prefixIs(buffer().masterParams().xref_package, "prettyref");
        bool const use_refstyle =
                buffer_ && buffer().params().xref_package == "refstyle";
-       bool special_case =  cmd == "formatted" ||
-                       cmd == "vref" ||
-                       cmd == "vpageref" ||
-                       cmd == "cpageref" ||
-                       cmd == "labelonly" ||
-                       (cmd == "eqref" && use_refstyle);
-       // we need to translate 'formatted' to prettyref or refstyle-type
-       // commands and just output the label with labelonly
+       bool const use_cleveref = buffer().masterParams().xref_package == 
"cleveref";
+       bool const use_zref = buffer().masterParams().xref_package == "zref";
+       vector<docstring> labels = getVectorFromString(asString(cell(0)));
+       int const nlabels = labels.size();
+       bool const use_nolink = 
buffer().masterParams().pdfoptions().use_hyperref && hasFeature("nolink");
+       // we need to translate 'formatted' to the appropriate commands
+       // (depending on xref package) and just output the label with labelonly
        // most of this is borrowed from InsetRef and should be kept in 
        // sync with that.
        ModeSpecifier specifier(os, currentMode(), lockedMode(), asciiOnly());
        MathEnsurer ensurer(os, false);
-       if (!special_case) {
-               os << from_ascii("\\") << cmd << "{" << cell(0) << 
from_ascii("}");
-       }
-       else if (cmd == "vref" || cmd == "vpageref") {
-               os << from_ascii("\\");
-               if (buffer_ && buffer().params().xref_package == "zref")
+       if (cmd == "vref" || cmd == "vpageref") {
+               os << "\\";
+               if (use_zref)
                        os << "z";
-               os << cmd << "{" << cell(0) << from_ascii("}");
-       }
-       else if (cmd == "cpageref") {
-               os << from_ascii("\\");
-               if (buffer_ && buffer().params().xref_package == "zref")
-                       os << "z";
-               if (buffer_ && (buffer().params().xref_package == "cleveref"
-                   || buffer().params().xref_package == "zref"))
-                       os << cmd;
+               os << cmd;
+               if (useRange())
+                       os << "range";
+               if (use_nolink)
+                       os << "*";
+               docstring opts = asString(cell(1));
+               if (use_zref && hasFeature("caps")) {
+                       if (!opts.empty())
+                               opts +=", ";
+                       opts += "S";
+               }
+               if (use_zref && !opts.empty())
+                       os << "[" << opts << "]";
+               bool first = true;
+               os << "{";
+               for (auto const & l : labels) {
+                       if (!first) {
+                               if (useRange())
+                                       os << "}{";
+                               else
+                                       os << ",";
+                       }
+                       os << l;
+                       first = false;
+               }
+               os << "}";
+       } else if (cmd == "cpageref" && use_cleveref) {
+               if (hasFeature("caps"))
+                       os << "\\Cpageref";
                else
-                       os << "pageref";
-               os << "{" << cell(0) << from_ascii("}");
-       }
-       else if (use_refstyle && cmd == "eqref") {
+                       os << "\\cpageref";
+               if (useRange())
+                       os << "range";
+               bool first = true;
+               os << "{";
+               for (auto const & l : labels) {
+                       if (!first) {
+                               if (useRange())
+                                       os << "}{";
+                               else
+                                       os << ",";
+                       }
+                       os << l;
+                       first = false;
+               }
+               os << "}";
+       } else if (cmd == "cpageref" && use_zref) {
+               os << "\\zcpageref";
+               if (use_nolink)
+                       os << "*";
+               docstring opts = asString(cell(1));
+               if (hasFeature("caps")) {
+                       if (!opts.empty())
+                               opts +=", ";
+                       opts += "S";
+               }
+               if (useRange()) {
+                       if (!opts.empty())
+                               opts +=", ";
+                       opts += "range";
+               }
+               if (!opts.empty())
+                       os << "[" << opts << "]";
+               bool first = true;
+               os << "{";
+               for (auto const & l : labels) {
+                       if (!first)
+                               os << ",";
+                       os << l;
+                       first = false;
+               }
+               os << "}";
+       } else if (cmd == "cpageref") {
+               bool first = true;
+               for (auto const & label : labels) {
+                       if (!first)
+                               os << ", ";
+                       os << "\\pageref";
+                       if (use_nolink)
+                               os << "*";
+                       os << '{' << label << '}';
+                       first = false;
+               }
+       } else if (nlabels > 1 && cmd == "ref" && use_cleveref) {
+               os << "\\labelcref" << '{' << cell(0) << '}';
+       } else if (nlabels > 1 && cmd == "pageref" && use_cleveref) {
+               os << "\\labelcpageref" << '{' << cell(0) << '}';
+       } else if (use_refstyle && cmd == "eqref") {
                // we advertise this as printing "(n)", so we'll do that, at 
least
                // for refstyle, since refstlye's own \eqref prints, by default,
                // "equation n". if one wants \eqref, one can get it by using a
                // formatted label in this case.
-               os << '(' << from_ascii("\\ref{") << cell(0) << 
from_ascii("})");
-       }
-       else if (cmd == "formatted") {
-               if (buffer_ && 
support::prefixIs(buffer().params().xref_package, "prettyref"))
-                       os << "\\prettyref{" << cell(0) << "}";
-               else {
-                       odocstringstream ods;
-                       // get the label we are referencing
-                       for (auto const & d : cell(0)) {
-                               ods << d;
+               os << '(' << from_ascii("\\ref");
+               if (use_nolink)
+                       os << "*";
+               os << "{" << cell(0) << from_ascii("})");
+       } else if (cmd == "formatted") {
+               odocstringstream ods;
+               // get the label we are referencing
+               for (auto const & d : cell(0))
+                       ods << d;
+               docstring const ref = ods.str();
+
+               vector<docstring> lbls;
+               docstring prefix;
+               docstring const fcmd =
+                       InsetRef::getFormattedCmd(ref, lbls, prefix,
+                                                 
buffer().params().xref_package,
+                                                 hasFeature("caps"), 
useRange());
+               os << fcmd;
+               if (use_nolink && (use_cleveref || use_zref))
+                       os << "*";
+               if (hasFeature("plural") && use_refstyle)
+                       os << "[s]";
+               else if (use_zref) {
+                       docstring opts = asString(cell(1));
+                       if (hasFeature("caps")) {
+                               if (!opts.empty())
+                                       opts +=", ";
+                               opts += "S";
+                       }
+                       if (useRange()) {
+                               if (!opts.empty())
+                                       opts +=", ";
+                               opts += "range";
+                       }
+                       if (!opts.empty())
+                               os << "[" << opts << "]";
+               }
+               bool first = true;
+               os << "{";
+               vector<docstring>::const_iterator it = lbls.begin();
+               vector<docstring>::const_iterator en = lbls.end();
+               for (size_t i = 0; it != en; ++it, ++i) {
+                       if (!first) {
+                               if (use_prettyref) {
+                                       os << "}";
+                                       if (lbls.size() == 2)
+                                               os << buffer().B_("[[reference 
1]] and [[reference2]]");
+                                       else if (i > 0 && i == lbls.size() - 1)
+                                               os << buffer().B_("[[reference 
1, ...]], and [[reference n]]");
+                                       else
+                                               os << buffer().B_("[[reference 
1]], [[reference2, ...]]");
+                                       os << "\\ref{";
+                               } else if (useRange() && !use_zref)
+                                       os << "}{";
+                               else
+                                       os << ",";
+                       }
+                       if (::contains(*it, ' ') && !useRange() && use_refstyle
+                           && 
buffer().masterParams().isRefStyleSupported(prefix))
+                               // refstyle bug: labels with blanks need to be 
grouped for known commands
+                               // otherwise the blanks will be gobbled
+                               os << "{" << *it << "}";
+                       else {
+                               if (use_prettyref && !::contains(*it, ':'))
+                                       // warn on invalid label
+                                       frontend::Alert::warning(_("Invalid 
label!"),
+                                                                bformat(_("The 
label `%1$s' does not have a prefix (e.g., `sec:'), "
+                                                                          
"which is needed for formatted references with prettyref.\n"
+                                                                          "You 
will most likely run into a LaTeX error."),
+                                                                        *it), 
true);
+                               os << *it;
                        }
-                       docstring const ref = ods.str();
-
-                       /*
-                       At the moment, the 'plural', 'nolink' and 'caps' 
options will
-                       not work here. Also ranges. The reason is that we 
handle these as
-                       'internal' LyX argumemts, but those are not handled by
-                       InsetCommandParams::getCommand, which is what is used
-                       in createInsetMath_fromDialogStr to interpret the data
-                       coming from the dialog.
-                       If this is fixed, then what follows will get the info
-                       we need.
-                       Fixing it, I think, would mean sub-classing
-                       InsetCommandParams to InsetRefParams, and the overriding
-                       getCommand.
-                       *******************************************************
-                       // reset
-                       ods.str(docstring());
-                       ods.clear();
-                       // get the options from the optional argument
-                       for (auto const & d : cell(1))
-                               ods << d;
-                       docstring const options = ods.str();
-                       bool const caps   = support::contains(options, 'C');
-                       bool const plural = support::contains(options, 's');
-                       */
-                       vector<docstring> label;
+                       first = false;
+               }
+               os << "}";
+       } else if (cmd == "labelonly") {
+               if (!hasFeature("noprefix"))
+                       os << cell(0);
+               else {
                        docstring prefix;
-                       docstring const fcmd =
-                               InsetRef::getFormattedCmd(ref, label, prefix, 
buffer().params().xref_package);
-                       os << fcmd;
-                       //if (plural)
-                       //      os << "[s]";
-                       bool first = true;
-                       os << "{";
-                       for (auto const & l : label) {
-                               if (!first) {
-//                                     if (useRange())
-//                                             os << "}{";
-//                                     else
-                                               os << ",";
-                               }
-                               os << l;
-                               first = false;
+                       vector <docstring> slrefs;
+                       for (auto const & r : labels) {
+                               docstring suffix = split(r, prefix, ':');
+                               if (suffix.empty()) {
+                                       LYXERR0("Label `" << r << "' contains 
no `:' separator.");
+                                       slrefs.push_back(r);
+                               } else
+                                       slrefs.push_back(suffix);
                        }
-                       os << "}";
+                       os << getStringFromVector(slrefs);
+               }
+       } else if (nlabels > 1 && cmd == "ref" && use_zref) {
+               os << "\\zcref";
+               if (use_nolink)
+                       os << "*";
+               docstring opts = asString(cell(1));
+               if (hasFeature("caps")) {
+                       if (!opts.empty())
+                               opts +=", ";
+                       opts += "noname";
+               }
+               if (use_zref && !opts.empty())
+                       os << "[" << opts << "]";
+               os << '{' << cell(0) << '}';
+       } else if (nlabels > 1 && cmd == "pageref" && use_zref) {
+               os << "\\zcref";
+               if (use_nolink)
+                       os << "*";
+               docstring opts = asString(cell(1));
+               if (hasFeature("caps")) {
+                       if (!opts.empty())
+                               opts +=", ";
+                       opts += "noname, page";
+               }
+               if (!opts.empty())
+                       os << "[" << opts << "]";
+               os << '{' << cell(0) << '}';
+       } else if (nlabels == 1) {
+               os << "\\" << cmd;
+               if (use_nolink)
+                       os << "*";
+               os << "{" << cell(0) << "}";
+       } else {
+               bool first = true;
+               vector<docstring>::const_iterator it = labels.begin();
+               vector<docstring>::const_iterator en = labels.end();
+               for (size_t i = 0; it != en; ++it, ++i) {
+                       if (!first) {
+                               if (labels.size() == 2)
+                                       os << buffer().B_("[[reference 1]] and 
[[reference2]]");
+                               else if (i > 0 && i == labels.size() - 1)
+                                       os << buffer().B_("[[reference 1, 
...]], and [[reference n]]");
+                               else
+                                       os << buffer().B_("[[reference 1]], 
[[reference2, ...]]");
+                       }
+                       os << "\\" << cmd;
+                       if (use_nolink)
+                               os << "*";
+                       os << '{' << *it << '}';
+                       first = false;
                }
-       }
-       else if (cmd == "labelonly") {
-               // noprefix does not work here, for reasons given above.
-               os << cell(0);
        }
 }
 
diff --git a/src/mathed/InsetMathRef.h b/src/mathed/InsetMathRef.h
index 23e3e8fdff..8cd04ac208 100644
--- a/src/mathed/InsetMathRef.h
+++ b/src/mathed/InsetMathRef.h
@@ -78,6 +78,10 @@ protected:
        ///
        bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const 
override;
 private:
+       ///
+       bool hasFeature(std::string const & string) const;
+       ///
+       bool useRange() const;
        ///
        Inset * clone() const override;
 };
diff --git a/src/mathed/MathFactory.cpp b/src/mathed/MathFactory.cpp
index 1f37fd849e..5c24158f38 100644
--- a/src/mathed/MathFactory.cpp
+++ b/src/mathed/MathFactory.cpp
@@ -863,7 +863,22 @@ bool createInsetMath_fromDialogStr(docstring const & str, 
MathData & md)
                InsetCommand::string2params(to_utf8(str), icp);
                Encoding const * const utf8 = encodings.fromLyXName("utf8");
                OutputParams op(utf8);
-               mathed_parse_cell(md, icp.getCommand(op, false, true));
+               odocstringstream ods;
+               ods << icp.getCommand(op, false, true);
+               vector<docstring> features;
+               if (icp["plural"] == from_ascii("true"))
+                       features.push_back(from_ascii("plural"));
+               if (icp["caps"] == from_ascii("true"))
+                       features.push_back(from_ascii("caps"));
+               if (icp["noprefix"] == from_ascii("true"))
+                       features.push_back(from_ascii("noprefix"));
+               if (icp["nolink"] == from_ascii("true"))
+                       features.push_back(from_ascii("nolink"));
+               if (icp["tuple"] != from_ascii("list"))
+                       features.push_back(from_ascii("range"));
+               if (!features.empty())
+                       ods << "[" << getStringFromVector(features) << "]";
+               mathed_parse_cell(md, ods.str());
        } else if (name == "mathspace") {
                InsetSpaceParams isp(true);
                InsetSpace::string2params(to_utf8(str), isp);
diff --git a/src/mathed/MathParser.cpp b/src/mathed/MathParser.cpp
index 24ccae6957..99ee7d5d6a 100644
--- a/src/mathed/MathParser.cpp
+++ b/src/mathed/MathParser.cpp
@@ -1574,11 +1574,16 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned 
flags,
                          || t.cs() == "vpageref" || t.cs() == "vref" 
                          || t.cs() == "formatted" || t.cs() == "labelonly") {
                        cell->emplace_back(new InsetMathRef(buf, t.cs()));
-                       docstring const opt = parse_verbatim_option();
+                       docstring const opt1 = parse_verbatim_option();
                        docstring const ref = parse_verbatim_item();
-                       if (!opt.empty()) {
+                       docstring const opt2 = parse_verbatim_option();
+                       if (!opt1.empty()) {
                                cell->back().nucleus()->cell(1).push_back(
-                                       MathAtom(new InsetMathString(buf, 
opt)));
+                                       MathAtom(new InsetMathString(buf, 
opt1)));
+                       }
+                       if (!opt2.empty()) {
+                               cell->back().nucleus()->cell(2).push_back(
+                                       MathAtom(new InsetMathString(buf, 
opt2)));
                        }
                        cell->back().nucleus()->cell(0).push_back(
                                        MathAtom(new InsetMathString(buf, 
ref)));
diff --git a/src/version.h b/src/version.h
index 9bb07d31ba..0f833454ad 100644
--- a/src/version.h
+++ b/src/version.h
@@ -32,8 +32,8 @@ extern char const * const lyx_version_info;
 
 // Do not remove the comment below, so we get merge conflict in
 // independent branches. Instead add your own.
-#define LYX_FORMAT_LYX 644 // spitz: linguistics xrefs
-#define LYX_FORMAT_TEX2LYX 644
+#define LYX_FORMAT_LYX 645 // spitz: math ref features
+#define LYX_FORMAT_TEX2LYX 645
 
 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX
 #ifndef _MSC_VER
-- 
lyx-cvs mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to