mathjax / mathjax/MathJax

ifstar, ifnextchar

Open
#2,428 18 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Code Example Feature Request v3
Dominant language
JavaScript
Stars
10.9k
Forks
1.2k
PR merge metrics
No merged PRs in 30d

Description

To ease the creation of starred TeX macros at the user level, it would be nice to include a TeX macro similar to @ifstar. If possible @ifnextchar as well.

Attached is a working first-draft v3 solution for \ifstar (without the ampersand) based an example I found for custom-delimited macros. (I'm sure it could be improved.) Something similar would be a useful permanent addition to MathJax.

<script>
MathJax = {
  startup: {
    ready() {
      //
      //  These would be replaced by import commands if you wanted to make 
      //  a proper extension.  These are are used here for convenience
      //  just to get it running.
      //
      const Configuration = MathJax._.input.tex.Configuration.Configuration;
      const CommandMap = MathJax._.input.tex.SymbolMap.CommandMap;
      const Macro = MathJax._.input.tex.Symbol.Macro;
      const TexError = MathJax._.input.tex.TexError.default;
      const ParseUtil = MathJax._.input.tex.ParseUtil.default;
      const expandable = MathJax._.util.Options.expandable;
      
      //
      //  This is the name of the command map
      //
      const IFSTARMAP = 'ifstarCoreMap';

      const ifstarDelimiters = (parser, name) => {
        //
        //  Get the macro parameters
        //
        const star = parser.GetStar();          // true if there is a *
//        const optBrackets = parser.GetBrackets(name);   // contents of optional brackets
        const resultstar = parser.GetArgument(name);  // the math to go inside the delimiters
        const resultnostar = parser.GetArgument(name);  // the math to go inside the delimiters
        //
        //  Construct the replacement string for the macro
        //
        const macro = [(star ? resultstar : resultnostar)].join('');
        //
        //  Insert the replacement string into the TeX string, and check 
        //  that there haven't been too many maxro substitutions (prevent's 
        //  infinite loops).
        //
        parser.string = ParseUtil.addArgs(parser, macro, parser.string.slice(parser.i));
        parser.i = 0;
        if (++parser.macroCount > parser.configuration.options.maxMacros) {
          throw new TexError('MaxMacroSub1',
                             'MathJax maximum macro substitution count exceeded; ' +
                             'is there a recursive macro call?');
        }
      };

      //
      //  The is the configuration for the ifstarDelimitersConfig TeX extension.
      //
      const ifstarDelimitersConfig = Configuration.create('ifstarCore', {
        //
        //  Initialize the extension by creating the command map for the
        //  macros defined by \DeclarePairedDelimiter, and add the
        //  \DeclarePairedDelimiter macro itself.  Then append the
        //  command map to the given configuration as a macro handler
        //
        init(config) {
          const map = new CommandMap(IFSTARMAP, {
//             DeclarePairedDelimiter: ['Declare_PairedDelimiter']
          }, {
            //
            //  Implements \DeclarePairedDelimiter control sequence.
            //
          }
          );
          config.append(Configuration.create('ifstarDelimiterDefs', {handler: {macro: [IFSTARMAP]}}));
        },

        //
        //  Add any user-defined paired delimiters (from the 
        //  ifstarCore configuration object in the document's 
        //  option list, if any).
        //
        config(config, jax) {
          const map = jax.parseOptions.handlers.retrieve(IFSTARMAP);
          const delimiters = jax.parseOptions.options.ifstarCore;
          for (const cs of Object.keys(delimiters)) {
            map.add(cs, new Macro(cs, ifstarDelimiters, delimiters[cs]));
          }
        },

        options: {
          ifstarCore: expandable({})
        }
      }
     );

      MathJax.startup.defaultReady();
    }
  },

  tex: {
    packages: {'[+]': ['ifstarCore']},  // use the ifstarCore package defined above
    ifstarCore: {
      ifstar: []               // a user-defined macro
    },
  }
}
</script>
<script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the attached startup example and its Configuration, CommandMap, Macro, TexError, and ParseUtil entry points. Review how the requested \ifstar and \ifnextchar behavior should fit MathJax's TeX parsing, then define the permanent extension shape. Done means MathJax includes the requested macro support as a maintained feature rather than a user-provided draft.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, tex
Domain
frontend, web-dev
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.