Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hljs.registerAlias Method #2540

Merged
merged 7 commits into from May 8, 2020

Conversation

taufik-nurrohman
Copy link
Member

src/highlight.js Outdated Show resolved Hide resolved
@joshgoebel
Copy link
Member

Nice. Changelog and perhaps a simple test or two?

@joshgoebel joshgoebel added parser enhancement An enhancement or new feature labels May 7, 2020
@joshgoebel joshgoebel added this to the 10.1 milestone May 7, 2020
@joshgoebel
Copy link
Member

joshgoebel commented May 7, 2020

Oh and you're going to need to update some docs also to add this since it's now public API.

api.rst

docs/api.rst Outdated Show resolved Hide resolved
src/highlight.js Outdated Show resolved Hide resolved
@wooorm
Copy link

wooorm commented May 8, 2020

Thanks! ✨ Looks functionally great, but I’m wondering about the api signature: Any reason why languageName is in an object, and not directly a parameter? Are there other values expected? Isn’t languageName the only really essential part of this call, whereas the aliases are optional?

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

The older and wiser I get any function that takes more one or two unnamed parameters is a code smell - Objective C got this right long ago with every argument being named. But the short form of the idea is that often functions are verbs that act on nouns... so the first param is often clear from context.

startEngine(a,...)
registerAlias(b,...)

But the second, third, forth... all start to be completely meaningless... the closest thing we have to named arguments in JS are objects, and the new destructuring stuff names it quite easy to work with them in this way. (it wasn't hard before, but it's nicer now)

This way it's 100% clear what parameter is which.

registerAlias("js", { languageName: 'javascript' });

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

Isn’t languageName the only really essential part of this call, whereas the aliases are optional?

I don't know what this means. You can't register an alias without telling it what language to point to... Both arguments are "essential". See verb/noun reasoning for why the "main subject" of the "action" of the function should be the first param.

Here is another API:

alterLanguage(languageName, addAliases: [...])

Same, verb/noun relationship... alter, what are we altering, the language. Very SQL like.

@wooorm
Copy link

wooorm commented May 8, 2020

As one could pass an empty list of aliases, it is not far off to see no alias the same as a list of no aliases.

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

As one could pass an empty list of aliases

Could, but that would serve no purpose. If you have no aliases, then you have nothing to register.

@joshgoebel
Copy link
Member

If I were being fancier I'd go with more English language:

register the alias "js" with the language: "javascript"

Translates to:

registerAlias("js", { withLanguage: 'javascript' });

@wooorm
Copy link

wooorm commented May 8, 2020

Could, but that would serve no purpose

Indeed, it should be a noop. Whereas no language should be an error.


I’m very glad this isn’t Java with its extremely long names. Or natural language.

For ordering: key, value|values is more in line with how other APIs, at least in JavaScript, work.

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

For ordering: key, value|values is more in line with how other APIs, at least in JavaScript, work.

I don't think it's very useful to say "other APIs" as a general term as if all APIs were to be somehow treated the same - although if you had one particular article that made a general case fairly well I wouldn't mind reading it... This type of thinking makes big assumptions about the internal data storage. It's not the callers concern to know how this data is represented inside the library.

From the callers perspective they are "registering an alias" (the name of the function you recommended) and associating it with a given language. There isn't a key or value concept here.

To get technical: the alias IS actually the literal key here and the language the value, FYI... but again I only use that to make the point of how silly it is to base the ordering purely on the internal data structures. I don't use that point to support the ordering I've chosen - because again how it's stored internally is not relevant. :-)

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

Indeed, it should be a noop. Whereas no language should be an error.

An empty array would be a noop right now, yes. No alias at all should be an error, just like no language should be. And I could imagine someone making a good argument for an empty array being an error condition too...

I get where you're going sort of, but I just don't agree with the premise. The whole idea makes little sense without both arguments, and even less with ONLY language.

registerAlias("javascript") // passing a language

Makes no sense... I can't do anything, I have no aliases, I have no subject for the verb - I have nothing to register. I could error or do literally nothing.

registerAlias("js") // passing an alias

I could actually do something here... I could keep a list of aliases waiting to be registered with languages, etc... pendingAliases(). Doesn't seem super useful to me... but since I have both the verb and subject I have options to decide what to do with that subject - HOW to register it.

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

Anyways, if you have an article I'm happy to read it - I do try to keep an open mind... but right now verb/noun seems crystal clear here to me (as I've explained above) - and I find this verb/noun pattern to be extremely common pattern when naming functions.

My only thought is that it may have been better named registerAliases... revealing the full power of the function - that it can take an array - which likely could be an array of 1. Where as registerAlias doesn't reveal the use case of passing multiple items.

@wooorm
Copy link

wooorm commented May 8, 2020

An empty array would be a noop right now, yes. No alias at all should be an error, just like no language should be.

The reason for my thinking is to allow for the current code:

if (lang.aliases) {
lang.aliases.forEach(function(alias) { aliases[alias] = name; });
}

To be written as:

if (lang.aliases) {
  registerAlias(name, lang.aliases)
}

Or even:

registerAlias(name, lang.aliases)

...instead of:

if (lang.aliases) {
  registerAlias(lang.aliases, {languageName: name})
}

My proposal is based on my mental model of alternative names: there is a primary name, and there are zero or more alternative names. That it is stored in reverse, is indeed an implementation detail that I don’t need to know about.
I think of this as a setter, hence my comparison to map.set(key, value), element.setAttribute(key, value), etc.
One could think up other examples such as getters (where I prefer getter(name) instead of getter({languageName: name})) or a way to clear aliases (delete(name))? All probably not very useful, but this hopefully explains why I think what I think.

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

This seems completely clear to me:

if (lang.aliases) {
  registerAliases(lang.aliases, {languageName})
}

Again there are no keys or values here, this isn't a Map. It's not a setter either, by it's very name. Your argument would hold up better if the language was the noun, but it's not.

if (lang.aliases) {
  alterLanguage(languageName, { addAliases: lang.aliases })
}

I would still have the conditional for clarity. I don't think passing null to the function and treating that the same as an empty array is a great habit to get into. (Writing TypeScript has made me think a lot harder about input and output typing). So I think your stated goal here (registerAlias(name, lang.aliases)) is NOT desirable when aliases is null/undefined.

Explicit almost always tops implicit for me.

One could think up other examples such as getters (where I prefer getter(name) instead of getter({languageName: name}))

Not sure I follow... this isn't [as large a] problem for single argument functions.

getLanguage(a)

Although this isn't immediately clear either... We can be sure what is returned by not was is passed, there could be multiple ways to find a language... by name, by unique ID, etc...

getLanguage("C++")
getLanguage("cpp")
getLanguage(32)
// vs
getLanguageBy({prettyName: "C++"})
getLanguageBy({name: "cpp"})
getLanguageBy({id: 32})

I think we're just going to have to agree to disagree.

@joshgoebel
Copy link
Member

It's also not fully correct to think of aliases as purely an attribute of a single language - even more so now that we can add them dynamically - opening them up to all sorts of creative uses. I've already seriously considered things like the following:

registerAlias("php", languageNames: ["php_snippet", "php_template"])

IE, PHP could refer to either language... perhaps decided based on relevancy, etc... and we actually have another language where there are two languages with the same name... the kind of thing you could solve with more powerful aliases, also...

registerAlias("basic", languageNames: ["trs100_basic", "microsoft_basic","c64_basic"])

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

I do think I finally understand you, but for me it's all about the naming. (that and my last point that I don't think of aliases are purely attributes of a language)

For me though as soon as the function name was registerAlias(es) (which is a reasonable enough name, and congruent with registerLanguage as you pointed out) there is no discussion to be had, the very name of the function tells you the first argument will be alias/aliases. Again, I think this verb/subject pattern is super common and used everywhere.

IF we had a different name then a different API might make sense (as I mentioned above somewhere). But we started with the name and we ended here.

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

The swift guidelines make a lot of sense to me:

https://swift.org/documentation/api-design-guidelines/#argument-labels

The last rule being "Label all other arguments.", so their definitely on the labeling side of things. :-)

@joshgoebel
Copy link
Member

joshgoebel commented May 8, 2020

The explicit naming also makes it clear that we expect a language name, not a language object...

var javascript = hljs.getLanguage("javascript")
// which is correct?  No way to know.
hljs.registerAlias("js", javascript)
hljs.registerAlias("js", "javascript")

It's completely unclear which would be the correct form (perhaps neither!), since you have no clue what that second argument is supposed to be. This is the problem with multiple unlabeled arguments - they lose all meaning at the call site. I could make it worse:

hljs.registerAlias("js", "javascript", true, false)

WHAT does that do? :-)

@joshgoebel
Copy link
Member

I just had an interesting discussion where a colleague pointed out to me that native language could have an effect on what feel naturals regarding naming.

I think native language may have an effect though on what feels natural to someone. Word order being rather different, as I recall, in German vs. English.

I never even considered that aspect and I definitely write code from a purely English grammatical perspective. It's possible that a language that sequences things entirely different could lend itself to a different naming approach. Hard to say for certain, but it's easy to imagine. But English is all I have to work with.

@wooorm
Copy link

wooorm commented May 19, 2020

Thanks for all your comments! Yes, I do believe natural language is important. What I’m mostly worried about is that you point to Swift guidelines, which has named parameters, whereas JavaScript does not. By accepting an object, you can indeed label them, but for me objects imply optionality and that multiple fields are (or will be) supported.

I still feel I prefer addIngredient(salad, 'tomato') or addIngredient(pasta, 'capers') over the inverse. And would think addIngredient(soup) a noop as there is no ingredient. But indeed, we do disagree about this style. And that’s fine!

@joshgoebel
Copy link
Member

joshgoebel commented May 19, 2020

What I’m mostly worried about is that you point to Swift guidelines, which has named parameters, whereas JavaScript does not.

Objects are the tool we have in JS to get named arguments. This was also a SUPER common pattern in Ruby also [named arguments passed as a Hash] until we got real named arguments. I'd imagine it's a pattern in other languages, also. This is what type definitions and documentation is for. :-) I pointed to that because it was an easy find vs spending more time looking for a JS example of someone preaching the same thing.

but for me objects imply optionality and that multiple fields are (or will be) supported.

You know what they say about implications, no wait that's assumptions, but still. :-) Consider letting go of that bias and I think you'll find some amazing new options in JS land. I think it's a very powerful, useful, and expressive pattern.

And that’s fine!

Indeed. :-)

0xflotus added a commit to 0xflotus/highlight.js that referenced this pull request Jun 12, 2020
* (docs) Add Chaos to supported languages (highlightjs#2510)

* fix(parser) fixes sublanguage with no rule matches (highlightjs#2506)

* fix(parser) fixes sublanguage with no rule matches

Resolves highlightjs#2504.

* (chore) Add ESLint config and clean up the major stuff (highlightjs#2503)

* (chore) eslint:recommended
* (chore): eslint_standard
* relax eslint rules for language grammars (to discourage rewriting them in one fell swoop; I'd rather have the blame history intact)
* remove extra escaping
* clean up variables
* more camelcase

* (docs) Add Visual Basic for Applications (VBA) to supported languages (highlightjs#2512)

* (yaml) improve tag support; add verbatim tags (highlightjs#2487)

* YAML parse non-word characters as part of tags
* adds support for verbatim tags

Co-authored-by: Josh Goebel <me@joshgoebel.com>

* fix(javascript/typescript): lambda with parens in parameters fails (highlightjs#2502)

* fix(javascript/typescript): lambda with parens in parameters fails

- Fixes both JavaScript and TypeScript grammars

Fixes samples like:

    const bad = ((a, b) => [...a, b]);
    sides.every((length,width=(3+2+(4/5))) => length > 0 );

This is done by counting parens in the regex that finds arrows
functions. Currently we can only handle 2 levels of nesting as
shown in the second example above.

* allow much richer highlighting inside params
* improve highlighting inside arguments on typescript

* enh(cpp): Improve highlighting of unterminated raw strings

PR highlightjs#1897 switched C++ raw strings to use backreferences, however this
breaks souce files where raw strings are truncated. Like comments, it
would be preferable to highlight them.

- Add `on:begin` and `on:end` to allow more granular matching when
  then end match is dynamic and based on a part of the begin match
- This deprecates the `endSameAsBegin` attribute. That attribute was
  a very specific way to solve this problem, but now we have a much
  more general solution in these added callbacks.

Also related: highlightjs#2259.

Co-authored-by: Josh Goebel <me@joshgoebel.com>

* (chore) C-like uses the new END_SAME_AS_BEGIN mode

* (chore) Ruby uses END_SAME_AS_BEGIN mode/rule

* (parser) make END_SAME_AS_BEGIN a function helper

Adds a mode helper to replace the deprecated `endSameAsBegin` attribute.

The first match group from the begin regex will be compared to the first
match group from the end regex and the end regex will only match if both
strings are identical.

Note this is more advanced functionality than before since now you can
match a larger selection of text yet only use a small portion of it for
the actual "end must match begin" portion.

* (pgsql) add test for $$ quoting existing behavior

- even if that existing behavior is questionable
- the ending span should really close before the $$, not after

Fixing this would involve delving into the sublanguage behavior and I'm
not sure we have time to tackle that right this moment.

* (chore) pgsql uses END_SAME_AS_BEGIN mode/rule now also

* (docs) rename to `mode_reference`; docs for callbacks

- I can never find this file because it's name didn't fully match.
- rename callbacks to `on:begin` and `on:end`

* prevented setter keyword conflicting with setTimeout|setInterval and highlighted them (highlightjs#2514) (highlightjs#2515)

* fix(javascript) prevent setter keyword 'set' conflicting with setTimeout|setInterval (highlightjs#2514)
* enh(javascript) setTimeout|setInterval now highlighted (highlightjs#2514)
* enh (javascript) clearInterval and clearTimeout now highlighted
* add keywords to TypeScript also

* (docs) add TLDR instructions for building and testing

* (dev) improve developer tool UI

* (parser) Build common EMCAscript foundation

Builds a common keyword foundation for any grammar that is
building on top of JavaScript:

- LiveScript
- CoffeeScript
- TypeScript

Also uses this common foundation for JS itself.

* (parser) Adds SHEBANG mode

* (yaml) Add support for inline sequences and mappings (highlightjs#2513)

* Use containers to match inline sequences and mappings
* Add string type for inside inline elements
* Handle nested inline sequences and mappings
* Disallow all braces brackets and commas from strings inside inline mappings or sequences
* clean up implementation
* feed the linter

Co-authored-by: Josh Goebel <me@joshgoebel.com>

* [enh] Add `OPTIMIZE:` and `HACK:` to comment doctags

* (build) browser build is CommonJS and IIFE, no more AMD (highlightjs#2511)

* (build) browser build is CommonJS and IIFE (global) now
* (build) dropping support for AMD, which we never truly supported
  properly in the first place
* (build) add test to make sure browser build works as commonJS module

  Resolves highlightjs#2505

* fix(parser) Fix freezing issue with illegal 0 width matches (highlightjs#2524)

* fix[parser] add edge case handle for illegal 0 width matches
* add last ditch catch all that tries to detect other uncaught freezes

* (docs) added unicorn-rails-log as an 3rd-party language (highlightjs#2528)

- (docs) Add syntax highlighting for Rails Unicorn logging to supported languages.

* (docs) fix supported languages link: it moved again! (highlightjs#2533)

* fix(ts/js) use identifier to match potential keywords (highlightjs#2519)

- (parser) Adds `keywords.$pattern` key to grammar definitions
- `lexemes` is now deprecated in favor of `keywords.$pattern` key
- enh(typescript) use identifier to match potential keywords, preventing false positives 
- enh(javascript) use identifier to match potential keywords, preventing false positives

* fix(javascript) fix regex inside parens after a non-regex (highlightjs#2531)

* make the object attr container smarter
* deal with multi-line comments also
* comments in any order, spanning multiple lines

Essentially makes the object attr container much more sensitive by allowing it to look-ahead thru comments to find object keys - and therefore prevent them from being incorrectly matched by the "value container" rule.

* (parser) Add hljs.registerAlias() public API (highlightjs#2540)

* Add hljs.registerAlias(alias, languageName) public API
* Add .registerAlias() test

* enh(cpp) add `pair`, `make_pair`, `priority_queue` as built-ins (highlightjs#2538)

* (fix) `fixMarkup` would rarely destroy markup when `useBR` was enabled (highlightjs#2532)

* enh(cpp) Recognize `priority_queue`, `pair` as containers (highlightjs#2541)

* chore: rename `registerAlias` to `registerAliases`

Plural form is clearly better as it's not surprising to the reader
to see it being passed an array - where as the singular form might
have been.  Meanwhile it's also easy to assume that it also supports
arrays of any size - including an array with a singular alias.

The fact that it can magically accept a string as the first argument
might not be obvious, but we document it and even if one didn't know
this they could still use the array form of the API without any issue
by passing a one item array.

* (swift) @objcMembers not completely highlighted (highlightjs#2543)

* Fixed @objcMembers in Swift

Would match `@objc` first, and the `Members` part would be unhighlighted

* Update CHANGES.md

* Update swift.js

* (docs) add OCL to list of supported languages (highlightjs#2547)

* (docs) Add Svelte to list of supported languages (highlightjs#2549)

* enh(dart) Add `late` and `required` keywords, and `Never` built-in type (Dart 2.9) (highlightjs#2551)

* Add new Dart 2.9 keywords for Null Safety language feature

* enh(erlang) add support for underscore separators in numeric literals (highlightjs#2554)

* (erlang) add support for underscore separators in numeric literals
* (erlang) add tests

* (docs) add Jolie to Supported Languages (highlightjs#2556)

* (parser/docs) Add jsdoc annotations and TypeScript type file (highlightjs#2517)

Adds JSDoc annotations and a .tsconfig that allows TypeScript to be run in it's "allowJS" mode and apply type and sanity checking to JavaScript code also. See Type Checking JavaScript Files.

I've been using TypeScript a lot lately and finding it very beneficial and wanted to get those same benefits here but without converting the whole project to TypeScript. It was rough at the beginning but now that this is finished I think it's about 80%-90% of the benefits without any of the TS compilation pipeline. The big difference in being JSDoc for adding typing information vs inline types with TypeScript.

Should be super helpful for maintainers using an editor with tight TypeScript integration and the improved docs/comments should help everyone else.

- Adds types/index.d.ts to NPM build (should be useful for TypeScript peeps)
- Improves documentation of many functions
- Adds JSDoc annotations to almost all functions
- Adds JSDoc type annotations to variables that can't be inferred
- Refactors a few smaller things to allow the TypeScript compiler to better infer what 
   is happening (and usually also made the code clearer)

* (parser) highlightBlock result key `re` => `relevance` (highlightjs#2553)

* enh(handlebars) Support for sub-expressions, path-expressions, hashes, block-parameters and literals (highlightjs#2344)

- `htmlbars` grammar is now deprecated. Use `handlebars` instead.

A stub is included so that anyone literally referencing the old `htmlbars` file (say manually requiring it in Node.js, etc) is still covered, but everyone should transition to `handlebars` now.

* fix(typescript) Add missing `readonly` keyword (highlightjs#2562)

* (docs) Mention `c` is a possible class for C (highlightjs#2577)

* fix(groovy) strings are not allowed inside ternary clauses (highlightjs#2565)

* fix(groovy) strings are not allowed inside ternary clauses
* whitespace can also include tabs

* Update @typescript-eslint/parser to the latest version 🚀 (highlightjs#2575)

* chore(package): update @typescript-eslint/parser to version 3.0.0
* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
Co-authored-by: Josh Goebel <me@joshgoebel.com>

* Update @typescript-eslint/eslint-plugin to the latest version 🚀 (highlightjs#2576)

* chore(package): update @typescript-eslint/eslint-plugin to version 3.0.0
* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
Co-authored-by: Josh Goebel <me@joshgoebel.com>

* (parser) properly escape ' and " in HTML output (highlightjs#2564)

* escape quotes also in final HTML output
* [style] update test coding style
* update markup tests with new escaping

This shouldn't be a security issue -- we've always escaped double quotes inside of HTML attribute values (where they could be used to break out of context) - and we've always used double quotes for enclosing attribute values. 

This just goes all the way and now properly escapes quotes everywhere.  Better safe than sorry.

* (docs) add changelog entry for last PR

* add nnfx theme (highlightjs#2571)

* (themes) Add new lioshi theme (highlightjs#2581)

* Added Cisco Command Line to SUPPORTED_LANGUAGES.md (highlightjs#2583)

* (themes) add `nnfx-dark` theme (highlightjs#2584)

* enh(protobuf) Support multiline comments  (highlightjs#2597)

* enh(java) added support for hexadecimal floating point literals (highlightjs#2509)

- Added support for many additional types of floating point literals
- Added related tests

There still may be a few gaps, but this is a pretty large improvement.

Co-authored-by: Josh Goebel <me@joshgoebel.com>

* (chore) Update issue templates (highlightjs#2574)

Co-authored-by: Vladimir Jimenez <allejo@me.com>

* enh(toml)(ini) Improve parsing of complex keys (highlightjs#2595)

Fixes: highlightjs#2594

* (chore) add `.js` extension to import statements (highlightjs#2601)

Adds file extensions to all import specifiers in ./src/ files.  This is useful to run the files straight from source with a web browser , Node.js ESM or Deno.

- Also add eslint rules regarding extensions for imports

* enh(dart) highlight built-in nullable types (highlightjs#2598)

* Dart: allow built-in nullable types with trailing ? to be highlighted

* enh(csharp) highlight generics in more cases (highlightjs#2599)

* (chore) fix tiny style issues, add linting npm task

- fixes tiny style issues
- adds `npm run lint` for linting the main library source
  (not languages which are still much messier)

* (chore) bump dev dependencies

* (chore) upgrade some dev stuff to newer versions

* bump v10.1.0

* (chore) bump copyright

* (chore) more import below metadata comment

Co-authored-by: M. Mert Yıldıran <mehmetmertyildiran@gmail.com>
Co-authored-by: Josh Goebel <me@joshgoebel.com>
Co-authored-by: Hugo Leblanc <dullin@hololink.org>
Co-authored-by: Peter Massey-Plantinga <plantinga.peter@gmail.com>
Co-authored-by: David Benjamin <davidben@google.com>
Co-authored-by: Vania Kucher <dev.kucher@gmail.com>
Co-authored-by: SweetPPro <sweetppro@users.noreply.github.com>
Co-authored-by: Alexandre ZANNI <16578570+noraj@users.noreply.github.com>
Co-authored-by: Taufik Nurrohman <t.nurrohman77@gmail.com>
Co-authored-by: Lin <50829219+Linhk1606@users.noreply.github.com>
Co-authored-by: nicked <nicked@gmail.com>
Co-authored-by: Nicolas Homble <nhomble@terpmail.umd.edu>
Co-authored-by: Ryandi Tjia <ryandi.tjia@me.com>
Co-authored-by: Sam Rawlins <srawlins@google.com>
Co-authored-by: Sergey Prokhorov <seriy.pr@gmail.com>
Co-authored-by: Brian Alberg <brian@alberg.org>
Co-authored-by: Nils Knappmeier <github@knappi.org>
Co-authored-by: Martin <7252614+Lhoerion@users.noreply.github.com>
Co-authored-by: Derek Lewis <DerekNonGeneric@inf.is>
Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
Co-authored-by: Jim Mason <jmason@ibinx.com>
Co-authored-by: lioshi <lionel.fenneteau@gmail.com>
Co-authored-by: BMatheas <65114274+BMatheas@users.noreply.github.com>
Co-authored-by: Pavel Evstigneev <pavel.evst@gmail.com>
Co-authored-by: Vladimir Jimenez <allejo@me.com>
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
Co-authored-by: TupikovVladimir <vladimir.tupikov@devexpress.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An enhancement or new feature parser
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants