diff --git a/NEWS b/NEWS index 13e27620..4f625ce4 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,10 @@ lnav v0.10.2: an array of values, enter the index to jump to. * The pretty-print view will now show breadcrumbs that indicate the location of the top line in the view with the prettified structure. + * Markdown files (those with a .md extension) are now rendered in the + TEXT view. The breadcrumb bar at the top will also be updated + depending on the section of the document that you are in and you + can use it to jump to different parts of the doc. * Added an integration with regex101.com to make it easier to edit log message regular expressions. Using the new "management CLI" (activated by the -m option), a log format can be created from diff --git a/aminclude_static.am b/aminclude_static.am index e6e5c362..1be3eb7f 100644 --- a/aminclude_static.am +++ b/aminclude_static.am @@ -1,6 +1,6 @@ # aminclude_static.am generated automatically by Autoconf -# from AX_AM_MACROS_STATIC on Fri Apr 22 09:39:38 PDT 2022 +# from AX_AM_MACROS_STATIC on Mon May 16 23:40:06 PDT 2022 # Code coverage diff --git a/configure.ac b/configure.ac index 44f9abe4..6cccede5 100644 --- a/configure.ac +++ b/configure.ac @@ -308,6 +308,9 @@ AM_CONDITIONAL(USE_INCLUDED_YAJL, test $HAVE_LOCAL_YAJL -eq 0) AM_CONDITIONAL(HAVE_LIBCURL, test x"$LIBCURL" != x"") AM_CONDITIONAL([DISABLE_DOCUMENTATION], [ test x"$cross_compiling" != x"no" ]) +USER_CXXFLAGS="${CXXFLAGS}" +AC_SUBST(USER_CXXFLAGS) + AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([TESTS_ENVIRONMENT]) diff --git a/docs/schemas/config-v1.schema.json b/docs/schemas/config-v1.schema.json index 650a2baf..61c4dc56 100644 --- a/docs/schemas/config-v1.schema.json +++ b/docs/schemas/config-v1.schema.json @@ -348,6 +348,16 @@ "title": "/ui/theme-defs//styles/h6", "$ref": "#/definitions/style" }, + "hr": { + "description": "Styling for horizontal rules", + "title": "/ui/theme-defs//styles/hr", + "$ref": "#/definitions/style" + }, + "hyperlink": { + "description": "Styling for hyperlinks", + "title": "/ui/theme-defs//styles/hyperlink", + "$ref": "#/definitions/style" + }, "list-glyph": { "description": "Styling for glyphs that prefix a list item", "title": "/ui/theme-defs//styles/list-glyph", @@ -357,6 +367,36 @@ "description": "Styling for the separator between breadcrumbs", "title": "/ui/theme-defs//styles/breadcrumb", "$ref": "#/definitions/style" + }, + "table-border": { + "description": "Styling for table borders", + "title": "/ui/theme-defs//styles/table-border", + "$ref": "#/definitions/style" + }, + "table-header": { + "description": "Styling for table headers", + "title": "/ui/theme-defs//styles/table-header", + "$ref": "#/definitions/style" + }, + "quote-border": { + "description": "Styling for quoted-block borders", + "title": "/ui/theme-defs//styles/quote-border", + "$ref": "#/definitions/style" + }, + "quoted-text": { + "description": "Styling for quoted text blocks", + "title": "/ui/theme-defs//styles/quoted-text", + "$ref": "#/definitions/style" + }, + "footnote-border": { + "description": "Styling for footnote borders", + "title": "/ui/theme-defs//styles/footnote-border", + "$ref": "#/definitions/style" + }, + "footnote-text": { + "description": "Styling for footnote text", + "title": "/ui/theme-defs//styles/footnote-text", + "$ref": "#/definitions/style" } }, "additionalProperties": false @@ -366,6 +406,16 @@ "title": "/ui/theme-defs//syntax-styles", "type": "object", "properties": { + "quoted-code": { + "description": "Styling for quoted code blocks", + "title": "/ui/theme-defs//syntax-styles/quoted-code", + "$ref": "#/definitions/style" + }, + "code-border": { + "description": "Styling for quoted-code borders", + "title": "/ui/theme-defs//syntax-styles/code-border", + "$ref": "#/definitions/style" + }, "keyword": { "description": "Styling for keywords in source files", "title": "/ui/theme-defs//syntax-styles/keyword", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fd0c9135..76a1f6f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -99,7 +99,7 @@ function(bin2c) DEPENDS bin2c "${FILE_TO_LINK}") endfunction(bin2c) -foreach (FILE_TO_LINK ansi-palette.json xterm-palette.json help.txt init.sql) +foreach (FILE_TO_LINK ansi-palette.json emojis.json xml-entities.json xterm-palette.json help.txt help.md init.sql) string(REPLACE "." "-" DST_FILE "${FILE_TO_LINK}") add_custom_command( OUTPUT "${DST_FILE}.h" "${DST_FILE}.cc" @@ -247,6 +247,7 @@ add_library( config.h.in all_logs_vtab.cc archive_manager.cc + document.sections.cc bin2c.hh bookmarks.cc bottom_status_source.cc @@ -294,6 +295,8 @@ add_library( log_search_table.cc logfile.cc logfile_sub_source.cc + md2attr_line.cc + md4cpp.cc network-extension-functions.cc data_scanner.cc data_scanner_re.cc @@ -343,11 +346,14 @@ add_library( xpath_vtab.cc xterm_mouse.cc spookyhash/SpookyV2.cpp + third-party/md4c/md4c.c third-party/sqlite/ext/series.c third-party/sqlite/ext/dbdump.c + all_logs_vtab.hh archive_manager.hh archive_manager.cfg.hh + document.sections.hh big_array.hh bottom_status_source.hh bound_tags.hh @@ -394,6 +400,8 @@ add_library( logfile.hh logfile_fwd.hh logfile_stats.hh + md2attr_line.hh + md4cpp.hh optional.hpp papertrail_proc.hh pcap_manager.hh @@ -477,6 +485,8 @@ add_library( third-party/CLI/ConfigFwd.hpp third-party/intervaltree/IntervalTree.h + + third-party/md4c/md4c.h ) set(lnav_SRCS lnav.cc) diff --git a/src/Makefile.am b/src/Makefile.am index 68f31575..676e918f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,13 +1,15 @@ include $(top_srcdir)/aminclude_static.am +CXXFLAGS = + SUBDIRS = tools fmtlib pcrepp base tailer pugixml yajl yajlpp formats/logfmt . bin_PROGRAMS = lnav noinst_PROGRAMS = lnav-test -noinst_LIBRARIES = libdiag.a +noinst_LIBRARIES = libdiag.a libdatascanner.a PTIME_V = $(PTIME_V_@AM_V@) PTIME_V_ = $(PTIME_V_@AM_DEFAULT_V@) @@ -52,6 +54,9 @@ builtin-sh-scripts.h builtin-sh-scripts.cc: tools/bin2c$(BUILD_EXEEXT) $(BUILTIN %-txt.cc %-txt.h: $(srcdir)/%.txt tools/bin2c$(BUILD_EXEEXT) $(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-txt $< +%-md.cc %-md.h: $(srcdir)/%.md tools/bin2c$(BUILD_EXEEXT) + $(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-md $< + %-sql.cc %-sql.h: $(srcdir)/%.sql tools/bin2c$(BUILD_EXEEXT) $(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-sql $< @@ -81,7 +86,9 @@ log_format_loader.$(OBJEXT): \ styling.$(OBJEXT): ansi-palette-json.h xterm-palette-json.h -view_helpers.$(OBJEXT): help-txt.h +view_helpers.$(OBJEXT): help-txt.h help-md.h + +md4cpp.$(OBJEXT): xml-entities-json.h emojis-json.h LNAV_BUILT_FILES = \ ansi-palette-json.h \ @@ -94,17 +101,23 @@ LNAV_BUILT_FILES = \ default-config.cc \ default-formats.h \ default-formats.cc \ + emojis-json.h \ + emojis-json.cc \ help-txt.h \ help-txt.cc \ + help-md.h \ + help-md.cc \ init-sql.h \ init-sql.cc \ time_fmts.cc \ + xml-entities-json.h \ + xml-entities-json.cc \ xterm-palette-json.h \ xterm-palette-json.cc AM_LIBS = $(CODE_COVERAGE_LIBS) AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) -AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) +AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) $(USER_CXXFLAGS) AM_LDFLAGS = \ $(STATIC_LDFLAGS) \ @@ -127,6 +140,7 @@ AM_CPPFLAGS = \ LDADD = \ libdiag.a \ + libdatascanner.a \ base/libbase.a \ formats/logfmt/liblogfmt.a \ fmtlib/libcppfmt.a \ @@ -143,16 +157,22 @@ LDADD = \ $(LIBARCHIVE_LIBS) \ $(LIBCURL) +# emojis.json is from https://gist.github.com/oliveratgithub/0bf11a9aff0d6da7b46f1490f86a71eb/ +# xml-entities.json is from https://html.spec.whatwg.org/entities.json + dist_noinst_DATA = \ alpha-release.sh \ ansi-palette.json \ + emojis.json \ $(BUILTIN_LNAVSCRIPTS) \ $(BUILTIN_SHSCRIPTS) \ $(CONFIG_FILES) \ $(FORMAT_FILES) \ + xml-entities.json \ xterm-palette.json noinst_HEADERS = \ + third-party/md4c/md4c.h \ all_logs_vtab.hh \ archive_manager.hh \ archive_manager.cfg.hh \ @@ -172,6 +192,7 @@ noinst_HEADERS = \ data_parser.hh \ db_sub_source.hh \ doc_status_source.hh \ + document.sections.hh \ dump_internals.hh \ elem_to_json.hh \ environ_vtab.hh \ @@ -187,6 +208,7 @@ noinst_HEADERS = \ fts_fuzzy_match.hh \ grep_highlighter.hh \ grep_proc.hh \ + help.md \ help.txt \ help_text.hh \ help_text_formatter.hh \ @@ -226,6 +248,8 @@ noinst_HEADERS = \ mapbox/variant.hpp \ mapbox/variant_io.hpp \ mapbox/variant_visitor.hpp \ + md2attr_line.hh \ + md4cpp.hh \ optional.hpp \ papertrail_proc.hh \ pcap_manager.hh \ @@ -322,9 +346,16 @@ THIRD_PARTY_SRCS = \ third-party/CLI/ConfigFwd.hpp \ third-party/doctest-root/doctest/doctest.h \ third-party/intervaltree/IntervalTree.h \ + third-party/md4c/md4c.c \ third-party/sqlite/ext/dbdump.c \ third-party/sqlite/ext/series.c +libdatascanner_a_SOURCES = \ + data_scanner_re.cc +# XXX The data_scanner_re optimized build is taking 30+ minutes to run for +# some reason, so we need to override the flags +libdatascanner_a_CXXFLAGS = -O1 + libdiag_a_SOURCES = \ $(THIRD_PARTY_SRCS) \ all_logs_vtab.cc \ @@ -337,8 +368,8 @@ libdiag_a_SOURCES = \ command_executor.cc \ curl_looper.cc \ data_scanner.cc \ - data_scanner_re.cc \ db_sub_source.cc \ + document.sections.cc \ dump_internals.cc \ elem_to_json.cc \ environ_vtab.cc \ @@ -377,6 +408,8 @@ libdiag_a_SOURCES = \ log_search_table.cc \ logfile.cc \ logfile_sub_source.cc \ + md2attr_line.cc \ + md4cpp.cc \ network-extension-functions.cc \ data_parser.cc \ papertrail_proc.cc \ diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 7cf7bf11..1d4e4b95 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -3,6 +3,7 @@ add_library( ../config.h.in ansi_scrubber.cc attr_line.cc + attr_line.builder.cc auto_pid.cc date_time_scanner.cc fs_util.cc @@ -24,6 +25,7 @@ add_library( ansi_scrubber.hh attr_line.hh + attr_line.builder.hh auto_fd.hh auto_mem.hh auto_pid.hh @@ -58,6 +60,7 @@ target_link_libraries(base cppfmt pcre::libpcre ncurses::libcurses pthread) add_executable( test_base + attr_line.tests.cc fs_util.tests.cc humanize.file_size.tests.cc humanize.network.tests.cc diff --git a/src/base/Makefile.am b/src/base/Makefile.am index 1f5509a5..77fd87ee 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -21,6 +21,7 @@ noinst_LIBRARIES = libbase.a noinst_HEADERS = \ ansi_scrubber.hh \ attr_line.hh \ + attr_line.builder.hh \ auto_fd.hh \ auto_mem.hh \ auto_pid.hh \ @@ -57,6 +58,7 @@ noinst_HEADERS = \ libbase_a_SOURCES = \ ansi_scrubber.cc \ attr_line.cc \ + attr_line.builder.cc \ auto_pid.cc \ date_time_scanner.cc \ fs_util.cc \ @@ -80,6 +82,7 @@ check_PROGRAMS = \ test_base test_base_SOURCES = \ + attr_line.tests.cc \ fs_util.tests.cc \ humanize.file_size.tests.cc \ humanize.network.tests.cc \ diff --git a/src/base/attr_line.builder.cc b/src/base/attr_line.builder.cc new file mode 100644 index 00000000..95416dc8 --- /dev/null +++ b/src/base/attr_line.builder.cc @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "attr_line.builder.hh" diff --git a/src/base/attr_line.builder.hh b/src/base/attr_line.builder.hh new file mode 100644 index 00000000..1e62532c --- /dev/null +++ b/src/base/attr_line.builder.hh @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_attr_line_builder_hh +#define lnav_attr_line_builder_hh + +#include + +#include "attr_line.hh" + +class attr_line_builder { +public: + explicit attr_line_builder(attr_line_t& al) : alb_line(al) {} + + class attr_guard { + public: + attr_guard(attr_line_t& al, string_attr_pair sap) + : ag_line(al), ag_start(al.get_string().length()), + ag_attr(std::move(sap)) + { + } + + attr_guard(const attr_guard&) = delete; + + attr_guard& operator=(const attr_guard&) = delete; + + attr_guard(attr_guard&& other) noexcept + : ag_line(other.ag_line), ag_start(other.ag_start), + ag_attr(std::move(other.ag_attr)) + { + other.ag_start = nonstd::nullopt; + } + + ~attr_guard() + { + if (this->ag_start) { + this->ag_line.al_attrs.emplace_back( + line_range{ + this->ag_start.value(), + (int) this->ag_line.get_string().length(), + }, + this->ag_attr); + } + } + + private: + attr_line_t& ag_line; + nonstd::optional ag_start; + string_attr_pair ag_attr; + }; + + attr_guard with_attr(string_attr_pair sap) + { + return {this->alb_line, std::move(sap)}; + } + + template + attr_line_builder& overlay_attr(Args... args) + { + this->alb_line.al_attrs.template emplace_back(args...); + return *this; + } + + template + attr_line_builder& overlay_attr_for_char(int index, Args... args) + { + this->alb_line.al_attrs.template emplace_back( + line_range{index, index + 1}, args...); + return *this; + } + + template + attr_line_builder& append(Args... args) + { + this->alb_line.append(args...); + + return *this; + } + + attr_line_builder& indent(size_t amount) + { + auto pre = this->with_attr(SA_PREFORMATTED.value()); + + this->append(amount, ' '); + + return *this; + } + +private: + attr_line_t& alb_line; +}; + +#endif diff --git a/src/base/attr_line.cc b/src/base/attr_line.cc index ec548fb1..05cf468c 100644 --- a/src/base/attr_line.cc +++ b/src/base/attr_line.cc @@ -35,6 +35,7 @@ #include "auto_mem.hh" #include "config.h" #include "lnav_log.hh" +#include "pcrepp/pcrepp.hh" attr_line_t& attr_line_t::with_ansi_string(const char* str, ...) @@ -63,6 +64,88 @@ attr_line_t::with_ansi_string(const std::string& str) return *this; } +namespace text_stream { +struct word { + string_fragment w_word; + string_fragment w_remaining; +}; + +struct space { + string_fragment s_value; + string_fragment s_remaining; +}; + +struct corrupt { + string_fragment c_value; + string_fragment c_remaining; +}; + +struct eof { + string_fragment e_remaining; +}; + +using chunk = mapbox::util::variant; + +chunk +consume(const string_fragment text) +{ + static const pcrepp WORD_RE(R"((*UTF)^[^\p{Z}\p{So}\p{C}]+)"); + static const pcrepp SPACE_RE(R"((*UTF)^\s)"); + + if (text.empty()) { + return eof{text}; + } + + pcre_input pi(text); + pcre_context_static<30> pc; + + if (WORD_RE.match(pc, pi)) { + auto split_res = text.split_n(pc.all()->length()).value(); + + return word{split_res.first, split_res.second}; + } + + if (SPACE_RE.match(pc, pi)) { + auto split_res = text.split_n(pc.all()->length()).value(); + + return space{split_res.first, split_res.second}; + } + + auto csize_res = ww898::utf::utf8::char_size( + [&text]() { return std::make_pair(text.front(), text.length()); }); + + if (csize_res.isErr()) { + auto split_res = text.split_n(1); + + return corrupt{split_res->first, split_res->second}; + } + + auto split_res = text.split_n(csize_res.unwrap()); + + return word{split_res->first, split_res->second}; +} + +} // namespace text_stream + +static void +split_attrs(attr_line_t& al, const line_range& lr) +{ + string_attrs_t new_attrs; + + for (auto& attr : al.al_attrs) { + if (!lr.intersects(attr.sa_range)) { + continue; + } + + new_attrs.emplace_back(line_range{lr.lr_end, attr.sa_range.lr_end}, + std::make_pair(attr.sa_type, attr.sa_value)); + attr.sa_range.lr_end = lr.lr_start; + } + for (auto& new_attr : new_attrs) { + al.al_attrs.emplace_back(std::move(new_attr)); + } +} + attr_line_t& attr_line_t::insert(size_t index, const attr_line_t& al, @@ -74,7 +157,7 @@ attr_line_t::insert(size_t index, this->al_string.insert(index, al.al_string); - for (auto& sa : al.al_attrs) { + for (const auto& sa : al.al_attrs) { this->al_attrs.emplace_back(sa); line_range& lr = this->al_attrs.back().sa_range; @@ -85,88 +168,148 @@ attr_line_t::insert(size_t index, } } - if (tws != nullptr && (int) this->al_string.length() > tws->tws_width) { - ssize_t start_pos = index; - ssize_t line_start = this->al_string.rfind('\n', start_pos); + if (tws == nullptr) { + return *this; + } - if (line_start == (ssize_t) std::string::npos) { - line_start = 0; - } else { - line_start += 1; - } + static const pcrepp SPACE_RE(R"(\s?)"); - ssize_t line_len = index - line_start; - ssize_t usable_width = tws->tws_width - tws->tws_indent; - ssize_t avail - = std::max((ssize_t) 0, (ssize_t) tws->tws_width - line_len); + ssize_t starting_line_index = this->al_string.rfind('\n', index); + if (starting_line_index == std::string::npos) { + starting_line_index = 0; + } else { + starting_line_index += 1; + } - if (avail == 0) { - avail = INT_MAX; + const ssize_t usable_width = tws->tws_width - tws->tws_indent; + + auto text_to_wrap + = string_fragment{this->al_string.data(), (int) starting_line_index}; + string_fragment last_word; + size_t line_ch_count = 0; + auto needs_indent = false; + + while (!text_to_wrap.empty()) { + if (needs_indent) { + this->insert(text_to_wrap.sf_begin, + tws->tws_indent + tws->tws_padding_indent, + ' '); + auto indent_lr = line_range{ + text_to_wrap.sf_begin, + text_to_wrap.sf_begin + tws->tws_indent, + }; + split_attrs(*this, indent_lr); + indent_lr.lr_end += tws->tws_padding_indent; + line_ch_count += tws->tws_padding_indent; + this->al_attrs.emplace_back(indent_lr, SA_PREFORMATTED.value()); + text_to_wrap = text_to_wrap.prepend( + this->al_string.data(), + tws->tws_indent + tws->tws_padding_indent); + needs_indent = false; } + auto chunk = text_stream::consume(text_to_wrap); - while (start_pos < (int) this->al_string.length()) { - ssize_t lpc; - - // Find the end of a word or a breakpoint. - for (lpc = start_pos; lpc < (int) this->al_string.length() - && (isalnum(this->al_string[lpc]) - || this->al_string[lpc] == ',' - || this->al_string[lpc] == '_' - || this->al_string[lpc] == '.' - || this->al_string[lpc] == ';'); - lpc++) - { - if (this->al_string[lpc] == '-' || this->al_string[lpc] == '.') - { - lpc += 1; - break; - } - } + text_to_wrap = chunk.match( + [&](text_stream::word word) { + auto ch_count = word.w_word.utf8_length().unwrap(); - if ((avail != usable_width) && (lpc - start_pos > avail)) { - // Need to wrap the word. Do the wrap. - this->insert(start_pos, 1, '\n') - .insert(start_pos + 1, tws->tws_indent, ' '); - start_pos += 1 + tws->tws_indent; - avail = tws->tws_width - tws->tws_indent; - } else { - // There's still room to add stuff. - avail -= (lpc - start_pos); - while (lpc < (int) this->al_string.length() && avail) { - if (this->al_string[lpc] == '\n') { - this->insert(lpc + 1, tws->tws_indent, ' '); - avail = usable_width; - lpc += 1 + tws->tws_indent; - break; - } - if (isalnum(this->al_string[lpc]) - || this->al_string[lpc] == '_') { - break; + if ((line_ch_count + ch_count) > usable_width + && find_string_attr_containing(this->al_attrs, + &SA_PREFORMATTED, + text_to_wrap.sf_begin) + == this->al_attrs.end()) + { + this->insert(word.w_word.sf_begin, 1, '\n'); + this->insert(word.w_word.sf_begin + 1, + tws->tws_indent + tws->tws_padding_indent, + ' '); + auto indent_lr = line_range{ + word.w_word.sf_begin + 1, + word.w_word.sf_begin + 1 + tws->tws_indent, + }; + split_attrs(*this, indent_lr); + indent_lr.lr_end += tws->tws_padding_indent; + this->al_attrs.emplace_back(indent_lr, + SA_PREFORMATTED.value()); + line_ch_count = tws->tws_padding_indent + ch_count; + auto trailing_space_count = 0; + if (!last_word.empty()) { + trailing_space_count + = word.w_word.sf_begin - last_word.sf_begin; + this->erase(last_word.sf_begin, trailing_space_count); } - avail -= 1; - lpc += 1; + return word.w_remaining + .erase_before(this->al_string.data(), + trailing_space_count) + .prepend(this->al_string.data(), + 1 + tws->tws_indent + tws->tws_padding_indent); + } + line_ch_count += ch_count; + + return word.w_remaining; + }, + [&](text_stream::space space) { + if (space.s_value == "\n") { + line_ch_count = 0; + needs_indent = true; + return space.s_remaining; } - start_pos = lpc; - if (!avail) { - this->insert(start_pos, 1, '\n') - .insert(start_pos + 1, tws->tws_indent, ' '); - start_pos += 1 + tws->tws_indent; - avail = usable_width; - - for (lpc = start_pos; lpc < (int) this->al_string.length() - && this->al_string[lpc] == ' '; - lpc++) - { - } - if (lpc != start_pos) { - this->erase(start_pos, (lpc - start_pos)); + if (line_ch_count > 0) { + auto ch_count = space.s_value.utf8_length().unwrap(); + + if ((line_ch_count + ch_count) > usable_width + && find_string_attr_containing(this->al_attrs, + &SA_PREFORMATTED, + text_to_wrap.sf_begin) + == this->al_attrs.end()) + { + this->erase(space.s_value.sf_begin, + space.s_value.length()); + this->insert(space.s_value.sf_begin, "\n"); + line_ch_count = 0; + needs_indent = true; + + auto trailing_space_count = 0; + if (!last_word.empty()) { + trailing_space_count + = space.s_value.sf_begin - last_word.sf_begin; + this->erase(last_word.sf_end, trailing_space_count); + } + + return space.s_remaining + .erase_before( + this->al_string.data(), + space.s_value.length() + trailing_space_count) + .prepend(this->al_string.data(), 1); } + line_ch_count += ch_count; + } else if (find_string_attr_containing(this->al_attrs, + &SA_PREFORMATTED, + text_to_wrap.sf_begin) + == this->al_attrs.end()) + { + this->erase(space.s_value.sf_begin, space.s_value.length()); + return space.s_remaining.erase_before( + this->al_string.data(), space.s_value.length()); } - } + + return space.s_remaining; + }, + [](text_stream::corrupt corrupt) { + return corrupt.c_remaining; + }, + [](text_stream::eof eof) { + return eof.e_remaining; + }); + + if (chunk.is()) { + last_word = text_to_wrap; } - } + ensure(this->al_string.data() == text_to_wrap.sf_string); + ensure(text_to_wrap.sf_begin <= text_to_wrap.sf_end); + } return *this; } @@ -303,10 +446,16 @@ attr_line_t::erase(size_t pos, size_t len) attr_line_t& attr_line_t::pad_to(size_t size) { - const auto curr_len = this->length(); + const auto curr_len = this->utf8_length_or_length(); if (curr_len < size) { this->append((size - curr_len), ' '); + for (auto& attr : this->al_attrs) { + if (attr.sa_range.lr_start == 0 && attr.sa_range.lr_end == curr_len) + { + attr.sa_range.lr_end = this->al_string.length(); + } + } } return *this; diff --git a/src/base/attr_line.hh b/src/base/attr_line.hh index 6f91c757..022e3baf 100644 --- a/src/base/attr_line.hh +++ b/src/base/attr_line.hh @@ -53,16 +53,15 @@ struct line_range { explicit line_range(int start = -1, int end = -1) : lr_start(start), lr_end(end){}; - bool is_valid() const - { - return this->lr_start != -1; - } + bool is_valid() const { return this->lr_start != -1; } int length() const { return this->lr_end == -1 ? INT_MAX : this->lr_end - this->lr_start; } + bool empty() const { return this->length() == 0; } + int end_for_string(const std::string& str) const { return this->lr_end == -1 ? str.length() : this->lr_end; @@ -70,17 +69,20 @@ struct line_range { bool contains(int pos) const { - return this->lr_start <= pos && pos < this->lr_end; + return this->lr_start <= pos + && (this->lr_end == -1 || pos < this->lr_end); } bool contains(const struct line_range& other) const { - return this->contains(other.lr_start) && other.lr_end <= this->lr_end; + return this->contains(other.lr_start) + && (this->lr_end == -1 || other.lr_end <= this->lr_end); } bool intersects(const struct line_range& other) const { - return this->contains(other.lr_start) || this->contains(other.lr_end); + return this->contains(other.lr_start) || this->contains(other.lr_end) + || other.contains(this->lr_start); } line_range intersection(const struct line_range& other) const; @@ -347,6 +349,12 @@ struct text_wrap_settings { return *this; } + text_wrap_settings& with_padding_indent(int indent) + { + this->tws_padding_indent = indent; + return *this; + } + text_wrap_settings& with_width(int width) { this->tws_width = width; @@ -355,6 +363,7 @@ struct text_wrap_settings { int tws_indent{2}; int tws_width{80}; + int tws_padding_indent{0}; }; /** @@ -504,6 +513,12 @@ public: return *this; } + attr_line_t& append(const string_fragment& str) + { + this->al_string.append(str.data(), str.length()); + return *this; + } + template attr_line_t& append(S str) { @@ -648,6 +663,16 @@ public: return retval; } + Result utf8_length() const + { + return utf8_string_length(this->al_string); + } + + ssize_t utf8_length_or_length() const + { + return utf8_string_length(this->al_string).unwrapOr(this->length()); + } + std::string get_substring(const line_range& lr) const { if (!lr.is_valid()) { diff --git a/src/base/attr_line.tests.cc b/src/base/attr_line.tests.cc new file mode 100644 index 00000000..53b338ea --- /dev/null +++ b/src/base/attr_line.tests.cc @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "attr_line.hh" + +#include "config.h" +#include "doctest/doctest.h" + +using namespace lnav::roles::literals; + +TEST_CASE("attr_line_t::basic-wrapping") +{ + text_wrap_settings tws = {3, 21}; + attr_line_t to_be_wrapped{"This line, right here, needs to be wrapped."}; + attr_line_t dst; + + to_be_wrapped.al_attrs.emplace_back( + line_range{0, (int) to_be_wrapped.al_string.length()}, + VC_ROLE.value(role_t::VCR_ERROR)); + dst.append(to_be_wrapped, &tws); + + CHECK(dst.get_string() == + "This line, right\n" + " here, needs to be\n" + " wrapped."); + + for (const auto& attr : dst.al_attrs) { + printf("attr %d:%d %s\n", + attr.sa_range.lr_start, + attr.sa_range.lr_end, + attr.sa_type->sat_name); + } +} + +TEST_CASE("attr_line_t::unicode-wrap") +{ + text_wrap_settings tws = {3, 21}; + attr_line_t prefix; + + prefix.append(" ") + .append("\u2022"_list_glyph) + .append(" ") + .with_attr_for_all(SA_PREFORMATTED.value()); + + attr_line_t body; + body.append("This is a long line that needs to be wrapped and indented"); + + attr_line_t li; + + li.append(prefix) + .append(body, &tws) + .with_attr_for_all(SA_PREFORMATTED.value()); + + attr_line_t dst; + + dst.append(li); + + CHECK(dst.get_string() + == " \u2022 This is a long\n" + " line that needs to\n" + " be wrapped and\n" + " indented"); +} diff --git a/src/base/intern_string.cc b/src/base/intern_string.cc index f11f6fe9..78b976d8 100644 --- a/src/base/intern_string.cc +++ b/src/base/intern_string.cc @@ -186,6 +186,26 @@ string_fragment::consume_n(int amount) const }; } +string_fragment::split_result +string_fragment::split_n(int amount) const +{ + if (amount > this->length()) { + return nonstd::nullopt; + } + + return std::make_pair( + string_fragment{ + this->sf_string, + this->sf_begin, + this->sf_begin + amount, + }, + string_fragment{ + this->sf_string, + this->sf_begin + amount, + this->sf_end, + }); +} + std::vector string_fragment::split_lines() const { diff --git a/src/base/intern_string.hh b/src/base/intern_string.hh index 71effc93..52ef80b5 100644 --- a/src/base/intern_string.hh +++ b/src/base/intern_string.hh @@ -41,6 +41,7 @@ #include "fmt/format.h" #include "optional.hpp" #include "strnatcmp.h" +#include "ww898/cp_utf8.hpp" struct string_fragment { using iterator = const char*; @@ -60,40 +61,41 @@ struct string_fragment { { } - bool is_valid() const - { - return this->sf_begin != -1; - }; + bool is_valid() const { return this->sf_begin != -1; } - int length() const - { - return this->sf_end - this->sf_begin; - }; + int length() const { return this->sf_end - this->sf_begin; } - const char* data() const + Result utf8_length() const { - return &this->sf_string[this->sf_begin]; - } + size_t retval = 0; - iterator begin() const - { - return &this->sf_string[this->sf_begin]; - } + for (ssize_t byte_index = this->sf_begin; byte_index < this->sf_end;) { + auto ch_size + = TRY(ww898::utf::utf8::char_size([this, byte_index]() { + return std::make_pair(this->sf_string[byte_index], + this->sf_end - byte_index); + })); + byte_index += ch_size; + retval += 1; + } - iterator end() const - { - return &this->sf_string[this->sf_end]; + return Ok(retval); } - bool empty() const - { - return !this->is_valid() || length() == 0; - }; + const char* data() const { return &this->sf_string[this->sf_begin]; } + + char front() const { return this->sf_string[this->sf_begin]; } + + iterator begin() const { return &this->sf_string[this->sf_begin]; } + + iterator end() const { return &this->sf_string[this->sf_end]; } + + bool empty() const { return !this->is_valid() || length() == 0; } char operator[](int index) const { return this->sf_string[sf_begin + index]; - }; + } bool operator==(const std::string& str) const { @@ -104,7 +106,7 @@ struct string_fragment { return memcmp( &this->sf_string[this->sf_begin], str.c_str(), str.length()) == 0; - }; + } bool operator==(const string_fragment& sf) const { @@ -113,7 +115,7 @@ struct string_fragment { } return memcmp(this->data(), sf.data(), sf.length()) == 0; - }; + } bool iequal(const string_fragment& sf) const { @@ -124,7 +126,7 @@ struct string_fragment { return strnatcasecmp( this->length(), this->data(), sf.length(), sf.data()) == 0; - }; + } bool operator==(const char* str) const { @@ -132,7 +134,7 @@ struct string_fragment { return len == (size_t) this->length() && strncmp(this->data(), str, this->length()) == 0; - }; + } bool operator!=(const char* str) const { @@ -240,6 +242,8 @@ struct string_fragment { }); } + split_result split_n(int amount) const; + std::vector split_lines() const; struct tag1 { @@ -273,7 +277,7 @@ struct string_fragment { buf[this->length()] = '\0'; return buf; - }; + } std::string to_string() const { @@ -284,16 +288,43 @@ struct string_fragment { { this->sf_begin = 0; this->sf_end = 0; - }; + } void invalidate() { this->sf_begin = -1; this->sf_end = -1; - }; + } void trim(const char* tokens); + string_fragment prepend(const char* str, int amount) const + { + return string_fragment{ + str, + this->sf_begin + amount, + this->sf_end + amount, + }; + } + + string_fragment erase_before(const char* str, int amount) const + { + return string_fragment{ + str, + this->sf_begin - amount, + this->sf_end - amount, + }; + } + + string_fragment erase(const char* str, int amount) const + { + return string_fragment{ + str, + this->sf_begin, + this->sf_end - amount, + }; + } + const char* sf_string; int sf_begin; int sf_end; diff --git a/src/base/itertools.hh b/src/base/itertools.hh index 48ea4e00..8ae6756f 100644 --- a/src/base/itertools.hh +++ b/src/base/itertools.hh @@ -67,6 +67,8 @@ struct find { T f_value; }; +struct second {}; + template struct filter_in { F f_func; @@ -115,6 +117,15 @@ struct skip { struct unique {}; +struct max_value {}; + +template +struct max_with_init { + T m_init; +}; + +struct sum {}; + } // namespace details template @@ -144,6 +155,12 @@ find(T value) }; } +inline details::second +second() +{ + return details::second{}; +} + inline details::nth nth(nonstd::optional index) { @@ -258,6 +275,25 @@ chain(const T& value1, const Args&... args) return retval; } +inline details::max_value +max() +{ + return details::max_value{}; +} + +template +inline details::max_with_init +max(T init) +{ + return details::max_with_init{init}; +} + +inline details::sum +sum() +{ + return details::sum{}; +} + } // namespace itertools } // namespace lnav @@ -309,6 +345,55 @@ operator|(const C& in, const lnav::itertools::details::nth indexer) return nonstd::nullopt; } + +template +nonstd::optional +operator|(const C& in, const lnav::itertools::details::max_value maxer) +{ + nonstd::optional retval; + + for (const auto& elem : in) { + if (!retval) { + retval = elem; + continue; + } + + if (elem > retval.value()) { + retval = elem; + } + } + + return retval; +} + +template +typename C::value_type +operator|(const C& in, const lnav::itertools::details::max_with_init maxer) +{ + typename C::value_type retval = (typename C::value_type) maxer.m_init; + + for (const auto& elem : in) { + if (elem > retval) { + retval = elem; + } + } + + return retval; +} + +template +typename C::value_type +operator|(const C& in, const lnav::itertools::details::sum summer) +{ + typename C::value_type retval{0}; + + for (const auto& elem : in) { + retval += elem; + } + + return retval; +} + template C operator|(const C& in, const lnav::itertools::details::skip& skipper) diff --git a/src/base/lnav.console.cc b/src/base/lnav.console.cc index 4d982db8..a28921c9 100644 --- a/src/base/lnav.console.cc +++ b/src/base/lnav.console.cc @@ -154,7 +154,13 @@ user_message::to_attr_line(std::set flags) const } retval.append(header).append("\n"); if (!snip.s_content.blank()) { - for (const auto& line : snip.s_content.split_lines()) { + auto snippet_lines = snip.s_content.split_lines(); + auto longest_line_length = snippet_lines + | lnav::itertools::map(&attr_line_t::utf8_length_or_length) + | lnav::itertools::max(40); + + for (auto& line : snippet_lines) { + line.pad_to(longest_line_length); retval.append(" | "_comment).append(line).append("\n"); } } @@ -254,6 +260,8 @@ println(FILE* file, const attr_line_t& al) nonstd::optional last_point; for (const auto& point : points) { if (last_point) { + auto default_fg_style = fmt::text_style{}; + auto default_bg_style = fmt::text_style{}; auto line_style = fmt::text_style{}; auto fg_style = fmt::text_style{}; auto start = last_point.value(); @@ -330,6 +338,12 @@ println(FILE* file, const attr_line_t& al) case role_t::VCR_LIST_GLYPH: line_style |= fmt::fg(fmt::terminal_color::yellow); break; + case role_t::VCR_QUOTED_CODE: + default_fg_style + = fmt::fg(fmt::terminal_color::white); + default_bg_style + = fmt::bg(fmt::terminal_color::black); + break; default: break; } @@ -339,6 +353,14 @@ println(FILE* file, const attr_line_t& al) if (!line_style.has_foreground() && fg_style.has_foreground()) { line_style |= fg_style; } + if (!line_style.has_foreground() + && default_fg_style.has_foreground()) { + line_style |= default_fg_style; + } + if (!line_style.has_background() + && default_bg_style.has_background()) { + line_style |= default_bg_style; + } if (start < str.size()) { auto actual_end @@ -357,7 +379,12 @@ println(FILE* file, const attr_line_t& al) void print(FILE* file, const user_message& um) { - println(file, um.to_attr_line().rtrim()); + auto al = um.to_attr_line(); + + if (endswith(al.get_string(), "\n")) { + al.erase(al.length() - 1); + } + println(file, al); } } // namespace console diff --git a/src/base/lnav_log.cc b/src/base/lnav_log.cc index b01b49a0..5f6525a5 100644 --- a/src/base/lnav_log.cc +++ b/src/base/lnav_log.cc @@ -95,7 +95,7 @@ #include "opt_util.hh" static const size_t BUFFER_SIZE = 256 * 1024; -static const size_t MAX_LOG_LINE_SIZE = 2048; +static const size_t MAX_LOG_LINE_SIZE = 2 * 1024; static const char* CRASH_MSG = "\n" diff --git a/src/base/string_attr_type.cc b/src/base/string_attr_type.cc index 933e2b19..b56c5883 100644 --- a/src/base/string_attr_type.cc +++ b/src/base/string_attr_type.cc @@ -36,6 +36,7 @@ string_attr_type SA_BODY("body"); string_attr_type SA_HIDDEN("hidden"); string_attr_type SA_FORMAT("format"); string_attr_type SA_REMOVED("removed"); +string_attr_type SA_PREFORMATTED("preformatted"); string_attr_type SA_INVALID("invalid"); string_attr_type SA_ERROR("error"); string_attr_type SA_LEVEL("level"); diff --git a/src/base/string_attr_type.hh b/src/base/string_attr_type.hh index 509f7e54..7741229b 100644 --- a/src/base/string_attr_type.hh +++ b/src/base/string_attr_type.hh @@ -83,6 +83,8 @@ enum class role_t : int32_t { VCR_POPUP, VCR_COLOR_HINT, + VCR_QUOTED_CODE, + VCR_CODE_BORDER, VCR_KEYWORD, VCR_STRING, VCR_COMMENT, @@ -109,8 +111,16 @@ enum class role_t : int32_t { VCR_H5, VCR_H6, + VCR_HR, + VCR_HYPERLINK, VCR_LIST_GLYPH, VCR_BREADCRUMB, + VCR_TABLE_BORDER, + VCR_TABLE_HEADER, + VCR_QUOTE_BORDER, + VCR_QUOTED_TEXT, + VCR_FOOTNOTE_BORDER, + VCR_FOOTNOTE_TEXT, VCR__MAX }; @@ -164,6 +174,7 @@ extern string_attr_type SA_BODY; extern string_attr_type SA_HIDDEN; extern string_attr_type SA_FORMAT; extern string_attr_type SA_REMOVED; +extern string_attr_type SA_PREFORMATTED; extern string_attr_type SA_INVALID; extern string_attr_type SA_ERROR; extern string_attr_type SA_LEVEL; @@ -176,6 +187,20 @@ extern string_attr_type VC_FOREGROUND; extern string_attr_type VC_BACKGROUND; namespace lnav { + +namespace string { +namespace attrs { + +template +inline std::pair +preformatted(S str) +{ + return std::make_pair(std::move(str), SA_PREFORMATTED.template value()); +} + +} // namespace attrs +} // namespace string + namespace roles { template @@ -282,6 +307,22 @@ identifier(S str) VC_ROLE.template value(role_t::VCR_IDENTIFIER)); } +template +inline std::pair +hr(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_HR)); +} + +template +inline std::pair +hyperlink(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_HYPERLINK)); +} + template inline std::pair list_glyph(S str) @@ -298,6 +339,70 @@ breadcrumb(S str) VC_ROLE.template value(role_t::VCR_BREADCRUMB)); } +template +inline std::pair +quoted_code(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_QUOTED_CODE)); +} + +template +inline std::pair +code_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_CODE_BORDER)); +} + +template +inline std::pair +table_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_TABLE_BORDER)); +} + +template +inline std::pair +table_header(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_TABLE_HEADER)); +} + +template +inline std::pair +quote_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_QUOTE_BORDER)); +} + +template +inline std::pair +quoted_text(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_QUOTED_TEXT)); +} + +template +inline std::pair +footnote_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_FOOTNOTE_BORDER)); +} + +template +inline std::pair +footnote_text(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_FOOTNOTE_TEXT)); +} + template inline std::pair h1(S str) @@ -397,6 +502,34 @@ inline std::pair operator"" _h3(const char* str, VC_ROLE.template value(role_t::VCR_H3)); } +inline std::pair operator"" _h4(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_H4)); +} + +inline std::pair operator"" _h5(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_H5)); +} + +inline std::pair operator"" _hr(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_HR)); +} + +inline std::pair operator"" _hyperlink( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_HYPERLINK)); +} + inline std::pair operator"" _list_glyph( const char* str, std::size_t len) { @@ -411,6 +544,55 @@ inline std::pair operator"" _breadcrumb( VC_ROLE.template value(role_t::VCR_BREADCRUMB)); } +inline std::pair operator"" _quoted_code( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_QUOTED_CODE)); +} + +inline std::pair operator"" _code_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_CODE_BORDER)); +} + +inline std::pair operator"" _table_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_TABLE_BORDER)); +} + +inline std::pair operator"" _quote_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_QUOTE_BORDER)); +} + +inline std::pair operator"" _quoted_text( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_QUOTED_TEXT)); +} + +inline std::pair operator"" _footnote_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_FOOTNOTE_BORDER)); +} + +inline std::pair operator"" _footnote_text( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_FOOTNOTE_BORDER)); +} + } // namespace literals } // namespace roles diff --git a/src/breadcrumb.hh b/src/breadcrumb.hh index f6fade55..c69e9f8c 100644 --- a/src/breadcrumb.hh +++ b/src/breadcrumb.hh @@ -97,9 +97,14 @@ struct crumb { crumb& with_possible_range(size_t count) { this->c_possible_range = count; - this->c_search_placeholder - = fmt::format(FMT_STRING("(Enter a number from 0 to {})"), - this->c_possible_range.value() - 1); + if (count == 0) { + this->c_search_placeholder = "(Array is empty)"; + } else if (count == 1) { + this->c_search_placeholder = "(Array contains a single element)"; + } else { + this->c_search_placeholder = fmt::format( + FMT_STRING("(Enter a number from 0 to {})"), count - 1); + } return *this; } diff --git a/src/breadcrumb_curses.cc b/src/breadcrumb_curses.cc index 1e80b8c9..e83f6d3d 100644 --- a/src/breadcrumb_curses.cc +++ b/src/breadcrumb_curses.cc @@ -111,7 +111,13 @@ breadcrumb_curses::reload_data() auto& selected_crumb_ref = this->bc_focused_crumbs[this->bc_selected_crumb.value()]; this->bc_possible_values = selected_crumb_ref.c_possibility_provider() - | lnav::itertools::sort_by(&breadcrumb::possibility::p_key); + | lnav::itertools::sort_with([](const auto& lhs, const auto& rhs) { + return strnatcasecmp(lhs.p_key.size(), + lhs.p_key.data(), + rhs.p_key.size(), + rhs.p_key.data()) + < 0; + }); nonstd::optional selected_value; this->bc_similar_values = this->bc_possible_values @@ -119,7 +125,10 @@ breadcrumb_curses::reload_data() [](const auto& elem) { return elem.p_key; }, this->bc_current_search, INT_MAX); - if (selected_crumb_ref.c_key.is()) { + if (selected_crumb_ref.c_key.is() + && selected_crumb_ref.c_expected_input + != breadcrumb::crumb::expected_input_t::anything) + { auto& selected_crumb_key = selected_crumb_ref.c_key.get(); auto found_poss_opt = this->bc_similar_values | lnav::itertools::find_if([&selected_crumb_key](const auto& elem) { @@ -157,10 +166,9 @@ breadcrumb_curses::reload_data() std::min(this->bc_match_source.get_lines().size() + 1, size_t{4}))); this->bc_match_view.set_width(width + 3); this->bc_match_view.set_needs_update(); - if (selected_value) { - this->bc_match_view.set_selection(vis_line_t(selected_value.value())); - this->bc_match_view.scroll_selection_into_view(); - } + this->bc_match_view.set_selection( + vis_line_t(selected_value.value_or(-1_vl))); + this->bc_match_view.scroll_selection_into_view(); this->bc_match_view.reload_data(); this->set_needs_update(); } @@ -186,6 +194,7 @@ breadcrumb_curses::blur() this->bc_selected_crumb = nonstd::nullopt; this->bc_current_search.clear(); this->bc_match_view.set_height(0_vl); + this->bc_match_view.set_selection(-1_vl); this->bc_match_source.clear(); this->set_needs_update(); } @@ -214,17 +223,21 @@ breadcrumb_curses::handle_key(int ch) case '\t': case KEY_RIGHT: if (this->bc_selected_crumb) { + this->perform_selection(perform_behavior_t::if_different); + this->blur(); + this->focus(); + this->reload_data(); if (this->bc_selected_crumb.value() < this->bc_focused_crumbs.size() - 1) { this->bc_selected_crumb = this->bc_selected_crumb.value() + 1; - } else { - this->bc_selected_crumb = 0; + retval = true; } this->bc_current_search.clear(); this->reload_data(); + } else { + retval = true; } - retval = true; break; case KEY_HOME: this->bc_match_view.set_selection(0_vl); @@ -261,33 +274,7 @@ breadcrumb_curses::handle_key(int ch) break; case KEY_ENTER: case '\r': - if (this->bc_selected_crumb) { - auto& selected_crumb_ref - = this->bc_focused_crumbs[this->bc_selected_crumb.value()]; - if (this->bc_match_view.get_selection() >= 0 - && this->bc_match_view.get_selection() - < this->bc_similar_values.size()) - { - const auto& new_value - = this->bc_similar_values[this->bc_match_view - .get_selection()] - .p_key; - - selected_crumb_ref.c_performer(new_value); - } else if (!this->bc_current_search.empty()) { - if (selected_crumb_ref.c_possible_range) { - size_t index; - - if (sscanf( - this->bc_current_search.c_str(), "%zu", &index) - == 1) { - selected_crumb_ref.c_performer(index); - } - } else { - selected_crumb_ref.c_performer(this->bc_current_search); - } - } - } + this->perform_selection(perform_behavior_t::always); break; default: if (isprint(ch)) { @@ -305,6 +292,52 @@ breadcrumb_curses::handle_key(int ch) return retval; } +void +breadcrumb_curses::perform_selection( + breadcrumb_curses::perform_behavior_t behavior) +{ + if (!this->bc_selected_crumb) { + return; + } + + auto& selected_crumb_ref + = this->bc_focused_crumbs[this->bc_selected_crumb.value()]; + auto match_sel = this->bc_match_view.get_selection(); + if (match_sel >= 0 && match_sel < this->bc_similar_values.size()) { + const auto& new_value = this->bc_similar_values[match_sel].p_key; + + switch (behavior) { + case perform_behavior_t::if_different: + if (breadcrumb::crumb::key_t{new_value} + == selected_crumb_ref.c_key) { + return; + } + break; + case perform_behavior_t::always: + break; + } + selected_crumb_ref.c_performer(new_value); + } else if (!this->bc_current_search.empty()) { + switch (selected_crumb_ref.c_expected_input) { + case breadcrumb::crumb::expected_input_t::exact: + break; + case breadcrumb::crumb::expected_input_t::index: + case breadcrumb::crumb::expected_input_t::index_or_exact: { + size_t index; + + if (sscanf(this->bc_current_search.c_str(), "%zu", &index) == 1) + { + selected_crumb_ref.c_performer(index); + } + break; + } + case breadcrumb::crumb::expected_input_t::anything: + selected_crumb_ref.c_performer(this->bc_current_search); + break; + } + } +} + bool breadcrumb_curses::search_overlay_source::list_value_for_overlay( const listview_curses& lv, diff --git a/src/breadcrumb_curses.hh b/src/breadcrumb_curses.hh index 4e860caf..06218c33 100644 --- a/src/breadcrumb_curses.hh +++ b/src/breadcrumb_curses.hh @@ -82,6 +82,13 @@ private: breadcrumb_curses* sos_parent{nullptr}; }; + enum class perform_behavior_t { + always, + if_different, + }; + + void perform_selection(perform_behavior_t behavior); + WINDOW* bc_window{nullptr}; std::function()> bc_line_source; int bc_y{0}; diff --git a/src/command_executor.cc b/src/command_executor.cc index d6515ccd..7bcef64e 100644 --- a/src/command_executor.cc +++ b/src/command_executor.cc @@ -947,11 +947,23 @@ exec_context::add_error_context(lnav::console::user_message& um) if (this->ec_current_help != nullptr) { attr_line_t help; - format_help_text_for_term(*this->ec_current_help, -1, help, true); + format_help_text_for_term(*this->ec_current_help, 70, help, true); um.with_help(help); } } +exec_context::source_guard +exec_context::enter_source(intern_string_t path, + int line_number, + const std::string& content) +{ + attr_line_t content_al{content}; + content_al.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE)); + this->ec_source.emplace( + lnav::console::snippet::from(path, content_al).with_line(line_number)); + return {this}; +} + exec_context::output_guard::output_guard(exec_context& context, std::string name, const nonstd::optional& file) diff --git a/src/command_executor.hh b/src/command_executor.hh index d204f7ad..9292b027 100644 --- a/src/command_executor.hh +++ b/src/command_executor.hh @@ -152,12 +152,7 @@ struct exec_context { source_guard enter_source(intern_string_t path, int line_number, - const std::string& content) - { - this->ec_source.emplace( - lnav::console::snippet::from(path, content).with_line(line_number)); - return {this}; - } + const std::string& content); struct error_cb_guard { error_cb_guard(exec_context* context) : sg_context(context) {} diff --git a/src/data_scanner.hh b/src/data_scanner.hh index 2f558bbd..4c814c0b 100644 --- a/src/data_scanner.hh +++ b/src/data_scanner.hh @@ -122,6 +122,13 @@ public: } } + explicit data_scanner(string_fragment sf) : ds_pcre_input(sf) + { + if (!sf.empty() && sf[sf.length() - 1] == '.') { + this->ds_pcre_input.pi_length -= 1; + } + } + data_scanner(shared_buffer_ref& line, size_t off = 0, size_t len = (size_t) -1) diff --git a/src/document.sections.cc b/src/document.sections.cc new file mode 100644 index 00000000..a06254f4 --- /dev/null +++ b/src/document.sections.cc @@ -0,0 +1,452 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "document.sections.hh" + +#include "base/enum_util.hh" +#include "base/itertools.hh" +#include "base/lnav_log.hh" +#include "base/opt_util.hh" +#include "data_scanner.hh" + +namespace lnav { +namespace document { + +nonstd::optional +hier_node::lookup_child(section_key_t key) const +{ + return make_optional_from_nullable(key.match( + [this](const std::string& str) -> hier_node* { + auto iter = this->hn_named_children.find(str); + if (iter != this->hn_named_children.end()) { + return iter->second; + } + return nullptr; + }, + [this](size_t index) -> hier_node* { + if (index < this->hn_children.size()) { + return this->hn_children[index].get(); + } + return nullptr; + })); +} + +nonstd::optional +hier_node::lookup_path(const hier_node* root, + const std::vector& path) +{ + auto retval = make_optional_from_nullable(root); + + for (const auto& comp : path) { + if (!retval) { + break; + } + + retval = retval.value()->lookup_child(comp); + } + + if (!retval) { + return nonstd::nullopt; + } + + return retval; +} + +metadata +discover_metadata(const attr_line_t& al) +{ + const auto& orig_attrs = al.get_attrs(); + auto headers = orig_attrs + | lnav::itertools::filter_in([](const string_attr& attr) { + if (attr.sa_type != &VC_ROLE) { + return false; + } + + auto role = attr.sa_value.get(); + switch (role) { + case role_t::VCR_H1: + case role_t::VCR_H2: + case role_t::VCR_H3: + case role_t::VCR_H4: + case role_t::VCR_H5: + case role_t::VCR_H6: + return true; + default: + return false; + } + }) + | lnav::itertools::sort_by(&string_attr::sa_range); + + // Remove headers from quoted text + for (const auto& orig_attr : orig_attrs) { + if (orig_attr.sa_type == &VC_ROLE + && orig_attr.sa_value.get() == role_t::VCR_QUOTED_TEXT) + { + remove_string_attr(headers, orig_attr.sa_range); + } + } + + std::vector intervals; + + struct open_interval_t { + open_interval_t(uint32_t level, file_off_t start, section_key_t id) + : oi_level(level), oi_start(start), oi_id(std::move(id)) + { + } + + uint32_t oi_level; + file_off_t oi_start; + section_key_t oi_id; + std::unique_ptr oi_node{std::make_unique()}; + }; + std::vector open_intervals; + auto root_node = std::make_unique(); + + for (const auto& hdr_attr : headers) { + auto role = hdr_attr.sa_value.get(); + auto role_num = lnav::enums::to_underlying(role) + - lnav::enums::to_underlying(role_t::VCR_H1); + std::vector new_open_intervals; + + for (auto& oi : open_intervals) { + if (oi.oi_level >= role_num) { + // close out this section + intervals.emplace_back( + oi.oi_start, hdr_attr.sa_range.lr_start - 1, oi.oi_id); + auto* node_ptr = oi.oi_node.get(); + auto* parent_node = oi.oi_node->hn_parent; + if (parent_node != nullptr) { + parent_node->hn_children.emplace_back( + std::move(oi.oi_node)); + parent_node->hn_named_children.insert({ + oi.oi_id.get(), + node_ptr, + }); + } + } else { + new_open_intervals.emplace_back(std::move(oi)); + } + } + auto* parent_node = new_open_intervals.empty() + ? root_node.get() + : new_open_intervals.back().oi_node.get(); + new_open_intervals.emplace_back(role_num, + hdr_attr.sa_range.lr_start, + al.get_substring(hdr_attr.sa_range)); + new_open_intervals.back().oi_node->hn_parent = parent_node; + new_open_intervals.back().oi_node->hn_start + = hdr_attr.sa_range.lr_start; + + open_intervals = std::move(new_open_intervals); + } + + for (auto& oi : open_intervals) { + // close out this section + intervals.emplace_back(oi.oi_start, al.length(), oi.oi_id); + auto* node_ptr = oi.oi_node.get(); + auto* parent_node = oi.oi_node->hn_parent; + if (parent_node == nullptr) { + root_node = std::move(oi.oi_node); + } else { + parent_node->hn_children.emplace_back(std::move(oi.oi_node)); + parent_node->hn_named_children.insert({ + oi.oi_id.get(), + node_ptr, + }); + } + } + + return { + sections_tree_t{std::move(intervals)}, + std::move(root_node), + }; +} + +class structure_walker { +public: + explicit structure_walker(string_fragment content) : sw_scanner(content) + { + this->sw_interval_state.resize(1); + this->sw_hier_nodes.push_back(std::make_unique()); + } + + metadata walk() + { + pcre_context_static<30> pc; + data_token_t dt = DT_INVALID; + lnav::document::metadata retval; + auto& pi = this->sw_scanner.get_input(); + + while (this->sw_scanner.tokenize2(pc, dt)) { + element el(dt, pc); + + switch (dt) { + case DT_XML_DECL_TAG: + case DT_XML_EMPTY_TAG: + this->sw_values.emplace_back(el); + break; + case DT_XML_OPEN_TAG: + this->flush_values(); + this->sw_interval_state.back().is_start + = el.e_capture.c_begin; + this->sw_interval_state.back().is_line_number + = this->sw_line_number; + this->sw_interval_state.back().is_name + = pi.get_substr(&el.e_capture); + this->sw_depth += 1; + this->sw_interval_state.resize(this->sw_depth + 1); + this->sw_hier_nodes.push_back( + std::make_unique()); + break; + case DT_XML_CLOSE_TAG: { + auto term = this->flush_values(); + if (this->sw_depth > 0) { + this->sw_depth -= 1; + this->append_child_node(term); + this->sw_interval_state.pop_back(); + this->sw_hier_stage + = std::move(this->sw_hier_nodes.back()); + this->sw_hier_nodes.pop_back(); + } + this->append_child_node(el.e_capture); + this->flush_values(); + break; + } + case DT_LCURLY: + case DT_LSQUARE: + case DT_LPAREN: + this->flush_values(); + this->sw_depth += 1; + if (!this->sw_interval_state.back().is_start) { + this->sw_interval_state.back().is_start + = el.e_capture.c_begin; + this->sw_interval_state.back().is_line_number + = this->sw_line_number; + } + this->sw_interval_state.resize(this->sw_depth + 1); + this->sw_hier_nodes.push_back( + std::make_unique()); + break; + case DT_RCURLY: + case DT_RSQUARE: + case DT_RPAREN: { + auto term = this->flush_values(); + if (this->sw_depth > 0) { + this->sw_depth -= 1; + this->append_child_node(term); + this->sw_interval_state.pop_back(); + this->sw_hier_stage + = std::move(this->sw_hier_nodes.back()); + this->sw_hier_nodes.pop_back(); + } + this->sw_values.emplace_back(el); + break; + } + case DT_COMMA: + if (this->sw_depth > 0) { + auto term = this->flush_values(); + this->append_child_node(term); + } + break; + case DT_LINE: + this->sw_line_number += 1; + break; + case DT_WHITE: + break; + default: + this->sw_values.emplace_back(el); + break; + } + } + this->flush_values(); + + if (this->sw_hier_stage != nullptr) { + this->sw_hier_stage->hn_parent = this->sw_hier_nodes.back().get(); + this->sw_hier_nodes.back()->hn_children.push_back( + std::move(this->sw_hier_stage)); + } + this->sw_hier_stage = std::move(this->sw_hier_nodes.back()); + this->sw_hier_nodes.pop_back(); + if (this->sw_hier_stage->hn_children.size() == 1 + && this->sw_hier_stage->hn_named_children.empty()) + { + this->sw_hier_stage + = std::move(this->sw_hier_stage->hn_children.front()); + this->sw_hier_stage->hn_parent = nullptr; + } + + retval.m_sections_root = std::move(this->sw_hier_stage); + retval.m_sections_tree = sections_tree_t(std::move(this->sw_intervals)); + + return retval; + } + +private: + struct element { + element(data_token_t token, pcre_context& pc) + : e_token(token), e_capture(*pc.all()) + { + } + + element(data_token_t token, pcre_context::capture_t& cap) + : e_token(token), e_capture(cap) + { + } + + data_token_t e_token; + pcre_context::capture_t e_capture; + }; + + struct interval_state { + nonstd::optional is_start; + size_t is_line_number{0}; + std::string is_name; + }; + + nonstd::optional flush_values() + { + nonstd::optional last_key; + nonstd::optional retval; + auto& pi = this->sw_scanner.get_input(); + + if (!this->sw_values.empty()) { + if (!this->sw_interval_state.back().is_start) { + this->sw_interval_state.back().is_start + = this->sw_values.front().e_capture.c_begin; + this->sw_interval_state.back().is_line_number + = this->sw_line_number; + } + retval = this->sw_values.back().e_capture; + } + for (const auto& el : this->sw_values) { + switch (el.e_token) { + case DT_SYMBOL: + case DT_CONSTANT: + case DT_WORD: + case DT_QUOTED_STRING: + last_key = el.e_capture; + break; + case DT_COLON: + case DT_EQUALS: + if (last_key) { + this->sw_interval_state.back().is_name + = pi.get_substr(&last_key.value()); + if (!this->sw_interval_state.back().is_name.empty()) { + this->sw_interval_state.back().is_start + = static_cast( + last_key.value().c_begin); + this->sw_interval_state.back().is_line_number + = this->sw_line_number; + } + last_key = nonstd::nullopt; + } + break; + default: + break; + } + } + + this->sw_values.clear(); + + return retval; + } + + void append_child_node(nonstd::optional terminator) + { + auto& ivstate = this->sw_interval_state.back(); + if (!ivstate.is_start || !terminator) { + return; + } + + auto* top_node = this->sw_hier_nodes.back().get(); + auto new_key = ivstate.is_name.empty() + ? lnav::document::section_key_t{top_node->hn_children.size()} + : lnav::document::section_key_t{ivstate.is_name}; + this->sw_intervals.emplace_back( + ivstate.is_start.value(), + static_cast(terminator.value().c_end), + new_key); + auto new_node = this->sw_hier_stage != nullptr + ? std::move(this->sw_hier_stage) + : std::make_unique(); + auto* retval = new_node.get(); + new_node->hn_parent = top_node; + new_node->hn_start = this->sw_intervals.back().start; + new_node->hn_line_number = ivstate.is_line_number; + if (!ivstate.is_name.empty()) { + top_node->hn_named_children.insert({ + ivstate.is_name, + retval, + }); + } + top_node->hn_children.emplace_back(std::move(new_node)); + ivstate.is_start = nonstd::nullopt; + ivstate.is_line_number = 0; + ivstate.is_name.clear(); + } + + data_scanner sw_scanner; + int sw_depth{0}; + size_t sw_line_number{0}; + std::vector sw_values{}; + std::vector sw_interval_state; + std::vector sw_intervals; + std::vector> sw_hier_nodes; + std::unique_ptr sw_hier_stage; +}; + +metadata +discover_structure(string_fragment content) +{ + return structure_walker(content).walk(); +} + +std::vector +metadata::possibility_provider(const std::vector& path) +{ + std::vector retval; + auto curr_node = lnav::document::hier_node::lookup_path( + this->m_sections_root.get(), path); + if (curr_node) { + auto* parent_node = curr_node.value()->hn_parent; + + if (parent_node != nullptr) { + for (const auto& sibling : parent_node->hn_named_children) { + retval.template emplace_back(sibling.first); + } + } + } + return retval; +} + +} // namespace document +} // namespace lnav diff --git a/src/document.sections.hh b/src/document.sections.hh new file mode 100644 index 00000000..f6b231c3 --- /dev/null +++ b/src/document.sections.hh @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_attr_line_breadcrumbs_hh +#define lnav_attr_line_breadcrumbs_hh + +#include +#include +#include + +#include "base/attr_line.hh" +#include "base/file_range.hh" +#include "breadcrumb.hh" +#include "intervaltree/IntervalTree.h" +#include "mapbox/variant.hpp" +#include "optional.hpp" + +namespace lnav { +namespace document { + +using section_key_t = mapbox::util::variant; +using section_interval_t = interval_tree::Interval; +using sections_tree_t = interval_tree::IntervalTree; + +struct hier_node { + hier_node* hn_parent{nullptr}; + file_off_t hn_start{0}; + size_t hn_line_number{0}; + std::multimap hn_named_children; + std::vector> hn_children; + + nonstd::optional lookup_child(section_key_t key) const; + + nonstd::optional find_line_number(const std::string& str) const + { + auto iter = this->hn_named_children.find(str); + if (iter != this->hn_named_children.end()) { + return nonstd::make_optional(iter->second->hn_line_number); + } + + return nonstd::nullopt; + } + + nonstd::optional find_line_number(size_t index) const + { + if (index < this->hn_children.size()) { + return nonstd::make_optional( + this->hn_children[index]->hn_line_number); + } + return nonstd::nullopt; + } + + bool is_named_only() const + { + return this->hn_children.size() == this->hn_named_children.size(); + } + + static nonstd::optional lookup_path( + const hier_node* root, const std::vector& path); + + template + static void depth_first(hier_node* root, F func) + { + for (auto& child : root->hn_children) { + depth_first(child.get(), func); + } + func(root); + } +}; + +struct metadata { + sections_tree_t m_sections_tree; + std::unique_ptr m_sections_root; + + std::vector possibility_provider( + const std::vector& path); +}; + +metadata discover_metadata(const attr_line_t& al); + +metadata discover_structure(string_fragment sf); + +} // namespace document +} // namespace lnav + +#endif diff --git a/src/emojis.json b/src/emojis.json new file mode 100644 index 00000000..62f25070 --- /dev/null +++ b/src/emojis.json @@ -0,0 +1,4036 @@ +{ + "emojis": [ + {"emoji": "👩‍👩‍👧‍👧", "name": "family: woman, woman, girl, girl", "shortname": ":woman_woman_girl_girl:", "unicode": "1F469 200D 1F469 200D 1F467 200D 1F467", "html": "👩‍👩‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👩‍👧‍👦", "name": "family: woman, woman, girl, boy", "shortname": ":woman_woman_girl_boy:", "unicode": "1F469 200D 1F469 200D 1F467 200D 1F466", "html": "👩‍👩‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👩‍👦‍👦", "name": "family: woman, woman, boy, boy", "shortname": ":woman_woman_boy_boy:", "unicode": "1F469 200D 1F469 200D 1F466 200D 1F466", "html": "👩‍👩‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👧‍👧", "name": "family: man, woman, girl, girl", "shortname": ":man_woman_girl_girl:", "unicode": "1F468 200D 1F469 200D 1F467 200D 1F467", "html": "👨‍👩‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👧‍👦", "name": "family: man, woman, girl, boy", "shortname": ":man_woman_girl_boy:", "unicode": "1F468 200D 1F469 200D 1F467 200D 1F466", "html": "👨‍👩‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👦‍👦", "name": "family: man, woman, boy, boy", "shortname": ":man_woman_boy_boy:", "unicode": "1F468 200D 1F469 200D 1F466 200D 1F466", "html": "👨‍👩‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👧‍👧", "name": "family: man, man, girl, girl", "shortname": ":man_man_girl_girl:", "unicode": "1F468 200D 1F468 200D 1F467 200D 1F467", "html": "👨‍👨‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👧‍👦", "name": "family: man, man, girl, boy", "shortname": ":man_man_girl_boy:", "unicode": "1F468 200D 1F468 200D 1F467 200D 1F466", "html": "👨‍👨‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👦‍👦", "name": "family: man, man, boy, boy", "shortname": ":man_man_boy_boy:", "unicode": "1F468 200D 1F468 200D 1F466 200D 1F466", "html": "👨‍👨‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👩‍👧", "name": "family: woman, woman, girl", "shortname": ":woman_woman_girl:", "unicode": "1F469 200D 1F469 200D 1F467", "html": "👩‍👩‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👩‍👦", "name": "family: woman, woman, boy", "shortname": ":woman_woman_boy:", "unicode": "1F469 200D 1F469 200D 1F466", "html": "👩‍👩‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👧‍👧", "name": "family: woman, girl, girl", "shortname": ":woman_girl_girl:", "unicode": "1F469 200D 1F467 200D 1F467", "html": "👩‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👧‍👦", "name": "family: woman, girl, boy", "shortname": ":woman_girl_boy:", "unicode": "1F469 200D 1F467 200D 1F466", "html": "👩‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👦‍👦", "name": "family: woman, boy, boy", "shortname": ":woman_boy_boy:", "unicode": "1F469 200D 1F466 200D 1F466", "html": "👩‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👧", "name": "family: man, woman, girl", "shortname": ":man_woman_girl:", "unicode": "1F468 200D 1F469 200D 1F467", "html": "👨‍👩‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👧", "name": "family: man, man, girl", "shortname": ":man_man_girl:", "unicode": "1F468 200D 1F468 200D 1F467", "html": "👨‍👨‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👦", "name": "family: man, man, boy", "shortname": ":man_man_boy:", "unicode": "1F468 200D 1F468 200D 1F466", "html": "👨‍👨‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👧‍👧", "name": "family: man, girl, girl", "shortname": ":man_girl_girl:", "unicode": "1F468 200D 1F467 200D 1F467", "html": "👨‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👧‍👦", "name": "family: man, girl, boy", "shortname": ":man_girl_boy:", "unicode": "1F468 200D 1F467 200D 1F466", "html": "👨‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👦‍👦", "name": "family: man, boy, boy", "shortname": ":man_boy_boy:", "unicode": "1F468 200D 1F466 200D 1F466", "html": "👨‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👧", "name": "family: woman, girl", "shortname": ":woman_girl:", "unicode": "1F469 200D 1F467", "html": "👩‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👦", "name": "family: woman, boy", "shortname": ":woman_boy:", "unicode": "1F469 200D 1F466", "html": "👩‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👧", "name": "family: man, girl", "shortname": ":man_girl:", "unicode": "1F468 200D 1F467", "html": "👨‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👦", "name": "family: man, boy", "shortname": ":man_boy:", "unicode": "1F468 200D 1F466", "html": "👨‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "😂", "name": "face with tears of joy", "shortname": ":joy:", "unicode": "1f602", "html": "😂", "category": "Smileys & Emotion (face-smiling)", "order": "3"}, + {"emoji": "❤️", "name": "red heart", "shortname": ":heart:", "unicode": "2764", "html": "❤", "category": "Smileys & Emotion (emotion)", "order": "1286"}, + {"emoji": "♥️", "name": "heart suit", "shortname": ":heart_suit:", "unicode": "2665 FE0F", "html": "♥️", "category": "Activities (game)", "order": ""}, + {"emoji": "😍", "name": "smiling face with heart-eyes", "shortname": ":heart_eyes:", "unicode": "1f60d", "html": "😍", "category": "Smileys & Emotion (face-affection)", "order": "13"}, + {"emoji": "😭", "name": "loudly crying face", "shortname": ":sob:", "unicode": "1f62d", "html": "😭", "category": "Smileys & Emotion (face-concerned)", "order": "55"}, + {"emoji": "😊", "name": "smiling face with smiling eyes", "shortname": ":blush:", "unicode": "1f60a", "html": "😊", "category": "Smileys & Emotion (face-smiling)", "order": "10"}, + {"emoji": "😒", "name": "unamused face", "shortname": ":unamused:", "unicode": "1f612", "html": "😒", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "41"}, + {"emoji": "😘", "name": "face blowing a kiss", "shortname": ":kissing_heart:", "unicode": "1f618", "html": "😘", "category": "Smileys & Emotion (face-affection)", "order": "14"}, + {"emoji": "💕", "name": "two hearts", "shortname": ":two_hearts:", "unicode": "1f495", "html": "💕", "category": "Smileys & Emotion (emotion)", "order": "1289"}, + {"emoji": "☺️", "name": "smiling face", "shortname": ":smiling:", "unicode": "263A FE0F", "html": "☺", "category": "Smileys & Emotion (face-affection)", "order": ""}, + {"emoji": "😩", "name": "weary face", "shortname": ":weary:", "unicode": "1f629", "html": "😩", "category": "Smileys & Emotion (face-concerned)", "order": "59"}, + {"emoji": "👌🏿", "name": "OK hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44C 1F3FF", "html": "👌🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌🏾", "name": "OK hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44C 1F3FE", "html": "👌🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌🏽", "name": "OK hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44C 1F3FD", "html": "👌🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌🏼", "name": "OK hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44C 1F3FC", "html": "👌🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌🏻", "name": "OK hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44C 1F3FB", "html": "👌🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌", "name": "OK hand", "shortname": ":ok_hand:", "unicode": "1f44c", "html": "👌", "category": "People & Body (hand-fingers-partial)", "order": "1170"}, + {"emoji": "😔", "name": "pensive face", "shortname": ":pensive:", "unicode": "1f614", "html": "😔", "category": "Smileys & Emotion (face-sleepy)", "order": "43"}, + {"emoji": "😏", "name": "smirking face", "shortname": ":smirk:", "unicode": "1f60f", "html": "😏", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "26"}, + {"emoji": "😁", "name": "beaming face with smiling eyes", "shortname": ":grin:", "unicode": "1f601", "html": "😁", "category": "Smileys & Emotion (face-smiling)", "order": "2"}, + {"emoji": "♻", "name": "recycling symbol", "shortname": ":recycle:", "unicode": "267b", "html": "♻", "category": "Symbols (other-symbol)", "order": "2072"}, + {"emoji": "😉", "name": "winking face", "shortname": ":wink:", "unicode": "1f609", "html": "😉", "category": "Smileys & Emotion (face-smiling)", "order": "9"}, + {"emoji": "👍🏿", "name": "thumbs up: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44D 1F3FF", "html": "👍🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍🏾", "name": "thumbs up: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44D 1F3FE", "html": "👍🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍🏽", "name": "thumbs up: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44D 1F3FD", "html": "👍🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍🏼", "name": "thumbs up: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44D 1F3FC", "html": "👍🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍🏻", "name": "thumbs up: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44D 1F3FB", "html": "👍🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍", "name": "thumbs up", "shortname": ":thumbsup:", "unicode": "1f44d", "html": "👍", "category": "People & Body (hand-fingers-closed)", "order": "1176"}, + {"emoji": "🙏🏿", "name": "folded hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64F 1F3FF", "html": "🙏🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏🏾", "name": "folded hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64F 1F3FE", "html": "🙏🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏🏽", "name": "folded hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64F 1F3FD", "html": "🙏🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏🏼", "name": "folded hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64F 1F3FC", "html": "🙏🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏🏻", "name": "folded hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64F 1F3FB", "html": "🙏🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏", "name": "folded hands", "shortname": ":pray:", "unicode": "1f64f", "html": "🙏", "category": "People & Body (hands)", "order": "1248"}, + {"emoji": "😌", "name": "relieved face", "shortname": ":relieved:", "unicode": "1f60c", "html": "😌", "category": "Smileys & Emotion (face-sleepy)", "order": "35"}, + {"emoji": "🎶", "name": "musical notes", "shortname": ":notes:", "unicode": "1f3b6", "html": "🎶", "category": "Objects (music)", "order": "1825"}, + {"emoji": "😳", "name": "flushed face", "shortname": ":flushed:", "unicode": "1f633", "html": "😳", "category": "Smileys & Emotion (face-concerned)", "order": "63"}, + {"emoji": "🙌🏿", "name": "raising hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64C 1F3FF", "html": "🙌🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌🏾", "name": "raising hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64C 1F3FE", "html": "🙌🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌🏽", "name": "raising hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64C 1F3FD", "html": "🙌🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌🏼", "name": "raising hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64C 1F3FC", "html": "🙌🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌🏻", "name": "raising hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64C 1F3FB", "html": "🙌🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌", "name": "raising hands", "shortname": ":raised_hands:", "unicode": "1f64c", "html": "🙌", "category": "People & Body (hands)", "order": "1242"}, + {"emoji": "🙈", "name": "see-no-evil monkey", "shortname": ":see_no_evil:", "unicode": "1f648", "html": "🙈", "category": "Smileys & Emotion (monkey-face)", "order": "96"}, + {"emoji": "😢", "name": "crying face", "shortname": ":cry:", "unicode": "1f622", "html": "😢", "category": "Smileys & Emotion (face-concerned)", "order": "54"}, + {"emoji": "😎", "name": "smiling face with sunglasses", "shortname": ":sunglasses:", "unicode": "1f60e", "html": "😎", "category": "Smileys & Emotion (face-glasses)", "order": "12"}, + {"emoji": "✌🏿", "name": "victory hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "270C 1F3FF", "html": "✌🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌🏾", "name": "victory hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "270C 1F3FE", "html": "✌🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌🏽", "name": "victory hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "270C 1F3FD", "html": "✌🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌🏼", "name": "victory hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "270C 1F3FC", "html": "✌🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌🏻", "name": "victory hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "270C 1F3FB", "html": "✌🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌️", "name": "victory hand", "shortname": ":v:", "unicode": "270c", "html": "✌", "category": "People & Body (hand-fingers-partial)", "order": "1128"}, + {"emoji": "👀", "name": "eyes", "shortname": ":eyes:", "unicode": "1f440", "html": "👀", "category": "People & Body (body-parts)", "order": "1279"}, + {"emoji": "😅", "name": "grinning face with sweat", "shortname": ":sweat_smile:", "unicode": "1f605", "html": "😅", "category": "Smileys & Emotion (face-smiling)", "order": "7"}, + {"emoji": "✨", "name": "sparkles", "shortname": ":sparkles:", "unicode": "2728", "html": "✨", "category": "Activities (event)", "order": "1760"}, + {"emoji": "😴", "name": "sleeping face", "shortname": ":sleeping:", "unicode": "1f634", "html": "😴", "category": "Smileys & Emotion (face-sleepy)", "order": "34"}, + {"emoji": "😄", "name": "grinning face with smiling eyes", "shortname": ":smile:", "unicode": "1f604", "html": "😄", "category": "Smileys & Emotion (face-smiling)", "order": "6"}, + {"emoji": "💜", "name": "purple heart", "shortname": ":purple_heart:", "unicode": "1f49c", "html": "💜", "category": "Smileys & Emotion (emotion)", "order": "1295"}, + {"emoji": "💔", "name": "broken heart", "shortname": ":broken_heart:", "unicode": "1f494", "html": "💔", "category": "Smileys & Emotion (emotion)", "order": "1288"}, + {"emoji": "💯", "name": "hundred points", "shortname": ":hundred_points:", "unicode": "1F4AF", "html": "💯", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "😑", "name": "expressionless face", "shortname": ":expressionless:", "unicode": "1f611", "html": "😑", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "23"}, + {"emoji": "💖", "name": "sparkling heart", "shortname": ":sparkling_heart:", "unicode": "1f496", "html": "💖", "category": "Smileys & Emotion (emotion)", "order": "1290"}, + {"emoji": "💙", "name": "blue heart", "shortname": ":blue_heart:", "unicode": "1f499", "html": "💙", "category": "Smileys & Emotion (emotion)", "order": "1292"}, + {"emoji": "😕", "name": "confused face", "shortname": ":confused:", "unicode": "1f615", "html": "😕", "category": "Smileys & Emotion (face-concerned)", "order": "44"}, + {"emoji": "💁🏿‍♂", "name": "man tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF 200D 2642", "html": "💁🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾‍♂", "name": "man tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE 200D 2642", "html": "💁🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽‍♂", "name": "man tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD 200D 2642", "html": "💁🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼‍♂", "name": "man tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC 200D 2642", "html": "💁🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻‍♂", "name": "man tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB 200D 2642", "html": "💁🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁‍♂", "name": "man tipping hand", "shortname": ":man_tipping_hand:", "unicode": "1F481 200D 2642", "html": "💁‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏿", "name": "person tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF", "html": "💁🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾", "name": "person tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE", "html": "💁🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽", "name": "person tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD", "html": "💁🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼", "name": "person tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC", "html": "💁🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻", "name": "person tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB", "html": "💁🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁", "name": "person tipping hand", "shortname": ":information_desk_person:", "unicode": "1f481", "html": "💁", "category": "People & Body (person-gesture)", "order": "567"}, + {"emoji": "😜", "name": "winking face with tongue", "shortname": ":stuck_out_tongue_winking_eye:", "unicode": "1f61c", "html": "😜", "category": "Smileys & Emotion (face-tongue)", "order": "38"}, + {"emoji": "😞", "name": "disappointed face", "shortname": ":disappointed:", "unicode": "1f61e", "html": "😞", "category": "Smileys & Emotion (face-concerned)", "order": "51"}, + {"emoji": "😋", "name": "face savoring food", "shortname": ":yum:", "unicode": "1f60b", "html": "😋", "category": "Smileys & Emotion (face-tongue)", "order": "11"}, + {"emoji": "😐", "name": "neutral face", "shortname": ":neutral_face:", "unicode": "1f610", "html": "😐", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "22"}, + {"emoji": "😪", "name": "sleepy face", "shortname": ":sleepy:", "unicode": "1f62a", "html": "😪", "category": "Smileys & Emotion (face-sleepy)", "order": "32"}, + {"emoji": "👏🏿", "name": "clapping hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44F 1F3FF", "html": "👏🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏🏾", "name": "clapping hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44F 1F3FE", "html": "👏🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏🏽", "name": "clapping hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44F 1F3FD", "html": "👏🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏🏼", "name": "clapping hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44F 1F3FC", "html": "👏🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏🏻", "name": "clapping hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44F 1F3FB", "html": "👏🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏", "name": "clapping hands", "shortname": ":clap:", "unicode": "1f44f", "html": "👏", "category": "People & Body (hands)", "order": "1224"}, + {"emoji": "💘", "name": "heart with arrow", "shortname": ":cupid:", "unicode": "1f498", "html": "💘", "category": "Smileys & Emotion (emotion)", "order": "1285"}, + {"emoji": "💗", "name": "growing heart", "shortname": ":heartpulse:", "unicode": "1f497", "html": "💗", "category": "Smileys & Emotion (emotion)", "order": "1291"}, + {"emoji": "💞", "name": "revolving hearts", "shortname": ":revolving_hearts:", "unicode": "1f49e", "html": "💞", "category": "Smileys & Emotion (emotion)", "order": "1298"}, + {"emoji": "⬅️", "name": "left arrow", "shortname": ":arrow_left:", "unicode": "2b05", "html": "⬅", "category": "Symbols (arrow)", "order": "2008"}, + {"emoji": "🙊", "name": "speak-no-evil monkey", "shortname": ":speak_no_evil:", "unicode": "1f64a", "html": "🙊", "category": "Smileys & Emotion (monkey-face)", "order": "98"}, + {"emoji": "✋🏿", "name": "raised hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "270B 1F3FF", "html": "✋🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋🏾", "name": "raised hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "270B 1F3FE", "html": "✋🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋🏽", "name": "raised hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "270B 1F3FD", "html": "✋🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋🏼", "name": "raised hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "270B 1F3FC", "html": "✋🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋🏻", "name": "raised hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "270B 1F3FB", "html": "✋🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋", "name": "raised hand", "shortname": ":raised_hand:", "unicode": "270B", "html": "✋", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "💋", "name": "kiss mark", "shortname": ":kiss:", "unicode": "1f48b", "html": "💋", "category": "Smileys & Emotion (emotion)", "order": "1284"}, + {"emoji": "👉🏿", "name": "backhand index pointing right: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F449 1F3FF", "html": "👉🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉🏾", "name": "backhand index pointing right: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F449 1F3FE", "html": "👉🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉🏽", "name": "backhand index pointing right: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F449 1F3FD", "html": "👉🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉🏼", "name": "backhand index pointing right: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F449 1F3FC", "html": "👉🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉🏻", "name": "backhand index pointing right: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F449 1F3FB", "html": "👉🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉", "name": "backhand index pointing right", "shortname": ":point_right:", "unicode": "1f449", "html": "👉", "category": "People & Body (hand-single-finger)", "order": "1098"}, + {"emoji": "🌸", "name": "cherry blossom", "shortname": ":cherry_blossom:", "unicode": "1f338", "html": "🌸", "category": "Animals & Nature (plant-flower)", "order": "1428"}, + {"emoji": "😱", "name": "face screaming in fear", "shortname": ":scream:", "unicode": "1f631", "html": "😱", "category": "Smileys & Emotion (face-concerned)", "order": "62"}, + {"emoji": "🔥", "name": "fire", "shortname": ":fire:", "unicode": "1f525", "html": "🔥", "category": "Travel & Places (sky & weather)", "order": "1753"}, + {"emoji": "😡", "name": "pouting face", "shortname": ":rage:", "unicode": "1f621", "html": "😡", "category": "Smileys & Emotion (face-negative)", "order": "65"}, + {"emoji": "😃", "name": "grinning face with big eyes", "shortname": ":smiley:", "unicode": "1f603", "html": "😃", "category": "Smileys & Emotion (face-smiling)", "order": "5"}, + {"emoji": "🎉", "name": "party popper", "shortname": ":tada:", "unicode": "1f389", "html": "🎉", "category": "Activities (event)", "order": "1762"}, + {"emoji": "👊🏿", "name": "oncoming fist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44A 1F3FF", "html": "👊🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊🏾", "name": "oncoming fist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44A 1F3FE", "html": "👊🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊🏽", "name": "oncoming fist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44A 1F3FD", "html": "👊🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊🏼", "name": "oncoming fist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44A 1F3FC", "html": "👊🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊🏻", "name": "oncoming fist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44A 1F3FB", "html": "👊🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊", "name": "oncoming fist", "shortname": ":oncoming_fist:", "unicode": "1F44A", "html": "👊", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "😫", "name": "tired face", "shortname": ":tired_face:", "unicode": "1f62b", "html": "😫", "category": "Smileys & Emotion (face-concerned)", "order": "33"}, + {"emoji": "📷", "name": "camera", "shortname": ":camera:", "unicode": "1f4f7", "html": "📷", "category": "Objects (light & video)", "order": "1861"}, + {"emoji": "🌹", "name": "rose", "shortname": ":rose:", "unicode": "1f339", "html": "🌹", "category": "Animals & Nature (plant-flower)", "order": "1431"}, + {"emoji": "😝", "name": "squinting face with tongue", "shortname": ":stuck_out_tongue_closed_eyes:", "unicode": "1f61d", "html": "😝", "category": "Smileys & Emotion (face-tongue)", "order": "39"}, + {"emoji": "💪🏿", "name": "flexed biceps: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F4AA 1F3FF", "html": "💪🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪🏾", "name": "flexed biceps: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F4AA 1F3FE", "html": "💪🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪🏽", "name": "flexed biceps: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F4AA 1F3FD", "html": "💪🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪🏼", "name": "flexed biceps: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F4AA 1F3FC", "html": "💪🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪🏻", "name": "flexed biceps: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F4AA 1F3FB", "html": "💪🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪", "name": "flexed biceps", "shortname": ":muscle:", "unicode": "1f4aa", "html": "💪", "category": "People & Body (body-parts)", "order": "1080"}, + {"emoji": "💀", "name": "skull", "shortname": ":skull:", "unicode": "1f480", "html": "💀", "category": "Smileys & Emotion (face-negative)", "order": "80"}, + {"emoji": "☀️", "name": "sun", "shortname": ":sunny:", "unicode": "2600", "html": "☀", "category": "Travel & Places (sky & weather)", "order": "1724"}, + {"emoji": "💛", "name": "yellow heart", "shortname": ":yellow_heart:", "unicode": "1f49b", "html": "💛", "category": "Smileys & Emotion (emotion)", "order": "1294"}, + {"emoji": "😤", "name": "face with steam from nose", "shortname": ":triumph:", "unicode": "1f624", "html": "😤", "category": "Smileys & Emotion (face-negative)", "order": "53"}, + {"emoji": "🌚", "name": "new moon face", "shortname": ":new_moon_with_face:", "unicode": "1f31a", "html": "🌚", "category": "Travel & Places (sky & weather)", "order": "1720"}, + {"emoji": "😆", "name": "grinning squinting face", "shortname": ":laughing:", "unicode": "1f606", "html": "😆", "category": "Smileys & Emotion (face-smiling)", "order": "8"}, + {"emoji": "😓", "name": "downcast face with sweat", "shortname": ":sweat:", "unicode": "1f613", "html": "😓", "category": "Smileys & Emotion (face-concerned)", "order": "42"}, + {"emoji": "👈🏿", "name": "backhand index pointing left: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F448 1F3FF", "html": "👈🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈🏾", "name": "backhand index pointing left: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F448 1F3FE", "html": "👈🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈🏽", "name": "backhand index pointing left: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F448 1F3FD", "html": "👈🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈🏼", "name": "backhand index pointing left: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F448 1F3FC", "html": "👈🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈🏻", "name": "backhand index pointing left: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F448 1F3FB", "html": "👈🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈", "name": "backhand index pointing left", "shortname": ":point_left:", "unicode": "1f448", "html": "👈", "category": "People & Body (hand-single-finger)", "order": "1092"}, + {"emoji": "✔️", "name": "check mark", "shortname": ":heavy_check_mark:", "unicode": "2714", "html": "✔", "category": "Symbols (other-symbol)", "order": "2080"}, + {"emoji": "😻", "name": "smiling cat with heart-eyes", "shortname": ":heart_eyes_cat:", "unicode": "1f63b", "html": "😻", "category": "Smileys & Emotion (cat-face)", "order": "90"}, + {"emoji": "😀", "name": "grinning face", "shortname": ":grinning:", "unicode": "1f600", "html": "😀", "category": "Smileys & Emotion (face-smiling)", "order": "1"}, + {"emoji": "😷", "name": "face with medical mask", "shortname": ":mask:", "unicode": "1f637", "html": "😷", "category": "Smileys & Emotion (face-unwell)", "order": "71"}, + {"emoji": "💚", "name": "green heart", "shortname": ":green_heart:", "unicode": "1f49a", "html": "💚", "category": "Smileys & Emotion (emotion)", "order": "1293"}, + {"emoji": "👋🏿", "name": "waving hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44B 1F3FF", "html": "👋🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋🏾", "name": "waving hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44B 1F3FE", "html": "👋🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋🏽", "name": "waving hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44B 1F3FD", "html": "👋🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋🏼", "name": "waving hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44B 1F3FC", "html": "👋🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋🏻", "name": "waving hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44B 1F3FB", "html": "👋🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋", "name": "waving hand", "shortname": ":wave:", "unicode": "1f44b", "html": "👋", "category": "People & Body (hand-fingers-open)", "order": "1218"}, + {"emoji": "😣", "name": "persevering face", "shortname": ":persevere:", "unicode": "1f623", "html": "😣", "category": "Smileys & Emotion (face-concerned)", "order": "27"}, + {"emoji": "💓", "name": "beating heart", "shortname": ":heartbeat:", "unicode": "1f493", "html": "💓", "category": "Smileys & Emotion (emotion)", "order": "1287"}, + {"emoji": "▶️", "name": "play button", "shortname": ":arrow_forward:", "unicode": "25b6", "html": "▶", "category": "Symbols (av-symbol)", "order": "2051"}, + {"emoji": "◀️", "name": "reverse button", "shortname": ":arrow_backward:", "unicode": "25c0", "html": "◀", "category": "Symbols (av-symbol)", "order": "2055"}, + {"emoji": "↪️", "name": "left arrow curving right", "shortname": ":arrow_right_hook:", "unicode": "21aa", "html": "↪", "category": "Symbols (arrow)", "order": "2013"}, + {"emoji": "↩️", "name": "right arrow curving left", "shortname": ":leftwards_arrow_with_hook:", "unicode": "21a9", "html": "↩", "category": "Symbols (arrow)", "order": "2012"}, + {"emoji": "👑", "name": "crown", "shortname": ":crown:", "unicode": "1f451", "html": "👑", "category": "Objects (clothing)", "order": "1333"}, + {"emoji": "😚", "name": "kissing face with closed eyes", "shortname": ":kissing_closed_eyes:", "unicode": "1f61a", "html": "😚", "category": "Smileys & Emotion (face-affection)", "order": "17"}, + {"emoji": "😛", "name": "face with tongue", "shortname": ":stuck_out_tongue:", "unicode": "1f61b", "html": "😛", "category": "Smileys & Emotion (face-tongue)", "order": "37"}, + {"emoji": "😥", "name": "sad but relieved face", "shortname": ":disappointed_relieved:", "unicode": "1f625", "html": "😥", "category": "Smileys & Emotion (face-concerned)", "order": "28"}, + {"emoji": "😇", "name": "smiling face with halo", "shortname": ":innocent:", "unicode": "1f607", "html": "😇", "category": "Smileys & Emotion (face-smiling)", "order": "67"}, + {"emoji": "🎧", "name": "headphone", "shortname": ":headphones:", "unicode": "1f3a7", "html": "🎧", "category": "Objects (music)", "order": "1830"}, + {"emoji": "✅", "name": "check mark button", "shortname": ":white_check_mark:", "unicode": "2705", "html": "✅", "category": "Symbols (other-symbol)", "order": "2078"}, + {"emoji": "😖", "name": "confounded face", "shortname": ":confounded:", "unicode": "1f616", "html": "😖", "category": "Smileys & Emotion (face-concerned)", "order": "50"}, + {"emoji": "➡", "name": "right arrow", "shortname": ":arrow_right:", "unicode": "27a1", "html": "➡", "category": "Symbols (arrow)", "order": "2004"}, + {"emoji": "😠", "name": "angry face", "shortname": ":angry:", "unicode": "1f620", "html": "😠", "category": "Smileys & Emotion (face-negative)", "order": "66"}, + {"emoji": "😬", "name": "grimacing face", "shortname": ":grimacing:", "unicode": "1f62c", "html": "😬", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "60"}, + {"emoji": "🌟", "name": "glowing star", "shortname": ":star2:", "unicode": "1f31f", "html": "🌟", "category": "Travel & Places (sky & weather)", "order": "1728"}, + {"emoji": "🔫", "name": "pistol", "shortname": ":gun:", "unicode": "1f52b", "html": "🔫", "category": "Objects (tool)", "order": "1956"}, + {"emoji": "🙋🏿‍♂", "name": "man raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF 200D 2642", "html": "🙋🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾‍♂", "name": "man raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE 200D 2642", "html": "🙋🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽‍♂", "name": "man raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD 200D 2642", "html": "🙋🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼‍♂", "name": "man raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC 200D 2642", "html": "🙋🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻‍♂", "name": "man raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB 200D 2642", "html": "🙋🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋‍♂", "name": "man raising hand", "shortname": ":man_raising_hand:", "unicode": "1F64B 200D 2642", "html": "🙋‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏿", "name": "person raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF", "html": "🙋🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾", "name": "person raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE", "html": "🙋🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽", "name": "person raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD", "html": "🙋🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼", "name": "person raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC", "html": "🙋🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻", "name": "person raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB", "html": "🙋🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋", "name": "person raising hand", "shortname": ":raising_hand:", "unicode": "1f64b", "html": "🙋", "category": "People & Body (person-gesture)", "order": "585"}, + {"emoji": "👎🏿", "name": "thumbs down: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44E 1F3FF", "html": "👎🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎🏾", "name": "thumbs down: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44E 1F3FE", "html": "👎🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎🏽", "name": "thumbs down: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44E 1F3FD", "html": "👎🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎🏼", "name": "thumbs down: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44E 1F3FC", "html": "👎🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎🏻", "name": "thumbs down: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44E 1F3FB", "html": "👎🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎", "name": "thumbs down", "shortname": ":thumbsdown:", "unicode": "1f44e", "html": "👎", "category": "People & Body (hand-fingers-closed)", "order": "1182"}, + {"emoji": "💃🏿", "name": "woman dancing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F483 1F3FF", "html": "💃🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃🏾", "name": "woman dancing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F483 1F3FE", "html": "💃🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃🏽", "name": "woman dancing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F483 1F3FD", "html": "💃🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃🏼", "name": "woman dancing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F483 1F3FC", "html": "💃🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃🏻", "name": "woman dancing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F483 1F3FB", "html": "💃🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃", "name": "woman dancing", "shortname": ":dancer:", "unicode": "1f483", "html": "💃", "category": "People & Body (person-activity)", "order": "729"}, + {"emoji": "🎵", "name": "musical note", "shortname": ":musical_note:", "unicode": "1f3b5", "html": "🎵", "category": "Objects (music)", "order": "1824"}, + {"emoji": "😶", "name": "face without mouth", "shortname": ":no_mouth:", "unicode": "1f636", "html": "😶", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "24"}, + {"emoji": "💫", "name": "dizzy", "shortname": ":dizzy:", "unicode": "1f4ab", "html": "💫", "category": "Smileys & Emotion (emotion)", "order": "1308"}, + {"emoji": "✊🏿", "name": "raised fist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "270A 1F3FF", "html": "✊🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊🏾", "name": "raised fist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "270A 1F3FE", "html": "✊🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊🏽", "name": "raised fist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "270A 1F3FD", "html": "✊🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊🏼", "name": "raised fist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "270A 1F3FC", "html": "✊🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊🏻", "name": "raised fist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "270A 1F3FB", "html": "✊🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊", "name": "raised fist", "shortname": ":fist:", "unicode": "270a", "html": "✊", "category": "People & Body (hand-fingers-closed)", "order": "1188"}, + {"emoji": "👇🏿", "name": "backhand index pointing down: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F447 1F3FF", "html": "👇🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇🏾", "name": "backhand index pointing down: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F447 1F3FE", "html": "👇🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇🏽", "name": "backhand index pointing down: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F447 1F3FD", "html": "👇🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇🏼", "name": "backhand index pointing down: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F447 1F3FC", "html": "👇🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇🏻", "name": "backhand index pointing down: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F447 1F3FB", "html": "👇🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇", "name": "backhand index pointing down", "shortname": ":point_down:", "unicode": "1f447", "html": "👇", "category": "People & Body (hand-single-finger)", "order": "1122"}, + {"emoji": "🔴", "name": "red circle", "shortname": ":red_circle:", "unicode": "1f534", "html": "🔴", "category": "Symbols (geometric)", "order": "2179"}, + {"emoji": "🙅🏿‍♂", "name": "man gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF 200D 2642", "html": "🙅🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾‍♂", "name": "man gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE 200D 2642", "html": "🙅🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽‍♂", "name": "man gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD 200D 2642", "html": "🙅🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼‍♂", "name": "man gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC 200D 2642", "html": "🙅🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻‍♂", "name": "man gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB 200D 2642", "html": "🙅🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅‍♂", "name": "man gesturing NO", "shortname": ":man_gesturing_NO:", "unicode": "1F645 200D 2642", "html": "🙅‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏿", "name": "person gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF", "html": "🙅🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾", "name": "person gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE", "html": "🙅🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽", "name": "person gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD", "html": "🙅🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼", "name": "person gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC", "html": "🙅🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻", "name": "person gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB", "html": "🙅🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅", "name": "person gesturing NO", "shortname": ":no_good:", "unicode": "1f645", "html": "🙅", "category": "People & Body (person-gesture)", "order": "531"}, + {"emoji": "💥", "name": "collision", "shortname": ":boom:", "unicode": "1f4a5", "html": "💥", "category": "Smileys & Emotion (emotion)", "order": "1305"}, + {"emoji": "©️", "name": "copyright", "shortname": ":copyright:", "unicode": "00A9 FE0F", "html": "©️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "💭", "name": "thought balloon", "shortname": ":thought_balloon:", "unicode": "1f4ad", "html": "💭", "category": "Smileys & Emotion (emotion)", "order": "1312"}, + {"emoji": "👅", "name": "tongue", "shortname": ":tongue:", "unicode": "1f445", "html": "👅", "category": "People & Body (body-parts)", "order": "1282"}, + {"emoji": "💩", "name": "pile of poo", "shortname": ":poop:", "unicode": "1f4a9", "html": "💩", "category": "Smileys & Emotion (face-costume)", "order": "85"}, + {"emoji": "😰", "name": "anxious face with sweat", "shortname": ":cold_sweat:", "unicode": "1f630", "html": "😰", "category": "Smileys & Emotion (face-concerned)", "order": "61"}, + {"emoji": "💎", "name": "gem stone", "shortname": ":gem:", "unicode": "1f48e", "html": "💎", "category": "Objects (clothing)", "order": "1341"}, + {"emoji": "🙆🏿‍♂", "name": "man gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF 200D 2642", "html": "🙆🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾‍♂", "name": "man gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE 200D 2642", "html": "🙆🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽‍♂", "name": "man gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD 200D 2642", "html": "🙆🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼‍♂", "name": "man gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC 200D 2642", "html": "🙆🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻‍♂", "name": "man gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB 200D 2642", "html": "🙆🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆‍♂", "name": "man gesturing OK", "shortname": ":man_gesturing_OK:", "unicode": "1F646 200D 2642", "html": "🙆‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏿", "name": "person gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF", "html": "🙆🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾", "name": "person gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE", "html": "🙆🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽", "name": "person gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD", "html": "🙆🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼", "name": "person gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC", "html": "🙆🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻", "name": "person gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB", "html": "🙆🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆", "name": "person gesturing OK", "shortname": ":ok_woman:", "unicode": "1f646", "html": "🙆", "category": "People & Body (person-gesture)", "order": "549"}, + {"emoji": "🍕", "name": "pizza", "shortname": ":pizza:", "unicode": "1f355", "html": "🍕", "category": "Food & Drink (food-prepared)", "order": "1484"}, + {"emoji": "😹", "name": "cat with tears of joy", "shortname": ":joy_cat:", "unicode": "1f639", "html": "😹", "category": "Smileys & Emotion (cat-face)", "order": "89"}, + {"emoji": "🌞", "name": "sun with face", "shortname": ":sun_with_face:", "unicode": "1f31e", "html": "🌞", "category": "Travel & Places (sky & weather)", "order": "1726"}, + {"emoji": "🍃", "name": "leaf fluttering in wind", "shortname": ":leaves:", "unicode": "1f343", "html": "🍃", "category": "Animals & Nature (plant-other)", "order": "1448"}, + {"emoji": "💦", "name": "sweat droplets", "shortname": ":sweat_drops:", "unicode": "1f4a6", "html": "💦", "category": "Smileys & Emotion (emotion)", "order": "1306"}, + {"emoji": "🐧", "name": "penguin", "shortname": ":penguin:", "unicode": "1f427", "html": "🐧", "category": "Animals & Nature (animal-bird)", "order": "1394"}, + {"emoji": "💤", "name": "zzz", "shortname": ":zzz:", "unicode": "1f4a4", "html": "💤", "category": "Smileys & Emotion (emotion)", "order": "1302"}, + {"emoji": "🚶🏿‍♀", "name": "woman walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF 200D 2640", "html": "🚶🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾‍♀", "name": "woman walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE 200D 2640", "html": "🚶🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽‍♀", "name": "woman walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD 200D 2640", "html": "🚶🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼‍♀", "name": "woman walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC 200D 2640", "html": "🚶🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻‍♀", "name": "woman walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB 200D 2640", "html": "🚶🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶‍♀", "name": "woman walking", "shortname": ":woman_walking:", "unicode": "1F6B6 200D 2640", "html": "🚶‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏿", "name": "person walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF", "html": "🚶🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾", "name": "person walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE", "html": "🚶🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽", "name": "person walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD", "html": "🚶🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼", "name": "person walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC", "html": "🚶🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻", "name": "person walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB", "html": "🚶🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶", "name": "person walking", "shortname": ":walking:", "unicode": "1f6b6", "html": "🚶", "category": "People & Body (person-activity)", "order": "693"}, + {"emoji": "✈️", "name": "airplane", "shortname": ":airplane:", "unicode": "2708", "html": "✈", "category": "Travel & Places (transport-air)", "order": "1650"}, + {"emoji": "🎈", "name": "balloon", "shortname": ":balloon:", "unicode": "1f388", "html": "🎈", "category": "Activities (event)", "order": "1761"}, + {"emoji": "⭐", "name": "star", "shortname": ":star:", "unicode": "2b50", "html": "⭐", "category": "Travel & Places (sky & weather)", "order": "1727"}, + {"emoji": "🎀", "name": "ribbon", "shortname": ":ribbon:", "unicode": "1f380", "html": "🎀", "category": "Activities (event)", "order": "1770"}, + {"emoji": "☑️", "name": "check box with check", "shortname": ":ballot_box_with_check:", "unicode": "2611", "html": "☑", "category": "Symbols (other-symbol)", "order": "2079"}, + {"emoji": "😟", "name": "worried face", "shortname": ":worried:", "unicode": "1f61f", "html": "😟", "category": "Smileys & Emotion (face-concerned)", "order": "52"}, + {"emoji": "🔞", "name": "no one under eighteen", "shortname": ":underage:", "unicode": "1f51e", "html": "🔞", "category": "Symbols (warning)", "order": "1999"}, + {"emoji": "😨", "name": "fearful face", "shortname": ":fearful:", "unicode": "1f628", "html": "😨", "category": "Smileys & Emotion (face-concerned)", "order": "58"}, + {"emoji": "🍀", "name": "four leaf clover", "shortname": ":four_leaf_clover:", "unicode": "1f340", "html": "🍀", "category": "Animals & Nature (plant-other)", "order": "1445"}, + {"emoji": "🌺", "name": "hibiscus", "shortname": ":hibiscus:", "unicode": "1f33a", "html": "🌺", "category": "Animals & Nature (plant-flower)", "order": "1433"}, + {"emoji": "🎤", "name": "microphone", "shortname": ":microphone:", "unicode": "1f3a4", "html": "🎤", "category": "Objects (music)", "order": "1829"}, + {"emoji": "👐🏿", "name": "open hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F450 1F3FF", "html": "👐🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐🏾", "name": "open hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F450 1F3FE", "html": "👐🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐🏽", "name": "open hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F450 1F3FD", "html": "👐🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐🏼", "name": "open hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F450 1F3FC", "html": "👐🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐🏻", "name": "open hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F450 1F3FB", "html": "👐🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐", "name": "open hands", "shortname": ":open_hands:", "unicode": "1f450", "html": "👐", "category": "People & Body (hands)", "order": "1236"}, + {"emoji": "👻", "name": "ghost", "shortname": ":ghost:", "unicode": "1f47b", "html": "👻", "category": "Smileys & Emotion (face-costume)", "order": "82"}, + {"emoji": "🌴", "name": "palm tree", "shortname": ":palm_tree:", "unicode": "1f334", "html": "🌴", "category": "Animals & Nature (plant-other)", "order": "1440"}, + {"emoji": "‼️", "name": "double exclamation mark", "shortname": ":bangbang:", "unicode": "203c", "html": "‼", "category": "Symbols (other-symbol)", "order": "2096"}, + {"emoji": "💅🏿", "name": "nail polish: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F485 1F3FF", "html": "💅🏿", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅🏾", "name": "nail polish: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F485 1F3FE", "html": "💅🏾", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅🏽", "name": "nail polish: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F485 1F3FD", "html": "💅🏽", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅🏼", "name": "nail polish: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F485 1F3FC", "html": "💅🏼", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅🏻", "name": "nail polish: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F485 1F3FB", "html": "💅🏻", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅", "name": "nail polish", "shortname": ":nail_care:", "unicode": "1f485", "html": "💅", "category": "People & Body (hand-prop)", "order": "1260"}, + {"emoji": "❌", "name": "cross mark", "shortname": ":x:", "unicode": "274c", "html": "❌", "category": "Symbols (other-symbol)", "order": "2082"}, + {"emoji": "👽", "name": "alien", "shortname": ":alien:", "unicode": "1f47d", "html": "👽", "category": "Smileys & Emotion (face-costume)", "order": "83"}, + {"emoji": "🙇🏿‍♀", "name": "woman bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF 200D 2640", "html": "🙇🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾‍♀", "name": "woman bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE 200D 2640", "html": "🙇🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽‍♀", "name": "woman bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD 200D 2640", "html": "🙇🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼‍♀", "name": "woman bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC 200D 2640", "html": "🙇🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻‍♀", "name": "woman bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB 200D 2640", "html": "🙇🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇‍♀", "name": "woman bowing", "shortname": ":woman_bowing:", "unicode": "1F647 200D 2640", "html": "🙇‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏿", "name": "person bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF", "html": "🙇🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾", "name": "person bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE", "html": "🙇🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽", "name": "person bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD", "html": "🙇🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼", "name": "person bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC", "html": "🙇🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻", "name": "person bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB", "html": "🙇🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇", "name": "person bowing", "shortname": ":bow:", "unicode": "1f647", "html": "🙇", "category": "People & Body (person-gesture)", "order": "603"}, + {"emoji": "☁", "name": "cloud", "shortname": ":cloud:", "unicode": "2601", "html": "☁", "category": "Travel & Places (sky & weather)", "order": "1730"}, + {"emoji": "⚽", "name": "soccer ball", "shortname": ":soccer:", "unicode": "26bd", "html": "⚽", "category": "Activities (sport)", "order": "1781"}, + {"emoji": "👼🏿", "name": "baby angel: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F47C 1F3FF", "html": "👼🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼🏾", "name": "baby angel: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F47C 1F3FE", "html": "👼🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼🏽", "name": "baby angel: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F47C 1F3FD", "html": "👼🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼🏼", "name": "baby angel: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F47C 1F3FC", "html": "👼🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼🏻", "name": "baby angel: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F47C 1F3FB", "html": "👼🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼", "name": "baby angel", "shortname": ":angel:", "unicode": "1f47c", "html": "👼", "category": "People & Body (person-fantasy)", "order": "141"}, + {"emoji": "👯‍♂", "name": "men with bunny ears", "shortname": ":men_with_bunny_ears:", "unicode": "1F46F 200D 2642", "html": "👯‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👯", "name": "people with bunny ears", "shortname": ":dancers:", "unicode": "1f46f", "html": "👯", "category": "People & Body (person-activity)", "order": "741"}, + {"emoji": "❗", "name": "exclamation mark", "shortname": ":exclamation:", "unicode": "2757", "html": "❗", "category": "Symbols (other-symbol)", "order": "2101"}, + {"emoji": "❄️", "name": "snowflake", "shortname": ":snowflake:", "unicode": "2744", "html": "❄", "category": "Travel & Places (sky & weather)", "order": "1749"}, + {"emoji": "☝🏿", "name": "index pointing up: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "261D 1F3FF", "html": "☝🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝🏾", "name": "index pointing up: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "261D 1F3FE", "html": "☝🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝🏽", "name": "index pointing up: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "261D 1F3FD", "html": "☝🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝🏼", "name": "index pointing up: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "261D 1F3FC", "html": "☝🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝🏻", "name": "index pointing up: light skin tone", "shortname": ":light_skin_tone:", "unicode": "261D 1F3FB", "html": "☝🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝️", "name": "index pointing up", "shortname": ":point_up:", "unicode": "261d", "html": "☝", "category": "People & Body (hand-single-finger)", "order": "1104"}, + {"emoji": "😙", "name": "kissing face with smiling eyes", "shortname": ":kissing_smiling_eyes:", "unicode": "1f619", "html": "😙", "category": "Smileys & Emotion (face-affection)", "order": "16"}, + {"emoji": "🌈", "name": "rainbow", "shortname": ":rainbow:", "unicode": "1f308", "html": "🌈", "category": "Travel & Places (sky & weather)", "order": "1743"}, + {"emoji": "🌙", "name": "crescent moon", "shortname": ":crescent_moon:", "unicode": "1f319", "html": "🌙", "category": "Travel & Places (sky & weather)", "order": "1719"}, + {"emoji": "💟", "name": "heart decoration", "shortname": ":heart_decoration:", "unicode": "1f49f", "html": "💟", "category": "Smileys & Emotion (emotion)", "order": "1299"}, + {"emoji": "💝", "name": "heart with ribbon", "shortname": ":gift_heart:", "unicode": "1f49d", "html": "💝", "category": "Smileys & Emotion (emotion)", "order": "1297"}, + {"emoji": "🎁", "name": "wrapped gift", "shortname": ":gift:", "unicode": "1f381", "html": "🎁", "category": "Activities (event)", "order": "1771"}, + {"emoji": "🍻", "name": "clinking beer mugs", "shortname": ":beers:", "unicode": "1f37b", "html": "🍻", "category": "Food & Drink (drink)", "order": "1530"}, + {"emoji": "😧", "name": "anguished face", "shortname": ":anguished:", "unicode": "1f627", "html": "😧", "category": "Smileys & Emotion (face-concerned)", "order": "57"}, + {"emoji": "🌍", "name": "globe showing Europe-Africa", "shortname": ":earth_africa:", "unicode": "1f30d", "html": "🌍", "category": "Travel & Places (place-map)", "order": "1538"}, + {"emoji": "🎥", "name": "movie camera", "shortname": ":movie_camera:", "unicode": "1f3a5", "html": "🎥", "category": "Objects (light & video)", "order": "1856"}, + {"emoji": "⚓", "name": "anchor", "shortname": ":anchor:", "unicode": "2693", "html": "⚓", "category": "Travel & Places (transport-water)", "order": "1642"}, + {"emoji": "⚡", "name": "high voltage", "shortname": ":zap:", "unicode": "26a1", "html": "⚡", "category": "Travel & Places (sky & weather)", "order": "1748"}, + {"emoji": "♣️", "name": "club suit", "shortname": ":club_suit:", "unicode": "2663 FE0F", "html": "♣️", "category": "Activities (game)", "order": ""}, + {"emoji": "✖️", "name": "multiplication sign", "shortname": ":heavy_multiplication_x:", "unicode": "2716", "html": "✖", "category": "Symbols (other-symbol)", "order": "2081"}, + {"emoji": "🏃🏿‍♀", "name": "woman running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF 200D 2640", "html": "🏃🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾‍♀", "name": "woman running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE 200D 2640", "html": "🏃🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽‍♀", "name": "woman running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD 200D 2640", "html": "🏃🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼‍♀", "name": "woman running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC 200D 2640", "html": "🏃🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻‍♀", "name": "woman running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB 200D 2640", "html": "🏃🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃‍♀", "name": "woman running", "shortname": ":woman_running:", "unicode": "1F3C3 200D 2640", "html": "🏃‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏿", "name": "person running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF", "html": "🏃🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾", "name": "person running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE", "html": "🏃🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽", "name": "person running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD", "html": "🏃🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼", "name": "person running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC", "html": "🏃🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻", "name": "person running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB", "html": "🏃🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃", "name": "person running", "shortname": ":runner:", "unicode": "1f3c3", "html": "🏃", "category": "People & Body (person-activity)", "order": "711"}, + {"emoji": "🌻", "name": "sunflower", "shortname": ":sunflower:", "unicode": "1f33b", "html": "🌻", "category": "Animals & Nature (plant-flower)", "order": "1434"}, + {"emoji": "🌎", "name": "globe showing Americas", "shortname": ":earth_americas:", "unicode": "1f30e", "html": "🌎", "category": "Travel & Places (place-map)", "order": "1539"}, + {"emoji": "💐", "name": "bouquet", "shortname": ":bouquet:", "unicode": "1f490", "html": "💐", "category": "Animals & Nature (plant-flower)", "order": "1427"}, + {"emoji": "🐶", "name": "dog face", "shortname": ":dog:", "unicode": "1f436", "html": "🐶", "category": "Animals & Nature (animal-mammal)", "order": "1345"}, + {"emoji": "💰", "name": "money bag", "shortname": ":moneybag:", "unicode": "1f4b0", "html": "💰", "category": "Objects (money)", "order": "1891"}, + {"emoji": "🌿", "name": "herb", "shortname": ":herb:", "unicode": "1f33f", "html": "🌿", "category": "Animals & Nature (plant-other)", "order": "1443"}, + {"emoji": "👫", "name": "woman and man holding hands", "shortname": ":couple:", "unicode": "1f46b", "html": "👫", "category": "People & Body (family)", "order": "1018"}, + {"emoji": "🍂", "name": "fallen leaf", "shortname": ":fallen_leaf:", "unicode": "1f342", "html": "🍂", "category": "Animals & Nature (plant-other)", "order": "1447"}, + {"emoji": "🌷", "name": "tulip", "shortname": ":tulip:", "unicode": "1f337", "html": "🌷", "category": "Animals & Nature (plant-flower)", "order": "1436"}, + {"emoji": "🎂", "name": "birthday cake", "shortname": ":birthday:", "unicode": "1f382", "html": "🎂", "category": "Food & Drink (food-sweet)", "order": "1513"}, + {"emoji": "🐱", "name": "cat face", "shortname": ":cat:", "unicode": "1f431", "html": "🐱", "category": "Animals & Nature (animal-mammal)", "order": "1350"}, + {"emoji": "☕", "name": "hot beverage", "shortname": ":coffee:", "unicode": "2615", "html": "☕", "category": "Food & Drink (drink)", "order": "1522"}, + {"emoji": "😵", "name": "dizzy face", "shortname": ":dizzy_face:", "unicode": "1f635", "html": "😵", "category": "Smileys & Emotion (face-unwell)", "order": "64"}, + {"emoji": "👆🏿", "name": "backhand index pointing up: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F446 1F3FF", "html": "👆🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆🏾", "name": "backhand index pointing up: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F446 1F3FE", "html": "👆🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆🏽", "name": "backhand index pointing up: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F446 1F3FD", "html": "👆🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆🏼", "name": "backhand index pointing up: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F446 1F3FC", "html": "👆🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆🏻", "name": "backhand index pointing up: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F446 1F3FB", "html": "👆🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆", "name": "backhand index pointing up", "shortname": ":point_up_2:", "unicode": "1f446", "html": "👆", "category": "People & Body (hand-single-finger)", "order": "1110"}, + {"emoji": "😮", "name": "face with open mouth", "shortname": ":open_mouth:", "unicode": "1f62e", "html": "😮", "category": "Smileys & Emotion (face-concerned)", "order": "29"}, + {"emoji": "😯", "name": "hushed face", "shortname": ":hushed:", "unicode": "1f62f", "html": "😯", "category": "Smileys & Emotion (face-concerned)", "order": "31"}, + {"emoji": "🏀", "name": "basketball", "shortname": ":basketball:", "unicode": "1f3c0", "html": "🏀", "category": "Activities (sport)", "order": "1783"}, + {"emoji": "🎄", "name": "Christmas tree", "shortname": ":christmas_tree:", "unicode": "1f384", "html": "🎄", "category": "Activities (event)", "order": "1757"}, + {"emoji": "💍", "name": "ring", "shortname": ":ring:", "unicode": "1f48d", "html": "💍", "category": "Objects (clothing)", "order": "1340"}, + {"emoji": "🌝", "name": "full moon face", "shortname": ":full_moon_with_face:", "unicode": "1f31d", "html": "🌝", "category": "Travel & Places (sky & weather)", "order": "1725"}, + {"emoji": "😲", "name": "astonished face", "shortname": ":astonished:", "unicode": "1f632", "html": "😲", "category": "Smileys & Emotion (face-concerned)", "order": "47"}, + {"emoji": "👭", "name": "women holding hands", "shortname": ":two_women_holding_hands:", "unicode": "1f46d", "html": "👭", "category": "People & Body (family)", "order": "1030"}, + {"emoji": "💸", "name": "money with wings", "shortname": ":money_with_wings:", "unicode": "1f4b8", "html": "💸", "category": "Objects (money)", "order": "1896"}, + {"emoji": "😿", "name": "crying cat", "shortname": ":crying_cat_face:", "unicode": "1f63f", "html": "😿", "category": "Smileys & Emotion (cat-face)", "order": "94"}, + {"emoji": "🙉", "name": "hear-no-evil monkey", "shortname": ":hear_no_evil:", "unicode": "1f649", "html": "🙉", "category": "Smileys & Emotion (monkey-face)", "order": "97"}, + {"emoji": "💨", "name": "dashing away", "shortname": ":dash:", "unicode": "1f4a8", "html": "💨", "category": "Smileys & Emotion (emotion)", "order": "1307"}, + {"emoji": "🌵", "name": "cactus", "shortname": ":cactus:", "unicode": "1f335", "html": "🌵", "category": "Animals & Nature (plant-other)", "order": "1441"}, + {"emoji": "♨️", "name": "hot springs", "shortname": ":hotsprings:", "unicode": "2668", "html": "♨", "category": "Travel & Places (place-other)", "order": "1591"}, + {"emoji": "☎️", "name": "telephone", "shortname": ":telephone:", "unicode": "260e", "html": "☎", "category": "Objects (phone)", "order": "1840"}, + {"emoji": "🍁", "name": "maple leaf", "shortname": ":maple_leaf:", "unicode": "1f341", "html": "🍁", "category": "Animals & Nature (plant-other)", "order": "1446"}, + {"emoji": "👸🏿", "name": "princess: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F478 1F3FF", "html": "👸🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸🏾", "name": "princess: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F478 1F3FE", "html": "👸🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸🏽", "name": "princess: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F478 1F3FD", "html": "👸🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸🏼", "name": "princess: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F478 1F3FC", "html": "👸🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸🏻", "name": "princess: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F478 1F3FB", "html": "👸🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸", "name": "princess", "shortname": ":princess:", "unicode": "1f478", "html": "👸", "category": "People & Body (person-role)", "order": "459"}, + {"emoji": "💆🏿‍♂", "name": "man getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF 200D 2642", "html": "💆🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾‍♂", "name": "man getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE 200D 2642", "html": "💆🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽‍♂", "name": "man getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD 200D 2642", "html": "💆🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼‍♂", "name": "man getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC 200D 2642", "html": "💆🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻‍♂", "name": "man getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB 200D 2642", "html": "💆🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆‍♂", "name": "man getting massage", "shortname": ":man_getting_massage:", "unicode": "1F486 200D 2642", "html": "💆‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏿", "name": "person getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF", "html": "💆🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾", "name": "person getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE", "html": "💆🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽", "name": "person getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD", "html": "💆🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼", "name": "person getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC", "html": "💆🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻", "name": "person getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB", "html": "💆🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆", "name": "person getting massage", "shortname": ":massage:", "unicode": "1f486", "html": "💆", "category": "People & Body (person-activity)", "order": "657"}, + {"emoji": "💌", "name": "love letter", "shortname": ":love_letter:", "unicode": "1f48c", "html": "💌", "category": "Smileys & Emotion (emotion)", "order": "1301"}, + {"emoji": "🏆", "name": "trophy", "shortname": ":trophy:", "unicode": "1f3c6", "html": "🏆", "category": "Activities (award-medal)", "order": "1776"}, + {"emoji": "🙍🏿‍♂", "name": "man frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF 200D 2642", "html": "🙍🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾‍♂", "name": "man frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE 200D 2642", "html": "🙍🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽‍♂", "name": "man frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD 200D 2642", "html": "🙍🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼‍♂", "name": "man frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC 200D 2642", "html": "🙍🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻‍♂", "name": "man frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB 200D 2642", "html": "🙍🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍‍♂", "name": "man frowning", "shortname": ":man_frowning:", "unicode": "1F64D 200D 2642", "html": "🙍‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏿", "name": "person frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF", "html": "🙍🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾", "name": "person frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE", "html": "🙍🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽", "name": "person frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD", "html": "🙍🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼", "name": "person frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC", "html": "🙍🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻", "name": "person frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB", "html": "🙍🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍", "name": "person frowning", "shortname": ":person_frowning:", "unicode": "1f64d", "html": "🙍", "category": "People & Body (person-gesture)", "order": "495"}, + {"emoji": "🇺🇸", "name": "flag: United States", "shortname": ":us:", "unicode": "1f1fa", "html": "🇺", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🎊", "name": "confetti ball", "shortname": ":confetti_ball:", "unicode": "1f38a", "html": "🎊", "category": "Activities (event)", "order": "1763"}, + {"emoji": "🌼", "name": "blossom", "shortname": ":blossom:", "unicode": "1f33c", "html": "🌼", "category": "Animals & Nature (plant-flower)", "order": "1435"}, + {"emoji": "🔪", "name": "kitchen knife", "shortname": ":kitchen_knife:", "unicode": "1F52A", "html": "🔪", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "👄", "name": "mouth", "shortname": ":lips:", "unicode": "1f444", "html": "👄", "category": "People & Body (body-parts)", "order": "1283"}, + {"emoji": "🍟", "name": "french fries", "shortname": ":fries:", "unicode": "1f35f", "html": "🍟", "category": "Food & Drink (food-prepared)", "order": "1483"}, + {"emoji": "🍩", "name": "doughnut", "shortname": ":doughnut:", "unicode": "1f369", "html": "🍩", "category": "Food & Drink (food-sweet)", "order": "1511"}, + {"emoji": "😦", "name": "frowning face with open mouth", "shortname": ":frowning:", "unicode": "1f626", "html": "😦", "category": "Smileys & Emotion (face-concerned)", "order": "56"}, + {"emoji": "🌊", "name": "water wave", "shortname": ":ocean:", "unicode": "1f30a", "html": "🌊", "category": "Travel & Places (sky & weather)", "order": "1755"}, + {"emoji": "💣", "name": "bomb", "shortname": ":bomb:", "unicode": "1f4a3", "html": "💣", "category": "Smileys & Emotion (emotion)", "order": "1304"}, + {"emoji": "🆗", "name": "OK button", "shortname": ":ok:", "unicode": "1f197", "html": "🆗", "category": "Symbols (alphanum)", "order": "2137"}, + {"emoji": "🌀", "name": "cyclone", "shortname": ":cyclone:", "unicode": "1f300", "html": "🌀", "category": "Travel & Places (sky & weather)", "order": "1742"}, + {"emoji": "🚀", "name": "rocket", "shortname": ":rocket:", "unicode": "1f680", "html": "🚀", "category": "Travel & Places (transport-air)", "order": "1659"}, + {"emoji": "☔", "name": "umbrella with rain drops", "shortname": ":umbrella:", "unicode": "2614", "html": "☔", "category": "Travel & Places (sky & weather)", "order": "1746"}, + {"emoji": "💏", "name": "kiss", "shortname": ":couplekiss:", "unicode": "1f48f", "html": "💏", "category": "People & Body (family)", "order": "1036"}, + {"emoji": "👩‍❤️‍💋‍👩", "name": "kiss: woman, woman", "shortname": ":couple_woman_kiss:", "unicode": "1F469 200D 2764 FE0F 200D 1F48B 200D 1F469", "html": "👩‍❤‍💋‍👩", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍❤️‍💋‍👨", "name": "kiss: man, man", "shortname": ":couple_man_kiss:", "unicode": "1F468 200D 2764 FE0F 200D 1F48B 200D 1F468", "html": "👨‍❤‍💋‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "💑", "name": "couple with heart", "shortname": ":couple_with_heart:", "unicode": "1f491", "html": "💑", "category": "People & Body (family)", "order": "1040"}, + {"emoji": "👩‍❤️‍👩", "name": "couple with heart: woman, woman", "shortname": ":woman_woman:", "unicode": "1F469 200D 2764 FE0F 200D 1F469", "html": "👩‍❤‍👩", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍❤️‍👨", "name": "couple with heart: man, man", "shortname": ":man_man_love:", "unicode": "1F468 200D 2764 FE0F 200D 1F468", "html": "👨‍❤‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "🍭", "name": "lollipop", "shortname": ":lollipop:", "unicode": "1f36d", "html": "🍭", "category": "Food & Drink (food-sweet)", "order": "1517"}, + {"emoji": "🎬", "name": "clapper board", "shortname": ":clapper:", "unicode": "1f3ac", "html": "🎬", "category": "Objects (light & video)", "order": "1859"}, + {"emoji": "🐷", "name": "pig face", "shortname": ":pig:", "unicode": "1f437", "html": "🐷", "category": "Animals & Nature (animal-mammal)", "order": "1364"}, + {"emoji": "😈", "name": "smiling face with horns", "shortname": ":smiling_imp:", "unicode": "1f608", "html": "😈", "category": "Smileys & Emotion (face-negative)", "order": "76"}, + {"emoji": "👿", "name": "angry face with horns", "shortname": ":imp:", "unicode": "1f47f", "html": "👿", "category": "Smileys & Emotion (face-negative)", "order": "77"}, + {"emoji": "🐝", "name": "honeybee", "shortname": ":bee:", "unicode": "1f41d", "html": "🐝", "category": "Animals & Nature (animal-bug)", "order": "1422"}, + {"emoji": "😽", "name": "kissing cat", "shortname": ":kissing_cat:", "unicode": "1f63d", "html": "😽", "category": "Smileys & Emotion (cat-face)", "order": "92"}, + {"emoji": "💢", "name": "anger symbol", "shortname": ":anger:", "unicode": "1f4a2", "html": "💢", "category": "Smileys & Emotion (emotion)", "order": "1303"}, + {"emoji": "🎼", "name": "musical score", "shortname": ":musical_score:", "unicode": "1f3bc", "html": "🎼", "category": "Objects (music)", "order": "1823"}, + {"emoji": "🎅🏿", "name": "Santa Claus: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F385 1F3FF", "html": "🎅🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅🏾", "name": "Santa Claus: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F385 1F3FE", "html": "🎅🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅🏽", "name": "Santa Claus: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F385 1F3FD", "html": "🎅🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅🏼", "name": "Santa Claus: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F385 1F3FC", "html": "🎅🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅🏻", "name": "Santa Claus: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F385 1F3FB", "html": "🎅🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅", "name": "Santa Claus", "shortname": ":santa:", "unicode": "1f385", "html": "🎅", "category": "People & Body (person-fantasy)", "order": "447"}, + {"emoji": "🌏", "name": "globe showing Asia-Australia", "shortname": ":earth_asia:", "unicode": "1f30f", "html": "🌏", "category": "Travel & Places (place-map)", "order": "1540"}, + {"emoji": "🏈", "name": "american football", "shortname": ":football:", "unicode": "1f3c8", "html": "🏈", "category": "Activities (sport)", "order": "1785"}, + {"emoji": "🎸", "name": "guitar", "shortname": ":guitar:", "unicode": "1f3b8", "html": "🎸", "category": "Objects (musical-instrument)", "order": "1833"}, + {"emoji": "♦️", "name": "diamond suit", "shortname": ":diamond_suit:", "unicode": "2666 FE0F", "html": "♦️", "category": "Activities (game)", "order": ""}, + {"emoji": "🐼", "name": "panda", "shortname": ":panda_face:", "unicode": "1f43c", "html": "🐼", "category": "Animals & Nature (animal-mammal)", "order": "1385"}, + {"emoji": "💬", "name": "speech balloon", "shortname": ":speech_balloon:", "unicode": "1f4ac", "html": "💬", "category": "Smileys & Emotion (emotion)", "order": "1309"}, + {"emoji": "🍓", "name": "strawberry", "shortname": ":strawberry:", "unicode": "1f353", "html": "🍓", "category": "Food & Drink (food-fruit)", "order": "1461"}, + {"emoji": "😼", "name": "cat with wry smile", "shortname": ":smirk_cat:", "unicode": "1f63c", "html": "😼", "category": "Smileys & Emotion (cat-face)", "order": "91"}, + {"emoji": "🍌", "name": "banana", "shortname": ":banana:", "unicode": "1f34c", "html": "🍌", "category": "Food & Drink (food-fruit)", "order": "1454"}, + {"emoji": "🍉", "name": "watermelon", "shortname": ":watermelon:", "unicode": "1f349", "html": "🍉", "category": "Food & Drink (food-fruit)", "order": "1451"}, + {"emoji": "⛄", "name": "snowman without snow", "shortname": ":snowman:", "unicode": "26c4", "html": "⛄", "category": "Travel & Places (sky & weather)", "order": "1751"}, + {"emoji": "😸", "name": "grinning cat with smiling eyes", "shortname": ":smile_cat:", "unicode": "1f638", "html": "😸", "category": "Smileys & Emotion (cat-face)", "order": "88"}, + {"emoji": "♠️", "name": "spade suit", "shortname": ":spade_suit:", "unicode": "2660 FE0F", "html": "♠️", "category": "Activities (game)", "order": ""}, + {"emoji": "🔝", "name": "TOP arrow", "shortname": ":top:", "unicode": "1f51d", "html": "🔝", "category": "Symbols (arrow)", "order": "2022"}, + {"emoji": "🍆", "name": "eggplant", "shortname": ":eggplant:", "unicode": "1f346", "html": "🍆", "category": "Food & Drink (food-vegetable)", "order": "1465"}, + {"emoji": "🔮", "name": "crystal ball", "shortname": ":crystal_ball:", "unicode": "1f52e", "html": "🔮", "category": "Activities (game)", "order": "1974"}, + {"emoji": "🍴", "name": "fork and knife", "shortname": ":fork_and_knife:", "unicode": "1f374", "html": "🍴", "category": "Food & Drink (dishware)", "order": "1534"}, + {"emoji": "📲", "name": "mobile phone with arrow", "shortname": ":calling:", "unicode": "1f4f2", "html": "📲", "category": "Objects (phone)", "order": "1839"}, + {"emoji": "📱", "name": "mobile phone", "shortname": ":iphone:", "unicode": "1f4f1", "html": "📱", "category": "Objects (phone)", "order": "1838"}, + {"emoji": "⛅", "name": "sun behind cloud", "shortname": ":partly_sunny:", "unicode": "26c5", "html": "⛅", "category": "Travel & Places (sky & weather)", "order": "1731"}, + {"emoji": "⚠️", "name": "warning", "shortname": ":warning:", "unicode": "26a0", "html": "⚠", "category": "Symbols (warning)", "order": "1989"}, + {"emoji": "🙀", "name": "weary cat", "shortname": ":scream_cat:", "unicode": "1f640", "html": "🙀", "category": "Smileys & Emotion (cat-face)", "order": "93"}, + {"emoji": "🔸", "name": "small orange diamond", "shortname": ":small_orange_diamond:", "unicode": "1f538", "html": "🔸", "category": "Symbols (geometric)", "order": "2169"}, + {"emoji": "👶🏿", "name": "baby: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F476 1F3FF", "html": "👶🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶🏾", "name": "baby: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F476 1F3FE", "html": "👶🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶🏽", "name": "baby: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F476 1F3FD", "html": "👶🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶🏼", "name": "baby: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F476 1F3FC", "html": "👶🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶🏻", "name": "baby: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F476 1F3FB", "html": "👶🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶", "name": "baby", "shortname": ":baby:", "unicode": "1f476", "html": "👶", "category": "People & Body (person)", "order": "135"}, + {"emoji": "🐾", "name": "paw prints", "shortname": ":feet:", "unicode": "1f43e", "html": "🐾", "category": "Animals & Nature (animal-mammal)", "order": "1386"}, + {"emoji": "👣", "name": "footprints", "shortname": ":footprints:", "unicode": "1f463", "html": "👣", "category": "People & Body (person-symbol)", "order": "1278"}, + {"emoji": "🍺", "name": "beer mug", "shortname": ":beer:", "unicode": "1f37a", "html": "🍺", "category": "Food & Drink (drink)", "order": "1529"}, + {"emoji": "🍷", "name": "wine glass", "shortname": ":wine_glass:", "unicode": "1f377", "html": "🍷", "category": "Food & Drink (drink)", "order": "1526"}, + {"emoji": "⭕", "name": "hollow red circle", "shortname": ":o:", "unicode": "2b55", "html": "⭕", "category": "Symbols (other-symbol)", "order": "2077"}, + {"emoji": "📹", "name": "video camera", "shortname": ":video_camera:", "unicode": "1f4f9", "html": "📹", "category": "Objects (light & video)", "order": "1863"}, + {"emoji": "🐰", "name": "rabbit face", "shortname": ":rabbit:", "unicode": "1f430", "html": "🐰", "category": "Animals & Nature (animal-mammal)", "order": "1379"}, + {"emoji": "🍹", "name": "tropical drink", "shortname": ":tropical_drink:", "unicode": "1f379", "html": "🍹", "category": "Food & Drink (drink)", "order": "1528"}, + {"emoji": "🚬", "name": "cigarette", "shortname": ":smoking:", "unicode": "1f6ac", "html": "🚬", "category": "Objects (other-object)", "order": "1969"}, + {"emoji": "👾", "name": "alien monster", "shortname": ":space_invader:", "unicode": "1f47e", "html": "👾", "category": "Smileys & Emotion (face-costume)", "order": "84"}, + {"emoji": "🍑", "name": "peach", "shortname": ":peach:", "unicode": "1f351", "html": "🍑", "category": "Food & Drink (food-fruit)", "order": "1459"}, + {"emoji": "🐍", "name": "snake", "shortname": ":snake:", "unicode": "1f40d", "html": "🐍", "category": "Animals & Nature (animal-reptile)", "order": "1403"}, + {"emoji": "🐢", "name": "turtle", "shortname": ":turtle:", "unicode": "1f422", "html": "🐢", "category": "Animals & Nature (animal-reptile)", "order": "1401"}, + {"emoji": "🍒", "name": "cherries", "shortname": ":cherries:", "unicode": "1f352", "html": "🍒", "category": "Food & Drink (food-fruit)", "order": "1460"}, + {"emoji": "😗", "name": "kissing face", "shortname": ":kissing:", "unicode": "1f617", "html": "😗", "category": "Smileys & Emotion (face-affection)", "order": "15"}, + {"emoji": "🐸", "name": "frog", "shortname": ":frog:", "unicode": "1f438", "html": "🐸", "category": "Animals & Nature (animal-amphibian)", "order": "1399"}, + {"emoji": "🌌", "name": "milky way", "shortname": ":milky_way:", "unicode": "1f30c", "html": "🌌", "category": "Travel & Places (sky & weather)", "order": "1592"}, + {"emoji": "🚨", "name": "police car light", "shortname": ":rotating_light:", "unicode": "1f6a8", "html": "🚨", "category": "Travel & Places (transport-ground)", "order": "1637"}, + {"emoji": "🐣", "name": "hatching chick", "shortname": ":hatching_chick:", "unicode": "1f423", "html": "🐣", "category": "Animals & Nature (animal-bird)", "order": "1390"}, + {"emoji": "📕", "name": "closed book", "shortname": ":closed_book:", "unicode": "1f4d5", "html": "📕", "category": "Objects (book-paper)", "order": "1875"}, + {"emoji": "🍬", "name": "candy", "shortname": ":candy:", "unicode": "1f36c", "html": "🍬", "category": "Food & Drink (food-sweet)", "order": "1516"}, + {"emoji": "🍔", "name": "hamburger", "shortname": ":hamburger:", "unicode": "1f354", "html": "🍔", "category": "Food & Drink (food-prepared)", "order": "1482"}, + {"emoji": "🐻", "name": "bear", "shortname": ":bear:", "unicode": "1f43b", "html": "🐻", "category": "Animals & Nature (animal-mammal)", "order": "1383"}, + {"emoji": "🐯", "name": "tiger face", "shortname": ":tiger:", "unicode": "1f42f", "html": "🐯", "category": "Animals & Nature (animal-mammal)", "order": "1353"}, + {"emoji": "🚗", "name": "automobile", "shortname": ":automobile:", "unicode": "1F697", "html": "🚗", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "⏩", "name": "fast-forward button", "shortname": ":fast_forward:", "unicode": "2.3E+10", "html": "⏩", "category": "Symbols (av-symbol)", "order": "2052"}, + {"emoji": "🍦", "name": "soft ice cream", "shortname": ":icecream:", "unicode": "1f366", "html": "🍦", "category": "Food & Drink (food-sweet)", "order": "1508"}, + {"emoji": "🍍", "name": "pineapple", "shortname": ":pineapple:", "unicode": "1f34d", "html": "🍍", "category": "Food & Drink (food-fruit)", "order": "1455"}, + {"emoji": "🌾", "name": "sheaf of rice", "shortname": ":ear_of_rice:", "unicode": "1f33e", "html": "🌾", "category": "Animals & Nature (plant-other)", "order": "1442"}, + {"emoji": "💉", "name": "syringe", "shortname": ":syringe:", "unicode": "1f489", "html": "💉", "category": "Objects (medical)", "order": "1967"}, + {"emoji": "🚮", "name": "litter in bin sign", "shortname": ":put_litter_in_its_place:", "unicode": "1f6ae", "html": "🚮", "category": "Symbols (transport-sign)", "order": "1977"}, + {"emoji": "🍫", "name": "chocolate bar", "shortname": ":chocolate_bar:", "unicode": "1f36b", "html": "🍫", "category": "Food & Drink (food-sweet)", "order": "1515"}, + {"emoji": "▪️", "name": "black small square", "shortname": ":black_small_square:", "unicode": "25aa", "html": "▪", "category": "Symbols (geometric)", "order": "2159"}, + {"emoji": "📺", "name": "television", "shortname": ":tv:", "unicode": "1f4fa", "html": "📺", "category": "Objects (light & video)", "order": "1860"}, + {"emoji": "💊", "name": "pill", "shortname": ":pill:", "unicode": "1f48a", "html": "💊", "category": "Objects (medical)", "order": "1968"}, + {"emoji": "🐙", "name": "octopus", "shortname": ":octopus:", "unicode": "1f419", "html": "🐙", "category": "Animals & Nature (animal-marine)", "order": "1413"}, + {"emoji": "🎃", "name": "jack-o-lantern", "shortname": ":jack_o_lantern:", "unicode": "1f383", "html": "🎃", "category": "Activities (event)", "order": "1756"}, + {"emoji": "🍇", "name": "grapes", "shortname": ":grapes:", "unicode": "1f347", "html": "🍇", "category": "Food & Drink (food-fruit)", "order": "1449"}, + {"emoji": "😺", "name": "grinning cat", "shortname": ":smiley_cat:", "unicode": "1f63a", "html": "😺", "category": "Smileys & Emotion (cat-face)", "order": "87"}, + {"emoji": "💿", "name": "optical disk", "shortname": ":cd:", "unicode": "1f4bf", "html": "💿", "category": "Objects (computer)", "order": "1854"}, + {"emoji": "🍸", "name": "cocktail glass", "shortname": ":cocktail:", "unicode": "1f378", "html": "🍸", "category": "Food & Drink (drink)", "order": "1527"}, + {"emoji": "🍰", "name": "shortcake", "shortname": ":cake:", "unicode": "1f370", "html": "🍰", "category": "Food & Drink (food-sweet)", "order": "1514"}, + {"emoji": "🎮", "name": "video game", "shortname": ":video_game:", "unicode": "1f3ae", "html": "🎮", "category": "Activities (game)", "order": "1804"}, + {"emoji": "™️", "name": "trade mark", "shortname": ":trade_mark:", "unicode": "2122 FE0F", "html": "™️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "⬇️", "name": "down arrow", "shortname": ":arrow_down:", "unicode": "2b07", "html": "⬇", "category": "Symbols (arrow)", "order": "2006"}, + {"emoji": "🚫", "name": "prohibited", "shortname": ":no_entry_sign:", "unicode": "1f6ab", "html": "🚫", "category": "Symbols (warning)", "order": "1992"}, + {"emoji": "💄", "name": "lipstick", "shortname": ":lipstick:", "unicode": "1f484", "html": "💄", "category": "Objects (clothing)", "order": "1339"}, + {"emoji": "🐳", "name": "spouting whale", "shortname": ":whale:", "unicode": "1f433", "html": "🐳", "category": "Animals & Nature (animal-marine)", "order": "1406"}, + {"emoji": "📝", "name": "memo", "shortname": ":memo:", "unicode": "1F4DD", "html": "📝", "category": "Objects (writing)", "order": ""}, + {"emoji": "®️", "name": "registered", "shortname": ":registered:", "unicode": "00AE FE0F", "html": "®️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "🍪", "name": "cookie", "shortname": ":cookie:", "unicode": "1f36a", "html": "🍪", "category": "Food & Drink (food-sweet)", "order": "1512"}, + {"emoji": "🐬", "name": "dolphin", "shortname": ":dolphin:", "unicode": "1f42c", "html": "🐬", "category": "Animals & Nature (animal-marine)", "order": "1408"}, + {"emoji": "🔊", "name": "speaker high volume", "shortname": ":loud_sound:", "unicode": "1f50a", "html": "🔊", "category": "Objects (sound)", "order": "1817"}, + {"emoji": "👨🏿", "name": "man: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF", "html": "👨🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾", "name": "man: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE", "html": "👨🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽", "name": "man: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD", "html": "👨🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼", "name": "man: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC", "html": "👨🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻", "name": "man: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB", "html": "👨🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨", "name": "man", "shortname": ":man:", "unicode": "1f468", "html": "👨", "category": "People & Body (person)", "order": "111"}, + {"emoji": "🐥", "name": "front-facing baby chick", "shortname": ":hatched_chick:", "unicode": "1f425", "html": "🐥", "category": "Animals & Nature (animal-bird)", "order": "1392"}, + {"emoji": "🐒", "name": "monkey", "shortname": ":monkey:", "unicode": "1f412", "html": "🐒", "category": "Animals & Nature (animal-mammal)", "order": "1343"}, + {"emoji": "📚", "name": "books", "shortname": ":books:", "unicode": "1f4da", "html": "📚", "category": "Objects (book-paper)", "order": "1880"}, + {"emoji": "👹", "name": "ogre", "shortname": ":japanese_ogre:", "unicode": "1f479", "html": "👹", "category": "Smileys & Emotion (face-costume)", "order": "78"}, + {"emoji": "💂🏿‍♀", "name": "woman guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF 200D 2640", "html": "💂🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾‍♀", "name": "woman guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE 200D 2640", "html": "💂🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽‍♀", "name": "woman guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD 200D 2640", "html": "💂🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼‍♀", "name": "woman guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC 200D 2640", "html": "💂🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻‍♀", "name": "woman guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB 200D 2640", "html": "💂🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂‍♀", "name": "woman guard", "shortname": ":woman_guard:", "unicode": "1F482 200D 2640", "html": "💂‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏿", "name": "guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF", "html": "💂🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾", "name": "guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE", "html": "💂🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽", "name": "guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD", "html": "💂🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼", "name": "guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC", "html": "💂🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻", "name": "guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB", "html": "💂🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂", "name": "guard", "shortname": ":guardsman:", "unicode": "1f482", "html": "💂", "category": "People & Body (person-role)", "order": "375"}, + {"emoji": "📢", "name": "loudspeaker", "shortname": ":loudspeaker:", "unicode": "1f4e2", "html": "📢", "category": "Objects (sound)", "order": "1818"}, + {"emoji": "✂️", "name": "scissors", "shortname": ":scissors:", "unicode": "2702", "html": "✂", "category": "Objects (office)", "order": "1940"}, + {"emoji": "👧🏿", "name": "girl: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F467 1F3FF", "html": "👧🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧🏾", "name": "girl: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F467 1F3FE", "html": "👧🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧🏽", "name": "girl: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F467 1F3FD", "html": "👧🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧🏼", "name": "girl: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F467 1F3FC", "html": "👧🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧🏻", "name": "girl: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F467 1F3FB", "html": "👧🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧", "name": "girl", "shortname": ":girl:", "unicode": "1f467", "html": "👧", "category": "People & Body (person)", "order": "105"}, + {"emoji": "🎓", "name": "graduation cap", "shortname": ":mortar_board:", "unicode": "1f393", "html": "🎓", "category": "Objects (clothing)", "order": "1336"}, + {"emoji": "🇫🇷", "name": "flag: France", "shortname": ":fr:", "unicode": "1f1eb", "html": "🇫", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "⚾️", "name": "", "shortname": ":baseball:", "unicode": "26be", "html": "⚾", "category": "", "order": "1782"}, + {"emoji": "🚦", "name": "vertical traffic light", "shortname": ":vertical_traffic_light:", "unicode": "1f6a6", "html": "🚦", "category": "Travel & Places (transport-ground)", "order": "1639"}, + {"emoji": "👩🏿", "name": "woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF", "html": "👩🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾", "name": "woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE", "html": "👩🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽", "name": "woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD", "html": "👩🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼", "name": "woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC", "html": "👩🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻", "name": "woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB", "html": "👩🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩", "name": "woman", "shortname": ":woman:", "unicode": "1f469", "html": "👩", "category": "People & Body (person)", "order": "117"}, + {"emoji": "🎆", "name": "fireworks", "shortname": ":fireworks:", "unicode": "1f386", "html": "🎆", "category": "Activities (event)", "order": "1758"}, + {"emoji": "🌠", "name": "shooting star", "shortname": ":stars:", "unicode": "1f320", "html": "🌠", "category": "Travel & Places (sky & weather)", "order": "1729"}, + {"emoji": "🆘", "name": "SOS button", "shortname": ":sos:", "unicode": "1f198", "html": "🆘", "category": "Symbols (alphanum)", "order": "2139"}, + {"emoji": "🍄", "name": "mushroom", "shortname": ":mushroom:", "unicode": "1f344", "html": "🍄", "category": "Food & Drink (food-vegetable)", "order": "1471"}, + {"emoji": "😾", "name": "pouting cat", "shortname": ":pouting_cat:", "unicode": "1f63e", "html": "😾", "category": "Smileys & Emotion (cat-face)", "order": "95"}, + {"emoji": "🛅", "name": "left luggage", "shortname": ":left_luggage:", "unicode": "1f6c5", "html": "🛅", "category": "Symbols (transport-sign)", "order": "1988"}, + {"emoji": "👠", "name": "high-heeled shoe", "shortname": ":high_heel:", "unicode": "1f460", "html": "👠", "category": "Objects (clothing)", "order": "1330"}, + {"emoji": "🎯", "name": "direct hit", "shortname": ":dart:", "unicode": "1f3af", "html": "🎯", "category": "Activities (game)", "order": "1798"}, + {"emoji": "🏊🏿‍♀", "name": "woman swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF 200D 2640", "html": "🏊🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾‍♀", "name": "woman swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE 200D 2640", "html": "🏊🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽‍♀", "name": "woman swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD 200D 2640", "html": "🏊🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼‍♀", "name": "woman swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC 200D 2640", "html": "🏊🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻‍♀", "name": "woman swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB 200D 2640", "html": "🏊🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊‍♀", "name": "woman swimming", "shortname": ":woman_swimming:", "unicode": "1F3CA 200D 2640", "html": "🏊‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏿", "name": "person swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF", "html": "🏊🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾", "name": "person swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE", "html": "🏊🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽", "name": "person swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD", "html": "🏊🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼", "name": "person swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC", "html": "🏊🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻", "name": "person swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB", "html": "🏊🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊", "name": "person swimming", "shortname": ":swimmer:", "unicode": "1f3ca", "html": "🏊", "category": "People & Body (person-sport)", "order": "836"}, + {"emoji": "🔑", "name": "key", "shortname": ":key:", "unicode": "1f511", "html": "🔑", "category": "Objects (lock)", "order": "1948"}, + {"emoji": "👙", "name": "bikini", "shortname": ":bikini:", "unicode": "1f459", "html": "👙", "category": "Objects (clothing)", "order": "1321"}, + {"emoji": "👪", "name": "family", "shortname": ":family:", "unicode": "1f46a", "html": "👪", "category": "People & Body (family)", "order": "1044"}, + {"emoji": "✏", "name": "pencil", "shortname": ":pencil2:", "unicode": "270f", "html": "✏", "category": "Objects (writing)", "order": "1914"}, + {"emoji": "🐘", "name": "elephant", "shortname": ":elephant:", "unicode": "1f418", "html": "🐘", "category": "Animals & Nature (animal-mammal)", "order": "1373"}, + {"emoji": "💧", "name": "droplet", "shortname": ":droplet:", "unicode": "1f4a7", "html": "💧", "category": "Travel & Places (sky & weather)", "order": "1754"}, + {"emoji": "🌱", "name": "seedling", "shortname": ":seedling:", "unicode": "1f331", "html": "🌱", "category": "Animals & Nature (plant-other)", "order": "1437"}, + {"emoji": "🍎", "name": "red apple", "shortname": ":apple:", "unicode": "1f34e", "html": "🍎", "category": "Food & Drink (food-fruit)", "order": "1456"}, + {"emoji": "🆒", "name": "COOL button", "shortname": ":cool:", "unicode": "1f192", "html": "🆒", "category": "Symbols (alphanum)", "order": "2129"}, + {"emoji": "📞", "name": "telephone receiver", "shortname": ":telephone_receiver:", "unicode": "1f4de", "html": "📞", "category": "Objects (phone)", "order": "1841"}, + {"emoji": "💵", "name": "dollar banknote", "shortname": ":dollar:", "unicode": "1f4b5", "html": "💵", "category": "Objects (money)", "order": "1893"}, + {"emoji": "🏡", "name": "house with garden", "shortname": ":house_with_garden:", "unicode": "1f3e1", "html": "🏡", "category": "Travel & Places (place-building)", "order": "1560"}, + {"emoji": "📖", "name": "open book", "shortname": ":book:", "unicode": "1f4d6", "html": "📖", "category": "Objects (book-paper)", "order": "1876"}, + {"emoji": "💇🏿‍♂", "name": "man getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF 200D 2642", "html": "💇🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾‍♂", "name": "man getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE 200D 2642", "html": "💇🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽‍♂", "name": "man getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD 200D 2642", "html": "💇🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼‍♂", "name": "man getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC 200D 2642", "html": "💇🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻‍♂", "name": "man getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB 200D 2642", "html": "💇🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇‍♂", "name": "man getting haircut", "shortname": ":man_getting_haircut:", "unicode": "1F487 200D 2642", "html": "💇‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏿", "name": "person getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF", "html": "💇🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾", "name": "person getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE", "html": "💇🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽", "name": "person getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD", "html": "💇🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼", "name": "person getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC", "html": "💇🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻", "name": "person getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB", "html": "💇🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇", "name": "person getting haircut", "shortname": ":haircut:", "unicode": "1f487", "html": "💇", "category": "People & Body (person-activity)", "order": "675"}, + {"emoji": "💻", "name": "laptop", "shortname": ":computer:", "unicode": "1f4bb", "html": "💻", "category": "Objects (computer)", "order": "1846"}, + {"emoji": "💡", "name": "light bulb", "shortname": ":bulb:", "unicode": "1f4a1", "html": "💡", "category": "Objects (light & video)", "order": "1871"}, + {"emoji": "❓", "name": "question mark", "shortname": ":question:", "unicode": "2753", "html": "❓", "category": "Symbols (other-symbol)", "order": "2098"}, + {"emoji": "🔙", "name": "BACK arrow", "shortname": ":back:", "unicode": "1f519", "html": "🔙", "category": "Symbols (arrow)", "order": "2018"}, + {"emoji": "👦🏿", "name": "boy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F466 1F3FF", "html": "👦🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦🏾", "name": "boy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F466 1F3FE", "html": "👦🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦🏽", "name": "boy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F466 1F3FD", "html": "👦🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦🏼", "name": "boy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F466 1F3FC", "html": "👦🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦🏻", "name": "boy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F466 1F3FB", "html": "👦🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦", "name": "boy", "shortname": ":boy:", "unicode": "1f466", "html": "👦", "category": "People & Body (person)", "order": "99"}, + {"emoji": "🔐", "name": "locked with key", "shortname": ":closed_lock_with_key:", "unicode": "1f510", "html": "🔐", "category": "Objects (lock)", "order": "1947"}, + {"emoji": "🙎🏿‍♂", "name": "man pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF 200D 2642", "html": "🙎🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾‍♂", "name": "man pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE 200D 2642", "html": "🙎🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽‍♂", "name": "man pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD 200D 2642", "html": "🙎🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼‍♂", "name": "man pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC 200D 2642", "html": "🙎🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻‍♂", "name": "man pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB 200D 2642", "html": "🙎🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎‍♂", "name": "man pouting", "shortname": ":man_pouting:", "unicode": "1F64E 200D 2642", "html": "🙎‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏿", "name": "person pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF", "html": "🙎🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾", "name": "person pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE", "html": "🙎🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽", "name": "person pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD", "html": "🙎🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼", "name": "person pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC", "html": "🙎🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻", "name": "person pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB", "html": "🙎🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎", "name": "person pouting", "shortname": ":person_with_pouting_face:", "unicode": "1f64e", "html": "🙎", "category": "People & Body (person-gesture)", "order": "513"}, + {"emoji": "🍊", "name": "tangerine", "shortname": ":tangerine:", "unicode": "1f34a", "html": "🍊", "category": "Food & Drink (food-fruit)", "order": "1452"}, + {"emoji": "↔️", "name": "left-right arrow", "shortname": ":leftright_arrow:", "unicode": "2194 FE0F", "html": "↔️", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "🌅", "name": "sunrise", "shortname": ":sunrise:", "unicode": "1f305", "html": "🌅", "category": "Travel & Places (place-other)", "order": "1587"}, + {"emoji": "🍗", "name": "poultry leg", "shortname": ":poultry_leg:", "unicode": "1f357", "html": "🍗", "category": "Food & Drink (food-prepared)", "order": "1480"}, + {"emoji": "🔵", "name": "blue circle", "shortname": ":blue_circle:", "unicode": "1f535", "html": "🔵", "category": "Symbols (geometric)", "order": "2180"}, + {"emoji": "🚘", "name": "oncoming automobile", "shortname": ":oncoming_automobile:", "unicode": "1f698", "html": "🚘", "category": "Travel & Places (transport-ground)", "order": "1625"}, + {"emoji": "🍧", "name": "shaved ice", "shortname": ":shaved_ice:", "unicode": "1f367", "html": "🍧", "category": "Food & Drink (food-sweet)", "order": "1509"}, + {"emoji": "🇮🇹", "name": "flag: Italy", "shortname": ":it:", "unicode": "1F1EE 1F1F9", "html": "🇮", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🐦", "name": "bird", "shortname": ":bird:", "unicode": "1f426", "html": "🐦", "category": "Animals & Nature (animal-bird)", "order": "1393"}, + {"emoji": "🇬🇧", "name": "flag: United Kingdom", "shortname": ":gb:", "unicode": "1F1EC 1F1E7", "html": "🇬", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🌛", "name": "first quarter moon face", "shortname": ":first_quarter_moon_with_face:", "unicode": "1f31b", "html": "🌛", "category": "Travel & Places (sky & weather)", "order": "1721"}, + {"emoji": "👓", "name": "glasses", "shortname": ":eyeglasses:", "unicode": "1f453", "html": "👓", "category": "Objects (clothing)", "order": "1314"}, + {"emoji": "🐐", "name": "goat", "shortname": ":goat:", "unicode": "1f410", "html": "🐐", "category": "Animals & Nature (animal-mammal)", "order": "1370"}, + {"emoji": "🌃", "name": "night with stars", "shortname": ":night_with_stars:", "unicode": "1f303", "html": "🌃", "category": "Travel & Places (place-other)", "order": "1585"}, + {"emoji": "👵🏿", "name": "old woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F475 1F3FF", "html": "👵🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵🏾", "name": "old woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F475 1F3FE", "html": "👵🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵🏽", "name": "old woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F475 1F3FD", "html": "👵🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵🏼", "name": "old woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F475 1F3FC", "html": "👵🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵🏻", "name": "old woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F475 1F3FB", "html": "👵🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵", "name": "old woman", "shortname": ":older_woman:", "unicode": "1f475", "html": "👵", "category": "People & Body (person)", "order": "129"}, + {"emoji": "⚫", "name": "black circle", "shortname": ":black_circle:", "unicode": "26ab", "html": "⚫", "category": "Symbols (geometric)", "order": "2178"}, + {"emoji": "🌑", "name": "new moon", "shortname": ":new_moon:", "unicode": "1f311", "html": "🌑", "category": "Travel & Places (sky & weather)", "order": "1711"}, + {"emoji": "👬", "name": "men holding hands", "shortname": ":two_men_holding_hands:", "unicode": "1f46c", "html": "👬", "category": "People & Body (family)", "order": "1024"}, + {"emoji": "⚪", "name": "white circle", "shortname": ":white_circle:", "unicode": "26aa", "html": "⚪", "category": "Symbols (geometric)", "order": "2177"}, + {"emoji": "🛃", "name": "customs", "shortname": ":customs:", "unicode": "1f6c3", "html": "🛃", "category": "Symbols (transport-sign)", "order": "1986"}, + {"emoji": "🐠", "name": "tropical fish", "shortname": ":tropical_fish:", "unicode": "1f420", "html": "🐠", "category": "Animals & Nature (animal-marine)", "order": "1410"}, + {"emoji": "🏠", "name": "house", "shortname": ":house:", "unicode": "1f3e0", "html": "🏠", "category": "Travel & Places (place-building)", "order": "1559"}, + {"emoji": "🔃", "name": "clockwise vertical arrows", "shortname": ":arrows_clockwise:", "unicode": "1f503", "html": "🔃", "category": "Symbols (arrow)", "order": "2016"}, + {"emoji": "🌜", "name": "last quarter moon face", "shortname": ":last_quarter_moon_with_face:", "unicode": "1f31c", "html": "🌜", "category": "Travel & Places (sky & weather)", "order": "1722"}, + {"emoji": "📍", "name": "round pushpin", "shortname": ":round_pushpin:", "unicode": "1f4cd", "html": "📍", "category": "Objects (office)", "order": "1935"}, + {"emoji": "🌕", "name": "full moon", "shortname": ":full_moon:", "unicode": "1f315", "html": "🌕", "category": "Travel & Places (sky & weather)", "order": "1715"}, + {"emoji": "👟", "name": "running shoe", "shortname": ":athletic_shoe:", "unicode": "1f45f", "html": "👟", "category": "Objects (clothing)", "order": "1329"}, + {"emoji": "🍋", "name": "lemon", "shortname": ":lemon:", "unicode": "1f34b", "html": "🍋", "category": "Food & Drink (food-fruit)", "order": "1453"}, + {"emoji": "🍼", "name": "baby bottle", "shortname": ":baby_bottle:", "unicode": "1f37c", "html": "🍼", "category": "Food & Drink (drink)", "order": "1520"}, + {"emoji": "🎨", "name": "artist palette", "shortname": ":artist_palette:", "unicode": "1F3A8", "html": "🎨", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "✉️", "name": "envelope", "shortname": ":envelope:", "unicode": "2709 FE0F", "html": "✉", "category": "Objects (mail)", "order": ""}, + {"emoji": "🍝", "name": "spaghetti", "shortname": ":spaghetti:", "unicode": "1f35d", "html": "🍝", "category": "Food & Drink (food-asian)", "order": "1501"}, + {"emoji": "🎐", "name": "wind chime", "shortname": ":wind_chime:", "unicode": "1f390", "html": "🎐", "category": "Activities (event)", "order": "1768"}, + {"emoji": "🍥", "name": "fish cake with swirl", "shortname": ":fish_cake:", "unicode": "1f365", "html": "🍥", "category": "Food & Drink (food-asian)", "order": "1506"}, + {"emoji": "🌲", "name": "evergreen tree", "shortname": ":evergreen_tree:", "unicode": "1f332", "html": "🌲", "category": "Animals & Nature (plant-other)", "order": "1438"}, + {"emoji": "🆙", "name": "UP! button", "shortname": ":up:", "unicode": "1f199", "html": "🆙", "category": "Symbols (alphanum)", "order": "2140"}, + {"emoji": "⬆️", "name": "up arrow", "shortname": ":arrow_up:", "unicode": "2b06", "html": "⬆", "category": "Symbols (arrow)", "order": "2002"}, + {"emoji": "↗️", "name": "up-right arrow", "shortname": ":arrow_upper_right:", "unicode": "2197", "html": "↗", "category": "Symbols (arrow)", "order": "2003"}, + {"emoji": "↘️", "name": "down-right arrow", "shortname": ":arrow_lower_right:", "unicode": "2198", "html": "↘", "category": "Symbols (arrow)", "order": "2005"}, + {"emoji": "↙️", "name": "down-left arrow", "shortname": ":arrow_lower_left:", "unicode": "2199", "html": "↙", "category": "Symbols (arrow)", "order": "2007"}, + {"emoji": "🎭", "name": "performing arts", "shortname": ":performing_arts:", "unicode": "1f3ad", "html": "🎭", "category": "Activities (arts & crafts)", "order": "1598"}, + {"emoji": "👃🏿", "name": "nose: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F443 1F3FF", "html": "👃🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃🏾", "name": "nose: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F443 1F3FE", "html": "👃🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃🏽", "name": "nose: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F443 1F3FD", "html": "👃🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃🏼", "name": "nose: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F443 1F3FC", "html": "👃🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃🏻", "name": "nose: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F443 1F3FB", "html": "👃🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃", "name": "nose", "shortname": ":nose:", "unicode": "1f443", "html": "👃", "category": "People & Body (body-parts)", "order": "1272"}, + {"emoji": "🐽", "name": "pig nose", "shortname": ":pig_nose:", "unicode": "1f43d", "html": "🐽", "category": "Animals & Nature (animal-mammal)", "order": "1367"}, + {"emoji": "🐟", "name": "fish", "shortname": ":fish:", "unicode": "1f41f", "html": "🐟", "category": "Animals & Nature (animal-marine)", "order": "1409"}, + {"emoji": "👳🏿‍♀", "name": "woman wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF 200D 2640", "html": "👳🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾‍♀", "name": "woman wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE 200D 2640", "html": "👳🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽‍♀", "name": "woman wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD 200D 2640", "html": "👳🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼‍♀", "name": "woman wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC 200D 2640", "html": "👳🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻‍♀", "name": "woman wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB 200D 2640", "html": "👳🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳‍♀", "name": "woman wearing turban", "shortname": ":woman_wearing_turban:", "unicode": "1F473 200D 2640", "html": "👳‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏿", "name": "person wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF", "html": "👳🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾", "name": "person wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE", "html": "👳🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽", "name": "person wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD", "html": "👳🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼", "name": "person wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC", "html": "👳🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻", "name": "person wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB", "html": "👳🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳", "name": "person wearing turban", "shortname": ":man_with_turban:", "unicode": "1f473", "html": "👳", "category": "People & Body (person-role)", "order": "411"}, + {"emoji": "🐨", "name": "koala", "shortname": ":koala:", "unicode": "1f428", "html": "🐨", "category": "Animals & Nature (animal-mammal)", "order": "1384"}, + {"emoji": "👂🏿", "name": "ear: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F442 1F3FF", "html": "👂🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂🏾", "name": "ear: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F442 1F3FE", "html": "👂🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂🏽", "name": "ear: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F442 1F3FD", "html": "👂🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂🏼", "name": "ear: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F442 1F3FC", "html": "👂🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂🏻", "name": "ear: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F442 1F3FB", "html": "👂🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂", "name": "ear", "shortname": ":ear:", "unicode": "1f442", "html": "👂", "category": "People & Body (body-parts)", "order": "1266"}, + {"emoji": "✳️", "name": "eight-spoked asterisk", "shortname": ":eight_spoked_asterisk:", "unicode": "2733", "html": "✳", "category": "Symbols (other-symbol)", "order": "2093"}, + {"emoji": "🔹", "name": "small blue diamond", "shortname": ":small_blue_diamond:", "unicode": "1f539", "html": "🔹", "category": "Symbols (geometric)", "order": "2170"}, + {"emoji": "🚿", "name": "shower", "shortname": ":shower:", "unicode": "1f6bf", "html": "🚿", "category": "Objects (household)", "order": "1672"}, + {"emoji": "🐛", "name": "bug", "shortname": ":bug:", "unicode": "1f41b", "html": "🐛", "category": "Animals & Nature (animal-bug)", "order": "1420"}, + {"emoji": "🍜", "name": "steaming bowl", "shortname": ":ramen:", "unicode": "1f35c", "html": "🍜", "category": "Food & Drink (food-asian)", "order": "1500"}, + {"emoji": "🎩", "name": "top hat", "shortname": ":tophat:", "unicode": "1f3a9", "html": "🎩", "category": "Objects (clothing)", "order": "1335"}, + {"emoji": "👰🏿", "name": "bride with veil: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F470 1F3FF", "html": "👰🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰🏾", "name": "bride with veil: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F470 1F3FE", "html": "👰🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰🏽", "name": "bride with veil: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F470 1F3FD", "html": "👰🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰🏼", "name": "bride with veil: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F470 1F3FC", "html": "👰🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰🏻", "name": "bride with veil: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F470 1F3FB", "html": "👰🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰", "name": "bride with veil", "shortname": ":bride_with_veil:", "unicode": "1f470", "html": "👰", "category": "People & Body (person-role)", "order": "471"}, + {"emoji": "⛽", "name": "fuel pump", "shortname": ":fuelpump:", "unicode": "26fd", "html": "⛽", "category": "Travel & Places (transport-ground)", "order": "1636"}, + {"emoji": "🏁", "name": "chequered flag", "shortname": ":checkered_flag:", "unicode": "1f3c1", "html": "🏁", "category": "Flags (flag)", "order": "2181"}, + {"emoji": "🐴", "name": "horse face", "shortname": ":horse:", "unicode": "1f434", "html": "🐴", "category": "Animals & Nature (animal-mammal)", "order": "1356"}, + {"emoji": "⌚", "name": "watch", "shortname": ":watch:", "unicode": "231a", "html": "⌚", "category": "Travel & Places (time)", "order": "1682"}, + {"emoji": "🐵", "name": "monkey face", "shortname": ":monkey_face:", "unicode": "1f435", "html": "🐵", "category": "Animals & Nature (animal-mammal)", "order": "1342"}, + {"emoji": "🚼", "name": "baby symbol", "shortname": ":baby_symbol:", "unicode": "1f6bc", "html": "🚼", "category": "Symbols (transport-sign)", "order": "1983"}, + {"emoji": "🆕", "name": "NEW button", "shortname": ":new:", "unicode": "1f195", "html": "🆕", "category": "Symbols (alphanum)", "order": "2134"}, + {"emoji": "🆓", "name": "FREE button", "shortname": ":free:", "unicode": "1f193", "html": "🆓", "category": "Symbols (alphanum)", "order": "2130"}, + {"emoji": "🎇", "name": "sparkler", "shortname": ":sparkler:", "unicode": "1f387", "html": "🎇", "category": "Activities (event)", "order": "1759"}, + {"emoji": "🌽", "name": "ear of corn", "shortname": ":corn:", "unicode": "1f33d", "html": "🌽", "category": "Food & Drink (food-vegetable)", "order": "1468"}, + {"emoji": "🎾", "name": "tennis", "shortname": ":tennis:", "unicode": "1f3be", "html": "🎾", "category": "Activities (sport)", "order": "1787"}, + {"emoji": "⏰", "name": "alarm clock", "shortname": ":alarm_clock:", "unicode": "23f0", "html": "⏰", "category": "Travel & Places (time)", "order": "1683"}, + {"emoji": "🔋", "name": "battery", "shortname": ":battery:", "unicode": "1f50b", "html": "🔋", "category": "Objects (computer)", "order": "1844"}, + {"emoji": "❕", "name": "white exclamation mark", "shortname": ":grey_exclamation:", "unicode": "2755", "html": "❕", "category": "Symbols (other-symbol)", "order": "2100"}, + {"emoji": "🐺", "name": "wolf", "shortname": ":wolf:", "unicode": "1f43a", "html": "🐺", "category": "Animals & Nature (animal-mammal)", "order": "1348"}, + {"emoji": "🗿", "name": "moai", "shortname": ":moyai:", "unicode": "1f5ff", "html": "🗿", "category": "Objects (other-object)", "order": "1972"}, + {"emoji": "🐮", "name": "cow face", "shortname": ":cow:", "unicode": "1f42e", "html": "🐮", "category": "Animals & Nature (animal-mammal)", "order": "1360"}, + {"emoji": "📣", "name": "megaphone", "shortname": ":mega:", "unicode": "1f4e3", "html": "📣", "category": "Objects (sound)", "order": "1819"}, + {"emoji": "👴🏿", "name": "old man: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F474 1F3FF", "html": "👴🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴🏾", "name": "old man: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F474 1F3FE", "html": "👴🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴🏽", "name": "old man: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F474 1F3FD", "html": "👴🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴🏼", "name": "old man: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F474 1F3FC", "html": "👴🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴🏻", "name": "old man: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F474 1F3FB", "html": "👴🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴", "name": "old man", "shortname": ":older_man:", "unicode": "1f474", "html": "👴", "category": "People & Body (person)", "order": "123"}, + {"emoji": "👗", "name": "dress", "shortname": ":dress:", "unicode": "1f457", "html": "👗", "category": "Objects (clothing)", "order": "1319"}, + {"emoji": "🔗", "name": "link", "shortname": ":link:", "unicode": "1f517", "html": "🔗", "category": "Objects (tool)", "order": "1965"}, + {"emoji": "🐔", "name": "chicken", "shortname": ":chicken:", "unicode": "1f414", "html": "🐔", "category": "Animals & Nature (animal-bird)", "order": "1388"}, + {"emoji": "🍳", "name": "cooking", "shortname": ":cooking:", "unicode": "1F373", "html": "🍳", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🐋", "name": "whale", "shortname": ":whale2:", "unicode": "1f40b", "html": "🐋", "category": "Animals & Nature (animal-marine)", "order": "1407"}, + {"emoji": "↖", "name": "up-left arrow", "shortname": ":arrow_upper_left:", "unicode": "2196", "html": "↖", "category": "Symbols (arrow)", "order": "2009"}, + {"emoji": "🌳", "name": "deciduous tree", "shortname": ":deciduous_tree:", "unicode": "1f333", "html": "🌳", "category": "Animals & Nature (plant-other)", "order": "1439"}, + {"emoji": "🍱", "name": "bento box", "shortname": ":bento:", "unicode": "1f371", "html": "🍱", "category": "Food & Drink (food-asian)", "order": "1495"}, + {"emoji": "📌", "name": "pushpin", "shortname": ":pushpin:", "unicode": "1f4cc", "html": "📌", "category": "Objects (office)", "order": "1934"}, + {"emoji": "🔜", "name": "SOON arrow", "shortname": ":soon:", "unicode": "1f51c", "html": "🔜", "category": "Symbols (arrow)", "order": "2021"}, + {"emoji": "🔁", "name": "repeat button", "shortname": ":repeat:", "unicode": "1f501", "html": "🔁", "category": "Symbols (av-symbol)", "order": "2049"}, + {"emoji": "🐉", "name": "dragon", "shortname": ":dragon:", "unicode": "1f409", "html": "🐉", "category": "Animals & Nature (animal-reptile)", "order": "1405"}, + {"emoji": "🐹", "name": "hamster", "shortname": ":hamster:", "unicode": "1f439", "html": "🐹", "category": "Animals & Nature (animal-mammal)", "order": "1378"}, + {"emoji": "⛳", "name": "flag in hole", "shortname": ":golf:", "unicode": "26f3", "html": "⛳", "category": "Activities (sport)", "order": "1799"}, + {"emoji": "🏄🏿‍♀", "name": "woman surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF 200D 2640", "html": "🏄🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾‍♀", "name": "woman surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE 200D 2640", "html": "🏄🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽‍♀", "name": "woman surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD 200D 2640", "html": "🏄🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼‍♀", "name": "woman surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC 200D 2640", "html": "🏄🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻‍♀", "name": "woman surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB 200D 2640", "html": "🏄🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄‍♀", "name": "woman surfing", "shortname": ":woman_surfing:", "unicode": "1F3C4 200D 2640", "html": "🏄‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏿", "name": "person surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF", "html": "🏄🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾", "name": "person surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE", "html": "🏄🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽", "name": "person surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD", "html": "🏄🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼", "name": "person surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC", "html": "🏄🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻", "name": "person surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB", "html": "🏄🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄", "name": "person surfing", "shortname": ":surfer:", "unicode": "1f3c4", "html": "🏄", "category": "People & Body (person-sport)", "order": "800"}, + {"emoji": "🐭", "name": "mouse face", "shortname": ":mouse:", "unicode": "1f42d", "html": "🐭", "category": "Animals & Nature (animal-mammal)", "order": "1375"}, + {"emoji": "🌒", "name": "waxing crescent moon", "shortname": ":waxing_crescent_moon:", "unicode": "1f312", "html": "🌒", "category": "Travel & Places (sky & weather)", "order": "1712"}, + {"emoji": "🚙", "name": "sport utility vehicle", "shortname": ":blue_car:", "unicode": "1f699", "html": "🚙", "category": "Travel & Places (transport-ground)", "order": "1626"}, + {"emoji": "🅰️", "name": "A button (blood type)", "shortname": ":a:", "unicode": "1f170", "html": "🅰", "category": "Symbols (alphanum)", "order": "2125"}, + {"emoji": "⁉️", "name": "exclamation question mark", "shortname": ":interrobang:", "unicode": "2049", "html": "⁉", "category": "Symbols (other-symbol)", "order": "2097"}, + {"emoji": "🈹", "name": "Japanese discount button", "shortname": ":u5272:", "unicode": "1f239", "html": "🈹", "category": "Symbols (alphanum)", "order": "2148"}, + {"emoji": "🔌", "name": "electric plug", "shortname": ":electric_plug:", "unicode": "1f50c", "html": "🔌", "category": "Objects (computer)", "order": "1845"}, + {"emoji": "🌓", "name": "first quarter moon", "shortname": ":first_quarter_moon:", "unicode": "1f313", "html": "🌓", "category": "Travel & Places (sky & weather)", "order": "1713"}, + {"emoji": "♋", "name": "Cancer", "shortname": ":cancer:", "unicode": "264b", "html": "♋", "category": "Symbols (zodiac)", "order": "2038"}, + {"emoji": "🔱", "name": "trident emblem", "shortname": ":trident:", "unicode": "1f531", "html": "🔱", "category": "Symbols (other-symbol)", "order": "2076"}, + {"emoji": "🍞", "name": "bread", "shortname": ":bread:", "unicode": "1f35e", "html": "🍞", "category": "Food & Drink (food-prepared)", "order": "1474"}, + {"emoji": "👮🏿‍♀", "name": "woman police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF 200D 2640", "html": "👮🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾‍♀", "name": "woman police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE 200D 2640", "html": "👮🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽‍♀", "name": "woman police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD 200D 2640", "html": "👮🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼‍♀", "name": "woman police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC 200D 2640", "html": "👮🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻‍♀", "name": "woman police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB 200D 2640", "html": "👮🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮‍♀", "name": "woman police officer", "shortname": ":woman_police_officer:", "unicode": "1F46E 200D 2640", "html": "👮‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏿", "name": "police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF", "html": "👮🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾", "name": "police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE", "html": "👮🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽", "name": "police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD", "html": "👮🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼", "name": "police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC", "html": "👮🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻", "name": "police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB", "html": "👮🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮", "name": "police officer", "shortname": ":cop:", "unicode": "1f46e", "html": "👮", "category": "People & Body (person-role)", "order": "339"}, + {"emoji": "🍵", "name": "teacup without handle", "shortname": ":tea:", "unicode": "1f375", "html": "🍵", "category": "Food & Drink (drink)", "order": "1523"}, + {"emoji": "🎣", "name": "fishing pole", "shortname": ":fishing_pole_and_fish:", "unicode": "1f3a3", "html": "🎣", "category": "Activities (sport)", "order": "1801"}, + {"emoji": "🌔", "name": "waxing gibbous moon", "shortname": ":waxing_gibbous_moon:", "unicode": "1F314", "html": "🌔", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🚲", "name": "bicycle", "shortname": ":bike:", "unicode": "1f6b2", "html": "🚲", "category": "Travel & Places (transport-ground)", "order": "1630"}, + {"emoji": "👤", "name": "bust in silhouette", "shortname": ":bust_in_silhouette:", "unicode": "1F464", "html": "👤", "category": "People & Body (person-symbol)", "order": ""}, + {"emoji": "🍚", "name": "cooked rice", "shortname": ":rice:", "unicode": "1f35a", "html": "🍚", "category": "Food & Drink (food-asian)", "order": "1498"}, + {"emoji": "📻", "name": "radio", "shortname": ":radio:", "unicode": "1f4fb", "html": "📻", "category": "Objects (music)", "order": "1831"}, + {"emoji": "🐤", "name": "baby chick", "shortname": ":baby_chick:", "unicode": "1f424", "html": "🐤", "category": "Animals & Nature (animal-bird)", "order": "1391"}, + {"emoji": "⤵️", "name": "right arrow curving down", "shortname": ":arrow_heading_down:", "unicode": "2935", "html": "⤵", "category": "Symbols (arrow)", "order": "2015"}, + {"emoji": "🌘", "name": "waning crescent moon", "shortname": ":waning_crescent_moon:", "unicode": "1f318", "html": "🌘", "category": "Travel & Places (sky & weather)", "order": "1718"}, + {"emoji": "↕", "name": "up-down arrow", "shortname": ":arrow_up_down:", "unicode": "2195", "html": "↕", "category": "Symbols (arrow)", "order": "2010"}, + {"emoji": "🇪", "name": "", "shortname": "", "unicode": "", "html": "🇪", "category": "", "order": ""}, + {"emoji": "🌗", "name": "last quarter moon", "shortname": ":last_quarter_moon:", "unicode": "1f317", "html": "🌗", "category": "Travel & Places (sky & weather)", "order": "1717"}, + {"emoji": "🔘", "name": "radio button", "shortname": ":radio_button:", "unicode": "1f518", "html": "🔘", "category": "Symbols (geometric)", "order": "2174"}, + {"emoji": "🐑", "name": "ewe", "shortname": ":sheep:", "unicode": "1f411", "html": "🐑", "category": "Animals & Nature (animal-mammal)", "order": "1369"}, + {"emoji": "👱🏿‍♀", "name": "woman: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF 200D 2640", "html": "👱🏿‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾‍♀", "name": "woman: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE 200D 2640", "html": "👱🏾‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽‍♀", "name": "woman: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD 200D 2640", "html": "👱🏽‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼‍♀", "name": "woman: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC 200D 2640", "html": "👱🏼‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻‍♀", "name": "woman: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB 200D 2640", "html": "👱🏻‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱‍♀", "name": "woman: blond hair", "shortname": ":blond_hair:", "unicode": "1F471 200D 2640", "html": "👱‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏿", "name": "person: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF", "html": "👱🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾", "name": "person: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE", "html": "👱🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽", "name": "person: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD", "html": "👱🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼", "name": "person: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC", "html": "👱🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻", "name": "person: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB", "html": "👱🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱", "name": "person: blond hair", "shortname": ":person_with_blond_hair:", "unicode": "1f471", "html": "👱", "category": "People & Body (person)", "order": "429"}, + {"emoji": "🌖", "name": "waning gibbous moon", "shortname": ":waning_gibbous_moon:", "unicode": "1f316", "html": "🌖", "category": "Travel & Places (sky & weather)", "order": "1716"}, + {"emoji": "🔒", "name": "locked", "shortname": ":lock:", "unicode": "1f512", "html": "🔒", "category": "Objects (lock)", "order": "1944"}, + {"emoji": "🍏", "name": "green apple", "shortname": ":green_apple:", "unicode": "1f34f", "html": "🍏", "category": "Food & Drink (food-fruit)", "order": "1457"}, + {"emoji": "👺", "name": "goblin", "shortname": ":japanese_goblin:", "unicode": "1f47a", "html": "👺", "category": "Smileys & Emotion (face-costume)", "order": "79"}, + {"emoji": "➰", "name": "curly loop", "shortname": ":curly_loop:", "unicode": "27b0", "html": "➰", "category": "Symbols (other-symbol)", "order": "2090"}, + {"emoji": "🚩", "name": "triangular flag", "shortname": ":triangular_flag_on_post:", "unicode": "1f6a9", "html": "🚩", "category": "Flags (flag)", "order": "2182"}, + {"emoji": "🔄", "name": "counterclockwise arrows button", "shortname": ":arrows_counterclockwise:", "unicode": "1f504", "html": "🔄", "category": "Symbols (arrow)", "order": "2017"}, + {"emoji": "🐎", "name": "horse", "shortname": ":racehorse:", "unicode": "1f40e", "html": "🐎", "category": "Animals & Nature (animal-mammal)", "order": "1357"}, + {"emoji": "🍤", "name": "fried shrimp", "shortname": ":fried_shrimp:", "unicode": "1f364", "html": "🍤", "category": "Food & Drink (food-asian)", "order": "1505"}, + {"emoji": "🌄", "name": "sunrise over mountains", "shortname": ":sunrise_over_mountains:", "unicode": "1f304", "html": "🌄", "category": "Travel & Places (place-other)", "order": "1586"}, + {"emoji": "🌋", "name": "volcano", "shortname": ":volcano:", "unicode": "1f30b", "html": "🌋", "category": "Travel & Places (place-geographic)", "order": "1546"}, + {"emoji": "🐓", "name": "rooster", "shortname": ":rooster:", "unicode": "1f413", "html": "🐓", "category": "Animals & Nature (animal-bird)", "order": "1389"}, + {"emoji": "📥", "name": "inbox tray", "shortname": ":inbox_tray:", "unicode": "1f4e5", "html": "📥", "category": "Objects (mail)", "order": "1906"}, + {"emoji": "💒", "name": "wedding", "shortname": ":wedding:", "unicode": "1f492", "html": "💒", "category": "Travel & Places (place-building)", "order": "1574"}, + {"emoji": "🍣", "name": "sushi", "shortname": ":sushi:", "unicode": "1f363", "html": "🍣", "category": "Food & Drink (food-asian)", "order": "1504"}, + {"emoji": "〰", "name": "wavy dash", "shortname": ":wavy_dash:", "unicode": "3030", "html": "〰", "category": "Symbols (other-symbol)", "order": "2102"}, + {"emoji": "🍨", "name": "ice cream", "shortname": ":ice_cream:", "unicode": "1f368", "html": "🍨", "category": "Food & Drink (food-sweet)", "order": "1510"}, + {"emoji": "⏪", "name": "fast reverse button", "shortname": ":rewind:", "unicode": "23ea", "html": "⏪", "category": "Symbols (av-symbol)", "order": "2056"}, + {"emoji": "🍅", "name": "tomato", "shortname": ":tomato:", "unicode": "1f345", "html": "🍅", "category": "Food & Drink (food-fruit)", "order": "1463"}, + {"emoji": "🐇", "name": "rabbit", "shortname": ":rabbit2:", "unicode": "1f407", "html": "🐇", "category": "Animals & Nature (animal-mammal)", "order": "1380"}, + {"emoji": "✴️", "name": "eight-pointed star", "shortname": ":eight_pointed_black_star:", "unicode": "2734", "html": "✴", "category": "Symbols (other-symbol)", "order": "2094"}, + {"emoji": "🔺", "name": "red triangle pointed up", "shortname": ":small_red_triangle:", "unicode": "1f53a", "html": "🔺", "category": "Symbols (geometric)", "order": "2171"}, + {"emoji": "🔆", "name": "bright button", "shortname": ":high_brightness:", "unicode": "1f506", "html": "🔆", "category": "Symbols (av-symbol)", "order": "2068"}, + {"emoji": "➕", "name": "plus sign", "shortname": ":heavy_plus_sign:", "unicode": "2795", "html": "➕", "category": "Symbols (other-symbol)", "order": "2084"}, + {"emoji": "👲🏿", "name": "man with skullcap: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F472 1F3FF", "html": "👲🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲🏾", "name": "man with skullcap: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F472 1F3FE", "html": "👲🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲🏽", "name": "man with skullcap: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F472 1F3FD", "html": "👲🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲🏼", "name": "man with skullcap: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F472 1F3FC", "html": "👲🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲🏻", "name": "man with skullcap: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F472 1F3FB", "html": "👲🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲", "name": "man with skullcap", "shortname": ":man_with_gua_pi_mao:", "unicode": "1f472", "html": "👲", "category": "People & Body (person-role)", "order": "489"}, + {"emoji": "🏪", "name": "convenience store", "shortname": ":convenience_store:", "unicode": "1f3ea", "html": "🏪", "category": "Travel & Places (place-building)", "order": "1568"}, + {"emoji": "👥", "name": "busts in silhouette", "shortname": ":busts_in_silhouette:", "unicode": "1f465", "html": "👥", "category": "People & Body (person-symbol)", "order": "767"}, + {"emoji": "🐞", "name": "lady beetle", "shortname": ":beetle:", "unicode": "1f41e", "html": "🐞", "category": "Animals & Nature (animal-bug)", "order": "1423"}, + {"emoji": "🔻", "name": "red triangle pointed down", "shortname": ":small_red_triangle_down:", "unicode": "1f53b", "html": "🔻", "category": "Symbols (geometric)", "order": "2172"}, + {"emoji": "🇩🇪", "name": "flag: Germany", "shortname": ":ger:", "unicode": "1F1E9 1F1EA", "html": "🇩", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "⤴️", "name": "right arrow curving up", "shortname": ":arrow_heading_up:", "unicode": "2934", "html": "⤴", "category": "Symbols (arrow)", "order": "2014"}, + {"emoji": "📛", "name": "name badge", "shortname": ":name_badge:", "unicode": "1f4db", "html": "📛", "category": "Symbols (other-symbol)", "order": "2073"}, + {"emoji": "🛀🏿", "name": "person taking bath: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6C0 1F3FF", "html": "🛀🏿", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀🏾", "name": "person taking bath: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6C0 1F3FE", "html": "🛀🏾", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀🏽", "name": "person taking bath: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6C0 1F3FD", "html": "🛀🏽", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀🏼", "name": "person taking bath: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6C0 1F3FC", "html": "🛀🏼", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀🏻", "name": "person taking bath: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6C0 1F3FB", "html": "🛀🏻", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀", "name": "person taking bath", "shortname": ":bath:", "unicode": "1f6c0", "html": "🛀", "category": "People & Body (person-resting)", "order": "1673"}, + {"emoji": "⛔", "name": "no entry", "shortname": ":no_entry:", "unicode": "26d4", "html": "⛔", "category": "Symbols (warning)", "order": "1991"}, + {"emoji": "🐊", "name": "crocodile", "shortname": ":crocodile:", "unicode": "1f40a", "html": "🐊", "category": "Animals & Nature (animal-reptile)", "order": "1400"}, + {"emoji": "🌰", "name": "chestnut", "shortname": ":chestnut:", "unicode": "1F330", "html": "🌰", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🐕", "name": "dog", "shortname": ":dog2:", "unicode": "1f415", "html": "🐕", "category": "Animals & Nature (animal-mammal)", "order": "1346"}, + {"emoji": "🐈", "name": "cat", "shortname": ":cat2:", "unicode": "1f408", "html": "🐈", "category": "Animals & Nature (animal-mammal)", "order": "1351"}, + {"emoji": "🔨", "name": "hammer", "shortname": ":hammer:", "unicode": "1f528", "html": "🔨", "category": "Objects (tool)", "order": "1950"}, + {"emoji": "🍖", "name": "meat on bone", "shortname": ":meat_on_bone:", "unicode": "1f356", "html": "🍖", "category": "Food & Drink (food-prepared)", "order": "1479"}, + {"emoji": "🐚", "name": "spiral shell", "shortname": ":shell:", "unicode": "1f41a", "html": "🐚", "category": "Animals & Nature (animal-marine)", "order": "1414"}, + {"emoji": "❇️", "name": "sparkle", "shortname": ":sparkle:", "unicode": "2747", "html": "❇", "category": "Symbols (other-symbol)", "order": "2095"}, + {"emoji": "⛵", "name": "sailboat", "shortname": ":sailboat:", "unicode": "26F5", "html": "⛵", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🅱️", "name": "B button (blood type)", "shortname": ":b:", "unicode": "1f171", "html": "🅱", "category": "Symbols (alphanum)", "order": "2127"}, + {"emoji": "Ⓜ️", "name": "circled M", "shortname": ":m:", "unicode": "24c2", "html": "Ⓜ", "category": "Symbols (alphanum)", "order": "2133"}, + {"emoji": "🐩", "name": "poodle", "shortname": ":poodle:", "unicode": "1f429", "html": "🐩", "category": "Animals & Nature (animal-mammal)", "order": "1347"}, + {"emoji": "♒", "name": "Aquarius", "shortname": ":aquarius:", "unicode": "2652", "html": "♒", "category": "Symbols (zodiac)", "order": "2045"}, + {"emoji": "🍲", "name": "pot of food", "shortname": ":stew:", "unicode": "1f372", "html": "🍲", "category": "Food & Drink (food-prepared)", "order": "1492"}, + {"emoji": "👖", "name": "jeans", "shortname": ":jeans:", "unicode": "1f456", "html": "👖", "category": "Objects (clothing)", "order": "1318"}, + {"emoji": "🍯", "name": "honey pot", "shortname": ":honey_pot:", "unicode": "1f36f", "html": "🍯", "category": "Food & Drink (food-sweet)", "order": "1519"}, + {"emoji": "🎹", "name": "musical keyboard", "shortname": ":musical_keyboard:", "unicode": "1f3b9", "html": "🎹", "category": "Objects (musical-instrument)", "order": "1834"}, + {"emoji": "🔓", "name": "unlocked", "shortname": ":unlock:", "unicode": "1f513", "html": "🔓", "category": "Objects (lock)", "order": "1945"}, + {"emoji": "✒", "name": "black nib", "shortname": ":black_nib:", "unicode": "2712", "html": "✒", "category": "Objects (writing)", "order": "1915"}, + {"emoji": "🗽", "name": "Statue of Liberty", "shortname": ":statue_of_liberty:", "unicode": "1f5fd", "html": "🗽", "category": "Travel & Places (place-building)", "order": "1576"}, + {"emoji": "💲", "name": "heavy dollar sign", "shortname": ":heavy_dollar_sign:", "unicode": "1f4b2", "html": "💲", "category": "Objects (money)", "order": "1900"}, + {"emoji": "🏂", "name": "snowboarder", "shortname": ":snowboarder:", "unicode": "1f3c2", "html": "🏂", "category": "People & Body (person-sport)", "order": "776"}, + {"emoji": "💮", "name": "white flower", "shortname": ":white_flower:", "unicode": "1f4ae", "html": "💮", "category": "Animals & Nature (plant-flower)", "order": "1429"}, + {"emoji": "👔", "name": "necktie", "shortname": ":necktie:", "unicode": "1f454", "html": "👔", "category": "Objects (clothing)", "order": "1316"}, + {"emoji": "💠", "name": "diamond with a dot", "shortname": ":diamond_shape_with_a_dot_inside:", "unicode": "1f4a0", "html": "💠", "category": "Symbols (geometric)", "order": "2173"}, + {"emoji": "♈", "name": "Aries", "shortname": ":aries:", "unicode": "2648", "html": "♈", "category": "Symbols (zodiac)", "order": "2035"}, + {"emoji": "🚺", "name": "women’s room", "shortname": ":womens:", "unicode": "1f6ba", "html": "🚺", "category": "Symbols (transport-sign)", "order": "1981"}, + {"emoji": "🐜", "name": "ant", "shortname": ":ant:", "unicode": "1f41c", "html": "🐜", "category": "Animals & Nature (animal-bug)", "order": "1421"}, + {"emoji": "♏", "name": "Scorpio", "shortname": ":scorpius:", "unicode": "264f", "html": "♏", "category": "Symbols (zodiac)", "order": "2042"}, + {"emoji": "🌇", "name": "sunset", "shortname": ":city_sunset:", "unicode": "1f307", "html": "🌇", "category": "Travel & Places (place-other)", "order": "1589"}, + {"emoji": "⏳", "name": "hourglass not done", "shortname": ":hourglass_flowing_sand:", "unicode": "23f3", "html": "⏳", "category": "Travel & Places (time)", "order": "1681"}, + {"emoji": "🅾️", "name": "O button (blood type)", "shortname": ":o2:", "unicode": "1f17e", "html": "🅾", "category": "Symbols (alphanum)", "order": "2136"}, + {"emoji": "🐲", "name": "dragon face", "shortname": ":dragon_face:", "unicode": "1f432", "html": "🐲", "category": "Animals & Nature (animal-reptile)", "order": "1404"}, + {"emoji": "🐌", "name": "snail", "shortname": ":snail:", "unicode": "1f40c", "html": "🐌", "category": "Animals & Nature (animal-bug)", "order": "1419"}, + {"emoji": "📀", "name": "dvd", "shortname": ":dvd:", "unicode": "1f4c0", "html": "📀", "category": "Objects (computer)", "order": "1855"}, + {"emoji": "👕", "name": "t-shirt", "shortname": ":shirt:", "unicode": "1f455", "html": "👕", "category": "Objects (clothing)", "order": "1317"}, + {"emoji": "🎲", "name": "game die", "shortname": ":game_die:", "unicode": "1f3b2", "html": "🎲", "category": "Activities (game)", "order": "1806"}, + {"emoji": "➖", "name": "minus sign", "shortname": ":heavy_minus_sign:", "unicode": "2796", "html": "➖", "category": "Symbols (other-symbol)", "order": "2088"}, + {"emoji": "🎎", "name": "Japanese dolls", "shortname": ":dolls:", "unicode": "1f38e", "html": "🎎", "category": "Activities (event)", "order": "1766"}, + {"emoji": "♐", "name": "Sagittarius", "shortname": ":sagittarius:", "unicode": "2650", "html": "♐", "category": "Symbols (zodiac)", "order": "2043"}, + {"emoji": "🎱", "name": "pool 8 ball", "shortname": ":8ball:", "unicode": "1f3b1", "html": "🎱", "category": "Activities (game)", "order": "1788"}, + {"emoji": "🚌", "name": "bus", "shortname": ":bus:", "unicode": "1f68c", "html": "🚌", "category": "Travel & Places (transport-ground)", "order": "1614"}, + {"emoji": "🍮", "name": "custard", "shortname": ":custard:", "unicode": "1f36e", "html": "🍮", "category": "Food & Drink (food-sweet)", "order": "1518"}, + {"emoji": "🎌", "name": "crossed flags", "shortname": ":crossed_flags:", "unicode": "1f38c", "html": "🎌", "category": "Flags (flag)", "order": "2183"}, + {"emoji": "〽️", "name": "part alternation mark", "shortname": ":part_alternation_mark:", "unicode": "303d", "html": "〽", "category": "Symbols (other-symbol)", "order": "2092"}, + {"emoji": "🐫", "name": "two-hump camel", "shortname": ":camel:", "unicode": "1f42b", "html": "🐫", "category": "Animals & Nature (animal-mammal)", "order": "1372"}, + {"emoji": "🍛", "name": "curry rice", "shortname": ":curry:", "unicode": "1f35b", "html": "🍛", "category": "Food & Drink (food-asian)", "order": "1499"}, + {"emoji": "🚂", "name": "locomotive", "shortname": ":steam_locomotive:", "unicode": "1f682", "html": "🚂", "category": "Travel & Places (transport-ground)", "order": "1602"}, + {"emoji": "🏥", "name": "hospital", "shortname": ":hospital:", "unicode": "1f3e5", "html": "🏥", "category": "Travel & Places (place-building)", "order": "1564"}, + {"emoji": "🇯🇵", "name": "flag: Japan", "shortname": ":jp:", "unicode": "1F1EF 1F1F5", "html": "🇯", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🔷", "name": "large blue diamond", "shortname": ":large_blue_diamond:", "unicode": "1f537", "html": "🔷", "category": "Symbols (geometric)", "order": "2168"}, + {"emoji": "🎋", "name": "tanabata tree", "shortname": ":tanabata_tree:", "unicode": "1f38b", "html": "🎋", "category": "Activities (event)", "order": "1764"}, + {"emoji": "🔔", "name": "bell", "shortname": ":bell:", "unicode": "1f514", "html": "🔔", "category": "Objects (sound)", "order": "1821"}, + {"emoji": "♌", "name": "Leo", "shortname": ":leo:", "unicode": "264c", "html": "♌", "category": "Symbols (zodiac)", "order": "2039"}, + {"emoji": "♊", "name": "Gemini", "shortname": ":gemini:", "unicode": "264a", "html": "♊", "category": "Symbols (zodiac)", "order": "2037"}, + {"emoji": "🍐", "name": "pear", "shortname": ":pear:", "unicode": "1f350", "html": "🍐", "category": "Food & Drink (food-fruit)", "order": "1458"}, + {"emoji": "🔶", "name": "large orange diamond", "shortname": ":large_orange_diamond:", "unicode": "1f536", "html": "🔶", "category": "Symbols (geometric)", "order": "2167"}, + {"emoji": "♉", "name": "Taurus", "shortname": ":taurus:", "unicode": "2649", "html": "♉", "category": "Symbols (zodiac)", "order": "2036"}, + {"emoji": "🌐", "name": "globe with meridians", "shortname": ":globe_with_meridians:", "unicode": "1f310", "html": "🌐", "category": "Travel & Places (place-map)", "order": "1541"}, + {"emoji": "🚪", "name": "door", "shortname": ":door:", "unicode": "1f6aa", "html": "🚪", "category": "Objects (household)", "order": "1662"}, + {"emoji": "🕕", "name": "six o’clock", "shortname": ":clock6:", "unicode": "1f555", "html": "🕕", "category": "Travel & Places (time)", "order": "1699"}, + {"emoji": "🚔", "name": "oncoming police car", "shortname": ":oncoming_police_car:", "unicode": "1f694", "html": "🚔", "category": "Travel & Places (transport-ground)", "order": "1621"}, + {"emoji": "📩", "name": "envelope with arrow", "shortname": ":envelope_with_arrow:", "unicode": "1f4e9", "html": "📩", "category": "Objects (mail)", "order": "1904"}, + {"emoji": "🌂", "name": "closed umbrella", "shortname": ":closed_umbrella:", "unicode": "1f302", "html": "🌂", "category": "Travel & Places (sky & weather)", "order": "1744"}, + {"emoji": "🎷", "name": "saxophone", "shortname": ":saxophone:", "unicode": "1f3b7", "html": "🎷", "category": "Objects (musical-instrument)", "order": "1832"}, + {"emoji": "⛪", "name": "church", "shortname": ":church:", "unicode": "26ea", "html": "⛪", "category": "Travel & Places (place-religious)", "order": "1577"}, + {"emoji": "🚴🏿‍♀", "name": "woman biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF 200D 2640", "html": "🚴🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾‍♀", "name": "woman biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE 200D 2640", "html": "🚴🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽‍♀", "name": "woman biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD 200D 2640", "html": "🚴🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼‍♀", "name": "woman biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC 200D 2640", "html": "🚴🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻‍♀", "name": "woman biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB 200D 2640", "html": "🚴🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴‍♀", "name": "woman biking", "shortname": ":woman_biking:", "unicode": "1F6B4 200D 2640", "html": "🚴‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏿", "name": "person biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF", "html": "🚴🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾", "name": "person biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE", "html": "🚴🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽", "name": "person biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD", "html": "🚴🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼", "name": "person biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC", "html": "🚴🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻", "name": "person biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB", "html": "🚴🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴", "name": "person biking", "shortname": ":bicyclist:", "unicode": "1f6b4", "html": "🚴", "category": "People & Body (person-sport)", "order": "890"}, + {"emoji": "♓", "name": "Pisces", "shortname": ":pisces:", "unicode": "2653", "html": "♓", "category": "Symbols (zodiac)", "order": "2046"}, + {"emoji": "🍡", "name": "dango", "shortname": ":dango:", "unicode": "1f361", "html": "🍡", "category": "Food & Drink (food-asian)", "order": "1507"}, + {"emoji": "♑", "name": "Capricorn", "shortname": ":capricorn:", "unicode": "2651", "html": "♑", "category": "Symbols (zodiac)", "order": "2044"}, + {"emoji": "🏢", "name": "office building", "shortname": ":office:", "unicode": "1f3e2", "html": "🏢", "category": "Travel & Places (place-building)", "order": "1561"}, + {"emoji": "🚣🏿‍♀", "name": "woman rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF 200D 2640", "html": "🚣🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾‍♀", "name": "woman rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE 200D 2640", "html": "🚣🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽‍♀", "name": "woman rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD 200D 2640", "html": "🚣🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼‍♀", "name": "woman rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC 200D 2640", "html": "🚣🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻‍♀", "name": "woman rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB 200D 2640", "html": "🚣🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣‍♀", "name": "woman rowing boat", "shortname": ":woman_rowing_boat:", "unicode": "1F6A3 200D 2640", "html": "🚣‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏿", "name": "person rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF", "html": "🚣🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾", "name": "person rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE", "html": "🚣🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽", "name": "person rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD", "html": "🚣🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼", "name": "person rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC", "html": "🚣🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻", "name": "person rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB", "html": "🚣🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣", "name": "person rowing boat", "shortname": ":rowboat:", "unicode": "1f6a3", "html": "🚣", "category": "People & Body (person-sport)", "order": "818"}, + {"emoji": "👒", "name": "woman’s hat", "shortname": ":womans_hat:", "unicode": "1f452", "html": "👒", "category": "Objects (clothing)", "order": "1334"}, + {"emoji": "👞", "name": "man’s shoe", "shortname": ":mans_shoe:", "unicode": "1f45e", "html": "👞", "category": "Objects (clothing)", "order": "1328"}, + {"emoji": "🏩", "name": "love hotel", "shortname": ":love_hotel:", "unicode": "1f3e9", "html": "🏩", "category": "Travel & Places (place-building)", "order": "1567"}, + {"emoji": "🗻", "name": "mount fuji", "shortname": ":mount_fuji:", "unicode": "1f5fb", "html": "🗻", "category": "Travel & Places (place-geographic)", "order": "1547"}, + {"emoji": "🐪", "name": "camel", "shortname": ":dromedary_camel:", "unicode": "1f42a", "html": "🐪", "category": "Animals & Nature (animal-mammal)", "order": "1371"}, + {"emoji": "👜", "name": "handbag", "shortname": ":handbag:", "unicode": "1f45c", "html": "👜", "category": "Objects (clothing)", "order": "1324"}, + {"emoji": "⌛", "name": "hourglass done", "shortname": ":hourglass:", "unicode": "231b", "html": "⌛", "category": "Travel & Places (time)", "order": "1680"}, + {"emoji": "❎", "name": "cross mark button", "shortname": ":negative_squared_cross_mark:", "unicode": "274e", "html": "❎", "category": "Symbols (other-symbol)", "order": "2083"}, + {"emoji": "🎺", "name": "trumpet", "shortname": ":trumpet:", "unicode": "1f3ba", "html": "🎺", "category": "Objects (musical-instrument)", "order": "1835"}, + {"emoji": "🏫", "name": "school", "shortname": ":school:", "unicode": "1f3eb", "html": "🏫", "category": "Travel & Places (place-building)", "order": "1569"}, + {"emoji": "🐄", "name": "cow", "shortname": ":cow2:", "unicode": "1f404", "html": "🐄", "category": "Animals & Nature (animal-mammal)", "order": "1363"}, + {"emoji": "🌆", "name": "cityscape at dusk", "shortname": ":cityscape_at_dusk:", "unicode": "1F306", "html": "🌆", "category": "Travel & Places (place-other)", "order": ""}, + {"emoji": "👷🏿‍♀", "name": "woman construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF 200D 2640", "html": "👷🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾‍♀", "name": "woman construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE 200D 2640", "html": "👷🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽‍♀", "name": "woman construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD 200D 2640", "html": "👷🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼‍♀", "name": "woman construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC 200D 2640", "html": "👷🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻‍♀", "name": "woman construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB 200D 2640", "html": "👷🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷‍♀", "name": "woman construction worker", "shortname": ":woman_construction_worker:", "unicode": "1F477 200D 2640", "html": "👷‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏿", "name": "construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF", "html": "👷🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾", "name": "construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE", "html": "👷🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽", "name": "construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD", "html": "👷🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼", "name": "construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC", "html": "👷🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻", "name": "construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB", "html": "👷🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷", "name": "construction worker", "shortname": ":construction_worker:", "unicode": "1f477", "html": "👷", "category": "People & Body (person-role)", "order": "393"}, + {"emoji": "🚽", "name": "toilet", "shortname": ":toilet:", "unicode": "1f6bd", "html": "🚽", "category": "Objects (household)", "order": "1671"}, + {"emoji": "🐖", "name": "pig", "shortname": ":pig2:", "unicode": "1f416", "html": "🐖", "category": "Animals & Nature (animal-mammal)", "order": "1365"}, + {"emoji": "❔", "name": "white question mark", "shortname": ":grey_question:", "unicode": "2754", "html": "❔", "category": "Symbols (other-symbol)", "order": "2099"}, + {"emoji": "🔰", "name": "Japanese symbol for beginner", "shortname": ":beginner:", "unicode": "1f530", "html": "🔰", "category": "Symbols (other-symbol)", "order": "2075"}, + {"emoji": "🎻", "name": "violin", "shortname": ":violin:", "unicode": "1f3bb", "html": "🎻", "category": "Objects (musical-instrument)", "order": "1836"}, + {"emoji": "🔛", "name": "ON! arrow", "shortname": ":on:", "unicode": "1f51b", "html": "🔛", "category": "Symbols (arrow)", "order": "2020"}, + {"emoji": "💳", "name": "credit card", "shortname": ":credit_card:", "unicode": "1f4b3", "html": "💳", "category": "Objects (money)", "order": "1897"}, + {"emoji": "🆔", "name": "ID button", "shortname": ":id:", "unicode": "1f194", "html": "🆔", "category": "Symbols (alphanum)", "order": "2132"}, + {"emoji": "㊙", "name": "Japanese secret button", "shortname": ":secret:", "unicode": "3299", "html": "㊙", "category": "Symbols (alphanum)", "order": "2156"}, + {"emoji": "🎡", "name": "ferris wheel", "shortname": ":ferris_wheel:", "unicode": "1f3a1", "html": "🎡", "category": "Travel & Places (place-other)", "order": "1594"}, + {"emoji": "🎳", "name": "bowling", "shortname": ":bowling:", "unicode": "1f3b3", "html": "🎳", "category": "Activities (sport)", "order": "1789"}, + {"emoji": "♎", "name": "Libra", "shortname": ":libra:", "unicode": "264e", "html": "♎", "category": "Symbols (zodiac)", "order": "2041"}, + {"emoji": "♍", "name": "Virgo", "shortname": ":virgo:", "unicode": "264d", "html": "♍", "category": "Symbols (zodiac)", "order": "2040"}, + {"emoji": "💈", "name": "barber pole", "shortname": ":barber:", "unicode": "1f488", "html": "💈", "category": "Travel & Places (place-other)", "order": "1596"}, + {"emoji": "👛", "name": "purse", "shortname": ":purse:", "unicode": "1f45b", "html": "👛", "category": "Objects (clothing)", "order": "1323"}, + {"emoji": "🎢", "name": "roller coaster", "shortname": ":roller_coaster:", "unicode": "1f3a2", "html": "🎢", "category": "Travel & Places (place-other)", "order": "1595"}, + {"emoji": "🐀", "name": "rat", "shortname": ":rat:", "unicode": "1f400", "html": "🐀", "category": "Animals & Nature (animal-mammal)", "order": "1377"}, + {"emoji": "📅", "name": "calendar", "shortname": ":date:", "unicode": "1f4c5", "html": "📅", "category": "Objects (office)", "order": "1925"}, + {"emoji": "🏉", "name": "rugby football", "shortname": ":rugby_football:", "unicode": "1f3c9", "html": "🏉", "category": "Activities (sport)", "order": "1786"}, + {"emoji": "🐏", "name": "ram", "shortname": ":ram:", "unicode": "1f40f", "html": "🐏", "category": "Animals & Nature (animal-mammal)", "order": "1368"}, + {"emoji": "🔼", "name": "upwards button", "shortname": ":arrow_up_small:", "unicode": "1f53c", "html": "🔼", "category": "Symbols (av-symbol)", "order": "2058"}, + {"emoji": "🔲", "name": "black square button", "shortname": ":black_square_button:", "unicode": "1f532", "html": "🔲", "category": "Symbols (geometric)", "order": "2175"}, + {"emoji": "📴", "name": "mobile phone off", "shortname": ":mobile_phone_off:", "unicode": "1f4f4", "html": "📴", "category": "Symbols (av-symbol)", "order": "2071"}, + {"emoji": "🗼", "name": "Tokyo tower", "shortname": ":tokyo_tower:", "unicode": "1f5fc", "html": "🗼", "category": "Travel & Places (place-building)", "order": "1575"}, + {"emoji": "㊗", "name": "Japanese congratulations button", "shortname": ":congratulations:", "unicode": "3297", "html": "㊗", "category": "Symbols (alphanum)", "order": "2155"}, + {"emoji": "👘", "name": "kimono", "shortname": ":kimono:", "unicode": "1f458", "html": "👘", "category": "Objects (clothing)", "order": "1320"}, + {"emoji": "🇷🇺", "name": "flag: Russia", "shortname": ":ru:", "unicode": "1F1F7 1F1FA", "html": "🇷", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🚢", "name": "ship", "shortname": ":ship:", "unicode": "1f6a2", "html": "🚢", "category": "Travel & Places (transport-water)", "order": "1649"}, + {"emoji": "🔎", "name": "magnifying glass tilted right", "shortname": ":mag_right:", "unicode": "1f50e", "html": "🔎", "category": "Objects (light & video)", "order": "1866"}, + {"emoji": "🔍", "name": "magnifying glass tilted left", "shortname": ":mag:", "unicode": "1f50d", "html": "🔍", "category": "Objects (light & video)", "order": "1865"}, + {"emoji": "🚒", "name": "fire engine", "shortname": ":fire_engine:", "unicode": "1f692", "html": "🚒", "category": "Travel & Places (transport-ground)", "order": "1619"}, + {"emoji": "🕦", "name": "eleven-thirty", "shortname": ":clock1130:", "unicode": "1f566", "html": "🕦", "category": "Travel & Places (time)", "order": "1710"}, + {"emoji": "🚓", "name": "police car", "shortname": ":police_car:", "unicode": "1f693", "html": "🚓", "category": "Travel & Places (transport-ground)", "order": "1620"}, + {"emoji": "🃏", "name": "joker", "shortname": ":black_joker:", "unicode": "1f0cf", "html": "🃏", "category": "Activities (game)", "order": "1811"}, + {"emoji": "🌉", "name": "bridge at night", "shortname": ":bridge_at_night:", "unicode": "1f309", "html": "🌉", "category": "Travel & Places (place-other)", "order": "1590"}, + {"emoji": "📦", "name": "package", "shortname": ":package:", "unicode": "1f4e6", "html": "📦", "category": "Objects (mail)", "order": "1907"}, + {"emoji": "🚖", "name": "oncoming taxi", "shortname": ":oncoming_taxi:", "unicode": "1f696", "html": "🚖", "category": "Travel & Places (transport-ground)", "order": "1623"}, + {"emoji": "📆", "name": "tear-off calendar", "shortname": ":calendar:", "unicode": "1f4c6", "html": "📆", "category": "Objects (office)", "order": "1926"}, + {"emoji": "🏇", "name": "horse racing", "shortname": ":horse_racing:", "unicode": "1f3c7", "html": "🏇", "category": "People & Body (person-sport)", "order": "769"}, + {"emoji": "🐅", "name": "tiger", "shortname": ":tiger2:", "unicode": "1f405", "html": "🐅", "category": "Animals & Nature (animal-mammal)", "order": "1354"}, + {"emoji": "👢", "name": "woman’s boot", "shortname": ":boot:", "unicode": "1f462", "html": "👢", "category": "Objects (clothing)", "order": "1332"}, + {"emoji": "🚑", "name": "ambulance", "shortname": ":ambulance:", "unicode": "1f691", "html": "🚑", "category": "Travel & Places (transport-ground)", "order": "1618"}, + {"emoji": "🔳", "name": "white square button", "shortname": ":white_square_button:", "unicode": "1f533", "html": "🔳", "category": "Symbols (geometric)", "order": "2176"}, + {"emoji": "🐗", "name": "boar", "shortname": ":boar:", "unicode": "1f417", "html": "🐗", "category": "Animals & Nature (animal-mammal)", "order": "1366"}, + {"emoji": "🎒", "name": "backpack", "shortname": ":school_satchel:", "unicode": "1f392", "html": "🎒", "category": "Objects (clothing)", "order": "1327"}, + {"emoji": "➿", "name": "double curly loop", "shortname": ":loop:", "unicode": "27bf", "html": "➿", "category": "Symbols (other-symbol)", "order": "2091"}, + {"emoji": "💷", "name": "pound banknote", "shortname": ":pound:", "unicode": "1f4b7", "html": "💷", "category": "Objects (money)", "order": "1895"}, + {"emoji": "ℹ", "name": "information", "shortname": ":information_source:", "unicode": "2139", "html": "ℹ", "category": "Symbols (alphanum)", "order": "2131"}, + {"emoji": "🐂", "name": "ox", "shortname": ":ox:", "unicode": "1f402", "html": "🐂", "category": "Animals & Nature (animal-mammal)", "order": "1361"}, + {"emoji": "🍙", "name": "rice ball", "shortname": ":rice_ball:", "unicode": "1f359", "html": "🍙", "category": "Food & Drink (food-asian)", "order": "1497"}, + {"emoji": "🆚", "name": "VS button", "shortname": ":vs:", "unicode": "1f19a", "html": "🆚", "category": "Symbols (alphanum)", "order": "2141"}, + {"emoji": "🔚", "name": "END arrow", "shortname": ":end:", "unicode": "1f51a", "html": "🔚", "category": "Symbols (arrow)", "order": "2019"}, + {"emoji": "🅿️", "name": "P button", "shortname": ":parking:", "unicode": "1f17f", "html": "🅿", "category": "Symbols (alphanum)", "order": "2138"}, + {"emoji": "👡", "name": "woman’s sandal", "shortname": ":sandal:", "unicode": "1f461", "html": "👡", "category": "Objects (clothing)", "order": "1331"}, + {"emoji": "⛺", "name": "tent", "shortname": ":tent:", "unicode": "26fa", "html": "⛺", "category": "Travel & Places (place-other)", "order": "1583"}, + {"emoji": "💺", "name": "seat", "shortname": ":seat:", "unicode": "1f4ba", "html": "💺", "category": "Travel & Places (transport-air)", "order": "1654"}, + {"emoji": "🚕", "name": "taxi", "shortname": ":taxi:", "unicode": "1f695", "html": "🚕", "category": "Travel & Places (transport-ground)", "order": "1622"}, + {"emoji": "◾", "name": "black medium-small square", "shortname": ":black_medium_small_square:", "unicode": "25fe", "html": "◾", "category": "Symbols (geometric)", "order": "2164"}, + {"emoji": "💼", "name": "briefcase", "shortname": ":briefcase:", "unicode": "1f4bc", "html": "💼", "category": "Objects (office)", "order": "1921"}, + {"emoji": "📰", "name": "newspaper", "shortname": ":newspaper:", "unicode": "1f4f0", "html": "📰", "category": "Objects (book-paper)", "order": "1886"}, + {"emoji": "🎪", "name": "circus tent", "shortname": ":circus_tent:", "unicode": "1f3aa", "html": "🎪", "category": "Travel & Places (place-other)", "order": "1597"}, + {"emoji": "🔯", "name": "dotted six-pointed star", "shortname": ":six_pointed_star:", "unicode": "1f52f", "html": "🔯", "category": "Symbols (religion)", "order": "2034"}, + {"emoji": "🚹", "name": "men’s room", "shortname": ":mens:", "unicode": "1f6b9", "html": "🚹", "category": "Symbols (transport-sign)", "order": "1980"}, + {"emoji": "🏰", "name": "castle", "shortname": ":european_castle:", "unicode": "1f3f0", "html": "🏰", "category": "Travel & Places (place-building)", "order": "1573"}, + {"emoji": "🔦", "name": "flashlight", "shortname": ":flashlight:", "unicode": "1f526", "html": "🔦", "category": "Objects (light & video)", "order": "1872"}, + {"emoji": "🌁", "name": "foggy", "shortname": ":foggy:", "unicode": "1f301", "html": "🌁", "category": "Travel & Places (place-other)", "order": "1584"}, + {"emoji": "⏫", "name": "fast up button", "shortname": ":arrow_double_up:", "unicode": "23eb", "html": "⏫", "category": "Symbols (av-symbol)", "order": "2059"}, + {"emoji": "🎍", "name": "pine decoration", "shortname": ":bamboo:", "unicode": "1f38d", "html": "🎍", "category": "Activities (event)", "order": "1765"}, + {"emoji": "🎫", "name": "ticket", "shortname": ":ticket:", "unicode": "1f3ab", "html": "🎫", "category": "Activities (event)", "order": "1774"}, + {"emoji": "🚁", "name": "helicopter", "shortname": ":helicopter:", "unicode": "1f681", "html": "🚁", "category": "Travel & Places (transport-air)", "order": "1655"}, + {"emoji": "💽", "name": "computer disk", "shortname": ":minidisc:", "unicode": "1f4bd", "html": "💽", "category": "Objects (computer)", "order": "1852"}, + {"emoji": "🚍", "name": "oncoming bus", "shortname": ":oncoming_bus:", "unicode": "1f68d", "html": "🚍", "category": "Travel & Places (transport-ground)", "order": "1615"}, + {"emoji": "🍈", "name": "melon", "shortname": ":melon:", "unicode": "1f348", "html": "🍈", "category": "Food & Drink (food-fruit)", "order": "1450"}, + {"emoji": "▫", "name": "white small square", "shortname": ":white_small_square:", "unicode": "25ab", "html": "▫", "category": "Symbols (geometric)", "order": "2160"}, + {"emoji": "🏤", "name": "post office", "shortname": ":european_post_office:", "unicode": "1f3e4", "html": "🏤", "category": "Travel & Places (place-building)", "order": "1563"}, + {"emoji": "🔟", "name": "keycap: 10", "shortname": ":keycap_ten:", "unicode": "1f51f", "html": "🔟", "category": "Symbols (keycap)", "order": "2118"}, + {"emoji": "📓", "name": "notebook", "shortname": ":notebook:", "unicode": "1f4d3", "html": "📓", "category": "Objects (book-paper)", "order": "1881"}, + {"emoji": "🔕", "name": "bell with slash", "shortname": ":no_bell:", "unicode": "1f515", "html": "🔕", "category": "Objects (sound)", "order": "1822"}, + {"emoji": "🍢", "name": "oden", "shortname": ":oden:", "unicode": "1f362", "html": "🍢", "category": "Food & Drink (food-asian)", "order": "1503"}, + {"emoji": "🎏", "name": "carp streamer", "shortname": ":flags:", "unicode": "1f38f", "html": "🎏", "category": "Activities (event)", "order": "1767"}, + {"emoji": "🎠", "name": "carousel horse", "shortname": ":carousel_horse:", "unicode": "1f3a0", "html": "🎠", "category": "Travel & Places (place-other)", "order": "1593"}, + {"emoji": "🐡", "name": "blowfish", "shortname": ":blowfish:", "unicode": "1f421", "html": "🐡", "category": "Animals & Nature (animal-marine)", "order": "1411"}, + {"emoji": "📈", "name": "chart increasing", "shortname": ":chart_with_upwards_trend:", "unicode": "1f4c8", "html": "📈", "category": "Objects (office)", "order": "1930"}, + {"emoji": "🍠", "name": "roasted sweet potato", "shortname": ":sweet_potato:", "unicode": "1f360", "html": "🍠", "category": "Food & Drink (food-asian)", "order": "1502"}, + {"emoji": "🎿", "name": "skis", "shortname": ":ski:", "unicode": "1f3bf", "html": "🎿", "category": "Activities (sport)", "order": "1803"}, + {"emoji": "🕛", "name": "twelve o’clock", "shortname": ":clock12:", "unicode": "1f55b", "html": "🕛", "category": "Travel & Places (time)", "order": "1687"}, + {"emoji": "📶", "name": "antenna bars", "shortname": ":signal_strength:", "unicode": "1f4f6", "html": "📶", "category": "Symbols (av-symbol)", "order": "2069"}, + {"emoji": "🚧", "name": "construction", "shortname": ":construction:", "unicode": "1f6a7", "html": "🚧", "category": "Travel & Places (transport-ground)", "order": "1640"}, + {"emoji": "#", "name": "", "shortname": "", "unicode": "", "html": "#", "category": "", "order": ""}, + {"emoji": "◼", "name": "black medium square", "shortname": ":black_medium_square:", "unicode": "25fc", "html": "◼", "category": "Symbols (geometric)", "order": "2162"}, + {"emoji": "📡", "name": "satellite antenna", "shortname": ":satellite:", "unicode": "1f4e1", "html": "📡", "category": "Objects (science)", "order": "1869"}, + {"emoji": "💶", "name": "euro banknote", "shortname": ":euro:", "unicode": "1f4b6", "html": "💶", "category": "Objects (money)", "order": "1894"}, + {"emoji": "👚", "name": "woman’s clothes", "shortname": ":womans_clothes:", "unicode": "1f45a", "html": "👚", "category": "Objects (clothing)", "order": "1322"}, + {"emoji": "📒", "name": "ledger", "shortname": ":ledger:", "unicode": "1f4d2", "html": "📒", "category": "Objects (book-paper)", "order": "1882"}, + {"emoji": "🐆", "name": "leopard", "shortname": ":leopard:", "unicode": "1f406", "html": "🐆", "category": "Animals & Nature (animal-mammal)", "order": "1355"}, + {"emoji": "🔅", "name": "dim button", "shortname": ":low_brightness:", "unicode": "1f505", "html": "🔅", "category": "Symbols (av-symbol)", "order": "2067"}, + {"emoji": "🕒", "name": "three o’clock", "shortname": ":clock3:", "unicode": "1f552", "html": "🕒", "category": "Travel & Places (time)", "order": "1693"}, + {"emoji": "🏬", "name": "department store", "shortname": ":department_store:", "unicode": "1f3ec", "html": "🏬", "category": "Travel & Places (place-building)", "order": "1570"}, + {"emoji": "🚚", "name": "delivery truck", "shortname": ":truck:", "unicode": "1f69a", "html": "🚚", "category": "Travel & Places (transport-ground)", "order": "1627"}, + {"emoji": "🍶", "name": "sake", "shortname": ":sake:", "unicode": "1f376", "html": "🍶", "category": "Food & Drink (drink)", "order": "1524"}, + {"emoji": "🚃", "name": "railway car", "shortname": ":railway_car:", "unicode": "1f683", "html": "🚃", "category": "Travel & Places (transport-ground)", "order": "1603"}, + {"emoji": "🚤", "name": "speedboat", "shortname": ":speedboat:", "unicode": "1f6a4", "html": "🚤", "category": "Travel & Places (transport-water)", "order": "1645"}, + {"emoji": "🇰🇷", "name": "flag: South Korea", "shortname": ":ko:", "unicode": "1F1F0 1F1F7", "html": "🇰", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "📼", "name": "videocassette", "shortname": ":vhs:", "unicode": "1f4fc", "html": "📼", "category": "Objects (light & video)", "order": "1864"}, + {"emoji": "🕐", "name": "one o’clock", "shortname": ":clock1:", "unicode": "1f550", "html": "🕐", "category": "Travel & Places (time)", "order": "1689"}, + {"emoji": "⏬", "name": "fast down button", "shortname": ":arrow_double_down:", "unicode": "23ec", "html": "⏬", "category": "Symbols (av-symbol)", "order": "2061"}, + {"emoji": "🐃", "name": "water buffalo", "shortname": ":water_buffalo:", "unicode": "1f403", "html": "🐃", "category": "Animals & Nature (animal-mammal)", "order": "1362"}, + {"emoji": "🔽", "name": "downwards button", "shortname": ":arrow_down_small:", "unicode": "1f53d", "html": "🔽", "category": "Symbols (av-symbol)", "order": "2060"}, + {"emoji": "💴", "name": "yen banknote", "shortname": ":yen:", "unicode": "1f4b4", "html": "💴", "category": "Objects (money)", "order": "1892"}, + {"emoji": "🔇", "name": "muted speaker", "shortname": ":mute:", "unicode": "1f507", "html": "🔇", "category": "Objects (sound)", "order": "1814"}, + {"emoji": "🎽", "name": "running shirt", "shortname": ":running_shirt_with_sash:", "unicode": "1f3bd", "html": "🎽", "category": "Activities (sport)", "order": "1802"}, + {"emoji": "⬜", "name": "white large square", "shortname": ":white_large_square:", "unicode": "2b1c", "html": "⬜", "category": "Symbols (geometric)", "order": "2166"}, + {"emoji": "♿", "name": "wheelchair symbol", "shortname": ":wheelchair:", "unicode": "267f", "html": "♿", "category": "Symbols (transport-sign)", "order": "1979"}, + {"emoji": "🕑", "name": "two o’clock", "shortname": ":clock2:", "unicode": "1f551", "html": "🕑", "category": "Travel & Places (time)", "order": "1691"}, + {"emoji": "📎", "name": "paperclip", "shortname": ":paperclip:", "unicode": "1f4ce", "html": "📎", "category": "Objects (office)", "order": "1936"}, + {"emoji": "🏧", "name": "ATM sign", "shortname": ":atm:", "unicode": "1f3e7", "html": "🏧", "category": "Symbols (transport-sign)", "order": "1976"}, + {"emoji": "🎦", "name": "cinema", "shortname": ":cinema:", "unicode": "1f3a6", "html": "🎦", "category": "Symbols (av-symbol)", "order": "2066"}, + {"emoji": "🔭", "name": "telescope", "shortname": ":telescope:", "unicode": "1f52d", "html": "🔭", "category": "Objects (science)", "order": "1868"}, + {"emoji": "🎑", "name": "moon viewing ceremony", "shortname": ":rice_scene:", "unicode": "1f391", "html": "🎑", "category": "Activities (event)", "order": "1769"}, + {"emoji": "📘", "name": "blue book", "shortname": ":blue_book:", "unicode": "1f4d8", "html": "📘", "category": "Objects (book-paper)", "order": "1878"}, + {"emoji": "◻️", "name": "white medium square", "shortname": ":white_medium_square:", "unicode": "25fb", "html": "◻", "category": "Symbols (geometric)", "order": "2161"}, + {"emoji": "📮", "name": "postbox", "shortname": ":postbox:", "unicode": "1f4ee", "html": "📮", "category": "Objects (mail)", "order": "1912"}, + {"emoji": "📧", "name": "e-mail", "shortname": ":e-mail:", "unicode": "1f4e7", "html": "📧", "category": "Objects (mail)", "order": "1902"}, + {"emoji": "🐁", "name": "mouse", "shortname": ":mouse2:", "unicode": "1f401", "html": "🐁", "category": "Animals & Nature (animal-mammal)", "order": "1376"}, + {"emoji": "🚄", "name": "high-speed train", "shortname": ":bullettrain_side:", "unicode": "1f684", "html": "🚄", "category": "Travel & Places (transport-ground)", "order": "1604"}, + {"emoji": "🉐", "name": "Japanese bargain button", "shortname": ":ideograph_advantage:", "unicode": "1f250", "html": "🉐", "category": "Symbols (alphanum)", "order": "2147"}, + {"emoji": "🔩", "name": "nut and bolt", "shortname": ":nut_and_bolt:", "unicode": "1f529", "html": "🔩", "category": "Objects (tool)", "order": "1960"}, + {"emoji": "🆖", "name": "NG button", "shortname": ":ng:", "unicode": "1f196", "html": "🆖", "category": "Symbols (alphanum)", "order": "2135"}, + {"emoji": "🏨", "name": "hotel", "shortname": ":hotel:", "unicode": "1f3e8", "html": "🏨", "category": "Travel & Places (place-building)", "order": "1566"}, + {"emoji": "🚾", "name": "water closet", "shortname": ":wc:", "unicode": "1f6be", "html": "🚾", "category": "Symbols (transport-sign)", "order": "1984"}, + {"emoji": "🏮", "name": "red paper lantern", "shortname": ":izakaya_lantern:", "unicode": "1f3ee", "html": "🏮", "category": "Objects (light & video)", "order": "1873"}, + {"emoji": "🔂", "name": "repeat single button", "shortname": ":repeat_one:", "unicode": "1f502", "html": "🔂", "category": "Symbols (av-symbol)", "order": "2050"}, + {"emoji": "📬", "name": "open mailbox with raised flag", "shortname": ":mailbox_with_mail:", "unicode": "1f4ec", "html": "📬", "category": "Objects (mail)", "order": "1910"}, + {"emoji": "📉", "name": "chart decreasing", "shortname": ":chart_with_downwards_trend:", "unicode": "1f4c9", "html": "📉", "category": "Objects (office)", "order": "1931"}, + {"emoji": "📗", "name": "green book", "shortname": ":green_book:", "unicode": "1f4d7", "html": "📗", "category": "Objects (book-paper)", "order": "1877"}, + {"emoji": "🚜", "name": "tractor", "shortname": ":tractor:", "unicode": "1f69c", "html": "🚜", "category": "Travel & Places (transport-ground)", "order": "1629"}, + {"emoji": "⛲", "name": "fountain", "shortname": ":fountain:", "unicode": "26f2", "html": "⛲", "category": "Travel & Places (place-other)", "order": "1582"}, + {"emoji": "🚇", "name": "metro", "shortname": ":metro:", "unicode": "1f687", "html": "🚇", "category": "Travel & Places (transport-ground)", "order": "1607"}, + {"emoji": "📋", "name": "clipboard", "shortname": ":clipboard:", "unicode": "1f4cb", "html": "📋", "category": "Objects (office)", "order": "1933"}, + {"emoji": "📵", "name": "no mobile phones", "shortname": ":no_mobile_phones:", "unicode": "1f4f5", "html": "📵", "category": "Symbols (warning)", "order": "1998"}, + {"emoji": "🕓", "name": "four o’clock", "shortname": ":clock4:", "unicode": "1f553", "html": "🕓", "category": "Travel & Places (time)", "order": "1695"}, + {"emoji": "🚭", "name": "no smoking", "shortname": ":no_smoking:", "unicode": "1f6ad", "html": "🚭", "category": "Symbols (warning)", "order": "1994"}, + {"emoji": "⬛", "name": "black large square", "shortname": ":black_large_square:", "unicode": "2b1b", "html": "⬛", "category": "Symbols (geometric)", "order": "2165"}, + {"emoji": "🎰", "name": "slot machine", "shortname": ":slot_machine:", "unicode": "1f3b0", "html": "🎰", "category": "Activities (game)", "order": "1601"}, + {"emoji": "🕔", "name": "five o’clock", "shortname": ":clock5:", "unicode": "1f554", "html": "🕔", "category": "Travel & Places (time)", "order": "1697"}, + {"emoji": "🛁", "name": "bathtub", "shortname": ":bathtub:", "unicode": "1f6c1", "html": "🛁", "category": "Objects (household)", "order": "1679"}, + {"emoji": "📜", "name": "scroll", "shortname": ":scroll:", "unicode": "1f4dc", "html": "📜", "category": "Objects (book-paper)", "order": "1884"}, + {"emoji": "🚉", "name": "station", "shortname": ":station:", "unicode": "1f689", "html": "🚉", "category": "Travel & Places (transport-ground)", "order": "1609"}, + {"emoji": "🍘", "name": "rice cracker", "shortname": ":rice_cracker:", "unicode": "1f358", "html": "🍘", "category": "Food & Drink (food-asian)", "order": "1496"}, + {"emoji": "🏦", "name": "bank", "shortname": ":bank:", "unicode": "1f3e6", "html": "🏦", "category": "Travel & Places (place-building)", "order": "1565"}, + {"emoji": "🔧", "name": "wrench", "shortname": ":wrench:", "unicode": "1f527", "html": "🔧", "category": "Objects (tool)", "order": "1959"}, + {"emoji": "🈯️", "name": "", "shortname": ":u6307:", "unicode": "1f22f", "html": "🈯", "category": "", "order": "2146"}, + {"emoji": "🚛", "name": "articulated lorry", "shortname": ":articulated_lorry:", "unicode": "1f69b", "html": "🚛", "category": "Travel & Places (transport-ground)", "order": "1628"}, + {"emoji": "📄", "name": "page facing up", "shortname": ":page_facing_up:", "unicode": "1f4c4", "html": "📄", "category": "Objects (book-paper)", "order": "1885"}, + {"emoji": "⛎", "name": "Ophiuchus", "shortname": ":ophiuchus:", "unicode": "26ce", "html": "⛎", "category": "Symbols (zodiac)", "order": "2047"}, + {"emoji": "📊", "name": "bar chart", "shortname": ":bar_chart:", "unicode": "1f4ca", "html": "📊", "category": "Objects (office)", "order": "1932"}, + {"emoji": "🚷", "name": "no pedestrians", "shortname": ":no_pedestrians:", "unicode": "1f6b7", "html": "🚷", "category": "Symbols (warning)", "order": "1997"}, + {"emoji": "🇨🇳", "name": "flag: China", "shortname": ":cn:", "unicode": "1F1E8 1F1F3", "html": "🇨", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "📳", "name": "vibration mode", "shortname": ":vibration_mode:", "unicode": "1f4f3", "html": "📳", "category": "Symbols (av-symbol)", "order": "2070"}, + {"emoji": "🕙", "name": "ten o’clock", "shortname": ":clock10:", "unicode": "1f559", "html": "🕙", "category": "Travel & Places (time)", "order": "1707"}, + {"emoji": "🕘", "name": "nine o’clock", "shortname": ":clock9:", "unicode": "1f558", "html": "🕘", "category": "Travel & Places (time)", "order": "1705"}, + {"emoji": "🚅", "name": "bullet train", "shortname": ":bullettrain_front:", "unicode": "1f685", "html": "🚅", "category": "Travel & Places (transport-ground)", "order": "1605"}, + {"emoji": "🚐", "name": "minibus", "shortname": ":minibus:", "unicode": "1f690", "html": "🚐", "category": "Travel & Places (transport-ground)", "order": "1617"}, + {"emoji": "🚊", "name": "tram", "shortname": ":tram:", "unicode": "1f68a", "html": "🚊", "category": "Travel & Places (transport-ground)", "order": "1610"}, + {"emoji": "🕗", "name": "eight o’clock", "shortname": ":clock8:", "unicode": "1f557", "html": "🕗", "category": "Travel & Places (time)", "order": "1703"}, + {"emoji": "🈳", "name": "Japanese vacancy button", "shortname": ":u7a7a:", "unicode": "1f233", "html": "🈳", "category": "Symbols (alphanum)", "order": "2154"}, + {"emoji": "🚥", "name": "horizontal traffic light", "shortname": ":traffic_light:", "unicode": "1f6a5", "html": "🚥", "category": "Travel & Places (transport-ground)", "order": "1638"}, + {"emoji": "🚵🏿‍♀", "name": "woman mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF 200D 2640", "html": "🚵🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾‍♀", "name": "woman mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE 200D 2640", "html": "🚵🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽‍♀", "name": "woman mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD 200D 2640", "html": "🚵🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼‍♀", "name": "woman mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC 200D 2640", "html": "🚵🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻‍♀", "name": "woman mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB 200D 2640", "html": "🚵🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵‍♀", "name": "woman mountain biking", "shortname": ":woman_mountain_biking:", "unicode": "1F6B5 200D 2640", "html": "🚵‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏿", "name": "person mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF", "html": "🚵🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾", "name": "person mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE", "html": "🚵🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽", "name": "person mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD", "html": "🚵🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼", "name": "person mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC", "html": "🚵🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻", "name": "person mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB", "html": "🚵🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵", "name": "person mountain biking", "shortname": ":mountain_bicyclist:", "unicode": "1f6b5", "html": "🚵", "category": "People & Body (person-sport)", "order": "908"}, + {"emoji": "🔬", "name": "microscope", "shortname": ":microscope:", "unicode": "1f52c", "html": "🔬", "category": "Objects (science)", "order": "1867"}, + {"emoji": "🏯", "name": "Japanese castle", "shortname": ":japanese_castle:", "unicode": "1f3ef", "html": "🏯", "category": "Travel & Places (place-building)", "order": "1572"}, + {"emoji": "🔖", "name": "bookmark", "shortname": ":bookmark:", "unicode": "1f516", "html": "🔖", "category": "Objects (book-paper)", "order": "1889"}, + {"emoji": "📑", "name": "bookmark tabs", "shortname": ":bookmark_tabs:", "unicode": "1f4d1", "html": "📑", "category": "Objects (book-paper)", "order": "1888"}, + {"emoji": "👝", "name": "clutch bag", "shortname": ":pouch:", "unicode": "1f45d", "html": "👝", "category": "Objects (clothing)", "order": "1325"}, + {"emoji": "🆎", "name": "AB button (blood type)", "shortname": ":ab:", "unicode": "1f18e", "html": "🆎", "category": "Symbols (alphanum)", "order": "2126"}, + {"emoji": "📃", "name": "page with curl", "shortname": ":page_with_curl:", "unicode": "1f4c3", "html": "📃", "category": "Objects (book-paper)", "order": "1883"}, + {"emoji": "🎴", "name": "flower playing cards", "shortname": ":flower_playing_cards:", "unicode": "1f3b4", "html": "🎴", "category": "Activities (game)", "order": "1813"}, + {"emoji": "🕚", "name": "eleven o’clock", "shortname": ":clock11:", "unicode": "1f55a", "html": "🕚", "category": "Travel & Places (time)", "order": "1709"}, + {"emoji": "📠", "name": "fax machine", "shortname": ":fax:", "unicode": "1f4e0", "html": "📠", "category": "Objects (phone)", "order": "1843"}, + {"emoji": "🕖", "name": "seven o’clock", "shortname": ":clock7:", "unicode": "1f556", "html": "🕖", "category": "Travel & Places (time)", "order": "1701"}, + {"emoji": "◽", "name": "white medium-small square", "shortname": ":white_medium_small_square:", "unicode": "25fd", "html": "◽", "category": "Symbols (geometric)", "order": "2163"}, + {"emoji": "💱", "name": "currency exchange", "shortname": ":currency_exchange:", "unicode": "1f4b1", "html": "💱", "category": "Objects (money)", "order": "1899"}, + {"emoji": "🔉", "name": "speaker medium volume", "shortname": ":sound:", "unicode": "1f509", "html": "🔉", "category": "Objects (sound)", "order": "1816"}, + {"emoji": "💹", "name": "chart increasing with yen", "shortname": ":chart:", "unicode": "1f4b9", "html": "💹", "category": "Objects (money)", "order": "1898"}, + {"emoji": "🆑", "name": "CL button", "shortname": ":cl:", "unicode": "1f191", "html": "🆑", "category": "Symbols (alphanum)", "order": "2128"}, + {"emoji": "💾", "name": "floppy disk", "shortname": ":floppy_disk:", "unicode": "1f4be", "html": "💾", "category": "Objects (computer)", "order": "1853"}, + {"emoji": "🏣", "name": "Japanese post office", "shortname": ":post_office:", "unicode": "1f3e3", "html": "🏣", "category": "Travel & Places (place-building)", "order": "1562"}, + {"emoji": "🔈", "name": "speaker low volume", "shortname": ":speaker:", "unicode": "1f508", "html": "🔈", "category": "Objects (sound)", "order": "1815"}, + {"emoji": "🗾", "name": "map of Japan", "shortname": ":japan:", "unicode": "1f5fe", "html": "🗾", "category": "Travel & Places (place-map)", "order": "1543"}, + {"emoji": "🈺", "name": "Japanese open for business button", "shortname": ":u55b6:", "unicode": "1f23a", "html": "🈺", "category": "Symbols (alphanum)", "order": "2157"}, + {"emoji": "🀄", "name": "mahjong red dragon", "shortname": ":mahjong:", "unicode": "1f004", "html": "🀄", "category": "Activities (game)", "order": "1812"}, + {"emoji": "📨", "name": "incoming envelope", "shortname": ":incoming_envelope:", "unicode": "1f4e8", "html": "📨", "category": "Objects (mail)", "order": "1903"}, + {"emoji": "📙", "name": "orange book", "shortname": ":orange_book:", "unicode": "1f4d9", "html": "📙", "category": "Objects (book-paper)", "order": "1879"}, + {"emoji": "🚻", "name": "restroom", "shortname": ":restroom:", "unicode": "1f6bb", "html": "🚻", "category": "Symbols (transport-sign)", "order": "1982"}, + {"emoji": "🈚️", "name": "", "shortname": ":u7121:", "unicode": "1f21a", "html": "🈚", "category": "", "order": "2149"}, + {"emoji": "🈶", "name": "Japanese not free of charge button", "shortname": ":u6709:", "unicode": "1f236", "html": "🈶", "category": "Symbols (alphanum)", "order": "2145"}, + {"emoji": "📐", "name": "triangular ruler", "shortname": ":triangular_ruler:", "unicode": "1f4d0", "html": "📐", "category": "Objects (office)", "order": "1939"}, + {"emoji": "🚋", "name": "tram car", "shortname": ":train:", "unicode": "1f68b", "html": "🚋", "category": "Travel & Places (transport-ground)", "order": "1613"}, + {"emoji": "🈸", "name": "Japanese application button", "shortname": ":u7533:", "unicode": "1f238", "html": "🈸", "category": "Symbols (alphanum)", "order": "2152"}, + {"emoji": "🚎", "name": "trolleybus", "shortname": ":trolleybus:", "unicode": "1f68e", "html": "🚎", "category": "Travel & Places (transport-ground)", "order": "1616"}, + {"emoji": "🈷", "name": "Japanese monthly amount button", "shortname": ":u6708:", "unicode": "1f237", "html": "🈷", "category": "Symbols (alphanum)", "order": "2144"}, + {"emoji": "🔢", "name": "input numbers", "shortname": ":input_numbers:", "unicode": "1F522", "html": "🔢", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "📔", "name": "notebook with decorative cover", "shortname": ":notebook_with_decorative_cover:", "unicode": "1f4d4", "html": "📔", "category": "Objects (book-paper)", "order": "1874"}, + {"emoji": "🈲", "name": "Japanese prohibited button", "shortname": ":u7981:", "unicode": "1f232", "html": "🈲", "category": "Symbols (alphanum)", "order": "2150"}, + {"emoji": "🈵", "name": "Japanese no vacancy button", "shortname": ":u6e80:", "unicode": "1f235", "html": "🈵", "category": "Symbols (alphanum)", "order": "2158"}, + {"emoji": "📯", "name": "postal horn", "shortname": ":postal_horn:", "unicode": "1f4ef", "html": "📯", "category": "Objects (sound)", "order": "1820"}, + {"emoji": "🏭", "name": "factory", "shortname": ":factory:", "unicode": "1f3ed", "html": "🏭", "category": "Travel & Places (place-building)", "order": "1571"}, + {"emoji": "🚸", "name": "children crossing", "shortname": ":children_crossing:", "unicode": "1f6b8", "html": "🚸", "category": "Symbols (warning)", "order": "1990"}, + {"emoji": "🚆", "name": "train", "shortname": ":train2:", "unicode": "1f686", "html": "🚆", "category": "Travel & Places (transport-ground)", "order": "1606"}, + {"emoji": "📏", "name": "straight ruler", "shortname": ":straight_ruler:", "unicode": "1f4cf", "html": "📏", "category": "Objects (office)", "order": "1938"}, + {"emoji": "📟", "name": "pager", "shortname": ":pager:", "unicode": "1f4df", "html": "📟", "category": "Objects (phone)", "order": "1842"}, + {"emoji": "🉑", "name": "Japanese acceptable button", "shortname": ":accept:", "unicode": "1f251", "html": "🉑", "category": "Symbols (alphanum)", "order": "2151"}, + {"emoji": "🈴", "name": "Japanese passing grade button", "shortname": ":u5408:", "unicode": "1f234", "html": "🈴", "category": "Symbols (alphanum)", "order": "2153"}, + {"emoji": "🔏", "name": "locked with pen", "shortname": ":lock_with_ink_pen:", "unicode": "1f50f", "html": "🔏", "category": "Objects (lock)", "order": "1946"}, + {"emoji": "🕜", "name": "one-thirty", "shortname": ":clock130:", "unicode": "1f55c", "html": "🕜", "category": "Travel & Places (time)", "order": "1690"}, + {"emoji": "🈂️", "name": "Japanese service charge button", "shortname": ":sa:", "unicode": "1f202", "html": "🈂", "category": "Symbols (alphanum)", "order": "2143"}, + {"emoji": "📤", "name": "outbox tray", "shortname": ":outbox_tray:", "unicode": "1f4e4", "html": "📤", "category": "Objects (mail)", "order": "1905"}, + {"emoji": "🔀", "name": "shuffle tracks button", "shortname": ":twisted_rightwards_arrows:", "unicode": "1f500", "html": "🔀", "category": "Symbols (av-symbol)", "order": "2048"}, + {"emoji": "📫", "name": "closed mailbox with raised flag", "shortname": ":mailbox:", "unicode": "1f4eb", "html": "📫", "category": "Objects (mail)", "order": "1908"}, + {"emoji": "🚈", "name": "light rail", "shortname": ":light_rail:", "unicode": "1f688", "html": "🚈", "category": "Travel & Places (transport-ground)", "order": "1608"}, + {"emoji": "🕤", "name": "nine-thirty", "shortname": ":clock930:", "unicode": "1f564", "html": "🕤", "category": "Travel & Places (time)", "order": "1706"}, + {"emoji": "🚏", "name": "bus stop", "shortname": ":busstop:", "unicode": "1f68f", "html": "🚏", "category": "Travel & Places (transport-ground)", "order": "1633"}, + {"emoji": "📂", "name": "open file folder", "shortname": ":open_file_folder:", "unicode": "1f4c2", "html": "📂", "category": "Objects (office)", "order": "1923"}, + {"emoji": "📁", "name": "file folder", "shortname": ":file_folder:", "unicode": "1f4c1", "html": "📁", "category": "Objects (office)", "order": "1922"}, + {"emoji": "🚰", "name": "potable water", "shortname": ":potable_water:", "unicode": "1f6b0", "html": "🚰", "category": "Symbols (transport-sign)", "order": "1978"}, + {"emoji": "📇", "name": "card index", "shortname": ":card_index:", "unicode": "1f4c7", "html": "📇", "category": "Objects (office)", "order": "1929"}, + {"emoji": "🕝", "name": "two-thirty", "shortname": ":clock230:", "unicode": "1f55d", "html": "🕝", "category": "Travel & Places (time)", "order": "1692"}, + {"emoji": "🚝", "name": "monorail", "shortname": ":monorail:", "unicode": "1f69d", "html": "🚝", "category": "Travel & Places (transport-ground)", "order": "1611"}, + {"emoji": "🕧", "name": "twelve-thirty", "shortname": ":clock1230:", "unicode": "1f567", "html": "🕧", "category": "Travel & Places (time)", "order": "1688"}, + {"emoji": "🕥", "name": "ten-thirty", "shortname": ":clock1030:", "unicode": "1f565", "html": "🕥", "category": "Travel & Places (time)", "order": "1708"}, + {"emoji": "🔤", "name": "input latin letters", "shortname": ":abc:", "unicode": "1f524", "html": "🔤", "category": "Symbols (alphanum)", "order": "2124"}, + {"emoji": "📪", "name": "closed mailbox with lowered flag", "shortname": ":mailbox_closed:", "unicode": "1f4ea", "html": "📪", "category": "Objects (mail)", "order": "1909"}, + {"emoji": "🕟", "name": "four-thirty", "shortname": ":clock430:", "unicode": "1f55f", "html": "🕟", "category": "Travel & Places (time)", "order": "1696"}, + {"emoji": "🚞", "name": "mountain railway", "shortname": ":mountain_railway:", "unicode": "1f69e", "html": "🚞", "category": "Travel & Places (transport-ground)", "order": "1612"}, + {"emoji": "🚯", "name": "no littering", "shortname": ":do_not_litter:", "unicode": "1f6af", "html": "🚯", "category": "Symbols (warning)", "order": "1995"}, + {"emoji": "🕞", "name": "three-thirty", "shortname": ":clock330:", "unicode": "1f55e", "html": "🕞", "category": "Travel & Places (time)", "order": "1694"}, + {"emoji": "➗", "name": "division sign", "shortname": ":heavy_division_sign:", "unicode": "2797", "html": "➗", "category": "Symbols (other-symbol)", "order": "2089"}, + {"emoji": "🕢", "name": "seven-thirty", "shortname": ":clock730:", "unicode": "1f562", "html": "🕢", "category": "Travel & Places (time)", "order": "1702"}, + {"emoji": "🕠", "name": "five-thirty", "shortname": ":clock530:", "unicode": "1f560", "html": "🕠", "category": "Travel & Places (time)", "order": "1698"}, + {"emoji": "🔠", "name": "input latin uppercase", "shortname": ":capital_abcd:", "unicode": "1f520", "html": "🔠", "category": "Symbols (alphanum)", "order": "2120"}, + {"emoji": "📭", "name": "open mailbox with lowered flag", "shortname": ":mailbox_with_no_mail:", "unicode": "1f4ed", "html": "📭", "category": "Objects (mail)", "order": "1911"}, + {"emoji": "🔣", "name": "input symbols", "shortname": ":symbols:", "unicode": "1f523", "html": "🔣", "category": "Symbols (alphanum)", "order": "2123"}, + {"emoji": "🚡", "name": "aerial tramway", "shortname": ":aerial_tramway:", "unicode": "1f6a1", "html": "🚡", "category": "Travel & Places (transport-air)", "order": "1658"}, + {"emoji": "🕣", "name": "eight-thirty", "shortname": ":clock830:", "unicode": "1f563", "html": "🕣", "category": "Travel & Places (time)", "order": "1704"}, + {"emoji": "🕡", "name": "six-thirty", "shortname": ":clock630:", "unicode": "1f561", "html": "🕡", "category": "Travel & Places (time)", "order": "1700"}, + {"emoji": "🔡", "name": "input latin lowercase", "shortname": ":abcd:", "unicode": "1f521", "html": "🔡", "category": "Symbols (alphanum)", "order": "2121"}, + {"emoji": "🚠", "name": "mountain cableway", "shortname": ":mountain_cableway:", "unicode": "1f6a0", "html": "🚠", "category": "Travel & Places (transport-air)", "order": "1657"}, + {"emoji": "🈁", "name": "Japanese here button", "shortname": ":koko:", "unicode": "1f201", "html": "🈁", "category": "Symbols (alphanum)", "order": "2142"}, + {"emoji": "🛂", "name": "passport control", "shortname": ":passport_control:", "unicode": "1f6c2", "html": "🛂", "category": "Symbols (transport-sign)", "order": "1985"}, + {"emoji": "🚱", "name": "non-potable water", "shortname": ":non-potable_water:", "unicode": "1f6b1", "html": "🚱", "category": "Symbols (warning)", "order": "1996"}, + {"emoji": "🚟", "name": "suspension railway", "shortname": ":suspension_railway:", "unicode": "1f69f", "html": "🚟", "category": "Travel & Places (transport-air)", "order": "1656"}, + {"emoji": "🛄", "name": "baggage claim", "shortname": ":baggage_claim:", "unicode": "1f6c4", "html": "🛄", "category": "Symbols (transport-sign)", "order": "1987"}, + {"emoji": "🚳", "name": "no bicycles", "shortname": ":no_bicycles:", "unicode": "1f6b3", "html": "🚳", "category": "Symbols (warning)", "order": "1993"}, + {"emoji": "🏳‍🌈", "name": "rainbow flag", "shortname": ":rainbow_flag:", "unicode": "1F3F3 200D 1F308", "html": "🏳‍🌈", "category": "Flags (flag)", "order": ""}, + {"emoji": "🕵🏿‍♀", "name": "woman detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF 200D 2640", "html": "🕵🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾‍♀", "name": "woman detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE 200D 2640", "html": "🕵🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽‍♀", "name": "woman detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD 200D 2640", "html": "🕵🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼‍♀", "name": "woman detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC 200D 2640", "html": "🕵🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻‍♀", "name": "woman detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB 200D 2640", "html": "🕵🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵‍♀", "name": "woman detective", "shortname": ":woman_detective:", "unicode": "1F575 200D 2640", "html": "🕵‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏿", "name": "detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF", "html": "🕵🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾", "name": "detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE", "html": "🕵🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽", "name": "detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD", "html": "🕵🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼", "name": "detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC", "html": "🕵🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻", "name": "detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB", "html": "🕵🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵", "name": "detective", "shortname": ":detective:", "unicode": "1F575", "html": "🕵", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "☹", "name": "frowning face", "shortname": ":frowning_face:", "unicode": "2639", "html": "☹", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "☠", "name": "skull and crossbones", "shortname": ":skull_crossbones:", "unicode": "2620", "html": "☠", "category": "Smileys & Emotion (face-negative)", "order": "81"}, + {"emoji": "🤗", "name": "hugging face", "shortname": ":hugging:", "unicode": "1f917", "html": "🤗", "category": "Smileys & Emotion (face-hand)", "order": "20"}, + {"emoji": "🤖", "name": "robot", "shortname": ":robot:", "unicode": "1F916", "html": "🤖", "category": "Smileys & Emotion (face-costume)", "order": ""}, + {"emoji": "🤕", "name": "face with head-bandage", "shortname": ":face_with_headbandage:", "unicode": "1F915", "html": "🤕", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤔", "name": "thinking face", "shortname": ":thinking:", "unicode": "1f914", "html": "🤔", "category": "Smileys & Emotion (face-hand)", "order": "21"}, + {"emoji": "🤓", "name": "nerd face", "shortname": ":nerd:", "unicode": "1f913", "html": "🤓", "category": "Smileys & Emotion (face-glasses)", "order": "36"}, + {"emoji": "🤒", "name": "face with thermometer", "shortname": ":face_with_thermometer:", "unicode": "1F912", "html": "🤒", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤑", "name": "money-mouth face", "shortname": ":moneymouth_face:", "unicode": "1F911", "html": "🤑", "category": "Smileys & Emotion (face-tongue)", "order": ""}, + {"emoji": "🤐", "name": "zipper-mouth face", "shortname": ":zipper_mouth:", "unicode": "1f910", "html": "🤐", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "30"}, + {"emoji": "🙄", "name": "face with rolling eyes", "shortname": ":rolling_eyes:", "unicode": "1f644", "html": "🙄", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "25"}, + {"emoji": "🙃", "name": "upside-down face", "shortname": ":upside_down:", "unicode": "1f643", "html": "🙃", "category": "Smileys & Emotion (face-smiling)", "order": "45"}, + {"emoji": "🙂", "name": "slightly smiling face", "shortname": ":slight_smile:", "unicode": "1f642", "html": "🙂", "category": "Smileys & Emotion (face-smiling)", "order": "19"}, + {"emoji": "🙁", "name": "slightly frowning face", "shortname": ":slightly_frowning_face:", "unicode": "1F641", "html": "🙁", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "🤘🏿", "name": "sign of the horns: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F918 1F3FF", "html": "🤘🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘🏾", "name": "sign of the horns: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F918 1F3FE", "html": "🤘🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘🏽", "name": "sign of the horns: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F918 1F3FD", "html": "🤘🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘🏼", "name": "sign of the horns: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F918 1F3FC", "html": "🤘🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘🏻", "name": "sign of the horns: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F918 1F3FB", "html": "🤘🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘", "name": "sign of the horns", "shortname": ":sign_of_the_horns:", "unicode": "1F918", "html": "🤘", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🖖🏿", "name": "vulcan salute: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F596 1F3FF", "html": "🖖🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖🏾", "name": "vulcan salute: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F596 1F3FE", "html": "🖖🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖🏽", "name": "vulcan salute: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F596 1F3FD", "html": "🖖🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖🏼", "name": "vulcan salute: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F596 1F3FC", "html": "🖖🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖🏻", "name": "vulcan salute: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F596 1F3FB", "html": "🖖🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖", "name": "vulcan salute", "shortname": ":vulcan_salute:", "unicode": "1F596", "html": "🖖", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖕🏿", "name": "middle finger: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F595 1F3FF", "html": "🖕🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕🏾", "name": "middle finger: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F595 1F3FE", "html": "🖕🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕🏽", "name": "middle finger: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F595 1F3FD", "html": "🖕🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕🏼", "name": "middle finger: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F595 1F3FC", "html": "🖕🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕🏻", "name": "middle finger: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F595 1F3FB", "html": "🖕🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕", "name": "middle finger", "shortname": ":middle_finger:", "unicode": "1f595", "html": "🖕", "category": "People & Body (hand-single-finger)", "order": "1116"}, + {"emoji": "🖐🏿", "name": "hand with fingers splayed: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F590 1F3FF", "html": "🖐🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐🏾", "name": "hand with fingers splayed: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F590 1F3FE", "html": "🖐🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐🏽", "name": "hand with fingers splayed: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F590 1F3FD", "html": "🖐🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐🏼", "name": "hand with fingers splayed: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F590 1F3FC", "html": "🖐🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐🏻", "name": "hand with fingers splayed: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F590 1F3FB", "html": "🖐🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐", "name": "hand with fingers splayed", "shortname": ":hand_with_fingers_splayed:", "unicode": "1F590", "html": "🖐", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✍🏿", "name": "writing hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "270D 1F3FF", "html": "✍🏿", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍🏾", "name": "writing hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "270D 1F3FE", "html": "✍🏾", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍🏽", "name": "writing hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "270D 1F3FD", "html": "✍🏽", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍🏼", "name": "writing hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "270D 1F3FC", "html": "✍🏼", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍🏻", "name": "writing hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "270D 1F3FB", "html": "✍🏻", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍", "name": "writing hand", "shortname": ":writing_hand:", "unicode": "270d", "html": "✍", "category": "People & Body (hand-prop)", "order": "1230"}, + {"emoji": "🕶", "name": "sunglasses", "shortname": ":dark_sunglasses:", "unicode": "1f576", "html": "🕶", "category": "Objects (clothing)", "order": "1315"}, + {"emoji": "👁‍🗨", "name": "eye in speech bubble", "shortname": ":eye_speachbubble:", "unicode": "1F441 200D 1F5E8", "html": "👁‍🗨", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "👁", "name": "eye", "shortname": ":eye:", "unicode": "1f441", "html": "👁", "category": "People & Body (body-parts)", "order": "1280"}, + {"emoji": "🏋🏿‍♀", "name": "woman lifting weights: dark skin tone", "shortname": ":weightlifter_woman_dt:", "unicode": "1F3CB 1F3FF 200D 2640", "html": "🏋🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾‍♀", "name": "woman lifting weights: medium-dark skin tone", "shortname": ":weightlifter_woman_mdt:", "unicode": "1F3CB 1F3FE 200D 2640", "html": "🏋🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽‍♀", "name": "woman lifting weights: medium skin tone", "shortname": ":weightlifter_woman_mt:", "unicode": "1F3CB 1F3FD 200D 2640", "html": "🏋🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼‍♀", "name": "woman lifting weights: medium-light skin tone", "shortname": ":weightlifter_woman_mlt:", "unicode": "1F3CB 1F3FC 200D 2640", "html": "🏋🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻‍♀", "name": "woman lifting weights: light skin tone", "shortname": ":weightlifter_woman_lt:", "unicode": "1F3CB 1F3FB 200D 2640", "html": "🏋🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋‍♀", "name": "woman lifting weights", "shortname": ":weightlifter_woman:", "unicode": "1F3CB 200D 2640", "html": "🏋‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏿", "name": "person lifting weights: dark skin tone", "shortname": ":weightlifter_dt:", "unicode": "1F3CB 1F3FF", "html": "🏋🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾", "name": "person lifting weights: medium-dark skin tone", "shortname": ":weightlifter_mdt:", "unicode": "1F3CB 1F3FE", "html": "🏋🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽", "name": "person lifting weights: medium skin tone", "shortname": ":weightlifter_mt:", "unicode": "1F3CB 1F3FD", "html": "🏋🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼", "name": "person lifting weights: medium-light skin tone", "shortname": ":weightlifter_mlt:", "unicode": "1F3CB 1F3FC", "html": "🏋🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻", "name": "person lifting weights: light skin tone", "shortname": ":weightlifter_lt:", "unicode": "1F3CB 1F3FB", "html": "🏋🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋", "name": "person lifting weights", "shortname": ":weightlifter:", "unicode": "1F3CB", "html": "🏋", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿‍♀", "name": "woman bouncing ball: dark skin tone", "shortname": ":basketballer_woman_dt:", "unicode": ":basketballer:", "html": "⛹🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾‍♀", "name": "woman bouncing ball: medium-dark skin tone", "shortname": ":basketballer_woman_mdt:", "unicode": ":basketballer:", "html": "⛹🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽‍♀", "name": "woman bouncing ball: medium skin tone", "shortname": ":basketballer_woman_mt:", "unicode": ":basketballer:", "html": "⛹🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼‍♀", "name": "woman bouncing ball: medium-light skin tone", "shortname": ":basketballer_woman_mlt:", "unicode": ":basketballer:", "html": "⛹🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻‍♀", "name": "woman bouncing ball: light skin tone", "shortname": ":basketballer_woman_lt:", "unicode": ":basketballer:", "html": "⛹🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹‍♀", "name": "woman bouncing ball", "shortname": ":woman_bouncing_ball:", "unicode": ":basketballer_woman:", "html": "⛹‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿", "name": "person bouncing ball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": ":basketballer_dt:", "html": "⛹🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾", "name": "person bouncing ball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": ":basketballer_mdt:", "html": "⛹🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽", "name": "person bouncing ball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": ":basketballer_mt:", "html": "⛹🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼", "name": "person bouncing ball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": ":basketballer_mlt:", "html": "⛹🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻", "name": "person bouncing ball: light skin tone", "shortname": ":basketballer_lt:", "unicode": "26F9 1F3FB", "html": "⛹🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹", "name": "person bouncing ball", "shortname": ":basketballer:", "unicode": "26F9", "html": "⛹", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🕴", "name": "man in suit levitating", "shortname": ":man_in_suit:", "unicode": "1f574", "html": "🕴", "category": "People & Body (person-activity)", "order": "784"}, + {"emoji": "🏌", "name": "person golfing", "shortname": ":golfer:", "unicode": "1f3cc", "html": "🏌", "category": "People & Body (person-sport)", "order": "782"}, + {"emoji": "🏌‍♀", "name": "woman golfing", "shortname": ":golfer_woman:", "unicode": "1F3CC 200D 2640", "html": "🏌‍♀", "category": "People & Body (person-sport)", "order": "783"}, + {"emoji": "*️⃣", "name": "keycap: #", "shortname": "*", "unicode": "0023 FE0F 20E3", "html": "*", "category": "Symbols (keycap)", "order": "2118"}, + {"emoji": "❣️", "name": "heart exclamation", "shortname": ":heart_exclamation:", "unicode": "2763", "html": "❣", "category": "Smileys & Emotion (emotion)", "order": "1300"}, + {"emoji": "✡️", "name": "star of David", "shortname": ":star_of_david:", "unicode": "2721", "html": "✡", "category": "Symbols (religion)", "order": "2026"}, + {"emoji": "✝️", "name": "latin cross", "shortname": ":cross:", "unicode": "271d", "html": "✝", "category": "Symbols (religion)", "order": "2029"}, + {"emoji": "⚜", "name": "fleur-de-lis", "shortname": ":fleur-de-lis:", "unicode": "269c", "html": "⚜", "category": "Symbols (other-symbol)", "order": "2074"}, + {"emoji": "⚛", "name": "atom symbol", "shortname": ":atom:", "unicode": "269b", "html": "⚛", "category": "Symbols (religion)", "order": "2024"}, + {"emoji": "☸", "name": "wheel of dharma", "shortname": ":wheel_of_dharma:", "unicode": "2638", "html": "☸", "category": "Symbols (religion)", "order": "2027"}, + {"emoji": "☯", "name": "yin yang", "shortname": ":yin_yang:", "unicode": "262f", "html": "☯", "category": "Symbols (religion)", "order": "2028"}, + {"emoji": "☮", "name": "peace symbol", "shortname": ":peace:", "unicode": "262e", "html": "☮", "category": "Symbols (religion)", "order": "2032"}, + {"emoji": "☪", "name": "star and crescent", "shortname": ":star_and_crescent:", "unicode": "262a", "html": "☪", "category": "Symbols (religion)", "order": "2031"}, + {"emoji": "☦", "name": "orthodox cross", "shortname": ":orthodox_cross:", "unicode": "2626", "html": "☦", "category": "Symbols (religion)", "order": "2030"}, + {"emoji": "☣", "name": "biohazard", "shortname": ":biohazard:", "unicode": "2623", "html": "☣", "category": "Symbols (warning)", "order": "2001"}, + {"emoji": "☢", "name": "radioactive", "shortname": ":radioactive:", "unicode": "2622", "html": "☢", "category": "Symbols (warning)", "order": "2000"}, + {"emoji": "🛐", "name": "place of worship", "shortname": ":place_of_worship:", "unicode": "1f6d0", "html": "🛐", "category": "Symbols (religion)", "order": "2023"}, + {"emoji": "🗯", "name": "right anger bubble", "shortname": ":anger_right:", "unicode": "1f5ef", "html": "🗯", "category": "Smileys & Emotion (emotion)", "order": "1311"}, + {"emoji": "🕎", "name": "menorah", "shortname": ":menorah:", "unicode": "1f54e", "html": "🕎", "category": "Symbols (religion)", "order": "2033"}, + {"emoji": "🕉", "name": "om", "shortname": ":om_symbol:", "unicode": "1f549", "html": "🕉", "category": "Symbols (religion)", "order": "2025"}, + {"emoji": "⚱", "name": "funeral urn", "shortname": ":funeral_urn:", "unicode": "26B1", "html": "⚱", "category": "Objects (other-object)", "order": ""}, + {"emoji": "⚰", "name": "coffin", "shortname": ":coffin:", "unicode": "26b0", "html": "⚰", "category": "Objects (other-object)", "order": "1970"}, + {"emoji": "⚙", "name": "gear", "shortname": ":gear:", "unicode": "2699", "html": "⚙", "category": "Objects (tool)", "order": "1961"}, + {"emoji": "⚗", "name": "alembic", "shortname": ":alembic:", "unicode": "2697", "html": "⚗", "category": "Objects (science)", "order": "1963"}, + {"emoji": "⚖", "name": "balance scale", "shortname": ":scales:", "unicode": "2696", "html": "⚖", "category": "Objects (tool)", "order": "1964"}, + {"emoji": "⚔", "name": "crossed swords", "shortname": ":crossed_swords:", "unicode": "2694", "html": "⚔", "category": "Objects (tool)", "order": "1955"}, + {"emoji": "⌨", "name": "keyboard", "shortname": ":keyboard:", "unicode": "2328", "html": "⌨", "category": "Objects (computer)", "order": "1849"}, + {"emoji": "🛢", "name": "oil drum", "shortname": ":oil_drum:", "unicode": "1F6E2", "html": "🛢", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛡", "name": "shield", "shortname": ":shield:", "unicode": "1f6e1", "html": "🛡", "category": "Objects (tool)", "order": "1958"}, + {"emoji": "🛠", "name": "hammer and wrench", "shortname": ":hammer_and_wrench:", "unicode": "1F6E0", "html": "🛠", "category": "Objects (tool)", "order": ""}, + {"emoji": "🛏", "name": "bed", "shortname": ":bed:", "unicode": "1f6cf", "html": "🛏", "category": "Objects (household)", "order": "1669"}, + {"emoji": "🛎", "name": "bellhop bell", "shortname": ":bellhop_bell:", "unicode": "1F6CE", "html": "🛎", "category": "Travel & Places (hotel)", "order": ""}, + {"emoji": "🛍", "name": "shopping bags", "shortname": ":shopping_bags:", "unicode": "1f6cd", "html": "🛍", "category": "Objects (clothing)", "order": "1326"}, + {"emoji": "🛌", "name": "person in bed", "shortname": ":sleeping_accommodation:", "unicode": "1f6cc", "html": "🛌", "category": "People & Body (person-resting)", "order": "1663"}, + {"emoji": "🛋", "name": "couch and lamp", "shortname": ":couch_and_lamp:", "unicode": "1F6CB", "html": "🛋", "category": "Objects (household)", "order": ""}, + {"emoji": "🗳", "name": "ballot box with ballot", "shortname": ":ballot_box:", "unicode": "1f5f3", "html": "🗳", "category": "Objects (mail)", "order": "1913"}, + {"emoji": "🗡", "name": "dagger", "shortname": ":dagger:", "unicode": "1F5E1", "html": "🗡", "category": "Objects (tool)", "order": ""}, + {"emoji": "🗞", "name": "rolled-up newspaper", "shortname": ":rolledup_newspaper:", "unicode": "1F5DE", "html": "🗞", "category": "Objects (book-paper)", "order": ""}, + {"emoji": "🗝", "name": "old key", "shortname": ":old_key:", "unicode": "1F5DD", "html": "🗝", "category": "Objects (lock)", "order": ""}, + {"emoji": "🗜", "name": "clamp", "shortname": ":compression:", "unicode": "1f5dc", "html": "🗜", "category": "Objects (tool)", "order": "1962"}, + {"emoji": "🗓", "name": "spiral calendar", "shortname": ":spiral_calendar:", "unicode": "1F5D3", "html": "🗓", "category": "Objects (office)", "order": ""}, + {"emoji": "🗒", "name": "spiral notepad", "shortname": ":spiral_notepad:", "unicode": "1F5D2", "html": "🗒", "category": "Objects (office)", "order": ""}, + {"emoji": "🗑", "name": "wastebasket", "shortname": ":wastebasket:", "unicode": "1f5d1", "html": "🗑", "category": "Objects (office)", "order": "1943"}, + {"emoji": "🗄", "name": "file cabinet", "shortname": ":file_cabinet:", "unicode": "1f5c4", "html": "🗄", "category": "Objects (office)", "order": "1942"}, + {"emoji": "🗃", "name": "card file box", "shortname": ":card_file_box:", "unicode": "1F5C3", "html": "🗃", "category": "Objects (office)", "order": ""}, + {"emoji": "🗂", "name": "card index dividers", "shortname": ":card_index_dividers:", "unicode": "1F5C2", "html": "🗂", "category": "Objects (office)", "order": ""}, + {"emoji": "🖼", "name": "framed picture", "shortname": ":framed_picture:", "unicode": "1F5BC", "html": "🖼", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "🖲", "name": "trackball", "shortname": ":trackball:", "unicode": "1f5b2", "html": "🖲", "category": "Objects (computer)", "order": "1851"}, + {"emoji": "🖱", "name": "computer mouse", "shortname": ":computer_mouse:", "unicode": "1F5B1", "html": "🖱", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖨", "name": "printer", "shortname": ":printer:", "unicode": "1f5a8", "html": "🖨", "category": "Objects (computer)", "order": "1848"}, + {"emoji": "🖥", "name": "desktop computer", "shortname": ":desktop_computer:", "unicode": "1F5A5", "html": "🖥", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖍", "name": "crayon", "shortname": ":crayon:", "unicode": "1F58D", "html": "🖍", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖌", "name": "paintbrush", "shortname": ":paintbrush:", "unicode": "1F58C", "html": "🖌", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖋", "name": "fountain pen", "shortname": ":fountain_pen:", "unicode": "1F58B", "html": "🖋", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖊", "name": "pen", "shortname": ":pen:", "unicode": "1F58A", "html": "🖊", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖇", "name": "linked paperclips", "shortname": ":linked_paperclips:", "unicode": "1F587", "html": "🖇", "category": "Objects (office)", "order": ""}, + {"emoji": "🕹", "name": "joystick", "shortname": ":joystick:", "unicode": "1f579", "html": "🕹", "category": "Activities (game)", "order": "1805"}, + {"emoji": "🕳", "name": "hole", "shortname": ":hole:", "unicode": "1f573", "html": "🕳", "category": "Smileys & Emotion (emotion)", "order": "1313"}, + {"emoji": "🕰", "name": "mantelpiece clock", "shortname": ":mantelpiece_clock:", "unicode": "1F570", "html": "🕰", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "🕯", "name": "candle", "shortname": ":candle:", "unicode": "1f56f", "html": "🕯", "category": "Objects (light & video)", "order": "1870"}, + {"emoji": "📿", "name": "prayer beads", "shortname": ":prayer_beads:", "unicode": "1f4ff", "html": "📿", "category": "Objects (clothing)", "order": "1338"}, + {"emoji": "📽", "name": "film projector", "shortname": ":film_projector:", "unicode": "1F4FD", "html": "📽", "category": "Objects (light & video)", "order": ""}, + {"emoji": "📸", "name": "camera with flash", "shortname": ":camera_with_flash:", "unicode": "1f4f8", "html": "📸", "category": "Objects (light & video)", "order": "1862"}, + {"emoji": "🏺", "name": "amphora", "shortname": ":amphora:", "unicode": "1f3fa", "html": "🏺", "category": "Food & Drink (dishware)", "order": "1537"}, + {"emoji": "🏷", "name": "label", "shortname": ":label:", "unicode": "1f3f7", "html": "🏷", "category": "Objects (book-paper)", "order": "1890"}, + {"emoji": "🏴", "name": "black flag", "shortname": ":flag_black:", "unicode": "1f3f4", "html": "🏴", "category": "Flags (flag)", "order": "2184"}, + {"emoji": "🏳", "name": "white flag", "shortname": ":flag_white:", "unicode": "1f3f3", "html": "🏳", "category": "Flags (flag)", "order": "2185"}, + {"emoji": "🎞", "name": "film frames", "shortname": ":film_frames:", "unicode": "1f39e", "html": "🎞", "category": "Objects (light & video)", "order": "1857"}, + {"emoji": "🎛", "name": "control knobs", "shortname": ":control_knobs:", "unicode": "1f39b", "html": "🎛", "category": "Objects (music)", "order": "1828"}, + {"emoji": "🎚", "name": "level slider", "shortname": ":level_slider:", "unicode": "1f39a", "html": "🎚", "category": "Objects (music)", "order": "1827"}, + {"emoji": "🎙", "name": "studio microphone", "shortname": ":studio_microphone:", "unicode": "1F399", "html": "🎙", "category": "Objects (music)", "order": ""}, + {"emoji": "🌡", "name": "thermometer", "shortname": ":thermometer:", "unicode": "1f321", "html": "🌡", "category": "Travel & Places (sky & weather)", "order": "1723"}, + {"emoji": "🛳", "name": "passenger ship", "shortname": ":passenger_ship:", "unicode": "1F6F3", "html": "🛳", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🛰", "name": "satellite", "shortname": ":satellite:", "unicode": "1F6F0", "html": "🛰", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛬", "name": "airplane arrival", "shortname": ":airplane_arriving:", "unicode": "1f6ec", "html": "🛬", "category": "Travel & Places (transport-air)", "order": "1653"}, + {"emoji": "🛫", "name": "airplane departure", "shortname": ":airplane_departure:", "unicode": "1f6eb", "html": "🛫", "category": "Travel & Places (transport-air)", "order": "1652"}, + {"emoji": "🛩", "name": "small airplane", "shortname": ":small_airplane:", "unicode": "1F6E9", "html": "🛩", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛥", "name": "motor boat", "shortname": ":motor_boat:", "unicode": "1F6E5", "html": "🛥", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🛤", "name": "railway track", "shortname": ":railway_track:", "unicode": "1f6e4", "html": "🛤", "category": "Travel & Places (transport-ground)", "order": "1635"}, + {"emoji": "🛣", "name": "motorway", "shortname": ":motorway:", "unicode": "1f6e3", "html": "🛣", "category": "Travel & Places (transport-ground)", "order": "1634"}, + {"emoji": "🗺", "name": "world map", "shortname": ":world_map:", "unicode": "1F5FA", "html": "🗺", "category": "Travel & Places (place-map)", "order": ""}, + {"emoji": "🕍", "name": "synagogue", "shortname": ":synagogue:", "unicode": "1f54d", "html": "🕍", "category": "Travel & Places (place-religious)", "order": "1579"}, + {"emoji": "🕌", "name": "mosque", "shortname": ":mosque:", "unicode": "1f54c", "html": "🕌", "category": "Travel & Places (place-religious)", "order": "1578"}, + {"emoji": "🕋", "name": "kaaba", "shortname": ":kaaba:", "unicode": "1f54b", "html": "🕋", "category": "Travel & Places (place-religious)", "order": "1581"}, + {"emoji": "🏟", "name": "stadium", "shortname": ":stadium:", "unicode": "1f3df", "html": "🏟", "category": "Travel & Places (place-building)", "order": "1553"}, + {"emoji": "🏞", "name": "national park", "shortname": ":national_park:", "unicode": "1F3DE", "html": "🏞", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏝", "name": "desert island", "shortname": ":desert_island:", "unicode": "1F3DD", "html": "🏝", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏜", "name": "desert", "shortname": ":desert:", "unicode": "1f3dc", "html": "🏜", "category": "Travel & Places (place-geographic)", "order": "1550"}, + {"emoji": "🏛", "name": "classical building", "shortname": ":classical_building:", "unicode": "1f3db", "html": "🏛", "category": "Travel & Places (place-building)", "order": "1554"}, + {"emoji": "🏚", "name": "derelict house", "shortname": ":derelict_house:", "unicode": "1F3DA", "html": "🏚", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏙", "name": "cityscape", "shortname": ":cityscape:", "unicode": "1f3d9", "html": "🏙", "category": "Travel & Places (place-other)", "order": "1557"}, + {"emoji": "🏘", "name": "houses", "shortname": ":houses:", "unicode": "1F3D8", "html": "🏘", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏗", "name": "building construction", "shortname": ":building_construction:", "unicode": "1F3D7", "html": "🏗", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏖", "name": "beach with umbrella", "shortname": ":beach_with_umbrella:", "unicode": "1F3D6", "html": "🏖", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏕", "name": "camping", "shortname": ":camping:", "unicode": "1f3d5", "html": "🏕", "category": "Travel & Places (place-geographic)", "order": "1548"}, + {"emoji": "🏔", "name": "snow-capped mountain", "shortname": ":snowcapped_mountain:", "unicode": "1F3D4", "html": "🏔", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏎", "name": "racing car", "shortname": ":racing_car:", "unicode": "1F3CE", "html": "🏎", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🏍", "name": "motorcycle", "shortname": ":motorcycle:", "unicode": "1F3CD", "html": "🏍", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🏹", "name": "bow and arrow", "shortname": ":bow_and_arrow:", "unicode": "1f3f9", "html": "🏹", "category": "Objects (tool)", "order": "1957"}, + {"emoji": "🏸", "name": "badminton", "shortname": ":badminton:", "unicode": "1F3F8", "html": "🏸", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏵", "name": "rosette", "shortname": ":rosette:", "unicode": "1f3f5", "html": "🏵", "category": "Animals & Nature (plant-flower)", "order": "1430"}, + {"emoji": "🏓", "name": "ping pong", "shortname": ":ping_pong:", "unicode": "1F3D3", "html": "🏓", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏒", "name": "ice hockey", "shortname": ":ice_hockey:", "unicode": "1F3D2", "html": "🏒", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏑", "name": "field hockey", "shortname": ":field_hockey:", "unicode": "1F3D1", "html": "🏑", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏐", "name": "volleyball", "shortname": ":volleyball:", "unicode": "1f3d0", "html": "🏐", "category": "Activities (sport)", "order": "1784"}, + {"emoji": "🏏", "name": "cricket game", "shortname": ":cricket_game:", "unicode": "1F3CF", "html": "🏏", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏅", "name": "sports medal", "shortname": ":medal:", "unicode": "1f3c5", "html": "🏅", "category": "Activities (award-medal)", "order": "1777"}, + {"emoji": "🎟", "name": "admission tickets", "shortname": ":admission_tickets:", "unicode": "1F39F", "html": "🎟", "category": "Activities (event)", "order": ""}, + {"emoji": "🎗", "name": "reminder ribbon", "shortname": ":reminder_ribbon:", "unicode": "1f397", "html": "🎗", "category": "Activities (event)", "order": "1772"}, + {"emoji": "🎖", "name": "military medal", "shortname": ":military_medal:", "unicode": "1F396", "html": "🎖", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "🧀", "name": "cheese wedge", "shortname": ":cheese_wedge:", "unicode": "1F9C0", "html": "🧀", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🍿", "name": "popcorn", "shortname": ":popcorn:", "unicode": "1f37f", "html": "🍿", "category": "Food & Drink (food-prepared)", "order": "1494"}, + {"emoji": "🍾", "name": "bottle with popping cork", "shortname": ":champagne:", "unicode": "1f37e", "html": "🍾", "category": "Food & Drink (drink)", "order": "1525"}, + {"emoji": "🍽", "name": "fork and knife with plate", "shortname": ":fork_and_knife_with_plate:", "unicode": "1F37D", "html": "🍽", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "🌶", "name": "hot pepper", "shortname": ":hot_pepper:", "unicode": "1f336", "html": "🌶", "category": "Food & Drink (food-vegetable)", "order": "1469"}, + {"emoji": "🌯", "name": "burrito", "shortname": ":burrito:", "unicode": "1f32f", "html": "🌯", "category": "Food & Drink (food-prepared)", "order": "1487"}, + {"emoji": "🌮", "name": "taco", "shortname": ":taco:", "unicode": "1f32e", "html": "🌮", "category": "Food & Drink (food-prepared)", "order": "1486"}, + {"emoji": "🌭", "name": "hot dog", "shortname": ":hotdog:", "unicode": "1f32d", "html": "🌭", "category": "Food & Drink (food-prepared)", "order": "1485"}, + {"emoji": "☘", "name": "shamrock", "shortname": ":shamrock:", "unicode": "2618", "html": "☘", "category": "Animals & Nature (plant-other)", "order": "1444"}, + {"emoji": "☄", "name": "comet", "shortname": ":comet:", "unicode": "2604", "html": "☄", "category": "Travel & Places (sky & weather)", "order": "1752"}, + {"emoji": "☃️", "name": "snowman", "shortname": ":snowman:", "unicode": "2603 FE0F", "html": "☃", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☂️", "name": "umbrella", "shortname": ":umbrella:", "unicode": "2602 FE0F", "html": "☂", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🦄", "name": "unicorn", "shortname": ":unicorn:", "unicode": "1F984", "html": "🦄", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦃", "name": "turkey", "shortname": ":turkey:", "unicode": "1f983", "html": "🦃", "category": "Animals & Nature (animal-bird)", "order": "1387"}, + {"emoji": "🦂", "name": "scorpion", "shortname": ":scorpion:", "unicode": "1f982", "html": "🦂", "category": "Animals & Nature (animal-bug)", "order": "1426"}, + {"emoji": "🦁", "name": "lion", "shortname": ":lion_face:", "unicode": "1f981", "html": "🦁", "category": "Animals & Nature (animal-mammal)", "order": "1352"}, + {"emoji": "🦀", "name": "crab", "shortname": ":crab:", "unicode": "1f980", "html": "🦀", "category": "Food & Drink (food-marine)", "order": "1415"}, + {"emoji": "🕸", "name": "spider web", "shortname": ":spider_web:", "unicode": "1f578", "html": "🕸", "category": "Animals & Nature (animal-bug)", "order": "1425"}, + {"emoji": "🕷", "name": "spider", "shortname": ":spider:", "unicode": "1f577", "html": "🕷", "category": "Animals & Nature (animal-bug)", "order": "1424"}, + {"emoji": "🕊", "name": "dove", "shortname": ":dove:", "unicode": "1F54A", "html": "🕊", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🐿", "name": "chipmunk", "shortname": ":chipmunk:", "unicode": "1f43f", "html": "🐿", "category": "Animals & Nature (animal-mammal)", "order": "1381"}, + {"emoji": "🌬", "name": "wind face", "shortname": ":wind_blowing_face:", "unicode": "1f32c", "html": "🌬", "category": "Travel & Places (sky & weather)", "order": "1741"}, + {"emoji": "🌫", "name": "fog", "shortname": ":fog:", "unicode": "1f32b", "html": "🌫", "category": "Travel & Places (sky & weather)", "order": "1740"}, + {"emoji": "🌪", "name": "tornado", "shortname": ":tornado:", "unicode": "1F32A", "html": "🌪", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌩", "name": "cloud with lightning", "shortname": ":cloud_with_lightning:", "unicode": "1F329", "html": "🌩", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌨", "name": "cloud with snow", "shortname": ":cloud_with_snow:", "unicode": "1F328", "html": "🌨", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌧", "name": "cloud with rain", "shortname": ":cloud_with_rain:", "unicode": "1F327", "html": "🌧", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌦", "name": "sun behind rain cloud", "shortname": ":sun_behind_rain_cloud:", "unicode": "1F326", "html": "🌦", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌥", "name": "sun behind large cloud", "shortname": ":sun_behind_large_cloud:", "unicode": "1F325", "html": "🌥", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌤", "name": "sun behind small cloud", "shortname": ":sun_behind_small_cloud:", "unicode": "1F324", "html": "🌤", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🗣", "name": "speaking head", "shortname": ":speaking_head:", "unicode": "1F5E3", "html": "🗣", "category": "People & Body (person-symbol)", "order": ""}, + {"emoji": "⏺", "name": "record button", "shortname": ":record_button:", "unicode": "23FA", "html": "⏺", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏹", "name": "stop button", "shortname": ":stop_button:", "unicode": "23F9", "html": "⏹", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏸", "name": "pause button", "shortname": ":pause_button:", "unicode": "23F8", "html": "⏸", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏯", "name": "play or pause button", "shortname": ":play_pause:", "unicode": "23ef", "html": "⏯", "category": "Symbols (av-symbol)", "order": "2054"}, + {"emoji": "⏮", "name": "last track button", "shortname": ":track_previous:", "unicode": "23ee", "html": "⏮", "category": "Symbols (av-symbol)", "order": "2057"}, + {"emoji": "⏭", "name": "next track button", "shortname": ":track_next:", "unicode": "23ed", "html": "⏭", "category": "Symbols (av-symbol)", "order": "2053"}, + {"emoji": "⛱", "name": "umbrella on ground", "shortname": ":beach_umbrella:", "unicode": "26f1", "html": "⛱", "category": "Travel & Places (sky & weather)", "order": "1747"}, + {"emoji": "⛓", "name": "chains", "shortname": ":chains:", "unicode": "26d3", "html": "⛓", "category": "Objects (tool)", "order": "1966"}, + {"emoji": "⛏", "name": "pick", "shortname": ":pick:", "unicode": "26cf", "html": "⛏", "category": "Objects (tool)", "order": "1951"}, + {"emoji": "⚒", "name": "hammer and pick", "shortname": ":hammer_and_pick:", "unicode": "2692", "html": "⚒", "category": "Objects (tool)", "order": ""}, + {"emoji": "⏲", "name": "timer clock", "shortname": ":timer_clock:", "unicode": "23F2", "html": "⏲", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "⏱", "name": "stopwatch", "shortname": ":stopwatch:", "unicode": "23f1", "html": "⏱", "category": "Travel & Places (time)", "order": "1684"}, + {"emoji": "⛴", "name": "ferry", "shortname": ":ferry:", "unicode": "26f4", "html": "⛴", "category": "Travel & Places (transport-water)", "order": "1647"}, + {"emoji": "⛰", "name": "mountain", "shortname": ":mountain:", "unicode": "26f0", "html": "⛰", "category": "Travel & Places (place-geographic)", "order": "1545"}, + {"emoji": "⛩", "name": "shinto shrine", "shortname": ":shinto_shrine:", "unicode": "2.6E+10", "html": "⛩", "category": "Travel & Places (place-religious)", "order": "1580"}, + {"emoji": "⛸", "name": "ice skate", "shortname": ":ice_skate:", "unicode": "26f8", "html": "⛸", "category": "Activities (sport)", "order": "1800"}, + {"emoji": "⛷", "name": "skier", "shortname": ":skier:", "unicode": "26f7", "html": "⛷", "category": "People & Body (person-sport)", "order": "775"}, + {"emoji": "⛈", "name": "cloud with lightning and rain", "shortname": ":cloud_with_lightning_and_rain:", "unicode": "26C8", "html": "⛈", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "⛑", "name": "rescue worker’s helmet", "shortname": ":rescue_worker’s_helmet:", "unicode": "26D1", "html": "⛑", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🇦🇨", "name": "flag: Ascension Island", "shortname": ":flag_ac:", "unicode": "1f1e6-1f1e8", "html": "🇦🇨", "category": "Flags (country-flag)", "order": "2187"}, + {"emoji": "🇦🇩", "name": "flag: Andorra", "shortname": ":flag_ad:", "unicode": "1f1e6-1f1e9", "html": "🇦🇩", "category": "Flags (country-flag)", "order": "2188"}, + {"emoji": "🇦🇪", "name": "flag: United Arab Emirates", "shortname": ":flag_ae:", "unicode": "1f1e6-1f1ea", "html": "🇦🇪", "category": "Flags (country-flag)", "order": "2189"}, + {"emoji": "🇦🇫", "name": "flag: Afghanistan", "shortname": ":flag_af:", "unicode": "1f1e6-1f1eb", "html": "🇦🇫", "category": "Flags (country-flag)", "order": "2190"}, + {"emoji": "🇦🇬", "name": "flag: Antigua & Barbuda", "shortname": ":flag_ag:", "unicode": "1f1e6-1f1ec", "html": "🇦🇬", "category": "Flags (country-flag)", "order": "2191"}, + {"emoji": "🇦🇮", "name": "flag: Anguilla", "shortname": ":flag_ai:", "unicode": "1f1e6-1f1ee", "html": "🇦🇮", "category": "Flags (country-flag)", "order": "2192"}, + {"emoji": "🇦🇱", "name": "flag: Albania", "shortname": ":flag_al:", "unicode": "1f1e6-1f1f1", "html": "🇦🇱", "category": "Flags (country-flag)", "order": "2193"}, + {"emoji": "🇦🇲", "name": "flag: Armenia", "shortname": ":flag_am:", "unicode": "1f1e6-1f1f2", "html": "🇦🇲", "category": "Flags (country-flag)", "order": "2194"}, + {"emoji": "🇦🇴", "name": "flag: Angola", "shortname": ":flag-ao:", "unicode": "1f1e6-1f1f4", "html": "🇦🇴", "category": "Flags (country-flag)", "order": "2195"}, + {"emoji": "🇦🇶", "name": "flag: Antarctica", "shortname": ":flag-aq:", "unicode": "1f1e6-1f1f6", "html": "🇦🇶", "category": "Flags (country-flag)", "order": "2196"}, + {"emoji": "🇦🇷", "name": "flag: Argentina", "shortname": ":flag-ar:", "unicode": "1f1e6-1f1f7", "html": "🇦🇷", "category": "Flags (country-flag)", "order": "2197"}, + {"emoji": "🇦🇸", "name": "flag: American Samoa", "shortname": ":flag-as:", "unicode": "1f1e6-1f1f8", "html": "🇦🇸", "category": "Flags (country-flag)", "order": "2198"}, + {"emoji": "🇦🇹", "name": "flag: Austria", "shortname": ":flag-at:", "unicode": "1f1e6-1f1f9", "html": "🇦🇹", "category": "Flags (country-flag)", "order": "2199"}, + {"emoji": "🇦🇺", "name": "flag: Australia", "shortname": ":flag-au:", "unicode": "1f1e6-1f1fa", "html": "🇦🇺", "category": "Flags (country-flag)", "order": "2200"}, + {"emoji": "🇦🇼", "name": "flag: Aruba", "shortname": ":flag-aw:", "unicode": "1f1e6-1f1fc", "html": "🇦🇼", "category": "Flags (country-flag)", "order": "2201"}, + {"emoji": "🇦🇽", "name": "flag: Åland Islands", "shortname": ":flag-ax:", "unicode": "1f1e6-1f1fd", "html": "🇦🇽", "category": "Flags (country-flag)", "order": "2202"}, + {"emoji": "🇦🇿", "name": "flag: Azerbaijan", "shortname": ":flag-az:", "unicode": "1f1e6-1f1ff", "html": "🇦🇿", "category": "Flags (country-flag)", "order": "2203"}, + {"emoji": "🇧🇦", "name": "flag: Bosnia & Herzegovina", "shortname": ":flag-ba:", "unicode": "1f1e7-1f1e6", "html": "🇧🇦", "category": "Flags (country-flag)", "order": "2204"}, + {"emoji": "🇧🇧", "name": "flag: Barbados", "shortname": ":flag-bb:", "unicode": "1f1e7-1f1e7", "html": "🇧🇧", "category": "Flags (country-flag)", "order": "2205"}, + {"emoji": "🇧🇩", "name": "flag: Bangladesh", "shortname": ":flag-bd:", "unicode": "1f1e7-1f1e9", "html": "🇧🇩", "category": "Flags (country-flag)", "order": "2206"}, + {"emoji": "🇧🇪", "name": "flag: Belgium", "shortname": ":flag-be:", "unicode": "1f1e7-1f1ea", "html": "🇧🇪", "category": "Flags (country-flag)", "order": "2207"}, + {"emoji": "🇧🇫", "name": "flag: Burkina Faso", "shortname": ":flag-bf:", "unicode": "1f1e7-1f1eb", "html": "🇧🇫", "category": "Flags (country-flag)", "order": "2208"}, + {"emoji": "🇧🇬", "name": "flag: Bulgaria", "shortname": ":flag-bg:", "unicode": "1f1e7-1f1ec", "html": "🇧🇬", "category": "Flags (country-flag)", "order": "2209"}, + {"emoji": "🇧🇭", "name": "flag: Bahrain", "shortname": ":flag-bh:", "unicode": "1f1e7-1f1ed", "html": "🇧🇭", "category": "Flags (country-flag)", "order": "2210"}, + {"emoji": "🇧🇮", "name": "flag: Burundi", "shortname": ":flag-bi:", "unicode": "1f1e7-1f1ee", "html": "🇧🇮", "category": "Flags (country-flag)", "order": "2211"}, + {"emoji": "🇧🇯", "name": "flag: Benin", "shortname": ":flag-bj:", "unicode": "1f1e7-1f1ef", "html": "🇧🇯", "category": "Flags (country-flag)", "order": "2212"}, + {"emoji": "🇧🇱", "name": "flag: St. Barthélemy", "shortname": ":flag-bl:", "unicode": "1f1e7-1f1f1", "html": "🇧🇱", "category": "Flags (country-flag)", "order": "2213"}, + {"emoji": "🇧🇲", "name": "flag: Bermuda", "shortname": ":flag-bm:", "unicode": "1f1e7-1f1f2", "html": "🇧🇲", "category": "Flags (country-flag)", "order": "2214"}, + {"emoji": "🇧🇳", "name": "flag: Brunei", "shortname": ":flag-bn:", "unicode": "1f1e7-1f1f3", "html": "🇧🇳", "category": "Flags (country-flag)", "order": "2215"}, + {"emoji": "🇧🇴", "name": "flag: Bolivia", "shortname": ":flag-bo:", "unicode": "1f1e7-1f1f4", "html": "🇧🇴", "category": "Flags (country-flag)", "order": "2216"}, + {"emoji": "🇧🇶", "name": "flag: Caribbean Netherlands", "shortname": ":flag-bq:", "unicode": "1f1e7-1f1f6", "html": "🇧🇶", "category": "Flags (country-flag)", "order": "2217"}, + {"emoji": "🇧🇷", "name": "flag: Brazil", "shortname": ":flag-br:", "unicode": "1f1e7-1f1f7", "html": "🇧🇷", "category": "Flags (country-flag)", "order": "2218"}, + {"emoji": "🇧🇸", "name": "flag: Bahamas", "shortname": ":flag-bs:", "unicode": "1f1e7-1f1f8", "html": "🇧🇸", "category": "Flags (country-flag)", "order": "2219"}, + {"emoji": "🇧🇹", "name": "flag: Bhutan", "shortname": ":flag-bt:", "unicode": "1f1e7-1f1f9", "html": "🇧🇹", "category": "Flags (country-flag)", "order": "2220"}, + {"emoji": "🇧🇻", "name": "flag: Bouvet Island", "shortname": ":flag-bv:", "unicode": "1f1e7-1f1fb", "html": "🇧🇻", "category": "Flags (country-flag)", "order": "2221"}, + {"emoji": "🇧🇼", "name": "flag: Botswana", "shortname": ":flag-bw:", "unicode": "1f1e7-1f1fc", "html": "🇧🇼", "category": "Flags (country-flag)", "order": "2222"}, + {"emoji": "🇧🇾", "name": "flag: Belarus", "shortname": ":flag-by:", "unicode": "1f1e7-1f1fe", "html": "🇧🇾", "category": "Flags (country-flag)", "order": "2223"}, + {"emoji": "🇧🇿", "name": "flag: Belize", "shortname": ":flag-bz:", "unicode": "1f1e7-1f1ff", "html": "🇧🇿", "category": "Flags (country-flag)", "order": "2224"}, + {"emoji": "🇨🇦", "name": "flag: Canada", "shortname": ":flag-ca:", "unicode": "1f1e8-1f1e6", "html": "🇨🇦", "category": "Flags (country-flag)", "order": "2225"}, + {"emoji": "🇨🇨", "name": "flag: Cocos (Keeling) Islands", "shortname": ":flag-cc:", "unicode": "1f1e8-1f1e8", "html": "🇨🇨", "category": "Flags (country-flag)", "order": "2226"}, + {"emoji": "🇨🇩", "name": "flag: Congo - Kinshasa", "shortname": ":flag-cd:", "unicode": "1f1e8-1f1e9", "html": "🇨🇩", "category": "Flags (country-flag)", "order": "2227"}, + {"emoji": "🇨🇫", "name": "flag: Central African Republic", "shortname": ":flag-cf:", "unicode": "1f1e8-1f1eb", "html": "🇨🇫", "category": "Flags (country-flag)", "order": "2228"}, + {"emoji": "🇨🇬", "name": "flag: Congo - Brazzaville", "shortname": ":flag-cg:", "unicode": "1f1e8-1f1ec", "html": "🇨🇬", "category": "Flags (country-flag)", "order": "2229"}, + {"emoji": "🇨🇭", "name": "flag: Switzerland", "shortname": ":flag-ch:", "unicode": "1f1e8-1f1ed", "html": "🇨🇭", "category": "Flags (country-flag)", "order": "2230"}, + {"emoji": "🇨🇮", "name": "flag: Côte d’Ivoire", "shortname": ":flag-ci:", "unicode": "1f1e8-1f1ee", "html": "🇨🇮", "category": "Flags (country-flag)", "order": "2231"}, + {"emoji": "🇨🇰", "name": "flag: Cook Islands", "shortname": ":flag-ck:", "unicode": "1f1e8-1f1f0", "html": "🇨🇰", "category": "Flags (country-flag)", "order": "2232"}, + {"emoji": "🇨🇱", "name": "flag: Chile", "shortname": ":flag-cl:", "unicode": "1f1e8-1f1f1", "html": "🇨🇱", "category": "Flags (country-flag)", "order": "2233"}, + {"emoji": "🇨🇲", "name": "flag: Cameroon", "shortname": ":flag-cm:", "unicode": "1f1e8-1f1f2", "html": "🇨🇲", "category": "Flags (country-flag)", "order": "2234"}, + {"emoji": "🇨🇳", "name": "flag: China", "shortname": ":flag-cn:", "unicode": "1f1e8-1f1f3", "html": "🇨🇳", "category": "Flags (country-flag)", "order": "2235"}, + {"emoji": "🇨🇴", "name": "flag: Colombia", "shortname": ":flag-co:", "unicode": "1f1e8-1f1f4", "html": "🇨🇴", "category": "Flags (country-flag)", "order": "2236"}, + {"emoji": "🇨🇵", "name": "flag: Clipperton Island", "shortname": ":flag-cp:", "unicode": "1f1e8-1f1f5", "html": "🇨🇵", "category": "Flags (country-flag)", "order": "2237"}, + {"emoji": "🇨🇷", "name": "flag: Costa Rica", "shortname": ":flag-cr:", "unicode": "1f1e8-1f1f7", "html": "🇨🇷", "category": "Flags (country-flag)", "order": "2238"}, + {"emoji": "🇨🇺", "name": "flag: Cuba", "shortname": ":flag-cu:", "unicode": "1f1e8-1f1fa", "html": "🇨🇺", "category": "Flags (country-flag)", "order": "2239"}, + {"emoji": "🇨🇻", "name": "flag: Cape Verde", "shortname": ":flag-cv:", "unicode": "1f1e8-1f1fb", "html": "🇨🇻", "category": "Flags (country-flag)", "order": "2240"}, + {"emoji": "🇨🇼", "name": "flag: Curaçao", "shortname": ":flag-cw:", "unicode": "1f1e8-1f1fc", "html": "🇨🇼", "category": "Flags (country-flag)", "order": "2241"}, + {"emoji": "🇨🇽", "name": "flag: Christmas Island", "shortname": ":flag-cx:", "unicode": "1f1e8-1f1fd", "html": "🇨🇽", "category": "Flags (country-flag)", "order": "2242"}, + {"emoji": "🇨🇾", "name": "flag: Cyprus", "shortname": ":flag-cy:", "unicode": "1f1e8-1f1fe", "html": "🇨🇾", "category": "Flags (country-flag)", "order": "2243"}, + {"emoji": "🇨🇿", "name": "flag: Czechia", "shortname": ":flag-cz:", "unicode": "1f1e8-1f1ff", "html": "🇨🇿", "category": "Flags (country-flag)", "order": "2244"}, + {"emoji": "🇩🇪", "name": "flag: Germany", "shortname": ":flag-de:", "unicode": "1f1e9-1f1ea", "html": "🇩🇪", "category": "Flags (country-flag)", "order": "2245"}, + {"emoji": "🇩🇬", "name": "flag: Diego Garcia", "shortname": ":flag-dg:", "unicode": "1f1e9-1f1ec", "html": "🇩🇬", "category": "Flags (country-flag)", "order": "2246"}, + {"emoji": "🇩🇯", "name": "flag: Djibouti", "shortname": ":flag-dj:", "unicode": "1f1e9-1f1ef", "html": "🇩🇯", "category": "Flags (country-flag)", "order": "2247"}, + {"emoji": "🇩🇰", "name": "flag: Denmark", "shortname": ":flag-dk:", "unicode": "1f1e9-1f1f0", "html": "🇩🇰", "category": "Flags (country-flag)", "order": "2248"}, + {"emoji": "🇩🇲", "name": "flag: Dominica", "shortname": ":flag-dm:", "unicode": "1f1e9-1f1f2", "html": "🇩🇲", "category": "Flags (country-flag)", "order": "2249"}, + {"emoji": "🇩🇴", "name": "flag: Dominican Republic", "shortname": ":flag-do:", "unicode": "1f1e9-1f1f4", "html": "🇩🇴", "category": "Flags (country-flag)", "order": "2250"}, + {"emoji": "🇩🇿", "name": "flag: Algeria", "shortname": ":flag-dz:", "unicode": "1f1e9-1f1ff", "html": "🇩🇿", "category": "Flags (country-flag)", "order": "2251"}, + {"emoji": "🇪🇦", "name": "flag: Ceuta & Melilla", "shortname": ":flag-ea:", "unicode": "1f1ea-1f1e6", "html": "🇪🇦", "category": "Flags (country-flag)", "order": "2252"}, + {"emoji": "🇪🇨", "name": "flag: Ecuador", "shortname": ":flag-ec:", "unicode": "1f1ea-1f1e8", "html": "🇪🇨", "category": "Flags (country-flag)", "order": "2253"}, + {"emoji": "🇪🇪", "name": "flag: Estonia", "shortname": ":flag-ee:", "unicode": "1f1ea-1f1ea", "html": "🇪🇪", "category": "Flags (country-flag)", "order": "2254"}, + {"emoji": "🇪🇬", "name": "flag: Egypt", "shortname": ":flag-eg:", "unicode": "1f1ea-1f1ec", "html": "🇪🇬", "category": "Flags (country-flag)", "order": "2255"}, + {"emoji": "🇪🇭", "name": "flag: Western Sahara", "shortname": ":flag-eh:", "unicode": "1f1ea-1f1ed", "html": "🇪🇭", "category": "Flags (country-flag)", "order": "2256"}, + {"emoji": "🇪🇷", "name": "flag: Eritrea", "shortname": ":flag-er:", "unicode": "1f1ea-1f1f7", "html": "🇪🇷", "category": "Flags (country-flag)", "order": "2257"}, + {"emoji": "🇪🇸", "name": "flag: Spain", "shortname": ":flag-es:", "unicode": "1f1ea-1f1f8", "html": "🇪🇸", "category": "Flags (country-flag)", "order": "2258"}, + {"emoji": "🇪🇹", "name": "flag: Ethiopia", "shortname": ":flag-et:", "unicode": "1f1ea-1f1f9", "html": "🇪🇹", "category": "Flags (country-flag)", "order": "2259"}, + {"emoji": "🇪🇺", "name": "flag: European Union", "shortname": ":flag-eu:", "unicode": "1f1ea-1f1fa", "html": "🇪🇺", "category": "Flags (country-flag)", "order": "2260"}, + {"emoji": "🇫🇮", "name": "flag: Finland", "shortname": ":flag-fi:", "unicode": "1f1eb-1f1ee", "html": "🇫🇮", "category": "Flags (country-flag)", "order": "2261"}, + {"emoji": "🇫🇯", "name": "flag: Fiji", "shortname": ":flag-fj:", "unicode": "1f1eb-1f1ef", "html": "🇫🇯", "category": "Flags (country-flag)", "order": "2262"}, + {"emoji": "🇫🇰", "name": "flag: Falkland Islands", "shortname": ":flag-fk:", "unicode": "1f1eb-1f1f0", "html": "🇫🇰", "category": "Flags (country-flag)", "order": "2263"}, + {"emoji": "🇫🇲", "name": "flag: Micronesia", "shortname": ":flag-fm:", "unicode": "1f1eb-1f1f2", "html": "🇫🇲", "category": "Flags (country-flag)", "order": "2264"}, + {"emoji": "🇫🇴", "name": "flag: Faroe Islands", "shortname": ":flag-fo:", "unicode": "1f1eb-1f1f4", "html": "🇫🇴", "category": "Flags (country-flag)", "order": "2265"}, + {"emoji": "🇫🇷", "name": "flag: France", "shortname": ":flag-fr:", "unicode": "1f1eb-1f1f7", "html": "🇫🇷", "category": "Flags (country-flag)", "order": "2266"}, + {"emoji": "🇬🇦", "name": "flag: Gabon", "shortname": ":flag-ga:", "unicode": "1f1ec-1f1e6", "html": "🇬🇦", "category": "Flags (country-flag)", "order": "2267"}, + {"emoji": "🇬🇧", "name": "flag: United Kingdom", "shortname": ":flag-gb:", "unicode": "1f1ec-1f1e7", "html": "🇬🇧", "category": "Flags (country-flag)", "order": "2268"}, + {"emoji": "🇬🇩", "name": "flag: Grenada", "shortname": ":flag-gd:", "unicode": "1f1ec-1f1e9", "html": "🇬🇩", "category": "Flags (country-flag)", "order": "2269"}, + {"emoji": "🇬🇪", "name": "flag: Georgia", "shortname": ":flag-ge:", "unicode": "1f1ec-1f1ea", "html": "🇬🇪", "category": "Flags (country-flag)", "order": "2270"}, + {"emoji": "🇬🇫", "name": "flag: French Guiana", "shortname": ":flag-gf:", "unicode": "1f1ec-1f1eb", "html": "🇬🇫", "category": "Flags (country-flag)", "order": "2271"}, + {"emoji": "🇬🇬", "name": "flag: Guernsey", "shortname": ":flag-gg:", "unicode": "1f1ec-1f1ec", "html": "🇬🇬", "category": "Flags (country-flag)", "order": "2272"}, + {"emoji": "🇬🇭", "name": "flag: Ghana", "shortname": ":flag-gh:", "unicode": "1f1ec-1f1ed", "html": "🇬🇭", "category": "Flags (country-flag)", "order": "2273"}, + {"emoji": "🇬🇮", "name": "flag: Gibraltar", "shortname": ":flag-gi:", "unicode": "1f1ec-1f1ee", "html": "🇬🇮", "category": "Flags (country-flag)", "order": "2274"}, + {"emoji": "🇬🇱", "name": "flag: Greenland", "shortname": ":flag-gl:", "unicode": "1f1ec-1f1f1", "html": "🇬🇱", "category": "Flags (country-flag)", "order": "2275"}, + {"emoji": "🇬🇲", "name": "flag: Gambia", "shortname": ":flag-gm:", "unicode": "1f1ec-1f1f2", "html": "🇬🇲", "category": "Flags (country-flag)", "order": "2276"}, + {"emoji": "🇬🇳", "name": "flag: Guinea", "shortname": ":flag-gn:", "unicode": "1f1ec-1f1f3", "html": "🇬🇳", "category": "Flags (country-flag)", "order": "2277"}, + {"emoji": "🇬🇵", "name": "flag: Guadeloupe", "shortname": ":flag-gp:", "unicode": "1f1ec-1f1f5", "html": "🇬🇵", "category": "Flags (country-flag)", "order": "2278"}, + {"emoji": "🇬🇶", "name": "flag: Equatorial Guinea", "shortname": ":flag-gq:", "unicode": "1f1ec-1f1f6", "html": "🇬🇶", "category": "Flags (country-flag)", "order": "2279"}, + {"emoji": "🇬🇷", "name": "flag: Greece", "shortname": ":flag-gr:", "unicode": "1f1ec-1f1f7", "html": "🇬🇷", "category": "Flags (country-flag)", "order": "2280"}, + {"emoji": "🇬🇸", "name": "flag: South Georgia & South Sandwich Islands", "shortname": ":flag-gs:", "unicode": "1f1ec-1f1f8", "html": "🇬🇸", "category": "Flags (country-flag)", "order": "2281"}, + {"emoji": "🇬🇹", "name": "flag: Guatemala", "shortname": ":flag-gt:", "unicode": "1f1ec-1f1f9", "html": "🇬🇹", "category": "Flags (country-flag)", "order": "2282"}, + {"emoji": "🇬🇺", "name": "flag: Guam", "shortname": ":flag-gu:", "unicode": "1f1ec-1f1fa", "html": "🇬🇺", "category": "Flags (country-flag)", "order": "2283"}, + {"emoji": "🇬🇼", "name": "flag: Guinea-Bissau", "shortname": ":flag-gw:", "unicode": "1f1ec-1f1fc", "html": "🇬🇼", "category": "Flags (country-flag)", "order": "2284"}, + {"emoji": "🇬🇾", "name": "flag: Guyana", "shortname": ":flag-gy:", "unicode": "1f1ec-1f1fe", "html": "🇬🇾", "category": "Flags (country-flag)", "order": "2285"}, + {"emoji": "🇭🇰", "name": "flag: Hong Kong SAR China", "shortname": ":flag-hk:", "unicode": "1f1ed-1f1f0", "html": "🇭🇰", "category": "Flags (country-flag)", "order": "2286"}, + {"emoji": "🇭🇲", "name": "flag: Heard & McDonald Islands", "shortname": ":flag-hm:", "unicode": "1f1ed-1f1f2", "html": "🇭🇲", "category": "Flags (country-flag)", "order": "2287"}, + {"emoji": "🇭🇳", "name": "flag: Honduras", "shortname": ":flag-hn:", "unicode": "1f1ed-1f1f3", "html": "🇭🇳", "category": "Flags (country-flag)", "order": "2288"}, + {"emoji": "🇭🇷", "name": "flag: Croatia", "shortname": ":flag-hr:", "unicode": "1f1ed-1f1f7", "html": "🇭🇷", "category": "Flags (country-flag)", "order": "2289"}, + {"emoji": "🇭🇹", "name": "flag: Haiti", "shortname": ":flag-ht:", "unicode": "1f1ed-1f1f9", "html": "🇭🇹", "category": "Flags (country-flag)", "order": "2290"}, + {"emoji": "🇭🇺", "name": "flag: Hungary", "shortname": ":flag-hu:", "unicode": "1f1ed-1f1fa", "html": "🇭🇺", "category": "Flags (country-flag)", "order": "2291"}, + {"emoji": "🇮🇨", "name": "flag: Canary Islands", "shortname": ":flag-ic:", "unicode": "1f1ee-1f1e8", "html": "🇮🇨", "category": "Flags (country-flag)", "order": "2292"}, + {"emoji": "🇮🇩", "name": "flag: Indonesia", "shortname": ":flag-id:", "unicode": "1f1ee-1f1e9", "html": "🇮🇩", "category": "Flags (country-flag)", "order": "2293"}, + {"emoji": "🇮🇪", "name": "flag: Ireland", "shortname": ":flag-ie:", "unicode": "1f1ee-1f1ea", "html": "🇮🇪", "category": "Flags (country-flag)", "order": "2294"}, + {"emoji": "🇮🇱", "name": "flag: Israel", "shortname": ":flag-il:", "unicode": "1f1ee-1f1f1", "html": "🇮🇱", "category": "Flags (country-flag)", "order": "2295"}, + {"emoji": "🇮🇲", "name": "flag: Isle of Man", "shortname": ":flag-im:", "unicode": "1f1ee-1f1f2", "html": "🇮🇲", "category": "Flags (country-flag)", "order": "2296"}, + {"emoji": "🇮🇳", "name": "flag: India", "shortname": ":flag-in:", "unicode": "1f1ee-1f1f3", "html": "🇮🇳", "category": "Flags (country-flag)", "order": "2297"}, + {"emoji": "🇮🇴", "name": "flag: British Indian Ocean Territory", "shortname": ":flag-io:", "unicode": "1f1ee-1f1f4", "html": "🇮🇴", "category": "Flags (country-flag)", "order": "2298"}, + {"emoji": "🇮🇶", "name": "flag: Iraq", "shortname": ":flag-iq:", "unicode": "1f1ee-1f1f6", "html": "🇮🇶", "category": "Flags (country-flag)", "order": "2299"}, + {"emoji": "🇮🇷", "name": "flag: Iran", "shortname": ":flag-ir:", "unicode": "1f1ee-1f1f7", "html": "🇮🇷", "category": "Flags (country-flag)", "order": "2300"}, + {"emoji": "🇮🇸", "name": "flag: Iceland", "shortname": ":flag-is:", "unicode": "1f1ee-1f1f8", "html": "🇮🇸", "category": "Flags (country-flag)", "order": "2301"}, + {"emoji": "🇮🇹", "name": "flag: Italy", "shortname": ":flag-it:", "unicode": "1f1ee-1f1f9", "html": "🇮🇹", "category": "Flags (country-flag)", "order": "2302"}, + {"emoji": "🇯🇪", "name": "flag: Jersey", "shortname": ":flag-je:", "unicode": "1f1ef-1f1ea", "html": "🇯🇪", "category": "Flags (country-flag)", "order": "2303"}, + {"emoji": "🇯🇲", "name": "flag: Jamaica", "shortname": ":flag-jm:", "unicode": "1f1ef-1f1f2", "html": "🇯🇲", "category": "Flags (country-flag)", "order": "2304"}, + {"emoji": "🇯🇴", "name": "flag: Jordan", "shortname": ":flag-jo:", "unicode": "1f1ef-1f1f4", "html": "🇯🇴", "category": "Flags (country-flag)", "order": "2305"}, + {"emoji": "🇯🇵", "name": "flag: Japan", "shortname": ":flag-jp:", "unicode": "1f1ef-1f1f5", "html": "🇯🇵", "category": "Flags (country-flag)", "order": "2306"}, + {"emoji": "🇰🇪", "name": "flag: Kenya", "shortname": ":flag-ke:", "unicode": "1f1f0-1f1ea", "html": "🇰🇪", "category": "Flags (country-flag)", "order": "2307"}, + {"emoji": "🇰🇬", "name": "flag: Kyrgyzstan", "shortname": ":flag-kg:", "unicode": "1f1f0-1f1ec", "html": "🇰🇬", "category": "Flags (country-flag)", "order": "2308"}, + {"emoji": "🇰🇭", "name": "flag: Cambodia", "shortname": ":flag-kh:", "unicode": "1f1f0-1f1ed", "html": "🇰🇭", "category": "Flags (country-flag)", "order": "2309"}, + {"emoji": "🇰🇮", "name": "flag: Kiribati", "shortname": ":flag-ki:", "unicode": "1f1f0-1f1ee", "html": "🇰🇮", "category": "Flags (country-flag)", "order": "2310"}, + {"emoji": "🇰🇲", "name": "flag: Comoros", "shortname": ":flag-km:", "unicode": "1f1f0-1f1f2", "html": "🇰🇲", "category": "Flags (country-flag)", "order": "2311"}, + {"emoji": "🇰🇳", "name": "flag: St. Kitts & Nevis", "shortname": ":flag-kn:", "unicode": "1f1f0-1f1f3", "html": "🇰🇳", "category": "Flags (country-flag)", "order": "2312"}, + {"emoji": "🇰🇵", "name": "flag: North Korea", "shortname": ":flag-kp:", "unicode": "1f1f0-1f1f5", "html": "🇰🇵", "category": "Flags (country-flag)", "order": "2313"}, + {"emoji": "🇰🇷", "name": "flag: South Korea", "shortname": ":flag-kr:", "unicode": "1f1f0-1f1f7", "html": "🇰🇷", "category": "Flags (country-flag)", "order": "2314"}, + {"emoji": "🇰🇼", "name": "flag: Kuwait", "shortname": ":flag-kw:", "unicode": "1f1f0-1f1fc", "html": "🇰🇼", "category": "Flags (country-flag)", "order": "2315"}, + {"emoji": "🇰🇾", "name": "flag: Cayman Islands", "shortname": ":flag-ky:", "unicode": "1f1f0-1f1fe", "html": "🇰🇾", "category": "Flags (country-flag)", "order": "2316"}, + {"emoji": "🇰🇿", "name": "flag: Kazakhstan", "shortname": ":flag-kz:", "unicode": "1f1f0-1f1ff", "html": "🇰🇿", "category": "Flags (country-flag)", "order": "2317"}, + {"emoji": "🇱🇦", "name": "flag: Laos", "shortname": ":flag-la:", "unicode": "1f1f1-1f1e6", "html": "🇱🇦", "category": "Flags (country-flag)", "order": "2318"}, + {"emoji": "🇱🇧", "name": "flag: Lebanon", "shortname": ":flag-lb:", "unicode": "1f1f1-1f1e7", "html": "🇱🇧", "category": "Flags (country-flag)", "order": "2319"}, + {"emoji": "🇱🇨", "name": "flag: St. Lucia", "shortname": ":flag-lc:", "unicode": "1f1f1-1f1e8", "html": "🇱🇨", "category": "Flags (country-flag)", "order": "2320"}, + {"emoji": "🇱🇮", "name": "flag: Liechtenstein", "shortname": ":flag-li:", "unicode": "1f1f1-1f1ee", "html": "🇱🇮", "category": "Flags (country-flag)", "order": "2321"}, + {"emoji": "🇱🇰", "name": "flag: Sri Lanka", "shortname": ":flag-lk:", "unicode": "1f1f1-1f1f0", "html": "🇱🇰", "category": "Flags (country-flag)", "order": "2322"}, + {"emoji": "🇱🇷", "name": "flag: Liberia", "shortname": ":flag-lr:", "unicode": "1f1f1-1f1f7", "html": "🇱🇷", "category": "Flags (country-flag)", "order": "2323"}, + {"emoji": "🇱🇸", "name": "flag: Lesotho", "shortname": ":flag-ls:", "unicode": "1f1f1-1f1f8", "html": "🇱🇸", "category": "Flags (country-flag)", "order": "2324"}, + {"emoji": "🇱🇹", "name": "flag: Lithuania", "shortname": ":flag-lt:", "unicode": "1f1f1-1f1f9", "html": "🇱🇹", "category": "Flags (country-flag)", "order": "2325"}, + {"emoji": "🇱🇺", "name": "flag: Luxembourg", "shortname": ":flag-lu:", "unicode": "1f1f1-1f1fa", "html": "🇱🇺", "category": "Flags (country-flag)", "order": "2326"}, + {"emoji": "🇱🇻", "name": "flag: Latvia", "shortname": ":flag-lv:", "unicode": "1f1f1-1f1fb", "html": "🇱🇻", "category": "Flags (country-flag)", "order": "2327"}, + {"emoji": "🇱🇾", "name": "flag: Libya", "shortname": ":flag-ly:", "unicode": "1f1f1-1f1fe", "html": "🇱🇾", "category": "Flags (country-flag)", "order": "2328"}, + {"emoji": "🇲🇦", "name": "flag: Morocco", "shortname": ":flag-ma:", "unicode": "1f1f2-1f1e6", "html": "🇲🇦", "category": "Flags (country-flag)", "order": "2329"}, + {"emoji": "🇲🇨", "name": "flag: Monaco", "shortname": ":flag-mc:", "unicode": "1f1f2-1f1e8", "html": "🇲🇨", "category": "Flags (country-flag)", "order": "2330"}, + {"emoji": "🇲🇩", "name": "flag: Moldova", "shortname": ":flag-md:", "unicode": "1f1f2-1f1e9", "html": "🇲🇩", "category": "Flags (country-flag)", "order": "2331"}, + {"emoji": "🇲🇪", "name": "flag: Montenegro", "shortname": ":flag-me:", "unicode": "1f1f2-1f1ea", "html": "🇲🇪", "category": "Flags (country-flag)", "order": "2332"}, + {"emoji": "🇲🇫", "name": "flag: St. Martin", "shortname": ":flag-mf:", "unicode": "1f1f2-1f1eb", "html": "🇲🇫", "category": "Flags (country-flag)", "order": "2333"}, + {"emoji": "🇲🇬", "name": "flag: Madagascar", "shortname": ":flag-mg:", "unicode": "1f1f2-1f1ec", "html": "🇲🇬", "category": "Flags (country-flag)", "order": "2334"}, + {"emoji": "🇲🇭", "name": "flag: Marshall Islands", "shortname": ":flag-mh:", "unicode": "1f1f2-1f1ed", "html": "🇲🇭", "category": "Flags (country-flag)", "order": "2335"}, + {"emoji": "🇲🇰", "name": "flag: North Macedonia", "shortname": ":flag-mk:", "unicode": "1f1f2-1f1f0", "html": "🇲🇰", "category": "Flags (country-flag)", "order": "2336"}, + {"emoji": "🇲🇱", "name": "flag: Mali", "shortname": ":flag-ml:", "unicode": "1f1f2-1f1f1", "html": "🇲🇱", "category": "Flags (country-flag)", "order": "2337"}, + {"emoji": "🇲🇲", "name": "flag: Myanmar (Burma)", "shortname": ":flag-mm:", "unicode": "1f1f2-1f1f2", "html": "🇲🇲", "category": "Flags (country-flag)", "order": "2338"}, + {"emoji": "🇲🇳", "name": "flag: Mongolia", "shortname": ":flag-mn:", "unicode": "1f1f2-1f1f3", "html": "🇲🇳", "category": "Flags (country-flag)", "order": "2339"}, + {"emoji": "🇲🇴", "name": "flag: Macao SAR China", "shortname": ":flag-mo:", "unicode": "1f1f2-1f1f4", "html": "🇲🇴", "category": "Flags (country-flag)", "order": "2340"}, + {"emoji": "🇲🇵", "name": "flag: Northern Mariana Islands", "shortname": ":flag-mp:", "unicode": "1f1f2-1f1f5", "html": "🇲🇵", "category": "Flags (country-flag)", "order": "2341"}, + {"emoji": "🇲🇶", "name": "flag: Martinique", "shortname": ":flag-mq:", "unicode": "1f1f2-1f1f6", "html": "🇲🇶", "category": "Flags (country-flag)", "order": "2342"}, + {"emoji": "🇲🇷", "name": "flag: Mauritania", "shortname": ":flag-mr:", "unicode": "1f1f2-1f1f7", "html": "🇲🇷", "category": "Flags (country-flag)", "order": "2343"}, + {"emoji": "🇲🇸", "name": "flag: Montserrat", "shortname": ":flag-ms:", "unicode": "1f1f2-1f1f8", "html": "🇲🇸", "category": "Flags (country-flag)", "order": "2344"}, + {"emoji": "🇲🇹", "name": "flag: Malta", "shortname": ":flag-mt:", "unicode": "1f1f2-1f1f9", "html": "🇲🇹", "category": "Flags (country-flag)", "order": "2345"}, + {"emoji": "🇲🇺", "name": "flag: Mauritius", "shortname": ":flag-mu:", "unicode": "1f1f2-1f1fa", "html": "🇲🇺", "category": "Flags (country-flag)", "order": "2346"}, + {"emoji": "🇲🇻", "name": "flag: Maldives", "shortname": ":flag-mv:", "unicode": "1f1f2-1f1fb", "html": "🇲🇻", "category": "Flags (country-flag)", "order": "2347"}, + {"emoji": "🇲🇼", "name": "flag: Malawi", "shortname": ":flag-mw:", "unicode": "1f1f2-1f1fc", "html": "🇲🇼", "category": "Flags (country-flag)", "order": "2348"}, + {"emoji": "🇲🇽", "name": "flag: Mexico", "shortname": ":flag-mx:", "unicode": "1f1f2-1f1fd", "html": "🇲🇽", "category": "Flags (country-flag)", "order": "2349"}, + {"emoji": "🇲🇾", "name": "flag: Malaysia", "shortname": ":flag-my:", "unicode": "1f1f2-1f1fe", "html": "🇲🇾", "category": "Flags (country-flag)", "order": "2350"}, + {"emoji": "🇲🇿", "name": "flag: Mozambique", "shortname": ":flag-mz:", "unicode": "1f1f2-1f1ff", "html": "🇲🇿", "category": "Flags (country-flag)", "order": "2351"}, + {"emoji": "🇳🇦", "name": "flag: Namibia", "shortname": ":flag-na:", "unicode": "1f1f3-1f1e6", "html": "🇳🇦", "category": "Flags (country-flag)", "order": "2352"}, + {"emoji": "🇳🇨", "name": "flag: New Caledonia", "shortname": ":flag-nc:", "unicode": "1f1f3-1f1e8", "html": "🇳🇨", "category": "Flags (country-flag)", "order": "2353"}, + {"emoji": "🇳🇪", "name": "flag: Niger", "shortname": ":flag-ne:", "unicode": "1f1f3-1f1ea", "html": "🇳🇪", "category": "Flags (country-flag)", "order": "2354"}, + {"emoji": "🇳🇫", "name": "flag: Norfolk Island", "shortname": ":flag-nf:", "unicode": "1f1f3-1f1eb", "html": "🇳🇫", "category": "Flags (country-flag)", "order": "2355"}, + {"emoji": "🇳🇬", "name": "flag: Nigeria", "shortname": ":flag-ng:", "unicode": "1f1f3-1f1ec", "html": "🇳🇬", "category": "Flags (country-flag)", "order": "2356"}, + {"emoji": "🇳🇮", "name": "flag: Nicaragua", "shortname": ":flag-ni:", "unicode": "1f1f3-1f1ee", "html": "🇳🇮", "category": "Flags (country-flag)", "order": "2357"}, + {"emoji": "🇳🇱", "name": "flag: Netherlands", "shortname": ":flag-nl:", "unicode": "1f1f3-1f1f1", "html": "🇳🇱", "category": "Flags (country-flag)", "order": "2358"}, + {"emoji": "🇳🇴", "name": "flag: Norway", "shortname": ":flag-no:", "unicode": "1f1f3-1f1f4", "html": "🇳🇴", "category": "Flags (country-flag)", "order": "2359"}, + {"emoji": "🇳🇵", "name": "flag: Nepal", "shortname": ":flag-np:", "unicode": "1f1f3-1f1f5", "html": "🇳🇵", "category": "Flags (country-flag)", "order": "2360"}, + {"emoji": "🇳🇷", "name": "flag: Nauru", "shortname": ":flag-nr:", "unicode": "1f1f3-1f1f7", "html": "🇳🇷", "category": "Flags (country-flag)", "order": "2361"}, + {"emoji": "🇳🇺", "name": "flag: Niue", "shortname": ":flag-nu:", "unicode": "1f1f3-1f1fa", "html": "🇳🇺", "category": "Flags (country-flag)", "order": "2362"}, + {"emoji": "🇳🇿", "name": "flag: New Zealand", "shortname": ":flag-nz:", "unicode": "1f1f3-1f1ff", "html": "🇳🇿", "category": "Flags (country-flag)", "order": "2363"}, + {"emoji": "🇴🇲", "name": "flag: Oman", "shortname": ":flag-om:", "unicode": "1f1f4-1f1f2", "html": "🇴🇲", "category": "Flags (country-flag)", "order": "2364"}, + {"emoji": "🇵🇦", "name": "flag: Panama", "shortname": ":flag-pa:", "unicode": "1f1f5-1f1e6", "html": "🇵🇦", "category": "Flags (country-flag)", "order": "2365"}, + {"emoji": "🇵🇪", "name": "flag: Peru", "shortname": ":flag-pe:", "unicode": "1f1f5-1f1ea", "html": "🇵🇪", "category": "Flags (country-flag)", "order": "2366"}, + {"emoji": "🇵🇫", "name": "flag: French Polynesia", "shortname": ":flag-pf:", "unicode": "1f1f5-1f1eb", "html": "🇵🇫", "category": "Flags (country-flag)", "order": "2367"}, + {"emoji": "🇵🇬", "name": "flag: Papua New Guinea", "shortname": ":flag-pg:", "unicode": "1f1f5-1f1ec", "html": "🇵🇬", "category": "Flags (country-flag)", "order": "2368"}, + {"emoji": "🇵🇭", "name": "flag: Philippines", "shortname": ":flag-ph:", "unicode": "1f1f5-1f1ed", "html": "🇵🇭", "category": "Flags (country-flag)", "order": "2369"}, + {"emoji": "🇵🇰", "name": "flag: Pakistan", "shortname": ":flag-pk:", "unicode": "1f1f5-1f1f0", "html": "🇵🇰", "category": "Flags (country-flag)", "order": "2370"}, + {"emoji": "🇵🇱", "name": "flag: Poland", "shortname": ":flag-pl:", "unicode": "1f1f5-1f1f1", "html": "🇵🇱", "category": "Flags (country-flag)", "order": "2371"}, + {"emoji": "🇵🇲", "name": "flag: St. Pierre & Miquelon", "shortname": ":flag-pm:", "unicode": "1f1f5-1f1f2", "html": "🇵🇲", "category": "Flags (country-flag)", "order": "2372"}, + {"emoji": "🇵🇳", "name": "flag: Pitcairn Islands", "shortname": ":flag-pn:", "unicode": "1f1f5-1f1f3", "html": "🇵🇳", "category": "Flags (country-flag)", "order": "2373"}, + {"emoji": "🇵🇷", "name": "flag: Puerto Rico", "shortname": ":flag-pr:", "unicode": "1f1f5-1f1f7", "html": "🇵🇷", "category": "Flags (country-flag)", "order": "2374"}, + {"emoji": "🇵🇸", "name": "flag: Palestinian Territories", "shortname": ":flag-ps:", "unicode": "1f1f5-1f1f8", "html": "🇵🇸", "category": "Flags (country-flag)", "order": "2375"}, + {"emoji": "🇵🇹", "name": "flag: Portugal", "shortname": ":flag-pt:", "unicode": "1f1f5-1f1f9", "html": "🇵🇹", "category": "Flags (country-flag)", "order": "2376"}, + {"emoji": "🇵🇼", "name": "flag: Palau", "shortname": ":flag-pw:", "unicode": "1f1f5-1f1fc", "html": "🇵🇼", "category": "Flags (country-flag)", "order": "2377"}, + {"emoji": "🇵🇾", "name": "flag: Paraguay", "shortname": ":flag-py:", "unicode": "1f1f5-1f1fe", "html": "🇵🇾", "category": "Flags (country-flag)", "order": "2378"}, + {"emoji": "🇶🇦", "name": "flag: Qatar", "shortname": ":flag-qa:", "unicode": "1f1f6-1f1e6", "html": "🇶🇦", "category": "Flags (country-flag)", "order": "2379"}, + {"emoji": "🇷🇪", "name": "flag: Réunion", "shortname": ":flag-re:", "unicode": "1f1f7-1f1ea", "html": "🇷🇪", "category": "Flags (country-flag)", "order": "2380"}, + {"emoji": "🇷🇴", "name": "flag: Romania", "shortname": ":flag-ro:", "unicode": "1f1f7-1f1f4", "html": "🇷🇴", "category": "Flags (country-flag)", "order": "2381"}, + {"emoji": "🇷🇸", "name": "flag: Serbia", "shortname": ":flag-rs:", "unicode": "1f1f7-1f1f8", "html": "🇷🇸", "category": "Flags (country-flag)", "order": "2382"}, + {"emoji": "🇷🇺", "name": "flag: Russia", "shortname": ":flag-ru:", "unicode": "1f1f7-1f1fa", "html": "🇷🇺", "category": "Flags (country-flag)", "order": "2383"}, + {"emoji": "🇷🇼", "name": "flag: Rwanda", "shortname": ":flag-rw:", "unicode": "1f1f7-1f1fc", "html": "🇷🇼", "category": "Flags (country-flag)", "order": "2384"}, + {"emoji": "🇸🇦", "name": "flag: Saudi Arabia", "shortname": ":flag-sa:", "unicode": "1f1f8-1f1e6", "html": "🇸🇦", "category": "Flags (country-flag)", "order": "2385"}, + {"emoji": "🇸🇧", "name": "flag: Solomon Islands", "shortname": ":flag-sb:", "unicode": "1f1f8-1f1e7", "html": "🇸🇧", "category": "Flags (country-flag)", "order": "2386"}, + {"emoji": "🇸🇨", "name": "flag: Seychelles", "shortname": ":flag-sc:", "unicode": "1f1f8-1f1e8", "html": "🇸🇨", "category": "Flags (country-flag)", "order": "2387"}, + {"emoji": "🇸🇩", "name": "flag: Sudan", "shortname": ":flag-sd:", "unicode": "1f1f8-1f1e9", "html": "🇸🇩", "category": "Flags (country-flag)", "order": "2388"}, + {"emoji": "🇸🇪", "name": "flag: Sweden", "shortname": ":flag-se:", "unicode": "1f1f8-1f1ea", "html": "🇸🇪", "category": "Flags (country-flag)", "order": "2389"}, + {"emoji": "🇸🇬", "name": "flag: Singapore", "shortname": ":flag-sg:", "unicode": "1f1f8-1f1ec", "html": "🇸🇬", "category": "Flags (country-flag)", "order": "2390"}, + {"emoji": "🇸🇭", "name": "flag: St. Helena", "shortname": ":flag-sh:", "unicode": "1f1f8-1f1ed", "html": "🇸🇭", "category": "Flags (country-flag)", "order": "2391"}, + {"emoji": "🇸🇮", "name": "flag: Slovenia", "shortname": ":flag-si:", "unicode": "1f1f8-1f1ee", "html": "🇸🇮", "category": "Flags (country-flag)", "order": "2392"}, + {"emoji": "🇸🇯", "name": "flag: Svalbard & Jan Mayen", "shortname": ":flag-sj:", "unicode": "1f1f8-1f1ef", "html": "🇸🇯", "category": "Flags (country-flag)", "order": "2393"}, + {"emoji": "🇸🇰", "name": "flag: Slovakia", "shortname": ":flag-sk:", "unicode": "1f1f8-1f1f0", "html": "🇸🇰", "category": "Flags (country-flag)", "order": "2394"}, + {"emoji": "🇸🇱", "name": "flag: Sierra Leone", "shortname": ":flag-sl:", "unicode": "1f1f8-1f1f1", "html": "🇸🇱", "category": "Flags (country-flag)", "order": "2395"}, + {"emoji": "🇸🇲", "name": "flag: San Marino", "shortname": ":flag-sm:", "unicode": "1f1f8-1f1f2", "html": "🇸🇲", "category": "Flags (country-flag)", "order": "2396"}, + {"emoji": "🇸🇳", "name": "flag: Senegal", "shortname": ":flag-sn:", "unicode": "1f1f8-1f1f3", "html": "🇸🇳", "category": "Flags (country-flag)", "order": "2397"}, + {"emoji": "🇸🇴", "name": "flag: Somalia", "shortname": ":flag-so:", "unicode": "1f1f8-1f1f4", "html": "🇸🇴", "category": "Flags (country-flag)", "order": "2398"}, + {"emoji": "🇸🇷", "name": "flag: Suriname", "shortname": ":flag-sr:", "unicode": "1f1f8-1f1f7", "html": "🇸🇷", "category": "Flags (country-flag)", "order": "2399"}, + {"emoji": "🇸🇸", "name": "flag: South Sudan", "shortname": ":flag-ss:", "unicode": "1f1f8-1f1f8", "html": "🇸🇸", "category": "Flags (country-flag)", "order": "2400"}, + {"emoji": "🇸🇹", "name": "flag: São Tomé & Príncipe", "shortname": ":flag-st:", "unicode": "1f1f8-1f1f9", "html": "🇸🇹", "category": "Flags (country-flag)", "order": "2401"}, + {"emoji": "🇸🇻", "name": "flag: El Salvador", "shortname": ":flag-sv:", "unicode": "1f1f8-1f1fb", "html": "🇸🇻", "category": "Flags (country-flag)", "order": "2402"}, + {"emoji": "🇸🇽", "name": "flag: Sint Maarten", "shortname": ":flag-sx:", "unicode": "1f1f8-1f1fd", "html": "🇸🇽", "category": "Flags (country-flag)", "order": "2403"}, + {"emoji": "🇸🇾", "name": "flag: Syria", "shortname": ":flag-sy:", "unicode": "1f1f8-1f1fe", "html": "🇸🇾", "category": "Flags (country-flag)", "order": "2404"}, + {"emoji": "🇸🇿", "name": "flag: Eswatini", "shortname": ":flag-sz:", "unicode": "1f1f8-1f1ff", "html": "🇸🇿", "category": "Flags (country-flag)", "order": "2405"}, + {"emoji": "🇹🇦", "name": "flag: Tristan da Cunha", "shortname": ":flag-ta:", "unicode": "1f1f9-1f1e6", "html": "🇹🇦", "category": "Flags (country-flag)", "order": "2406"}, + {"emoji": "🇹🇨", "name": "flag: Turks & Caicos Islands", "shortname": ":flag-tc:", "unicode": "1f1f9-1f1e8", "html": "🇹🇨", "category": "Flags (country-flag)", "order": "2407"}, + {"emoji": "🇹🇩", "name": "flag: Chad", "shortname": ":flag-td:", "unicode": "1f1f9-1f1e9", "html": "🇹🇩", "category": "Flags (country-flag)", "order": "2408"}, + {"emoji": "🇹🇫", "name": "flag: French Southern Territories", "shortname": ":flag-tf:", "unicode": "1f1f9-1f1eb", "html": "🇹🇫", "category": "Flags (country-flag)", "order": "2409"}, + {"emoji": "🇹🇬", "name": "flag: Togo", "shortname": ":flag-tg:", "unicode": "1f1f9-1f1ec", "html": "🇹🇬", "category": "Flags (country-flag)", "order": "2410"}, + {"emoji": "🇹🇭", "name": "flag: Thailand", "shortname": ":flag-th:", "unicode": "1f1f9-1f1ed", "html": "🇹🇭", "category": "Flags (country-flag)", "order": "2411"}, + {"emoji": "🇹🇯", "name": "flag: Tajikistan", "shortname": ":flag-tj:", "unicode": "1f1f9-1f1ef", "html": "🇹🇯", "category": "Flags (country-flag)", "order": "2412"}, + {"emoji": "🇹🇰", "name": "flag: Tokelau", "shortname": ":flag-tk:", "unicode": "1f1f9-1f1f0", "html": "🇹🇰", "category": "Flags (country-flag)", "order": "2413"}, + {"emoji": "🇹🇱", "name": "flag: Timor-Leste", "shortname": ":flag-tl:", "unicode": "1f1f9-1f1f1", "html": "🇹🇱", "category": "Flags (country-flag)", "order": "2414"}, + {"emoji": "🇹🇲", "name": "flag: Turkmenistan", "shortname": ":flag-tm:", "unicode": "1f1f9-1f1f2", "html": "🇹🇲", "category": "Flags (country-flag)", "order": "2415"}, + {"emoji": "🇹🇳", "name": "flag: Tunisia", "shortname": ":flag-tn:", "unicode": "1f1f9-1f1f3", "html": "🇹🇳", "category": "Flags (country-flag)", "order": "2416"}, + {"emoji": "🇹🇴", "name": "flag: Tonga", "shortname": ":flag-to:", "unicode": "1f1f9-1f1f4", "html": "🇹🇴", "category": "Flags (country-flag)", "order": "2417"}, + {"emoji": "🇹🇷", "name": "flag: Turkey", "shortname": ":flag-tr:", "unicode": "1f1f9-1f1f7", "html": "🇹🇷", "category": "Flags (country-flag)", "order": "2418"}, + {"emoji": "🇹🇹", "name": "flag: Trinidad & Tobago", "shortname": ":flag-tt:", "unicode": "1f1f9-1f1f9", "html": "🇹🇹", "category": "Flags (country-flag)", "order": "2419"}, + {"emoji": "🇹🇻", "name": "flag: Tuvalu", "shortname": ":flag-tv:", "unicode": "1f1f9-1f1fb", "html": "🇹🇻", "category": "Flags (country-flag)", "order": "2420"}, + {"emoji": "🇹🇼", "name": "flag: Taiwan", "shortname": ":flag-tw:", "unicode": "1f1f9-1f1fc", "html": "🇹🇼", "category": "Flags (country-flag)", "order": "2421"}, + {"emoji": "🇹🇿", "name": "flag: Tanzania", "shortname": ":flag-tz:", "unicode": "1f1f9-1f1ff", "html": "🇹🇿", "category": "Flags (country-flag)", "order": "2422"}, + {"emoji": "🇺🇦", "name": "flag: Ukraine", "shortname": ":flag-ua:", "unicode": "1f1fa-1f1e6", "html": "🇺🇦", "category": "Flags (country-flag)", "order": "2423"}, + {"emoji": "🇺🇬", "name": "flag: Uganda", "shortname": ":flag-ug:", "unicode": "1f1fa-1f1ec", "html": "🇺🇬", "category": "Flags (country-flag)", "order": "2424"}, + {"emoji": "🇺🇲", "name": "flag: U.S. Outlying Islands", "shortname": ":flag-um:", "unicode": "1f1fa-1f1f2", "html": "🇺🇲", "category": "Flags (country-flag)", "order": "2425"}, + {"emoji": "🇺🇸", "name": "flag: United States", "shortname": ":flag-us:", "unicode": "1f1fa-1f1f8", "html": "🇺🇸", "category": "Flags (country-flag)", "order": "2427"}, + {"emoji": "🇺🇾", "name": "flag: Uruguay", "shortname": ":flag-uy:", "unicode": "1f1fa-1f1fe", "html": "🇺🇾", "category": "Flags (country-flag)", "order": "2428"}, + {"emoji": "🇺🇿", "name": "flag: Uzbekistan", "shortname": ":flag-uz:", "unicode": "1f1fa-1f1ff", "html": "🇺🇿", "category": "Flags (country-flag)", "order": "2429"}, + {"emoji": "🇻🇦", "name": "flag: Vatican City", "shortname": ":flag-va:", "unicode": "1f1fb-1f1e6", "html": "🇻🇦", "category": "Flags (country-flag)", "order": "2430"}, + {"emoji": "🇻🇨", "name": "flag: St. Vincent & Grenadines", "shortname": ":flag-vc:", "unicode": "1f1fb-1f1e8", "html": "🇻🇨", "category": "Flags (country-flag)", "order": "2431"}, + {"emoji": "🇻🇪", "name": "flag: Venezuela", "shortname": ":flag-ve:", "unicode": "1f1fb-1f1ea", "html": "🇻🇪", "category": "Flags (country-flag)", "order": "2432"}, + {"emoji": "🇻🇬", "name": "flag: British Virgin Islands", "shortname": ":flag-vg:", "unicode": "1f1fb-1f1ec", "html": "🇻🇬", "category": "Flags (country-flag)", "order": "2433"}, + {"emoji": "🇻🇮", "name": "flag: U.S. Virgin Islands", "shortname": ":flag-vi:", "unicode": "1f1fb-1f1ee", "html": "🇻🇮", "category": "Flags (country-flag)", "order": "2434"}, + {"emoji": "🇻🇳", "name": "flag: Vietnam", "shortname": ":flag-vn:", "unicode": "1f1fb-1f1f3", "html": "🇻🇳", "category": "Flags (country-flag)", "order": "2435"}, + {"emoji": "🇻🇺", "name": "flag: Vanuatu", "shortname": ":flag_vu:", "unicode": "1f1fb-1f1fa", "html": "🇻🇺", "category": "Flags (country-flag)", "order": "2436"}, + {"emoji": "🇼🇫", "name": "flag: Wallis & Futuna", "shortname": ":flag_wf:", "unicode": "1f1fc-1f1eb", "html": "🇼🇫", "category": "Flags (country-flag)", "order": "2437"}, + {"emoji": "🇼🇸", "name": "flag: Samoa", "shortname": ":flag_ws:", "unicode": "1f1fc-1f1f8", "html": "🇼🇸", "category": "Flags (country-flag)", "order": "2438"}, + {"emoji": "🇽🇰", "name": "flag: Kosovo", "shortname": ":flag_xk:", "unicode": "1f1fd-1f1f0", "html": "🇽🇰", "category": "Flags (country-flag)", "order": "2439"}, + {"emoji": "🇾🇪", "name": "flag: Yemen", "shortname": ":flag_ye:", "unicode": "1f1fe-1f1ea", "html": "🇾🇪", "category": "Flags (country-flag)", "order": "2440"}, + {"emoji": "🇾🇹", "name": "flag: Mayotte", "shortname": ":flag_yt:", "unicode": "1f1fe-1f1f9", "html": "🇾🇹", "category": "Flags (country-flag)", "order": "2441"}, + {"emoji": "🇿🇦", "name": "flag: South Africa", "shortname": ":flag_za:", "unicode": "1f1ff-1f1e6", "html": "🇿🇦", "category": "Flags (country-flag)", "order": "2442"}, + {"emoji": "🇿🇲", "name": "flag: Zambia", "shortname": ":flag_zm:", "unicode": "1f1ff-1f1f2", "html": "🇿🇲", "category": "Flags (country-flag)", "order": "2443"}, + {"emoji": "🇿🇼", "name": "flag: Zimbabwe", "shortname": ":flag_zw:", "unicode": "1f1ff-1f1fc", "html": "🇿🇼", "category": "Flags (country-flag)", "order": "2444"}, + {"emoji": "🖤", "name": "black heart", "shortname": ":black_heart:", "unicode": "1f5a4", "html": "🖤", "category": "Smileys & Emotion (emotion)", "order": "1296"}, + {"emoji": "🗨", "name": "left speech bubble", "shortname": ":speech_left:", "unicode": "1f5e8", "html": "🗨", "category": "Smileys & Emotion (emotion)", "order": "1310"}, + {"emoji": "🥚", "name": "egg", "shortname": ":egg:", "unicode": "1f95a", "html": "🥚", "category": "Food & Drink (food-prepared)", "order": "1489"}, + {"emoji": "🛑", "name": "stop sign", "shortname": ":octagonal_sign:", "unicode": "1f6d1", "html": "🛑", "category": "Travel & Places (transport-ground)", "order": "1641"}, + {"emoji": "♠", "name": "spade suit", "shortname": ":spades:", "unicode": "2660", "html": "♠", "category": "Activities (game)", "order": "1807"}, + {"emoji": "♥", "name": "heart suit", "shortname": ":hearts:", "unicode": "2665", "html": "♥", "category": "Activities (game)", "order": "1808"}, + {"emoji": "♦", "name": "diamond suit", "shortname": ":diamonds:", "unicode": "2666", "html": "♦", "category": "Activities (game)", "order": "1809"}, + {"emoji": "♣", "name": "club suit", "shortname": ":clubs:", "unicode": "2663", "html": "♣", "category": "Activities (game)", "order": "1810"}, + {"emoji": "🥁", "name": "drum", "shortname": ":drum:", "unicode": "1f941", "html": "🥁", "category": "Objects (musical-instrument)", "order": "1837"}, + {"emoji": "↔", "name": "left-right arrow", "shortname": ":left_right_arrow:", "unicode": "2194", "html": "↔", "category": "Symbols (arrow)", "order": "2011"}, + {"emoji": "©", "name": "copyright", "shortname": ":copyright:", "unicode": "00a9", "html": "©", "category": "Symbols (other-symbol)", "order": "2103"}, + {"emoji": "®", "name": "registered", "shortname": ":registered:", "unicode": "00ae", "html": "®", "category": "Symbols (other-symbol)", "order": "2104"}, + {"emoji": "™", "name": "trade mark", "shortname": ":tm:", "unicode": "2122", "html": "™", "category": "Symbols (other-symbol)", "order": "2105"}, + {"emoji": "0️⃣", "name": "keycap: 0", "shortname": ":0:", "unicode": "0030 FE0F 20E3", "html": "0", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "1️⃣", "name": "keycap: 1", "shortname": ":1:", "unicode": "0031 FE0F 20E3", "html": "1", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "2️⃣", "name": "keycap: 2", "shortname": ":2:", "unicode": "0032 FE0F 20E3", "html": "2", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "3️⃣", "name": "keycap: 3", "shortname": ":3:", "unicode": "0033 FE0F 20E3", "html": "3", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "4️⃣", "name": "keycap: 4", "shortname": ":4:", "unicode": "0034 FE0F 20E3", "html": "4", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "5️⃣", "name": "keycap: 5", "shortname": ":5:", "unicode": "0035 FE0F 20E3", "html": "5", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "6️⃣", "name": "keycap: 6", "shortname": ":6:", "unicode": "0036 FE0F 20E3", "html": "6", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "7️⃣", "name": "keycap: 7", "shortname": ":7:", "unicode": "0037 FE0F 20E3", "html": "7", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "8️⃣", "name": "keycap: 8", "shortname": ":8:", "unicode": "0038 FE0F 20E3", "html": "8", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "9️⃣", "name": "keycap: 9", "shortname": ":9:", "unicode": "0039 FE0F 20E3", "html": "9", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "#⃣", "name": "keycap: #", "shortname": ":hash:", "unicode": "0023-20e3", "html": "#⃣", "category": "Symbols (keycap)", "order": "2106"}, + {"emoji": "*⃣", "name": "keycap: #", "shortname": ":asterisk:", "unicode": "002a-20e3", "html": "*⃣", "category": "Symbols (keycap)", "order": "2107"}, + {"emoji": "0⃣", "name": "keycap: 0", "shortname": ":zero:", "unicode": "0030-20e3", "html": "0⃣", "category": "Symbols (keycap)", "order": "2108"}, + {"emoji": "1⃣", "name": "keycap: 1", "shortname": ":one:", "unicode": "0031-20e3", "html": "1⃣", "category": "Symbols (keycap)", "order": "2109"}, + {"emoji": "2⃣", "name": "keycap: 2", "shortname": ":two:", "unicode": "0032-20e3", "html": "2⃣", "category": "Symbols (keycap)", "order": "2110"}, + {"emoji": "3⃣", "name": "keycap: 3", "shortname": ":three:", "unicode": "0033-20e3", "html": "3⃣", "category": "Symbols (keycap)", "order": "2111"}, + {"emoji": "4⃣", "name": "keycap: 4", "shortname": ":four:", "unicode": "0034-20e3", "html": "4⃣", "category": "Symbols (keycap)", "order": "2112"}, + {"emoji": "5⃣", "name": "keycap: 5", "shortname": ":five:", "unicode": "0035-20e3", "html": "5⃣", "category": "Symbols (keycap)", "order": "2113"}, + {"emoji": "6⃣", "name": "keycap: 6", "shortname": ":six:", "unicode": "0036-20e3", "html": "6⃣", "category": "Symbols (keycap)", "order": "2114"}, + {"emoji": "7⃣", "name": "keycap: 7", "shortname": ":seven:", "unicode": "0037-20e3", "html": "7⃣", "category": "Symbols (keycap)", "order": "2115"}, + {"emoji": "8⃣", "name": "keycap: 8", "shortname": ":eight:", "unicode": "0038-20e3", "html": "8⃣", "category": "Symbols (keycap)", "order": "2116"}, + {"emoji": "9⃣", "name": "keycap: 9", "shortname": ":nine:", "unicode": "0039-20e3", "html": "9⃣", "category": "Symbols (keycap)", "order": "2117"}, + {"emoji": "🤣", "name": "rolling on the floor laughing", "shortname": ":rolling_on_the_floor_laughing:", "unicode": "1F923", "html": "🤣", "category": "Smileys & Emotion (face-smiling)", "order": ""}, + {"emoji": "🥰", "name": "smiling face with hearts", "shortname": ":smiling_face_with_hearts:", "unicode": "1F970", "html": "🥰", "category": "Smileys & Emotion (face-affection)", "order": ""}, + {"emoji": "🤩", "name": "star-struck", "shortname": ":starstruck:", "unicode": "1F929", "html": "🤩", "category": "Smileys & Emotion (face-affection)", "order": ""}, + {"emoji": "☺", "name": "smiling face", "shortname": ":smiling_face:", "unicode": "263A", "html": "☺", "category": "Smileys & Emotion (face-affection)", "order": ""}, + {"emoji": "🤪", "name": "zany face", "shortname": ":zany_face:", "unicode": "1F92A", "html": "🤪", "category": "Smileys & Emotion (face-tongue)", "order": ""}, + {"emoji": "🤭", "name": "face with hand over mouth", "shortname": ":face_with_hand_over_mouth:", "unicode": "1F92D", "html": "🤭", "category": "Smileys & Emotion (face-hand)", "order": ""}, + {"emoji": "🤫", "name": "shushing face", "shortname": ":shushing_face:", "unicode": "1F92B", "html": "🤫", "category": "Smileys & Emotion (face-hand)", "order": ""}, + {"emoji": "🤨", "name": "face with raised eyebrow", "shortname": ":face_with_raised_eyebrow:", "unicode": "1F928", "html": "🤨", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": ""}, + {"emoji": "🤥", "name": "lying face", "shortname": ":lying_face:", "unicode": "1F925", "html": "🤥", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": ""}, + {"emoji": "🤤", "name": "drooling face", "shortname": ":drooling_face:", "unicode": "1F924", "html": "🤤", "category": "Smileys & Emotion (face-sleepy)", "order": ""}, + {"emoji": "🤢", "name": "nauseated face", "shortname": ":nauseated_face:", "unicode": "1F922", "html": "🤢", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤮", "name": "face vomiting", "shortname": ":face_vomiting:", "unicode": "1F92E", "html": "🤮", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤧", "name": "sneezing face", "shortname": ":sneezing_face:", "unicode": "1F927", "html": "🤧", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🥵", "name": "hot face", "shortname": ":hot_face:", "unicode": "1F975", "html": "🥵", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🥶", "name": "cold face", "shortname": ":cold_face:", "unicode": "1F976", "html": "🥶", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🥴", "name": "woozy face", "shortname": ":woozy_face:", "unicode": "1F974", "html": "🥴", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤯", "name": "exploding head", "shortname": ":exploding_head:", "unicode": "1F92F", "html": "🤯", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤠", "name": "cowboy hat face", "shortname": ":cowboy_hat_face:", "unicode": "1F920", "html": "🤠", "category": "Smileys & Emotion (face-hat)", "order": ""}, + {"emoji": "🥳", "name": "partying face", "shortname": ":partying_face:", "unicode": "1F973", "html": "🥳", "category": "Smileys & Emotion (face-hat)", "order": ""}, + {"emoji": "🧐", "name": "face with monocle", "shortname": ":face_with_monocle:", "unicode": "1F9D0", "html": "🧐", "category": "Smileys & Emotion (face-glasses)", "order": ""}, + {"emoji": "☹️", "name": "frowning face", "shortname": ":frowning_face:", "unicode": "2639 FE0F", "html": "☹️", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "🥺", "name": "pleading face", "shortname": ":pleading_face:", "unicode": "1F97A", "html": "🥺", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "🥱", "name": "yawning face", "shortname": ":yawning_face:", "unicode": "1F971", "html": "🥱", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "🤬", "name": "face with symbols on mouth", "shortname": ":face_with_symbols_on_mouth:", "unicode": "1F92C", "html": "🤬", "category": "Smileys & Emotion (face-negative)", "order": ""}, + {"emoji": "☠️", "name": "skull and crossbones", "shortname": ":skull_and_crossbones:", "unicode": "2620 FE0F", "html": "☠️", "category": "Smileys & Emotion (face-negative)", "order": ""}, + {"emoji": "🤡", "name": "clown face", "shortname": ":clown_face:", "unicode": "1F921", "html": "🤡", "category": "Smileys & Emotion (face-costume)", "order": ""}, + {"emoji": "❣", "name": "heart exclamation", "shortname": ":heart_exclamation:", "unicode": "2763", "html": "❣", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "❤", "name": "red heart", "shortname": ":red_heart:", "unicode": "2764", "html": "❤", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🧡", "name": "orange heart", "shortname": ":orange_heart:", "unicode": "1F9E1", "html": "🧡", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🤎", "name": "brown heart", "shortname": ":brown_heart:", "unicode": "1F90E", "html": "🤎", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🤍", "name": "white heart", "shortname": ":white_heart:", "unicode": "1F90D", "html": "🤍", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🕳️", "name": "hole", "shortname": ":hole:", "unicode": "1F573 FE0F", "html": "🕳️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "👁️‍🗨️", "name": "eye in speech bubble", "shortname": ":eye_in_speech_bubble:", "unicode": "1F441 FE0F 200D 1F5E8 FE0F", "html": "👁️‍🗨️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "👁‍🗨️", "name": "eye in speech bubble", "shortname": ":eye_in_speech_bubble:", "unicode": "1F441 200D 1F5E8 FE0F", "html": "👁‍🗨️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "👁️‍🗨", "name": "eye in speech bubble", "shortname": ":eye_in_speech_bubble:", "unicode": "1F441 FE0F 200D 1F5E8", "html": "👁️‍🗨", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🗨️", "name": "left speech bubble", "shortname": ":left_speech_bubble:", "unicode": "1F5E8 FE0F", "html": "🗨️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🗯️", "name": "right anger bubble", "shortname": ":right_anger_bubble:", "unicode": "1F5EF FE0F", "html": "🗯️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🤚", "name": "raised back of hand", "shortname": ":raised_back_of_hand:", "unicode": "1F91A", "html": "🤚", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏻", "name": "raised back of hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91A 1F3FB", "html": "🤚🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏼", "name": "raised back of hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91A 1F3FC", "html": "🤚🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏽", "name": "raised back of hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91A 1F3FD", "html": "🤚🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏾", "name": "raised back of hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91A 1F3FE", "html": "🤚🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏿", "name": "raised back of hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91A 1F3FF", "html": "🤚🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐️", "name": "hand with fingers splayed", "shortname": ":hand_with_fingers_splayed:", "unicode": "1F590 FE0F", "html": "🖐️", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤏", "name": "pinching hand", "shortname": ":pinching_hand:", "unicode": "1F90F", "html": "🤏", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏻", "name": "pinching hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F90F 1F3FB", "html": "🤏🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏼", "name": "pinching hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F90F 1F3FC", "html": "🤏🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏽", "name": "pinching hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F90F 1F3FD", "html": "🤏🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏾", "name": "pinching hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F90F 1F3FE", "html": "🤏🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏿", "name": "pinching hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F90F 1F3FF", "html": "🤏🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌", "name": "victory hand", "shortname": ":victory_hand:", "unicode": "270C", "html": "✌", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞", "name": "crossed fingers", "shortname": ":crossed_fingers:", "unicode": "1F91E", "html": "🤞", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏻", "name": "crossed fingers: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91E 1F3FB", "html": "🤞🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏼", "name": "crossed fingers: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91E 1F3FC", "html": "🤞🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏽", "name": "crossed fingers: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91E 1F3FD", "html": "🤞🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏾", "name": "crossed fingers: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91E 1F3FE", "html": "🤞🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏿", "name": "crossed fingers: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91E 1F3FF", "html": "🤞🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟", "name": "love-you gesture", "shortname": ":loveyou_gesture:", "unicode": "1F91F", "html": "🤟", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏻", "name": "love-you gesture: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91F 1F3FB", "html": "🤟🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏼", "name": "love-you gesture: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91F 1F3FC", "html": "🤟🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏽", "name": "love-you gesture: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91F 1F3FD", "html": "🤟🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏾", "name": "love-you gesture: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91F 1F3FE", "html": "🤟🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏿", "name": "love-you gesture: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91F 1F3FF", "html": "🤟🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙", "name": "call me hand", "shortname": ":call_me_hand:", "unicode": "1F919", "html": "🤙", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏻", "name": "call me hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F919 1F3FB", "html": "🤙🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏼", "name": "call me hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F919 1F3FC", "html": "🤙🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏽", "name": "call me hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F919 1F3FD", "html": "🤙🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏾", "name": "call me hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F919 1F3FE", "html": "🤙🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏿", "name": "call me hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F919 1F3FF", "html": "🤙🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "☝", "name": "index pointing up", "shortname": ":index_pointing_up:", "unicode": "261D", "html": "☝", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🤛", "name": "left-facing fist", "shortname": ":leftfacing_fist:", "unicode": "1F91B", "html": "🤛", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏻", "name": "left-facing fist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91B 1F3FB", "html": "🤛🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏼", "name": "left-facing fist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91B 1F3FC", "html": "🤛🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏽", "name": "left-facing fist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91B 1F3FD", "html": "🤛🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏾", "name": "left-facing fist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91B 1F3FE", "html": "🤛🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏿", "name": "left-facing fist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91B 1F3FF", "html": "🤛🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜", "name": "right-facing fist", "shortname": ":rightfacing_fist:", "unicode": "1F91C", "html": "🤜", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏻", "name": "right-facing fist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91C 1F3FB", "html": "🤜🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏼", "name": "right-facing fist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91C 1F3FC", "html": "🤜🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏽", "name": "right-facing fist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91C 1F3FD", "html": "🤜🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏾", "name": "right-facing fist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91C 1F3FE", "html": "🤜🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏿", "name": "right-facing fist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91C 1F3FF", "html": "🤜🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤲", "name": "palms up together", "shortname": ":palms_up_together:", "unicode": "1F932", "html": "🤲", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏻", "name": "palms up together: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F932 1F3FB", "html": "🤲🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏼", "name": "palms up together: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F932 1F3FC", "html": "🤲🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏽", "name": "palms up together: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F932 1F3FD", "html": "🤲🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏾", "name": "palms up together: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F932 1F3FE", "html": "🤲🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏿", "name": "palms up together: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F932 1F3FF", "html": "🤲🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤝", "name": "handshake", "shortname": ":handshake:", "unicode": "1F91D", "html": "🤝", "category": "People & Body (hands)", "order": ""}, + {"emoji": "✍️", "name": "writing hand", "shortname": ":writing_hand:", "unicode": "270D FE0F", "html": "✍️", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳", "name": "selfie", "shortname": ":selfie:", "unicode": "1F933", "html": "🤳", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏻", "name": "selfie: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F933 1F3FB", "html": "🤳🏻", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏼", "name": "selfie: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F933 1F3FC", "html": "🤳🏼", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏽", "name": "selfie: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F933 1F3FD", "html": "🤳🏽", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏾", "name": "selfie: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F933 1F3FE", "html": "🤳🏾", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏿", "name": "selfie: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F933 1F3FF", "html": "🤳🏿", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🦾", "name": "mechanical arm", "shortname": ":mechanical_arm:", "unicode": "1F9BE", "html": "🦾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦿", "name": "mechanical leg", "shortname": ":mechanical_leg:", "unicode": "1F9BF", "html": "🦿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵", "name": "leg", "shortname": ":leg:", "unicode": "1F9B5", "html": "🦵", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏻", "name": "leg: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B5 1F3FB", "html": "🦵🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏼", "name": "leg: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B5 1F3FC", "html": "🦵🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏽", "name": "leg: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B5 1F3FD", "html": "🦵🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏾", "name": "leg: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B5 1F3FE", "html": "🦵🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏿", "name": "leg: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B5 1F3FF", "html": "🦵🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶", "name": "foot", "shortname": ":foot:", "unicode": "1F9B6", "html": "🦶", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏻", "name": "foot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B6 1F3FB", "html": "🦶🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏼", "name": "foot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B6 1F3FC", "html": "🦶🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏽", "name": "foot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B6 1F3FD", "html": "🦶🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏾", "name": "foot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B6 1F3FE", "html": "🦶🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏿", "name": "foot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B6 1F3FF", "html": "🦶🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻", "name": "ear with hearing aid", "shortname": ":ear_with_hearing_aid:", "unicode": "1F9BB", "html": "🦻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏻", "name": "ear with hearing aid: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9BB 1F3FB", "html": "🦻🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏼", "name": "ear with hearing aid: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9BB 1F3FC", "html": "🦻🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏽", "name": "ear with hearing aid: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9BB 1F3FD", "html": "🦻🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏾", "name": "ear with hearing aid: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9BB 1F3FE", "html": "🦻🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏿", "name": "ear with hearing aid: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9BB 1F3FF", "html": "🦻🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🧠", "name": "brain", "shortname": ":brain:", "unicode": "1F9E0", "html": "🧠", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦷", "name": "tooth", "shortname": ":tooth:", "unicode": "1F9B7", "html": "🦷", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦴", "name": "bone", "shortname": ":bone:", "unicode": "1F9B4", "html": "🦴", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👁️", "name": "eye", "shortname": ":eye:", "unicode": "1F441 FE0F", "html": "👁️", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🧒", "name": "child", "shortname": ":child:", "unicode": "1F9D2", "html": "🧒", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏻", "name": "child: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D2 1F3FB", "html": "🧒🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏼", "name": "child: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D2 1F3FC", "html": "🧒🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏽", "name": "child: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D2 1F3FD", "html": "🧒🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏾", "name": "child: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D2 1F3FE", "html": "🧒🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏿", "name": "child: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D2 1F3FF", "html": "🧒🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑", "name": "person", "shortname": ":person:", "unicode": "1F9D1", "html": "🧑", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻", "name": "person: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB", "html": "🧑🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼", "name": "person: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC", "html": "🧑🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽", "name": "person: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD", "html": "🧑🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾", "name": "person: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE", "html": "🧑🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿", "name": "person: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF", "html": "🧑🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔", "name": "man: beard", "shortname": ":beard:", "unicode": "1F9D4", "html": "🧔", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏻", "name": "man: light skin tone, beard", "shortname": ":light_skin_tone_beard:", "unicode": "1F9D4 1F3FB", "html": "🧔🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏼", "name": "man: medium-light skin tone, beard", "shortname": ":mediumlight_skin_tone_beard:", "unicode": "1F9D4 1F3FC", "html": "🧔🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏽", "name": "man: medium skin tone, beard", "shortname": ":medium_skin_tone_beard:", "unicode": "1F9D4 1F3FD", "html": "🧔🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏾", "name": "man: medium-dark skin tone, beard", "shortname": ":mediumdark_skin_tone_beard:", "unicode": "1F9D4 1F3FE", "html": "🧔🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏿", "name": "man: dark skin tone, beard", "shortname": ":dark_skin_tone_beard:", "unicode": "1F9D4 1F3FF", "html": "🧔🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨‍🦰", "name": "man: red hair", "shortname": ":red_hair:", "unicode": "1F468 200D 1F9B0", "html": "👨‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻‍🦰", "name": "man: light skin tone, red hair", "shortname": ":light_skin_tone_red_hair:", "unicode": "1F468 1F3FB 200D 1F9B0", "html": "👨🏻‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼‍🦰", "name": "man: medium-light skin tone, red hair", "shortname": ":mediumlight_skin_tone_red_hair:", "unicode": "1F468 1F3FC 200D 1F9B0", "html": "👨🏼‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽‍🦰", "name": "man: medium skin tone, red hair", "shortname": ":medium_skin_tone_red_hair:", "unicode": "1F468 1F3FD 200D 1F9B0", "html": "👨🏽‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾‍🦰", "name": "man: medium-dark skin tone, red hair", "shortname": ":mediumdark_skin_tone_red_hair:", "unicode": "1F468 1F3FE 200D 1F9B0", "html": "👨🏾‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏿‍🦰", "name": "man: dark skin tone, red hair", "shortname": ":dark_skin_tone_red_hair:", "unicode": "1F468 1F3FF 200D 1F9B0", "html": "👨🏿‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨‍🦱", "name": "man: curly hair", "shortname": ":curly_hair:", "unicode": "1F468 200D 1F9B1", "html": "👨‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻‍🦱", "name": "man: light skin tone, curly hair", "shortname": ":light_skin_tone_curly_hair:", "unicode": "1F468 1F3FB 200D 1F9B1", "html": "👨🏻‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼‍🦱", "name": "man: medium-light skin tone, curly hair", "shortname": ":mediumlight_skin_tone_curly_hair:", "unicode": "1F468 1F3FC 200D 1F9B1", "html": "👨🏼‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽‍🦱", "name": "man: medium skin tone, curly hair", "shortname": ":medium_skin_tone_curly_hair:", "unicode": "1F468 1F3FD 200D 1F9B1", "html": "👨🏽‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾‍🦱", "name": "man: medium-dark skin tone, curly hair", "shortname": ":mediumdark_skin_tone_curly_hair:", "unicode": "1F468 1F3FE 200D 1F9B1", "html": "👨🏾‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏿‍🦱", "name": "man: dark skin tone, curly hair", "shortname": ":dark_skin_tone_curly_hair:", "unicode": "1F468 1F3FF 200D 1F9B1", "html": "👨🏿‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨‍🦳", "name": "man: white hair", "shortname": ":white_hair:", "unicode": "1F468 200D 1F9B3", "html": "👨‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻‍🦳", "name": "man: light skin tone, white hair", "shortname": ":light_skin_tone_white_hair:", "unicode": "1F468 1F3FB 200D 1F9B3", "html": "👨🏻‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼‍🦳", "name": "man: medium-light skin tone, white hair", "shortname": ":mediumlight_skin_tone_white_hair:", "unicode": "1F468 1F3FC 200D 1F9B3", "html": "👨🏼‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽‍🦳", "name": "man: medium skin tone, white hair", "shortname": ":medium_skin_tone_white_hair:", "unicode": "1F468 1F3FD 200D 1F9B3", "html": "👨🏽‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾‍🦳", "name": "man: medium-dark skin tone, white hair", "shortname": ":mediumdark_skin_tone_white_hair:", "unicode": "1F468 1F3FE 200D 1F9B3", "html": "👨🏾‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏿‍🦳", "name": "man: dark skin tone, white hair", "shortname": ":dark_skin_tone_white_hair:", "unicode": "1F468 1F3FF 200D 1F9B3", "html": "👨🏿‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨‍🦲", "name": "man: bald", "shortname": ":bald:", "unicode": "1F468 200D 1F9B2", "html": "👨‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻‍🦲", "name": "man: light skin tone, bald", "shortname": ":light_skin_tone_bald:", "unicode": "1F468 1F3FB 200D 1F9B2", "html": "👨🏻‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼‍🦲", "name": "man: medium-light skin tone, bald", "shortname": ":mediumlight_skin_tone_bald:", "unicode": "1F468 1F3FC 200D 1F9B2", "html": "👨🏼‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽‍🦲", "name": "man: medium skin tone, bald", "shortname": ":medium_skin_tone_bald:", "unicode": "1F468 1F3FD 200D 1F9B2", "html": "👨🏽‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾‍🦲", "name": "man: medium-dark skin tone, bald", "shortname": ":mediumdark_skin_tone_bald:", "unicode": "1F468 1F3FE 200D 1F9B2", "html": "👨🏾‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏿‍🦲", "name": "man: dark skin tone, bald", "shortname": ":dark_skin_tone_bald:", "unicode": "1F468 1F3FF 200D 1F9B2", "html": "👨🏿‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩‍🦰", "name": "woman: red hair", "shortname": ":red_hair:", "unicode": "1F469 200D 1F9B0", "html": "👩‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻‍🦰", "name": "woman: light skin tone, red hair", "shortname": ":light_skin_tone_red_hair:", "unicode": "1F469 1F3FB 200D 1F9B0", "html": "👩🏻‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼‍🦰", "name": "woman: medium-light skin tone, red hair", "shortname": ":mediumlight_skin_tone_red_hair:", "unicode": "1F469 1F3FC 200D 1F9B0", "html": "👩🏼‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽‍🦰", "name": "woman: medium skin tone, red hair", "shortname": ":medium_skin_tone_red_hair:", "unicode": "1F469 1F3FD 200D 1F9B0", "html": "👩🏽‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾‍🦰", "name": "woman: medium-dark skin tone, red hair", "shortname": ":mediumdark_skin_tone_red_hair:", "unicode": "1F469 1F3FE 200D 1F9B0", "html": "👩🏾‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏿‍🦰", "name": "woman: dark skin tone, red hair", "shortname": ":dark_skin_tone_red_hair:", "unicode": "1F469 1F3FF 200D 1F9B0", "html": "👩🏿‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑‍🦰", "name": "person: red hair", "shortname": ":red_hair:", "unicode": "1F9D1 200D 1F9B0", "html": "🧑‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻‍🦰", "name": "person: light skin tone, red hair", "shortname": ":light_skin_tone_red_hair:", "unicode": "1F9D1 1F3FB 200D 1F9B0", "html": "🧑🏻‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼‍🦰", "name": "person: medium-light skin tone, red hair", "shortname": ":mediumlight_skin_tone_red_hair:", "unicode": "1F9D1 1F3FC 200D 1F9B0", "html": "🧑🏼‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽‍🦰", "name": "person: medium skin tone, red hair", "shortname": ":medium_skin_tone_red_hair:", "unicode": "1F9D1 1F3FD 200D 1F9B0", "html": "🧑🏽‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾‍🦰", "name": "person: medium-dark skin tone, red hair", "shortname": ":mediumdark_skin_tone_red_hair:", "unicode": "1F9D1 1F3FE 200D 1F9B0", "html": "🧑🏾‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿‍🦰", "name": "person: dark skin tone, red hair", "shortname": ":dark_skin_tone_red_hair:", "unicode": "1F9D1 1F3FF 200D 1F9B0", "html": "🧑🏿‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩‍🦱", "name": "woman: curly hair", "shortname": ":curly_hair:", "unicode": "1F469 200D 1F9B1", "html": "👩‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻‍🦱", "name": "woman: light skin tone, curly hair", "shortname": ":light_skin_tone_curly_hair:", "unicode": "1F469 1F3FB 200D 1F9B1", "html": "👩🏻‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼‍🦱", "name": "woman: medium-light skin tone, curly hair", "shortname": ":mediumlight_skin_tone_curly_hair:", "unicode": "1F469 1F3FC 200D 1F9B1", "html": "👩🏼‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽‍🦱", "name": "woman: medium skin tone, curly hair", "shortname": ":medium_skin_tone_curly_hair:", "unicode": "1F469 1F3FD 200D 1F9B1", "html": "👩🏽‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾‍🦱", "name": "woman: medium-dark skin tone, curly hair", "shortname": ":mediumdark_skin_tone_curly_hair:", "unicode": "1F469 1F3FE 200D 1F9B1", "html": "👩🏾‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏿‍🦱", "name": "woman: dark skin tone, curly hair", "shortname": ":dark_skin_tone_curly_hair:", "unicode": "1F469 1F3FF 200D 1F9B1", "html": "👩🏿‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑‍🦱", "name": "person: curly hair", "shortname": ":curly_hair:", "unicode": "1F9D1 200D 1F9B1", "html": "🧑‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻‍🦱", "name": "person: light skin tone, curly hair", "shortname": ":light_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FB 200D 1F9B1", "html": "🧑🏻‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼‍🦱", "name": "person: medium-light skin tone, curly hair", "shortname": ":mediumlight_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FC 200D 1F9B1", "html": "🧑🏼‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽‍🦱", "name": "person: medium skin tone, curly hair", "shortname": ":medium_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FD 200D 1F9B1", "html": "🧑🏽‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾‍🦱", "name": "person: medium-dark skin tone, curly hair", "shortname": ":mediumdark_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FE 200D 1F9B1", "html": "🧑🏾‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿‍🦱", "name": "person: dark skin tone, curly hair", "shortname": ":dark_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FF 200D 1F9B1", "html": "🧑🏿‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩‍🦳", "name": "woman: white hair", "shortname": ":white_hair:", "unicode": "1F469 200D 1F9B3", "html": "👩‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻‍🦳", "name": "woman: light skin tone, white hair", "shortname": ":light_skin_tone_white_hair:", "unicode": "1F469 1F3FB 200D 1F9B3", "html": "👩🏻‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼‍🦳", "name": "woman: medium-light skin tone, white hair", "shortname": ":mediumlight_skin_tone_white_hair:", "unicode": "1F469 1F3FC 200D 1F9B3", "html": "👩🏼‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽‍🦳", "name": "woman: medium skin tone, white hair", "shortname": ":medium_skin_tone_white_hair:", "unicode": "1F469 1F3FD 200D 1F9B3", "html": "👩🏽‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾‍🦳", "name": "woman: medium-dark skin tone, white hair", "shortname": ":mediumdark_skin_tone_white_hair:", "unicode": "1F469 1F3FE 200D 1F9B3", "html": "👩🏾‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏿‍🦳", "name": "woman: dark skin tone, white hair", "shortname": ":dark_skin_tone_white_hair:", "unicode": "1F469 1F3FF 200D 1F9B3", "html": "👩🏿‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑‍🦳", "name": "person: white hair", "shortname": ":white_hair:", "unicode": "1F9D1 200D 1F9B3", "html": "🧑‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻‍🦳", "name": "person: light skin tone, white hair", "shortname": ":light_skin_tone_white_hair:", "unicode": "1F9D1 1F3FB 200D 1F9B3", "html": "🧑🏻‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼‍🦳", "name": "person: medium-light skin tone, white hair", "shortname": ":mediumlight_skin_tone_white_hair:", "unicode": "1F9D1 1F3FC 200D 1F9B3", "html": "🧑🏼‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽‍🦳", "name": "person: medium skin tone, white hair", "shortname": ":medium_skin_tone_white_hair:", "unicode": "1F9D1 1F3FD 200D 1F9B3", "html": "🧑🏽‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾‍🦳", "name": "person: medium-dark skin tone, white hair", "shortname": ":mediumdark_skin_tone_white_hair:", "unicode": "1F9D1 1F3FE 200D 1F9B3", "html": "🧑🏾‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿‍🦳", "name": "person: dark skin tone, white hair", "shortname": ":dark_skin_tone_white_hair:", "unicode": "1F9D1 1F3FF 200D 1F9B3", "html": "🧑🏿‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩‍🦲", "name": "woman: bald", "shortname": ":bald:", "unicode": "1F469 200D 1F9B2", "html": "👩‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻‍🦲", "name": "woman: light skin tone, bald", "shortname": ":light_skin_tone_bald:", "unicode": "1F469 1F3FB 200D 1F9B2", "html": "👩🏻‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼‍🦲", "name": "woman: medium-light skin tone, bald", "shortname": ":mediumlight_skin_tone_bald:", "unicode": "1F469 1F3FC 200D 1F9B2", "html": "👩🏼‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽‍🦲", "name": "woman: medium skin tone, bald", "shortname": ":medium_skin_tone_bald:", "unicode": "1F469 1F3FD 200D 1F9B2", "html": "👩🏽‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾‍🦲", "name": "woman: medium-dark skin tone, bald", "shortname": ":mediumdark_skin_tone_bald:", "unicode": "1F469 1F3FE 200D 1F9B2", "html": "👩🏾‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏿‍🦲", "name": "woman: dark skin tone, bald", "shortname": ":dark_skin_tone_bald:", "unicode": "1F469 1F3FF 200D 1F9B2", "html": "👩🏿‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑‍🦲", "name": "person: bald", "shortname": ":bald:", "unicode": "1F9D1 200D 1F9B2", "html": "🧑‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻‍🦲", "name": "person: light skin tone, bald", "shortname": ":light_skin_tone_bald:", "unicode": "1F9D1 1F3FB 200D 1F9B2", "html": "🧑🏻‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼‍🦲", "name": "person: medium-light skin tone, bald", "shortname": ":mediumlight_skin_tone_bald:", "unicode": "1F9D1 1F3FC 200D 1F9B2", "html": "🧑🏼‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽‍🦲", "name": "person: medium skin tone, bald", "shortname": ":medium_skin_tone_bald:", "unicode": "1F9D1 1F3FD 200D 1F9B2", "html": "🧑🏽‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾‍🦲", "name": "person: medium-dark skin tone, bald", "shortname": ":mediumdark_skin_tone_bald:", "unicode": "1F9D1 1F3FE 200D 1F9B2", "html": "🧑🏾‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿‍🦲", "name": "person: dark skin tone, bald", "shortname": ":dark_skin_tone_bald:", "unicode": "1F9D1 1F3FF 200D 1F9B2", "html": "🧑🏿‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱‍♀️", "name": "woman: blond hair", "shortname": ":blond_hair:", "unicode": "1F471 200D 2640 FE0F", "html": "👱‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻‍♀️", "name": "woman: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB 200D 2640 FE0F", "html": "👱🏻‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼‍♀️", "name": "woman: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC 200D 2640 FE0F", "html": "👱🏼‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽‍♀️", "name": "woman: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD 200D 2640 FE0F", "html": "👱🏽‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾‍♀️", "name": "woman: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE 200D 2640 FE0F", "html": "👱🏾‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏿‍♀️", "name": "woman: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF 200D 2640 FE0F", "html": "👱🏿‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱‍♂️", "name": "man: blond hair", "shortname": ":blond_hair:", "unicode": "1F471 200D 2642 FE0F", "html": "👱‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱‍♂", "name": "man: blond hair", "shortname": ":blond_hair:", "unicode": "1F471 200D 2642", "html": "👱‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻‍♂️", "name": "man: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB 200D 2642 FE0F", "html": "👱🏻‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻‍♂", "name": "man: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB 200D 2642", "html": "👱🏻‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼‍♂️", "name": "man: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC 200D 2642 FE0F", "html": "👱🏼‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼‍♂", "name": "man: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC 200D 2642", "html": "👱🏼‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽‍♂️", "name": "man: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD 200D 2642 FE0F", "html": "👱🏽‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽‍♂", "name": "man: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD 200D 2642", "html": "👱🏽‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾‍♂️", "name": "man: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE 200D 2642 FE0F", "html": "👱🏾‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾‍♂", "name": "man: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE 200D 2642", "html": "👱🏾‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏿‍♂️", "name": "man: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF 200D 2642 FE0F", "html": "👱🏿‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏿‍♂", "name": "man: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF 200D 2642", "html": "👱🏿‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓", "name": "older person", "shortname": ":older_person:", "unicode": "1F9D3", "html": "🧓", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏻", "name": "older person: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D3 1F3FB", "html": "🧓🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏼", "name": "older person: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D3 1F3FC", "html": "🧓🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏽", "name": "older person: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D3 1F3FD", "html": "🧓🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏾", "name": "older person: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D3 1F3FE", "html": "🧓🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏿", "name": "older person: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D3 1F3FF", "html": "🧓🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "🙍‍♂️", "name": "man frowning", "shortname": ":man_frowning:", "unicode": "1F64D 200D 2642 FE0F", "html": "🙍‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻‍♂️", "name": "man frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB 200D 2642 FE0F", "html": "🙍🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼‍♂️", "name": "man frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC 200D 2642 FE0F", "html": "🙍🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽‍♂️", "name": "man frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD 200D 2642 FE0F", "html": "🙍🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾‍♂️", "name": "man frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE 200D 2642 FE0F", "html": "🙍🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏿‍♂️", "name": "man frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF 200D 2642 FE0F", "html": "🙍🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍‍♀️", "name": "woman frowning", "shortname": ":woman_frowning:", "unicode": "1F64D 200D 2640 FE0F", "html": "🙍‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍‍♀", "name": "woman frowning", "shortname": ":woman_frowning:", "unicode": "1F64D 200D 2640", "html": "🙍‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻‍♀️", "name": "woman frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB 200D 2640 FE0F", "html": "🙍🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻‍♀", "name": "woman frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB 200D 2640", "html": "🙍🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼‍♀️", "name": "woman frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC 200D 2640 FE0F", "html": "🙍🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼‍♀", "name": "woman frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC 200D 2640", "html": "🙍🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽‍♀️", "name": "woman frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD 200D 2640 FE0F", "html": "🙍🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽‍♀", "name": "woman frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD 200D 2640", "html": "🙍🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾‍♀️", "name": "woman frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE 200D 2640 FE0F", "html": "🙍🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾‍♀", "name": "woman frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE 200D 2640", "html": "🙍🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏿‍♀️", "name": "woman frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF 200D 2640 FE0F", "html": "🙍🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏿‍♀", "name": "woman frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF 200D 2640", "html": "🙍🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎‍♂️", "name": "man pouting", "shortname": ":man_pouting:", "unicode": "1F64E 200D 2642 FE0F", "html": "🙎‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻‍♂️", "name": "man pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB 200D 2642 FE0F", "html": "🙎🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼‍♂️", "name": "man pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC 200D 2642 FE0F", "html": "🙎🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽‍♂️", "name": "man pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD 200D 2642 FE0F", "html": "🙎🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾‍♂️", "name": "man pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE 200D 2642 FE0F", "html": "🙎🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏿‍♂️", "name": "man pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF 200D 2642 FE0F", "html": "🙎🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎‍♀️", "name": "woman pouting", "shortname": ":woman_pouting:", "unicode": "1F64E 200D 2640 FE0F", "html": "🙎‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎‍♀", "name": "woman pouting", "shortname": ":woman_pouting:", "unicode": "1F64E 200D 2640", "html": "🙎‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻‍♀️", "name": "woman pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB 200D 2640 FE0F", "html": "🙎🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻‍♀", "name": "woman pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB 200D 2640", "html": "🙎🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼‍♀️", "name": "woman pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC 200D 2640 FE0F", "html": "🙎🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼‍♀", "name": "woman pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC 200D 2640", "html": "🙎🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽‍♀️", "name": "woman pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD 200D 2640 FE0F", "html": "🙎🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽‍♀", "name": "woman pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD 200D 2640", "html": "🙎🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾‍♀️", "name": "woman pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE 200D 2640 FE0F", "html": "🙎🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾‍♀", "name": "woman pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE 200D 2640", "html": "🙎🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏿‍♀️", "name": "woman pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF 200D 2640 FE0F", "html": "🙎🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏿‍♀", "name": "woman pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF 200D 2640", "html": "🙎🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅‍♂️", "name": "man gesturing NO", "shortname": ":man_gesturing_NO:", "unicode": "1F645 200D 2642 FE0F", "html": "🙅‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻‍♂️", "name": "man gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB 200D 2642 FE0F", "html": "🙅🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼‍♂️", "name": "man gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC 200D 2642 FE0F", "html": "🙅🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽‍♂️", "name": "man gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD 200D 2642 FE0F", "html": "🙅🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾‍♂️", "name": "man gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE 200D 2642 FE0F", "html": "🙅🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏿‍♂️", "name": "man gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF 200D 2642 FE0F", "html": "🙅🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅‍♀️", "name": "woman gesturing NO", "shortname": ":woman_gesturing_NO:", "unicode": "1F645 200D 2640 FE0F", "html": "🙅‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅‍♀", "name": "woman gesturing NO", "shortname": ":woman_gesturing_NO:", "unicode": "1F645 200D 2640", "html": "🙅‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻‍♀️", "name": "woman gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB 200D 2640 FE0F", "html": "🙅🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻‍♀", "name": "woman gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB 200D 2640", "html": "🙅🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼‍♀️", "name": "woman gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC 200D 2640 FE0F", "html": "🙅🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼‍♀", "name": "woman gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC 200D 2640", "html": "🙅🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽‍♀️", "name": "woman gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD 200D 2640 FE0F", "html": "🙅🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽‍♀", "name": "woman gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD 200D 2640", "html": "🙅🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾‍♀️", "name": "woman gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE 200D 2640 FE0F", "html": "🙅🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾‍♀", "name": "woman gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE 200D 2640", "html": "🙅🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏿‍♀️", "name": "woman gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF 200D 2640 FE0F", "html": "🙅🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏿‍♀", "name": "woman gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF 200D 2640", "html": "🙅🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆‍♂️", "name": "man gesturing OK", "shortname": ":man_gesturing_OK:", "unicode": "1F646 200D 2642 FE0F", "html": "🙆‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻‍♂️", "name": "man gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB 200D 2642 FE0F", "html": "🙆🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼‍♂️", "name": "man gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC 200D 2642 FE0F", "html": "🙆🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽‍♂️", "name": "man gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD 200D 2642 FE0F", "html": "🙆🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾‍♂️", "name": "man gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE 200D 2642 FE0F", "html": "🙆🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏿‍♂️", "name": "man gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF 200D 2642 FE0F", "html": "🙆🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆‍♀️", "name": "woman gesturing OK", "shortname": ":woman_gesturing_OK:", "unicode": "1F646 200D 2640 FE0F", "html": "🙆‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆‍♀", "name": "woman gesturing OK", "shortname": ":woman_gesturing_OK:", "unicode": "1F646 200D 2640", "html": "🙆‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻‍♀️", "name": "woman gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB 200D 2640 FE0F", "html": "🙆🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻‍♀", "name": "woman gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB 200D 2640", "html": "🙆🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼‍♀️", "name": "woman gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC 200D 2640 FE0F", "html": "🙆🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼‍♀", "name": "woman gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC 200D 2640", "html": "🙆🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽‍♀️", "name": "woman gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD 200D 2640 FE0F", "html": "🙆🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽‍♀", "name": "woman gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD 200D 2640", "html": "🙆🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾‍♀️", "name": "woman gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE 200D 2640 FE0F", "html": "🙆🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾‍♀", "name": "woman gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE 200D 2640", "html": "🙆🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏿‍♀️", "name": "woman gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF 200D 2640 FE0F", "html": "🙆🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏿‍♀", "name": "woman gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF 200D 2640", "html": "🙆🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁‍♂️", "name": "man tipping hand", "shortname": ":man_tipping_hand:", "unicode": "1F481 200D 2642 FE0F", "html": "💁‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻‍♂️", "name": "man tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB 200D 2642 FE0F", "html": "💁🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼‍♂️", "name": "man tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC 200D 2642 FE0F", "html": "💁🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽‍♂️", "name": "man tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD 200D 2642 FE0F", "html": "💁🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾‍♂️", "name": "man tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE 200D 2642 FE0F", "html": "💁🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏿‍♂️", "name": "man tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF 200D 2642 FE0F", "html": "💁🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁‍♀️", "name": "woman tipping hand", "shortname": ":woman_tipping_hand:", "unicode": "1F481 200D 2640 FE0F", "html": "💁‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁‍♀", "name": "woman tipping hand", "shortname": ":woman_tipping_hand:", "unicode": "1F481 200D 2640", "html": "💁‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻‍♀️", "name": "woman tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB 200D 2640 FE0F", "html": "💁🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻‍♀", "name": "woman tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB 200D 2640", "html": "💁🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼‍♀️", "name": "woman tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC 200D 2640 FE0F", "html": "💁🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼‍♀", "name": "woman tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC 200D 2640", "html": "💁🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽‍♀️", "name": "woman tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD 200D 2640 FE0F", "html": "💁🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽‍♀", "name": "woman tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD 200D 2640", "html": "💁🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾‍♀️", "name": "woman tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE 200D 2640 FE0F", "html": "💁🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾‍♀", "name": "woman tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE 200D 2640", "html": "💁🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏿‍♀️", "name": "woman tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF 200D 2640 FE0F", "html": "💁🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏿‍♀", "name": "woman tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF 200D 2640", "html": "💁🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋‍♂️", "name": "man raising hand", "shortname": ":man_raising_hand:", "unicode": "1F64B 200D 2642 FE0F", "html": "🙋‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻‍♂️", "name": "man raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB 200D 2642 FE0F", "html": "🙋🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼‍♂️", "name": "man raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC 200D 2642 FE0F", "html": "🙋🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽‍♂️", "name": "man raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD 200D 2642 FE0F", "html": "🙋🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾‍♂️", "name": "man raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE 200D 2642 FE0F", "html": "🙋🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏿‍♂️", "name": "man raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF 200D 2642 FE0F", "html": "🙋🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋‍♀️", "name": "woman raising hand", "shortname": ":woman_raising_hand:", "unicode": "1F64B 200D 2640 FE0F", "html": "🙋‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋‍♀", "name": "woman raising hand", "shortname": ":woman_raising_hand:", "unicode": "1F64B 200D 2640", "html": "🙋‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻‍♀️", "name": "woman raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB 200D 2640 FE0F", "html": "🙋🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻‍♀", "name": "woman raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB 200D 2640", "html": "🙋🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼‍♀️", "name": "woman raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC 200D 2640 FE0F", "html": "🙋🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼‍♀", "name": "woman raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC 200D 2640", "html": "🙋🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽‍♀️", "name": "woman raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD 200D 2640 FE0F", "html": "🙋🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽‍♀", "name": "woman raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD 200D 2640", "html": "🙋🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾‍♀️", "name": "woman raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE 200D 2640 FE0F", "html": "🙋🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾‍♀", "name": "woman raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE 200D 2640", "html": "🙋🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏿‍♀️", "name": "woman raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF 200D 2640 FE0F", "html": "🙋🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏿‍♀", "name": "woman raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF 200D 2640", "html": "🙋🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏", "name": "deaf person", "shortname": ":deaf_person:", "unicode": "1F9CF", "html": "🧏", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻", "name": "deaf person: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB", "html": "🧏🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼", "name": "deaf person: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC", "html": "🧏🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽", "name": "deaf person: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD", "html": "🧏🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾", "name": "deaf person: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE", "html": "🧏🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿", "name": "deaf person: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF", "html": "🧏🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏‍♂️", "name": "deaf man", "shortname": ":deaf_man:", "unicode": "1F9CF 200D 2642 FE0F", "html": "🧏‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏‍♂", "name": "deaf man", "shortname": ":deaf_man:", "unicode": "1F9CF 200D 2642", "html": "🧏‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻‍♂️", "name": "deaf man: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB 200D 2642 FE0F", "html": "🧏🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻‍♂", "name": "deaf man: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB 200D 2642", "html": "🧏🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼‍♂️", "name": "deaf man: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC 200D 2642 FE0F", "html": "🧏🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼‍♂", "name": "deaf man: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC 200D 2642", "html": "🧏🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽‍♂️", "name": "deaf man: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD 200D 2642 FE0F", "html": "🧏🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽‍♂", "name": "deaf man: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD 200D 2642", "html": "🧏🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾‍♂️", "name": "deaf man: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE 200D 2642 FE0F", "html": "🧏🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾‍♂", "name": "deaf man: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE 200D 2642", "html": "🧏🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿‍♂️", "name": "deaf man: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF 200D 2642 FE0F", "html": "🧏🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿‍♂", "name": "deaf man: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF 200D 2642", "html": "🧏🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏‍♀️", "name": "deaf woman", "shortname": ":deaf_woman:", "unicode": "1F9CF 200D 2640 FE0F", "html": "🧏‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏‍♀", "name": "deaf woman", "shortname": ":deaf_woman:", "unicode": "1F9CF 200D 2640", "html": "🧏‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻‍♀️", "name": "deaf woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB 200D 2640 FE0F", "html": "🧏🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻‍♀", "name": "deaf woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB 200D 2640", "html": "🧏🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼‍♀️", "name": "deaf woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC 200D 2640 FE0F", "html": "🧏🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼‍♀", "name": "deaf woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC 200D 2640", "html": "🧏🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽‍♀️", "name": "deaf woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD 200D 2640 FE0F", "html": "🧏🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽‍♀", "name": "deaf woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD 200D 2640", "html": "🧏🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾‍♀️", "name": "deaf woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE 200D 2640 FE0F", "html": "🧏🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾‍♀", "name": "deaf woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE 200D 2640", "html": "🧏🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿‍♀️", "name": "deaf woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF 200D 2640 FE0F", "html": "🧏🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿‍♀", "name": "deaf woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF 200D 2640", "html": "🧏🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇‍♂️", "name": "man bowing", "shortname": ":man_bowing:", "unicode": "1F647 200D 2642 FE0F", "html": "🙇‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇‍♂", "name": "man bowing", "shortname": ":man_bowing:", "unicode": "1F647 200D 2642", "html": "🙇‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻‍♂️", "name": "man bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB 200D 2642 FE0F", "html": "🙇🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻‍♂", "name": "man bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB 200D 2642", "html": "🙇🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼‍♂️", "name": "man bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC 200D 2642 FE0F", "html": "🙇🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼‍♂", "name": "man bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC 200D 2642", "html": "🙇🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽‍♂️", "name": "man bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD 200D 2642 FE0F", "html": "🙇🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽‍♂", "name": "man bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD 200D 2642", "html": "🙇🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾‍♂️", "name": "man bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE 200D 2642 FE0F", "html": "🙇🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾‍♂", "name": "man bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE 200D 2642", "html": "🙇🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏿‍♂️", "name": "man bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF 200D 2642 FE0F", "html": "🙇🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏿‍♂", "name": "man bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF 200D 2642", "html": "🙇🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇‍♀️", "name": "woman bowing", "shortname": ":woman_bowing:", "unicode": "1F647 200D 2640 FE0F", "html": "🙇‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻‍♀️", "name": "woman bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB 200D 2640 FE0F", "html": "🙇🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼‍♀️", "name": "woman bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC 200D 2640 FE0F", "html": "🙇🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽‍♀️", "name": "woman bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD 200D 2640 FE0F", "html": "🙇🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾‍♀️", "name": "woman bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE 200D 2640 FE0F", "html": "🙇🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏿‍♀️", "name": "woman bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF 200D 2640 FE0F", "html": "🙇🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦", "name": "person facepalming", "shortname": ":person_facepalming:", "unicode": "1F926", "html": "🤦", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻", "name": "person facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB", "html": "🤦🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼", "name": "person facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC", "html": "🤦🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽", "name": "person facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD", "html": "🤦🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾", "name": "person facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE", "html": "🤦🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿", "name": "person facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF", "html": "🤦🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦‍♂️", "name": "man facepalming", "shortname": ":man_facepalming:", "unicode": "1F926 200D 2642 FE0F", "html": "🤦‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦‍♂", "name": "man facepalming", "shortname": ":man_facepalming:", "unicode": "1F926 200D 2642", "html": "🤦‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻‍♂️", "name": "man facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB 200D 2642 FE0F", "html": "🤦🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻‍♂", "name": "man facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB 200D 2642", "html": "🤦🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼‍♂️", "name": "man facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC 200D 2642 FE0F", "html": "🤦🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼‍♂", "name": "man facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC 200D 2642", "html": "🤦🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽‍♂️", "name": "man facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD 200D 2642 FE0F", "html": "🤦🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽‍♂", "name": "man facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD 200D 2642", "html": "🤦🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾‍♂️", "name": "man facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE 200D 2642 FE0F", "html": "🤦🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾‍♂", "name": "man facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE 200D 2642", "html": "🤦🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿‍♂️", "name": "man facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF 200D 2642 FE0F", "html": "🤦🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿‍♂", "name": "man facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF 200D 2642", "html": "🤦🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦‍♀️", "name": "woman facepalming", "shortname": ":woman_facepalming:", "unicode": "1F926 200D 2640 FE0F", "html": "🤦‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦‍♀", "name": "woman facepalming", "shortname": ":woman_facepalming:", "unicode": "1F926 200D 2640", "html": "🤦‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻‍♀️", "name": "woman facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB 200D 2640 FE0F", "html": "🤦🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻‍♀", "name": "woman facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB 200D 2640", "html": "🤦🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼‍♀️", "name": "woman facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC 200D 2640 FE0F", "html": "🤦🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼‍♀", "name": "woman facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC 200D 2640", "html": "🤦🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽‍♀️", "name": "woman facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD 200D 2640 FE0F", "html": "🤦🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽‍♀", "name": "woman facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD 200D 2640", "html": "🤦🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾‍♀️", "name": "woman facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE 200D 2640 FE0F", "html": "🤦🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾‍♀", "name": "woman facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE 200D 2640", "html": "🤦🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿‍♀️", "name": "woman facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF 200D 2640 FE0F", "html": "🤦🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿‍♀", "name": "woman facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF 200D 2640", "html": "🤦🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷", "name": "person shrugging", "shortname": ":person_shrugging:", "unicode": "1F937", "html": "🤷", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻", "name": "person shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB", "html": "🤷🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼", "name": "person shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC", "html": "🤷🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽", "name": "person shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD", "html": "🤷🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾", "name": "person shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE", "html": "🤷🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿", "name": "person shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF", "html": "🤷🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷‍♂️", "name": "man shrugging", "shortname": ":man_shrugging:", "unicode": "1F937 200D 2642 FE0F", "html": "🤷‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷‍♂", "name": "man shrugging", "shortname": ":man_shrugging:", "unicode": "1F937 200D 2642", "html": "🤷‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻‍♂️", "name": "man shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB 200D 2642 FE0F", "html": "🤷🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻‍♂", "name": "man shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB 200D 2642", "html": "🤷🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼‍♂️", "name": "man shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC 200D 2642 FE0F", "html": "🤷🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼‍♂", "name": "man shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC 200D 2642", "html": "🤷🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽‍♂️", "name": "man shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD 200D 2642 FE0F", "html": "🤷🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽‍♂", "name": "man shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD 200D 2642", "html": "🤷🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾‍♂️", "name": "man shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE 200D 2642 FE0F", "html": "🤷🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾‍♂", "name": "man shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE 200D 2642", "html": "🤷🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿‍♂️", "name": "man shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF 200D 2642 FE0F", "html": "🤷🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿‍♂", "name": "man shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF 200D 2642", "html": "🤷🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷‍♀️", "name": "woman shrugging", "shortname": ":woman_shrugging:", "unicode": "1F937 200D 2640 FE0F", "html": "🤷‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷‍♀", "name": "woman shrugging", "shortname": ":woman_shrugging:", "unicode": "1F937 200D 2640", "html": "🤷‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻‍♀️", "name": "woman shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB 200D 2640 FE0F", "html": "🤷🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻‍♀", "name": "woman shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB 200D 2640", "html": "🤷🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼‍♀️", "name": "woman shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC 200D 2640 FE0F", "html": "🤷🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼‍♀", "name": "woman shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC 200D 2640", "html": "🤷🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽‍♀️", "name": "woman shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD 200D 2640 FE0F", "html": "🤷🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽‍♀", "name": "woman shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD 200D 2640", "html": "🤷🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾‍♀️", "name": "woman shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE 200D 2640 FE0F", "html": "🤷🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾‍♀", "name": "woman shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE 200D 2640", "html": "🤷🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿‍♀️", "name": "woman shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF 200D 2640 FE0F", "html": "🤷🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿‍♀", "name": "woman shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF 200D 2640", "html": "🤷🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧑‍⚕️", "name": "health worker", "shortname": ":health_worker:", "unicode": "1F9D1 200D 2695 FE0F", "html": "🧑‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍⚕", "name": "health worker", "shortname": ":health_worker:", "unicode": "1F9D1 200D 2695", "html": "🧑‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍⚕️", "name": "health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2695 FE0F", "html": "🧑🏻‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍⚕", "name": "health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2695", "html": "🧑🏻‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍⚕️", "name": "health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2695 FE0F", "html": "🧑🏼‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍⚕", "name": "health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2695", "html": "🧑🏼‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍⚕️", "name": "health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2695 FE0F", "html": "🧑🏽‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍⚕", "name": "health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2695", "html": "🧑🏽‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍⚕️", "name": "health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2695 FE0F", "html": "🧑🏾‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍⚕", "name": "health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2695", "html": "🧑🏾‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍⚕️", "name": "health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2695 FE0F", "html": "🧑🏿‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍⚕", "name": "health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2695", "html": "🧑🏿‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍⚕️", "name": "man health worker", "shortname": ":man_health_worker:", "unicode": "1F468 200D 2695 FE0F", "html": "👨‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍⚕", "name": "man health worker", "shortname": ":man_health_worker:", "unicode": "1F468 200D 2695", "html": "👨‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍⚕️", "name": "man health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2695 FE0F", "html": "👨🏻‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍⚕", "name": "man health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2695", "html": "👨🏻‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍⚕️", "name": "man health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2695 FE0F", "html": "👨🏼‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍⚕", "name": "man health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2695", "html": "👨🏼‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍⚕️", "name": "man health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2695 FE0F", "html": "👨🏽‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍⚕", "name": "man health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2695", "html": "👨🏽‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍⚕️", "name": "man health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2695 FE0F", "html": "👨🏾‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍⚕", "name": "man health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2695", "html": "👨🏾‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍⚕️", "name": "man health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2695 FE0F", "html": "👨🏿‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍⚕", "name": "man health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2695", "html": "👨🏿‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍⚕️", "name": "woman health worker", "shortname": ":woman_health_worker:", "unicode": "1F469 200D 2695 FE0F", "html": "👩‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍⚕", "name": "woman health worker", "shortname": ":woman_health_worker:", "unicode": "1F469 200D 2695", "html": "👩‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍⚕️", "name": "woman health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2695 FE0F", "html": "👩🏻‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍⚕", "name": "woman health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2695", "html": "👩🏻‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍⚕️", "name": "woman health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2695 FE0F", "html": "👩🏼‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍⚕", "name": "woman health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2695", "html": "👩🏼‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍⚕️", "name": "woman health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2695 FE0F", "html": "👩🏽‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍⚕", "name": "woman health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2695", "html": "👩🏽‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍⚕️", "name": "woman health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2695 FE0F", "html": "👩🏾‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍⚕", "name": "woman health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2695", "html": "👩🏾‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍⚕️", "name": "woman health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2695 FE0F", "html": "👩🏿‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍⚕", "name": "woman health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2695", "html": "👩🏿‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🎓", "name": "student", "shortname": ":student:", "unicode": "1F9D1 200D 1F393", "html": "🧑‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🎓", "name": "student: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F393", "html": "🧑🏻‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🎓", "name": "student: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F393", "html": "🧑🏼‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🎓", "name": "student: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F393", "html": "🧑🏽‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🎓", "name": "student: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F393", "html": "🧑🏾‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🎓", "name": "student: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F393", "html": "🧑🏿‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🎓", "name": "man student", "shortname": ":man_student:", "unicode": "1F468 200D 1F393", "html": "👨‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🎓", "name": "man student: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F393", "html": "👨🏻‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🎓", "name": "man student: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F393", "html": "👨🏼‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🎓", "name": "man student: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F393", "html": "👨🏽‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🎓", "name": "man student: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F393", "html": "👨🏾‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🎓", "name": "man student: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F393", "html": "👨🏿‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🎓", "name": "woman student", "shortname": ":woman_student:", "unicode": "1F469 200D 1F393", "html": "👩‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🎓", "name": "woman student: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F393", "html": "👩🏻‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🎓", "name": "woman student: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F393", "html": "👩🏼‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🎓", "name": "woman student: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F393", "html": "👩🏽‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🎓", "name": "woman student: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F393", "html": "👩🏾‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🎓", "name": "woman student: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F393", "html": "👩🏿‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🏫", "name": "teacher", "shortname": ":teacher:", "unicode": "1F9D1 200D 1F3EB", "html": "🧑‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🏫", "name": "teacher: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F3EB", "html": "🧑🏻‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🏫", "name": "teacher: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F3EB", "html": "🧑🏼‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🏫", "name": "teacher: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F3EB", "html": "🧑🏽‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🏫", "name": "teacher: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F3EB", "html": "🧑🏾‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🏫", "name": "teacher: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F3EB", "html": "🧑🏿‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🏫", "name": "man teacher", "shortname": ":man_teacher:", "unicode": "1F468 200D 1F3EB", "html": "👨‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🏫", "name": "man teacher: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F3EB", "html": "👨🏻‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🏫", "name": "man teacher: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F3EB", "html": "👨🏼‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🏫", "name": "man teacher: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F3EB", "html": "👨🏽‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🏫", "name": "man teacher: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F3EB", "html": "👨🏾‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🏫", "name": "man teacher: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F3EB", "html": "👨🏿‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🏫", "name": "woman teacher", "shortname": ":woman_teacher:", "unicode": "1F469 200D 1F3EB", "html": "👩‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🏫", "name": "woman teacher: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F3EB", "html": "👩🏻‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🏫", "name": "woman teacher: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F3EB", "html": "👩🏼‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🏫", "name": "woman teacher: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F3EB", "html": "👩🏽‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🏫", "name": "woman teacher: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F3EB", "html": "👩🏾‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🏫", "name": "woman teacher: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F3EB", "html": "👩🏿‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍⚖️", "name": "judge", "shortname": ":judge:", "unicode": "1F9D1 200D 2696 FE0F", "html": "🧑‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍⚖", "name": "judge", "shortname": ":judge:", "unicode": "1F9D1 200D 2696", "html": "🧑‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍⚖️", "name": "judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2696 FE0F", "html": "🧑🏻‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍⚖", "name": "judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2696", "html": "🧑🏻‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍⚖️", "name": "judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2696 FE0F", "html": "🧑🏼‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍⚖", "name": "judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2696", "html": "🧑🏼‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍⚖️", "name": "judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2696 FE0F", "html": "🧑🏽‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍⚖", "name": "judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2696", "html": "🧑🏽‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍⚖️", "name": "judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2696 FE0F", "html": "🧑🏾‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍⚖", "name": "judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2696", "html": "🧑🏾‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍⚖️", "name": "judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2696 FE0F", "html": "🧑🏿‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍⚖", "name": "judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2696", "html": "🧑🏿‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍⚖️", "name": "man judge", "shortname": ":man_judge:", "unicode": "1F468 200D 2696 FE0F", "html": "👨‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍⚖", "name": "man judge", "shortname": ":man_judge:", "unicode": "1F468 200D 2696", "html": "👨‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍⚖️", "name": "man judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2696 FE0F", "html": "👨🏻‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍⚖", "name": "man judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2696", "html": "👨🏻‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍⚖️", "name": "man judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2696 FE0F", "html": "👨🏼‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍⚖", "name": "man judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2696", "html": "👨🏼‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍⚖️", "name": "man judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2696 FE0F", "html": "👨🏽‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍⚖", "name": "man judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2696", "html": "👨🏽‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍⚖️", "name": "man judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2696 FE0F", "html": "👨🏾‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍⚖", "name": "man judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2696", "html": "👨🏾‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍⚖️", "name": "man judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2696 FE0F", "html": "👨🏿‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍⚖", "name": "man judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2696", "html": "👨🏿‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍⚖️", "name": "woman judge", "shortname": ":woman_judge:", "unicode": "1F469 200D 2696 FE0F", "html": "👩‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍⚖", "name": "woman judge", "shortname": ":woman_judge:", "unicode": "1F469 200D 2696", "html": "👩‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍⚖️", "name": "woman judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2696 FE0F", "html": "👩🏻‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍⚖", "name": "woman judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2696", "html": "👩🏻‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍⚖️", "name": "woman judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2696 FE0F", "html": "👩🏼‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍⚖", "name": "woman judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2696", "html": "👩🏼‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍⚖️", "name": "woman judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2696 FE0F", "html": "👩🏽‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍⚖", "name": "woman judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2696", "html": "👩🏽‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍⚖️", "name": "woman judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2696 FE0F", "html": "👩🏾‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍⚖", "name": "woman judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2696", "html": "👩🏾‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍⚖️", "name": "woman judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2696 FE0F", "html": "👩🏿‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍⚖", "name": "woman judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2696", "html": "👩🏿‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🌾", "name": "farmer", "shortname": ":farmer:", "unicode": "1F9D1 200D 1F33E", "html": "🧑‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🌾", "name": "farmer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F33E", "html": "🧑🏻‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🌾", "name": "farmer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F33E", "html": "🧑🏼‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🌾", "name": "farmer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F33E", "html": "🧑🏽‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🌾", "name": "farmer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F33E", "html": "🧑🏾‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🌾", "name": "farmer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F33E", "html": "🧑🏿‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🌾", "name": "man farmer", "shortname": ":man_farmer:", "unicode": "1F468 200D 1F33E", "html": "👨‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🌾", "name": "man farmer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F33E", "html": "👨🏻‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🌾", "name": "man farmer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F33E", "html": "👨🏼‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🌾", "name": "man farmer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F33E", "html": "👨🏽‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🌾", "name": "man farmer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F33E", "html": "👨🏾‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🌾", "name": "man farmer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F33E", "html": "👨🏿‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🌾", "name": "woman farmer", "shortname": ":woman_farmer:", "unicode": "1F469 200D 1F33E", "html": "👩‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🌾", "name": "woman farmer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F33E", "html": "👩🏻‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🌾", "name": "woman farmer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F33E", "html": "👩🏼‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🌾", "name": "woman farmer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F33E", "html": "👩🏽‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🌾", "name": "woman farmer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F33E", "html": "👩🏾‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🌾", "name": "woman farmer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F33E", "html": "👩🏿‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🍳", "name": "cook", "shortname": ":cook:", "unicode": "1F9D1 200D 1F373", "html": "🧑‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🍳", "name": "cook: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F373", "html": "🧑🏻‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🍳", "name": "cook: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F373", "html": "🧑🏼‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🍳", "name": "cook: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F373", "html": "🧑🏽‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🍳", "name": "cook: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F373", "html": "🧑🏾‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🍳", "name": "cook: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F373", "html": "🧑🏿‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🍳", "name": "man cook", "shortname": ":man_cook:", "unicode": "1F468 200D 1F373", "html": "👨‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🍳", "name": "man cook: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F373", "html": "👨🏻‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🍳", "name": "man cook: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F373", "html": "👨🏼‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🍳", "name": "man cook: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F373", "html": "👨🏽‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🍳", "name": "man cook: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F373", "html": "👨🏾‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🍳", "name": "man cook: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F373", "html": "👨🏿‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🍳", "name": "woman cook", "shortname": ":woman_cook:", "unicode": "1F469 200D 1F373", "html": "👩‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🍳", "name": "woman cook: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F373", "html": "👩🏻‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🍳", "name": "woman cook: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F373", "html": "👩🏼‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🍳", "name": "woman cook: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F373", "html": "👩🏽‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🍳", "name": "woman cook: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F373", "html": "👩🏾‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🍳", "name": "woman cook: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F373", "html": "👩🏿‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🔧", "name": "mechanic", "shortname": ":mechanic:", "unicode": "1F9D1 200D 1F527", "html": "🧑‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🔧", "name": "mechanic: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F527", "html": "🧑🏻‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🔧", "name": "mechanic: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F527", "html": "🧑🏼‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🔧", "name": "mechanic: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F527", "html": "🧑🏽‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🔧", "name": "mechanic: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F527", "html": "🧑🏾‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🔧", "name": "mechanic: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F527", "html": "🧑🏿‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🔧", "name": "man mechanic", "shortname": ":man_mechanic:", "unicode": "1F468 200D 1F527", "html": "👨‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🔧", "name": "man mechanic: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F527", "html": "👨🏻‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🔧", "name": "man mechanic: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F527", "html": "👨🏼‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🔧", "name": "man mechanic: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F527", "html": "👨🏽‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🔧", "name": "man mechanic: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F527", "html": "👨🏾‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🔧", "name": "man mechanic: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F527", "html": "👨🏿‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🔧", "name": "woman mechanic", "shortname": ":woman_mechanic:", "unicode": "1F469 200D 1F527", "html": "👩‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🔧", "name": "woman mechanic: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F527", "html": "👩🏻‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🔧", "name": "woman mechanic: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F527", "html": "👩🏼‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🔧", "name": "woman mechanic: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F527", "html": "👩🏽‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🔧", "name": "woman mechanic: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F527", "html": "👩🏾‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🔧", "name": "woman mechanic: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F527", "html": "👩🏿‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🏭", "name": "factory worker", "shortname": ":factory_worker:", "unicode": "1F9D1 200D 1F3ED", "html": "🧑‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🏭", "name": "factory worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F3ED", "html": "🧑🏻‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🏭", "name": "factory worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F3ED", "html": "🧑🏼‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🏭", "name": "factory worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F3ED", "html": "🧑🏽‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🏭", "name": "factory worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F3ED", "html": "🧑🏾‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🏭", "name": "factory worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F3ED", "html": "🧑🏿‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🏭", "name": "man factory worker", "shortname": ":man_factory_worker:", "unicode": "1F468 200D 1F3ED", "html": "👨‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🏭", "name": "man factory worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F3ED", "html": "👨🏻‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🏭", "name": "man factory worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F3ED", "html": "👨🏼‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🏭", "name": "man factory worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F3ED", "html": "👨🏽‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🏭", "name": "man factory worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F3ED", "html": "👨🏾‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🏭", "name": "man factory worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F3ED", "html": "👨🏿‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🏭", "name": "woman factory worker", "shortname": ":woman_factory_worker:", "unicode": "1F469 200D 1F3ED", "html": "👩‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🏭", "name": "woman factory worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F3ED", "html": "👩🏻‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🏭", "name": "woman factory worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F3ED", "html": "👩🏼‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🏭", "name": "woman factory worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F3ED", "html": "👩🏽‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🏭", "name": "woman factory worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F3ED", "html": "👩🏾‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🏭", "name": "woman factory worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F3ED", "html": "👩🏿‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍💼", "name": "office worker", "shortname": ":office_worker:", "unicode": "1F9D1 200D 1F4BC", "html": "🧑‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍💼", "name": "office worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F4BC", "html": "🧑🏻‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍💼", "name": "office worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F4BC", "html": "🧑🏼‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍💼", "name": "office worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F4BC", "html": "🧑🏽‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍💼", "name": "office worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F4BC", "html": "🧑🏾‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍💼", "name": "office worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F4BC", "html": "🧑🏿‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍💼", "name": "man office worker", "shortname": ":man_office_worker:", "unicode": "1F468 200D 1F4BC", "html": "👨‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍💼", "name": "man office worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F4BC", "html": "👨🏻‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍💼", "name": "man office worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F4BC", "html": "👨🏼‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍💼", "name": "man office worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F4BC", "html": "👨🏽‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍💼", "name": "man office worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F4BC", "html": "👨🏾‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍💼", "name": "man office worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F4BC", "html": "👨🏿‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍💼", "name": "woman office worker", "shortname": ":woman_office_worker:", "unicode": "1F469 200D 1F4BC", "html": "👩‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍💼", "name": "woman office worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F4BC", "html": "👩🏻‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍💼", "name": "woman office worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F4BC", "html": "👩🏼‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍💼", "name": "woman office worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F4BC", "html": "👩🏽‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍💼", "name": "woman office worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F4BC", "html": "👩🏾‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍💼", "name": "woman office worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F4BC", "html": "👩🏿‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🔬", "name": "scientist", "shortname": ":scientist:", "unicode": "1F9D1 200D 1F52C", "html": "🧑‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🔬", "name": "scientist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F52C", "html": "🧑🏻‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🔬", "name": "scientist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F52C", "html": "🧑🏼‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🔬", "name": "scientist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F52C", "html": "🧑🏽‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🔬", "name": "scientist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F52C", "html": "🧑🏾‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🔬", "name": "scientist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F52C", "html": "🧑🏿‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🔬", "name": "man scientist", "shortname": ":man_scientist:", "unicode": "1F468 200D 1F52C", "html": "👨‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🔬", "name": "man scientist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F52C", "html": "👨🏻‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🔬", "name": "man scientist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F52C", "html": "👨🏼‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🔬", "name": "man scientist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F52C", "html": "👨🏽‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🔬", "name": "man scientist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F52C", "html": "👨🏾‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🔬", "name": "man scientist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F52C", "html": "👨🏿‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🔬", "name": "woman scientist", "shortname": ":woman_scientist:", "unicode": "1F469 200D 1F52C", "html": "👩‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🔬", "name": "woman scientist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F52C", "html": "👩🏻‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🔬", "name": "woman scientist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F52C", "html": "👩🏼‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🔬", "name": "woman scientist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F52C", "html": "👩🏽‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🔬", "name": "woman scientist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F52C", "html": "👩🏾‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🔬", "name": "woman scientist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F52C", "html": "👩🏿‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍💻", "name": "technologist", "shortname": ":technologist:", "unicode": "1F9D1 200D 1F4BB", "html": "🧑‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍💻", "name": "technologist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F4BB", "html": "🧑🏻‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍💻", "name": "technologist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F4BB", "html": "🧑🏼‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍💻", "name": "technologist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F4BB", "html": "🧑🏽‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍💻", "name": "technologist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F4BB", "html": "🧑🏾‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍💻", "name": "technologist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F4BB", "html": "🧑🏿‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍💻", "name": "man technologist", "shortname": ":man_technologist:", "unicode": "1F468 200D 1F4BB", "html": "👨‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍💻", "name": "man technologist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F4BB", "html": "👨🏻‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍💻", "name": "man technologist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F4BB", "html": "👨🏼‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍💻", "name": "man technologist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F4BB", "html": "👨🏽‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍💻", "name": "man technologist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F4BB", "html": "👨🏾‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍💻", "name": "man technologist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F4BB", "html": "👨🏿‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍💻", "name": "woman technologist", "shortname": ":woman_technologist:", "unicode": "1F469 200D 1F4BB", "html": "👩‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍💻", "name": "woman technologist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F4BB", "html": "👩🏻‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍💻", "name": "woman technologist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F4BB", "html": "👩🏼‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍💻", "name": "woman technologist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F4BB", "html": "👩🏽‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍💻", "name": "woman technologist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F4BB", "html": "👩🏾‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍💻", "name": "woman technologist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F4BB", "html": "👩🏿‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🎤", "name": "singer", "shortname": ":singer:", "unicode": "1F9D1 200D 1F3A4", "html": "🧑‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🎤", "name": "singer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F3A4", "html": "🧑🏻‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🎤", "name": "singer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F3A4", "html": "🧑🏼‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🎤", "name": "singer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F3A4", "html": "🧑🏽‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🎤", "name": "singer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F3A4", "html": "🧑🏾‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🎤", "name": "singer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F3A4", "html": "🧑🏿‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🎤", "name": "man singer", "shortname": ":man_singer:", "unicode": "1F468 200D 1F3A4", "html": "👨‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🎤", "name": "man singer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F3A4", "html": "👨🏻‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🎤", "name": "man singer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F3A4", "html": "👨🏼‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🎤", "name": "man singer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F3A4", "html": "👨🏽‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🎤", "name": "man singer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F3A4", "html": "👨🏾‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🎤", "name": "man singer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F3A4", "html": "👨🏿‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🎤", "name": "woman singer", "shortname": ":woman_singer:", "unicode": "1F469 200D 1F3A4", "html": "👩‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🎤", "name": "woman singer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F3A4", "html": "👩🏻‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🎤", "name": "woman singer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F3A4", "html": "👩🏼‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🎤", "name": "woman singer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F3A4", "html": "👩🏽‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🎤", "name": "woman singer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F3A4", "html": "👩🏾‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🎤", "name": "woman singer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F3A4", "html": "👩🏿‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🎨", "name": "artist", "shortname": ":artist:", "unicode": "1F9D1 200D 1F3A8", "html": "🧑‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🎨", "name": "artist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F3A8", "html": "🧑🏻‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🎨", "name": "artist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F3A8", "html": "🧑🏼‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🎨", "name": "artist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F3A8", "html": "🧑🏽‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🎨", "name": "artist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F3A8", "html": "🧑🏾‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🎨", "name": "artist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F3A8", "html": "🧑🏿‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🎨", "name": "man artist", "shortname": ":man_artist:", "unicode": "1F468 200D 1F3A8", "html": "👨‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🎨", "name": "man artist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F3A8", "html": "👨🏻‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🎨", "name": "man artist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F3A8", "html": "👨🏼‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🎨", "name": "man artist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F3A8", "html": "👨🏽‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🎨", "name": "man artist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F3A8", "html": "👨🏾‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🎨", "name": "man artist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F3A8", "html": "👨🏿‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🎨", "name": "woman artist", "shortname": ":woman_artist:", "unicode": "1F469 200D 1F3A8", "html": "👩‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🎨", "name": "woman artist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F3A8", "html": "👩🏻‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🎨", "name": "woman artist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F3A8", "html": "👩🏼‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🎨", "name": "woman artist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F3A8", "html": "👩🏽‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🎨", "name": "woman artist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F3A8", "html": "👩🏾‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🎨", "name": "woman artist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F3A8", "html": "👩🏿‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍✈️", "name": "pilot", "shortname": ":pilot:", "unicode": "1F9D1 200D 2708 FE0F", "html": "🧑‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍✈", "name": "pilot", "shortname": ":pilot:", "unicode": "1F9D1 200D 2708", "html": "🧑‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍✈️", "name": "pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2708 FE0F", "html": "🧑🏻‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍✈", "name": "pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2708", "html": "🧑🏻‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍✈️", "name": "pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2708 FE0F", "html": "🧑🏼‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍✈", "name": "pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2708", "html": "🧑🏼‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍✈️", "name": "pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2708 FE0F", "html": "🧑🏽‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍✈", "name": "pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2708", "html": "🧑🏽‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍✈️", "name": "pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2708 FE0F", "html": "🧑🏾‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍✈", "name": "pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2708", "html": "🧑🏾‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍✈️", "name": "pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2708 FE0F", "html": "🧑🏿‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍✈", "name": "pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2708", "html": "🧑🏿‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍✈️", "name": "man pilot", "shortname": ":man_pilot:", "unicode": "1F468 200D 2708 FE0F", "html": "👨‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍✈", "name": "man pilot", "shortname": ":man_pilot:", "unicode": "1F468 200D 2708", "html": "👨‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍✈️", "name": "man pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2708 FE0F", "html": "👨🏻‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍✈", "name": "man pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2708", "html": "👨🏻‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍✈️", "name": "man pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2708 FE0F", "html": "👨🏼‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍✈", "name": "man pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2708", "html": "👨🏼‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍✈️", "name": "man pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2708 FE0F", "html": "👨🏽‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍✈", "name": "man pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2708", "html": "👨🏽‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍✈️", "name": "man pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2708 FE0F", "html": "👨🏾‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍✈", "name": "man pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2708", "html": "👨🏾‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍✈️", "name": "man pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2708 FE0F", "html": "👨🏿‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍✈", "name": "man pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2708", "html": "👨🏿‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍✈️", "name": "woman pilot", "shortname": ":woman_pilot:", "unicode": "1F469 200D 2708 FE0F", "html": "👩‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍✈", "name": "woman pilot", "shortname": ":woman_pilot:", "unicode": "1F469 200D 2708", "html": "👩‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍✈️", "name": "woman pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2708 FE0F", "html": "👩🏻‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍✈", "name": "woman pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2708", "html": "👩🏻‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍✈️", "name": "woman pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2708 FE0F", "html": "👩🏼‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍✈", "name": "woman pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2708", "html": "👩🏼‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍✈️", "name": "woman pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2708 FE0F", "html": "👩🏽‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍✈", "name": "woman pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2708", "html": "👩🏽‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍✈️", "name": "woman pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2708 FE0F", "html": "👩🏾‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍✈", "name": "woman pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2708", "html": "👩🏾‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍✈️", "name": "woman pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2708 FE0F", "html": "👩🏿‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍✈", "name": "woman pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2708", "html": "👩🏿‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🚀", "name": "astronaut", "shortname": ":astronaut:", "unicode": "1F9D1 200D 1F680", "html": "🧑‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🚀", "name": "astronaut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F680", "html": "🧑🏻‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🚀", "name": "astronaut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F680", "html": "🧑🏼‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🚀", "name": "astronaut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F680", "html": "🧑🏽‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🚀", "name": "astronaut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F680", "html": "🧑🏾‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🚀", "name": "astronaut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F680", "html": "🧑🏿‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🚀", "name": "man astronaut", "shortname": ":man_astronaut:", "unicode": "1F468 200D 1F680", "html": "👨‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🚀", "name": "man astronaut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F680", "html": "👨🏻‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🚀", "name": "man astronaut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F680", "html": "👨🏼‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🚀", "name": "man astronaut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F680", "html": "👨🏽‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🚀", "name": "man astronaut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F680", "html": "👨🏾‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🚀", "name": "man astronaut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F680", "html": "👨🏿‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🚀", "name": "woman astronaut", "shortname": ":woman_astronaut:", "unicode": "1F469 200D 1F680", "html": "👩‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🚀", "name": "woman astronaut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F680", "html": "👩🏻‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🚀", "name": "woman astronaut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F680", "html": "👩🏼‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🚀", "name": "woman astronaut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F680", "html": "👩🏽‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🚀", "name": "woman astronaut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F680", "html": "👩🏾‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🚀", "name": "woman astronaut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F680", "html": "👩🏿‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🚒", "name": "firefighter", "shortname": ":firefighter:", "unicode": "1F9D1 200D 1F692", "html": "🧑‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🚒", "name": "firefighter: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F692", "html": "🧑🏻‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🚒", "name": "firefighter: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F692", "html": "🧑🏼‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🚒", "name": "firefighter: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F692", "html": "🧑🏽‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🚒", "name": "firefighter: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F692", "html": "🧑🏾‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🚒", "name": "firefighter: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F692", "html": "🧑🏿‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🚒", "name": "man firefighter", "shortname": ":man_firefighter:", "unicode": "1F468 200D 1F692", "html": "👨‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🚒", "name": "man firefighter: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F692", "html": "👨🏻‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🚒", "name": "man firefighter: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F692", "html": "👨🏼‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🚒", "name": "man firefighter: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F692", "html": "👨🏽‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🚒", "name": "man firefighter: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F692", "html": "👨🏾‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🚒", "name": "man firefighter: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F692", "html": "👨🏿‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🚒", "name": "woman firefighter", "shortname": ":woman_firefighter:", "unicode": "1F469 200D 1F692", "html": "👩‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🚒", "name": "woman firefighter: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F692", "html": "👩🏻‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🚒", "name": "woman firefighter: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F692", "html": "👩🏼‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🚒", "name": "woman firefighter: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F692", "html": "👩🏽‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🚒", "name": "woman firefighter: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F692", "html": "👩🏾‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🚒", "name": "woman firefighter: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F692", "html": "👩🏿‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮‍♂️", "name": "man police officer", "shortname": ":man_police_officer:", "unicode": "1F46E 200D 2642 FE0F", "html": "👮‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮‍♂", "name": "man police officer", "shortname": ":man_police_officer:", "unicode": "1F46E 200D 2642", "html": "👮‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻‍♂️", "name": "man police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB 200D 2642 FE0F", "html": "👮🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻‍♂", "name": "man police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB 200D 2642", "html": "👮🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼‍♂️", "name": "man police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC 200D 2642 FE0F", "html": "👮🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼‍♂", "name": "man police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC 200D 2642", "html": "👮🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽‍♂️", "name": "man police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD 200D 2642 FE0F", "html": "👮🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽‍♂", "name": "man police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD 200D 2642", "html": "👮🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾‍♂️", "name": "man police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE 200D 2642 FE0F", "html": "👮🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾‍♂", "name": "man police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE 200D 2642", "html": "👮🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏿‍♂️", "name": "man police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF 200D 2642 FE0F", "html": "👮🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏿‍♂", "name": "man police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF 200D 2642", "html": "👮🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮‍♀️", "name": "woman police officer", "shortname": ":woman_police_officer:", "unicode": "1F46E 200D 2640 FE0F", "html": "👮‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻‍♀️", "name": "woman police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB 200D 2640 FE0F", "html": "👮🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼‍♀️", "name": "woman police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC 200D 2640 FE0F", "html": "👮🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽‍♀️", "name": "woman police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD 200D 2640 FE0F", "html": "👮🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾‍♀️", "name": "woman police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE 200D 2640 FE0F", "html": "👮🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏿‍♀️", "name": "woman police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF 200D 2640 FE0F", "html": "👮🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️", "name": "detective", "shortname": ":detective:", "unicode": "1F575 FE0F", "html": "🕵️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️‍♂️", "name": "man detective", "shortname": ":man_detective:", "unicode": "1F575 FE0F 200D 2642 FE0F", "html": "🕵️‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵‍♂️", "name": "man detective", "shortname": ":man_detective:", "unicode": "1F575 200D 2642 FE0F", "html": "🕵‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️‍♂", "name": "man detective", "shortname": ":man_detective:", "unicode": "1F575 FE0F 200D 2642", "html": "🕵️‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵‍♂", "name": "man detective", "shortname": ":man_detective:", "unicode": "1F575 200D 2642", "html": "🕵‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻‍♂️", "name": "man detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB 200D 2642 FE0F", "html": "🕵🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻‍♂", "name": "man detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB 200D 2642", "html": "🕵🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼‍♂️", "name": "man detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC 200D 2642 FE0F", "html": "🕵🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼‍♂", "name": "man detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC 200D 2642", "html": "🕵🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽‍♂️", "name": "man detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD 200D 2642 FE0F", "html": "🕵🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽‍♂", "name": "man detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD 200D 2642", "html": "🕵🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾‍♂️", "name": "man detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE 200D 2642 FE0F", "html": "🕵🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾‍♂", "name": "man detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE 200D 2642", "html": "🕵🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏿‍♂️", "name": "man detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF 200D 2642 FE0F", "html": "🕵🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏿‍♂", "name": "man detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF 200D 2642", "html": "🕵🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️‍♀️", "name": "woman detective", "shortname": ":woman_detective:", "unicode": "1F575 FE0F 200D 2640 FE0F", "html": "🕵️‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵‍♀️", "name": "woman detective", "shortname": ":woman_detective:", "unicode": "1F575 200D 2640 FE0F", "html": "🕵‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️‍♀", "name": "woman detective", "shortname": ":woman_detective:", "unicode": "1F575 FE0F 200D 2640", "html": "🕵️‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻‍♀️", "name": "woman detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB 200D 2640 FE0F", "html": "🕵🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼‍♀️", "name": "woman detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC 200D 2640 FE0F", "html": "🕵🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽‍♀️", "name": "woman detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD 200D 2640 FE0F", "html": "🕵🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾‍♀️", "name": "woman detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE 200D 2640 FE0F", "html": "🕵🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏿‍♀️", "name": "woman detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF 200D 2640 FE0F", "html": "🕵🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂‍♂️", "name": "man guard", "shortname": ":man_guard:", "unicode": "1F482 200D 2642 FE0F", "html": "💂‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂‍♂", "name": "man guard", "shortname": ":man_guard:", "unicode": "1F482 200D 2642", "html": "💂‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻‍♂️", "name": "man guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB 200D 2642 FE0F", "html": "💂🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻‍♂", "name": "man guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB 200D 2642", "html": "💂🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼‍♂️", "name": "man guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC 200D 2642 FE0F", "html": "💂🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼‍♂", "name": "man guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC 200D 2642", "html": "💂🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽‍♂️", "name": "man guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD 200D 2642 FE0F", "html": "💂🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽‍♂", "name": "man guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD 200D 2642", "html": "💂🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾‍♂️", "name": "man guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE 200D 2642 FE0F", "html": "💂🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾‍♂", "name": "man guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE 200D 2642", "html": "💂🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏿‍♂️", "name": "man guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF 200D 2642 FE0F", "html": "💂🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏿‍♂", "name": "man guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF 200D 2642", "html": "💂🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂‍♀️", "name": "woman guard", "shortname": ":woman_guard:", "unicode": "1F482 200D 2640 FE0F", "html": "💂‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻‍♀️", "name": "woman guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB 200D 2640 FE0F", "html": "💂🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼‍♀️", "name": "woman guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC 200D 2640 FE0F", "html": "💂🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽‍♀️", "name": "woman guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD 200D 2640 FE0F", "html": "💂🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾‍♀️", "name": "woman guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE 200D 2640 FE0F", "html": "💂🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏿‍♀️", "name": "woman guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF 200D 2640 FE0F", "html": "💂🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷‍♂️", "name": "man construction worker", "shortname": ":man_construction_worker:", "unicode": "1F477 200D 2642 FE0F", "html": "👷‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷‍♂", "name": "man construction worker", "shortname": ":man_construction_worker:", "unicode": "1F477 200D 2642", "html": "👷‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻‍♂️", "name": "man construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB 200D 2642 FE0F", "html": "👷🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻‍♂", "name": "man construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB 200D 2642", "html": "👷🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼‍♂️", "name": "man construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC 200D 2642 FE0F", "html": "👷🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼‍♂", "name": "man construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC 200D 2642", "html": "👷🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽‍♂️", "name": "man construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD 200D 2642 FE0F", "html": "👷🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽‍♂", "name": "man construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD 200D 2642", "html": "👷🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾‍♂️", "name": "man construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE 200D 2642 FE0F", "html": "👷🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾‍♂", "name": "man construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE 200D 2642", "html": "👷🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏿‍♂️", "name": "man construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF 200D 2642 FE0F", "html": "👷🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏿‍♂", "name": "man construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF 200D 2642", "html": "👷🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷‍♀️", "name": "woman construction worker", "shortname": ":woman_construction_worker:", "unicode": "1F477 200D 2640 FE0F", "html": "👷‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻‍♀️", "name": "woman construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB 200D 2640 FE0F", "html": "👷🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼‍♀️", "name": "woman construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC 200D 2640 FE0F", "html": "👷🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽‍♀️", "name": "woman construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD 200D 2640 FE0F", "html": "👷🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾‍♀️", "name": "woman construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE 200D 2640 FE0F", "html": "👷🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏿‍♀️", "name": "woman construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF 200D 2640 FE0F", "html": "👷🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴", "name": "prince", "shortname": ":prince:", "unicode": "1F934", "html": "🤴", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏻", "name": "prince: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F934 1F3FB", "html": "🤴🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏼", "name": "prince: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F934 1F3FC", "html": "🤴🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏽", "name": "prince: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F934 1F3FD", "html": "🤴🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏾", "name": "prince: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F934 1F3FE", "html": "🤴🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏿", "name": "prince: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F934 1F3FF", "html": "🤴🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳‍♂️", "name": "man wearing turban", "shortname": ":man_wearing_turban:", "unicode": "1F473 200D 2642 FE0F", "html": "👳‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳‍♂", "name": "man wearing turban", "shortname": ":man_wearing_turban:", "unicode": "1F473 200D 2642", "html": "👳‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻‍♂️", "name": "man wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB 200D 2642 FE0F", "html": "👳🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻‍♂", "name": "man wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB 200D 2642", "html": "👳🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼‍♂️", "name": "man wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC 200D 2642 FE0F", "html": "👳🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼‍♂", "name": "man wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC 200D 2642", "html": "👳🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽‍♂️", "name": "man wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD 200D 2642 FE0F", "html": "👳🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽‍♂", "name": "man wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD 200D 2642", "html": "👳🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾‍♂️", "name": "man wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE 200D 2642 FE0F", "html": "👳🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾‍♂", "name": "man wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE 200D 2642", "html": "👳🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏿‍♂️", "name": "man wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF 200D 2642 FE0F", "html": "👳🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏿‍♂", "name": "man wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF 200D 2642", "html": "👳🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳‍♀️", "name": "woman wearing turban", "shortname": ":woman_wearing_turban:", "unicode": "1F473 200D 2640 FE0F", "html": "👳‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻‍♀️", "name": "woman wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB 200D 2640 FE0F", "html": "👳🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼‍♀️", "name": "woman wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC 200D 2640 FE0F", "html": "👳🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽‍♀️", "name": "woman wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD 200D 2640 FE0F", "html": "👳🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾‍♀️", "name": "woman wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE 200D 2640 FE0F", "html": "👳🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏿‍♀️", "name": "woman wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF 200D 2640 FE0F", "html": "👳🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕", "name": "woman with headscarf", "shortname": ":woman_with_headscarf:", "unicode": "1F9D5", "html": "🧕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏻", "name": "woman with headscarf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D5 1F3FB", "html": "🧕🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏼", "name": "woman with headscarf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D5 1F3FC", "html": "🧕🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏽", "name": "woman with headscarf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D5 1F3FD", "html": "🧕🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏾", "name": "woman with headscarf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D5 1F3FE", "html": "🧕🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏿", "name": "woman with headscarf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D5 1F3FF", "html": "🧕🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵", "name": "man in tuxedo", "shortname": ":man_in_tuxedo:", "unicode": "1F935", "html": "🤵", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏻", "name": "man in tuxedo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F935 1F3FB", "html": "🤵🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏼", "name": "man in tuxedo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F935 1F3FC", "html": "🤵🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏽", "name": "man in tuxedo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F935 1F3FD", "html": "🤵🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏾", "name": "man in tuxedo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F935 1F3FE", "html": "🤵🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏿", "name": "man in tuxedo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F935 1F3FF", "html": "🤵🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰", "name": "pregnant woman", "shortname": ":pregnant_woman:", "unicode": "1F930", "html": "🤰", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏻", "name": "pregnant woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F930 1F3FB", "html": "🤰🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏼", "name": "pregnant woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F930 1F3FC", "html": "🤰🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏽", "name": "pregnant woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F930 1F3FD", "html": "🤰🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏾", "name": "pregnant woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F930 1F3FE", "html": "🤰🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏿", "name": "pregnant woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F930 1F3FF", "html": "🤰🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱", "name": "breast-feeding", "shortname": ":breastfeeding:", "unicode": "1F931", "html": "🤱", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏻", "name": "breast-feeding: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F931 1F3FB", "html": "🤱🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏼", "name": "breast-feeding: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F931 1F3FC", "html": "🤱🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏽", "name": "breast-feeding: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F931 1F3FD", "html": "🤱🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏾", "name": "breast-feeding: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F931 1F3FE", "html": "🤱🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏿", "name": "breast-feeding: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F931 1F3FF", "html": "🤱🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤶", "name": "Mrs. Claus", "shortname": ":Mrs_Claus:", "unicode": "1F936", "html": "🤶", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏻", "name": "Mrs. Claus: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F936 1F3FB", "html": "🤶🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏼", "name": "Mrs. Claus: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F936 1F3FC", "html": "🤶🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏽", "name": "Mrs. Claus: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F936 1F3FD", "html": "🤶🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏾", "name": "Mrs. Claus: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F936 1F3FE", "html": "🤶🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏿", "name": "Mrs. Claus: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F936 1F3FF", "html": "🤶🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸", "name": "superhero", "shortname": ":superhero:", "unicode": "1F9B8", "html": "🦸", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻", "name": "superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB", "html": "🦸🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼", "name": "superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC", "html": "🦸🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽", "name": "superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD", "html": "🦸🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾", "name": "superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE", "html": "🦸🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿", "name": "superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF", "html": "🦸🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸‍♂️", "name": "man superhero", "shortname": ":man_superhero:", "unicode": "1F9B8 200D 2642 FE0F", "html": "🦸‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸‍♂", "name": "man superhero", "shortname": ":man_superhero:", "unicode": "1F9B8 200D 2642", "html": "🦸‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻‍♂️", "name": "man superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB 200D 2642 FE0F", "html": "🦸🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻‍♂", "name": "man superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB 200D 2642", "html": "🦸🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼‍♂️", "name": "man superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC 200D 2642 FE0F", "html": "🦸🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼‍♂", "name": "man superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC 200D 2642", "html": "🦸🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽‍♂️", "name": "man superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD 200D 2642 FE0F", "html": "🦸🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽‍♂", "name": "man superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD 200D 2642", "html": "🦸🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾‍♂️", "name": "man superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE 200D 2642 FE0F", "html": "🦸🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾‍♂", "name": "man superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE 200D 2642", "html": "🦸🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿‍♂️", "name": "man superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF 200D 2642 FE0F", "html": "🦸🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿‍♂", "name": "man superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF 200D 2642", "html": "🦸🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸‍♀️", "name": "woman superhero", "shortname": ":woman_superhero:", "unicode": "1F9B8 200D 2640 FE0F", "html": "🦸‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸‍♀", "name": "woman superhero", "shortname": ":woman_superhero:", "unicode": "1F9B8 200D 2640", "html": "🦸‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻‍♀️", "name": "woman superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB 200D 2640 FE0F", "html": "🦸🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻‍♀", "name": "woman superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB 200D 2640", "html": "🦸🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼‍♀️", "name": "woman superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC 200D 2640 FE0F", "html": "🦸🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼‍♀", "name": "woman superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC 200D 2640", "html": "🦸🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽‍♀️", "name": "woman superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD 200D 2640 FE0F", "html": "🦸🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽‍♀", "name": "woman superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD 200D 2640", "html": "🦸🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾‍♀️", "name": "woman superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE 200D 2640 FE0F", "html": "🦸🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾‍♀", "name": "woman superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE 200D 2640", "html": "🦸🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿‍♀️", "name": "woman superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF 200D 2640 FE0F", "html": "🦸🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿‍♀", "name": "woman superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF 200D 2640", "html": "🦸🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹", "name": "supervillain", "shortname": ":supervillain:", "unicode": "1F9B9", "html": "🦹", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻", "name": "supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB", "html": "🦹🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼", "name": "supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC", "html": "🦹🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽", "name": "supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD", "html": "🦹🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾", "name": "supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE", "html": "🦹🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿", "name": "supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF", "html": "🦹🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹‍♂️", "name": "man supervillain", "shortname": ":man_supervillain:", "unicode": "1F9B9 200D 2642 FE0F", "html": "🦹‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹‍♂", "name": "man supervillain", "shortname": ":man_supervillain:", "unicode": "1F9B9 200D 2642", "html": "🦹‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻‍♂️", "name": "man supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB 200D 2642 FE0F", "html": "🦹🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻‍♂", "name": "man supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB 200D 2642", "html": "🦹🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼‍♂️", "name": "man supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC 200D 2642 FE0F", "html": "🦹🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼‍♂", "name": "man supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC 200D 2642", "html": "🦹🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽‍♂️", "name": "man supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD 200D 2642 FE0F", "html": "🦹🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽‍♂", "name": "man supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD 200D 2642", "html": "🦹🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾‍♂️", "name": "man supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE 200D 2642 FE0F", "html": "🦹🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾‍♂", "name": "man supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE 200D 2642", "html": "🦹🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿‍♂️", "name": "man supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF 200D 2642 FE0F", "html": "🦹🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿‍♂", "name": "man supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF 200D 2642", "html": "🦹🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹‍♀️", "name": "woman supervillain", "shortname": ":woman_supervillain:", "unicode": "1F9B9 200D 2640 FE0F", "html": "🦹‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹‍♀", "name": "woman supervillain", "shortname": ":woman_supervillain:", "unicode": "1F9B9 200D 2640", "html": "🦹‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻‍♀️", "name": "woman supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB 200D 2640 FE0F", "html": "🦹🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻‍♀", "name": "woman supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB 200D 2640", "html": "🦹🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼‍♀️", "name": "woman supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC 200D 2640 FE0F", "html": "🦹🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼‍♀", "name": "woman supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC 200D 2640", "html": "🦹🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽‍♀️", "name": "woman supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD 200D 2640 FE0F", "html": "🦹🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽‍♀", "name": "woman supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD 200D 2640", "html": "🦹🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾‍♀️", "name": "woman supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE 200D 2640 FE0F", "html": "🦹🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾‍♀", "name": "woman supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE 200D 2640", "html": "🦹🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿‍♀️", "name": "woman supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF 200D 2640 FE0F", "html": "🦹🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿‍♀", "name": "woman supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF 200D 2640", "html": "🦹🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙", "name": "mage", "shortname": ":mage:", "unicode": "1F9D9", "html": "🧙", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻", "name": "mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB", "html": "🧙🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼", "name": "mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC", "html": "🧙🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽", "name": "mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD", "html": "🧙🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾", "name": "mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE", "html": "🧙🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿", "name": "mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF", "html": "🧙🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙‍♂️", "name": "man mage", "shortname": ":man_mage:", "unicode": "1F9D9 200D 2642 FE0F", "html": "🧙‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙‍♂", "name": "man mage", "shortname": ":man_mage:", "unicode": "1F9D9 200D 2642", "html": "🧙‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻‍♂️", "name": "man mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB 200D 2642 FE0F", "html": "🧙🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻‍♂", "name": "man mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB 200D 2642", "html": "🧙🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼‍♂️", "name": "man mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC 200D 2642 FE0F", "html": "🧙🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼‍♂", "name": "man mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC 200D 2642", "html": "🧙🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽‍♂️", "name": "man mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD 200D 2642 FE0F", "html": "🧙🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽‍♂", "name": "man mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD 200D 2642", "html": "🧙🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾‍♂️", "name": "man mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE 200D 2642 FE0F", "html": "🧙🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾‍♂", "name": "man mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE 200D 2642", "html": "🧙🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿‍♂️", "name": "man mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF 200D 2642 FE0F", "html": "🧙🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿‍♂", "name": "man mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF 200D 2642", "html": "🧙🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙‍♀️", "name": "woman mage", "shortname": ":woman_mage:", "unicode": "1F9D9 200D 2640 FE0F", "html": "🧙‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙‍♀", "name": "woman mage", "shortname": ":woman_mage:", "unicode": "1F9D9 200D 2640", "html": "🧙‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻‍♀️", "name": "woman mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB 200D 2640 FE0F", "html": "🧙🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻‍♀", "name": "woman mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB 200D 2640", "html": "🧙🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼‍♀️", "name": "woman mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC 200D 2640 FE0F", "html": "🧙🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼‍♀", "name": "woman mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC 200D 2640", "html": "🧙🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽‍♀️", "name": "woman mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD 200D 2640 FE0F", "html": "🧙🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽‍♀", "name": "woman mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD 200D 2640", "html": "🧙🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾‍♀️", "name": "woman mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE 200D 2640 FE0F", "html": "🧙🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾‍♀", "name": "woman mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE 200D 2640", "html": "🧙🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿‍♀️", "name": "woman mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF 200D 2640 FE0F", "html": "🧙🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿‍♀", "name": "woman mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF 200D 2640", "html": "🧙🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚", "name": "fairy", "shortname": ":fairy:", "unicode": "1F9DA", "html": "🧚", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻", "name": "fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB", "html": "🧚🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼", "name": "fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC", "html": "🧚🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽", "name": "fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD", "html": "🧚🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾", "name": "fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE", "html": "🧚🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿", "name": "fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF", "html": "🧚🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚‍♂️", "name": "man fairy", "shortname": ":man_fairy:", "unicode": "1F9DA 200D 2642 FE0F", "html": "🧚‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚‍♂", "name": "man fairy", "shortname": ":man_fairy:", "unicode": "1F9DA 200D 2642", "html": "🧚‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻‍♂️", "name": "man fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB 200D 2642 FE0F", "html": "🧚🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻‍♂", "name": "man fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB 200D 2642", "html": "🧚🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼‍♂️", "name": "man fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC 200D 2642 FE0F", "html": "🧚🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼‍♂", "name": "man fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC 200D 2642", "html": "🧚🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽‍♂️", "name": "man fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD 200D 2642 FE0F", "html": "🧚🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽‍♂", "name": "man fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD 200D 2642", "html": "🧚🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾‍♂️", "name": "man fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE 200D 2642 FE0F", "html": "🧚🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾‍♂", "name": "man fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE 200D 2642", "html": "🧚🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿‍♂️", "name": "man fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF 200D 2642 FE0F", "html": "🧚🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿‍♂", "name": "man fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF 200D 2642", "html": "🧚🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚‍♀️", "name": "woman fairy", "shortname": ":woman_fairy:", "unicode": "1F9DA 200D 2640 FE0F", "html": "🧚‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚‍♀", "name": "woman fairy", "shortname": ":woman_fairy:", "unicode": "1F9DA 200D 2640", "html": "🧚‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻‍♀️", "name": "woman fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB 200D 2640 FE0F", "html": "🧚🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻‍♀", "name": "woman fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB 200D 2640", "html": "🧚🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼‍♀️", "name": "woman fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC 200D 2640 FE0F", "html": "🧚🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼‍♀", "name": "woman fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC 200D 2640", "html": "🧚🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽‍♀️", "name": "woman fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD 200D 2640 FE0F", "html": "🧚🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽‍♀", "name": "woman fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD 200D 2640", "html": "🧚🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾‍♀️", "name": "woman fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE 200D 2640 FE0F", "html": "🧚🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾‍♀", "name": "woman fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE 200D 2640", "html": "🧚🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿‍♀️", "name": "woman fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF 200D 2640 FE0F", "html": "🧚🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿‍♀", "name": "woman fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF 200D 2640", "html": "🧚🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛", "name": "vampire", "shortname": ":vampire:", "unicode": "1F9DB", "html": "🧛", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻", "name": "vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB", "html": "🧛🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼", "name": "vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC", "html": "🧛🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽", "name": "vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD", "html": "🧛🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾", "name": "vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE", "html": "🧛🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿", "name": "vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF", "html": "🧛🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛‍♂️", "name": "man vampire", "shortname": ":man_vampire:", "unicode": "1F9DB 200D 2642 FE0F", "html": "🧛‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛‍♂", "name": "man vampire", "shortname": ":man_vampire:", "unicode": "1F9DB 200D 2642", "html": "🧛‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻‍♂️", "name": "man vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB 200D 2642 FE0F", "html": "🧛🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻‍♂", "name": "man vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB 200D 2642", "html": "🧛🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼‍♂️", "name": "man vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC 200D 2642 FE0F", "html": "🧛🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼‍♂", "name": "man vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC 200D 2642", "html": "🧛🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽‍♂️", "name": "man vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD 200D 2642 FE0F", "html": "🧛🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽‍♂", "name": "man vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD 200D 2642", "html": "🧛🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾‍♂️", "name": "man vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE 200D 2642 FE0F", "html": "🧛🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾‍♂", "name": "man vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE 200D 2642", "html": "🧛🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿‍♂️", "name": "man vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF 200D 2642 FE0F", "html": "🧛🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿‍♂", "name": "man vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF 200D 2642", "html": "🧛🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛‍♀️", "name": "woman vampire", "shortname": ":woman_vampire:", "unicode": "1F9DB 200D 2640 FE0F", "html": "🧛‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛‍♀", "name": "woman vampire", "shortname": ":woman_vampire:", "unicode": "1F9DB 200D 2640", "html": "🧛‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻‍♀️", "name": "woman vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB 200D 2640 FE0F", "html": "🧛🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻‍♀", "name": "woman vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB 200D 2640", "html": "🧛🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼‍♀️", "name": "woman vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC 200D 2640 FE0F", "html": "🧛🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼‍♀", "name": "woman vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC 200D 2640", "html": "🧛🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽‍♀️", "name": "woman vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD 200D 2640 FE0F", "html": "🧛🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽‍♀", "name": "woman vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD 200D 2640", "html": "🧛🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾‍♀️", "name": "woman vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE 200D 2640 FE0F", "html": "🧛🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾‍♀", "name": "woman vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE 200D 2640", "html": "🧛🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿‍♀️", "name": "woman vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF 200D 2640 FE0F", "html": "🧛🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿‍♀", "name": "woman vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF 200D 2640", "html": "🧛🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜", "name": "merperson", "shortname": ":merperson:", "unicode": "1F9DC", "html": "🧜", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻", "name": "merperson: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB", "html": "🧜🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼", "name": "merperson: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC", "html": "🧜🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽", "name": "merperson: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD", "html": "🧜🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾", "name": "merperson: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE", "html": "🧜🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿", "name": "merperson: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF", "html": "🧜🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜‍♂️", "name": "merman", "shortname": ":merman:", "unicode": "1F9DC 200D 2642 FE0F", "html": "🧜‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜‍♂", "name": "merman", "shortname": ":merman:", "unicode": "1F9DC 200D 2642", "html": "🧜‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻‍♂️", "name": "merman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB 200D 2642 FE0F", "html": "🧜🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻‍♂", "name": "merman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB 200D 2642", "html": "🧜🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼‍♂️", "name": "merman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC 200D 2642 FE0F", "html": "🧜🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼‍♂", "name": "merman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC 200D 2642", "html": "🧜🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽‍♂️", "name": "merman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD 200D 2642 FE0F", "html": "🧜🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽‍♂", "name": "merman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD 200D 2642", "html": "🧜🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾‍♂️", "name": "merman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE 200D 2642 FE0F", "html": "🧜🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾‍♂", "name": "merman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE 200D 2642", "html": "🧜🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿‍♂️", "name": "merman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF 200D 2642 FE0F", "html": "🧜🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿‍♂", "name": "merman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF 200D 2642", "html": "🧜🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜‍♀️", "name": "mermaid", "shortname": ":mermaid:", "unicode": "1F9DC 200D 2640 FE0F", "html": "🧜‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜‍♀", "name": "mermaid", "shortname": ":mermaid:", "unicode": "1F9DC 200D 2640", "html": "🧜‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻‍♀️", "name": "mermaid: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB 200D 2640 FE0F", "html": "🧜🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻‍♀", "name": "mermaid: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB 200D 2640", "html": "🧜🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼‍♀️", "name": "mermaid: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC 200D 2640 FE0F", "html": "🧜🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼‍♀", "name": "mermaid: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC 200D 2640", "html": "🧜🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽‍♀️", "name": "mermaid: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD 200D 2640 FE0F", "html": "🧜🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽‍♀", "name": "mermaid: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD 200D 2640", "html": "🧜🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾‍♀️", "name": "mermaid: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE 200D 2640 FE0F", "html": "🧜🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾‍♀", "name": "mermaid: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE 200D 2640", "html": "🧜🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿‍♀️", "name": "mermaid: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF 200D 2640 FE0F", "html": "🧜🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿‍♀", "name": "mermaid: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF 200D 2640", "html": "🧜🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝", "name": "elf", "shortname": ":elf:", "unicode": "1F9DD", "html": "🧝", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻", "name": "elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB", "html": "🧝🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼", "name": "elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC", "html": "🧝🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽", "name": "elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD", "html": "🧝🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾", "name": "elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE", "html": "🧝🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿", "name": "elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF", "html": "🧝🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝‍♂️", "name": "man elf", "shortname": ":man_elf:", "unicode": "1F9DD 200D 2642 FE0F", "html": "🧝‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝‍♂", "name": "man elf", "shortname": ":man_elf:", "unicode": "1F9DD 200D 2642", "html": "🧝‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻‍♂️", "name": "man elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB 200D 2642 FE0F", "html": "🧝🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻‍♂", "name": "man elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB 200D 2642", "html": "🧝🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼‍♂️", "name": "man elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC 200D 2642 FE0F", "html": "🧝🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼‍♂", "name": "man elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC 200D 2642", "html": "🧝🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽‍♂️", "name": "man elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD 200D 2642 FE0F", "html": "🧝🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽‍♂", "name": "man elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD 200D 2642", "html": "🧝🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾‍♂️", "name": "man elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE 200D 2642 FE0F", "html": "🧝🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾‍♂", "name": "man elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE 200D 2642", "html": "🧝🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿‍♂️", "name": "man elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF 200D 2642 FE0F", "html": "🧝🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿‍♂", "name": "man elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF 200D 2642", "html": "🧝🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝‍♀️", "name": "woman elf", "shortname": ":woman_elf:", "unicode": "1F9DD 200D 2640 FE0F", "html": "🧝‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝‍♀", "name": "woman elf", "shortname": ":woman_elf:", "unicode": "1F9DD 200D 2640", "html": "🧝‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻‍♀️", "name": "woman elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB 200D 2640 FE0F", "html": "🧝🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻‍♀", "name": "woman elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB 200D 2640", "html": "🧝🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼‍♀️", "name": "woman elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC 200D 2640 FE0F", "html": "🧝🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼‍♀", "name": "woman elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC 200D 2640", "html": "🧝🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽‍♀️", "name": "woman elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD 200D 2640 FE0F", "html": "🧝🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽‍♀", "name": "woman elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD 200D 2640", "html": "🧝🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾‍♀️", "name": "woman elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE 200D 2640 FE0F", "html": "🧝🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾‍♀", "name": "woman elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE 200D 2640", "html": "🧝🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿‍♀️", "name": "woman elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF 200D 2640 FE0F", "html": "🧝🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿‍♀", "name": "woman elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF 200D 2640", "html": "🧝🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞", "name": "genie", "shortname": ":genie:", "unicode": "1F9DE", "html": "🧞", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞‍♂️", "name": "man genie", "shortname": ":man_genie:", "unicode": "1F9DE 200D 2642 FE0F", "html": "🧞‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞‍♂", "name": "man genie", "shortname": ":man_genie:", "unicode": "1F9DE 200D 2642", "html": "🧞‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞‍♀️", "name": "woman genie", "shortname": ":woman_genie:", "unicode": "1F9DE 200D 2640 FE0F", "html": "🧞‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞‍♀", "name": "woman genie", "shortname": ":woman_genie:", "unicode": "1F9DE 200D 2640", "html": "🧞‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟", "name": "zombie", "shortname": ":zombie:", "unicode": "1F9DF", "html": "🧟", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟‍♂️", "name": "man zombie", "shortname": ":man_zombie:", "unicode": "1F9DF 200D 2642 FE0F", "html": "🧟‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟‍♂", "name": "man zombie", "shortname": ":man_zombie:", "unicode": "1F9DF 200D 2642", "html": "🧟‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟‍♀️", "name": "woman zombie", "shortname": ":woman_zombie:", "unicode": "1F9DF 200D 2640 FE0F", "html": "🧟‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟‍♀", "name": "woman zombie", "shortname": ":woman_zombie:", "unicode": "1F9DF 200D 2640", "html": "🧟‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "💆‍♂️", "name": "man getting massage", "shortname": ":man_getting_massage:", "unicode": "1F486 200D 2642 FE0F", "html": "💆‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻‍♂️", "name": "man getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB 200D 2642 FE0F", "html": "💆🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼‍♂️", "name": "man getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC 200D 2642 FE0F", "html": "💆🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽‍♂️", "name": "man getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD 200D 2642 FE0F", "html": "💆🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾‍♂️", "name": "man getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE 200D 2642 FE0F", "html": "💆🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏿‍♂️", "name": "man getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF 200D 2642 FE0F", "html": "💆🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆‍♀️", "name": "woman getting massage", "shortname": ":woman_getting_massage:", "unicode": "1F486 200D 2640 FE0F", "html": "💆‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆‍♀", "name": "woman getting massage", "shortname": ":woman_getting_massage:", "unicode": "1F486 200D 2640", "html": "💆‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻‍♀️", "name": "woman getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB 200D 2640 FE0F", "html": "💆🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻‍♀", "name": "woman getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB 200D 2640", "html": "💆🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼‍♀️", "name": "woman getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC 200D 2640 FE0F", "html": "💆🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼‍♀", "name": "woman getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC 200D 2640", "html": "💆🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽‍♀️", "name": "woman getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD 200D 2640 FE0F", "html": "💆🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽‍♀", "name": "woman getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD 200D 2640", "html": "💆🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾‍♀️", "name": "woman getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE 200D 2640 FE0F", "html": "💆🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾‍♀", "name": "woman getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE 200D 2640", "html": "💆🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏿‍♀️", "name": "woman getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF 200D 2640 FE0F", "html": "💆🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏿‍♀", "name": "woman getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF 200D 2640", "html": "💆🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇‍♂️", "name": "man getting haircut", "shortname": ":man_getting_haircut:", "unicode": "1F487 200D 2642 FE0F", "html": "💇‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻‍♂️", "name": "man getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB 200D 2642 FE0F", "html": "💇🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼‍♂️", "name": "man getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC 200D 2642 FE0F", "html": "💇🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽‍♂️", "name": "man getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD 200D 2642 FE0F", "html": "💇🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾‍♂️", "name": "man getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE 200D 2642 FE0F", "html": "💇🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏿‍♂️", "name": "man getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF 200D 2642 FE0F", "html": "💇🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇‍♀️", "name": "woman getting haircut", "shortname": ":woman_getting_haircut:", "unicode": "1F487 200D 2640 FE0F", "html": "💇‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇‍♀", "name": "woman getting haircut", "shortname": ":woman_getting_haircut:", "unicode": "1F487 200D 2640", "html": "💇‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻‍♀️", "name": "woman getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB 200D 2640 FE0F", "html": "💇🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻‍♀", "name": "woman getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB 200D 2640", "html": "💇🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼‍♀️", "name": "woman getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC 200D 2640 FE0F", "html": "💇🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼‍♀", "name": "woman getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC 200D 2640", "html": "💇🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽‍♀️", "name": "woman getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD 200D 2640 FE0F", "html": "💇🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽‍♀", "name": "woman getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD 200D 2640", "html": "💇🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾‍♀️", "name": "woman getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE 200D 2640 FE0F", "html": "💇🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾‍♀", "name": "woman getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE 200D 2640", "html": "💇🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏿‍♀️", "name": "woman getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF 200D 2640 FE0F", "html": "💇🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏿‍♀", "name": "woman getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF 200D 2640", "html": "💇🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶‍♂️", "name": "man walking", "shortname": ":man_walking:", "unicode": "1F6B6 200D 2642 FE0F", "html": "🚶‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶‍♂", "name": "man walking", "shortname": ":man_walking:", "unicode": "1F6B6 200D 2642", "html": "🚶‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻‍♂️", "name": "man walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB 200D 2642 FE0F", "html": "🚶🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻‍♂", "name": "man walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB 200D 2642", "html": "🚶🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼‍♂️", "name": "man walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC 200D 2642 FE0F", "html": "🚶🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼‍♂", "name": "man walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC 200D 2642", "html": "🚶🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽‍♂️", "name": "man walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD 200D 2642 FE0F", "html": "🚶🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽‍♂", "name": "man walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD 200D 2642", "html": "🚶🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾‍♂️", "name": "man walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE 200D 2642 FE0F", "html": "🚶🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾‍♂", "name": "man walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE 200D 2642", "html": "🚶🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏿‍♂️", "name": "man walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF 200D 2642 FE0F", "html": "🚶🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏿‍♂", "name": "man walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF 200D 2642", "html": "🚶🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶‍♀️", "name": "woman walking", "shortname": ":woman_walking:", "unicode": "1F6B6 200D 2640 FE0F", "html": "🚶‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻‍♀️", "name": "woman walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB 200D 2640 FE0F", "html": "🚶🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼‍♀️", "name": "woman walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC 200D 2640 FE0F", "html": "🚶🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽‍♀️", "name": "woman walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD 200D 2640 FE0F", "html": "🚶🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾‍♀️", "name": "woman walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE 200D 2640 FE0F", "html": "🚶🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏿‍♀️", "name": "woman walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF 200D 2640 FE0F", "html": "🚶🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍", "name": "person standing", "shortname": ":person_standing:", "unicode": "1F9CD", "html": "🧍", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻", "name": "person standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB", "html": "🧍🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼", "name": "person standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC", "html": "🧍🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽", "name": "person standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD", "html": "🧍🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾", "name": "person standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE", "html": "🧍🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿", "name": "person standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF", "html": "🧍🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍‍♂️", "name": "man standing", "shortname": ":man_standing:", "unicode": "1F9CD 200D 2642 FE0F", "html": "🧍‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍‍♂", "name": "man standing", "shortname": ":man_standing:", "unicode": "1F9CD 200D 2642", "html": "🧍‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻‍♂️", "name": "man standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB 200D 2642 FE0F", "html": "🧍🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻‍♂", "name": "man standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB 200D 2642", "html": "🧍🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼‍♂️", "name": "man standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC 200D 2642 FE0F", "html": "🧍🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼‍♂", "name": "man standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC 200D 2642", "html": "🧍🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽‍♂️", "name": "man standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD 200D 2642 FE0F", "html": "🧍🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽‍♂", "name": "man standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD 200D 2642", "html": "🧍🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾‍♂️", "name": "man standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE 200D 2642 FE0F", "html": "🧍🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾‍♂", "name": "man standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE 200D 2642", "html": "🧍🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿‍♂️", "name": "man standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF 200D 2642 FE0F", "html": "🧍🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿‍♂", "name": "man standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF 200D 2642", "html": "🧍🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍‍♀️", "name": "woman standing", "shortname": ":woman_standing:", "unicode": "1F9CD 200D 2640 FE0F", "html": "🧍‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍‍♀", "name": "woman standing", "shortname": ":woman_standing:", "unicode": "1F9CD 200D 2640", "html": "🧍‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻‍♀️", "name": "woman standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB 200D 2640 FE0F", "html": "🧍🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻‍♀", "name": "woman standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB 200D 2640", "html": "🧍🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼‍♀️", "name": "woman standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC 200D 2640 FE0F", "html": "🧍🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼‍♀", "name": "woman standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC 200D 2640", "html": "🧍🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽‍♀️", "name": "woman standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD 200D 2640 FE0F", "html": "🧍🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽‍♀", "name": "woman standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD 200D 2640", "html": "🧍🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾‍♀️", "name": "woman standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE 200D 2640 FE0F", "html": "🧍🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾‍♀", "name": "woman standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE 200D 2640", "html": "🧍🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿‍♀️", "name": "woman standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF 200D 2640 FE0F", "html": "🧍🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿‍♀", "name": "woman standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF 200D 2640", "html": "🧍🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎", "name": "person kneeling", "shortname": ":person_kneeling:", "unicode": "1F9CE", "html": "🧎", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻", "name": "person kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB", "html": "🧎🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼", "name": "person kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC", "html": "🧎🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽", "name": "person kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD", "html": "🧎🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾", "name": "person kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE", "html": "🧎🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿", "name": "person kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF", "html": "🧎🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎‍♂️", "name": "man kneeling", "shortname": ":man_kneeling:", "unicode": "1F9CE 200D 2642 FE0F", "html": "🧎‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎‍♂", "name": "man kneeling", "shortname": ":man_kneeling:", "unicode": "1F9CE 200D 2642", "html": "🧎‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻‍♂️", "name": "man kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB 200D 2642 FE0F", "html": "🧎🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻‍♂", "name": "man kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB 200D 2642", "html": "🧎🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼‍♂️", "name": "man kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC 200D 2642 FE0F", "html": "🧎🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼‍♂", "name": "man kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC 200D 2642", "html": "🧎🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽‍♂️", "name": "man kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD 200D 2642 FE0F", "html": "🧎🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽‍♂", "name": "man kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD 200D 2642", "html": "🧎🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾‍♂️", "name": "man kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE 200D 2642 FE0F", "html": "🧎🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾‍♂", "name": "man kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE 200D 2642", "html": "🧎🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿‍♂️", "name": "man kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF 200D 2642 FE0F", "html": "🧎🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿‍♂", "name": "man kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF 200D 2642", "html": "🧎🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎‍♀️", "name": "woman kneeling", "shortname": ":woman_kneeling:", "unicode": "1F9CE 200D 2640 FE0F", "html": "🧎‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎‍♀", "name": "woman kneeling", "shortname": ":woman_kneeling:", "unicode": "1F9CE 200D 2640", "html": "🧎‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻‍♀️", "name": "woman kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB 200D 2640 FE0F", "html": "🧎🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻‍♀", "name": "woman kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB 200D 2640", "html": "🧎🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼‍♀️", "name": "woman kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC 200D 2640 FE0F", "html": "🧎🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼‍♀", "name": "woman kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC 200D 2640", "html": "🧎🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽‍♀️", "name": "woman kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD 200D 2640 FE0F", "html": "🧎🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽‍♀", "name": "woman kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD 200D 2640", "html": "🧎🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾‍♀️", "name": "woman kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE 200D 2640 FE0F", "html": "🧎🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾‍♀", "name": "woman kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE 200D 2640", "html": "🧎🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿‍♀️", "name": "woman kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF 200D 2640 FE0F", "html": "🧎🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿‍♀", "name": "woman kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF 200D 2640", "html": "🧎🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑‍🦯", "name": "person with probing cane", "shortname": ":person_with_probing_cane:", "unicode": "1F9D1 200D 1F9AF", "html": "🧑‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏻‍🦯", "name": "person with probing cane: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F9AF", "html": "🧑🏻‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏼‍🦯", "name": "person with probing cane: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F9AF", "html": "🧑🏼‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏽‍🦯", "name": "person with probing cane: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F9AF", "html": "🧑🏽‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏾‍🦯", "name": "person with probing cane: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F9AF", "html": "🧑🏾‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏿‍🦯", "name": "person with probing cane: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F9AF", "html": "🧑🏿‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨‍🦯", "name": "man with probing cane", "shortname": ":man_with_probing_cane:", "unicode": "1F468 200D 1F9AF", "html": "👨‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏻‍🦯", "name": "man with probing cane: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F9AF", "html": "👨🏻‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏼‍🦯", "name": "man with probing cane: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F9AF", "html": "👨🏼‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏽‍🦯", "name": "man with probing cane: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F9AF", "html": "👨🏽‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏾‍🦯", "name": "man with probing cane: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F9AF", "html": "👨🏾‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏿‍🦯", "name": "man with probing cane: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F9AF", "html": "👨🏿‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩‍🦯", "name": "woman with probing cane", "shortname": ":woman_with_probing_cane:", "unicode": "1F469 200D 1F9AF", "html": "👩‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏻‍🦯", "name": "woman with probing cane: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F9AF", "html": "👩🏻‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏼‍🦯", "name": "woman with probing cane: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F9AF", "html": "👩🏼‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏽‍🦯", "name": "woman with probing cane: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F9AF", "html": "👩🏽‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏾‍🦯", "name": "woman with probing cane: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F9AF", "html": "👩🏾‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏿‍🦯", "name": "woman with probing cane: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F9AF", "html": "👩🏿‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑‍🦼", "name": "person in motorized wheelchair", "shortname": ":person_in_motorized_wheelchair:", "unicode": "1F9D1 200D 1F9BC", "html": "🧑‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏻‍🦼", "name": "person in motorized wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F9BC", "html": "🧑🏻‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏼‍🦼", "name": "person in motorized wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F9BC", "html": "🧑🏼‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏽‍🦼", "name": "person in motorized wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F9BC", "html": "🧑🏽‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏾‍🦼", "name": "person in motorized wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F9BC", "html": "🧑🏾‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏿‍🦼", "name": "person in motorized wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F9BC", "html": "🧑🏿‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨‍🦼", "name": "man in motorized wheelchair", "shortname": ":man_in_motorized_wheelchair:", "unicode": "1F468 200D 1F9BC", "html": "👨‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏻‍🦼", "name": "man in motorized wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F9BC", "html": "👨🏻‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏼‍🦼", "name": "man in motorized wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F9BC", "html": "👨🏼‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏽‍🦼", "name": "man in motorized wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F9BC", "html": "👨🏽‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏾‍🦼", "name": "man in motorized wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F9BC", "html": "👨🏾‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏿‍🦼", "name": "man in motorized wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F9BC", "html": "👨🏿‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩‍🦼", "name": "woman in motorized wheelchair", "shortname": ":woman_in_motorized_wheelchair:", "unicode": "1F469 200D 1F9BC", "html": "👩‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏻‍🦼", "name": "woman in motorized wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F9BC", "html": "👩🏻‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏼‍🦼", "name": "woman in motorized wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F9BC", "html": "👩🏼‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏽‍🦼", "name": "woman in motorized wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F9BC", "html": "👩🏽‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏾‍🦼", "name": "woman in motorized wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F9BC", "html": "👩🏾‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏿‍🦼", "name": "woman in motorized wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F9BC", "html": "👩🏿‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑‍🦽", "name": "person in manual wheelchair", "shortname": ":person_in_manual_wheelchair:", "unicode": "1F9D1 200D 1F9BD", "html": "🧑‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏻‍🦽", "name": "person in manual wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F9BD", "html": "🧑🏻‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏼‍🦽", "name": "person in manual wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F9BD", "html": "🧑🏼‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏽‍🦽", "name": "person in manual wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F9BD", "html": "🧑🏽‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏾‍🦽", "name": "person in manual wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F9BD", "html": "🧑🏾‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏿‍🦽", "name": "person in manual wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F9BD", "html": "🧑🏿‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨‍🦽", "name": "man in manual wheelchair", "shortname": ":man_in_manual_wheelchair:", "unicode": "1F468 200D 1F9BD", "html": "👨‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏻‍🦽", "name": "man in manual wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F9BD", "html": "👨🏻‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏼‍🦽", "name": "man in manual wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F9BD", "html": "👨🏼‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏽‍🦽", "name": "man in manual wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F9BD", "html": "👨🏽‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏾‍🦽", "name": "man in manual wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F9BD", "html": "👨🏾‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏿‍🦽", "name": "man in manual wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F9BD", "html": "👨🏿‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩‍🦽", "name": "woman in manual wheelchair", "shortname": ":woman_in_manual_wheelchair:", "unicode": "1F469 200D 1F9BD", "html": "👩‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏻‍🦽", "name": "woman in manual wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F9BD", "html": "👩🏻‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏼‍🦽", "name": "woman in manual wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F9BD", "html": "👩🏼‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏽‍🦽", "name": "woman in manual wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F9BD", "html": "👩🏽‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏾‍🦽", "name": "woman in manual wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F9BD", "html": "👩🏾‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏿‍🦽", "name": "woman in manual wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F9BD", "html": "👩🏿‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃‍♂️", "name": "man running", "shortname": ":man_running:", "unicode": "1F3C3 200D 2642 FE0F", "html": "🏃‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃‍♂", "name": "man running", "shortname": ":man_running:", "unicode": "1F3C3 200D 2642", "html": "🏃‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻‍♂️", "name": "man running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB 200D 2642 FE0F", "html": "🏃🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻‍♂", "name": "man running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB 200D 2642", "html": "🏃🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼‍♂️", "name": "man running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC 200D 2642 FE0F", "html": "🏃🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼‍♂", "name": "man running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC 200D 2642", "html": "🏃🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽‍♂️", "name": "man running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD 200D 2642 FE0F", "html": "🏃🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽‍♂", "name": "man running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD 200D 2642", "html": "🏃🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾‍♂️", "name": "man running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE 200D 2642 FE0F", "html": "🏃🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾‍♂", "name": "man running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE 200D 2642", "html": "🏃🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏿‍♂️", "name": "man running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF 200D 2642 FE0F", "html": "🏃🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏿‍♂", "name": "man running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF 200D 2642", "html": "🏃🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃‍♀️", "name": "woman running", "shortname": ":woman_running:", "unicode": "1F3C3 200D 2640 FE0F", "html": "🏃‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻‍♀️", "name": "woman running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB 200D 2640 FE0F", "html": "🏃🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼‍♀️", "name": "woman running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC 200D 2640 FE0F", "html": "🏃🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽‍♀️", "name": "woman running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD 200D 2640 FE0F", "html": "🏃🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾‍♀️", "name": "woman running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE 200D 2640 FE0F", "html": "🏃🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏿‍♀️", "name": "woman running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF 200D 2640 FE0F", "html": "🏃🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺", "name": "man dancing", "shortname": ":man_dancing:", "unicode": "1F57A", "html": "🕺", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏻", "name": "man dancing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F57A 1F3FB", "html": "🕺🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏼", "name": "man dancing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F57A 1F3FC", "html": "🕺🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏽", "name": "man dancing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F57A 1F3FD", "html": "🕺🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏾", "name": "man dancing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F57A 1F3FE", "html": "🕺🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏿", "name": "man dancing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F57A 1F3FF", "html": "🕺🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴️", "name": "man in suit levitating", "shortname": ":man_in_suit_levitating:", "unicode": "1F574 FE0F", "html": "🕴️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏻", "name": "man in suit levitating: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F574 1F3FB", "html": "🕴🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏼", "name": "man in suit levitating: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F574 1F3FC", "html": "🕴🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏽", "name": "man in suit levitating: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F574 1F3FD", "html": "🕴🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏾", "name": "man in suit levitating: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F574 1F3FE", "html": "🕴🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏿", "name": "man in suit levitating: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F574 1F3FF", "html": "🕴🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👯‍♂️", "name": "men with bunny ears", "shortname": ":men_with_bunny_ears:", "unicode": "1F46F 200D 2642 FE0F", "html": "👯‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👯‍♀️", "name": "women with bunny ears", "shortname": ":women_with_bunny_ears:", "unicode": "1F46F 200D 2640 FE0F", "html": "👯‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👯‍♀", "name": "women with bunny ears", "shortname": ":women_with_bunny_ears:", "unicode": "1F46F 200D 2640", "html": "👯‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖", "name": "person in steamy room", "shortname": ":person_in_steamy_room:", "unicode": "1F9D6", "html": "🧖", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻", "name": "person in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB", "html": "🧖🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼", "name": "person in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC", "html": "🧖🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽", "name": "person in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD", "html": "🧖🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾", "name": "person in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE", "html": "🧖🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿", "name": "person in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF", "html": "🧖🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖‍♂️", "name": "man in steamy room", "shortname": ":man_in_steamy_room:", "unicode": "1F9D6 200D 2642 FE0F", "html": "🧖‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖‍♂", "name": "man in steamy room", "shortname": ":man_in_steamy_room:", "unicode": "1F9D6 200D 2642", "html": "🧖‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻‍♂️", "name": "man in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB 200D 2642 FE0F", "html": "🧖🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻‍♂", "name": "man in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB 200D 2642", "html": "🧖🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼‍♂️", "name": "man in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC 200D 2642 FE0F", "html": "🧖🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼‍♂", "name": "man in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC 200D 2642", "html": "🧖🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽‍♂️", "name": "man in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD 200D 2642 FE0F", "html": "🧖🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽‍♂", "name": "man in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD 200D 2642", "html": "🧖🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾‍♂️", "name": "man in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE 200D 2642 FE0F", "html": "🧖🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾‍♂", "name": "man in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE 200D 2642", "html": "🧖🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿‍♂️", "name": "man in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF 200D 2642 FE0F", "html": "🧖🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿‍♂", "name": "man in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF 200D 2642", "html": "🧖🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖‍♀️", "name": "woman in steamy room", "shortname": ":woman_in_steamy_room:", "unicode": "1F9D6 200D 2640 FE0F", "html": "🧖‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖‍♀", "name": "woman in steamy room", "shortname": ":woman_in_steamy_room:", "unicode": "1F9D6 200D 2640", "html": "🧖‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻‍♀️", "name": "woman in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB 200D 2640 FE0F", "html": "🧖🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻‍♀", "name": "woman in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB 200D 2640", "html": "🧖🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼‍♀️", "name": "woman in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC 200D 2640 FE0F", "html": "🧖🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼‍♀", "name": "woman in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC 200D 2640", "html": "🧖🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽‍♀️", "name": "woman in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD 200D 2640 FE0F", "html": "🧖🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽‍♀", "name": "woman in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD 200D 2640", "html": "🧖🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾‍♀️", "name": "woman in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE 200D 2640 FE0F", "html": "🧖🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾‍♀", "name": "woman in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE 200D 2640", "html": "🧖🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿‍♀️", "name": "woman in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF 200D 2640 FE0F", "html": "🧖🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿‍♀", "name": "woman in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF 200D 2640", "html": "🧖🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗", "name": "person climbing", "shortname": ":person_climbing:", "unicode": "1F9D7", "html": "🧗", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻", "name": "person climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB", "html": "🧗🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼", "name": "person climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC", "html": "🧗🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽", "name": "person climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD", "html": "🧗🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾", "name": "person climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE", "html": "🧗🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿", "name": "person climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF", "html": "🧗🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗‍♂️", "name": "man climbing", "shortname": ":man_climbing:", "unicode": "1F9D7 200D 2642 FE0F", "html": "🧗‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗‍♂", "name": "man climbing", "shortname": ":man_climbing:", "unicode": "1F9D7 200D 2642", "html": "🧗‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻‍♂️", "name": "man climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB 200D 2642 FE0F", "html": "🧗🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻‍♂", "name": "man climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB 200D 2642", "html": "🧗🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼‍♂️", "name": "man climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC 200D 2642 FE0F", "html": "🧗🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼‍♂", "name": "man climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC 200D 2642", "html": "🧗🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽‍♂️", "name": "man climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD 200D 2642 FE0F", "html": "🧗🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽‍♂", "name": "man climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD 200D 2642", "html": "🧗🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾‍♂️", "name": "man climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE 200D 2642 FE0F", "html": "🧗🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾‍♂", "name": "man climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE 200D 2642", "html": "🧗🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿‍♂️", "name": "man climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF 200D 2642 FE0F", "html": "🧗🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿‍♂", "name": "man climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF 200D 2642", "html": "🧗🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗‍♀️", "name": "woman climbing", "shortname": ":woman_climbing:", "unicode": "1F9D7 200D 2640 FE0F", "html": "🧗‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗‍♀", "name": "woman climbing", "shortname": ":woman_climbing:", "unicode": "1F9D7 200D 2640", "html": "🧗‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻‍♀️", "name": "woman climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB 200D 2640 FE0F", "html": "🧗🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻‍♀", "name": "woman climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB 200D 2640", "html": "🧗🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼‍♀️", "name": "woman climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC 200D 2640 FE0F", "html": "🧗🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼‍♀", "name": "woman climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC 200D 2640", "html": "🧗🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽‍♀️", "name": "woman climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD 200D 2640 FE0F", "html": "🧗🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽‍♀", "name": "woman climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD 200D 2640", "html": "🧗🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾‍♀️", "name": "woman climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE 200D 2640 FE0F", "html": "🧗🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾‍♀", "name": "woman climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE 200D 2640", "html": "🧗🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿‍♀️", "name": "woman climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF 200D 2640 FE0F", "html": "🧗🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿‍♀", "name": "woman climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF 200D 2640", "html": "🧗🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🤺", "name": "person fencing", "shortname": ":person_fencing:", "unicode": "1F93A", "html": "🤺", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏻", "name": "horse racing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C7 1F3FB", "html": "🏇🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏼", "name": "horse racing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C7 1F3FC", "html": "🏇🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏽", "name": "horse racing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C7 1F3FD", "html": "🏇🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏾", "name": "horse racing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C7 1F3FE", "html": "🏇🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏿", "name": "horse racing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C7 1F3FF", "html": "🏇🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛷️", "name": "skier", "shortname": ":skier:", "unicode": "26F7 FE0F", "html": "⛷️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏻", "name": "snowboarder: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C2 1F3FB", "html": "🏂🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏼", "name": "snowboarder: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C2 1F3FC", "html": "🏂🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏽", "name": "snowboarder: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C2 1F3FD", "html": "🏂🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏾", "name": "snowboarder: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C2 1F3FE", "html": "🏂🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏿", "name": "snowboarder: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C2 1F3FF", "html": "🏂🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️", "name": "person golfing", "shortname": ":person_golfing:", "unicode": "1F3CC FE0F", "html": "🏌️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻", "name": "person golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB", "html": "🏌🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼", "name": "person golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC", "html": "🏌🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽", "name": "person golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD", "html": "🏌🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾", "name": "person golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE", "html": "🏌🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿", "name": "person golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF", "html": "🏌🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️‍♂️", "name": "man golfing", "shortname": ":man_golfing:", "unicode": "1F3CC FE0F 200D 2642 FE0F", "html": "🏌️‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌‍♂️", "name": "man golfing", "shortname": ":man_golfing:", "unicode": "1F3CC 200D 2642 FE0F", "html": "🏌‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️‍♂", "name": "man golfing", "shortname": ":man_golfing:", "unicode": "1F3CC FE0F 200D 2642", "html": "🏌️‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌‍♂", "name": "man golfing", "shortname": ":man_golfing:", "unicode": "1F3CC 200D 2642", "html": "🏌‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻‍♂️", "name": "man golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB 200D 2642 FE0F", "html": "🏌🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻‍♂", "name": "man golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB 200D 2642", "html": "🏌🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼‍♂️", "name": "man golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC 200D 2642 FE0F", "html": "🏌🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼‍♂", "name": "man golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC 200D 2642", "html": "🏌🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽‍♂️", "name": "man golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD 200D 2642 FE0F", "html": "🏌🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽‍♂", "name": "man golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD 200D 2642", "html": "🏌🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾‍♂️", "name": "man golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE 200D 2642 FE0F", "html": "🏌🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾‍♂", "name": "man golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE 200D 2642", "html": "🏌🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿‍♂️", "name": "man golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF 200D 2642 FE0F", "html": "🏌🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿‍♂", "name": "man golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF 200D 2642", "html": "🏌🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️‍♀️", "name": "woman golfing", "shortname": ":woman_golfing:", "unicode": "1F3CC FE0F 200D 2640 FE0F", "html": "🏌️‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌‍♀️", "name": "woman golfing", "shortname": ":woman_golfing:", "unicode": "1F3CC 200D 2640 FE0F", "html": "🏌‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️‍♀", "name": "woman golfing", "shortname": ":woman_golfing:", "unicode": "1F3CC FE0F 200D 2640", "html": "🏌️‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻‍♀️", "name": "woman golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB 200D 2640 FE0F", "html": "🏌🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻‍♀", "name": "woman golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB 200D 2640", "html": "🏌🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼‍♀️", "name": "woman golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC 200D 2640 FE0F", "html": "🏌🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼‍♀", "name": "woman golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC 200D 2640", "html": "🏌🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽‍♀️", "name": "woman golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD 200D 2640 FE0F", "html": "🏌🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽‍♀", "name": "woman golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD 200D 2640", "html": "🏌🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾‍♀️", "name": "woman golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE 200D 2640 FE0F", "html": "🏌🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾‍♀", "name": "woman golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE 200D 2640", "html": "🏌🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿‍♀️", "name": "woman golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF 200D 2640 FE0F", "html": "🏌🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿‍♀", "name": "woman golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF 200D 2640", "html": "🏌🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄‍♂️", "name": "man surfing", "shortname": ":man_surfing:", "unicode": "1F3C4 200D 2642 FE0F", "html": "🏄‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄‍♂", "name": "man surfing", "shortname": ":man_surfing:", "unicode": "1F3C4 200D 2642", "html": "🏄‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻‍♂️", "name": "man surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB 200D 2642 FE0F", "html": "🏄🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻‍♂", "name": "man surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB 200D 2642", "html": "🏄🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼‍♂️", "name": "man surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC 200D 2642 FE0F", "html": "🏄🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼‍♂", "name": "man surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC 200D 2642", "html": "🏄🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽‍♂️", "name": "man surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD 200D 2642 FE0F", "html": "🏄🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽‍♂", "name": "man surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD 200D 2642", "html": "🏄🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾‍♂️", "name": "man surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE 200D 2642 FE0F", "html": "🏄🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾‍♂", "name": "man surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE 200D 2642", "html": "🏄🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏿‍♂️", "name": "man surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF 200D 2642 FE0F", "html": "🏄🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏿‍♂", "name": "man surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF 200D 2642", "html": "🏄🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄‍♀️", "name": "woman surfing", "shortname": ":woman_surfing:", "unicode": "1F3C4 200D 2640 FE0F", "html": "🏄‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻‍♀️", "name": "woman surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB 200D 2640 FE0F", "html": "🏄🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼‍♀️", "name": "woman surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC 200D 2640 FE0F", "html": "🏄🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽‍♀️", "name": "woman surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD 200D 2640 FE0F", "html": "🏄🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾‍♀️", "name": "woman surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE 200D 2640 FE0F", "html": "🏄🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏿‍♀️", "name": "woman surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF 200D 2640 FE0F", "html": "🏄🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣‍♂️", "name": "man rowing boat", "shortname": ":man_rowing_boat:", "unicode": "1F6A3 200D 2642 FE0F", "html": "🚣‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣‍♂", "name": "man rowing boat", "shortname": ":man_rowing_boat:", "unicode": "1F6A3 200D 2642", "html": "🚣‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻‍♂️", "name": "man rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB 200D 2642 FE0F", "html": "🚣🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻‍♂", "name": "man rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB 200D 2642", "html": "🚣🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼‍♂️", "name": "man rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC 200D 2642 FE0F", "html": "🚣🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼‍♂", "name": "man rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC 200D 2642", "html": "🚣🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽‍♂️", "name": "man rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD 200D 2642 FE0F", "html": "🚣🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽‍♂", "name": "man rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD 200D 2642", "html": "🚣🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾‍♂️", "name": "man rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE 200D 2642 FE0F", "html": "🚣🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾‍♂", "name": "man rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE 200D 2642", "html": "🚣🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏿‍♂️", "name": "man rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF 200D 2642 FE0F", "html": "🚣🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏿‍♂", "name": "man rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF 200D 2642", "html": "🚣🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣‍♀️", "name": "woman rowing boat", "shortname": ":woman_rowing_boat:", "unicode": "1F6A3 200D 2640 FE0F", "html": "🚣‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻‍♀️", "name": "woman rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB 200D 2640 FE0F", "html": "🚣🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼‍♀️", "name": "woman rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC 200D 2640 FE0F", "html": "🚣🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽‍♀️", "name": "woman rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD 200D 2640 FE0F", "html": "🚣🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾‍♀️", "name": "woman rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE 200D 2640 FE0F", "html": "🚣🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏿‍♀️", "name": "woman rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF 200D 2640 FE0F", "html": "🚣🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊‍♂️", "name": "man swimming", "shortname": ":man_swimming:", "unicode": "1F3CA 200D 2642 FE0F", "html": "🏊‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊‍♂", "name": "man swimming", "shortname": ":man_swimming:", "unicode": "1F3CA 200D 2642", "html": "🏊‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻‍♂️", "name": "man swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB 200D 2642 FE0F", "html": "🏊🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻‍♂", "name": "man swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB 200D 2642", "html": "🏊🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼‍♂️", "name": "man swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC 200D 2642 FE0F", "html": "🏊🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼‍♂", "name": "man swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC 200D 2642", "html": "🏊🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽‍♂️", "name": "man swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD 200D 2642 FE0F", "html": "🏊🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽‍♂", "name": "man swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD 200D 2642", "html": "🏊🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾‍♂️", "name": "man swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE 200D 2642 FE0F", "html": "🏊🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾‍♂", "name": "man swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE 200D 2642", "html": "🏊🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏿‍♂️", "name": "man swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF 200D 2642 FE0F", "html": "🏊🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏿‍♂", "name": "man swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF 200D 2642", "html": "🏊🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊‍♀️", "name": "woman swimming", "shortname": ":woman_swimming:", "unicode": "1F3CA 200D 2640 FE0F", "html": "🏊‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻‍♀️", "name": "woman swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB 200D 2640 FE0F", "html": "🏊🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼‍♀️", "name": "woman swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC 200D 2640 FE0F", "html": "🏊🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽‍♀️", "name": "woman swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD 200D 2640 FE0F", "html": "🏊🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾‍♀️", "name": "woman swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE 200D 2640 FE0F", "html": "🏊🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏿‍♀️", "name": "woman swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF 200D 2640 FE0F", "html": "🏊🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️", "name": "person bouncing ball", "shortname": ":person_bouncing_ball:", "unicode": "26F9 FE0F", "html": "⛹️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️‍♂️", "name": "man bouncing ball", "shortname": ":man_bouncing_ball:", "unicode": "26F9 FE0F 200D 2642 FE0F", "html": "⛹️‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹‍♂️", "name": "man bouncing ball", "shortname": ":man_bouncing_ball:", "unicode": "26F9 200D 2642 FE0F", "html": "⛹‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️‍♂", "name": "man bouncing ball", "shortname": ":man_bouncing_ball:", "unicode": "26F9 FE0F 200D 2642", "html": "⛹️‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹‍♂", "name": "man bouncing ball", "shortname": ":man_bouncing_ball:", "unicode": "26F9 200D 2642", "html": "⛹‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻‍♂️", "name": "man bouncing ball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "26F9 1F3FB 200D 2642 FE0F", "html": "⛹🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻‍♂", "name": "man bouncing ball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "26F9 1F3FB 200D 2642", "html": "⛹🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼‍♂️", "name": "man bouncing ball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "26F9 1F3FC 200D 2642 FE0F", "html": "⛹🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼‍♂", "name": "man bouncing ball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "26F9 1F3FC 200D 2642", "html": "⛹🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽‍♂️", "name": "man bouncing ball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "26F9 1F3FD 200D 2642 FE0F", "html": "⛹🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽‍♂", "name": "man bouncing ball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "26F9 1F3FD 200D 2642", "html": "⛹🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾‍♂️", "name": "man bouncing ball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "26F9 1F3FE 200D 2642 FE0F", "html": "⛹🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾‍♂", "name": "man bouncing ball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "26F9 1F3FE 200D 2642", "html": "⛹🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿‍♂️", "name": "man bouncing ball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "26F9 1F3FF 200D 2642 FE0F", "html": "⛹🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿‍♂", "name": "man bouncing ball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "26F9 1F3FF 200D 2642", "html": "⛹🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️‍♀️", "name": "woman bouncing ball", "shortname": ":woman_bouncing_ball:", "unicode": "26F9 FE0F 200D 2640 FE0F", "html": "⛹️‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹‍♀️", "name": "woman bouncing ball", "shortname": ":woman_bouncing_ball:", "unicode": "26F9 200D 2640 FE0F", "html": "⛹‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️‍♀", "name": "woman bouncing ball", "shortname": ":woman_bouncing_ball:", "unicode": "26F9 FE0F 200D 2640", "html": "⛹️‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻‍♀️", "name": "woman bouncing ball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "26F9 1F3FB 200D 2640 FE0F", "html": "⛹🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼‍♀️", "name": "woman bouncing ball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "26F9 1F3FC 200D 2640 FE0F", "html": "⛹🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽‍♀️", "name": "woman bouncing ball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "26F9 1F3FD 200D 2640 FE0F", "html": "⛹🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾‍♀️", "name": "woman bouncing ball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "26F9 1F3FE 200D 2640 FE0F", "html": "⛹🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿‍♀️", "name": "woman bouncing ball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "26F9 1F3FF 200D 2640 FE0F", "html": "⛹🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️", "name": "person lifting weights", "shortname": ":person_lifting_weights:", "unicode": "1F3CB FE0F", "html": "🏋️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️‍♂️", "name": "man lifting weights", "shortname": ":man_lifting_weights:", "unicode": "1F3CB FE0F 200D 2642 FE0F", "html": "🏋️‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋‍♂️", "name": "man lifting weights", "shortname": ":man_lifting_weights:", "unicode": "1F3CB 200D 2642 FE0F", "html": "🏋‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️‍♂", "name": "man lifting weights", "shortname": ":man_lifting_weights:", "unicode": "1F3CB FE0F 200D 2642", "html": "🏋️‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋‍♂", "name": "man lifting weights", "shortname": ":man_lifting_weights:", "unicode": "1F3CB 200D 2642", "html": "🏋‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻‍♂️", "name": "man lifting weights: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CB 1F3FB 200D 2642 FE0F", "html": "🏋🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻‍♂", "name": "man lifting weights: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CB 1F3FB 200D 2642", "html": "🏋🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼‍♂️", "name": "man lifting weights: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CB 1F3FC 200D 2642 FE0F", "html": "🏋🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼‍♂", "name": "man lifting weights: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CB 1F3FC 200D 2642", "html": "🏋🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽‍♂️", "name": "man lifting weights: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CB 1F3FD 200D 2642 FE0F", "html": "🏋🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽‍♂", "name": "man lifting weights: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CB 1F3FD 200D 2642", "html": "🏋🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾‍♂️", "name": "man lifting weights: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CB 1F3FE 200D 2642 FE0F", "html": "🏋🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾‍♂", "name": "man lifting weights: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CB 1F3FE 200D 2642", "html": "🏋🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏿‍♂️", "name": "man lifting weights: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CB 1F3FF 200D 2642 FE0F", "html": "🏋🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏿‍♂", "name": "man lifting weights: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CB 1F3FF 200D 2642", "html": "🏋🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️‍♀️", "name": "woman lifting weights", "shortname": ":woman_lifting_weights:", "unicode": "1F3CB FE0F 200D 2640 FE0F", "html": "🏋️‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋‍♀️", "name": "woman lifting weights", "shortname": ":woman_lifting_weights:", "unicode": "1F3CB 200D 2640 FE0F", "html": "🏋‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️‍♀", "name": "woman lifting weights", "shortname": ":woman_lifting_weights:", "unicode": "1F3CB FE0F 200D 2640", "html": "🏋️‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻‍♀️", "name": "woman lifting weights: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CB 1F3FB 200D 2640 FE0F", "html": "🏋🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼‍♀️", "name": "woman lifting weights: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CB 1F3FC 200D 2640 FE0F", "html": "🏋🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽‍♀️", "name": "woman lifting weights: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CB 1F3FD 200D 2640 FE0F", "html": "🏋🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾‍♀️", "name": "woman lifting weights: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CB 1F3FE 200D 2640 FE0F", "html": "🏋🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏿‍♀️", "name": "woman lifting weights: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CB 1F3FF 200D 2640 FE0F", "html": "🏋🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴‍♂️", "name": "man biking", "shortname": ":man_biking:", "unicode": "1F6B4 200D 2642 FE0F", "html": "🚴‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴‍♂", "name": "man biking", "shortname": ":man_biking:", "unicode": "1F6B4 200D 2642", "html": "🚴‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻‍♂️", "name": "man biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB 200D 2642 FE0F", "html": "🚴🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻‍♂", "name": "man biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB 200D 2642", "html": "🚴🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼‍♂️", "name": "man biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC 200D 2642 FE0F", "html": "🚴🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼‍♂", "name": "man biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC 200D 2642", "html": "🚴🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽‍♂️", "name": "man biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD 200D 2642 FE0F", "html": "🚴🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽‍♂", "name": "man biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD 200D 2642", "html": "🚴🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾‍♂️", "name": "man biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE 200D 2642 FE0F", "html": "🚴🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾‍♂", "name": "man biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE 200D 2642", "html": "🚴🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏿‍♂️", "name": "man biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF 200D 2642 FE0F", "html": "🚴🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏿‍♂", "name": "man biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF 200D 2642", "html": "🚴🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴‍♀️", "name": "woman biking", "shortname": ":woman_biking:", "unicode": "1F6B4 200D 2640 FE0F", "html": "🚴‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻‍♀️", "name": "woman biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB 200D 2640 FE0F", "html": "🚴🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼‍♀️", "name": "woman biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC 200D 2640 FE0F", "html": "🚴🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽‍♀️", "name": "woman biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD 200D 2640 FE0F", "html": "🚴🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾‍♀️", "name": "woman biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE 200D 2640 FE0F", "html": "🚴🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏿‍♀️", "name": "woman biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF 200D 2640 FE0F", "html": "🚴🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵‍♂️", "name": "man mountain biking", "shortname": ":man_mountain_biking:", "unicode": "1F6B5 200D 2642 FE0F", "html": "🚵‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵‍♂", "name": "man mountain biking", "shortname": ":man_mountain_biking:", "unicode": "1F6B5 200D 2642", "html": "🚵‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻‍♂️", "name": "man mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB 200D 2642 FE0F", "html": "🚵🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻‍♂", "name": "man mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB 200D 2642", "html": "🚵🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼‍♂️", "name": "man mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC 200D 2642 FE0F", "html": "🚵🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼‍♂", "name": "man mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC 200D 2642", "html": "🚵🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽‍♂️", "name": "man mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD 200D 2642 FE0F", "html": "🚵🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽‍♂", "name": "man mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD 200D 2642", "html": "🚵🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾‍♂️", "name": "man mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE 200D 2642 FE0F", "html": "🚵🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾‍♂", "name": "man mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE 200D 2642", "html": "🚵🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏿‍♂️", "name": "man mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF 200D 2642 FE0F", "html": "🚵🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏿‍♂", "name": "man mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF 200D 2642", "html": "🚵🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵‍♀️", "name": "woman mountain biking", "shortname": ":woman_mountain_biking:", "unicode": "1F6B5 200D 2640 FE0F", "html": "🚵‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻‍♀️", "name": "woman mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB 200D 2640 FE0F", "html": "🚵🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼‍♀️", "name": "woman mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC 200D 2640 FE0F", "html": "🚵🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽‍♀️", "name": "woman mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD 200D 2640 FE0F", "html": "🚵🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾‍♀️", "name": "woman mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE 200D 2640 FE0F", "html": "🚵🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏿‍♀️", "name": "woman mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF 200D 2640 FE0F", "html": "🚵🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸", "name": "person cartwheeling", "shortname": ":person_cartwheeling:", "unicode": "1F938", "html": "🤸", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻", "name": "person cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB", "html": "🤸🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼", "name": "person cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC", "html": "🤸🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽", "name": "person cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD", "html": "🤸🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾", "name": "person cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE", "html": "🤸🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿", "name": "person cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF", "html": "🤸🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸‍♂️", "name": "man cartwheeling", "shortname": ":man_cartwheeling:", "unicode": "1F938 200D 2642 FE0F", "html": "🤸‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸‍♂", "name": "man cartwheeling", "shortname": ":man_cartwheeling:", "unicode": "1F938 200D 2642", "html": "🤸‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻‍♂️", "name": "man cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB 200D 2642 FE0F", "html": "🤸🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻‍♂", "name": "man cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB 200D 2642", "html": "🤸🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼‍♂️", "name": "man cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC 200D 2642 FE0F", "html": "🤸🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼‍♂", "name": "man cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC 200D 2642", "html": "🤸🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽‍♂️", "name": "man cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD 200D 2642 FE0F", "html": "🤸🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽‍♂", "name": "man cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD 200D 2642", "html": "🤸🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾‍♂️", "name": "man cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE 200D 2642 FE0F", "html": "🤸🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾‍♂", "name": "man cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE 200D 2642", "html": "🤸🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿‍♂️", "name": "man cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF 200D 2642 FE0F", "html": "🤸🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿‍♂", "name": "man cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF 200D 2642", "html": "🤸🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸‍♀️", "name": "woman cartwheeling", "shortname": ":woman_cartwheeling:", "unicode": "1F938 200D 2640 FE0F", "html": "🤸‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸‍♀", "name": "woman cartwheeling", "shortname": ":woman_cartwheeling:", "unicode": "1F938 200D 2640", "html": "🤸‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻‍♀️", "name": "woman cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB 200D 2640 FE0F", "html": "🤸🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻‍♀", "name": "woman cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB 200D 2640", "html": "🤸🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼‍♀️", "name": "woman cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC 200D 2640 FE0F", "html": "🤸🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼‍♀", "name": "woman cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC 200D 2640", "html": "🤸🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽‍♀️", "name": "woman cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD 200D 2640 FE0F", "html": "🤸🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽‍♀", "name": "woman cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD 200D 2640", "html": "🤸🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾‍♀️", "name": "woman cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE 200D 2640 FE0F", "html": "🤸🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾‍♀", "name": "woman cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE 200D 2640", "html": "🤸🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿‍♀️", "name": "woman cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF 200D 2640 FE0F", "html": "🤸🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿‍♀", "name": "woman cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF 200D 2640", "html": "🤸🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼", "name": "people wrestling", "shortname": ":people_wrestling:", "unicode": "1F93C", "html": "🤼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼‍♂️", "name": "men wrestling", "shortname": ":men_wrestling:", "unicode": "1F93C 200D 2642 FE0F", "html": "🤼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼‍♂", "name": "men wrestling", "shortname": ":men_wrestling:", "unicode": "1F93C 200D 2642", "html": "🤼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼‍♀️", "name": "women wrestling", "shortname": ":women_wrestling:", "unicode": "1F93C 200D 2640 FE0F", "html": "🤼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼‍♀", "name": "women wrestling", "shortname": ":women_wrestling:", "unicode": "1F93C 200D 2640", "html": "🤼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽", "name": "person playing water polo", "shortname": ":person_playing_water_polo:", "unicode": "1F93D", "html": "🤽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻", "name": "person playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB", "html": "🤽🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼", "name": "person playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC", "html": "🤽🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽", "name": "person playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD", "html": "🤽🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾", "name": "person playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE", "html": "🤽🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿", "name": "person playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF", "html": "🤽🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽‍♂️", "name": "man playing water polo", "shortname": ":man_playing_water_polo:", "unicode": "1F93D 200D 2642 FE0F", "html": "🤽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽‍♂", "name": "man playing water polo", "shortname": ":man_playing_water_polo:", "unicode": "1F93D 200D 2642", "html": "🤽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻‍♂️", "name": "man playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB 200D 2642 FE0F", "html": "🤽🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻‍♂", "name": "man playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB 200D 2642", "html": "🤽🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼‍♂️", "name": "man playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC 200D 2642 FE0F", "html": "🤽🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼‍♂", "name": "man playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC 200D 2642", "html": "🤽🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽‍♂️", "name": "man playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD 200D 2642 FE0F", "html": "🤽🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽‍♂", "name": "man playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD 200D 2642", "html": "🤽🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾‍♂️", "name": "man playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE 200D 2642 FE0F", "html": "🤽🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾‍♂", "name": "man playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE 200D 2642", "html": "🤽🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿‍♂️", "name": "man playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF 200D 2642 FE0F", "html": "🤽🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿‍♂", "name": "man playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF 200D 2642", "html": "🤽🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽‍♀️", "name": "woman playing water polo", "shortname": ":woman_playing_water_polo:", "unicode": "1F93D 200D 2640 FE0F", "html": "🤽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽‍♀", "name": "woman playing water polo", "shortname": ":woman_playing_water_polo:", "unicode": "1F93D 200D 2640", "html": "🤽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻‍♀️", "name": "woman playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB 200D 2640 FE0F", "html": "🤽🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻‍♀", "name": "woman playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB 200D 2640", "html": "🤽🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼‍♀️", "name": "woman playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC 200D 2640 FE0F", "html": "🤽🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼‍♀", "name": "woman playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC 200D 2640", "html": "🤽🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽‍♀️", "name": "woman playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD 200D 2640 FE0F", "html": "🤽🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽‍♀", "name": "woman playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD 200D 2640", "html": "🤽🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾‍♀️", "name": "woman playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE 200D 2640 FE0F", "html": "🤽🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾‍♀", "name": "woman playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE 200D 2640", "html": "🤽🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿‍♀️", "name": "woman playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF 200D 2640 FE0F", "html": "🤽🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿‍♀", "name": "woman playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF 200D 2640", "html": "🤽🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾", "name": "person playing handball", "shortname": ":person_playing_handball:", "unicode": "1F93E", "html": "🤾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻", "name": "person playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB", "html": "🤾🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼", "name": "person playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC", "html": "🤾🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽", "name": "person playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD", "html": "🤾🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾", "name": "person playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE", "html": "🤾🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿", "name": "person playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF", "html": "🤾🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾‍♂️", "name": "man playing handball", "shortname": ":man_playing_handball:", "unicode": "1F93E 200D 2642 FE0F", "html": "🤾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾‍♂", "name": "man playing handball", "shortname": ":man_playing_handball:", "unicode": "1F93E 200D 2642", "html": "🤾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻‍♂️", "name": "man playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB 200D 2642 FE0F", "html": "🤾🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻‍♂", "name": "man playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB 200D 2642", "html": "🤾🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼‍♂️", "name": "man playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC 200D 2642 FE0F", "html": "🤾🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼‍♂", "name": "man playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC 200D 2642", "html": "🤾🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽‍♂️", "name": "man playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD 200D 2642 FE0F", "html": "🤾🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽‍♂", "name": "man playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD 200D 2642", "html": "🤾🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾‍♂️", "name": "man playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE 200D 2642 FE0F", "html": "🤾🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾‍♂", "name": "man playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE 200D 2642", "html": "🤾🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿‍♂️", "name": "man playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF 200D 2642 FE0F", "html": "🤾🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿‍♂", "name": "man playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF 200D 2642", "html": "🤾🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾‍♀️", "name": "woman playing handball", "shortname": ":woman_playing_handball:", "unicode": "1F93E 200D 2640 FE0F", "html": "🤾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾‍♀", "name": "woman playing handball", "shortname": ":woman_playing_handball:", "unicode": "1F93E 200D 2640", "html": "🤾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻‍♀️", "name": "woman playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB 200D 2640 FE0F", "html": "🤾🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻‍♀", "name": "woman playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB 200D 2640", "html": "🤾🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼‍♀️", "name": "woman playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC 200D 2640 FE0F", "html": "🤾🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼‍♀", "name": "woman playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC 200D 2640", "html": "🤾🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽‍♀️", "name": "woman playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD 200D 2640 FE0F", "html": "🤾🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽‍♀", "name": "woman playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD 200D 2640", "html": "🤾🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾‍♀️", "name": "woman playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE 200D 2640 FE0F", "html": "🤾🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾‍♀", "name": "woman playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE 200D 2640", "html": "🤾🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿‍♀️", "name": "woman playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF 200D 2640 FE0F", "html": "🤾🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿‍♀", "name": "woman playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF 200D 2640", "html": "🤾🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹", "name": "person juggling", "shortname": ":person_juggling:", "unicode": "1F939", "html": "🤹", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻", "name": "person juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB", "html": "🤹🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼", "name": "person juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC", "html": "🤹🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽", "name": "person juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD", "html": "🤹🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾", "name": "person juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE", "html": "🤹🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿", "name": "person juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF", "html": "🤹🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹‍♂️", "name": "man juggling", "shortname": ":man_juggling:", "unicode": "1F939 200D 2642 FE0F", "html": "🤹‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹‍♂", "name": "man juggling", "shortname": ":man_juggling:", "unicode": "1F939 200D 2642", "html": "🤹‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻‍♂️", "name": "man juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB 200D 2642 FE0F", "html": "🤹🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻‍♂", "name": "man juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB 200D 2642", "html": "🤹🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼‍♂️", "name": "man juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC 200D 2642 FE0F", "html": "🤹🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼‍♂", "name": "man juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC 200D 2642", "html": "🤹🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽‍♂️", "name": "man juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD 200D 2642 FE0F", "html": "🤹🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽‍♂", "name": "man juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD 200D 2642", "html": "🤹🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾‍♂️", "name": "man juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE 200D 2642 FE0F", "html": "🤹🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾‍♂", "name": "man juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE 200D 2642", "html": "🤹🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿‍♂️", "name": "man juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF 200D 2642 FE0F", "html": "🤹🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿‍♂", "name": "man juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF 200D 2642", "html": "🤹🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹‍♀️", "name": "woman juggling", "shortname": ":woman_juggling:", "unicode": "1F939 200D 2640 FE0F", "html": "🤹‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹‍♀", "name": "woman juggling", "shortname": ":woman_juggling:", "unicode": "1F939 200D 2640", "html": "🤹‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻‍♀️", "name": "woman juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB 200D 2640 FE0F", "html": "🤹🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻‍♀", "name": "woman juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB 200D 2640", "html": "🤹🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼‍♀️", "name": "woman juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC 200D 2640 FE0F", "html": "🤹🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼‍♀", "name": "woman juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC 200D 2640", "html": "🤹🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽‍♀️", "name": "woman juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD 200D 2640 FE0F", "html": "🤹🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽‍♀", "name": "woman juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD 200D 2640", "html": "🤹🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾‍♀️", "name": "woman juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE 200D 2640 FE0F", "html": "🤹🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾‍♀", "name": "woman juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE 200D 2640", "html": "🤹🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿‍♀️", "name": "woman juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF 200D 2640 FE0F", "html": "🤹🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿‍♀", "name": "woman juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF 200D 2640", "html": "🤹🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🧘", "name": "person in lotus position", "shortname": ":person_in_lotus_position:", "unicode": "1F9D8", "html": "🧘", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻", "name": "person in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB", "html": "🧘🏻", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼", "name": "person in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC", "html": "🧘🏼", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽", "name": "person in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD", "html": "🧘🏽", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾", "name": "person in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE", "html": "🧘🏾", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿", "name": "person in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF", "html": "🧘🏿", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘‍♂️", "name": "man in lotus position", "shortname": ":man_in_lotus_position:", "unicode": "1F9D8 200D 2642 FE0F", "html": "🧘‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘‍♂", "name": "man in lotus position", "shortname": ":man_in_lotus_position:", "unicode": "1F9D8 200D 2642", "html": "🧘‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻‍♂️", "name": "man in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB 200D 2642 FE0F", "html": "🧘🏻‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻‍♂", "name": "man in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB 200D 2642", "html": "🧘🏻‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼‍♂️", "name": "man in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC 200D 2642 FE0F", "html": "🧘🏼‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼‍♂", "name": "man in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC 200D 2642", "html": "🧘🏼‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽‍♂️", "name": "man in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD 200D 2642 FE0F", "html": "🧘🏽‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽‍♂", "name": "man in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD 200D 2642", "html": "🧘🏽‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾‍♂️", "name": "man in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE 200D 2642 FE0F", "html": "🧘🏾‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾‍♂", "name": "man in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE 200D 2642", "html": "🧘🏾‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿‍♂️", "name": "man in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF 200D 2642 FE0F", "html": "🧘🏿‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿‍♂", "name": "man in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF 200D 2642", "html": "🧘🏿‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘‍♀️", "name": "woman in lotus position", "shortname": ":woman_in_lotus_position:", "unicode": "1F9D8 200D 2640 FE0F", "html": "🧘‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘‍♀", "name": "woman in lotus position", "shortname": ":woman_in_lotus_position:", "unicode": "1F9D8 200D 2640", "html": "🧘‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻‍♀️", "name": "woman in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB 200D 2640 FE0F", "html": "🧘🏻‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻‍♀", "name": "woman in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB 200D 2640", "html": "🧘🏻‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼‍♀️", "name": "woman in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC 200D 2640 FE0F", "html": "🧘🏼‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼‍♀", "name": "woman in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC 200D 2640", "html": "🧘🏼‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽‍♀️", "name": "woman in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD 200D 2640 FE0F", "html": "🧘🏽‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽‍♀", "name": "woman in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD 200D 2640", "html": "🧘🏽‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾‍♀️", "name": "woman in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE 200D 2640 FE0F", "html": "🧘🏾‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾‍♀", "name": "woman in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE 200D 2640", "html": "🧘🏾‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿‍♀️", "name": "woman in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF 200D 2640 FE0F", "html": "🧘🏿‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿‍♀", "name": "woman in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF 200D 2640", "html": "🧘🏿‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏻", "name": "person in bed: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6CC 1F3FB", "html": "🛌🏻", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏼", "name": "person in bed: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6CC 1F3FC", "html": "🛌🏼", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏽", "name": "person in bed: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6CC 1F3FD", "html": "🛌🏽", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏾", "name": "person in bed: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6CC 1F3FE", "html": "🛌🏾", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏿", "name": "person in bed: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6CC 1F3FF", "html": "🛌🏿", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧑‍🤝‍🧑", "name": "people holding hands", "shortname": ":people_holding_hands:", "unicode": "1F9D1 200D 1F91D 200D 1F9D1", "html": "🧑‍🤝‍🧑", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏻", "name": "people holding hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏻‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏼", "name": "people holding hands: light skin tone, medium-light skin tone", "shortname": ":light_skin_tone_mediumlight_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏻‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏽", "name": "people holding hands: light skin tone, medium skin tone", "shortname": ":light_skin_tone_medium_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏻‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏾", "name": "people holding hands: light skin tone, medium-dark skin tone", "shortname": ":light_skin_tone_mediumdark_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏻‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏿", "name": "people holding hands: light skin tone, dark skin tone", "shortname": ":light_skin_tone_dark_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏻‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏻", "name": "people holding hands: medium-light skin tone, light skin tone", "shortname": ":mediumlight_skin_tone_light_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏼‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏼", "name": "people holding hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏼‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏽", "name": "people holding hands: medium-light skin tone, medium skin tone", "shortname": ":mediumlight_skin_tone_medium_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏼‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏾", "name": "people holding hands: medium-light skin tone, medium-dark skin tone", "shortname": ":mediumlight_skin_tone_medium-dark_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏼‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏿", "name": "people holding hands: medium-light skin tone, dark skin tone", "shortname": ":mediumlight_skin_tone_dark_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏼‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏻", "name": "people holding hands: medium skin tone, light skin tone", "shortname": ":medium_skin_tone_light_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏽‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏼", "name": "people holding hands: medium skin tone, medium-light skin tone", "shortname": ":medium_skin_tone_mediumlight_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏽‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏽", "name": "people holding hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏽‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏾", "name": "people holding hands: medium skin tone, medium-dark skin tone", "shortname": ":medium_skin_tone_mediumdark_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏽‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏿", "name": "people holding hands: medium skin tone, dark skin tone", "shortname": ":medium_skin_tone_dark_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏽‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏻", "name": "people holding hands: medium-dark skin tone, light skin tone", "shortname": ":mediumdark_skin_tone_light_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏾‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏼", "name": "people holding hands: medium-dark skin tone, medium-light skin tone", "shortname": ":mediumdark_skin_tone_medium-light_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏾‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏽", "name": "people holding hands: medium-dark skin tone, medium skin tone", "shortname": ":mediumdark_skin_tone_medium_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏾‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏾", "name": "people holding hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏾‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏿", "name": "people holding hands: medium-dark skin tone, dark skin tone", "shortname": ":mediumdark_skin_tone_dark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏾‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏻", "name": "people holding hands: dark skin tone, light skin tone", "shortname": ":dark_skin_tone_light_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏿‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏼", "name": "people holding hands: dark skin tone, medium-light skin tone", "shortname": ":dark_skin_tone_mediumlight_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏿‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏽", "name": "people holding hands: dark skin tone, medium skin tone", "shortname": ":dark_skin_tone_medium_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏿‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏾", "name": "people holding hands: dark skin tone, medium-dark skin tone", "shortname": ":dark_skin_tone_mediumdark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏿‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏿", "name": "people holding hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏿‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏻", "name": "women holding hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46D 1F3FB", "html": "👭🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👩🏼", "name": "women holding hands: light skin tone, medium-light skin tone", "shortname": ":light_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F469 1F3FC", "html": "👩🏻‍🤝‍👩🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👩🏽", "name": "women holding hands: light skin tone, medium skin tone", "shortname": ":light_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F469 1F3FD", "html": "👩🏻‍🤝‍👩🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👩🏾", "name": "women holding hands: light skin tone, medium-dark skin tone", "shortname": ":light_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F469 1F3FE", "html": "👩🏻‍🤝‍👩🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👩🏿", "name": "women holding hands: light skin tone, dark skin tone", "shortname": ":light_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F469 1F3FF", "html": "👩🏻‍🤝‍👩🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👩🏻", "name": "women holding hands: medium-light skin tone, light skin tone", "shortname": ":mediumlight_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F469 1F3FB", "html": "👩🏼‍🤝‍👩🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏼", "name": "women holding hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46D 1F3FC", "html": "👭🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👩🏽", "name": "women holding hands: medium-light skin tone, medium skin tone", "shortname": ":mediumlight_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F469 1F3FD", "html": "👩🏼‍🤝‍👩🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👩🏾", "name": "women holding hands: medium-light skin tone, medium-dark skin tone", "shortname": ":mediumlight_skin_tone_medium-dark_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F469 1F3FE", "html": "👩🏼‍🤝‍👩🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👩🏿", "name": "women holding hands: medium-light skin tone, dark skin tone", "shortname": ":mediumlight_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F469 1F3FF", "html": "👩🏼‍🤝‍👩🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👩🏻", "name": "women holding hands: medium skin tone, light skin tone", "shortname": ":medium_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F469 1F3FB", "html": "👩🏽‍🤝‍👩🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👩🏼", "name": "women holding hands: medium skin tone, medium-light skin tone", "shortname": ":medium_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F469 1F3FC", "html": "👩🏽‍🤝‍👩🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏽", "name": "women holding hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46D 1F3FD", "html": "👭🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👩🏾", "name": "women holding hands: medium skin tone, medium-dark skin tone", "shortname": ":medium_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F469 1F3FE", "html": "👩🏽‍🤝‍👩🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👩🏿", "name": "women holding hands: medium skin tone, dark skin tone", "shortname": ":medium_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F469 1F3FF", "html": "👩🏽‍🤝‍👩🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👩🏻", "name": "women holding hands: medium-dark skin tone, light skin tone", "shortname": ":mediumdark_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F469 1F3FB", "html": "👩🏾‍🤝‍👩🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👩🏼", "name": "women holding hands: medium-dark skin tone, medium-light skin tone", "shortname": ":mediumdark_skin_tone_medium-light_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F469 1F3FC", "html": "👩🏾‍🤝‍👩🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👩🏽", "name": "women holding hands: medium-dark skin tone, medium skin tone", "shortname": ":mediumdark_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F469 1F3FD", "html": "👩🏾‍🤝‍👩🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏾", "name": "women holding hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46D 1F3FE", "html": "👭🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👩🏿", "name": "women holding hands: medium-dark skin tone, dark skin tone", "shortname": ":mediumdark_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F469 1F3FF", "html": "👩🏾‍🤝‍👩🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👩🏻", "name": "women holding hands: dark skin tone, light skin tone", "shortname": ":dark_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F469 1F3FB", "html": "👩🏿‍🤝‍👩🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👩🏼", "name": "women holding hands: dark skin tone, medium-light skin tone", "shortname": ":dark_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F469 1F3FC", "html": "👩🏿‍🤝‍👩🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👩🏽", "name": "women holding hands: dark skin tone, medium skin tone", "shortname": ":dark_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F469 1F3FD", "html": "👩🏿‍🤝‍👩🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👩🏾", "name": "women holding hands: dark skin tone, medium-dark skin tone", "shortname": ":dark_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F469 1F3FE", "html": "👩🏿‍🤝‍👩🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏿", "name": "women holding hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46D 1F3FF", "html": "👭🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏻", "name": "woman and man holding hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46B 1F3FB", "html": "👫🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👨🏼", "name": "woman and man holding hands: light skin tone, medium-light skin tone", "shortname": ":light_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F468 1F3FC", "html": "👩🏻‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👨🏽", "name": "woman and man holding hands: light skin tone, medium skin tone", "shortname": ":light_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F468 1F3FD", "html": "👩🏻‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👨🏾", "name": "woman and man holding hands: light skin tone, medium-dark skin tone", "shortname": ":light_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F468 1F3FE", "html": "👩🏻‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👨🏿", "name": "woman and man holding hands: light skin tone, dark skin tone", "shortname": ":light_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F468 1F3FF", "html": "👩🏻‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👨🏻", "name": "woman and man holding hands: medium-light skin tone, light skin tone", "shortname": ":mediumlight_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F468 1F3FB", "html": "👩🏼‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏼", "name": "woman and man holding hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46B 1F3FC", "html": "👫🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👨🏽", "name": "woman and man holding hands: medium-light skin tone, medium skin tone", "shortname": ":mediumlight_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F468 1F3FD", "html": "👩🏼‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👨🏾", "name": "woman and man holding hands: medium-light skin tone, medium-dark skin tone", "shortname": ":mediumlight_skin_tone_medium-dark_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F468 1F3FE", "html": "👩🏼‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👨🏿", "name": "woman and man holding hands: medium-light skin tone, dark skin tone", "shortname": ":mediumlight_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F468 1F3FF", "html": "👩🏼‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👨🏻", "name": "woman and man holding hands: medium skin tone, light skin tone", "shortname": ":medium_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F468 1F3FB", "html": "👩🏽‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👨🏼", "name": "woman and man holding hands: medium skin tone, medium-light skin tone", "shortname": ":medium_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F468 1F3FC", "html": "👩🏽‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏽", "name": "woman and man holding hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46B 1F3FD", "html": "👫🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👨🏾", "name": "woman and man holding hands: medium skin tone, medium-dark skin tone", "shortname": ":medium_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F468 1F3FE", "html": "👩🏽‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👨🏿", "name": "woman and man holding hands: medium skin tone, dark skin tone", "shortname": ":medium_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F468 1F3FF", "html": "👩🏽‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👨🏻", "name": "woman and man holding hands: medium-dark skin tone, light skin tone", "shortname": ":mediumdark_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F468 1F3FB", "html": "👩🏾‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👨🏼", "name": "woman and man holding hands: medium-dark skin tone, medium-light skin tone", "shortname": ":mediumdark_skin_tone_medium-light_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F468 1F3FC", "html": "👩🏾‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👨🏽", "name": "woman and man holding hands: medium-dark skin tone, medium skin tone", "shortname": ":mediumdark_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F468 1F3FD", "html": "👩🏾‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏾", "name": "woman and man holding hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46B 1F3FE", "html": "👫🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👨🏿", "name": "woman and man holding hands: medium-dark skin tone, dark skin tone", "shortname": ":mediumdark_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F468 1F3FF", "html": "👩🏾‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👨🏻", "name": "woman and man holding hands: dark skin tone, light skin tone", "shortname": ":dark_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F468 1F3FB", "html": "👩🏿‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👨🏼", "name": "woman and man holding hands: dark skin tone, medium-light skin tone", "shortname": ":dark_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F468 1F3FC", "html": "👩🏿‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👨🏽", "name": "woman and man holding hands: dark skin tone, medium skin tone", "shortname": ":dark_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F468 1F3FD", "html": "👩🏿‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👨🏾", "name": "woman and man holding hands: dark skin tone, medium-dark skin tone", "shortname": ":dark_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F468 1F3FE", "html": "👩🏿‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏿", "name": "woman and man holding hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46B 1F3FF", "html": "👫🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏻", "name": "men holding hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46C 1F3FB", "html": "👬🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏻‍🤝‍👨🏼", "name": "men holding hands: light skin tone, medium-light skin tone", "shortname": ":light_skin_tone_mediumlight_skin_tone:", "unicode": "1F468 1F3FB 200D 1F91D 200D 1F468 1F3FC", "html": "👨🏻‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏻‍🤝‍👨🏽", "name": "men holding hands: light skin tone, medium skin tone", "shortname": ":light_skin_tone_medium_skin_tone:", "unicode": "1F468 1F3FB 200D 1F91D 200D 1F468 1F3FD", "html": "👨🏻‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏻‍🤝‍👨🏾", "name": "men holding hands: light skin tone, medium-dark skin tone", "shortname": ":light_skin_tone_mediumdark_skin_tone:", "unicode": "1F468 1F3FB 200D 1F91D 200D 1F468 1F3FE", "html": "👨🏻‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏻‍🤝‍👨🏿", "name": "men holding hands: light skin tone, dark skin tone", "shortname": ":light_skin_tone_dark_skin_tone:", "unicode": "1F468 1F3FB 200D 1F91D 200D 1F468 1F3FF", "html": "👨🏻‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏼‍🤝‍👨🏻", "name": "men holding hands: medium-light skin tone, light skin tone", "shortname": ":mediumlight_skin_tone_light_skin_tone:", "unicode": "1F468 1F3FC 200D 1F91D 200D 1F468 1F3FB", "html": "👨🏼‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏼", "name": "men holding hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46C 1F3FC", "html": "👬🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏼‍🤝‍👨🏽", "name": "men holding hands: medium-light skin tone, medium skin tone", "shortname": ":mediumlight_skin_tone_medium_skin_tone:", "unicode": "1F468 1F3FC 200D 1F91D 200D 1F468 1F3FD", "html": "👨🏼‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏼‍🤝‍👨🏾", "name": "men holding hands: medium-light skin tone, medium-dark skin tone", "shortname": ":mediumlight_skin_tone_medium-dark_skin_tone:", "unicode": "1F468 1F3FC 200D 1F91D 200D 1F468 1F3FE", "html": "👨🏼‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏼‍🤝‍👨🏿", "name": "men holding hands: medium-light skin tone, dark skin tone", "shortname": ":mediumlight_skin_tone_dark_skin_tone:", "unicode": "1F468 1F3FC 200D 1F91D 200D 1F468 1F3FF", "html": "👨🏼‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏽‍🤝‍👨🏻", "name": "men holding hands: medium skin tone, light skin tone", "shortname": ":medium_skin_tone_light_skin_tone:", "unicode": "1F468 1F3FD 200D 1F91D 200D 1F468 1F3FB", "html": "👨🏽‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏽‍🤝‍👨🏼", "name": "men holding hands: medium skin tone, medium-light skin tone", "shortname": ":medium_skin_tone_mediumlight_skin_tone:", "unicode": "1F468 1F3FD 200D 1F91D 200D 1F468 1F3FC", "html": "👨🏽‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏽", "name": "men holding hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46C 1F3FD", "html": "👬🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏽‍🤝‍👨🏾", "name": "men holding hands: medium skin tone, medium-dark skin tone", "shortname": ":medium_skin_tone_mediumdark_skin_tone:", "unicode": "1F468 1F3FD 200D 1F91D 200D 1F468 1F3FE", "html": "👨🏽‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏽‍🤝‍👨🏿", "name": "men holding hands: medium skin tone, dark skin tone", "shortname": ":medium_skin_tone_dark_skin_tone:", "unicode": "1F468 1F3FD 200D 1F91D 200D 1F468 1F3FF", "html": "👨🏽‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏾‍🤝‍👨🏻", "name": "men holding hands: medium-dark skin tone, light skin tone", "shortname": ":mediumdark_skin_tone_light_skin_tone:", "unicode": "1F468 1F3FE 200D 1F91D 200D 1F468 1F3FB", "html": "👨🏾‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏾‍🤝‍👨🏼", "name": "men holding hands: medium-dark skin tone, medium-light skin tone", "shortname": ":mediumdark_skin_tone_medium-light_skin_tone:", "unicode": "1F468 1F3FE 200D 1F91D 200D 1F468 1F3FC", "html": "👨🏾‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏾‍🤝‍👨🏽", "name": "men holding hands: medium-dark skin tone, medium skin tone", "shortname": ":mediumdark_skin_tone_medium_skin_tone:", "unicode": "1F468 1F3FE 200D 1F91D 200D 1F468 1F3FD", "html": "👨🏾‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏾", "name": "men holding hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46C 1F3FE", "html": "👬🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏾‍🤝‍👨🏿", "name": "men holding hands: medium-dark skin tone, dark skin tone", "shortname": ":mediumdark_skin_tone_dark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F91D 200D 1F468 1F3FF", "html": "👨🏾‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏿‍🤝‍👨🏻", "name": "men holding hands: dark skin tone, light skin tone", "shortname": ":dark_skin_tone_light_skin_tone:", "unicode": "1F468 1F3FF 200D 1F91D 200D 1F468 1F3FB", "html": "👨🏿‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏿‍🤝‍👨🏼", "name": "men holding hands: dark skin tone, medium-light skin tone", "shortname": ":dark_skin_tone_mediumlight_skin_tone:", "unicode": "1F468 1F3FF 200D 1F91D 200D 1F468 1F3FC", "html": "👨🏿‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏿‍🤝‍👨🏽", "name": "men holding hands: dark skin tone, medium skin tone", "shortname": ":dark_skin_tone_medium_skin_tone:", "unicode": "1F468 1F3FF 200D 1F91D 200D 1F468 1F3FD", "html": "👨🏿‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏿‍🤝‍👨🏾", "name": "men holding hands: dark skin tone, medium-dark skin tone", "shortname": ":dark_skin_tone_mediumdark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F91D 200D 1F468 1F3FE", "html": "👨🏿‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏿", "name": "men holding hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46C 1F3FF", "html": "👬🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤️‍💋‍👨", "name": "kiss: woman, man", "shortname": ":woman_man:", "unicode": "1F469 200D 2764 FE0F 200D 1F48B 200D 1F468", "html": "👩‍❤️‍💋‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤‍💋‍👨", "name": "kiss: woman, man", "shortname": ":woman_man:", "unicode": "1F469 200D 2764 200D 1F48B 200D 1F468", "html": "👩‍❤‍💋‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍❤‍💋‍👨", "name": "kiss: man, man", "shortname": ":man_man:", "unicode": "1F468 200D 2764 200D 1F48B 200D 1F468", "html": "👨‍❤‍💋‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤‍💋‍👩", "name": "kiss: woman, woman", "shortname": ":woman_woman:", "unicode": "1F469 200D 2764 200D 1F48B 200D 1F469", "html": "👩‍❤‍💋‍👩", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤️‍👨", "name": "couple with heart: woman, man", "shortname": ":woman_man:", "unicode": "1F469 200D 2764 FE0F 200D 1F468", "html": "👩‍❤️‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤‍👨", "name": "couple with heart: woman, man", "shortname": ":woman_man:", "unicode": "1F469 200D 2764 200D 1F468", "html": "👩‍❤‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍❤‍👨", "name": "couple with heart: man, man", "shortname": ":man_man:", "unicode": "1F468 200D 2764 200D 1F468", "html": "👨‍❤‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤‍👩", "name": "couple with heart: woman, woman", "shortname": ":woman_woman:", "unicode": "1F469 200D 2764 200D 1F469", "html": "👩‍❤‍👩", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👦", "name": "family: man, woman, boy", "shortname": ":man_woman_boy:", "unicode": "1F468 200D 1F469 200D 1F466", "html": "👨‍👩‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "🗣️", "name": "speaking head", "shortname": ":speaking_head:", "unicode": "1F5E3 FE0F", "html": "🗣️", "category": "People & Body (person-symbol)", "order": ""}, + {"emoji": "🏻", "name": "light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3FB", "html": "🏻", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🏼", "name": "medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3FC", "html": "🏼", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🏽", "name": "medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3FD", "html": "🏽", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🏾", "name": "medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3FE", "html": "🏾", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🏿", "name": "dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3FF", "html": "🏿", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🦰", "name": "red hair", "shortname": ":red_hair:", "unicode": "1F9B0", "html": "🦰", "category": "Component (hair-style)", "order": ""}, + {"emoji": "🦱", "name": "curly hair", "shortname": ":curly_hair:", "unicode": "1F9B1", "html": "🦱", "category": "Component (hair-style)", "order": ""}, + {"emoji": "🦳", "name": "white hair", "shortname": ":white_hair:", "unicode": "1F9B3", "html": "🦳", "category": "Component (hair-style)", "order": ""}, + {"emoji": "🦲", "name": "bald", "shortname": ":bald:", "unicode": "1F9B2", "html": "🦲", "category": "Component (hair-style)", "order": ""}, + {"emoji": "🦍", "name": "gorilla", "shortname": ":gorilla:", "unicode": "1F98D", "html": "🦍", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦧", "name": "orangutan", "shortname": ":orangutan:", "unicode": "1F9A7", "html": "🦧", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦮", "name": "guide dog", "shortname": ":guide_dog:", "unicode": "1F9AE", "html": "🦮", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🐕‍🦺", "name": "service dog", "shortname": ":service_dog:", "unicode": "1F415 200D 1F9BA", "html": "🐕‍🦺", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦊", "name": "fox", "shortname": ":fox:", "unicode": "1F98A", "html": "🦊", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦝", "name": "raccoon", "shortname": ":raccoon:", "unicode": "1F99D", "html": "🦝", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦓", "name": "zebra", "shortname": ":zebra:", "unicode": "1F993", "html": "🦓", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦌", "name": "deer", "shortname": ":deer:", "unicode": "1F98C", "html": "🦌", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦙", "name": "llama", "shortname": ":llama:", "unicode": "1F999", "html": "🦙", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦒", "name": "giraffe", "shortname": ":giraffe:", "unicode": "1F992", "html": "🦒", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦏", "name": "rhinoceros", "shortname": ":rhinoceros:", "unicode": "1F98F", "html": "🦏", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦛", "name": "hippopotamus", "shortname": ":hippopotamus:", "unicode": "1F99B", "html": "🦛", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🐿️", "name": "chipmunk", "shortname": ":chipmunk:", "unicode": "1F43F FE0F", "html": "🐿️", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦔", "name": "hedgehog", "shortname": ":hedgehog:", "unicode": "1F994", "html": "🦔", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦇", "name": "bat", "shortname": ":bat:", "unicode": "1F987", "html": "🦇", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦥", "name": "sloth", "shortname": ":sloth:", "unicode": "1F9A5", "html": "🦥", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦦", "name": "otter", "shortname": ":otter:", "unicode": "1F9A6", "html": "🦦", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦨", "name": "skunk", "shortname": ":skunk:", "unicode": "1F9A8", "html": "🦨", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦘", "name": "kangaroo", "shortname": ":kangaroo:", "unicode": "1F998", "html": "🦘", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦡", "name": "badger", "shortname": ":badger:", "unicode": "1F9A1", "html": "🦡", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🕊️", "name": "dove", "shortname": ":dove:", "unicode": "1F54A FE0F", "html": "🕊️", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦅", "name": "eagle", "shortname": ":eagle:", "unicode": "1F985", "html": "🦅", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦆", "name": "duck", "shortname": ":duck:", "unicode": "1F986", "html": "🦆", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦢", "name": "swan", "shortname": ":swan:", "unicode": "1F9A2", "html": "🦢", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦉", "name": "owl", "shortname": ":owl:", "unicode": "1F989", "html": "🦉", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦩", "name": "flamingo", "shortname": ":flamingo:", "unicode": "1F9A9", "html": "🦩", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦚", "name": "peacock", "shortname": ":peacock:", "unicode": "1F99A", "html": "🦚", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦜", "name": "parrot", "shortname": ":parrot:", "unicode": "1F99C", "html": "🦜", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦎", "name": "lizard", "shortname": ":lizard:", "unicode": "1F98E", "html": "🦎", "category": "Animals & Nature (animal-reptile)", "order": ""}, + {"emoji": "🦕", "name": "sauropod", "shortname": ":sauropod:", "unicode": "1F995", "html": "🦕", "category": "Animals & Nature (animal-reptile)", "order": ""}, + {"emoji": "🦖", "name": "T-Rex", "shortname": ":TRex:", "unicode": "1F996", "html": "🦖", "category": "Animals & Nature (animal-reptile)", "order": ""}, + {"emoji": "🦈", "name": "shark", "shortname": ":shark:", "unicode": "1F988", "html": "🦈", "category": "Animals & Nature (animal-marine)", "order": ""}, + {"emoji": "🦋", "name": "butterfly", "shortname": ":butterfly:", "unicode": "1F98B", "html": "🦋", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🦗", "name": "cricket", "shortname": ":cricket:", "unicode": "1F997", "html": "🦗", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🕷️", "name": "spider", "shortname": ":spider:", "unicode": "1F577 FE0F", "html": "🕷️", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🕸️", "name": "spider web", "shortname": ":spider_web:", "unicode": "1F578 FE0F", "html": "🕸️", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🦟", "name": "mosquito", "shortname": ":mosquito:", "unicode": "1F99F", "html": "🦟", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🦠", "name": "microbe", "shortname": ":microbe:", "unicode": "1F9A0", "html": "🦠", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🏵️", "name": "rosette", "shortname": ":rosette:", "unicode": "1F3F5 FE0F", "html": "🏵️", "category": "Animals & Nature (plant-flower)", "order": ""}, + {"emoji": "🥀", "name": "wilted flower", "shortname": ":wilted_flower:", "unicode": "1F940", "html": "🥀", "category": "Animals & Nature (plant-flower)", "order": ""}, + {"emoji": "☘️", "name": "shamrock", "shortname": ":shamrock:", "unicode": "2618 FE0F", "html": "☘️", "category": "Animals & Nature (plant-other)", "order": ""}, + {"emoji": "🥭", "name": "mango", "shortname": ":mango:", "unicode": "1F96D", "html": "🥭", "category": "Food & Drink (food-fruit)", "order": ""}, + {"emoji": "🥝", "name": "kiwi fruit", "shortname": ":kiwi_fruit:", "unicode": "1F95D", "html": "🥝", "category": "Food & Drink (food-fruit)", "order": ""}, + {"emoji": "🥥", "name": "coconut", "shortname": ":coconut:", "unicode": "1F965", "html": "🥥", "category": "Food & Drink (food-fruit)", "order": ""}, + {"emoji": "🥑", "name": "avocado", "shortname": ":avocado:", "unicode": "1F951", "html": "🥑", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥔", "name": "potato", "shortname": ":potato:", "unicode": "1F954", "html": "🥔", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥕", "name": "carrot", "shortname": ":carrot:", "unicode": "1F955", "html": "🥕", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🌶️", "name": "hot pepper", "shortname": ":hot_pepper:", "unicode": "1F336 FE0F", "html": "🌶️", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥒", "name": "cucumber", "shortname": ":cucumber:", "unicode": "1F952", "html": "🥒", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥬", "name": "leafy green", "shortname": ":leafy_green:", "unicode": "1F96C", "html": "🥬", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥦", "name": "broccoli", "shortname": ":broccoli:", "unicode": "1F966", "html": "🥦", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🧄", "name": "garlic", "shortname": ":garlic:", "unicode": "1F9C4", "html": "🧄", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🧅", "name": "onion", "shortname": ":onion:", "unicode": "1F9C5", "html": "🧅", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥜", "name": "peanuts", "shortname": ":peanuts:", "unicode": "1F95C", "html": "🥜", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥐", "name": "croissant", "shortname": ":croissant:", "unicode": "1F950", "html": "🥐", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥖", "name": "baguette bread", "shortname": ":baguette_bread:", "unicode": "1F956", "html": "🥖", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥨", "name": "pretzel", "shortname": ":pretzel:", "unicode": "1F968", "html": "🥨", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥯", "name": "bagel", "shortname": ":bagel:", "unicode": "1F96F", "html": "🥯", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥞", "name": "pancakes", "shortname": ":pancakes:", "unicode": "1F95E", "html": "🥞", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🧇", "name": "waffle", "shortname": ":waffle:", "unicode": "1F9C7", "html": "🧇", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥩", "name": "cut of meat", "shortname": ":cut_of_meat:", "unicode": "1F969", "html": "🥩", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥓", "name": "bacon", "shortname": ":bacon:", "unicode": "1F953", "html": "🥓", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥪", "name": "sandwich", "shortname": ":sandwich:", "unicode": "1F96A", "html": "🥪", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥙", "name": "stuffed flatbread", "shortname": ":stuffed_flatbread:", "unicode": "1F959", "html": "🥙", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🧆", "name": "falafel", "shortname": ":falafel:", "unicode": "1F9C6", "html": "🧆", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥘", "name": "shallow pan of food", "shortname": ":shallow_pan_of_food:", "unicode": "1F958", "html": "🥘", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥣", "name": "bowl with spoon", "shortname": ":bowl_with_spoon:", "unicode": "1F963", "html": "🥣", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥗", "name": "green salad", "shortname": ":green_salad:", "unicode": "1F957", "html": "🥗", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🧈", "name": "butter", "shortname": ":butter:", "unicode": "1F9C8", "html": "🧈", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🧂", "name": "salt", "shortname": ":salt:", "unicode": "1F9C2", "html": "🧂", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥫", "name": "canned food", "shortname": ":canned_food:", "unicode": "1F96B", "html": "🥫", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥮", "name": "moon cake", "shortname": ":moon_cake:", "unicode": "1F96E", "html": "🥮", "category": "Food & Drink (food-asian)", "order": ""}, + {"emoji": "🥟", "name": "dumpling", "shortname": ":dumpling:", "unicode": "1F95F", "html": "🥟", "category": "Food & Drink (food-asian)", "order": ""}, + {"emoji": "🥠", "name": "fortune cookie", "shortname": ":fortune_cookie:", "unicode": "1F960", "html": "🥠", "category": "Food & Drink (food-asian)", "order": ""}, + {"emoji": "🥡", "name": "takeout box", "shortname": ":takeout_box:", "unicode": "1F961", "html": "🥡", "category": "Food & Drink (food-asian)", "order": ""}, + {"emoji": "🦞", "name": "lobster", "shortname": ":lobster:", "unicode": "1F99E", "html": "🦞", "category": "Food & Drink (food-marine)", "order": ""}, + {"emoji": "🦐", "name": "shrimp", "shortname": ":shrimp:", "unicode": "1F990", "html": "🦐", "category": "Food & Drink (food-marine)", "order": ""}, + {"emoji": "🦑", "name": "squid", "shortname": ":squid:", "unicode": "1F991", "html": "🦑", "category": "Food & Drink (food-marine)", "order": ""}, + {"emoji": "🦪", "name": "oyster", "shortname": ":oyster:", "unicode": "1F9AA", "html": "🦪", "category": "Food & Drink (food-marine)", "order": ""}, + {"emoji": "🧁", "name": "cupcake", "shortname": ":cupcake:", "unicode": "1F9C1", "html": "🧁", "category": "Food & Drink (food-sweet)", "order": ""}, + {"emoji": "🥧", "name": "pie", "shortname": ":pie:", "unicode": "1F967", "html": "🥧", "category": "Food & Drink (food-sweet)", "order": ""}, + {"emoji": "🥛", "name": "glass of milk", "shortname": ":glass_of_milk:", "unicode": "1F95B", "html": "🥛", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🥂", "name": "clinking glasses", "shortname": ":clinking_glasses:", "unicode": "1F942", "html": "🥂", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🥃", "name": "tumbler glass", "shortname": ":tumbler_glass:", "unicode": "1F943", "html": "🥃", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🥤", "name": "cup with straw", "shortname": ":cup_with_straw:", "unicode": "1F964", "html": "🥤", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🧃", "name": "beverage box", "shortname": ":beverage_box:", "unicode": "1F9C3", "html": "🧃", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🧉", "name": "mate", "shortname": ":mate:", "unicode": "1F9C9", "html": "🧉", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🧊", "name": "ice", "shortname": ":ice:", "unicode": "1F9CA", "html": "🧊", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🥢", "name": "chopsticks", "shortname": ":chopsticks:", "unicode": "1F962", "html": "🥢", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "🍽️", "name": "fork and knife with plate", "shortname": ":fork_and_knife_with_plate:", "unicode": "1F37D FE0F", "html": "🍽️", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "🥄", "name": "spoon", "shortname": ":spoon:", "unicode": "1F944", "html": "🥄", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "🗺️", "name": "world map", "shortname": ":world_map:", "unicode": "1F5FA FE0F", "html": "🗺️", "category": "Travel & Places (place-map)", "order": ""}, + {"emoji": "🧭", "name": "compass", "shortname": ":compass:", "unicode": "1F9ED", "html": "🧭", "category": "Travel & Places (place-map)", "order": ""}, + {"emoji": "🏔️", "name": "snow-capped mountain", "shortname": ":snowcapped_mountain:", "unicode": "1F3D4 FE0F", "html": "🏔️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "⛰️", "name": "mountain", "shortname": ":mountain:", "unicode": "26F0 FE0F", "html": "⛰️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏕️", "name": "camping", "shortname": ":camping:", "unicode": "1F3D5 FE0F", "html": "🏕️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏖️", "name": "beach with umbrella", "shortname": ":beach_with_umbrella:", "unicode": "1F3D6 FE0F", "html": "🏖️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏜️", "name": "desert", "shortname": ":desert:", "unicode": "1F3DC FE0F", "html": "🏜️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏝️", "name": "desert island", "shortname": ":desert_island:", "unicode": "1F3DD FE0F", "html": "🏝️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏞️", "name": "national park", "shortname": ":national_park:", "unicode": "1F3DE FE0F", "html": "🏞️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏟️", "name": "stadium", "shortname": ":stadium:", "unicode": "1F3DF FE0F", "html": "🏟️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏛️", "name": "classical building", "shortname": ":classical_building:", "unicode": "1F3DB FE0F", "html": "🏛️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏗️", "name": "building construction", "shortname": ":building_construction:", "unicode": "1F3D7 FE0F", "html": "🏗️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🧱", "name": "brick", "shortname": ":brick:", "unicode": "1F9F1", "html": "🧱", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏘️", "name": "houses", "shortname": ":houses:", "unicode": "1F3D8 FE0F", "html": "🏘️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏚️", "name": "derelict house", "shortname": ":derelict_house:", "unicode": "1F3DA FE0F", "html": "🏚️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🛕", "name": "hindu temple", "shortname": ":hindu_temple:", "unicode": "1F6D5", "html": "🛕", "category": "Travel & Places (place-religious)", "order": ""}, + {"emoji": "⛩️", "name": "shinto shrine", "shortname": ":shinto_shrine:", "unicode": "26E9 FE0F", "html": "⛩️", "category": "Travel & Places (place-religious)", "order": ""}, + {"emoji": "🏙️", "name": "cityscape", "shortname": ":cityscape:", "unicode": "1F3D9 FE0F", "html": "🏙️", "category": "Travel & Places (place-other)", "order": ""}, + {"emoji": "♨", "name": "hot springs", "shortname": ":hot_springs:", "unicode": "2668", "html": "♨", "category": "Travel & Places (place-other)", "order": ""}, + {"emoji": "🏎️", "name": "racing car", "shortname": ":racing_car:", "unicode": "1F3CE FE0F", "html": "🏎️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🏍️", "name": "motorcycle", "shortname": ":motorcycle:", "unicode": "1F3CD FE0F", "html": "🏍️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛵", "name": "motor scooter", "shortname": ":motor_scooter:", "unicode": "1F6F5", "html": "🛵", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🦽", "name": "manual wheelchair", "shortname": ":manual_wheelchair:", "unicode": "1F9BD", "html": "🦽", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🦼", "name": "motorized wheelchair", "shortname": ":motorized_wheelchair:", "unicode": "1F9BC", "html": "🦼", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛺", "name": "auto rickshaw", "shortname": ":auto_rickshaw:", "unicode": "1F6FA", "html": "🛺", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛴", "name": "kick scooter", "shortname": ":kick_scooter:", "unicode": "1F6F4", "html": "🛴", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛹", "name": "skateboard", "shortname": ":skateboard:", "unicode": "1F6F9", "html": "🛹", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛣️", "name": "motorway", "shortname": ":motorway:", "unicode": "1F6E3 FE0F", "html": "🛣️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛤️", "name": "railway track", "shortname": ":railway_track:", "unicode": "1F6E4 FE0F", "html": "🛤️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛢️", "name": "oil drum", "shortname": ":oil_drum:", "unicode": "1F6E2 FE0F", "html": "🛢️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛶", "name": "canoe", "shortname": ":canoe:", "unicode": "1F6F6", "html": "🛶", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🛳️", "name": "passenger ship", "shortname": ":passenger_ship:", "unicode": "1F6F3 FE0F", "html": "🛳️", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "⛴️", "name": "ferry", "shortname": ":ferry:", "unicode": "26F4 FE0F", "html": "⛴️", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🛥️", "name": "motor boat", "shortname": ":motor_boat:", "unicode": "1F6E5 FE0F", "html": "🛥️", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "✈", "name": "airplane", "shortname": ":airplane:", "unicode": "2708", "html": "✈", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛩️", "name": "small airplane", "shortname": ":small_airplane:", "unicode": "1F6E9 FE0F", "html": "🛩️", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🪂", "name": "parachute", "shortname": ":parachute:", "unicode": "1FA82", "html": "🪂", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛰️", "name": "satellite", "shortname": ":satellite:", "unicode": "1F6F0 FE0F", "html": "🛰️", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛸", "name": "flying saucer", "shortname": ":flying_saucer:", "unicode": "1F6F8", "html": "🛸", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛎️", "name": "bellhop bell", "shortname": ":bellhop_bell:", "unicode": "1F6CE FE0F", "html": "🛎️", "category": "Travel & Places (hotel)", "order": ""}, + {"emoji": "🧳", "name": "luggage", "shortname": ":luggage:", "unicode": "1F9F3", "html": "🧳", "category": "Travel & Places (hotel)", "order": ""}, + {"emoji": "⏱️", "name": "stopwatch", "shortname": ":stopwatch:", "unicode": "23F1 FE0F", "html": "⏱️", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "⏲️", "name": "timer clock", "shortname": ":timer_clock:", "unicode": "23F2 FE0F", "html": "⏲️", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "🕰️", "name": "mantelpiece clock", "shortname": ":mantelpiece_clock:", "unicode": "1F570 FE0F", "html": "🕰️", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "🌡️", "name": "thermometer", "shortname": ":thermometer:", "unicode": "1F321 FE0F", "html": "🌡️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☀", "name": "sun", "shortname": ":sun:", "unicode": "2600", "html": "☀", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🪐", "name": "ringed planet", "shortname": ":ringed_planet:", "unicode": "1FA90", "html": "🪐", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☁️", "name": "cloud", "shortname": ":cloud:", "unicode": "2601 FE0F", "html": "☁️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "⛈️", "name": "cloud with lightning and rain", "shortname": ":cloud_with_lightning_and_rain:", "unicode": "26C8 FE0F", "html": "⛈️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌤️", "name": "sun behind small cloud", "shortname": ":sun_behind_small_cloud:", "unicode": "1F324 FE0F", "html": "🌤️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌥️", "name": "sun behind large cloud", "shortname": ":sun_behind_large_cloud:", "unicode": "1F325 FE0F", "html": "🌥️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌦️", "name": "sun behind rain cloud", "shortname": ":sun_behind_rain_cloud:", "unicode": "1F326 FE0F", "html": "🌦️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌧️", "name": "cloud with rain", "shortname": ":cloud_with_rain:", "unicode": "1F327 FE0F", "html": "🌧️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌨️", "name": "cloud with snow", "shortname": ":cloud_with_snow:", "unicode": "1F328 FE0F", "html": "🌨️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌩️", "name": "cloud with lightning", "shortname": ":cloud_with_lightning:", "unicode": "1F329 FE0F", "html": "🌩️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌪️", "name": "tornado", "shortname": ":tornado:", "unicode": "1F32A FE0F", "html": "🌪️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌫️", "name": "fog", "shortname": ":fog:", "unicode": "1F32B FE0F", "html": "🌫️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌬️", "name": "wind face", "shortname": ":wind_face:", "unicode": "1F32C FE0F", "html": "🌬️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☂", "name": "umbrella", "shortname": ":umbrella:", "unicode": "2602", "html": "☂", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "⛱️", "name": "umbrella on ground", "shortname": ":umbrella_on_ground:", "unicode": "26F1 FE0F", "html": "⛱️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "❄", "name": "snowflake", "shortname": ":snowflake:", "unicode": "2744", "html": "❄", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☃", "name": "snowman", "shortname": ":snowman:", "unicode": "2603", "html": "☃", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☄️", "name": "comet", "shortname": ":comet:", "unicode": "2604 FE0F", "html": "☄️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🧨", "name": "firecracker", "shortname": ":firecracker:", "unicode": "1F9E8", "html": "🧨", "category": "Activities (event)", "order": ""}, + {"emoji": "🧧", "name": "red envelope", "shortname": ":red_envelope:", "unicode": "1F9E7", "html": "🧧", "category": "Activities (event)", "order": ""}, + {"emoji": "🎗️", "name": "reminder ribbon", "shortname": ":reminder_ribbon:", "unicode": "1F397 FE0F", "html": "🎗️", "category": "Activities (event)", "order": ""}, + {"emoji": "🎟️", "name": "admission tickets", "shortname": ":admission_tickets:", "unicode": "1F39F FE0F", "html": "🎟️", "category": "Activities (event)", "order": ""}, + {"emoji": "🎖️", "name": "military medal", "shortname": ":military_medal:", "unicode": "1F396 FE0F", "html": "🎖️", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "🥇", "name": "1st place medal", "shortname": ":1st_place_medal:", "unicode": "1F947", "html": "🥇", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "🥈", "name": "2nd place medal", "shortname": ":2nd_place_medal:", "unicode": "1F948", "html": "🥈", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "🥉", "name": "3rd place medal", "shortname": ":3rd_place_medal:", "unicode": "1F949", "html": "🥉", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "⚾", "name": "baseball", "shortname": ":baseball:", "unicode": "26BE", "html": "⚾", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥎", "name": "softball", "shortname": ":softball:", "unicode": "1F94E", "html": "🥎", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥏", "name": "flying disc", "shortname": ":flying_disc:", "unicode": "1F94F", "html": "🥏", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥍", "name": "lacrosse", "shortname": ":lacrosse:", "unicode": "1F94D", "html": "🥍", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥊", "name": "boxing glove", "shortname": ":boxing_glove:", "unicode": "1F94A", "html": "🥊", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥋", "name": "martial arts uniform", "shortname": ":martial_arts_uniform:", "unicode": "1F94B", "html": "🥋", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥅", "name": "goal net", "shortname": ":goal_net:", "unicode": "1F945", "html": "🥅", "category": "Activities (sport)", "order": ""}, + {"emoji": "⛸️", "name": "ice skate", "shortname": ":ice_skate:", "unicode": "26F8 FE0F", "html": "⛸️", "category": "Activities (sport)", "order": ""}, + {"emoji": "🤿", "name": "diving mask", "shortname": ":diving_mask:", "unicode": "1F93F", "html": "🤿", "category": "Activities (sport)", "order": ""}, + {"emoji": "🛷", "name": "sled", "shortname": ":sled:", "unicode": "1F6F7", "html": "🛷", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥌", "name": "curling stone", "shortname": ":curling_stone:", "unicode": "1F94C", "html": "🥌", "category": "Activities (sport)", "order": ""}, + {"emoji": "🪀", "name": "yo-yo", "shortname": ":yoyo:", "unicode": "1FA80", "html": "🪀", "category": "Activities (game)", "order": ""}, + {"emoji": "🪁", "name": "kite", "shortname": ":kite:", "unicode": "1FA81", "html": "🪁", "category": "Activities (game)", "order": ""}, + {"emoji": "🧿", "name": "nazar amulet", "shortname": ":nazar_amulet:", "unicode": "1F9FF", "html": "🧿", "category": "Activities (game)", "order": ""}, + {"emoji": "🕹️", "name": "joystick", "shortname": ":joystick:", "unicode": "1F579 FE0F", "html": "🕹️", "category": "Activities (game)", "order": ""}, + {"emoji": "🧩", "name": "puzzle piece", "shortname": ":puzzle_piece:", "unicode": "1F9E9", "html": "🧩", "category": "Activities (game)", "order": ""}, + {"emoji": "🧸", "name": "teddy bear", "shortname": ":teddy_bear:", "unicode": "1F9F8", "html": "🧸", "category": "Activities (game)", "order": ""}, + {"emoji": "♟️", "name": "chess pawn", "shortname": ":chess_pawn:", "unicode": "265F FE0F", "html": "♟️", "category": "Activities (game)", "order": ""}, + {"emoji": "♟", "name": "chess pawn", "shortname": ":chess_pawn:", "unicode": "265F", "html": "♟", "category": "Activities (game)", "order": ""}, + {"emoji": "🖼️", "name": "framed picture", "shortname": ":framed_picture:", "unicode": "1F5BC FE0F", "html": "🖼️", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "🧵", "name": "thread", "shortname": ":thread:", "unicode": "1F9F5", "html": "🧵", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "🧶", "name": "yarn", "shortname": ":yarn:", "unicode": "1F9F6", "html": "🧶", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "🕶️", "name": "sunglasses", "shortname": ":sunglasses:", "unicode": "1F576 FE0F", "html": "🕶️", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥽", "name": "goggles", "shortname": ":goggles:", "unicode": "1F97D", "html": "🥽", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥼", "name": "lab coat", "shortname": ":lab_coat:", "unicode": "1F97C", "html": "🥼", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🦺", "name": "safety vest", "shortname": ":safety_vest:", "unicode": "1F9BA", "html": "🦺", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧣", "name": "scarf", "shortname": ":scarf:", "unicode": "1F9E3", "html": "🧣", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧤", "name": "gloves", "shortname": ":gloves:", "unicode": "1F9E4", "html": "🧤", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧥", "name": "coat", "shortname": ":coat:", "unicode": "1F9E5", "html": "🧥", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧦", "name": "socks", "shortname": ":socks:", "unicode": "1F9E6", "html": "🧦", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥻", "name": "sari", "shortname": ":sari:", "unicode": "1F97B", "html": "🥻", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🩱", "name": "one-piece swimsuit", "shortname": ":onepiece_swimsuit:", "unicode": "1FA71", "html": "🩱", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🩲", "name": "briefs", "shortname": ":briefs:", "unicode": "1FA72", "html": "🩲", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🩳", "name": "shorts", "shortname": ":shorts:", "unicode": "1FA73", "html": "🩳", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🛍️", "name": "shopping bags", "shortname": ":shopping_bags:", "unicode": "1F6CD FE0F", "html": "🛍️", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥾", "name": "hiking boot", "shortname": ":hiking_boot:", "unicode": "1F97E", "html": "🥾", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥿", "name": "flat shoe", "shortname": ":flat_shoe:", "unicode": "1F97F", "html": "🥿", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🩰", "name": "ballet shoes", "shortname": ":ballet_shoes:", "unicode": "1FA70", "html": "🩰", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧢", "name": "billed cap", "shortname": ":billed_cap:", "unicode": "1F9E2", "html": "🧢", "category": "Objects (clothing)", "order": ""}, + {"emoji": "⛑️", "name": "rescue worker’s helmet", "shortname": ":rescue_worker’s_helmet:", "unicode": "26D1 FE0F", "html": "⛑️", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🎙️", "name": "studio microphone", "shortname": ":studio_microphone:", "unicode": "1F399 FE0F", "html": "🎙️", "category": "Objects (music)", "order": ""}, + {"emoji": "🎚️", "name": "level slider", "shortname": ":level_slider:", "unicode": "1F39A FE0F", "html": "🎚️", "category": "Objects (music)", "order": ""}, + {"emoji": "🎛️", "name": "control knobs", "shortname": ":control_knobs:", "unicode": "1F39B FE0F", "html": "🎛️", "category": "Objects (music)", "order": ""}, + {"emoji": "🪕", "name": "banjo", "shortname": ":banjo:", "unicode": "1FA95", "html": "🪕", "category": "Objects (musical-instrument)", "order": ""}, + {"emoji": "☎", "name": "telephone", "shortname": ":telephone:", "unicode": "260E", "html": "☎", "category": "Objects (phone)", "order": ""}, + {"emoji": "🖥️", "name": "desktop computer", "shortname": ":desktop_computer:", "unicode": "1F5A5 FE0F", "html": "🖥️", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖨️", "name": "printer", "shortname": ":printer:", "unicode": "1F5A8 FE0F", "html": "🖨️", "category": "Objects (computer)", "order": ""}, + {"emoji": "⌨️", "name": "keyboard", "shortname": ":keyboard:", "unicode": "2328 FE0F", "html": "⌨️", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖱️", "name": "computer mouse", "shortname": ":computer_mouse:", "unicode": "1F5B1 FE0F", "html": "🖱️", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖲️", "name": "trackball", "shortname": ":trackball:", "unicode": "1F5B2 FE0F", "html": "🖲️", "category": "Objects (computer)", "order": ""}, + {"emoji": "🧮", "name": "abacus", "shortname": ":abacus:", "unicode": "1F9EE", "html": "🧮", "category": "Objects (computer)", "order": ""}, + {"emoji": "🎞️", "name": "film frames", "shortname": ":film_frames:", "unicode": "1F39E FE0F", "html": "🎞️", "category": "Objects (light & video)", "order": ""}, + {"emoji": "📽️", "name": "film projector", "shortname": ":film_projector:", "unicode": "1F4FD FE0F", "html": "📽️", "category": "Objects (light & video)", "order": ""}, + {"emoji": "🕯️", "name": "candle", "shortname": ":candle:", "unicode": "1F56F FE0F", "html": "🕯️", "category": "Objects (light & video)", "order": ""}, + {"emoji": "🪔", "name": "diya lamp", "shortname": ":diya_lamp:", "unicode": "1FA94", "html": "🪔", "category": "Objects (light & video)", "order": ""}, + {"emoji": "🗞️", "name": "rolled-up newspaper", "shortname": ":rolledup_newspaper:", "unicode": "1F5DE FE0F", "html": "🗞️", "category": "Objects (book-paper)", "order": ""}, + {"emoji": "🏷️", "name": "label", "shortname": ":label:", "unicode": "1F3F7 FE0F", "html": "🏷️", "category": "Objects (book-paper)", "order": ""}, + {"emoji": "🧾", "name": "receipt", "shortname": ":receipt:", "unicode": "1F9FE", "html": "🧾", "category": "Objects (money)", "order": ""}, + {"emoji": "✉", "name": "envelope", "shortname": ":envelope:", "unicode": "2709", "html": "✉", "category": "Objects (mail)", "order": ""}, + {"emoji": "🗳️", "name": "ballot box with ballot", "shortname": ":ballot_box_with_ballot:", "unicode": "1F5F3 FE0F", "html": "🗳️", "category": "Objects (mail)", "order": ""}, + {"emoji": "✏️", "name": "pencil", "shortname": ":pencil:", "unicode": "270F FE0F", "html": "✏️", "category": "Objects (writing)", "order": ""}, + {"emoji": "✒️", "name": "black nib", "shortname": ":black_nib:", "unicode": "2712 FE0F", "html": "✒️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖋️", "name": "fountain pen", "shortname": ":fountain_pen:", "unicode": "1F58B FE0F", "html": "🖋️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖊️", "name": "pen", "shortname": ":pen:", "unicode": "1F58A FE0F", "html": "🖊️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖌️", "name": "paintbrush", "shortname": ":paintbrush:", "unicode": "1F58C FE0F", "html": "🖌️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖍️", "name": "crayon", "shortname": ":crayon:", "unicode": "1F58D FE0F", "html": "🖍️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🗂️", "name": "card index dividers", "shortname": ":card_index_dividers:", "unicode": "1F5C2 FE0F", "html": "🗂️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗒️", "name": "spiral notepad", "shortname": ":spiral_notepad:", "unicode": "1F5D2 FE0F", "html": "🗒️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗓️", "name": "spiral calendar", "shortname": ":spiral_calendar:", "unicode": "1F5D3 FE0F", "html": "🗓️", "category": "Objects (office)", "order": ""}, + {"emoji": "🖇️", "name": "linked paperclips", "shortname": ":linked_paperclips:", "unicode": "1F587 FE0F", "html": "🖇️", "category": "Objects (office)", "order": ""}, + {"emoji": "✂", "name": "scissors", "shortname": ":scissors:", "unicode": "2702", "html": "✂", "category": "Objects (office)", "order": ""}, + {"emoji": "🗃️", "name": "card file box", "shortname": ":card_file_box:", "unicode": "1F5C3 FE0F", "html": "🗃️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗄️", "name": "file cabinet", "shortname": ":file_cabinet:", "unicode": "1F5C4 FE0F", "html": "🗄️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗑️", "name": "wastebasket", "shortname": ":wastebasket:", "unicode": "1F5D1 FE0F", "html": "🗑️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗝️", "name": "old key", "shortname": ":old_key:", "unicode": "1F5DD FE0F", "html": "🗝️", "category": "Objects (lock)", "order": ""}, + {"emoji": "🪓", "name": "axe", "shortname": ":axe:", "unicode": "1FA93", "html": "🪓", "category": "Objects (tool)", "order": ""}, + {"emoji": "⛏️", "name": "pick", "shortname": ":pick:", "unicode": "26CF FE0F", "html": "⛏️", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚒️", "name": "hammer and pick", "shortname": ":hammer_and_pick:", "unicode": "2692 FE0F", "html": "⚒️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🛠️", "name": "hammer and wrench", "shortname": ":hammer_and_wrench:", "unicode": "1F6E0 FE0F", "html": "🛠️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🗡️", "name": "dagger", "shortname": ":dagger:", "unicode": "1F5E1 FE0F", "html": "🗡️", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚔️", "name": "crossed swords", "shortname": ":crossed_swords:", "unicode": "2694 FE0F", "html": "⚔️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🛡️", "name": "shield", "shortname": ":shield:", "unicode": "1F6E1 FE0F", "html": "🛡️", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚙️", "name": "gear", "shortname": ":gear:", "unicode": "2699 FE0F", "html": "⚙️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🗜️", "name": "clamp", "shortname": ":clamp:", "unicode": "1F5DC FE0F", "html": "🗜️", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚖️", "name": "balance scale", "shortname": ":balance_scale:", "unicode": "2696 FE0F", "html": "⚖️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🦯", "name": "probing cane", "shortname": ":probing_cane:", "unicode": "1F9AF", "html": "🦯", "category": "Objects (tool)", "order": ""}, + {"emoji": "⛓️", "name": "chains", "shortname": ":chains:", "unicode": "26D3 FE0F", "html": "⛓️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🧰", "name": "toolbox", "shortname": ":toolbox:", "unicode": "1F9F0", "html": "🧰", "category": "Objects (tool)", "order": ""}, + {"emoji": "🧲", "name": "magnet", "shortname": ":magnet:", "unicode": "1F9F2", "html": "🧲", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚗️", "name": "alembic", "shortname": ":alembic:", "unicode": "2697 FE0F", "html": "⚗️", "category": "Objects (science)", "order": ""}, + {"emoji": "🧪", "name": "test tube", "shortname": ":test_tube:", "unicode": "1F9EA", "html": "🧪", "category": "Objects (science)", "order": ""}, + {"emoji": "🧫", "name": "petri dish", "shortname": ":petri_dish:", "unicode": "1F9EB", "html": "🧫", "category": "Objects (science)", "order": ""}, + {"emoji": "🧬", "name": "dna", "shortname": ":dna:", "unicode": "1F9EC", "html": "🧬", "category": "Objects (science)", "order": ""}, + {"emoji": "🩸", "name": "drop of blood", "shortname": ":drop_of_blood:", "unicode": "1FA78", "html": "🩸", "category": "Objects (medical)", "order": ""}, + {"emoji": "🩹", "name": "adhesive bandage", "shortname": ":adhesive_bandage:", "unicode": "1FA79", "html": "🩹", "category": "Objects (medical)", "order": ""}, + {"emoji": "🩺", "name": "stethoscope", "shortname": ":stethoscope:", "unicode": "1FA7A", "html": "🩺", "category": "Objects (medical)", "order": ""}, + {"emoji": "🛏️", "name": "bed", "shortname": ":bed:", "unicode": "1F6CF FE0F", "html": "🛏️", "category": "Objects (household)", "order": ""}, + {"emoji": "🛋️", "name": "couch and lamp", "shortname": ":couch_and_lamp:", "unicode": "1F6CB FE0F", "html": "🛋️", "category": "Objects (household)", "order": ""}, + {"emoji": "🪑", "name": "chair", "shortname": ":chair:", "unicode": "1FA91", "html": "🪑", "category": "Objects (household)", "order": ""}, + {"emoji": "🪒", "name": "razor", "shortname": ":razor:", "unicode": "1FA92", "html": "🪒", "category": "Objects (household)", "order": ""}, + {"emoji": "🧴", "name": "lotion bottle", "shortname": ":lotion_bottle:", "unicode": "1F9F4", "html": "🧴", "category": "Objects (household)", "order": ""}, + {"emoji": "🧷", "name": "safety pin", "shortname": ":safety_pin:", "unicode": "1F9F7", "html": "🧷", "category": "Objects (household)", "order": ""}, + {"emoji": "🧹", "name": "broom", "shortname": ":broom:", "unicode": "1F9F9", "html": "🧹", "category": "Objects (household)", "order": ""}, + {"emoji": "🧺", "name": "basket", "shortname": ":basket:", "unicode": "1F9FA", "html": "🧺", "category": "Objects (household)", "order": ""}, + {"emoji": "🧻", "name": "roll of paper", "shortname": ":roll_of_paper:", "unicode": "1F9FB", "html": "🧻", "category": "Objects (household)", "order": ""}, + {"emoji": "🧼", "name": "soap", "shortname": ":soap:", "unicode": "1F9FC", "html": "🧼", "category": "Objects (household)", "order": ""}, + {"emoji": "🧽", "name": "sponge", "shortname": ":sponge:", "unicode": "1F9FD", "html": "🧽", "category": "Objects (household)", "order": ""}, + {"emoji": "🧯", "name": "fire extinguisher", "shortname": ":fire_extinguisher:", "unicode": "1F9EF", "html": "🧯", "category": "Objects (household)", "order": ""}, + {"emoji": "🛒", "name": "shopping cart", "shortname": ":shopping_cart:", "unicode": "1F6D2", "html": "🛒", "category": "Objects (household)", "order": ""}, + {"emoji": "⚰️", "name": "coffin", "shortname": ":coffin:", "unicode": "26B0 FE0F", "html": "⚰️", "category": "Objects (other-object)", "order": ""}, + {"emoji": "⚱️", "name": "funeral urn", "shortname": ":funeral_urn:", "unicode": "26B1 FE0F", "html": "⚱️", "category": "Objects (other-object)", "order": ""}, + {"emoji": "⚠", "name": "warning", "shortname": ":warning:", "unicode": "26A0", "html": "⚠", "category": "Symbols (warning)", "order": ""}, + {"emoji": "☢️", "name": "radioactive", "shortname": ":radioactive:", "unicode": "2622 FE0F", "html": "☢️", "category": "Symbols (warning)", "order": ""}, + {"emoji": "☣️", "name": "biohazard", "shortname": ":biohazard:", "unicode": "2623 FE0F", "html": "☣️", "category": "Symbols (warning)", "order": ""}, + {"emoji": "⬆", "name": "up arrow", "shortname": ":up_arrow:", "unicode": "2B06", "html": "⬆", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↗", "name": "up-right arrow", "shortname": ":upright_arrow:", "unicode": "2197", "html": "↗", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "➡️", "name": "right arrow", "shortname": ":right_arrow:", "unicode": "27A1 FE0F", "html": "➡️", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↘", "name": "down-right arrow", "shortname": ":downright_arrow:", "unicode": "2198", "html": "↘", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⬇", "name": "down arrow", "shortname": ":down_arrow:", "unicode": "2B07", "html": "⬇", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↙", "name": "down-left arrow", "shortname": ":downleft_arrow:", "unicode": "2199", "html": "↙", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⬅", "name": "left arrow", "shortname": ":left_arrow:", "unicode": "2B05", "html": "⬅", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↖️", "name": "up-left arrow", "shortname": ":upleft_arrow:", "unicode": "2196 FE0F", "html": "↖️", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↕️", "name": "up-down arrow", "shortname": ":updown_arrow:", "unicode": "2195 FE0F", "html": "↕️", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↩", "name": "right arrow curving left", "shortname": ":right_arrow_curving_left:", "unicode": "21A9", "html": "↩", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↪", "name": "left arrow curving right", "shortname": ":left_arrow_curving_right:", "unicode": "21AA", "html": "↪", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⤴", "name": "right arrow curving up", "shortname": ":right_arrow_curving_up:", "unicode": "2934", "html": "⤴", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⤵", "name": "right arrow curving down", "shortname": ":right_arrow_curving_down:", "unicode": "2935", "html": "⤵", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⚛️", "name": "atom symbol", "shortname": ":atom_symbol:", "unicode": "269B FE0F", "html": "⚛️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "🕉️", "name": "om", "shortname": ":om:", "unicode": "1F549 FE0F", "html": "🕉️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "✡", "name": "star of David", "shortname": ":star_of_David:", "unicode": "2721", "html": "✡", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☸️", "name": "wheel of dharma", "shortname": ":wheel_of_dharma:", "unicode": "2638 FE0F", "html": "☸️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☯️", "name": "yin yang", "shortname": ":yin_yang:", "unicode": "262F FE0F", "html": "☯️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "✝", "name": "latin cross", "shortname": ":latin_cross:", "unicode": "271D", "html": "✝", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☦️", "name": "orthodox cross", "shortname": ":orthodox_cross:", "unicode": "2626 FE0F", "html": "☦️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☪️", "name": "star and crescent", "shortname": ":star_and_crescent:", "unicode": "262A FE0F", "html": "☪️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☮️", "name": "peace symbol", "shortname": ":peace_symbol:", "unicode": "262E FE0F", "html": "☮️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "▶", "name": "play button", "shortname": ":play_button:", "unicode": "25B6", "html": "▶", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏭️", "name": "next track button", "shortname": ":next_track_button:", "unicode": "23ED FE0F", "html": "⏭️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏯️", "name": "play or pause button", "shortname": ":play_or_pause_button:", "unicode": "23EF FE0F", "html": "⏯️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "◀", "name": "reverse button", "shortname": ":reverse_button:", "unicode": "25C0", "html": "◀", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏮️", "name": "last track button", "shortname": ":last_track_button:", "unicode": "23EE FE0F", "html": "⏮️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏸️", "name": "pause button", "shortname": ":pause_button:", "unicode": "23F8 FE0F", "html": "⏸️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏹️", "name": "stop button", "shortname": ":stop_button:", "unicode": "23F9 FE0F", "html": "⏹️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏺️", "name": "record button", "shortname": ":record_button:", "unicode": "23FA FE0F", "html": "⏺️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏏️", "name": "eject button", "shortname": ":eject_button:", "unicode": "23CF FE0F", "html": "⏏️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏏", "name": "eject button", "shortname": ":eject_button:", "unicode": "23CF", "html": "⏏", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "♀️", "name": "female sign", "shortname": ":female_sign:", "unicode": "2640 FE0F", "html": "♀️", "category": "Symbols (gender)", "order": ""}, + {"emoji": "♀", "name": "female sign", "shortname": ":female_sign:", "unicode": "2640", "html": "♀", "category": "Symbols (gender)", "order": ""}, + {"emoji": "♂️", "name": "male sign", "shortname": ":male_sign:", "unicode": "2642 FE0F", "html": "♂️", "category": "Symbols (gender)", "order": ""}, + {"emoji": "♂", "name": "male sign", "shortname": ":male_sign:", "unicode": "2642", "html": "♂", "category": "Symbols (gender)", "order": ""}, + {"emoji": "⚕️", "name": "medical symbol", "shortname": ":medical_symbol:", "unicode": "2695 FE0F", "html": "⚕️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "⚕", "name": "medical symbol", "shortname": ":medical_symbol:", "unicode": "2695", "html": "⚕", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "♾️", "name": "infinity", "shortname": ":infinity:", "unicode": "267E FE0F", "html": "♾️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "♾", "name": "infinity", "shortname": ":infinity:", "unicode": "267E", "html": "♾", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "♻️", "name": "recycling symbol", "shortname": ":recycling_symbol:", "unicode": "267B FE0F", "html": "♻️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "⚜️", "name": "fleur-de-lis", "shortname": ":fleurde-lis:", "unicode": "269C FE0F", "html": "⚜️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "☑", "name": "check box with check", "shortname": ":check_box_with_check:", "unicode": "2611", "html": "☑", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "✔", "name": "check mark", "shortname": ":check_mark:", "unicode": "2714", "html": "✔", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "✖", "name": "multiplication sign", "shortname": ":multiplication_sign:", "unicode": "2716", "html": "✖", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "〽", "name": "part alternation mark", "shortname": ":part_alternation_mark:", "unicode": "303D", "html": "〽", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "✳", "name": "eight-spoked asterisk", "shortname": ":eightspoked_asterisk:", "unicode": "2733", "html": "✳", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "✴", "name": "eight-pointed star", "shortname": ":eightpointed_star:", "unicode": "2734", "html": "✴", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "❇", "name": "sparkle", "shortname": ":sparkle:", "unicode": "2747", "html": "❇", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "‼", "name": "double exclamation mark", "shortname": ":double_exclamation_mark:", "unicode": "203C", "html": "‼", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "⁉", "name": "exclamation question mark", "shortname": ":exclamation_question_mark:", "unicode": "2049", "html": "⁉", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "〰️", "name": "wavy dash", "shortname": ":wavy_dash:", "unicode": "3030 FE0F", "html": "〰️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "#️⃣", "name": "keycap: #", "shortname": ":#:", "unicode": "0023 FE0F 20E3", "html": "#️⃣", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "🅰", "name": "A button (blood type)", "shortname": ":A_button_blood_type:", "unicode": "1F170", "html": "🅰", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🅱", "name": "B button (blood type)", "shortname": ":B_button_blood_type:", "unicode": "1F171", "html": "🅱", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "ℹ️", "name": "information", "shortname": ":information:", "unicode": "2139 FE0F", "html": "ℹ️", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "Ⓜ", "name": "circled M", "shortname": ":circled_M:", "unicode": "24C2", "html": "Ⓜ", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🅾", "name": "O button (blood type)", "shortname": ":O_button_blood_type:", "unicode": "1F17E", "html": "🅾", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🅿", "name": "P button", "shortname": ":P_button:", "unicode": "1F17F", "html": "🅿", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🈂", "name": "Japanese service charge button", "shortname": ":Japanese_service_charge_button:", "unicode": "1F202", "html": "🈂", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🈷️", "name": "Japanese monthly amount button", "shortname": ":Japanese_monthly_amount_button:", "unicode": "1F237 FE0F", "html": "🈷️", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🈯", "name": "Japanese reserved button", "shortname": ":Japanese_reserved_button:", "unicode": "1F22F", "html": "🈯", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🈚", "name": "Japanese free of charge button", "shortname": ":Japanese_free_of_charge_button:", "unicode": "1F21A", "html": "🈚", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "㊗️", "name": "Japanese congratulations button", "shortname": ":Japanese_congratulations_button:", "unicode": "3297 FE0F", "html": "㊗️", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "㊙️", "name": "Japanese secret button", "shortname": ":Japanese_secret_button:", "unicode": "3299 FE0F", "html": "㊙️", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🟠", "name": "orange circle", "shortname": ":orange_circle:", "unicode": "1F7E0", "html": "🟠", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟡", "name": "yellow circle", "shortname": ":yellow_circle:", "unicode": "1F7E1", "html": "🟡", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟢", "name": "green circle", "shortname": ":green_circle:", "unicode": "1F7E2", "html": "🟢", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟣", "name": "purple circle", "shortname": ":purple_circle:", "unicode": "1F7E3", "html": "🟣", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟤", "name": "brown circle", "shortname": ":brown_circle:", "unicode": "1F7E4", "html": "🟤", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟥", "name": "red square", "shortname": ":red_square:", "unicode": "1F7E5", "html": "🟥", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟧", "name": "orange square", "shortname": ":orange_square:", "unicode": "1F7E7", "html": "🟧", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟨", "name": "yellow square", "shortname": ":yellow_square:", "unicode": "1F7E8", "html": "🟨", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟩", "name": "green square", "shortname": ":green_square:", "unicode": "1F7E9", "html": "🟩", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟦", "name": "blue square", "shortname": ":blue_square:", "unicode": "1F7E6", "html": "🟦", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "▪", "name": "black small square", "shortname": ":black_small_square:", "unicode": "25AA", "html": "▪", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "▫️", "name": "white small square", "shortname": ":white_small_square:", "unicode": "25AB FE0F", "html": "▫️", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🏳️", "name": "white flag", "shortname": ":white_flag:", "unicode": "1F3F3 FE0F", "html": "🏳️", "category": "Flags (flag)", "order": ""}, + {"emoji": "🏳️‍🌈", "name": "rainbow flag", "shortname": ":rainbow_flag:", "unicode": "1F3F3 FE0F 200D 1F308", "html": "🏳️‍🌈", "category": "Flags (flag)", "order": ""}, + {"emoji": "🏴‍☠️", "name": "pirate flag", "shortname": ":pirate_flag:", "unicode": "1F3F4 200D 2620 FE0F", "html": "🏴‍☠️", "category": "Flags (flag)", "order": ""}, + {"emoji": "🏴‍☠", "name": "pirate flag", "shortname": ":pirate_flag:", "unicode": "1F3F4 200D 2620", "html": "🏴‍☠", "category": "Flags (flag)", "order": ""}, + {"emoji": "🇺🇳", "name": "flag: United Nations", "shortname": ":United_Nations:", "unicode": "1F1FA 1F1F3", "html": "🇺🇳", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🏴󠁧󠁢󠁥󠁮󠁧󠁿", "name": "flag: England", "shortname": ":England:", "unicode": "1F3F4 E0067 E0062 E0065 E006E E0067 E007F", "html": "🏴󠁧󠁢󠁥󠁮󠁧󠁿", "category": "Flags (subdivision-flag)", "order": ""}, + {"emoji": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", "name": "flag: Scotland", "shortname": ":Scotland:", "unicode": "1F3F4 E0067 E0062 E0073 E0063 E0074 E007F", "html": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", "category": "Flags (subdivision-flag)", "order": ""}, + {"emoji": "🏴󠁧󠁢󠁷󠁬󠁳󠁿", "name": "Wales", "shortname": ":wales:", "unicode": "1F3F4 E0067 E0062 E0077 E006C E0073 E007F", "html": "🏴󠁧󠁢󠁷󠁬󠁳󠁿", "category": "(subdivision-flag)", "order": ""} + ] +} \ No newline at end of file diff --git a/src/environ_vtab.cc b/src/environ_vtab.cc index 3211f801..d62b0895 100644 --- a/src/environ_vtab.cc +++ b/src/environ_vtab.cc @@ -41,8 +41,8 @@ extern char** environ; const char* const ENVIRON_CREATE_STMT = R"( -- Access lnav's environment variables through this table. CREATE TABLE environ ( - name text PRIMARY KEY, - value text + name TEXT PRIMARY KEY, + value TEXT ); )"; diff --git a/src/files_sub_source.cc b/src/files_sub_source.cc index 53ffd571..0bac62cb 100644 --- a/src/files_sub_source.cc +++ b/src/files_sub_source.cc @@ -302,7 +302,6 @@ files_sub_source::text_attrs_for_line(textview_curses& tc, bool selected = lnav_data.ld_mode == ln_mode_t::FILES && line == tc.get_selection(); const auto& fc = lnav_data.ld_active_files; - auto& vcolors = view_colors::singleton(); const auto dim = tc.get_dimensions(); auto filename_width = std::min(fc.fc_largest_path_length, @@ -360,8 +359,7 @@ files_sub_source::text_attrs_for_line(textview_curses& tc, VC_GRAPHIC.value(visible)); if (visible == ACS_DIAMOND) { value_out.emplace_back(line_range{2, 3}, - VC_FOREGROUND.value( - vcolors.ansi_to_theme_color(COLOR_GREEN))); + VC_FOREGROUND.value(COLOR_GREEN)); } auto lr = line_range{ @@ -372,9 +370,7 @@ files_sub_source::text_attrs_for_line(textview_curses& tc, lr.lr_start = this->fss_last_line_len; lr.lr_end = -1; - value_out.emplace_back(lr, - VC_FOREGROUND.value( - vcolors.ansi_to_theme_color(COLOR_YELLOW))); + value_out.emplace_back(lr, VC_FOREGROUND.value(COLOR_YELLOW)); } size_t diff --git a/src/filter_sub_source.cc b/src/filter_sub_source.cc index 245039b1..6fc6087e 100644 --- a/src/filter_sub_source.cc +++ b/src/filter_sub_source.cc @@ -335,7 +335,6 @@ filter_sub_source::text_attrs_for_line(textview_curses& tc, int line, string_attrs_t& value_out) { - auto& vcolors = view_colors::singleton(); textview_curses* top_view = *lnav_data.ld_view_stack.top(); text_sub_source* tss = top_view->get_sub_source(); filter_stack& fs = tss->get_filters(); @@ -353,8 +352,7 @@ filter_sub_source::text_attrs_for_line(textview_curses& tc, line_range lr{2, 3}; value_out.emplace_back(lr, VC_GRAPHIC.value(enabled)); if (tf->is_enabled()) { - value_out.emplace_back( - lr, VC_FOREGROUND.value(vcolors.ansi_to_theme_color(COLOR_GREEN))); + value_out.emplace_back(lr, VC_FOREGROUND.value(COLOR_GREEN)); } role_t fg_role = tf->get_type() == text_filter::INCLUDE ? role_t::VCR_OK diff --git a/src/help.md b/src/help.md new file mode 100644 index 00000000..8dc51e0f --- /dev/null +++ b/src/help.md @@ -0,0 +1,525 @@ +# lnav + +A fancy log file viewer for the terminal. + +## Overview + +The Logfile Navigator, **lnav**, is an enhanced log file viewer that +takes advantage of any semantic information that can be gleaned from +the files being viewed, such as timestamps and log levels. Using this +extra semantic information, lnav can do things like interleaving +messages from different files, generate histograms of messages over +time, and providing hotkeys for navigating through the file. It is +hoped that these features will allow the user to quickly and +efficiently zero in on problems. + +## Opening Paths/URLs + +The main arguments to lnav are the files, directories, glob patterns, +or URLs to be viewed. If no arguments are given, the default syslog +file for your system will be opened. These arguments will be polled +periodically so that any new data or files will be automatically +loaded. If a previously loaded file is removed or replaced, it will +be closed and the replacement opened. + +Note: When opening SFTP URLs, if the password is not provided for the +host, the SSH agent can be used to do authentication. + +## Options + +Lnav takes a list of files to view and/or you can use the flag +arguments to load well-known log files, such as the syslog log +files. The flag arguments are: + +* `-a` Load all of the most recent log file types. +* `-r` Recursively load files from the given directory hierarchies. +* `-R` Load older rotated log files as well. + +When using the flag arguments, lnav will look for the files relative +to the current directory and its parent directories. In other words, +if you are working within a directory that has the well-known log +files, those will be preferred over any others. + +If you do not want the default syslog file to be loaded when +no files are specified, you can pass the `-N` flag. + +Any files given on the command-line are scanned to determine their log +file format and to create an index for each line in the file. You do +not have to manually specify the log file format. The currently +supported formats are: syslog, apache, strace, tcsh history, and +generic log files with timestamps. + +Lnav will also display data piped in on the standard input. The +following options are available when doing so: + +* `-t` Prepend timestamps to the lines of data being read in + on the standard input. +* `-w file` Write the contents of the standard input to this file. + +To automatically execute queries or lnav commands after the files +have been loaded, you can use the following options: + +* `-c cmd` A command, query, or file to execute. The first character + determines the type of operation: a colon (`:`) is used for the + built-in commands; a semi-colon (`;`) for SQL queries; and a + pipe symbol (`|`) for executing a file containing other + commands. For example, to open the file "foo.log" and go + to the tenth line in the file, you can do: + + ```shell + lnav -c ':goto 10' foo.log + ``` + + This option can be given multiple times to execute multiple + operations in sequence. +* `-f file` A file that contains commands, queries, or files to execute. + This option is a shortcut for `-c '|file'`. You can use a dash + (`-`) to execute commands from the standard input. + +To execute commands/queries without opening the interactive text UI, +you can pass the `-n` option. This combination of options allows you to +write scripts for processing logs with lnav. For example, to get a list +of IP addresses that dhclient has bound to in CSV format: + +```lnav +#! /usr/bin/lnav -nf + +# Usage: dhcp_ip.lnav /var/log/messages +# Only include lines that look like: +# Apr 29 00:31:56 example-centos5 dhclient: bound to 10.1.10.103 -- renewal in 9938 seconds. + +:filter-in dhclient: bound to + +# The log message parser will extract the IP address +# as col_0, so we select that and alias it to "dhcp_ip". +;SELECT DISTINCT col_0 AS dhcp_ip FROM logline; + +# Finally, write the results of the query to stdout. +:write-csv-to - +``` + +## Display + +The main part of the display shows the log lines from the files interleaved +based on time-of-day. New lines are automatically loaded as they are appended +to the files and, if you are viewing the bottom of the files, lnav will scroll +down to display the new lines, much like `tail -f`. + +On color displays, the lines will be highlighted as follows: + +* Errors will be colored in ${ansi_red}red${ansi_norm}; +* warnings will be ${ansi_yellow}yellow${ansi_norm}; +* boundaries between days will be ${ansi_underline}underlined${ansi_norm}; and +* various color highlights will be applied to: IP addresses, SQL keywords, + XML tags, file and line numbers in Java backtraces, and quoted strings. + +To give you an idea of where you are spatially, the right side of the +display has a proportionally sized 'scroll bar' that indicates your +current position in the files. The scroll bar will also show areas of +the file where warnings or errors are detected by coloring the bar +yellow or red, respectively. Tick marks will also be added to the +left and right hand side of the bar, for search hits and bookmarks. + +A bar on the left side is color coded and broken up to indicate which +messages are from the same file. Pressing the left-arrow or `h` will +reveal the source file names for each message and pressing again will +show the full paths. + +When at the bottom of the log view, a summary line will be displayed on the +right-hand-side to give you some more information about your logs, including: +how long ago the last message was generated, the number of log files, the +error rate, and how much time the logs cover. The error rate display shows +the errors-per-minute over the last five minutes. A bar chart is also +overlaid on the "Error rate" label to show the error rate over the past ten +seconds. For example, if there have not been many errors in the past five +minutes and there is a sudden spike, the bar chart will fill up completely. +But, if there has been a steady stream of errors, then the chart will only +partially fill based on the recent error frequency. + +Above and below the main body are status lines that display: + +* the current time; +* the name of the file the top line was pulled from; +* the log format for the top line; +* the current view; +* the line number for the top line in the display; +* the current search hit, the total number of hits, and the search term; + +If the view supports filtering, there will be a status line showing the +following: + +* the number of enabled filters and the total number of filters; +* the number of lines not displayed because of filtering. + +To edit the filters, you can press TAB to change the focus from the main +view to the filter editor. The editor allows you to create, enable/disable, +and delete filters easily. + +Finally, the last line on the display is where you can enter search +patterns and execute internal commands, such as converting a +unix-timestamp into a human-readable date. The command-line is +implemented using the readline library, so the usual set of keyboard +shortcuts are available. Most commands and searches also support +tab-completion. + +The body of the display is also used to display other content, such +as: the help file, histograms of the log messages over time, and +SQL results. The views are organized into a stack so that any time +you activate a new view with a key press or command, the new view +is pushed onto the stack. Pressing the same key again will pop the +view off of the stack and return you to the previous view. Note +that you can always use `q` to pop the top view off of the stack. + +## Default Key Bindings + +### Views + +| Key(s) | Action | +| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **?** | View/leave this help message. | +| **q** | Leave the current view or quit the program when in the log file view. | +| Q | Similar to `q`, except it will try to sync the top time between the current and former views. For example, when leaving the spectrogram view with `Q`, the top time in that view will be matched to the top time in the log view. | +| TAB | Toggle focusing on the filter editor or the main view. | +| a/A | Restore the view that was previously popped with `q`/`Q`. The `A` hotkey will try to match the top times between the two views. | +| X | Close the current text file or log file. | + +### Spatial Navigation + +| Key(s) | Action | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| g/Home | Move to the top of the file. | +| G/End | Move to the end of the file. If the view is already at the end, it will move to the last line. | +| SPACE/PgDn | Move down a page. | +| b/PgUp | Move up a page. | +| j/↓ | Move down a line. | +| k/↑ | Move up a line. | +| h/← | Move to the left. In the log view, moving left will reveal the source log file names for each line. Pressing again will reveal the full path. | +| l/→ | Move to the right. | +| H/Shift ← | Move to the left by a smaller increment. | +| L/Shift → | Move to the right by a smaller increment. | +| e/E | Move to the next/previous error. | +| w/W | Move to the next/previous warning. | +| n/N | Move to the next/previous search hit. When pressed repeatedly within a short time, the view will move at least a full page at a time instead of moving to the next hit. | +| f/F | Move to the next/previous file. In the log view, this moves to the next line from a different file. In the text view, this rotates the view to the next file. | +| >/< | Move horizontally to the next/previous search hit. | +| o/O | Move forward/backward to the log message with a matching 'operation ID' (opid) field. | +| u/U | Move forward/backward through any user bookmarks you have added using the 'm' key. This hotkey will also jump to the start of any log partitions that have been created with the 'partition-name' command. | +| s/S | Move to the next/previous "slow down" in the log message rate. A slow down is detected by measuring how quickly the message rate has changed over the previous several messages. For example, if one message is logged every second for five seconds and then the last message arrives five seconds later, the last message will be highlighted as a slow down. | +| {/} | Move to the previous/next location in history. Whenever you jump to a new location in the view, the location will be added to the history. The history is not updated when using only the arrow keys. | + +### Chronological Navigation + +| Key(s) | Action | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| d/D | Move forward/backward 24 hours from the current position in the log file. | +| 1-6/Shift 1-6 | Move to the next/previous n'th ten minute of the hour. For example, '4' would move to the first log line in the fortieth minute of the current hour in the log. And, '6' would move to the next hour boundary. | +| 7/8 | Move to the previous/next minute. | +| 0/Shift 0 | Move to the next/previous day boundary. | +| r/R | Move forward/backward based on the relative time that was last used with the 'goto' command. For example, executing ':goto a minute later' will move the log view forward a minute and then pressing 'r' will move it forward a minute again. Pressing 'R' will then move the view in the opposite direction, so backwards a minute. | + +### Bookmarks + +| Key(s) | Action | +| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| m | Mark/unmark the line at the top of the display. The line will be highlighted with reverse video to indicate that it is a user bookmark. You can use the `u` hotkey to iterate through marks you have added. | +| M | Mark/unmark all the lines between the top of the display and the last line marked/unmarked. | +| J | Mark/unmark the next line after the previously marked line. | +| K | Like `J` except it toggles the mark on the previous line. | +| c | Copy the marked text to the X11 selection buffer or OS X clipboard. | +| C | Clear all marked lines. | + +### Display options + +| Key(s) | Action | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| P | Switch to/from the pretty-printed view of the log or text files currently displayed. In this view, structured data, such as XML, will be reformatted to make it easier to read. | +| t | Switch to/from the text file view. The text file view is for any files that are not recognized as log files. | +| = | Pause/unpause loading of new file data. | +| Ctrl-L | (Lo-fi mode) Exit screen-mode and write the displayed log lines in plain text to the terminal until a key is pressed. Useful for copying long lines from the terminal without picking up any of the extra decorations. | +| T | Toggle the display of the "elapsed time" column that shows the time elapsed since the beginning of the logs or the offset from the previous bookmark. Sharp changes in the message rate are highlighted by coloring the separator between the time column and the log message. A red highlight means the message rate has slowed down and green means it has sped up. You can use the "s/S" hotkeys to scan through the slow downs. | +| i | View/leave a histogram of the log messages over time. The histogram counts the number of displayed log lines for each bucket of time. The bars are layed out horizontally with colored segments representing the different log levels. You can use the `z` hotkey to change the size of the time buckets (e.g. ten minutes, one hour, one day). | +| I | Switch between the log and histogram views while keeping the time displayed at the top of each view in sync. For example, if the top line in the log view is "11:40", hitting `I` will switch to the histogram view and scrolled to display "11:00" at the top (if the zoom level is hours). | +| z/Shift Z | Zoom in or out one step in the histogram view. | +| v | Switch to/from the SQL result view. | +| V | Switch between the log and SQL result views while keeping the top line number in the log view in sync with the log_line column in the SQL view. For example, doing a query that selects for "log_idle_msecs" and "log_line", you can move the top of the SQL view to a line and hit 'V' to switch to the log view and move to the line number that was selected in the "log_line" column. If there is no "log_line" column, lnav will find the first column with a timestamp and move to corresponding time in the log view. | +| TAB/Shift TAB | In the SQL result view, cycle through the columns that are graphed. Initially, all number values are displayed in a stacked graph. Pressing TAB will change the display to only graph the first column. Repeatedly pressing TAB will cycle through the columns until they are all graphed again. | +| p | In the log view: enable or disable the display of the fields that the log message parser knows about or has discovered. This overlay is temporarily enabled when the semicolon key (;) is pressed so that it is easier to write queries. | +| | In the DB view: enable or disable the display of values in columns containing JSON-encoded values in the top row. The overlay will display the JSON-Pointer reference and value for all fields in the JSON data. | +| CTRL-W | Toggle word-wrapping. | +| CTRL-P | Show/hide the data preview panel that may be opened when entering commands or SQL queries. | +| CTRL-F | Toggle the enabled/disabled state of all filters in the current view. | +| x | Toggle the hiding of log message fields. The hidden fields will be replaced with three bullets and highlighted in yellow. | +| F2 | Toggle mouse support. | + +### Query + +| Key(s) | Action | +| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **/**_regexp_ | Start a search for the given regular expression. The search is live, so when there is a pause in typing, the currently running search will be canceled and a new one started. The first ten lines that match the search will be displayed in the preview window at the bottom of the view. History is maintained for your searches so you can rerun them easily. Words that are currently displayed are also available for tab-completion, so you can easily search for values without needing to copy-and-paste the string. If there is an error encountered while trying to interpret the expression, the error will be displayed in red on the status line. While the search is active, the 'hits' field in the status line will be green, when finished it will turn back to black. | +| **:**<command> | Execute an internal command. The commands are listed below. History is also supported in this context as well as tab-completion for commands and some arguments. The result of the command replaces the command you typed. | +| **;**<sql> | Execute an SQL query. Most supported log file formats provide a sqlite virtual table backend that can be used in queries. See the SQL section below for more information. | +| **|**<script> [arg1...] | Execute an lnav script contained in a format directory (e.g. \~/.lnav/formats/default). The script can contain lines starting with `:`, `;`, or `\|` to execute commands, SQL queries or execute other files in lnav. Any values after the script name are treated as arguments can be referenced in the script using `\$1`, `\$2`, and so on, like in a shell script. | +| CTRL+], ESCAPE | Abort command-line entry started with `/`, `:`, `;`, or `\|`. | + + Note: The regular expression format used by is PCRE + (Perl-Compatible Regular Expressions). For example, + if you wanted to search for ethernet device names, + regardless of their ID number, you can type: + + eth\\d+ + + You can find more information about Perl regular + expressions at: + + http://perldoc.perl.org/perlre.html + + If the search string is not valid PCRE, a search + is done for the exact string instead of doing a + regex search. + + +## Session + +| Key(s) | Action | +| ------ | ---------------------------------------------------------------------------------------------------------------------------------------- | +| CTRL-R | Reset the session state. This will save the current session state (filters, highlights) and then reset the state to the factory default. | + +## Filter Editor + +The following hotkeys are only available when the focus is on the filter +editor. You can change the focus by pressing TAB. + +| Key(s) | Action | +| ------------- | ------------------------------------------------------------------- | +| q | Switch the focus back to the main view. | +| j/↓ | Select the next filter. | +| k/↑ | Select the previous filter. | +| o | Create a new "out" filter. | +| i | Create a new "in" filter . | +| SPACE | Toggle the enabled/disabled state of the currently selected filter. | +| t | Toggle the type of filter between "in" and "out". | +| ENTER | Edit the selected filter. | +| D | Delete the selected filter. | + +## Mouse Support (experimental) + +If you are using Xterm, or a compatible terminal, you can use the mouse to +mark lines of text and move the view by grabbing the scrollbar. + +NOTE: You need to manually enable this feature by setting the LNAV_EXP +environment variable to "mouse". F2 toggles mouse support. + +## SQL Queries (experimental) + +Lnav has support for performing SQL queries on log files using the +Sqlite3 "virtual" table feature. For all supported log file types, +lnav will create tables that can be queried using the subset of SQL +that is supported by Sqlite3. For example, to get the top ten URLs +being accessed in any loaded Apache log files, you can execute: + +```lnav +;SELECT cs_uri_stem, count(*) AS total FROM access_log + GROUP BY cs_uri_stem ORDER BY total DESC LIMIT 10; +``` + +The query result view shows the results and graphs any numeric +values found in the result, much like the histogram view. + +The builtin set of log tables are listed below. Note that only the +log messages that match a particular format can be queried by a +particular table. You can find the file format and table name for +the top log message by looking in the upper right hand corner of the +log file view. + +Some commonly used format tables are: + +| Name | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| access_log | Apache common access log format | +| syslog_log | Syslog format | +| strace_log | Strace log format | +| generic_log | 'Generic' log format. This table contains messages from files that have a very simple format with a leading timestamp followed by the message. | + +NOTE: You can get a dump of the schema for the internal tables, and +any attached databases, by running the `.schema` SQL command. + +The columns available for the top log line in the view will +automatically be displayed after pressing the semicolon (`;`) key. +All log tables contain at least the following columns: + +| Column | Description | +| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| log_line | The line number in the file, starting at zero. | +| log_part | The name of the partition. You can change this column using an UPDATE SQL statement or with the 'partition-name' command. After a value is set, the following log messages will have the same partition name up until another name is set. | +| log_time | The time of the log entry. | +| log_idle_msecs | The amount of time, in milliseconds, between the current log message and the previous one. | +| log_level | The log level (e.g. info, error, etc...). | +| log_mark | The bookmark status for the line. This column can be written to using an UPDATE query. | +| log_path | The full path to the file. | +| log_text | The raw line of text. Note that this column is not included in the result of a 'select *', but it does exist. | + +The following tables include the basic columns as listed above and +include a few more columns since the log file format is more +structured. + +* `syslog_log` + + | Column | Description | + | ------------ | ---------------------------------------------------- | + | log_hostname | The hostname the message was received from. | + | log_procname | The name of the process that sent the message. | + | log_pid | The process ID of the process that sent the message. | + +* `access_log` (The column names are the same as those in the + Microsoft LogParser tool.) + + | Column | Description | + | ------------- | ----------------------------------------- | + | c_ip | The client IP address. | + | cs_username | The client user name. | + | cs_method | The HTTP method. | + | cs_uri_stem | The stem portion of the URI. | + | cs_uri_query | The query portion of the URI. | + | cs_version | The HTTP version string. | + | sc_status | The status number returned to the client. | + | sc_bytes | The number of bytes sent to the client. | + | cs_referrer | The URL of the referring page. | + | cs_user_agent | The user agent string. | + +* `strace_log` (Currently, you need to run strace with the `-tt -T` + options so there are timestamps for each function call.) + + | Column | Description | + | ----------- | ---------------------------------------- | + | funcname | The name of the syscall. | + | result | The result code. | + | duration | The amount of time spent in the syscall. | + | arg0 - arg9 | The arguments passed to the syscall. | + +These tables are created dynamically and not stored in memory or on +disk. If you would like to persist some information from the tables, +you can attach another database and create tables in that database. +For example, if you wanted to save the results from the earlier +example of a top ten query into the "/tmp/topten.db" file, you can do: + +```lnav +;ATTACH DATABASE '/tmp/topten.db' AS topten; +;CREATE TABLE topten.foo AS SELECT cs_uri_stem, count(*) AS total + FROM access_log GROUP BY cs_uri_stem ORDER BY total DESC + LIMIT 10; +``` + +## Dynamic logline Table (experimental) + +(NOTE: This feature is still very new and not completely reliable yet, +use with care.) + +For log formats that lack message structure, lnav can parse the log +message and attempt to extract any data fields that it finds. This +feature is available through the `logline` log table. This table is +dynamically created and defined based on the message at the top of +the log view. For example, given the following log message from "sudo", +lnav will create the "logline" table with columns for "TTY", "PWD", +"USER", and "COMMAND": + +``` +May 24 06:48:38 Tim-Stacks-iMac.local sudo[76387]: stack : TTY=ttys003 ; PWD=/Users/stack/github/lbuild ; USER=root ; COMMAND=/bin/echo Hello, World! +``` + +Queries executed against this table will then only return results for +other log messages that have the same format. So, if you were to +execute the following query while viewing the above line, you might +get the following results: + +```lnav +;SELECT USER,COMMAND FROM logline; +``` + +| USER | COMMAND | +| ---- | ------------------------- | +| root | /bin/echo Hello, World! | +| mal | /bin/echo Goodbye, World! | + +The log parser works by examining each message for key/value pairs +separated by an equal sign (=) or a colon (:). For example, in the +previous example of a "sudo" message, the parser sees the "USER=root" +string as a pair where the key is "USER" and the value is "root". +If no pairs can be found, then anything that looks like a value is +extracted and assigned a numbered column. For example, the following +line is from "dhcpd": + +``` +Sep 16 22:35:57 drill dhcpd: DHCPDISCOVER from 00:16:ce:54:4e:f3 via hme3 +``` + +In this case, the lnav parser recognizes that "DHCPDISCOVER", the MAC +address and the "hme3" device name are values and not normal words. So, +it builds a table with three columns for each of these values. The +regular words in the message, like "from" and "via", are then used to +find other messages with a similar format. + +If you would like to execute queries against log messages of different +formats at the same time, you can use the 'create-logline-table' command +to permanently create a table using the top line of the log view as a +template. + +## Other SQL Features + +Environment variables can be used in SQL statements by prefixing the +variable name with a dollar-sign (\$). For example, to read the value of +the `HOME` variable, you can do: + +```lnav +;SELECT \$HOME; +``` + +To select the syslog messages that have a hostname field that is equal +to the `HOSTNAME` variable: + +```lnav +;SELECT * FROM syslog_log WHERE log_hostname = \$HOSTNAME; +``` + +NOTE: Variable substitution is done for fields in the query and is not +a plain text substitution. For example, the following statement +WILL NOT WORK: + +```lnav +;SELECT * FROM \$TABLE_NAME; -- Syntax error +``` + +Access to lnav's environment variables is also available via the "environ" +table. The table has two columns (name, value) and can be read and written +to using SQL SELECT, INSERT, UPDATE, and DELETE statements. For example, +to set the "FOO" variable to the value "BAR": + +```lnav +;INSERT INTO environ SELECT 'FOO', 'BAR'; +``` + +As a more complex example, you can set the variable "LAST" to the last +syslog line number by doing: + +```lnav +;INSERT INTO environ SELECT 'LAST', (SELECT max(log_line) FROM syslog_log); +``` + +A delete will unset the environment variable: + +```lnav +;DELETE FROM environ WHERE name='LAST'; +``` + +The table allows you to easily use the results of a SQL query in lnav +commands, which is especially useful when scripting lnav. + +## Contact + +For more information, visit the lnav website at: + +http://lnav.org + +For support questions, email: + +lnav@googlegroups.com +support@lnav.org diff --git a/src/help_text_formatter.cc b/src/help_text_formatter.cc index 33bc008e..4a5e78c1 100644 --- a/src/help_text_formatter.cc +++ b/src/help_text_formatter.cc @@ -33,6 +33,7 @@ #include "help_text_formatter.hh" #include "base/ansi_scrubber.hh" +#include "base/attr_line.builder.hh" #include "base/string_util.hh" #include "config.h" #include "fmt/format.h" @@ -85,6 +86,7 @@ format_help_text_for_term(const help_text& ht, { static const size_t body_indent = 2; + attr_line_builder alb(out); text_wrap_settings tws; size_t start_index = out.get_string().length(); @@ -92,11 +94,9 @@ format_help_text_for_term(const help_text& ht, switch (ht.ht_context) { case help_context_t::HC_COMMAND: { - out.append("Synopsis"_h2) - .append("\n") - .append(body_indent, ' ') - .append(":") - .append(lnav::roles::symbol(ht.ht_name)); + auto line_start = out.al_string.length(); + + out.append(":").append(lnav::roles::symbol(ht.ht_name)); for (const auto& param : ht.ht_parameters) { out.append(" "); if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) { @@ -116,26 +116,27 @@ format_help_text_for_term(const help_text& ht, out.append("]"); } } - out.append(" - ") + out.with_attr(string_attr{ + line_range{(int) line_start, (int) out.get_string().length()}, + VC_ROLE.value(role_t::VCR_H3), + }); + alb.append("\n") + .append( + lnav::roles::table_border(repeat("\u2550", tws.tws_width))) + .append("\n") + .indent(body_indent) .append(attr_line_t::from_ansi_str(ht.ht_summary), - &tws.with_indent(body_indent + 2)) + &tws.with_indent(body_indent)) .append("\n"); break; } case help_context_t::HC_SQL_FUNCTION: case help_context_t::HC_SQL_TABLE_VALUED_FUNCTION: { - size_t line_start = body_indent; + auto line_start = out.al_string.length(); bool break_all = false; bool needs_comma = false; - if (!synopsis_only) { - out.append("Synopsis"_h2).append("\n"); - } - - line_start = out.length(); - out.append(body_indent, ' ') - .append(lnav::roles::symbol(ht.ht_name)) - .append("("); + out.append(lnav::roles::symbol(ht.ht_name)).append("("); for (const auto& param : ht.ht_parameters) { if (!param.ht_flag_name && needs_comma) { out.append(", "); @@ -146,7 +147,7 @@ format_help_text_for_term(const help_text& ht, { out.append("\n"); line_start = out.get_string().length(); - out.append(body_indent + strlen(ht.ht_name) + 1, ' '); + alb.indent(body_indent + strlen(ht.ht_name) + 1); break_all = true; } if (param.ht_flag_name) { @@ -168,22 +169,33 @@ format_help_text_for_term(const help_text& ht, } needs_comma = true; } - out.append(") -- "); + out.append(")"); + out.with_attr(string_attr{ + line_range{(int) line_start, (int) out.get_string().length()}, + VC_ROLE.value(role_t::VCR_H3), + }); if (break_all) { - out.append("\n").append(body_indent + strlen(ht.ht_name) + 1, - ' '); + alb.append("\n") + .append(lnav::roles::table_border( + repeat("\u2550", tws.tws_width))) + .append("\n") + .indent(body_indent + strlen(ht.ht_name) + 1); + } else { + alb.append("\n") + .append(lnav::roles::table_border( + repeat("\u2550", tws.tws_width))) + .append("\n") + .indent(body_indent); } out.append(attr_line_t::from_ansi_str(ht.ht_summary), - &tws.with_indent(body_indent + 2)) + &tws.with_indent(body_indent)) .append("\n"); break; } case help_context_t::HC_SQL_COMMAND: { - out.append("Synopsis"_h2) - .append("\n") - .append(body_indent, ' ') - .append(";") - .append(lnav::roles::symbol(ht.ht_name)); + auto line_start = out.al_string.length(); + + out.append(";").append(lnav::roles::symbol(ht.ht_name)); for (const auto& param : ht.ht_parameters) { out.append(" "); if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) { @@ -203,7 +215,15 @@ format_help_text_for_term(const help_text& ht, out.append("]"); } } - out.append(" - ") + out.with_attr(string_attr{ + line_range{(int) line_start, (int) out.get_string().length()}, + VC_ROLE.value(role_t::VCR_H3), + }); + alb.append("\n") + .append( + lnav::roles::table_border(repeat("\u2550", tws.tws_width))) + .append("\n") + .indent(body_indent) .append(attr_line_t::from_ansi_str(ht.ht_summary), &tws.with_indent(body_indent + 2)) .append("\n"); @@ -211,14 +231,10 @@ format_help_text_for_term(const help_text& ht, } case help_context_t::HC_SQL_INFIX: case help_context_t::HC_SQL_KEYWORD: { - size_t line_start = body_indent; + size_t line_start = out.get_string().length(); bool break_all = false; bool is_infix = ht.ht_context == help_context_t::HC_SQL_INFIX; - if (!synopsis_only) { - out.append("Synopsis"_h2).append("\n"); - } - out.append(body_indent, ' '); if (is_infix) { out.append(ht.ht_name); } else { @@ -232,7 +248,7 @@ format_help_text_for_term(const help_text& ht, { out.append("\n"); line_start = out.get_string().length(); - out.append(body_indent + strlen(ht.ht_name) + 1, ' '); + alb.indent(body_indent + strlen(ht.ht_name) + 1); break_all = true; } if (param.ht_nargs == help_nargs_t::HN_ZERO_OR_MORE @@ -312,13 +328,20 @@ format_help_text_for_term(const help_text& ht, out.append("]"); } } - if (!synopsis_only) { - out.append("\n\n") - .append(body_indent, ' ') + out.with_attr(string_attr{ + line_range{(int) line_start, (int) out.get_string().length()}, + VC_ROLE.value(role_t::VCR_H3), + }); + if (synopsis_only) { + out.append("\n"); + } else { + alb.append("\n") + .append(lnav::roles::table_border( + repeat("\u2550", tws.tws_width))) + .append("\n") + .indent(body_indent) .append(ht.ht_summary, &tws) .append("\n"); - } else { - out.append("\n"); } break; } @@ -334,8 +357,8 @@ format_help_text_for_term(const help_text& ht, = std::max(strlen(param.ht_name), max_param_name_width); } - out.append(ht.ht_parameters.size() == 1 ? "Parameter"_h2 - : "Parameters"_h2) + out.append(ht.ht_parameters.size() == 1 ? "Parameter"_h4 + : "Parameters"_h4) .append("\n"); for (const auto& param : ht.ht_parameters) { @@ -343,7 +366,7 @@ format_help_text_for_term(const help_text& ht, continue; } - out.append(body_indent, ' ') + alb.indent(body_indent) .append(lnav::roles::variable(param.ht_name)) .append(max_param_name_width - strlen(param.ht_name), ' ') .append(" ") @@ -360,7 +383,7 @@ format_help_text_for_term(const help_text& ht, = std::max(strlen(result.ht_name), max_result_name_width); } - out.append(ht.ht_results.size() == 1 ? "Result"_h2 : "Results"_h2) + out.append(ht.ht_results.size() == 1 ? "Result"_h4 : "Results"_h4) .append("\n"); for (const auto& result : ht.ht_results) { @@ -368,7 +391,7 @@ format_help_text_for_term(const help_text& ht, continue; } - out.append(body_indent, ' ') + alb.indent(body_indent) .append(lnav::roles::variable(result.ht_name)) .append(max_result_name_width - strlen(result.ht_name), ' ') .append(" ") @@ -398,17 +421,18 @@ format_help_text_for_term(const help_text& ht, } stable_sort(related_refs.begin(), related_refs.end()); - out.append("See Also"_h2).append("\n").append(body_indent, ' '); + alb.append("See Also"_h4).append("\n").indent(body_indent); bool first = true; - size_t line_start = out.length(); + size_t line_start = out.get_string().length(); for (const auto& ref : related_refs) { if (!first) { out.append(", "); } - if ((out.length() - line_start + ref.length()) > width) { - out.append("\n").append(body_indent, ' '); - line_start = out.length(); + if ((out.get_string().length() - line_start + ref.length()) > width) + { + alb.append("\n").indent(body_indent); + line_start = out.get_string().length(); } out.append(lnav::roles::symbol(ref)); first = false; @@ -422,59 +446,58 @@ format_example_text_for_term(const help_text& ht, size_t width, attr_line_t& out) { - text_wrap_settings tws; + if (ht.ht_example.empty()) { + return; + } - tws.with_width(width); + attr_line_builder alb(out); + int count = 1; - if (!ht.ht_example.empty()) { - int count = 1; + out.append(ht.ht_example.size() == 1 ? "Example"_h4 : "Examples"_h4) + .append("\n"); + for (const auto& ex : ht.ht_example) { + attr_line_t ex_line(ex.he_cmd); + const char* prompt = ""; + text_wrap_settings tws; - out.append(ht.ht_example.size() == 1 ? "Example"_h2 : "Examples"_h2) - .append("\n"); - for (const auto& ex : ht.ht_example) { - attr_line_t ex_line(ex.he_cmd); - size_t keyword_offset = 0; - const char* space = strchr(ex.he_cmd, ' '); - const char* prompt = ""; - - if (space) { - keyword_offset = space - ex.he_cmd; - } - if (count > 1) { - out.append("\n"); - } - switch (ht.ht_context) { - case help_context_t::HC_COMMAND: - ex_line.insert(0, 1, ' '); - ex_line.insert(0, 1, ':'); - ex_line.insert(1, ht.ht_name); - readline_command_highlighter(ex_line, 0); - break; - case help_context_t::HC_SQL_INFIX: - case help_context_t::HC_SQL_KEYWORD: - case help_context_t::HC_SQL_FUNCTION: - case help_context_t::HC_SQL_TABLE_VALUED_FUNCTION: - readline_sqlite_highlighter(ex_line, 0); - prompt = ";"; - break; - default: - break; - } + tws.with_width(width); + if (count > 1) { + out.append("\n"); + } + switch (ht.ht_context) { + case help_context_t::HC_COMMAND: + ex_line.insert(0, 1, ' '); + ex_line.insert(0, 1, ':'); + ex_line.insert(1, ht.ht_name); + readline_command_highlighter(ex_line, 0); + break; + case help_context_t::HC_SQL_INFIX: + case help_context_t::HC_SQL_KEYWORD: + case help_context_t::HC_SQL_FUNCTION: + case help_context_t::HC_SQL_TABLE_VALUED_FUNCTION: + readline_sqlite_highlighter(ex_line, 0); + prompt = ";"; + break; + default: + break; + } - out.append("#") - .append(fmt::to_string(count)) - .append(" ") - .append(ex.he_description, &tws.with_indent(3)) - .append(":\n ") - .append(prompt) - .append(ex_line, &tws.with_indent(3 + keyword_offset + 1)) - .append("\n") - .append(3, ' ') - .append(eval(ht, ex), &tws.with_indent(3)) - .append("\n"); + ex_line.pad_to(50).with_attr_for_all( + VC_ROLE.value(role_t::VCR_QUOTED_CODE)); + alb.append("#") + .append(fmt::to_string(count)) + .append(" ") + .append(ex.he_description, &tws.with_indent(3)) + .append(":\n") + .indent(3) + .append(prompt, VC_ROLE.value(role_t::VCR_QUOTED_CODE)) + .append(ex_line, &tws.with_indent(3).with_padding_indent(3)) + .append("\n") + .indent(3) + .append(eval(ht, ex), &tws.with_indent(3)) + .append("\n"); - count += 1; - } + count += 1; } } diff --git a/src/init.sql b/src/init.sql index d12d7ddc..6495e16f 100644 --- a/src/init.sql +++ b/src/init.sql @@ -66,24 +66,24 @@ INSERT INTO http_status_codes VALUES (510, 'Not Extended'); INSERT INTO http_status_codes VALUES (511, 'Network Authentication Required'); CREATE TABLE lnav_example_log ( - log_line integer PRIMARY KEY, - log_part text collate naturalnocase, + log_line INTEGER PRIMARY KEY, + log_part TEXT collate naturalnocase, log_time datetime, log_actual_time datetime hidden, log_idle_msecs int, - log_level text collate loglevel, + log_level TEXT collate loglevel, log_mark boolean, log_comment TEXT, log_tags TEXT, log_filters TEXT, - ex_procname text collate 'BINARY', - ex_duration integer, + ex_procname TEXT collate 'BINARY', + ex_duration INTEGER, log_time_msecs int hidden, - log_path text hidden collate naturalnocase, - log_text text hidden, - log_body text hidden + log_path TEXT hidden collate naturalnocase, + log_text TEXT hidden, + log_body TEXT hidden ); CREATE VIEW lnav_top_view AS diff --git a/src/internals/sql-ref.rst b/src/internals/sql-ref.rst index fa109f7d..ee3d375f 100644 --- a/src/internals/sql-ref.rst +++ b/src/internals/sql-ref.rst @@ -3370,7 +3370,8 @@ timeslice(*time*, *slice*) .. code-block:: custsqlite - ;SELECT timeslice(log_time_msecs, '5m') AS slice, count(1) FROM lnav_example_log GROUP BY slice + ;SELECT timeslice(log_time_msecs, '5m') AS slice, count(1) + FROM lnav_example_log GROUP BY slice slice count(1) 2017-02-03 04:05:00.000 2 2017-02-03 04:25:00.000 1 diff --git a/src/listview_curses.cc b/src/listview_curses.cc index 97498c50..4e67a5c1 100644 --- a/src/listview_curses.cc +++ b/src/listview_curses.cc @@ -475,7 +475,8 @@ listview_curses::set_top(vis_line_t top, bool suppress_flash) } else if (this->lv_top != top) { this->lv_top = top; if (this->lv_selectable) { - if (this->lv_selection < top) { + if (this->lv_selection < 0_vl) { + } else if (this->lv_selection < top) { this->lv_selection = top; } else { auto bot = this->get_bottom(); @@ -545,3 +546,22 @@ listview_curses::rows_available(vis_line_t line, return retval; } + +void +listview_curses::scroll_selection_into_view() +{ + unsigned long width; + vis_line_t height; + + this->get_dimensions(height, width); + if (height <= 0) { + return; + } + if (this->lv_selection < 0_vl) { + this->set_top(0_vl); + } else if (this->lv_selection >= (this->lv_top + height - 1)) { + this->set_top(this->lv_selection - height + 2_vl, true); + } else if (this->lv_selection < this->lv_top) { + this->set_top(this->lv_selection, true); + } +} diff --git a/src/listview_curses.hh b/src/listview_curses.hh index 647759b3..1daa4305 100644 --- a/src/listview_curses.hh +++ b/src/listview_curses.hh @@ -203,21 +203,7 @@ public: } } - void scroll_selection_into_view() - { - unsigned long width; - vis_line_t height; - - this->get_dimensions(height, width); - if (height <= 0) { - return; - } - if (this->lv_selection >= (this->lv_top + height - 1)) { - this->set_top(this->lv_selection - height + 2_vl, true); - } else if (this->lv_selection < this->lv_top) { - this->set_top(this->lv_selection, true); - } - } + void scroll_selection_into_view(); void shift_selection(int offset); diff --git a/src/lnav.cc b/src/lnav.cc index 4fcfb81e..98cf415d 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -508,11 +508,8 @@ usage() ex1_term.append(lnav::roles::ok("$")) .append(" ") .append(lnav::roles::file("lnav")) - .pad_to(40); - ex1_term.get_attrs().emplace_back(line_range{0, (int) ex1_term.length()}, - VC_BACKGROUND.value(COLOR_BLACK)); - ex1_term.get_attrs().emplace_back(line_range{0, (int) ex1_term.length()}, - VC_FOREGROUND.value(COLOR_WHITE)); + .pad_to(40) + .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE)); attr_line_t ex2_term; @@ -521,11 +518,8 @@ usage() .append(lnav::roles::file("lnav")) .append(" ") .append(lnav::roles::file("/var/log")) - .pad_to(40); - ex2_term.get_attrs().emplace_back(line_range{0, (int) ex2_term.length()}, - VC_BACKGROUND.value(COLOR_BLACK)); - ex2_term.get_attrs().emplace_back(line_range{0, (int) ex2_term.length()}, - VC_FOREGROUND.value(COLOR_WHITE)); + .pad_to(40) + .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE)); attr_line_t ex3_term; @@ -536,11 +530,8 @@ usage() .append(lnav::roles::file("lnav")) .append(" ") .append("-t"_symbol) - .pad_to(40); - ex3_term.get_attrs().emplace_back(line_range{0, (int) ex3_term.length()}, - VC_BACKGROUND.value(COLOR_BLACK)); - ex3_term.get_attrs().emplace_back(line_range{0, (int) ex3_term.length()}, - VC_FOREGROUND.value(COLOR_WHITE)); + .pad_to(40) + .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE)); attr_line_t usage_al; @@ -1964,7 +1955,8 @@ main(int argc, char* argv[]) auto cmd_appender = [](std::string cmd) { lnav_data.ld_commands.emplace_back(cmd); }; auto cmd_validator = [&arg_errors](std::string cmd) -> std::string { - static const auto ARG_SRC = intern_string::lookup("arg"); + static const auto ARG_SRC + = intern_string::lookup("command-line argument"); if (cmd.empty()) { return "empty commands are not allowed"; @@ -1977,6 +1969,7 @@ main(int argc, char* argv[]) case '|': break; default: + cmd.push_back(' '); arg_errors.emplace_back( lnav::console::user_message::error( attr_line_t("invalid value for ") @@ -1984,8 +1977,9 @@ main(int argc, char* argv[]) .append(" option")) .with_snippet(lnav::console::snippet::from( ARG_SRC, - attr_line_t(" -c ") - .append(cmd) + attr_line_t() + .append(" -c "_quoted_code) + .append(lnav::roles::quoted_code(cmd)) .append("\n") .append(4, ' ') .append(lnav::roles::error( @@ -2261,7 +2255,7 @@ main(int argc, char* argv[]) lnav_data.ld_log_source.set_exec_context(&lnav_data.ld_exec_context); lnav_data.ld_views[LNV_HELP] .set_sub_source(&lnav_data.ld_help_source) - .set_word_wrap(true); + .set_word_wrap(false); auto log_fos = new field_overlay_source(lnav_data.ld_log_source, lnav_data.ld_text_source); if (lnav_data.ld_flags & LNF_HEADLESS) { diff --git a/src/lnav.indexing.cc b/src/lnav.indexing.cc index 0a89c63d..e8b25347 100644 --- a/src/lnav.indexing.cc +++ b/src/lnav.indexing.cc @@ -110,9 +110,10 @@ rebuild_hist() lss.reload_index_delegate(); } -class textfile_callback { +class textfile_callback : public textfile_sub_source::scan_callback { public: - void closed_files(const std::vector>& files) + void closed_files( + const std::vector>& files) override { for (const auto& lf : files) { log_info("closed text files: %s", lf->get_filename().c_str()); @@ -120,7 +121,7 @@ public: lnav_data.ld_active_files.close_files(files); } - void promote_file(const std::shared_ptr& lf) + void promote_file(const std::shared_ptr& lf) override { if (lnav_data.ld_log_source.insert_file(lf)) { this->did_promotion = true; @@ -150,7 +151,7 @@ public: } } - void scanned_file(const std::shared_ptr& lf) + void scanned_file(const std::shared_ptr& lf) override { if (!lnav_data.ld_files_to_front.empty() && lnav_data.ld_files_to_front.front().first == lf->get_filename()) diff --git a/src/lnav_config.cc b/src/lnav_config.cc index fb4845b1..f2175d26 100644 --- a/src/lnav_config.cc +++ b/src/lnav_config.cc @@ -640,6 +640,20 @@ static const struct json_path_container theme_styles_handlers = { return &root->lt_style_header[5]; }) .with_children(style_config_handlers), + yajlpp::property_handler("hr") + .with_description("Styling for horizontal rules") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_hr; + }) + .with_children(style_config_handlers), + yajlpp::property_handler("hyperlink") + .with_description("Styling for hyperlinks") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_hyperlink; + }) + .with_children(style_config_handlers), yajlpp::property_handler("list-glyph") .with_description("Styling for glyphs that prefix a list item") .with_obj_provider( @@ -654,9 +668,65 @@ static const struct json_path_container theme_styles_handlers = { return &root->lt_style_breadcrumb; }) .with_children(style_config_handlers), + yajlpp::property_handler("table-border") + .with_description("Styling for table borders") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_table_border; + }) + .with_children(style_config_handlers), + yajlpp::property_handler("table-header") + .with_description("Styling for table headers") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_table_header; + }) + .with_children(style_config_handlers), + yajlpp::property_handler("quote-border") + .with_description("Styling for quoted-block borders") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_quote_border; + }) + .with_children(style_config_handlers), + yajlpp::property_handler("quoted-text") + .with_description("Styling for quoted text blocks") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_quoted_text; + }) + .with_children(style_config_handlers), + yajlpp::property_handler("footnote-border") + .with_description("Styling for footnote borders") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_footnote_border; + }) + .with_children(style_config_handlers), + yajlpp::property_handler("footnote-text") + .with_description("Styling for footnote text") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_footnote_text; + }) + .with_children(style_config_handlers), }; static const struct json_path_container theme_syntax_styles_handlers = { + yajlpp::property_handler("quoted-code") + .with_description("Styling for quoted code blocks") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_quoted_code; + }) + .with_children(style_config_handlers), + yajlpp::property_handler("code-border") + .with_description("Styling for quoted-code borders") + .with_obj_provider( + [](const yajlpp_provider_context& ypc, lnav_theme* root) { + return &root->lt_style_code_border; + }) + .with_children(style_config_handlers), yajlpp::property_handler("keyword") .with_description("Styling for keywords in source files") .with_obj_provider( diff --git a/src/logfile.cc b/src/logfile.cc index 1239ed5f..7e3b7003 100644 --- a/src/logfile.cc +++ b/src/logfile.cc @@ -495,10 +495,11 @@ logfile::rebuild_index(nonstd::optional deadline) this->lf_text_format = avail_data - .map([](const shared_buffer_ref& avail_sbr) + .map([path = this->get_path()]( + const shared_buffer_ref& avail_sbr) -> text_format_t { - return detect_text_format(avail_sbr.get_data(), - avail_sbr.length()); + return detect_text_format( + avail_sbr.to_string_fragment(), path); }) .unwrapOr(text_format_t::TF_UNKNOWN); log_debug("setting text format to %d", this->lf_text_format); @@ -647,6 +648,19 @@ logfile::read_line(logfile::iterator ll) } } +Result +logfile::read_file() +{ + if (this->lf_stat.st_size > line_buffer::MAX_LINE_BUFFER_SIZE) { + return Err(std::string("file is too large to read")); + } + + auto retval + = TRY(this->lf_line_buffer.read_range({0, this->lf_stat.st_size})); + + return Ok(to_string(retval)); +} + void logfile::read_full_message(logfile::const_iterator ll, shared_buffer_ref& msg_out, @@ -842,3 +856,39 @@ logfile::original_line_time(logfile::iterator ll) return ll->get_timeval(); } + +nonstd::optional +logfile::line_for_offset(file_off_t off) const +{ + struct cmper { + bool operator()(const file_off_t& lhs, const logline& rhs) const + { + return lhs < rhs.get_offset(); + } + + bool operator()(const logline& lhs, const file_off_t& rhs) const + { + return lhs.get_offset() < rhs; + } + }; + + if (this->lf_index.empty()) { + return nonstd::nullopt; + } + + auto iter = std::lower_bound( + this->lf_index.begin(), this->lf_index.end(), off, cmper{}); + if (iter == this->lf_index.end()) { + if (this->lf_index.back().get_offset() <= off + && off < this->lf_index_size) { + return nonstd::make_optional(iter); + } + return nonstd::nullopt; + } + + if (off < iter->get_offset() && iter != this->lf_index.begin()) { + --iter; + } + + return nonstd::make_optional(iter); +} diff --git a/src/logfile.hh b/src/logfile.hh index b87b705a..253db247 100644 --- a/src/logfile.hh +++ b/src/logfile.hh @@ -144,6 +144,8 @@ public: file_off_t get_index_size() const { return this->lf_index_size; } + nonstd::optional line_for_offset(file_off_t off) const; + /** * @return The detected format, rebuild_index() must be called before this * will return a value other than NULL. @@ -249,6 +251,8 @@ public: Result read_line(iterator ll); + Result read_file(); + iterator line_base(iterator ll) { auto retval = ll; diff --git a/src/logfile_sub_source.cc b/src/logfile_sub_source.cc index f56c32bc..b47bcd1d 100644 --- a/src/logfile_sub_source.cc +++ b/src/logfile_sub_source.cc @@ -380,9 +380,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv, continue; } - value_out.emplace_back( - token_attr.sa_range, - VC_ROLE.value(role_t::VCR_INVALID_MSG)); + value_out.emplace_back(token_attr.sa_range, + VC_ROLE.value(role_t::VCR_INVALID_MSG)); } } @@ -410,9 +409,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv, this->lss_token_value.c_str(), this->lss_token_value.length()); } - value_out.emplace_back( - ident_range, - VC_ROLE.value(role_t::VCR_IDENTIFIER)); + value_out.emplace_back(ident_range, + VC_ROLE.value(role_t::VCR_IDENTIFIER)); } if (this->lss_token_shift_size) { @@ -453,8 +451,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv, vis_line_t(row))) { lr.lr_start = 0; lr.lr_end = 1; - value_out.emplace_back(lr, - VC_STYLE.value(A_REVERSE)); + value_out.emplace_back(lr, VC_STYLE.value(A_REVERSE)); } } } @@ -484,10 +481,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv, shift_string_attrs(value_out, 0, time_offset_end); - value_out.emplace_back( - lr, VC_ROLE.value(role_t::VCR_OFFSET_TIME)); - value_out.emplace_back(line_range(12, 13), - VC_GRAPHIC.value(ACS_VLINE)); + value_out.emplace_back(lr, VC_ROLE.value(role_t::VCR_OFFSET_TIME)); + value_out.emplace_back(line_range(12, 13), VC_GRAPHIC.value(ACS_VLINE)); role_t bar_role = role_t::VCR_NONE; @@ -502,8 +497,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv, break; } if (bar_role != role_t::VCR_NONE) { - value_out.emplace_back(line_range(12, 13), - VC_ROLE.value(bar_role)); + value_out.emplace_back(line_range(12, 13), VC_ROLE.value(bar_role)); } } @@ -549,9 +543,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv, = find_string_attr_range(value_out, &logline::L_TIMESTAMP); if (time_range.lr_end != -1) { - value_out.emplace_back( - time_range, - VC_ROLE.value(role_t::VCR_ADJUSTED_TIME)); + value_out.emplace_back(time_range, + VC_ROLE.value(role_t::VCR_ADJUSTED_TIME)); } } @@ -560,9 +553,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv, = find_string_attr_range(value_out, &logline::L_TIMESTAMP); if (time_range.lr_end != -1) { - value_out.emplace_back( - time_range, - VC_ROLE.value(role_t::VCR_SKEWED_TIME)); + value_out.emplace_back(time_range, + VC_ROLE.value(role_t::VCR_SKEWED_TIME)); } } @@ -584,8 +576,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv, color = COLOR_GREEN; } else { color = COLOR_RED; - value_out.emplace_back( - line_range{0, 1}, VC_STYLE.value(A_BLINK)); + value_out.emplace_back(line_range{0, 1}, + VC_STYLE.value(A_BLINK)); } } value_out.emplace_back(line_range{0, 1}, @@ -2039,133 +2031,243 @@ logfile_sub_source::text_crumbs_for_line(int line, } auto line_pair_opt = this->find_line_with_file(vis_line_t(line)); - if (line_pair_opt) { - auto line_pair = line_pair_opt.value(); - auto& lf = line_pair.first; - auto format = lf->get_format(); - char ts[64]; - - sql_strftime(ts, sizeof(ts), line_pair.second->get_timeval(), 'T'); - - crumbs.emplace_back( - std::string(ts), - timestamp_poss, - [ec = this->lss_exec_context](const auto& ts) { - ec->execute(fmt::format(FMT_STRING(":goto {}"), - ts.template get())); - }); - crumbs.back().c_expected_input - = breadcrumb::crumb::expected_input_t::anything; - crumbs.back().c_search_placeholder - = "(Enter an absolute or relative time)"; + if (!line_pair_opt) { + return; + } + auto line_pair = line_pair_opt.value(); + auto& lf = line_pair.first; + auto format = lf->get_format(); + char ts[64]; + + sql_strftime(ts, sizeof(ts), line_pair.second->get_timeval(), 'T'); + + crumbs.emplace_back( + std::string(ts), + timestamp_poss, + [ec = this->lss_exec_context](const auto& ts) { + ec->execute(fmt::format(FMT_STRING(":goto {}"), + ts.template get())); + }); + crumbs.back().c_expected_input + = breadcrumb::crumb::expected_input_t::anything; + crumbs.back().c_search_placeholder = "(Enter an absolute or relative time)"; + + auto format_name = format->get_name().to_string(); + crumbs.emplace_back( + format_name, + attr_line_t().append(lnav::roles::identifier(format_name)), + [this]() -> std::vector { + return this->lss_files + | lnav::itertools::filter_in([](const auto& file_data) { + return file_data->is_visible(); + }) + | lnav::itertools::map(&logfile_data::get_file_ptr) + | lnav::itertools::map(&logfile::get_format_name) + | lnav::itertools::unique() + | lnav::itertools::map([](const auto& elem) { + return breadcrumb::possibility{ + elem.to_string(), + }; + }); + }, + [ec = this->lss_exec_context](const auto& format_name) { + static const std::string MOVE_STMT = R"(;UPDATE lnav_views + SET top = ifnull((SELECT log_line FROM all_logs WHERE log_format = $format_name LIMIT 1), top) + WHERE name = 'log' +)"; - auto format_name = format->get_name().to_string(); - crumbs.emplace_back( - format_name, - attr_line_t().append(lnav::roles::identifier(format_name)), - [this]() -> std::vector { - return this->lss_files - | lnav::itertools::filter_in([](const auto& file_data) { - return file_data->is_visible(); - }) - | lnav::itertools::map(&logfile_data::get_file_ptr) - | lnav::itertools::map(&logfile::get_format_name) - | lnav::itertools::unique() - | lnav::itertools::map([](const auto& elem) { - return breadcrumb::possibility{ - elem.to_string(), - }; - }); - }, - [ec = this->lss_exec_context](const auto& format_name) { - static const std::string MOVE_STMT = R"(;UPDATE lnav_views - SET top = (SELECT log_line FROM all_logs WHERE log_format = $format_name LIMIT 1) + ec->execute_with( + MOVE_STMT, + std::make_pair("format_name", + format_name.template get())); + }); + + auto msg_start_iter = lf->message_start(line_pair.second); + auto file_line_number = std::distance(lf->begin(), msg_start_iter); + crumbs.emplace_back( + lf->get_unique_path(), + attr_line_t() + .append(lnav::roles::identifier(lf->get_unique_path())) + .append(FMT_STRING("[{:L}]"), file_line_number), + [this]() -> std::vector { + return this->lss_files + | lnav::itertools::filter_in([](const auto& file_data) { + return file_data->is_visible(); + }) + | lnav::itertools::map([](const auto& file_data) { + return breadcrumb::possibility{ + file_data->get_file_ptr()->get_unique_path(), + attr_line_t( + file_data->get_file_ptr()->get_unique_path()), + }; + }); + }, + [ec = this->lss_exec_context](const auto& uniq_path) { + static const std::string MOVE_STMT = R"(;UPDATE lnav_views + SET top = ifnull((SELECT log_line FROM all_logs WHERE log_unique_path = $uniq_path LIMIT 1), top) WHERE name = 'log' )"; - ec->execute_with( - MOVE_STMT, - std::make_pair("format_name", - format_name.template get())); - }); + ec->execute_with( + MOVE_STMT, + std::make_pair("uniq_path", + uniq_path.template get())); + }); + + shared_buffer_ref sbr; + string_attrs_t sa; + std::vector values; - auto msg_start_iter = lf->message_start(line_pair.second); - auto file_line_number = std::distance(lf->begin(), msg_start_iter); + lf->read_full_message(msg_start_iter, sbr); + format->annotate(file_line_number, sbr, sa, values); + + auto opid_opt = get_string_attr(sa, logline::L_OPID); + if (opid_opt) { + const auto& opid_range = opid_opt.value().saw_string_attr->sa_range; + const auto opid_str + = sbr.to_string_fragment(opid_range.lr_start, opid_range.length()) + .to_string(); crumbs.emplace_back( - lf->get_unique_path(), - attr_line_t() - .append(lnav::roles::identifier(lf->get_unique_path())) - .append(FMT_STRING("[{:L}]"), file_line_number), + opid_str, + attr_line_t().append(lnav::roles::identifier(opid_str)), [this]() -> std::vector { - return this->lss_files - | lnav::itertools::filter_in([](const auto& file_data) { - return file_data->is_visible(); - }) - | lnav::itertools::map([](const auto& file_data) { + std::set opids; + + for (const auto& file_data : this->lss_files) { + safe::ReadAccess r_opid_map( + file_data->get_file_ptr()->get_opids()); + + for (const auto& pair : *r_opid_map) { + opids.insert(pair.first); + } + } + + return opids | lnav::itertools::map([](const auto& elem) { return breadcrumb::possibility{ - file_data->get_file_ptr()->get_unique_path(), - attr_line_t(file_data->get_file_ptr() - ->get_unique_path()), + elem, }; }); }, - [ec = this->lss_exec_context](const auto& uniq_path) { + [ec = this->lss_exec_context](const auto& opid) { static const std::string MOVE_STMT = R"(;UPDATE lnav_views - SET top = (SELECT log_line FROM all_logs WHERE log_unique_path = $uniq_path LIMIT 1) - WHERE name = 'log' -)"; + SET top = ifnull((SELECT log_line FROM all_logs WHERE log_opid = $opid LIMIT 1), top) + WHERE name = 'log' + )"; ec->execute_with( MOVE_STMT, - std::make_pair("uniq_path", - uniq_path.template get())); + std::make_pair("opid", opid.template get())); }); + } - shared_buffer_ref sbr; - string_attrs_t sa; - std::vector values; - - lf->read_full_message(msg_start_iter, sbr); - format->annotate(file_line_number, sbr, sa, values); - - auto opid_opt = get_string_attr(sa, logline::L_OPID); - if (opid_opt) { - const auto& opid_range = opid_opt.value().saw_string_attr->sa_range; - const auto opid_str = sbr.to_string_fragment(opid_range.lr_start, - opid_range.length()) - .to_string(); - crumbs.emplace_back( - opid_str, - attr_line_t().append(lnav::roles::identifier(opid_str)), - [this]() -> std::vector { - std::set opids; - - for (const auto& file_data : this->lss_files) { - safe::ReadAccess r_opid_map( - file_data->get_file_ptr()->get_opids()); - - for (const auto& pair : *r_opid_map) { - opids.insert(pair.first); + auto sf = sbr.to_string_fragment(); + auto sf_lines = sf.split_lines(); + auto msg_line_number = std::distance(msg_start_iter, line_pair.second); + auto line_from_top = line - msg_line_number; + if (sf_lines.size() > 1) { + this->lss_token_meta = lnav::document::discover_structure(sf); + const auto initial_size = crumbs.size(); + file_off_t line_offset = 0; + file_off_t line_end_offset = sf.length(); + size_t line_number = 0; + + for (const auto& sf_line : sf_lines) { + if (line_number >= msg_line_number) { + line_end_offset = line_offset + sf_line.length(); + break; + } + line_number += 1; + line_offset += sf_line.length(); + } + + this->lss_token_meta.m_sections_tree.visit_overlapping( + line_offset, + line_end_offset, + [this, + initial_size, + meta = &this->lss_token_meta, + &crumbs, + line_from_top](const auto& iv) { + auto path = crumbs | lnav::itertools::skip(initial_size) + | lnav::itertools::map(&breadcrumb::crumb::c_key) + | lnav::itertools::append(iv.value); + auto curr_node = lnav::document::hier_node::lookup_path( + meta->m_sections_root.get(), path); + + crumbs.template emplace_back( + iv.value, + [meta, path]() { return meta->possibility_provider(path); }, + [this, curr_node, path, line_from_top](const auto& key) { + if (!curr_node) { + return; } - } + auto* parent_node = curr_node.value()->hn_parent; + if (parent_node == nullptr) { + return; + } + key.template match( + [parent_node](const std::string& str) { + return parent_node->find_line_number(str); + }, + [parent_node](size_t index) { + return parent_node->find_line_number(index); + }) + | [this, line_from_top](auto line_number) { + this->tss_view->set_top( + vis_line_t(line_from_top + line_number)); + }; + }); + if (curr_node && curr_node.value()->hn_parent->is_named_only()) + { + auto node = lnav::document::hier_node::lookup_path( + meta->m_sections_root.get(), path); + + crumbs.back().c_expected_input + = curr_node.value() + ->hn_parent->hn_named_children.empty() + ? breadcrumb::crumb::expected_input_t::index + : breadcrumb::crumb::expected_input_t::index_or_exact; + crumbs.back().with_possible_range( + node | lnav::itertools::map([](const auto hn) { + return hn->hn_parent->hn_children.size(); + }) + | lnav::itertools::unwrap_or(size_t{0})); + } + }); - return opids | lnav::itertools::map([](const auto& elem) { - return breadcrumb::possibility{ - elem, - }; - }); - }, - [ec = this->lss_exec_context](const auto& opid) { - static const std::string MOVE_STMT = R"(;UPDATE lnav_views - SET top = (SELECT log_line FROM all_logs WHERE log_opid = $opid LIMIT 1) - WHERE name = 'log' - )"; + auto path = crumbs | lnav::itertools::skip(initial_size) + | lnav::itertools::map(&breadcrumb::crumb::c_key); + auto node = lnav::document::hier_node::lookup_path( + this->lss_token_meta.m_sections_root.get(), path); - ec->execute_with( - MOVE_STMT, - std::make_pair("opid", - opid.template get())); - }); + if (node && !node.value()->hn_children.empty()) { + auto poss_provider = [curr_node = node.value()]() { + std::vector retval; + for (const auto& child : curr_node->hn_named_children) { + retval.template emplace_back(child.first); + } + return retval; + }; + auto path_performer + = [this, curr_node = node.value(), line_from_top]( + const breadcrumb::crumb::key_t& value) { + value.template match( + [curr_node](const std::string& str) { + return curr_node->find_line_number(str); + }, + [curr_node](size_t index) { + return curr_node->find_line_number(index); + }) + | [this, line_from_top](size_t line_number) { + this->tss_view->set_top( + vis_line_t(line_from_top + line_number)); + }; + }; + crumbs.emplace_back("", "\u22ef", poss_provider, path_performer); + crumbs.back().c_expected_input + = node.value()->hn_named_children.empty() + ? breadcrumb::crumb::expected_input_t::index + : breadcrumb::crumb::expected_input_t::index_or_exact; } } } diff --git a/src/logfile_sub_source.hh b/src/logfile_sub_source.hh index 9a119963..ae0c43c0 100644 --- a/src/logfile_sub_source.hh +++ b/src/logfile_sub_source.hh @@ -45,6 +45,7 @@ #include "base/time_util.hh" #include "big_array.hh" #include "bookmarks.hh" +#include "document.sections.hh" #include "filter_observer.hh" #include "log_accel.hh" #include "log_format.hh" @@ -958,6 +959,7 @@ private: std::shared_ptr lss_token_file; std::string lss_token_value; string_attrs_t lss_token_attrs; + lnav::document::metadata lss_token_meta; std::vector lss_token_values; int lss_token_shift_start{0}; int lss_token_shift_size{0}; diff --git a/src/md2attr_line.cc b/src/md2attr_line.cc new file mode 100644 index 00000000..26866b7d --- /dev/null +++ b/src/md2attr_line.cc @@ -0,0 +1,523 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "md2attr_line.hh" + +#include "base/attr_line.builder.hh" +#include "base/itertools.hh" +#include "base/lnav_log.hh" +#include "pcrepp/pcrepp.hh" +#include "readline_highlighters.hh" +#include "view_curses.hh" + +using namespace lnav::roles::literals; + +void +md2attr_line::flush_footnotes() +{ + if (this->ml_footnotes.empty()) { + return; + } + + auto& block_text = this->ml_blocks.back(); + auto longest_foot = this->ml_footnotes + | lnav::itertools::map(&attr_line_t::utf8_length_or_length) + | lnav::itertools::max(0); + size_t index = 1; + + block_text.append("\n"); + for (auto& foot : this->ml_footnotes) { + block_text.append(lnav::string::attrs::preformatted(" ")) + .append("\u258c"_footnote_border) + .append(lnav::roles::footnote_text( + index < 10 && this->ml_footnotes.size() >= 10 ? " " : "")) + .append(lnav::roles::footnote_text( + fmt::format(FMT_STRING("[{}] - "), index))) + .append(foot.pad_to(longest_foot)) + .append("\n"); + index += 1; + } + this->ml_footnotes.clear(); +} + +Result +md2attr_line::enter_block(const md4cpp::event_handler::block& bl) +{ + if (this->ml_list_stack.empty() + && (bl.is() || bl.is() + || bl.is())) + { + this->flush_footnotes(); + } + + this->ml_blocks.resize(this->ml_blocks.size() + 1); + if (bl.is()) { + auto* ol_detail = bl.get(); + + this->ml_list_stack.emplace_back(*ol_detail); + } else if (bl.is()) { + this->ml_list_stack.emplace_back(bl.get()); + } else if (bl.is()) { + this->ml_tables.resize(this->ml_tables.size() + 1); + } else if (bl.is()) { + this->ml_tables.back().t_rows.resize( + this->ml_tables.back().t_rows.size() + 1); + } else if (bl.is()) { + this->ml_code_depth += 1; + } + + return Ok(); +} + +Result +md2attr_line::leave_block(const md4cpp::event_handler::block& bl) +{ + auto block_text = std::move(this->ml_blocks.back()); + this->ml_blocks.pop_back(); + + auto& last_block = this->ml_blocks.back(); + if (!endswith(block_text.get_string(), "\n")) { + block_text.append("\n"); + } + if (bl.is()) { + auto* hbl = bl.get(); + auto role = role_t::VCR_TEXT; + + switch (hbl->level) { + case 1: + role = role_t::VCR_H1; + break; + case 2: + role = role_t::VCR_H2; + break; + case 3: + role = role_t::VCR_H3; + break; + case 4: + role = role_t::VCR_H4; + break; + case 5: + role = role_t::VCR_H5; + break; + case 6: + role = role_t::VCR_H6; + break; + } + block_text.rtrim().with_attr_for_all(VC_ROLE.value(role)); + last_block.append("\n").append(block_text).append("\n"); + } else if (bl.is()) { + block_text = attr_line_t() + .append(lnav::roles::hr(repeat("\u2501", 70))) + .with_attr_for_all(SA_PREFORMATTED.value()); + last_block.append("\n").append(block_text).append("\n"); + } else if (bl.is() || bl.is()) { + this->ml_list_stack.pop_back(); + if (last_block.empty()) { + last_block.append("\n"); + } else { + if (!endswith(last_block.get_string(), "\n")) { + last_block.append("\n"); + } + if (this->ml_list_stack.empty() + && !endswith(last_block.get_string(), "\n\n")) { + last_block.append("\n"); + } + } + last_block.append(block_text); + } else if (bl.is()) { + auto last_list_block = this->ml_list_stack.back(); + text_wrap_settings tws = {0, 60}; + + attr_line_builder alb(last_block); + { + auto prefix = alb.with_attr(SA_PREFORMATTED.value()); + + alb.append(" ") + .append(last_list_block.match( + [this, &tws](const MD_BLOCK_UL_DETAIL*) { + tws.tws_indent = 3; + return this->ml_list_stack.size() % 2 == 1 + ? "\u2022"_list_glyph + : "\u2014"_list_glyph; + }, + [this, &tws](MD_BLOCK_OL_DETAIL ol_detail) { + auto retval = lnav::roles::list_glyph( + fmt::format(FMT_STRING("{}{}"), + ol_detail.start, + ol_detail.mark_delimiter)); + tws.tws_indent = retval.first.length() + 2; + + this->ml_list_stack.pop_back(); + ol_detail.start += 1; + this->ml_list_stack.emplace_back(ol_detail); + return retval; + })) + .append(" "); + } + + alb.append(block_text, &tws); + } else if (bl.is()) { + auto* code_detail = bl.get(); + + this->ml_code_depth -= 1; + + auto lang_sf = string_fragment{ + code_detail->lang.text, + 0, + (int) code_detail->lang.size, + }; + if (lang_sf == "lnav") { + readline_lnav_highlighter(block_text, block_text.length()); + } + + auto code_lines = block_text.rtrim().split_lines(); + auto max_width = code_lines + | lnav::itertools::map(&attr_line_t::utf8_length_or_length) + | lnav::itertools::max(0); + attr_line_t padded_text; + + for (auto& line : code_lines) { + line.pad_to(std::max(max_width + 4, ssize_t{40})) + .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE)); + padded_text.append(lnav::string::attrs::preformatted(" ")) + .append("\u258c"_code_border) + .append(line) + .append("\n"); + } + padded_text.with_attr_for_all(SA_PREFORMATTED.value()); + last_block.append("\n").append(padded_text); + } else if (bl.is()) { + text_wrap_settings tws = {0, 60}; + attr_line_t wrapped_text; + + wrapped_text.append(block_text.rtrim(), &tws); + auto quoted_lines = wrapped_text.split_lines(); + auto max_width = quoted_lines + | lnav::itertools::map(&attr_line_t::utf8_length_or_length) + | lnav::itertools::max(0); + attr_line_t padded_text; + + for (auto& line : quoted_lines) { + line.pad_to(max_width + 1) + .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_TEXT)); + padded_text.append(" ") + .append("\u258c"_quote_border) + .append(line) + .append("\n"); + } + padded_text.with_attr_for_all(SA_PREFORMATTED.value()); + last_block.append("\n").append(padded_text); + } else if (bl.is()) { + auto* table_detail = bl.get(); + auto tab = std::move(this->ml_tables.back()); + this->ml_tables.pop_back(); + std::vector max_col_sizes; + + block_text.clear(); + block_text.append("\n"); + max_col_sizes.resize(table_detail->col_count); + for (size_t lpc = 0; lpc < table_detail->col_count; lpc++) { + if (lpc < tab.t_headers.size()) { + max_col_sizes[lpc] = tab.t_headers[lpc].utf8_length_or_length(); + tab.t_headers[lpc].with_attr_for_all( + VC_ROLE.value(role_t::VCR_TABLE_HEADER)); + } + } + for (const auto& row : tab.t_rows) { + for (size_t lpc = 0; lpc < table_detail->col_count; lpc++) { + if (lpc >= row.r_columns.size()) { + continue; + } + auto col_len = row.r_columns[lpc].utf8_length_or_length(); + if (col_len > max_col_sizes[lpc]) { + max_col_sizes[lpc] = col_len; + } + } + } + auto col_sizes + = max_col_sizes | lnav::itertools::map([](const auto& elem) { + return std::min(elem, ssize_t{50}); + }); + auto full_width = col_sizes | lnav::itertools::sum(); + text_wrap_settings tws = {0, 50}; + std::vector cells; + size_t max_cell_lines = 0; + for (size_t lpc = 0; lpc < tab.t_headers.size(); lpc++) { + tws.with_width(col_sizes[lpc]); + + attr_line_t td_block; + td_block.append(tab.t_headers[lpc], &tws); + cells.emplace_back(td_block.rtrim().split_lines()); + if (cells.back().cl_lines.size() > max_cell_lines) { + max_cell_lines = cells.back().cl_lines.size(); + } + } + for (size_t line_index = 0; line_index < max_cell_lines; line_index++) { + size_t col = 0; + for (const auto& cell : cells) { + block_text.append(" "); + if (line_index < cell.cl_lines.size()) { + block_text.append(cell.cl_lines[line_index]); + block_text.append( + col_sizes[col] + - cell.cl_lines[line_index].utf8_length_or_length(), + ' '); + } else { + block_text.append(col_sizes[col], ' '); + } + col += 1; + } + block_text.append("\n") + .append(lnav::roles::table_border( + repeat("\u2550", full_width + col_sizes.size()))) + .append("\n"); + } + for (const auto& row : tab.t_rows) { + cells.clear(); + max_cell_lines = 0; + for (size_t lpc = 0; lpc < row.r_columns.size(); lpc++) { + tws.with_width(col_sizes[lpc]); + + attr_line_t td_block; + td_block.append(row.r_columns[lpc], &tws); + cells.emplace_back(td_block.rtrim().split_lines()); + if (cells.back().cl_lines.size() > max_cell_lines) { + max_cell_lines = cells.back().cl_lines.size(); + } + } + for (size_t line_index = 0; line_index < max_cell_lines; + line_index++) { + size_t col = 0; + for (const auto& cell : cells) { + block_text.append(" "); + if (line_index < cell.cl_lines.size()) { + block_text.append(cell.cl_lines[line_index]); + if (col < col_sizes.size() - 1) { + block_text.append( + col_sizes[col] + - cell.cl_lines[line_index] + .utf8_length_or_length(), + ' '); + } + } else if (col < col_sizes.size() - 1) { + block_text.append(col_sizes[col], ' '); + } + col += 1; + } + block_text.append("\n"); + } + } + block_text.with_attr_for_all(SA_PREFORMATTED.value()); + last_block.append(block_text); + } else if (bl.is()) { + this->ml_tables.back().t_headers.push_back(block_text); + } else if (bl.is()) { + this->ml_tables.back().t_rows.back().r_columns.push_back(block_text); + } else { + text_wrap_settings tws = {0, this->ml_blocks.size() == 1 ? 70 : 10000}; + + if (!last_block.empty()) { + last_block.append("\n"); + } + last_block.append(block_text, &tws); + } + if (bl.is()) { + this->flush_footnotes(); + } + return Ok(); +} + +Result +md2attr_line::enter_span(const md4cpp::event_handler::span& sp) +{ + auto& last_block = this->ml_blocks.back(); + this->ml_span_starts.push_back(last_block.length()); + if (sp.is()) { + last_block.append(" "); + this->ml_code_depth += 1; + } + return Ok(); +} + +Result +md2attr_line::leave_span(const md4cpp::event_handler::span& sp) +{ + auto& last_block = this->ml_blocks.back(); + if (sp.is()) { + this->ml_code_depth -= 1; + last_block.append(" "); + line_range lr{ + static_cast(this->ml_span_starts.back()), + static_cast(last_block.length()), + }; + last_block.with_attr({ + lr, + VC_ROLE.value(role_t::VCR_QUOTED_CODE), + }); + last_block.with_attr({ + lr, + SA_PREFORMATTED.value(), + }); + } else if (sp.is()) { + line_range lr{ + static_cast(this->ml_span_starts.back()), + static_cast(last_block.length()), + }; + last_block.with_attr({ + lr, + VC_STYLE.value(A_ITALIC), + }); + } else if (sp.is()) { + line_range lr{ + static_cast(this->ml_span_starts.back()), + static_cast(last_block.length()), + }; + last_block.with_attr({ + lr, + VC_STYLE.value(A_BOLD), + }); + } else if (sp.is()) { + auto* a_detail = sp.get(); + auto href_str = std::string(a_detail->href.text, a_detail->href.size); + + this->append_url_footnote(href_str); + } else if (sp.is()) { + auto* img_detail = sp.get(); + auto src_str = std::string(img_detail->src.text, img_detail->src.size); + + this->append_url_footnote(src_str); + } + this->ml_span_starts.pop_back(); + return Ok(); +} + +Result +md2attr_line::text(MD_TEXTTYPE tt, const string_fragment& sf) +{ + static const auto& entity_map = md4cpp::get_xml_entity_map(); + + auto& last_block = this->ml_blocks.back(); + + switch (tt) { + case MD_TEXT_BR: + last_block.append("\n"); + break; + case MD_TEXT_SOFTBR: { + if (!last_block.empty() && !isspace(last_block.get_string().back())) + { + last_block.append(" "); + } + break; + } + case MD_TEXT_ENTITY: { + auto xe_iter = entity_map.xem_entities.find(sf.to_string()); + + if (xe_iter != entity_map.xem_entities.end()) { + last_block.append(xe_iter->second.xe_chars); + } + break; + } + default: { + static const pcrepp REPL_RE(R"(-{2,3}|:[^:\s]*(?:::[^:\s]*)*:)"); + static const auto& emojis = md4cpp::get_emoji_map(); + + if (this->ml_code_depth > 0) { + last_block.append(sf); + return Ok(); + } + + pcre_input pi(sf); + pcre_context_static<30> pc; + + while (REPL_RE.match(pc, pi)) { + auto prev = string_fragment{ + sf.sf_string, + (int) pi.pi_offset, + pc.all()->c_begin, + }; + + last_block.append(prev); + + auto matched = pi.get_string_fragment(pc.all()); + + if (matched == "--") { + last_block.append("\u2013"); + } else if (matched == "---") { + last_block.append("\u2014"); + } else if (matched.startswith(":")) { + auto em_iter + = emojis.em_shortname2emoji.find(matched.to_string()); + if (em_iter == emojis.em_shortname2emoji.end()) { + last_block.append(matched); + } else { + last_block.append(em_iter->second.get().e_value); + } + } + } + + this->ml_blocks.back().append(string_fragment{ + sf.sf_string, + (int) pi.pi_offset, + sf.sf_end, + }); + break; + } + } + return Ok(); +} + +void +md2attr_line::append_url_footnote(std::string href_str) +{ + if (startswith(href_str, "#")) { + return; + } + + auto& last_block = this->ml_blocks.back(); + last_block.append(FMT_STRING("[{}]"), this->ml_footnotes.size() + 1); + last_block.with_attr(string_attr{ + line_range{ + (int) this->ml_span_starts.back(), + (int) last_block.length(), + }, + VC_STYLE.value(A_UNDERLINE), + }); + if (this->ml_source_path && href_str.find(":") == std::string::npos) { + auto link_path = ghc::filesystem::absolute( + this->ml_source_path.value().parent_path() / href_str); + + href_str = fmt::format(FMT_STRING("file://{}"), link_path.string()); + } + + auto href + = attr_line_t().append(lnav::roles::hyperlink(href_str)).append(" "); + href.with_attr_for_all(VC_ROLE.value(role_t::VCR_FOOTNOTE_TEXT)); + href.with_attr_for_all(SA_PREFORMATTED.value()); + this->ml_footnotes.emplace_back(href); +} diff --git a/src/md2attr_line.hh b/src/md2attr_line.hh new file mode 100644 index 00000000..c1aa7bbe --- /dev/null +++ b/src/md2attr_line.hh @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_md2attr_line_hh +#define lnav_md2attr_line_hh + +#include "base/attr_line.hh" +#include "ghc/filesystem.hpp" +#include "md4cpp.hh" + +class md2attr_line : public md4cpp::typed_event_handler { +public: + md2attr_line() { this->ml_blocks.resize(1); } + + md2attr_line& with_source_path(nonstd::optional path) + { + this->ml_source_path = path; + return *this; + } + + Result enter_block(const block& bl) override; + + Result leave_block(const block& bl) override; + Result enter_span(const span& bl) override; + Result leave_span(const span& sp) override; + Result text(MD_TEXTTYPE tt, + const string_fragment& sf) override; + + attr_line_t get_result() override { return this->ml_blocks.back(); } + +private: + struct table_t { + struct row_t { + std::vector r_columns; + }; + + std::vector t_headers; + std::vector t_rows; + }; + + struct cell_lines { + cell_lines(std::vector lines) : cl_lines(std::move(lines)) + { + } + + std::vector cl_lines; + }; + + using list_block_t + = mapbox::util::variant; + + void append_url_footnote(std::string href); + void flush_footnotes(); + + nonstd::optional ml_source_path; + std::vector ml_blocks; + std::vector ml_list_stack; + std::vector ml_tables; + std::vector ml_span_starts; + std::vector ml_footnotes; + int32_t ml_code_depth{0}; +}; + +#endif diff --git a/src/md4cpp.cc b/src/md4cpp.cc new file mode 100644 index 00000000..e0556a7d --- /dev/null +++ b/src/md4cpp.cc @@ -0,0 +1,287 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "md4cpp.hh" + +#include "base/lnav_log.hh" +#include "emojis-json.h" +#include "xml-entities-json.h" +#include "yajlpp/yajlpp_def.hh" + +namespace md4cpp { + +static const typed_json_path_container xml_entity_handlers = { + yajlpp::property_handler("characters").for_field(&xml_entity::xe_chars), +}; + +static const typed_json_path_container xml_entity_map_handlers + = { + yajlpp::pattern_property_handler("(?\\&\\w+;?)") + .with_synopsis("") + .with_path_provider( + [](struct xml_entity_map* xem, + std::vector& paths_out) { + for (const auto& iter : xem->xem_entities) { + paths_out.emplace_back(iter.first); + } + }) + .with_obj_provider( + [](const yajlpp_provider_context& ypc, xml_entity_map* xem) { + auto entity_name = ypc.get_substr(0); + return &xem->xem_entities[entity_name]; + }) + .with_children(xml_entity_handlers), +}; + +static const typed_json_path_container emoji_handlers = { + yajlpp::property_handler("emoji").for_field(&emoji::e_value), + yajlpp::property_handler("shortname").for_field(&emoji::e_shortname), +}; + +static const typed_json_path_container emoji_map_handlers = { + yajlpp::property_handler("emojis#") + .for_field(&emoji_map::em_emojis) + .with_children(emoji_handlers), +}; + +static xml_entity_map +load_xml_entity_map() +{ + static const intern_string_t name + = intern_string::lookup(xml_entities_json.get_name()); + auto parse_res + = xml_entity_map_handlers.parser_for(name).with_ignore_unused(true).of( + xml_entities_json.to_string_fragment()); + + assert(parse_res.isOk()); + + return parse_res.unwrap(); +} + +const xml_entity_map& +get_xml_entity_map() +{ + static const auto retval = load_xml_entity_map(); + + return retval; +} + +static emoji_map +load_emoji_map() +{ + static const intern_string_t name + = intern_string::lookup(emojis_json.get_name()); + auto parse_res + = emoji_map_handlers.parser_for(name).with_ignore_unused(true).of( + emojis_json.to_string_fragment()); + + assert(parse_res.isOk()); + + auto retval = parse_res.unwrap(); + for (auto& em : retval.em_emojis) { + retval.em_shortname2emoji.emplace(em.e_shortname, em); + } + + return retval; +} + +const emoji_map& +get_emoji_map() +{ + static const auto retval = load_emoji_map(); + + return retval; +} + +struct parse_userdata { + event_handler& pu_handler; + std::string pu_error_msg; +}; + +static event_handler::block +build_block(MD_BLOCKTYPE type, void* detail) +{ + switch (type) { + case MD_BLOCK_DOC: + return event_handler::block_doc{}; + case MD_BLOCK_QUOTE: + return event_handler::block_quote{}; + case MD_BLOCK_UL: + return static_cast(detail); + case MD_BLOCK_OL: + return static_cast(detail); + case MD_BLOCK_LI: + return static_cast(detail); + case MD_BLOCK_HR: + return event_handler::block_hr{}; + case MD_BLOCK_H: + return static_cast(detail); + case MD_BLOCK_CODE: + return static_cast(detail); + case MD_BLOCK_HTML: + return event_handler::block_html{}; + case MD_BLOCK_P: + return event_handler::block_p{}; + case MD_BLOCK_TABLE: + return static_cast(detail); + case MD_BLOCK_THEAD: + return event_handler::block_thead{}; + case MD_BLOCK_TBODY: + return event_handler::block_tbody{}; + case MD_BLOCK_TR: + return event_handler::block_tr{}; + case MD_BLOCK_TH: + return event_handler::block_th{}; + case MD_BLOCK_TD: + return static_cast(detail); + } + + return {}; +} + +static event_handler::span +build_span(MD_SPANTYPE type, void* detail) +{ + switch (type) { + case MD_SPAN_EM: + return event_handler::span_em{}; + case MD_SPAN_STRONG: + return event_handler::span_strong{}; + case MD_SPAN_A: + return static_cast(detail); + case MD_SPAN_IMG: + return static_cast(detail); + case MD_SPAN_CODE: + return event_handler::span_code{}; + case MD_SPAN_DEL: + return event_handler::span_del{}; + case MD_SPAN_U: + return event_handler::span_u{}; + default: + break; + } + + return {}; +} + +static int +md4cpp_enter_block(MD_BLOCKTYPE type, void* detail, void* userdata) +{ + auto* pu = static_cast(userdata); + + auto enter_res = pu->pu_handler.enter_block(build_block(type, detail)); + if (enter_res.isErr()) { + pu->pu_error_msg = enter_res.unwrapErr(); + return 1; + } + + return 0; +} + +static int +md4cpp_leave_block(MD_BLOCKTYPE type, void* detail, void* userdata) +{ + auto* pu = static_cast(userdata); + + auto leave_res = pu->pu_handler.leave_block(build_block(type, detail)); + if (leave_res.isErr()) { + pu->pu_error_msg = leave_res.unwrapErr(); + return 1; + } + + return 0; +} + +static int +md4cpp_enter_span(MD_SPANTYPE type, void* detail, void* userdata) +{ + auto* pu = static_cast(userdata); + + auto enter_res = pu->pu_handler.enter_span(build_span(type, detail)); + if (enter_res.isErr()) { + pu->pu_error_msg = enter_res.unwrapErr(); + return 1; + } + + return 0; +} + +static int +md4cpp_leave_span(MD_SPANTYPE type, void* detail, void* userdata) +{ + auto* pu = static_cast(userdata); + + auto leave_res = pu->pu_handler.leave_span(build_span(type, detail)); + if (leave_res.isErr()) { + pu->pu_error_msg = leave_res.unwrapErr(); + return 1; + } + + return 0; +} + +static int +md4cpp_text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) +{ + auto* pu = static_cast(userdata); + auto leave_res = pu->pu_handler.text(type, string_fragment(text, 0, size)); + if (leave_res.isErr()) { + pu->pu_error_msg = leave_res.unwrapErr(); + return 1; + } + + return 0; +} + +namespace details { +Result +parse(const string_fragment& sf, event_handler& eh) +{ + MD_PARSER parser = {0}; + auto pu = parse_userdata{eh}; + + parser.abi_version = 0; + parser.flags = MD_DIALECT_GITHUB; + parser.enter_block = md4cpp_enter_block; + parser.leave_block = md4cpp_leave_block; + parser.enter_span = md4cpp_enter_span; + parser.leave_span = md4cpp_leave_span; + parser.text = md4cpp_text; + + auto rc = md_parse(sf.data(), sf.length(), &parser, &pu); + + if (rc == 0) { + return Ok(); + } + + return Err(pu.pu_error_msg); +} +} // namespace details + +} // namespace md4cpp diff --git a/src/md4cpp.hh b/src/md4cpp.hh new file mode 100644 index 00000000..99d09e05 --- /dev/null +++ b/src/md4cpp.hh @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_md4cpp_hh +#define lnav_md4cpp_hh + +#include +#include +#include + +#include "base/intern_string.hh" +#include "base/result.h" +#include "mapbox/variant.hpp" +#include "md4c/md4c.h" + +namespace md4cpp { + +struct xml_entity { + std::string xe_chars; +}; + +struct xml_entity_map { + std::map xem_entities; +}; + +struct emoji { + std::string e_shortname; + std::string e_value; +}; + +struct emoji_map { + std::vector em_emojis; + std::unordered_map> + em_shortname2emoji; +}; + +class event_handler { +public: + virtual ~event_handler() = default; + + struct block_doc {}; + struct block_quote {}; + struct block_hr {}; + struct block_html {}; + struct block_p {}; + struct block_thead {}; + struct block_tbody {}; + struct block_tr {}; + struct block_th {}; + + using block = mapbox::util::variant; + + virtual Result enter_block(const block& bl) = 0; + virtual Result leave_block(const block& bl) = 0; + + struct span_em {}; + struct span_strong {}; + struct span_code {}; + struct span_del {}; + struct span_u {}; + + using span = mapbox::util::variant; + + virtual Result enter_span(const span& bl) = 0; + virtual Result leave_span(const span& bl) = 0; + + virtual Result text(MD_TEXTTYPE tt, + const string_fragment& sf) + = 0; +}; + +namespace details { +Result parse(const string_fragment& sf, event_handler& eh); +} + +template +class typed_event_handler : public event_handler { +public: + virtual T get_result() = 0; +}; + +template +Result +parse(const string_fragment& sf, typed_event_handler& eh) +{ + TRY(details::parse(sf, eh)); + + return Ok(eh.get_result()); +} + +const xml_entity_map& get_xml_entity_map(); + +const emoji_map& get_emoji_map(); + +} // namespace md4cpp + +#endif diff --git a/src/pcrepp/pcrepp.hh b/src/pcrepp/pcrepp.hh index ce31f8a7..2093c6e1 100644 --- a/src/pcrepp/pcrepp.hh +++ b/src/pcrepp/pcrepp.hh @@ -189,17 +189,23 @@ public: pcre_input(const string_fragment& s) : pi_offset(0), pi_next_offset(0), pi_length(s.length()), - pi_string(s.data()){}; + pi_string(s.data()) + { + } pcre_input(const intern_string_t& s) : pi_offset(0), pi_next_offset(0), pi_length(s.size()), - pi_string(s.get()){}; + pi_string(s.get()) + { + } pcre_input(const string_fragment&&) = delete; pcre_input(const std::string& str, size_t off = 0) : pi_offset(off), pi_next_offset(off), pi_length(str.length()), - pi_string(str.c_str()){}; + pi_string(str.c_str()) + { + } pcre_input(const std::string&&, size_t off = 0) = delete; @@ -229,6 +235,15 @@ public: iter->length()); } + string_fragment get_string_fragment(pcre_context::const_iterator iter) const + { + return string_fragment{ + this->pi_string, + iter->c_begin, + iter->c_end, + }; + } + nonstd::optional get_substr_opt( pcre_context::const_iterator iter) const { @@ -276,9 +291,11 @@ struct pcre_named_capture { class iterator { public: iterator(pcre_named_capture* pnc, size_t name_len) - : i_named_capture(pnc), i_name_len(name_len){}; + : i_named_capture(pnc), i_name_len(name_len) + { + } - iterator() : i_named_capture(nullptr), i_name_len(0){}; + iterator() : i_named_capture(nullptr), i_name_len(0) {} const pcre_named_capture& operator*() const { @@ -341,12 +358,14 @@ public: class error : public std::exception { public: error(std::string msg, int offset = 0) - : e_msg(std::move(msg)), e_offset(offset){}; + : e_msg(std::move(msg)), e_offset(offset) + { + } const char* what() const noexcept override { return this->e_msg.c_str(); - }; + } const std::string e_msg; int e_offset; diff --git a/src/plain_text_source.cc b/src/plain_text_source.cc index 2136cae8..bbf9f769 100644 --- a/src/plain_text_source.cc +++ b/src/plain_text_source.cc @@ -78,6 +78,7 @@ plain_text_source& plain_text_source::replace_with(const attr_line_t& text_lines) { this->tds_lines.clear(); + this->tds_doc_sections = lnav::document::discover_metadata(text_lines); this->tds_lines = to_text_line(text_lines.split_lines()); this->tds_longest_line = this->compute_longest_line(); return *this; @@ -165,7 +166,7 @@ plain_text_source::compute_longest_line() } nonstd::optional -plain_text_source::line_for_offset(file_off_t off) +plain_text_source::line_for_offset(file_off_t off) const { struct cmper { bool operator()(const file_off_t& lhs, const text_line& rhs) @@ -200,3 +201,98 @@ plain_text_source::line_for_offset(file_off_t off) return nonstd::make_optional( vis_line_t(std::distance(this->tds_lines.begin(), iter))); } + +void +plain_text_source::text_crumbs_for_line(int line, + std::vector& crumbs) +{ + const auto initial_size = crumbs.size(); + const auto& tl = this->tds_lines[line]; + + this->tds_doc_sections.m_sections_tree.visit_overlapping( + tl.tl_offset, + [&crumbs, initial_size, meta = &this->tds_doc_sections, this]( + const auto& iv) { + auto path = crumbs | lnav::itertools::skip(initial_size) + | lnav::itertools::map(&breadcrumb::crumb::c_key) + | lnav::itertools::append(iv.value); + crumbs.template emplace_back( + iv.value, + [meta, path]() { return meta->possibility_provider(path); }, + [this, meta, path](const auto& key) { + auto curr_node = lnav::document::hier_node::lookup_path( + meta->m_sections_root.get(), path); + if (!curr_node) { + return; + } + auto* parent_node = curr_node.value()->hn_parent; + + if (parent_node == nullptr) { + return; + } + key.template match( + [this, parent_node](const std::string& str) { + auto sib_iter + = parent_node->hn_named_children.find(str); + if (sib_iter + == parent_node->hn_named_children.end()) { + return; + } + this->line_for_offset(sib_iter->second->hn_start) | + [this](const auto new_top) { + this->tss_view->set_top(new_top); + }; + }, + [this, parent_node](size_t index) { + if (index >= parent_node->hn_children.size()) { + return; + } + auto sib = parent_node->hn_children[index].get(); + this->line_for_offset(sib->hn_start) | + [this](const auto new_top) { + this->tss_view->set_top(new_top); + }; + }); + }); + }); + + auto path = crumbs | lnav::itertools::skip(initial_size) + | lnav::itertools::map(&breadcrumb::crumb::c_key); + auto node = lnav::document::hier_node::lookup_path( + this->tds_doc_sections.m_sections_root.get(), path); + + if (node && !node.value()->hn_children.empty()) { + auto poss_provider = [curr_node = node.value()]() { + std::vector retval; + for (const auto& child : curr_node->hn_named_children) { + retval.template emplace_back(child.first); + } + return retval; + }; + auto path_performer = [this, curr_node = node.value()]( + const breadcrumb::crumb::key_t& value) { + value.template match( + [this, curr_node](const std::string& str) { + auto child_iter = curr_node->hn_named_children.find(str); + if (child_iter != curr_node->hn_named_children.end()) { + this->line_for_offset(child_iter->second->hn_start) | + [this](const auto new_top) { + this->tss_view->set_top(new_top); + }; + } + }, + [this, curr_node](size_t index) { + auto* child = curr_node->hn_children[index].get(); + this->line_for_offset(child->hn_start) | + [this](const auto new_top) { + this->tss_view->set_top(new_top); + }; + }); + }; + crumbs.emplace_back( + "", "\u22ef", std::move(poss_provider), std::move(path_performer)); + crumbs.back().c_expected_input = node.value()->hn_named_children.empty() + ? breadcrumb::crumb::expected_input_t::index + : breadcrumb::crumb::expected_input_t::index_or_exact; + } +} diff --git a/src/plain_text_source.hh b/src/plain_text_source.hh index 97b73555..f901725a 100644 --- a/src/plain_text_source.hh +++ b/src/plain_text_source.hh @@ -35,6 +35,7 @@ #include "base/attr_line.hh" #include "base/file_range.hh" +#include "document.sections.hh" #include "textview_curses.hh" class plain_text_source @@ -116,15 +117,19 @@ public: return this; } + void text_crumbs_for_line(int line, + std::vector& crumbs) override; + protected: size_t compute_longest_line(); - nonstd::optional line_for_offset(file_off_t off); + nonstd::optional line_for_offset(file_off_t off) const; std::vector tds_lines; text_format_t tds_text_format{text_format_t::TF_UNKNOWN}; size_t tds_longest_line{0}; bool tds_reverse_selection{false}; + lnav::document::metadata tds_doc_sections; }; #endif // LNAV_PLAIN_TEXT_SOURCE_HH diff --git a/src/pretty_printer.cc b/src/pretty_printer.cc index b96c8ee7..30083f6f 100644 --- a/src/pretty_printer.cc +++ b/src/pretty_printer.cc @@ -106,7 +106,9 @@ pretty_printer::append_to(attr_line_t& al) case DT_COMMA: if (this->pp_depth > 0) { this->flush_values(true); - this->append_child_node(); + if (!this->pp_is_xml) { + this->append_child_node(); + } this->write_element(el); this->start_new_line(); this->pp_interval_state.back().is_start @@ -315,7 +317,9 @@ pretty_printer::ascend() this->pp_body_lines.pop(); this->pp_body_lines.top() += lines; - this->append_child_node(); + if (!this->pp_is_xml) { + this->append_child_node(); + } this->pp_interval_state.pop_back(); this->pp_hier_stage = std::move(this->pp_hier_nodes.back()); this->pp_hier_nodes.pop_back(); @@ -330,7 +334,8 @@ pretty_printer::descend() this->pp_depth += 1; this->pp_body_lines.push(0); this->pp_interval_state.resize(this->pp_depth + 1); - this->pp_hier_nodes.push_back(std::make_unique()); + this->pp_hier_nodes.push_back( + std::make_unique()); } void @@ -342,15 +347,16 @@ pretty_printer::append_child_node() } auto* top_node = this->pp_hier_nodes.back().get(); - auto new_key = ivstate.is_name.empty() ? key_t{top_node->hn_children.size()} - : key_t{ivstate.is_name}; + auto new_key = ivstate.is_name.empty() + ? lnav::document::section_key_t{top_node->hn_children.size()} + : lnav::document::section_key_t{ivstate.is_name}; this->pp_intervals.emplace_back( ivstate.is_start.value(), static_cast(this->pp_stream.tellp()), new_key); auto new_node = this->pp_hier_stage != nullptr ? std::move(this->pp_hier_stage) - : std::make_unique(); + : std::make_unique(); auto* retval = new_node.get(); new_node->hn_parent = top_node; new_node->hn_start = this->pp_intervals.back().start; @@ -364,43 +370,3 @@ pretty_printer::append_child_node() ivstate.is_start = nonstd::nullopt; ivstate.is_name.clear(); } - -nonstd::optional -pretty_printer::hier_node::lookup_child(pretty_printer::key_t key) const -{ - return make_optional_from_nullable(key.match( - [this](const std::string& str) -> pretty_printer::hier_node* { - auto iter = this->hn_named_children.find(str); - if (iter != this->hn_named_children.end()) { - return iter->second; - } - return nullptr; - }, - [this](size_t index) -> pretty_printer::hier_node* { - if (index < this->hn_children.size()) { - return this->hn_children[index].get(); - } - return nullptr; - })); -} - -nonstd::optional -pretty_printer::hier_node::lookup_path(const pretty_printer::hier_node* root, - const std::vector& path) -{ - auto retval = nonstd::make_optional(root); - - for (const auto& comp : path) { - if (!retval) { - break; - } - - retval = retval.value()->lookup_child(comp); - } - - if (!retval) { - return nonstd::nullopt; - } - - return retval; -} diff --git a/src/pretty_printer.hh b/src/pretty_printer.hh index 7f530740..339051fa 100644 --- a/src/pretty_printer.hh +++ b/src/pretty_printer.hh @@ -43,14 +43,10 @@ #include "base/file_range.hh" #include "base/opt_util.hh" #include "data_scanner.hh" -#include "intervaltree/IntervalTree.h" +#include "document.sections.hh" class pretty_printer { public: - using key_t = mapbox::util::variant; - using pretty_interval = interval_tree::Interval; - using pretty_tree = interval_tree::IntervalTree; - struct element { element(data_token_t token, pcre_context& pc) : e_token(token), e_capture(*pc.all()) @@ -66,27 +62,6 @@ public: pcre_context::capture_t e_capture; }; - struct hier_node { - hier_node* hn_parent{nullptr}; - file_off_t hn_start{0}; - std::multimap hn_named_children; - std::vector> hn_children; - - nonstd::optional lookup_child(key_t key) const; - - static nonstd::optional lookup_path( - const hier_node* root, const std::vector& path); - - template - static void depth_first(hier_node* root, F func) - { - for (auto& child : root->hn_children) { - depth_first(child.get(), func); - } - func(root); - } - }; - pretty_printer(data_scanner* ds, string_attrs_t sa, int leading_indent = 0) : pp_leading_indent(leading_indent), pp_scanner(ds), pp_attrs(std::move(sa)) @@ -105,17 +80,18 @@ public: } this->pp_interval_state.resize(1); - this->pp_hier_nodes.push_back(std::make_unique()); + this->pp_hier_nodes.push_back( + std::make_unique()); } void append_to(attr_line_t& al); - std::vector take_intervals() + std::vector take_intervals() { return std::move(this->pp_intervals); } - std::unique_ptr take_hier_root() + std::unique_ptr take_hier_root() { return std::move(this->pp_hier_stage); } @@ -152,9 +128,9 @@ private: int pp_shift_accum{0}; bool pp_is_xml{false}; std::vector pp_interval_state; - std::vector pp_intervals; - std::vector> pp_hier_nodes; - std::unique_ptr pp_hier_stage; + std::vector pp_intervals; + std::vector> pp_hier_nodes; + std::unique_ptr pp_hier_stage; }; #endif diff --git a/src/readline_highlighters.cc b/src/readline_highlighters.cc index c79fcc85..d5533ca5 100644 --- a/src/readline_highlighters.cc +++ b/src/readline_highlighters.cc @@ -31,6 +31,7 @@ #include "readline_highlighters.hh" +#include "base/attr_line.builder.hh" #include "base/string_util.hh" #include "config.h" #include "pcrepp/pcrepp.hh" @@ -39,7 +40,13 @@ #include "sql_util.hh" #include "view_curses.hh" -static void readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip); +static void readline_sqlite_highlighter_int(attr_line_t& al, + int x, + line_range sub); + +static void readline_shlex_highlighter_int(attr_line_t& al, + int x, + line_range sub); static bool check_re_prev(const std::string& line, int x) @@ -69,80 +76,79 @@ is_bracket(const std::string& str, int index, bool is_lit) } static void -find_matching_bracket(attr_line_t& al, int x, char left, char right) +find_matching_bracket( + attr_line_t& al, int x, line_range sub, char left, char right) { - view_colors& vc = view_colors::singleton(); - int matching_bracket_attrs - = A_BOLD | A_REVERSE | vc.attrs_for_role(role_t::VCR_OK); - int missing_bracket_attrs - = A_BOLD | A_REVERSE | vc.attrs_for_role(role_t::VCR_ERROR); bool is_lit = (left == 'Q'); - const std::string& line = al.get_string(); + attr_line_builder alb(al); + const auto& line = al.get_string(); int depth = 0; - if (x < 0 || x > (int) line.length()) { + if (x < sub.lr_start || x > sub.lr_end) { return; } if (line[x] == right && is_bracket(line, x, is_lit)) { - for (int lpc = x - 1; lpc > 0; lpc--) { + for (int lpc = x - 1; lpc >= sub.lr_start; lpc--) { if (line[lpc] == right && is_bracket(line, lpc, is_lit)) { depth += 1; } else if (line[lpc] == left && is_bracket(line, lpc, is_lit)) { if (depth == 0) { - al.get_attrs().emplace_back( - line_range(lpc, lpc + 1), - VC_STYLE.value(matching_bracket_attrs)); + alb.overlay_attr_for_char( + lpc, VC_STYLE.value(A_BOLD | A_REVERSE)); + alb.overlay_attr_for_char(lpc, + VC_ROLE.value(role_t::VCR_OK)); break; - } else { - depth -= 1; } + depth -= 1; } } } if (line[x] == left && is_bracket(line, x, is_lit)) { - for (size_t lpc = x + 1; lpc < line.length(); lpc++) { + for (size_t lpc = x + 1; lpc < sub.lr_end; lpc++) { if (line[lpc] == left && is_bracket(line, lpc, is_lit)) { depth += 1; } else if (line[lpc] == right && is_bracket(line, lpc, is_lit)) { if (depth == 0) { - al.get_attrs().emplace_back( - line_range(lpc, lpc + 1), - VC_STYLE.value(matching_bracket_attrs)); + alb.overlay_attr_for_char( + lpc, VC_STYLE.value(A_BOLD | A_REVERSE)); + alb.overlay_attr_for_char(lpc, + VC_ROLE.value(role_t::VCR_OK)); break; - } else { - depth -= 1; } + depth -= 1; } } } - int first_left = -1; + nonstd::optional first_left; depth = 0; - for (size_t lpc = 1; lpc < line.length(); lpc++) { + for (size_t lpc = sub.lr_start; lpc < sub.lr_end; lpc++) { if (line[lpc] == left && is_bracket(line, lpc, is_lit)) { depth += 1; - if (first_left == -1) { + if (!first_left) { first_left = lpc; } } else if (line[lpc] == right && is_bracket(line, lpc, is_lit)) { if (depth > 0) { depth -= 1; } else { - al.get_attrs().emplace_back( - line_range(is_lit ? lpc - 1 : lpc, lpc + 1), - VC_STYLE.value(missing_bracket_attrs)); + auto lr = line_range(is_lit ? lpc - 1 : lpc, lpc + 1); + alb.overlay_attr(lr, VC_STYLE.value(A_BOLD | A_REVERSE)); + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_ERROR)); } } } if (depth > 0) { - al.get_attrs().emplace_back( - line_range(is_lit ? first_left - 1 : first_left, first_left + 1), - VC_STYLE.value(missing_bracket_attrs)); + auto lr + = line_range(is_lit ? first_left.value() - 1 : first_left.value(), + first_left.value() + 1); + alb.overlay_attr(lr, VC_STYLE.value(A_BOLD | A_REVERSE)); + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_ERROR)); } } @@ -157,16 +163,8 @@ safe_read(const std::string& str, std::string::size_type index) } static void -readline_regex_highlighter_int(attr_line_t& al, int x, int skip) +readline_regex_highlighter_int(attr_line_t& al, int x, line_range sub) { - view_colors& vc = view_colors::singleton(); - int special_char = (A_BOLD | vc.attrs_for_role(role_t::VCR_RE_SPECIAL)); - int class_attrs = (A_BOLD | vc.attrs_for_role(role_t::VCR_SYMBOL)); - int repeated_char_attrs = vc.attrs_for_role(role_t::VCR_RE_REPEAT); - int bracket_attrs = vc.attrs_for_role(role_t::VCR_OK); - int error_attrs - = (A_BOLD | A_REVERSE | vc.attrs_for_role(role_t::VCR_ERROR)); - static const char* brackets[] = { "[]", "{}", @@ -176,10 +174,11 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip) nullptr, }; - auto& line = al.get_string(); + const auto& line = al.get_string(); + attr_line_builder alb(al); bool backslash_is_quoted = false; - for (size_t lpc = skip; lpc < line.length(); lpc++) { + for (size_t lpc = sub.lr_start; lpc < sub.lr_end; lpc++) { if (line[lpc - 1] != '\\') { switch (line[lpc]) { case '^': @@ -188,20 +187,24 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip) case '+': case '|': case '.': - al.get_attrs().emplace_back(line_range(lpc, lpc + 1), - VC_STYLE.value(special_char)); + alb.overlay_attr_for_char( + lpc, VC_ROLE.value(role_t::VCR_RE_SPECIAL)); if ((line[lpc] == '*' || line[lpc] == '+') && check_re_prev(line, lpc)) { - al.get_attrs().emplace_back( - line_range(lpc - 1, lpc), - VC_STYLE.value(repeated_char_attrs)); + alb.overlay_attr_for_char( + lpc - 1, VC_ROLE.value(role_t::VCR_RE_REPEAT)); } break; case '?': { struct line_range lr(lpc, lpc + 1); - if (line[lpc - 1] == '(') { + if (lpc == sub.lr_start || (lpc - sub.lr_start) == 0) { + alb.overlay_attr_for_char( + lpc, VC_STYLE.value(A_BOLD | A_REVERSE)); + alb.overlay_attr_for_char( + lpc, VC_ROLE.value(role_t::VCR_ERROR)); + } else if (line[lpc - 1] == '(') { switch (line[lpc + 1]) { case ':': case '!': @@ -211,16 +214,14 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip) lr.lr_end += 1; break; } - al.get_attrs().emplace_back( - lr, VC_STYLE.value(bracket_attrs)); + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_OK)); } else { - al.get_attrs().emplace_back( - lr, VC_STYLE.value(special_char)); + alb.overlay_attr(lr, + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); if (check_re_prev(line, lpc)) { - al.get_attrs().emplace_back( - line_range(lpc - 1, lpc), - VC_STYLE.value(repeated_char_attrs)); + alb.overlay_attr_for_char( + lpc - 1, VC_ROLE.value(role_t::VCR_RE_REPEAT)); } } break; @@ -232,8 +233,8 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip) case '}': case '[': case ']': - al.get_attrs().emplace_back(line_range(lpc, lpc + 1), - VC_STYLE.value(bracket_attrs)); + alb.overlay_attr_for_char(lpc, + VC_ROLE.value(role_t::VCR_OK)); break; } } @@ -245,8 +246,8 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip) switch (line[lpc]) { case '\\': backslash_is_quoted = true; - al.with_attr(string_attr(line_range(lpc - 1, lpc + 1), - VC_STYLE.value(special_char))); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); break; case 'd': case 'D': @@ -268,38 +269,41 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip) case 'G': case 'Z': case 'z': - al.get_attrs().emplace_back(line_range(lpc - 1, lpc + 1), - VC_STYLE.value(class_attrs)); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_SYMBOL)); break; case ' ': - al.get_attrs().emplace_back(line_range(lpc - 1, lpc + 1), - VC_STYLE.value(error_attrs)); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_STYLE.value(A_BOLD | A_REVERSE)); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_ERROR)); break; case '0': case 'x': if (safe_read(line, lpc + 1) == '{') { - al.with_attr(string_attr(line_range(lpc - 1, lpc + 1), - VC_STYLE.value(special_char))); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); } else if (isdigit(safe_read(line, lpc + 1)) && isdigit(safe_read(line, lpc + 2))) { - al.with_attr(string_attr(line_range(lpc - 1, lpc + 3), - VC_STYLE.value(special_char))); + alb.overlay_attr(line_range(lpc - 1, lpc + 3), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); } else { - al.with_attr(string_attr(line_range(lpc - 1, lpc + 1), - VC_STYLE.value(error_attrs))); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_STYLE.value(A_BOLD | A_REVERSE)); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_ERROR)); } break; case 'Q': case 'E': - al.with_attr(string_attr(line_range(lpc - 1, lpc + 1), - VC_STYLE.value(bracket_attrs))); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_OK)); break; default: if (isdigit(line[lpc])) { - al.get_attrs().emplace_back( - line_range(lpc - 1, lpc + 1), - VC_STYLE.value(special_char)); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); } break; } @@ -307,18 +311,19 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip) } for (int lpc = 0; brackets[lpc]; lpc++) { - find_matching_bracket(al, x, brackets[lpc][0], brackets[lpc][1]); + find_matching_bracket(al, x, sub, brackets[lpc][0], brackets[lpc][1]); } } void readline_regex_highlighter(attr_line_t& al, int x) { - readline_regex_highlighter_int(al, x, 1); + readline_regex_highlighter_int( + al, x, line_range{1, (int) al.get_string().size()}); } void -readline_command_highlighter(attr_line_t& al, int x) +readline_command_highlighter_int(attr_line_t& al, int x, line_range sub) { static const pcrepp RE_PREFIXES( R"(^:(filter-in|filter-out|delete-filter|enable-filter|disable-filter|highlight|clear-highlight|create-search-table\s+[^\s]+\s+))"); @@ -329,54 +334,56 @@ readline_command_highlighter(attr_line_t& al, int x) static const pcrepp COLOR_PREFIXES("^:(config)"); static const pcrepp COLOR_RE("(#(?:[a-fA-F0-9]{6}|[a-fA-F0-9]{3}))"); - view_colors& vc = view_colors::singleton(); - int keyword_attrs = (A_BOLD | vc.attrs_for_role(role_t::VCR_KEYWORD)); - + attr_line_builder alb(al); const auto& line = al.get_string(); pcre_context_static<30> pc; - pcre_input pi(line); + pcre_input pi(&line[sub.lr_start], 0, sub.length()); size_t ws_index; - ws_index = line.find(' '); - auto command = line.substr(0, ws_index); + ws_index = line.find(' ', sub.lr_start); + auto command = line.substr(sub.lr_start, ws_index); if (ws_index != std::string::npos) { - al.get_attrs().emplace_back(line_range(1, ws_index), - VC_STYLE.value(keyword_attrs)); + alb.overlay_attr(line_range(sub.lr_start + 1, ws_index), + VC_ROLE.value(role_t::VCR_KEYWORD)); } if (RE_PREFIXES.match(pc, pi)) { - readline_regex_highlighter_int(al, x, 1 + pc[0]->length()); + readline_regex_highlighter_int( + al, x, line_range{(int) ws_index, sub.lr_end}); } - pi.reset(line); + pi.reset(&line[sub.lr_start], 0, sub.length()); if (SH_PREFIXES.match(pc, pi)) { - readline_shlex_highlighter(al, x); + readline_shlex_highlighter_int( + al, x, line_range{(int) ws_index, sub.lr_end}); } - pi.reset(line); + pi.reset(&line[sub.lr_start], 0, sub.length()); if (SQL_PREFIXES.match(pc, pi)) { - readline_sqlite_highlighter_int(al, x, 1 + pc[0]->length()); + readline_sqlite_highlighter_int( + al, x, line_range{(int) ws_index, sub.lr_end}); } - pi.reset(line); + pi.reset(&line[sub.lr_start], 0, sub.length()); if (COLOR_PREFIXES.match(pc, pi)) { - pi.reset(line); + pi.reset(&line[sub.lr_start], 0, sub.length()); if (COLOR_RE.match(pc, pi)) { pcre_context::capture_t* cap = pc[0]; auto hash_color = pi.get_substr(cap); styling::color_unit::from_str(hash_color) .then([&](const auto& rgb_fg) { - al.get_attrs().emplace_back( - line_range{cap->c_begin, cap->c_begin + 1}, + alb.template overlay_attr( + line_range{sub.lr_start + cap->c_begin, + sub.lr_start + cap->c_begin + 1}, VC_ROLE.value(role_t::VCR_COLOR_HINT)); }); } } - pi.reset(line); + pi.reset(&line[sub.lr_start], 0, sub.length()); if (IDENT_PREFIXES.match(pc, pi) && ws_index != std::string::npos) { size_t start = ws_index, last; do { - for (; start < line.length() && isspace(line[start]); start++) + for (; start < sub.length() && isspace(line[start]); start++) ; - for (last = start; last < line.length() && !isspace(line[last]); + for (last = start; last < sub.length() && !isspace(line[last]); last++) ; struct line_range lr { @@ -392,169 +399,139 @@ readline_command_highlighter(attr_line_t& al, int x) { value = "#" + value; } - al.get_attrs().emplace_back( - lr, VC_STYLE.value(vc.attrs_for_ident(value))); + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_IDENTIFIER)); } start = last; - } while (start < line.length()); + } while (start < sub.length()); } } -static void -readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip) +void +readline_command_highlighter(attr_line_t& al, int x) { - static const auto keyword_re_str = sql_keyword_re(); - static pcrepp keyword_pcre(keyword_re_str.c_str(), PCRE_CASELESS); - static pcrepp string_literal_pcre("'[^']*('(?:'[^']*')*|$)"); - static pcrepp ident_pcre( - "(?:\\$|:)?(\\b[a-z_]\\w*)|\"([^\"]+)\"|\\[([^\\]]+)]", PCRE_CASELESS); - - static const char* brackets[] = {"[]", - "()", + readline_command_highlighter_int( + al, x, line_range{0, (int) al.get_string().length()}); +} - nullptr}; +static void +readline_sqlite_highlighter_int(attr_line_t& al, int x, line_range sub) +{ + static const char* brackets[] = { + "[]", + "()", - auto& vc = view_colors::singleton(); + nullptr, + }; - int keyword_attrs = vc.attrs_for_role(role_t::VCR_KEYWORD); - int symbol_attrs = vc.attrs_for_role(role_t::VCR_SYMBOL); - int string_attrs = vc.attrs_for_role(role_t::VCR_STRING); - int error_attrs = vc.attrs_for_role(role_t::VCR_ERROR) | A_REVERSE; + attr_line_builder alb(al); + const auto& line = al.get_string(); - pcre_context_static<30> pc; - pcre_input pi(al.get_string(), skip); - auto& line = al.get_string(); + auto anno_sql = al.subline(sub.lr_start, sub.length()); + anno_sql.get_attrs().clear(); + annotate_sql_statement(anno_sql); - if (startswith(line, ";.")) { - auto space = line.find(' '); - struct line_range lr { - 2, -1 + for (const auto& attr : anno_sql.al_attrs) { + line_range lr{ + sub.lr_start + attr.sa_range.lr_start, + sub.lr_start + attr.sa_range.lr_end, }; - - if (space != std::string::npos) { - lr.lr_end = space; - } - al.get_attrs().emplace_back(lr, VC_STYLE.value(keyword_attrs)); - return; - } - - while (ident_pcre.match(pc, pi)) { - pcre_context::capture_t* cap = pc.first_valid(); - int attrs = vc.attrs_for_ident(pi.get_substr_start(cap), cap->length()); - struct line_range lr(cap->c_begin, cap->c_end); - - if (line[cap->c_end] == '(') { - } else if (!lr.contains(x) && !lr.contains(x - 1)) { - al.get_attrs().emplace_back(lr, VC_STYLE.value(attrs)); - } - } - - pi.reset(line, skip); - - while (keyword_pcre.match(pc, pi)) { - pcre_context::capture_t* cap = pc.all(); - - al.get_attrs().emplace_back(line_range(cap->c_begin, cap->c_end), - VC_STYLE.value(keyword_attrs)); - } - - for (size_t lpc = skip; lpc < line.length(); lpc++) { - switch (line[lpc]) { - case '*': - case '<': - case '>': - case '=': - case '!': - case '-': - case '+': - al.get_attrs().emplace_back(line_range(lpc, lpc + 1), - VC_STYLE.value(symbol_attrs)); - break; - } - } - - pi.reset(line, skip); - - while (string_literal_pcre.match(pc, pi)) { - pcre_context::capture_t* cap = pc.all(); - struct line_range lr(cap->c_begin, cap->c_end); - string_attrs_t& sa = al.get_attrs(); - - remove_string_attr(sa, lr); - - if (line[cap->c_end - 1] != '\'') { - sa.emplace_back(line_range(cap->c_begin, cap->c_begin + 1), - VC_STYLE.value(error_attrs)); - lr.lr_start += 1; + if (attr.sa_type == &SQL_COMMAND_ATTR + || attr.sa_type == &SQL_KEYWORD_ATTR) { + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_KEYWORD)); + } else if (attr.sa_type == &SQL_IDENTIFIER_ATTR) { + if (!attr.sa_range.contains(x) && attr.sa_range.lr_end != x) { + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_IDENTIFIER)); + } + } else if (attr.sa_type == &SQL_FUNCTION_ATTR) { + alb.overlay_attr( + line_range{lr.lr_start, (int) line.find('(', lr.lr_start)}, + VC_ROLE.value(role_t::VCR_SYMBOL)); + } else if (attr.sa_type == &SQL_STRING_ATTR) { + if (lr.length() > 1 && al.al_string[lr.lr_end - 1] == '\'') { + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_STRING)); + } else { + alb.overlay_attr_for_char(lr.lr_start, + VC_STYLE.value(A_REVERSE)); + alb.overlay_attr_for_char(lr.lr_start, + VC_ROLE.value(role_t::VCR_ERROR)); + } + } else if (attr.sa_type == &SQL_OPERATOR_ATTR) { + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_SYMBOL)); + } else if (attr.sa_type == &SQL_COMMENT_ATTR) { + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_COMMENT)); } - sa.emplace_back(lr, VC_STYLE.value(string_attrs)); } for (int lpc = 0; brackets[lpc]; lpc++) { - find_matching_bracket(al, x, brackets[lpc][0], brackets[lpc][1]); + find_matching_bracket(al, x, sub, brackets[lpc][0], brackets[lpc][1]); } } void readline_sqlite_highlighter(attr_line_t& al, int x) { - readline_sqlite_highlighter_int(al, x, 0); + readline_sqlite_highlighter_int( + al, x, line_range{0, (int) al.get_string().length()}); } -void -readline_shlex_highlighter(attr_line_t& al, int x) +static void +readline_shlex_highlighter_int(attr_line_t& al, int x, line_range sub) { - view_colors& vc = view_colors::singleton(); - int special_char = (A_BOLD | vc.attrs_for_role(role_t::VCR_SYMBOL)); - int error_attrs = vc.attrs_for_role(role_t::VCR_ERROR) | A_REVERSE; - int string_attrs = vc.attrs_for_role(role_t::VCR_STRING); + attr_line_builder alb(al); const auto& str = al.get_string(); pcre_context::capture_t cap; shlex_token_t token; - int quote_start = -1; - shlex lexer(str); + nonstd::optional quote_start; + shlex lexer(string_fragment{al.al_string.data(), sub.lr_start, sub.lr_end}); while (lexer.tokenize(cap, token)) { switch (token) { case shlex_token_t::ST_ERROR: - al.with_attr(string_attr(line_range(cap.c_begin, cap.c_end), - VC_STYLE.value(error_attrs))); + alb.overlay_attr(line_range(sub.lr_start + cap.c_begin, + sub.lr_start + cap.c_end), + VC_STYLE.value(A_REVERSE)); + alb.overlay_attr(line_range(sub.lr_start + cap.c_begin, + sub.lr_start + cap.c_end), + VC_ROLE.value(role_t::VCR_ERROR)); break; case shlex_token_t::ST_TILDE: case shlex_token_t::ST_ESCAPE: - al.with_attr(string_attr(line_range(cap.c_begin, cap.c_end), - VC_STYLE.value(special_char))); + alb.overlay_attr(line_range(sub.lr_start + cap.c_begin, + sub.lr_start + cap.c_end), + VC_ROLE.value(role_t::VCR_SYMBOL)); break; case shlex_token_t::ST_DOUBLE_QUOTE_START: case shlex_token_t::ST_SINGLE_QUOTE_START: - quote_start = cap.c_begin; + quote_start = sub.lr_start + cap.c_begin; break; case shlex_token_t::ST_DOUBLE_QUOTE_END: case shlex_token_t::ST_SINGLE_QUOTE_END: - al.with_attr(string_attr(line_range(quote_start, cap.c_end), - VC_STYLE.value(string_attrs))); - quote_start = -1; + alb.overlay_attr( + line_range(quote_start.value(), sub.lr_start + cap.c_end), + VC_ROLE.value(role_t::VCR_STRING)); + quote_start = nonstd::nullopt; break; case shlex_token_t::ST_VARIABLE_REF: case shlex_token_t::ST_QUOTED_VARIABLE_REF: { int extra = token == shlex_token_t::ST_VARIABLE_REF ? 0 : 1; - auto ident = str.substr(cap.c_begin + 1 + extra, + auto ident = str.substr(sub.lr_start + cap.c_begin + 1 + extra, cap.length() - 1 - extra * 2); - int attrs = vc.attrs_for_ident(ident); - - al.with_attr(string_attr( - line_range(cap.c_begin, cap.c_begin + 1 + extra), - VC_STYLE.value(special_char))); - al.with_attr(string_attr( - line_range(cap.c_begin + 1 + extra, cap.c_end - extra), - VC_STYLE.value(x == cap.c_end || cap.contains(x) - ? special_char - : attrs))); + alb.overlay_attr( + line_range(sub.lr_start + cap.c_begin, + sub.lr_start + cap.c_begin + 1 + extra), + VC_ROLE.value(role_t::VCR_SYMBOL)); + alb.overlay_attr( + line_range(sub.lr_start + cap.c_begin + 1 + extra, + sub.lr_start + cap.c_end - extra), + VC_ROLE.value(x == sub.lr_start + cap.c_end + || cap.contains(x) + ? role_t::VCR_SYMBOL + : role_t::VCR_IDENTIFIER)); if (extra) { - al.with_attr( - string_attr(line_range(cap.c_end - 1, cap.c_end), - VC_STYLE.value(special_char))); + alb.overlay_attr_for_char( + sub.lr_start + cap.c_end - 1, + VC_ROLE.value(role_t::VCR_SYMBOL)); } break; } @@ -563,8 +540,111 @@ readline_shlex_highlighter(attr_line_t& al, int x) } } - if (quote_start != -1) { - al.with_attr(string_attr(line_range(quote_start, quote_start + 1), - VC_STYLE.value(error_attrs))); + if (quote_start) { + alb.overlay_attr_for_char(quote_start.value(), + VC_ROLE.value(role_t::VCR_ERROR)); + } +} + +void +readline_shlex_highlighter(attr_line_t& al, int x) +{ + readline_shlex_highlighter_int( + al, x, line_range{0, (int) al.al_string.length()}); +} + +static void +readline_lnav_highlighter_int(attr_line_t& al, int x, line_range sub) +{ + switch (al.al_string[sub.lr_start]) { + case ':': + readline_command_highlighter_int(al, x, sub); + break; + case ';': + readline_sqlite_highlighter_int(al, + x, + line_range{ + sub.lr_start + 1, + sub.lr_end, + }); + break; + case '|': + break; + case '/': + readline_regex_highlighter_int(al, + x, + line_range{ + sub.lr_start + 1, + sub.lr_end, + }); + break; + } +} + +void +readline_lnav_highlighter(attr_line_t& al, int x) +{ + static const pcrepp COMMENT_RE{R"(^\s*#)"}; + + attr_line_builder alb(al); + size_t start = 0, lf_pos; + nonstd::optional section_start; + + while ((lf_pos = al.get_string().find('\n', start)) != std::string::npos) { + line_range line{(int) start, (int) lf_pos}; + + if (line.empty()) { + start = lf_pos + 1; + continue; + } + + pcre_input pi(&al.al_string[line.lr_start], 0, line.length()); + pcre_context_static<30> pc; + + if (COMMENT_RE.match(pc, pi)) { + if (section_start) { + readline_lnav_highlighter_int(al, + x, + line_range{ + (int) section_start.value(), + line.lr_start, + }); + section_start = nonstd::nullopt; + } + const auto* cap = pc.all(); + alb.overlay_attr( + line_range{line.lr_start + cap->c_begin, (int) lf_pos}, + VC_ROLE.value(role_t::VCR_COMMENT)); + } else { + switch (al.al_string[line.lr_start]) { + case ':': + case ';': + case '|': + case '/': + if (section_start) { + readline_lnav_highlighter_int( + al, + x, + line_range{ + (int) section_start.value(), + line.lr_start, + }); + } + + section_start = line.lr_start; + break; + } + } + + start = lf_pos; + } + + if (section_start) { + readline_lnav_highlighter_int(al, + x, + line_range{ + (int) section_start.value(), + (int) al.al_string.length(), + }); } } diff --git a/src/readline_highlighters.hh b/src/readline_highlighters.hh index ab537713..800cc1bd 100644 --- a/src/readline_highlighters.hh +++ b/src/readline_highlighters.hh @@ -42,4 +42,6 @@ void readline_sqlite_highlighter(attr_line_t& line, int x); void readline_shlex_highlighter(attr_line_t& line, int x); +void readline_lnav_highlighter(attr_line_t& line, int x); + #endif diff --git a/src/regexp_vtab.cc b/src/regexp_vtab.cc index 530727b0..ca444abb 100644 --- a/src/regexp_vtab.cc +++ b/src/regexp_vtab.cc @@ -52,15 +52,15 @@ struct regexp_capture { -- The regexp_capture() table-valued function allows you to execute a regular- -- expression over a given string and get the captured data as rows in a table. CREATE TABLE regexp_capture ( - match_index integer, - capture_index integer, - capture_name text, - capture_count integer, - range_start integer, - range_stop integer, - content text, - value text HIDDEN, - pattern text HIDDEN + match_index INTEGER, + capture_index INTEGER, + capture_name TEXT, + capture_count INTEGER, + range_start INTEGER, + range_stop INTEGER, + content TEXT, + value TEXT HIDDEN, + pattern TEXT HIDDEN ); )"; diff --git a/src/shared_buffer.hh b/src/shared_buffer.hh index eca5e8bf..8e372133 100644 --- a/src/shared_buffer.hh +++ b/src/shared_buffer.hh @@ -115,12 +115,17 @@ public: return nullptr; } - string_fragment to_string_fragment(off_t offset, size_t len) + string_fragment to_string_fragment(off_t offset, size_t len) const { return string_fragment{ this->sb_data, (int) offset, (int) (offset + len)}; } + string_fragment to_string_fragment() const + { + return string_fragment{this->sb_data, 0, (int) this->length()}; + } + void share(shared_buffer& sb, char* data, size_t len); bool subset(shared_buffer_ref& other, off_t offset, size_t len); diff --git a/src/sql_help.hh b/src/sql_help.hh index 203d053c..6e1d6c22 100644 --- a/src/sql_help.hh +++ b/src/sql_help.hh @@ -45,6 +45,7 @@ extern string_attr_type SQL_STRING_ATTR; extern string_attr_type SQL_OPERATOR_ATTR; extern string_attr_type SQL_PAREN_ATTR; extern string_attr_type SQL_GARBAGE_ATTR; +extern string_attr_type SQL_COMMENT_ATTR; void annotate_sql_statement(attr_line_t& al_inout); diff --git a/src/sql_util.cc b/src/sql_util.cc index d2e73657..43a624cd 100644 --- a/src/sql_util.cc +++ b/src/sql_util.cc @@ -685,6 +685,8 @@ sql_compile_script(sqlite3* db, } else { sql_content.append(script); } + sql_content.with_attr_for_all( + VC_ROLE.value(role_t::VCR_QUOTED_CODE)); errors.emplace_back( lnav::console::user_message::error( "failed to compile SQL statement") @@ -766,16 +768,18 @@ sql_execute_script(sqlite3* db, } default: { + attr_line_t sql_content(sqlite3_sql(stmt)); const char* errmsg; errmsg = sqlite3_errmsg(db); + sql_content.with_attr_for_all( + VC_ROLE.value(role_t::VCR_QUOTED_CODE)); errors.emplace_back( lnav::console::user_message::error( "failed to execute SQL statement") .with_reason(errmsg) .with_snippet(lnav::console::snippet::from( - intern_string::lookup(src_name), - sqlite3_sql(stmt)))); + intern_string::lookup(src_name), sql_content))); done = true; break; } @@ -897,10 +901,12 @@ string_attr_type SQL_KEYWORD_ATTR("sql_keyword"); string_attr_type SQL_IDENTIFIER_ATTR("sql_ident"); string_attr_type SQL_FUNCTION_ATTR("sql_func"); string_attr_type SQL_STRING_ATTR("sql_string"); +string_attr_type SQL_UNTERMINATED_STRING_ATTR("sql_unstring"); string_attr_type SQL_OPERATOR_ATTR("sql_oper"); string_attr_type SQL_PAREN_ATTR("sql_paren"); string_attr_type SQL_COMMA_ATTR("sql_comma"); string_attr_type SQL_GARBAGE_ATTR("sql_garbage"); +string_attr_type SQL_COMMENT_ATTR("sql_comment"); void annotate_sql_statement(attr_line_t& al) @@ -919,6 +925,7 @@ annotate_sql_statement(attr_line_t& al) {pcrepp{R"(\A(\$?\b[a-z_]\w*)|\"([^\"]+)\"|\[([^\]]+)])", PCRE_CASELESS}, &SQL_IDENTIFIER_ATTR}, + {pcrepp{R"(\A--.*)"}, &SQL_COMMENT_ATTR}, {pcrepp{R"(\A(\*|<|>|=|!|\-|\+|\|\|))"}, &SQL_OPERATOR_ATTR}, {pcrepp{R"(\A.)"}, &SQL_GARBAGE_ATTR}, }; diff --git a/src/styling.hh b/src/styling.hh index 0c6bdfdb..14283683 100644 --- a/src/styling.hh +++ b/src/styling.hh @@ -184,6 +184,8 @@ struct lnav_theme { style_config lt_style_status_disabled_title; style_config lt_style_status_subtitle; style_config lt_style_status_hotkey; + style_config lt_style_quoted_code; + style_config lt_style_code_border; style_config lt_style_keyword; style_config lt_style_string; style_config lt_style_comment; @@ -207,8 +209,16 @@ struct lnav_theme { style_config lt_style_inactive_alert_status; style_config lt_style_file; style_config lt_style_header[6]; + style_config lt_style_hr; + style_config lt_style_hyperlink; style_config lt_style_list_glyph; style_config lt_style_breadcrumb; + style_config lt_style_table_border; + style_config lt_style_table_header; + style_config lt_style_quote_border; + style_config lt_style_quoted_text; + style_config lt_style_footnote_border; + style_config lt_style_footnote_text; std::map lt_level_styles; std::map lt_highlights; }; diff --git a/src/tailer/Makefile.am b/src/tailer/Makefile.am index 3d2a4b59..8ad9c89e 100644 --- a/src/tailer/Makefile.am +++ b/src/tailer/Makefile.am @@ -40,7 +40,8 @@ libtailercommon_a_SOURCES = \ libtailerpp_a_CPPFLAGS = \ $(AM_CPPFLAGS) \ -I$(srcdir)/.. \ - -I$(srcdir)/../fmtlib + -I$(srcdir)/../fmtlib \ + -I$(srcdir)/../third-party libtailerpp_a_SOURCES = \ tailerpp.cc @@ -51,7 +52,8 @@ tailerbin.h tailerbin.cc: tailer tailer.ape ../tools/bin2c$(BUILD_EXEEXT) libtailerservice_a_CPPFLAGS = \ $(AM_CPPFLAGS) \ -I$(srcdir)/.. \ - -I$(srcdir)/../fmtlib + -I$(srcdir)/../fmtlib \ + -I$(srcdir)/../third-party libtailerservice_a_LIBADD = \ libtailercommon.a \ diff --git a/src/text_format.cc b/src/text_format.cc index 0350d091..1cca5796 100644 --- a/src/text_format.cc +++ b/src/text_format.cc @@ -36,8 +36,13 @@ #include "yajl/api/yajl_parse.h" text_format_t -detect_text_format(const char* str, size_t len) +detect_text_format(string_fragment sf, + nonstd::optional path) { + static const auto GZ_EXT = ghc::filesystem::path(".gz"); + static const auto BZ2_EXT = ghc::filesystem::path(".bz2"); + static const auto MD_EXT = ghc::filesystem::path(".md"); + // XXX This is a pretty crude way of detecting format... static const pcrepp PYTHON_MATCHERS = pcrepp( "(?:" @@ -87,14 +92,30 @@ detect_text_format(const char* str, size_t len) PCRE_MULTILINE | PCRE_CASELESS); text_format_t retval = text_format_t::TF_UNKNOWN; - pcre_input pi(str, 0, len); + + if (path) { + if (path->extension() == GZ_EXT) { + path = path->stem(); + } + if (path->extension() == BZ2_EXT) { + path = path->stem(); + } + + if (path->extension() == MD_EXT) { + return text_format_t::TF_MARKDOWN; + } + } + + pcre_input pi(sf); pcre_context_static<30> pc; { auto_mem jhandle(yajl_free); jhandle = yajl_alloc(nullptr, nullptr, nullptr); - if (yajl_parse(jhandle, (unsigned char*) str, len) == yajl_status_ok) { + if (yajl_parse(jhandle, (unsigned char*) sf.data(), sf.length()) + == yajl_status_ok) + { return text_format_t::TF_JSON; } } diff --git a/src/text_format.hh b/src/text_format.hh index 19502703..d7690fc9 100644 --- a/src/text_format.hh +++ b/src/text_format.hh @@ -36,7 +36,9 @@ #include +#include "base/intern_string.hh" #include "fmt/format.h" +#include "ghc/filesystem.hpp" enum class text_format_t { TF_UNKNOWN, @@ -48,6 +50,7 @@ enum class text_format_t { TF_SQL, TF_XML, TF_JSON, + TF_MARKDOWN, }; namespace fmt { @@ -85,6 +88,9 @@ struct formatter : formatter { case text_format_t::TF_JSON: name = "application/json"; break; + case text_format_t::TF_MARKDOWN: + name = "text/markdown"; + break; } return formatter::format(name, ctx); } @@ -94,16 +100,10 @@ struct formatter : formatter { /** * Try to detect the format of the given text file fragment. * - * @param str The text to scan. - * @param len The length of the 'str' buffer. * @return The detected format. */ -text_format_t detect_text_format(const char* str, size_t len); - -inline text_format_t -detect_text_format(const std::string& str) -{ - return detect_text_format(str.c_str(), str.length()); -} +text_format_t detect_text_format(string_fragment sf, + nonstd::optional path + = nonstd::nullopt); #endif diff --git a/src/textfile_sub_source.cc b/src/textfile_sub_source.cc index 974258ff..c9a89d31 100644 --- a/src/textfile_sub_source.cc +++ b/src/textfile_sub_source.cc @@ -29,8 +29,10 @@ #include "textfile_sub_source.hh" +#include "base/fs_util.hh" #include "base/itertools.hh" #include "config.h" +#include "md2attr_line.hh" size_t textfile_sub_source::text_line_count() @@ -39,8 +41,13 @@ textfile_sub_source::text_line_count() if (!this->tss_files.empty()) { std::shared_ptr lf = this->current_file(); - auto* lfo = (line_filter_observer*) lf->get_logline_observer(); - retval = lfo->lfo_filter_state.tfs_index.size(); + auto rend_iter = this->tss_rendered_files.find(lf->get_filename()); + if (rend_iter == this->tss_rendered_files.end()) { + auto* lfo = (line_filter_observer*) lf->get_logline_observer(); + retval = lfo->lfo_filter_state.tfs_index.size(); + } else { + retval = rend_iter->second.rf_text_source->text_line_count(); + } } return retval; @@ -54,11 +61,18 @@ textfile_sub_source::text_value_for_line(textview_curses& tc, { if (!this->tss_files.empty()) { std::shared_ptr lf = this->current_file(); - auto* lfo = (line_filter_observer*) lf->get_logline_observer(); - auto read_result = lf->read_line( - lf->begin() + lfo->lfo_filter_state.tfs_index[line]); - if (read_result.isOk()) { - value_out = to_string(read_result.unwrap()); + auto rend_iter = this->tss_rendered_files.find(lf->get_filename()); + if (rend_iter == this->tss_rendered_files.end()) { + auto* lfo = dynamic_cast( + lf->get_logline_observer()); + auto read_result = lf->read_line( + lf->begin() + lfo->lfo_filter_state.tfs_index[line]); + if (read_result.isOk()) { + value_out = to_string(read_result.unwrap()); + } + } else { + rend_iter->second.rf_text_source->text_value_for_line( + tc, line, value_out, flags); } } else { value_out.clear(); @@ -70,10 +84,17 @@ textfile_sub_source::text_attrs_for_line(textview_curses& tc, int row, string_attrs_t& value_out) { - if (this->current_file() == nullptr) { + auto lf = this->current_file(); + if (lf == nullptr) { return; } + auto rend_iter = this->tss_rendered_files.find(lf->get_filename()); + if (rend_iter != this->tss_rendered_files.end()) { + rend_iter->second.rf_text_source->text_attrs_for_line( + tc, row, value_out); + } + struct line_range lr; lr.lr_start = 0; @@ -90,9 +111,16 @@ textfile_sub_source::text_size_for_line(textview_curses& tc, if (!this->tss_files.empty()) { std::shared_ptr lf = this->current_file(); - auto* lfo = (line_filter_observer*) lf->get_logline_observer(); - retval = lf->line_length(lf->begin() - + lfo->lfo_filter_state.tfs_index[line]); + auto rend_iter = this->tss_rendered_files.find(lf->get_filename()); + if (rend_iter == this->tss_rendered_files.end()) { + auto* lfo = dynamic_cast( + lf->get_logline_observer()); + retval = lf->line_length(lf->begin() + + lfo->lfo_filter_state.tfs_index[line]); + } else { + retval = rend_iter->second.rf_text_source->text_size_for_line( + tc, line, flags); + } } return retval; @@ -208,8 +236,11 @@ textfile_sub_source::get_filtered_count() const int retval = 0; if (lf != nullptr) { - auto* lfo = (line_filter_observer*) lf->get_logline_observer(); - retval = lf->size() - lfo->lfo_filter_state.tfs_index.size(); + auto rend_iter = this->tss_rendered_files.find(lf->get_filename()); + if (rend_iter == this->tss_rendered_files.end()) { + auto* lfo = (line_filter_observer*) lf->get_logline_observer(); + retval = lf->size() - lfo->lfo_filter_state.tfs_index.size(); + } } return retval; } @@ -223,7 +254,7 @@ textfile_sub_source::get_filtered_count_for(size_t filter_index) const return 0; } - auto* lfo = (line_filter_observer*) lf->get_logline_observer(); + auto* lfo = dynamic_cast(lf->get_logline_observer()); return lfo->lfo_filter_state.tfs_filter_hits[filter_index]; } @@ -274,4 +305,312 @@ textfile_sub_source::text_crumbs_for_line( this->to_front(lf_opt.value()); this->tss_view->reload_data(); }); + + auto rend_iter = this->tss_rendered_files.find(lf->get_filename()); + if (rend_iter != this->tss_rendered_files.end()) { + rend_iter->second.rf_text_source->text_crumbs_for_line(line, crumbs); + } + + auto meta_iter = this->tss_doc_metadata.find(lf->get_filename()); + if (meta_iter != this->tss_doc_metadata.end()) { + auto* lfo + = dynamic_cast(lf->get_logline_observer()); + auto ll_iter = lf->begin() + lfo->lfo_filter_state.tfs_index[line]; + auto ll_next_iter = ll_iter + 1; + auto end_offset = (ll_next_iter == lf->end()) + ? lf->get_index_size() + : ll_next_iter->get_offset(); + const auto initial_size = crumbs.size(); + + meta_iter->second.ms_metadata.m_sections_tree.visit_overlapping( + ll_iter->get_offset(), + end_offset, + [&crumbs, + initial_size, + meta = &meta_iter->second.ms_metadata, + this, + lf](const auto& iv) { + auto path = crumbs | lnav::itertools::skip(initial_size) + | lnav::itertools::map(&breadcrumb::crumb::c_key) + | lnav::itertools::append(iv.value); + auto curr_node = lnav::document::hier_node::lookup_path( + meta->m_sections_root.get(), path); + crumbs.template emplace_back( + iv.value, + [meta, path]() { return meta->possibility_provider(path); }, + [this, curr_node, path, lf](const auto& key) { + if (!curr_node) { + return; + } + auto* parent_node = curr_node.value()->hn_parent; + if (parent_node == nullptr) { + return; + } + key.template match( + [this, parent_node](const std::string& str) { + auto sib_iter + = parent_node->hn_named_children.find(str); + if (sib_iter + == parent_node->hn_named_children.end()) { + return; + } + this->set_top_from_off( + sib_iter->second->hn_start); + }, + [this, parent_node](size_t index) { + if (index >= parent_node->hn_children.size()) { + return; + } + auto sib + = parent_node->hn_children[index].get(); + this->set_top_from_off(sib->hn_start); + }); + }); + if (curr_node + && curr_node.value()->hn_parent->hn_children.size() + != curr_node.value() + ->hn_parent->hn_named_children.size()) + { + auto node = lnav::document::hier_node::lookup_path( + meta->m_sections_root.get(), path); + + crumbs.back().c_expected_input + = curr_node.value() + ->hn_parent->hn_named_children.empty() + ? breadcrumb::crumb::expected_input_t::index + : breadcrumb::crumb::expected_input_t::index_or_exact; + crumbs.back().with_possible_range( + node | lnav::itertools::map([](const auto hn) { + return hn->hn_parent->hn_children.size(); + }) + | lnav::itertools::unwrap_or(size_t{0})); + } + }); + + auto path = crumbs | lnav::itertools::skip(initial_size) + | lnav::itertools::map(&breadcrumb::crumb::c_key); + auto node = lnav::document::hier_node::lookup_path( + meta_iter->second.ms_metadata.m_sections_root.get(), path); + + if (node && !node.value()->hn_children.empty()) { + auto poss_provider = [curr_node = node.value()]() { + std::vector retval; + for (const auto& child : curr_node->hn_named_children) { + retval.template emplace_back(child.first); + } + return retval; + }; + auto path_performer = [this, curr_node = node.value()]( + const breadcrumb::crumb::key_t& value) { + value.template match( + [this, curr_node](const std::string& str) { + auto child_iter + = curr_node->hn_named_children.find(str); + if (child_iter != curr_node->hn_named_children.end()) { + this->set_top_from_off( + child_iter->second->hn_start); + } + }, + [this, curr_node](size_t index) { + if (index >= curr_node->hn_children.size()) { + return; + } + auto* child = curr_node->hn_children[index].get(); + this->set_top_from_off(child->hn_start); + }); + }; + crumbs.emplace_back("", "\u22ef", poss_provider, path_performer); + crumbs.back().c_expected_input + = node.value()->hn_named_children.empty() + ? breadcrumb::crumb::expected_input_t::index + : breadcrumb::crumb::expected_input_t::index_or_exact; + } + } +} + +bool +textfile_sub_source::rescan_files( + textfile_sub_source::scan_callback& callback, + nonstd::optional deadline) +{ + file_iterator iter; + bool retval = false; + + if (this->tss_view->is_paused()) { + return retval; + } + + std::vector> closed_files; + for (iter = this->tss_files.begin(); iter != this->tss_files.end();) { + std::shared_ptr lf = (*iter); + + if (lf->is_closed()) { + iter = this->tss_files.erase(iter); + this->tss_rendered_files.erase(lf->get_filename()); + this->tss_doc_metadata.erase(lf->get_filename()); + this->detach_observer(lf); + closed_files.template emplace_back(lf); + continue; + } + + try { + const auto& st = lf->get_stat(); + uint32_t old_size = lf->size(); + auto new_text_data = lf->rebuild_index(deadline); + + if (lf->get_format() != nullptr) { + iter = this->tss_files.erase(iter); + this->tss_rendered_files.erase(lf->get_filename()); + this->tss_doc_metadata.erase(lf->get_filename()); + this->detach_observer(lf); + callback.promote_file(lf); + continue; + } + + switch (new_text_data) { + case logfile::rebuild_result_t::NEW_LINES: + case logfile::rebuild_result_t::NEW_ORDER: + retval = true; + break; + default: + break; + } + callback.scanned_file(lf); + + if (lf->get_text_format() == text_format_t::TF_MARKDOWN) { + auto rend_iter + = this->tss_rendered_files.find(lf->get_filename()); + if (rend_iter != this->tss_rendered_files.end()) { + if (rend_iter->second.rf_file_size == st.st_size + && rend_iter->second.rf_mtime == st.st_mtime) + { + ++iter; + continue; + } + log_info("markdown file has been updated, re-rendering: %s", + lf->get_filename().c_str()); + this->tss_rendered_files.erase(rend_iter); + } + + auto read_res = lf->read_file(); + if (read_res.isOk()) { + auto content = read_res.unwrap(); + auto content_sf = string_fragment{content}; + auto front_matter_terminator = content.length() > 8 + ? content.find("\n---\n", 4) + : std::string::npos; + + if (startswith(content, "---\n") + && front_matter_terminator != std::string::npos) + { + content_sf + = content_sf.substr(front_matter_terminator + 4); + } + + md2attr_line mdal; + + mdal.with_source_path(lf->get_actual_path()); + auto parse_res = md4cpp::parse(content_sf, mdal); + if (parse_res.isOk()) { + auto& rf = this->tss_rendered_files[lf->get_filename()]; + rf.rf_mtime = st.st_mtime; + rf.rf_file_size = st.st_size; + rf.rf_text_source + = std::make_unique(); + rf.rf_text_source->register_view(this->tss_view); + rf.rf_text_source->replace_with(parse_res.unwrap()); + log_info("successfully rendered markdown file: %s", + lf->get_filename().c_str()); + } else { + log_error("unable to parse markdown file: %s -- %s", + lf->get_filename().c_str(), + parse_res.unwrapErr().c_str()); + } + } else { + log_error("unable to read markdown file: %s -- %s", + lf->get_filename().c_str(), + read_res.unwrapErr().c_str()); + } + ++iter; + continue; + } + + if (!retval) { + auto ms_iter = this->tss_doc_metadata.find(lf->get_filename()); + + if (ms_iter != this->tss_doc_metadata.end()) { + if (st.st_mtime != ms_iter->second.ms_mtime + || st.st_size != ms_iter->second.ms_file_size) + { + this->tss_doc_metadata.erase(ms_iter); + ms_iter = this->tss_doc_metadata.end(); + } + } + + if (ms_iter == this->tss_doc_metadata.end()) { + auto read_res = lf->read_file(); + + if (read_res.isOk()) { + auto content = read_res.unwrap(); + + this->tss_doc_metadata[lf->get_filename()] + = metadata_state{ + st.st_mtime, + static_cast(st.st_size), + lnav::document::discover_structure(content), + }; + } + } + } + + uint32_t filter_in_mask, filter_out_mask; + + this->get_filters().get_enabled_mask(filter_in_mask, + filter_out_mask); + auto* lfo = (line_filter_observer*) lf->get_logline_observer(); + for (uint32_t lpc = old_size; lpc < lf->size(); lpc++) { + if (this->tss_apply_filters + && lfo->excluded(filter_in_mask, filter_out_mask, lpc)) + { + continue; + } + lfo->lfo_filter_state.tfs_index.push_back(lpc); + } + } catch (const line_buffer::error& e) { + iter = this->tss_files.erase(iter); + this->tss_rendered_files.erase(lf->get_filename()); + this->tss_doc_metadata.erase(lf->get_filename()); + lf->close(); + this->detach_observer(lf); + closed_files.template emplace_back(lf); + continue; + } + + ++iter; + } + if (!closed_files.empty()) { + callback.closed_files(closed_files); + } + + if (retval) { + this->tss_view->search_new_data(); + } + + return retval; +} + +void +textfile_sub_source::set_top_from_off(file_off_t off) +{ + auto lf = this->current_file(); + + lf->line_for_offset(off) | [this, lf](auto new_top_iter) { + auto* lfo = (line_filter_observer*) lf->get_logline_observer(); + auto new_top_opt = lfo->lfo_filter_state.content_line_to_vis_line( + std::distance(lf->cbegin(), new_top_iter)); + + if (new_top_opt) { + this->tss_view->set_top(vis_line_t(new_top_opt.value())); + } + }; } diff --git a/src/textfile_sub_source.hh b/src/textfile_sub_source.hh index 7f9cd552..8572a0e0 100644 --- a/src/textfile_sub_source.hh +++ b/src/textfile_sub_source.hh @@ -34,6 +34,7 @@ #include "filter_observer.hh" #include "logfile.hh" +#include "plain_text_source.hh" #include "textview_curses.hh" class textfile_sub_source @@ -92,6 +93,8 @@ public: void to_front(const std::shared_ptr& lf); + void set_top_from_off(file_off_t off); + void rotate_left(); void rotate_right(); @@ -100,84 +103,18 @@ public: void push_back(const std::shared_ptr& lf); - template - bool rescan_files(T& callback, - nonstd::optional deadline - = nonstd::nullopt) - { - file_iterator iter; - bool retval = false; - - if (this->tss_view->is_paused()) { - return retval; - } - - std::vector> closed_files; - for (iter = this->tss_files.begin(); iter != this->tss_files.end();) { - std::shared_ptr lf = (*iter); - - if (lf->is_closed()) { - iter = this->tss_files.erase(iter); - this->detach_observer(lf); - closed_files.template emplace_back(lf); - continue; - } - - try { - uint32_t old_size = lf->size(); - logfile::rebuild_result_t new_text_data - = lf->rebuild_index(deadline); - - if (lf->get_format() != nullptr) { - iter = this->tss_files.erase(iter); - this->detach_observer(lf); - callback.promote_file(lf); - continue; - } - - switch (new_text_data) { - case logfile::rebuild_result_t::NEW_LINES: - case logfile::rebuild_result_t::NEW_ORDER: - retval = true; - break; - default: - break; - } - callback.scanned_file(lf); - - uint32_t filter_in_mask, filter_out_mask; - - this->get_filters().get_enabled_mask(filter_in_mask, - filter_out_mask); - auto* lfo = (line_filter_observer*) lf->get_logline_observer(); - for (uint32_t lpc = old_size; lpc < lf->size(); lpc++) { - if (this->tss_apply_filters - && lfo->excluded(filter_in_mask, filter_out_mask, lpc)) - { - continue; - } - lfo->lfo_filter_state.tfs_index.push_back(lpc); - } - } catch (const line_buffer::error& e) { - iter = this->tss_files.erase(iter); - lf->close(); - this->detach_observer(lf); - closed_files.template emplace_back(lf); - continue; - } - - ++iter; - } - if (!closed_files.empty()) { - callback.closed_files(closed_files); - } - - if (retval) { - this->tss_view->search_new_data(); - } + class scan_callback { + public: + virtual void closed_files( + const std::vector>& files) + = 0; + virtual void promote_file(const std::shared_ptr& lf) = 0; + virtual void scanned_file(const std::shared_ptr& lf) = 0; + }; - return retval; - } + bool rescan_files(scan_callback& callback, + nonstd::optional deadline + = nonstd::nullopt); void text_filters_changed() override; @@ -203,8 +140,22 @@ private: delete lfo; } + struct rendered_file { + time_t rf_mtime; + file_size_t rf_file_size; + std::unique_ptr rf_text_source; + }; + + struct metadata_state { + time_t ms_mtime; + file_size_t ms_file_size; + lnav::document::metadata ms_metadata; + }; + std::deque> tss_files; std::deque> tss_hidden_files; + std::unordered_map tss_rendered_files; + std::unordered_map tss_doc_metadata; }; #endif diff --git a/src/textview_curses.cc b/src/textview_curses.cc index ca3c7ca7..8c908e80 100644 --- a/src/textview_curses.cc +++ b/src/textview_curses.cc @@ -985,3 +985,91 @@ text_sub_source::text_crumbs_for_line(int line, std::vector& crumbs) { } + +logfile_filter_state::logfile_filter_state(std::shared_ptr lf) + : tfs_logfile(std::move(lf)) +{ + memset(this->tfs_filter_count, 0, sizeof(this->tfs_filter_count)); + memset(this->tfs_filter_hits, 0, sizeof(this->tfs_filter_hits)); + memset(this->tfs_message_matched, 0, sizeof(this->tfs_message_matched)); + memset(this->tfs_lines_for_message, 0, sizeof(this->tfs_lines_for_message)); + memset(this->tfs_last_message_matched, + 0, + sizeof(this->tfs_last_message_matched)); + memset(this->tfs_last_lines_for_message, + 0, + sizeof(this->tfs_last_lines_for_message)); + this->tfs_mask.reserve(64 * 1024); +} + +void +logfile_filter_state::clear() +{ + this->tfs_logfile = nullptr; + memset(this->tfs_filter_count, 0, sizeof(this->tfs_filter_count)); + memset(this->tfs_filter_hits, 0, sizeof(this->tfs_filter_hits)); + memset(this->tfs_message_matched, 0, sizeof(this->tfs_message_matched)); + memset(this->tfs_lines_for_message, 0, sizeof(this->tfs_lines_for_message)); + memset(this->tfs_last_message_matched, + 0, + sizeof(this->tfs_last_message_matched)); + memset(this->tfs_last_lines_for_message, + 0, + sizeof(this->tfs_last_lines_for_message)); + this->tfs_mask.clear(); + this->tfs_index.clear(); +} + +void +logfile_filter_state::clear_filter_state(size_t index) +{ + this->tfs_filter_count[index] = 0; + this->tfs_filter_hits[index] = 0; + this->tfs_message_matched[index] = false; + this->tfs_lines_for_message[index] = 0; + this->tfs_last_message_matched[index] = false; + this->tfs_last_lines_for_message[index] = 0; +} + +void +logfile_filter_state::clear_deleted_filter_state(uint32_t used_mask) +{ + for (int lpc = 0; lpc < MAX_FILTERS; lpc++) { + if (!(used_mask & (1L << lpc))) { + this->clear_filter_state(lpc); + } + } + for (size_t lpc = 0; lpc < this->tfs_mask.size(); lpc++) { + this->tfs_mask[lpc] &= used_mask; + } +} + +void +logfile_filter_state::resize(size_t newsize) +{ + size_t old_mask_size = this->tfs_mask.size(); + + this->tfs_mask.resize(newsize); + if (newsize > old_mask_size) { + memset(&this->tfs_mask[old_mask_size], + 0, + sizeof(uint32_t) * (newsize - old_mask_size)); + } +} + +nonstd::optional +logfile_filter_state::content_line_to_vis_line(uint32_t line) +{ + if (this->tfs_index.empty()) { + return nonstd::nullopt; + } + + auto iter = std::lower_bound( + this->tfs_index.begin(), this->tfs_index.end(), line); + + if (iter == this->tfs_index.end() || *iter != line) { + return nonstd::nullopt; + } + + return nonstd::make_optional(std::distance(this->tfs_index.begin(), iter)); +} diff --git a/src/textview_curses.hh b/src/textview_curses.hh index 776c4358..52671b4b 100644 --- a/src/textview_curses.hh +++ b/src/textview_curses.hh @@ -54,76 +54,17 @@ using vis_bookmarks = bookmarks::type; class logfile_filter_state { public: - logfile_filter_state(std::shared_ptr lf = nullptr) - : tfs_logfile(std::move(lf)) - { - memset(this->tfs_filter_count, 0, sizeof(this->tfs_filter_count)); - memset(this->tfs_filter_hits, 0, sizeof(this->tfs_filter_hits)); - memset(this->tfs_message_matched, 0, sizeof(this->tfs_message_matched)); - memset(this->tfs_lines_for_message, - 0, - sizeof(this->tfs_lines_for_message)); - memset(this->tfs_last_message_matched, - 0, - sizeof(this->tfs_last_message_matched)); - memset(this->tfs_last_lines_for_message, - 0, - sizeof(this->tfs_last_lines_for_message)); - this->tfs_mask.reserve(64 * 1024); - } - - void clear() - { - this->tfs_logfile = nullptr; - memset(this->tfs_filter_count, 0, sizeof(this->tfs_filter_count)); - memset(this->tfs_filter_hits, 0, sizeof(this->tfs_filter_hits)); - memset(this->tfs_message_matched, 0, sizeof(this->tfs_message_matched)); - memset(this->tfs_lines_for_message, - 0, - sizeof(this->tfs_lines_for_message)); - memset(this->tfs_last_message_matched, - 0, - sizeof(this->tfs_last_message_matched)); - memset(this->tfs_last_lines_for_message, - 0, - sizeof(this->tfs_last_lines_for_message)); - this->tfs_mask.clear(); - this->tfs_index.clear(); - } - - void clear_filter_state(size_t index) - { - this->tfs_filter_count[index] = 0; - this->tfs_filter_hits[index] = 0; - this->tfs_message_matched[index] = false; - this->tfs_lines_for_message[index] = 0; - this->tfs_last_message_matched[index] = false; - this->tfs_last_lines_for_message[index] = 0; - } - - void clear_deleted_filter_state(uint32_t used_mask) - { - for (int lpc = 0; lpc < MAX_FILTERS; lpc++) { - if (!(used_mask & (1L << lpc))) { - this->clear_filter_state(lpc); - } - } - for (size_t lpc = 0; lpc < this->tfs_mask.size(); lpc++) { - this->tfs_mask[lpc] &= used_mask; - } - } + logfile_filter_state(std::shared_ptr lf = nullptr); - void resize(size_t newsize) - { - size_t old_mask_size = this->tfs_mask.size(); + void clear(); - this->tfs_mask.resize(newsize); - if (newsize > old_mask_size) { - memset(&this->tfs_mask[old_mask_size], - 0, - sizeof(uint32_t) * (newsize - old_mask_size)); - } - } + void clear_filter_state(size_t index); + + void clear_deleted_filter_state(uint32_t used_mask); + + void resize(size_t newsize); + + nonstd::optional content_line_to_vis_line(uint32_t line); const static int MAX_FILTERS = 32; diff --git a/src/themes/monocai.json b/src/themes/monocai.json index 20f18b11..2753ad48 100644 --- a/src/themes/monocai.json +++ b/src/themes/monocai.json @@ -72,13 +72,15 @@ "background-color": "#888" }, "h1": { - "underline": true + "color": "$magenta", + "bold": true }, "h2": { + "color": "$magenta", "underline": true }, "h3": { - "underline": true + "color": "$magenta" }, "h4": { "underline": true @@ -89,14 +91,49 @@ "h6": { "underline": true }, + "hr": { + "color": "#444" + }, + "hyperlink": { + "underline": true + }, "list-glyph": { "color": "$yellow" }, "breadcrumb": { "color": "#99a" + }, + "table-border": { + "color": "#444" + }, + "table-header": { + "bold": true + }, + "quote-border": { + "color": "#666", + "background-color": "#444" + }, + "quoted-text": { + "background-color": "#444" + }, + "footnote-border": { + "color": "$blue", + "background-color": "#444" + }, + "footnote-text": { + "color": "#eee", + "background-color": "#444" } }, "syntax-styles": { + "quoted-code": { + "color": "#eee", + "background-color": "#121212" + }, + "code-border": { + "color": "#444", + "background-color": "#121212" + }, "keyword": { "color": "#ff6188", "bold": true @@ -106,7 +143,7 @@ "bold": true }, "comment": { - "color": "#727072" + "color": "#949194" }, "doc-directive": { "color": "#a9dc76" diff --git a/src/third-party/md4c/md4c.c b/src/third-party/md4c/md4c.c new file mode 100644 index 00000000..3677c0e0 --- /dev/null +++ b/src/third-party/md4c/md4c.c @@ -0,0 +1,6410 @@ +/* + * MD4C: Markdown parser for C + * (http://github.com/mity/md4c) + * + * Copyright (c) 2016-2020 Martin Mitas + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "md4c.h" + +#include +#include +#include +#include + + +/***************************** + *** Miscellaneous Stuff *** + *****************************/ + +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199409L + /* C89/90 or old compilers in general may not understand "inline". */ + #if defined __GNUC__ + #define inline __inline__ + #elif defined _MSC_VER + #define inline __inline + #else + #define inline + #endif +#endif + +/* Make the UTF-8 support the default. */ +#if !defined MD4C_USE_ASCII && !defined MD4C_USE_UTF8 && !defined MD4C_USE_UTF16 + #define MD4C_USE_UTF8 +#endif + +/* Magic for making wide literals with MD4C_USE_UTF16. */ +#ifdef _T + #undef _T +#endif +#if defined MD4C_USE_UTF16 + #define _T(x) L##x +#else + #define _T(x) x +#endif + +/* Misc. macros. */ +#define SIZEOF_ARRAY(a) (sizeof(a) / sizeof(a[0])) + +#define STRINGIZE_(x) #x +#define STRINGIZE(x) STRINGIZE_(x) + +#ifndef TRUE + #define TRUE 1 + #define FALSE 0 +#endif + +#define MD_LOG(msg) \ + do { \ + if(ctx->parser.debug_log != NULL) \ + ctx->parser.debug_log((msg), ctx->userdata); \ + } while(0) + +#ifdef DEBUG + #define MD_ASSERT(cond) \ + do { \ + if(!(cond)) { \ + MD_LOG(__FILE__ ":" STRINGIZE(__LINE__) ": " \ + "Assertion '" STRINGIZE(cond) "' failed."); \ + exit(1); \ + } \ + } while(0) + + #define MD_UNREACHABLE() MD_ASSERT(1 == 0) +#else + #ifdef __GNUC__ + #define MD_ASSERT(cond) do { if(!(cond)) __builtin_unreachable(); } while(0) + #define MD_UNREACHABLE() do { __builtin_unreachable(); } while(0) + #elif defined _MSC_VER && _MSC_VER > 120 + #define MD_ASSERT(cond) do { __assume(cond); } while(0) + #define MD_UNREACHABLE() do { __assume(0); } while(0) + #else + #define MD_ASSERT(cond) do {} while(0) + #define MD_UNREACHABLE() do {} while(0) + #endif +#endif + +/* For falling through case labels in switch statements. */ +#if defined __clang__ && __clang_major__ >= 12 + #define MD_FALLTHROUGH() __attribute__((fallthrough)) +#elif defined __GNUC__ && __GNUC__ >= 7 + #define MD_FALLTHROUGH() __attribute__((fallthrough)) +#else + #define MD_FALLTHROUGH() ((void)0) +#endif + +/* Suppress "unused parameter" warnings. */ +#define MD_UNUSED(x) ((void)x) + + +/************************ + *** Internal Types *** + ************************/ + +/* These are omnipresent so lets save some typing. */ +#define CHAR MD_CHAR +#define SZ MD_SIZE +#define OFF MD_OFFSET + +typedef struct MD_MARK_tag MD_MARK; +typedef struct MD_BLOCK_tag MD_BLOCK; +typedef struct MD_CONTAINER_tag MD_CONTAINER; +typedef struct MD_REF_DEF_tag MD_REF_DEF; + + +/* During analyzes of inline marks, we need to manage some "mark chains", + * of (yet unresolved) openers. This structure holds start/end of the chain. + * The chain internals are then realized through MD_MARK::prev and ::next. + */ +typedef struct MD_MARKCHAIN_tag MD_MARKCHAIN; +struct MD_MARKCHAIN_tag { + int head; /* Index of first mark in the chain, or -1 if empty. */ + int tail; /* Index of last mark in the chain, or -1 if empty. */ +}; + +/* Context propagated through all the parsing. */ +typedef struct MD_CTX_tag MD_CTX; +struct MD_CTX_tag { + /* Immutable stuff (parameters of md_parse()). */ + const CHAR* text; + SZ size; + MD_PARSER parser; + void* userdata; + + /* When this is true, it allows some optimizations. */ + int doc_ends_with_newline; + + /* Helper temporary growing buffer. */ + CHAR* buffer; + unsigned alloc_buffer; + + /* Reference definitions. */ + MD_REF_DEF* ref_defs; + int n_ref_defs; + int alloc_ref_defs; + void** ref_def_hashtable; + int ref_def_hashtable_size; + + /* Stack of inline/span markers. + * This is only used for parsing a single block contents but by storing it + * here we may reuse the stack for subsequent blocks; i.e. we have fewer + * (re)allocations. */ + MD_MARK* marks; + int n_marks; + int alloc_marks; + +#if defined MD4C_USE_UTF16 + char mark_char_map[128]; +#else + char mark_char_map[256]; +#endif + + /* For resolving of inline spans. */ + MD_MARKCHAIN mark_chains[13]; +#define PTR_CHAIN (ctx->mark_chains[0]) +#define TABLECELLBOUNDARIES (ctx->mark_chains[1]) +#define ASTERISK_OPENERS_extraword_mod3_0 (ctx->mark_chains[2]) +#define ASTERISK_OPENERS_extraword_mod3_1 (ctx->mark_chains[3]) +#define ASTERISK_OPENERS_extraword_mod3_2 (ctx->mark_chains[4]) +#define ASTERISK_OPENERS_intraword_mod3_0 (ctx->mark_chains[5]) +#define ASTERISK_OPENERS_intraword_mod3_1 (ctx->mark_chains[6]) +#define ASTERISK_OPENERS_intraword_mod3_2 (ctx->mark_chains[7]) +#define UNDERSCORE_OPENERS (ctx->mark_chains[8]) +#define TILDE_OPENERS_1 (ctx->mark_chains[9]) +#define TILDE_OPENERS_2 (ctx->mark_chains[10]) +#define BRACKET_OPENERS (ctx->mark_chains[11]) +#define DOLLAR_OPENERS (ctx->mark_chains[12]) +#define OPENERS_CHAIN_FIRST 1 +#define OPENERS_CHAIN_LAST 12 + + int n_table_cell_boundaries; + + /* For resolving links. */ + int unresolved_link_head; + int unresolved_link_tail; + + /* For resolving raw HTML. */ + OFF html_comment_horizon; + OFF html_proc_instr_horizon; + OFF html_decl_horizon; + OFF html_cdata_horizon; + + /* For block analysis. + * Notes: + * -- It holds MD_BLOCK as well as MD_LINE structures. After each + * MD_BLOCK, its (multiple) MD_LINE(s) follow. + * -- For MD_BLOCK_HTML and MD_BLOCK_CODE, MD_VERBATIMLINE(s) are used + * instead of MD_LINE(s). + */ + void* block_bytes; + MD_BLOCK* current_block; + int n_block_bytes; + int alloc_block_bytes; + + /* For container block analysis. */ + MD_CONTAINER* containers; + int n_containers; + int alloc_containers; + + /* Minimal indentation to call the block "indented code block". */ + unsigned code_indent_offset; + + /* Contextual info for line analysis. */ + SZ code_fence_length; /* For checking closing fence length. */ + int html_block_type; /* For checking closing raw HTML condition. */ + int last_line_has_list_loosening_effect; + int last_list_item_starts_with_two_blank_lines; +}; + +enum MD_LINETYPE_tag { + MD_LINE_BLANK, + MD_LINE_HR, + MD_LINE_ATXHEADER, + MD_LINE_SETEXTHEADER, + MD_LINE_SETEXTUNDERLINE, + MD_LINE_INDENTEDCODE, + MD_LINE_FENCEDCODE, + MD_LINE_HTML, + MD_LINE_TEXT, + MD_LINE_TABLE, + MD_LINE_TABLEUNDERLINE +}; +typedef enum MD_LINETYPE_tag MD_LINETYPE; + +typedef struct MD_LINE_ANALYSIS_tag MD_LINE_ANALYSIS; +struct MD_LINE_ANALYSIS_tag { + MD_LINETYPE type : 16; + unsigned data : 16; + OFF beg; + OFF end; + unsigned indent; /* Indentation level. */ +}; + +typedef struct MD_LINE_tag MD_LINE; +struct MD_LINE_tag { + OFF beg; + OFF end; +}; + +typedef struct MD_VERBATIMLINE_tag MD_VERBATIMLINE; +struct MD_VERBATIMLINE_tag { + OFF beg; + OFF end; + OFF indent; +}; + + +/***************** + *** Helpers *** + *****************/ + +/* Character accessors. */ +#define CH(off) (ctx->text[(off)]) +#define STR(off) (ctx->text + (off)) + +/* Character classification. + * Note we assume ASCII compatibility of code points < 128 here. */ +#define ISIN_(ch, ch_min, ch_max) ((ch_min) <= (unsigned)(ch) && (unsigned)(ch) <= (ch_max)) +#define ISANYOF_(ch, palette) ((ch) != _T('\0') && md_strchr((palette), (ch)) != NULL) +#define ISANYOF2_(ch, ch1, ch2) ((ch) == (ch1) || (ch) == (ch2)) +#define ISANYOF3_(ch, ch1, ch2, ch3) ((ch) == (ch1) || (ch) == (ch2) || (ch) == (ch3)) +#define ISASCII_(ch) ((unsigned)(ch) <= 127) +#define ISBLANK_(ch) (ISANYOF2_((ch), _T(' '), _T('\t'))) +#define ISNEWLINE_(ch) (ISANYOF2_((ch), _T('\r'), _T('\n'))) +#define ISWHITESPACE_(ch) (ISBLANK_(ch) || ISANYOF2_((ch), _T('\v'), _T('\f'))) +#define ISCNTRL_(ch) ((unsigned)(ch) <= 31 || (unsigned)(ch) == 127) +#define ISPUNCT_(ch) (ISIN_(ch, 33, 47) || ISIN_(ch, 58, 64) || ISIN_(ch, 91, 96) || ISIN_(ch, 123, 126)) +#define ISUPPER_(ch) (ISIN_(ch, _T('A'), _T('Z'))) +#define ISLOWER_(ch) (ISIN_(ch, _T('a'), _T('z'))) +#define ISALPHA_(ch) (ISUPPER_(ch) || ISLOWER_(ch)) +#define ISDIGIT_(ch) (ISIN_(ch, _T('0'), _T('9'))) +#define ISXDIGIT_(ch) (ISDIGIT_(ch) || ISIN_(ch, _T('A'), _T('F')) || ISIN_(ch, _T('a'), _T('f'))) +#define ISALNUM_(ch) (ISALPHA_(ch) || ISDIGIT_(ch)) + +#define ISANYOF(off, palette) ISANYOF_(CH(off), (palette)) +#define ISANYOF2(off, ch1, ch2) ISANYOF2_(CH(off), (ch1), (ch2)) +#define ISANYOF3(off, ch1, ch2, ch3) ISANYOF3_(CH(off), (ch1), (ch2), (ch3)) +#define ISASCII(off) ISASCII_(CH(off)) +#define ISBLANK(off) ISBLANK_(CH(off)) +#define ISNEWLINE(off) ISNEWLINE_(CH(off)) +#define ISWHITESPACE(off) ISWHITESPACE_(CH(off)) +#define ISCNTRL(off) ISCNTRL_(CH(off)) +#define ISPUNCT(off) ISPUNCT_(CH(off)) +#define ISUPPER(off) ISUPPER_(CH(off)) +#define ISLOWER(off) ISLOWER_(CH(off)) +#define ISALPHA(off) ISALPHA_(CH(off)) +#define ISDIGIT(off) ISDIGIT_(CH(off)) +#define ISXDIGIT(off) ISXDIGIT_(CH(off)) +#define ISALNUM(off) ISALNUM_(CH(off)) + + +#if defined MD4C_USE_UTF16 + #define md_strchr wcschr +#else + #define md_strchr strchr +#endif + + +/* Case insensitive check of string equality. */ +static inline int +md_ascii_case_eq(const CHAR* s1, const CHAR* s2, SZ n) +{ + OFF i; + for(i = 0; i < n; i++) { + CHAR ch1 = s1[i]; + CHAR ch2 = s2[i]; + + if(ISLOWER_(ch1)) + ch1 += ('A'-'a'); + if(ISLOWER_(ch2)) + ch2 += ('A'-'a'); + if(ch1 != ch2) + return FALSE; + } + return TRUE; +} + +static inline int +md_ascii_eq(const CHAR* s1, const CHAR* s2, SZ n) +{ + return memcmp(s1, s2, n * sizeof(CHAR)) == 0; +} + +static int +md_text_with_null_replacement(MD_CTX* ctx, MD_TEXTTYPE type, const CHAR* str, SZ size) +{ + OFF off = 0; + int ret = 0; + + while(1) { + while(off < size && str[off] != _T('\0')) + off++; + + if(off > 0) { + ret = ctx->parser.text(type, str, off, ctx->userdata); + if(ret != 0) + return ret; + + str += off; + size -= off; + off = 0; + } + + if(off >= size) + return 0; + + ret = ctx->parser.text(MD_TEXT_NULLCHAR, _T(""), 1, ctx->userdata); + if(ret != 0) + return ret; + off++; + } +} + + +#define MD_CHECK(func) \ + do { \ + ret = (func); \ + if(ret < 0) \ + goto abort; \ + } while(0) + + +#define MD_TEMP_BUFFER(sz) \ + do { \ + if(sz > ctx->alloc_buffer) { \ + CHAR* new_buffer; \ + SZ new_size = ((sz) + (sz) / 2 + 128) & ~127; \ + \ + new_buffer = realloc(ctx->buffer, new_size); \ + if(new_buffer == NULL) { \ + MD_LOG("realloc() failed."); \ + ret = -1; \ + goto abort; \ + } \ + \ + ctx->buffer = new_buffer; \ + ctx->alloc_buffer = new_size; \ + } \ + } while(0) + + +#define MD_ENTER_BLOCK(type, arg) \ + do { \ + ret = ctx->parser.enter_block((type), (arg), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from enter_block() callback."); \ + goto abort; \ + } \ + } while(0) + +#define MD_LEAVE_BLOCK(type, arg) \ + do { \ + ret = ctx->parser.leave_block((type), (arg), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from leave_block() callback."); \ + goto abort; \ + } \ + } while(0) + +#define MD_ENTER_SPAN(type, arg) \ + do { \ + ret = ctx->parser.enter_span((type), (arg), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from enter_span() callback."); \ + goto abort; \ + } \ + } while(0) + +#define MD_LEAVE_SPAN(type, arg) \ + do { \ + ret = ctx->parser.leave_span((type), (arg), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from leave_span() callback."); \ + goto abort; \ + } \ + } while(0) + +#define MD_TEXT(type, str, size) \ + do { \ + if(size > 0) { \ + ret = ctx->parser.text((type), (str), (size), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from text() callback."); \ + goto abort; \ + } \ + } \ + } while(0) + +#define MD_TEXT_INSECURE(type, str, size) \ + do { \ + if(size > 0) { \ + ret = md_text_with_null_replacement(ctx, type, str, size); \ + if(ret != 0) { \ + MD_LOG("Aborted from text() callback."); \ + goto abort; \ + } \ + } \ + } while(0) + + +/* If the offset falls into a gap between line, we return the following + * line. */ +static const MD_LINE* +md_lookup_line(OFF off, const MD_LINE* lines, int n_lines) +{ + int lo, hi; + int pivot; + const MD_LINE* line; + + lo = 0; + hi = n_lines - 1; + while(lo <= hi) { + pivot = (lo + hi) / 2; + line = &lines[pivot]; + + if(off < line->beg) { + hi = pivot - 1; + if(hi < 0 || lines[hi].end <= off) + return line; + } else if(off > line->end) { + lo = pivot + 1; + } else { + return line; + } + } + + return NULL; +} + + +/************************* + *** Unicode Support *** + *************************/ + +typedef struct MD_UNICODE_FOLD_INFO_tag MD_UNICODE_FOLD_INFO; +struct MD_UNICODE_FOLD_INFO_tag { + unsigned codepoints[3]; + unsigned n_codepoints; +}; + + +#if defined MD4C_USE_UTF16 || defined MD4C_USE_UTF8 + /* Binary search over sorted "map" of codepoints. Consecutive sequences + * of codepoints may be encoded in the map by just using the + * (MIN_CODEPOINT | 0x40000000) and (MAX_CODEPOINT | 0x80000000). + * + * Returns index of the found record in the map (in the case of ranges, + * the minimal value is used); or -1 on failure. */ + static int + md_unicode_bsearch__(unsigned codepoint, const unsigned* map, size_t map_size) + { + int beg, end; + int pivot_beg, pivot_end; + + beg = 0; + end = (int) map_size-1; + while(beg <= end) { + /* Pivot may be a range, not just a single value. */ + pivot_beg = pivot_end = (beg + end) / 2; + if(map[pivot_end] & 0x40000000) + pivot_end++; + if(map[pivot_beg] & 0x80000000) + pivot_beg--; + + if(codepoint < (map[pivot_beg] & 0x00ffffff)) + end = pivot_beg - 1; + else if(codepoint > (map[pivot_end] & 0x00ffffff)) + beg = pivot_end + 1; + else + return pivot_beg; + } + + return -1; + } + + static int + md_is_unicode_whitespace__(unsigned codepoint) + { +#define R(cp_min, cp_max) ((cp_min) | 0x40000000), ((cp_max) | 0x80000000) +#define S(cp) (cp) + /* Unicode "Zs" category. + * (generated by scripts/build_whitespace_map.py) */ + static const unsigned WHITESPACE_MAP[] = { + S(0x0020), S(0x00a0), S(0x1680), R(0x2000,0x200a), S(0x202f), S(0x205f), S(0x3000) + }; +#undef R +#undef S + + /* The ASCII ones are the most frequently used ones, also CommonMark + * specification requests few more in this range. */ + if(codepoint <= 0x7f) + return ISWHITESPACE_(codepoint); + + return (md_unicode_bsearch__(codepoint, WHITESPACE_MAP, SIZEOF_ARRAY(WHITESPACE_MAP)) >= 0); + } + + static int + md_is_unicode_punct__(unsigned codepoint) + { +#define R(cp_min, cp_max) ((cp_min) | 0x40000000), ((cp_max) | 0x80000000) +#define S(cp) (cp) + /* Unicode "Pc", "Pd", "Pe", "Pf", "Pi", "Po", "Ps" categories. + * (generated by scripts/build_punct_map.py) */ + static const unsigned PUNCT_MAP[] = { + R(0x0021,0x0023), R(0x0025,0x002a), R(0x002c,0x002f), R(0x003a,0x003b), R(0x003f,0x0040), + R(0x005b,0x005d), S(0x005f), S(0x007b), S(0x007d), S(0x00a1), S(0x00a7), S(0x00ab), R(0x00b6,0x00b7), + S(0x00bb), S(0x00bf), S(0x037e), S(0x0387), R(0x055a,0x055f), R(0x0589,0x058a), S(0x05be), S(0x05c0), + S(0x05c3), S(0x05c6), R(0x05f3,0x05f4), R(0x0609,0x060a), R(0x060c,0x060d), S(0x061b), R(0x061e,0x061f), + R(0x066a,0x066d), S(0x06d4), R(0x0700,0x070d), R(0x07f7,0x07f9), R(0x0830,0x083e), S(0x085e), + R(0x0964,0x0965), S(0x0970), S(0x09fd), S(0x0a76), S(0x0af0), S(0x0c77), S(0x0c84), S(0x0df4), S(0x0e4f), + R(0x0e5a,0x0e5b), R(0x0f04,0x0f12), S(0x0f14), R(0x0f3a,0x0f3d), S(0x0f85), R(0x0fd0,0x0fd4), + R(0x0fd9,0x0fda), R(0x104a,0x104f), S(0x10fb), R(0x1360,0x1368), S(0x1400), S(0x166e), R(0x169b,0x169c), + R(0x16eb,0x16ed), R(0x1735,0x1736), R(0x17d4,0x17d6), R(0x17d8,0x17da), R(0x1800,0x180a), + R(0x1944,0x1945), R(0x1a1e,0x1a1f), R(0x1aa0,0x1aa6), R(0x1aa8,0x1aad), R(0x1b5a,0x1b60), + R(0x1bfc,0x1bff), R(0x1c3b,0x1c3f), R(0x1c7e,0x1c7f), R(0x1cc0,0x1cc7), S(0x1cd3), R(0x2010,0x2027), + R(0x2030,0x2043), R(0x2045,0x2051), R(0x2053,0x205e), R(0x207d,0x207e), R(0x208d,0x208e), + R(0x2308,0x230b), R(0x2329,0x232a), R(0x2768,0x2775), R(0x27c5,0x27c6), R(0x27e6,0x27ef), + R(0x2983,0x2998), R(0x29d8,0x29db), R(0x29fc,0x29fd), R(0x2cf9,0x2cfc), R(0x2cfe,0x2cff), S(0x2d70), + R(0x2e00,0x2e2e), R(0x2e30,0x2e4f), S(0x2e52), R(0x3001,0x3003), R(0x3008,0x3011), R(0x3014,0x301f), + S(0x3030), S(0x303d), S(0x30a0), S(0x30fb), R(0xa4fe,0xa4ff), R(0xa60d,0xa60f), S(0xa673), S(0xa67e), + R(0xa6f2,0xa6f7), R(0xa874,0xa877), R(0xa8ce,0xa8cf), R(0xa8f8,0xa8fa), S(0xa8fc), R(0xa92e,0xa92f), + S(0xa95f), R(0xa9c1,0xa9cd), R(0xa9de,0xa9df), R(0xaa5c,0xaa5f), R(0xaade,0xaadf), R(0xaaf0,0xaaf1), + S(0xabeb), R(0xfd3e,0xfd3f), R(0xfe10,0xfe19), R(0xfe30,0xfe52), R(0xfe54,0xfe61), S(0xfe63), S(0xfe68), + R(0xfe6a,0xfe6b), R(0xff01,0xff03), R(0xff05,0xff0a), R(0xff0c,0xff0f), R(0xff1a,0xff1b), + R(0xff1f,0xff20), R(0xff3b,0xff3d), S(0xff3f), S(0xff5b), S(0xff5d), R(0xff5f,0xff65), R(0x10100,0x10102), + S(0x1039f), S(0x103d0), S(0x1056f), S(0x10857), S(0x1091f), S(0x1093f), R(0x10a50,0x10a58), S(0x10a7f), + R(0x10af0,0x10af6), R(0x10b39,0x10b3f), R(0x10b99,0x10b9c), S(0x10ead), R(0x10f55,0x10f59), + R(0x11047,0x1104d), R(0x110bb,0x110bc), R(0x110be,0x110c1), R(0x11140,0x11143), R(0x11174,0x11175), + R(0x111c5,0x111c8), S(0x111cd), S(0x111db), R(0x111dd,0x111df), R(0x11238,0x1123d), S(0x112a9), + R(0x1144b,0x1144f), R(0x1145a,0x1145b), S(0x1145d), S(0x114c6), R(0x115c1,0x115d7), R(0x11641,0x11643), + R(0x11660,0x1166c), R(0x1173c,0x1173e), S(0x1183b), R(0x11944,0x11946), S(0x119e2), R(0x11a3f,0x11a46), + R(0x11a9a,0x11a9c), R(0x11a9e,0x11aa2), R(0x11c41,0x11c45), R(0x11c70,0x11c71), R(0x11ef7,0x11ef8), + S(0x11fff), R(0x12470,0x12474), R(0x16a6e,0x16a6f), S(0x16af5), R(0x16b37,0x16b3b), S(0x16b44), + R(0x16e97,0x16e9a), S(0x16fe2), S(0x1bc9f), R(0x1da87,0x1da8b), R(0x1e95e,0x1e95f) + }; +#undef R +#undef S + + /* The ASCII ones are the most frequently used ones, also CommonMark + * specification requests few more in this range. */ + if(codepoint <= 0x7f) + return ISPUNCT_(codepoint); + + return (md_unicode_bsearch__(codepoint, PUNCT_MAP, SIZEOF_ARRAY(PUNCT_MAP)) >= 0); + } + + static void + md_get_unicode_fold_info(unsigned codepoint, MD_UNICODE_FOLD_INFO* info) + { +#define R(cp_min, cp_max) ((cp_min) | 0x40000000), ((cp_max) | 0x80000000) +#define S(cp) (cp) + /* Unicode "Pc", "Pd", "Pe", "Pf", "Pi", "Po", "Ps" categories. + * (generated by scripts/build_folding_map.py) */ + static const unsigned FOLD_MAP_1[] = { + R(0x0041,0x005a), S(0x00b5), R(0x00c0,0x00d6), R(0x00d8,0x00de), R(0x0100,0x012e), R(0x0132,0x0136), + R(0x0139,0x0147), R(0x014a,0x0176), S(0x0178), R(0x0179,0x017d), S(0x017f), S(0x0181), S(0x0182), + S(0x0184), S(0x0186), S(0x0187), S(0x0189), S(0x018a), S(0x018b), S(0x018e), S(0x018f), S(0x0190), + S(0x0191), S(0x0193), S(0x0194), S(0x0196), S(0x0197), S(0x0198), S(0x019c), S(0x019d), S(0x019f), + R(0x01a0,0x01a4), S(0x01a6), S(0x01a7), S(0x01a9), S(0x01ac), S(0x01ae), S(0x01af), S(0x01b1), S(0x01b2), + S(0x01b3), S(0x01b5), S(0x01b7), S(0x01b8), S(0x01bc), S(0x01c4), S(0x01c5), S(0x01c7), S(0x01c8), + S(0x01ca), R(0x01cb,0x01db), R(0x01de,0x01ee), S(0x01f1), S(0x01f2), S(0x01f4), S(0x01f6), S(0x01f7), + R(0x01f8,0x021e), S(0x0220), R(0x0222,0x0232), S(0x023a), S(0x023b), S(0x023d), S(0x023e), S(0x0241), + S(0x0243), S(0x0244), S(0x0245), R(0x0246,0x024e), S(0x0345), S(0x0370), S(0x0372), S(0x0376), S(0x037f), + S(0x0386), R(0x0388,0x038a), S(0x038c), S(0x038e), S(0x038f), R(0x0391,0x03a1), R(0x03a3,0x03ab), + S(0x03c2), S(0x03cf), S(0x03d0), S(0x03d1), S(0x03d5), S(0x03d6), R(0x03d8,0x03ee), S(0x03f0), S(0x03f1), + S(0x03f4), S(0x03f5), S(0x03f7), S(0x03f9), S(0x03fa), R(0x03fd,0x03ff), R(0x0400,0x040f), + R(0x0410,0x042f), R(0x0460,0x0480), R(0x048a,0x04be), S(0x04c0), R(0x04c1,0x04cd), R(0x04d0,0x052e), + R(0x0531,0x0556), R(0x10a0,0x10c5), S(0x10c7), S(0x10cd), R(0x13f8,0x13fd), S(0x1c80), S(0x1c81), + S(0x1c82), S(0x1c83), S(0x1c84), S(0x1c85), S(0x1c86), S(0x1c87), S(0x1c88), R(0x1c90,0x1cba), + R(0x1cbd,0x1cbf), R(0x1e00,0x1e94), S(0x1e9b), R(0x1ea0,0x1efe), R(0x1f08,0x1f0f), R(0x1f18,0x1f1d), + R(0x1f28,0x1f2f), R(0x1f38,0x1f3f), R(0x1f48,0x1f4d), S(0x1f59), S(0x1f5b), S(0x1f5d), S(0x1f5f), + R(0x1f68,0x1f6f), S(0x1fb8), S(0x1fb9), S(0x1fba), S(0x1fbb), S(0x1fbe), R(0x1fc8,0x1fcb), S(0x1fd8), + S(0x1fd9), S(0x1fda), S(0x1fdb), S(0x1fe8), S(0x1fe9), S(0x1fea), S(0x1feb), S(0x1fec), S(0x1ff8), + S(0x1ff9), S(0x1ffa), S(0x1ffb), S(0x2126), S(0x212a), S(0x212b), S(0x2132), R(0x2160,0x216f), S(0x2183), + R(0x24b6,0x24cf), R(0x2c00,0x2c2e), S(0x2c60), S(0x2c62), S(0x2c63), S(0x2c64), R(0x2c67,0x2c6b), + S(0x2c6d), S(0x2c6e), S(0x2c6f), S(0x2c70), S(0x2c72), S(0x2c75), S(0x2c7e), S(0x2c7f), R(0x2c80,0x2ce2), + S(0x2ceb), S(0x2ced), S(0x2cf2), R(0xa640,0xa66c), R(0xa680,0xa69a), R(0xa722,0xa72e), R(0xa732,0xa76e), + S(0xa779), S(0xa77b), S(0xa77d), R(0xa77e,0xa786), S(0xa78b), S(0xa78d), S(0xa790), S(0xa792), + R(0xa796,0xa7a8), S(0xa7aa), S(0xa7ab), S(0xa7ac), S(0xa7ad), S(0xa7ae), S(0xa7b0), S(0xa7b1), S(0xa7b2), + S(0xa7b3), R(0xa7b4,0xa7be), S(0xa7c2), S(0xa7c4), S(0xa7c5), S(0xa7c6), S(0xa7c7), S(0xa7c9), S(0xa7f5), + R(0xab70,0xabbf), R(0xff21,0xff3a), R(0x10400,0x10427), R(0x104b0,0x104d3), R(0x10c80,0x10cb2), + R(0x118a0,0x118bf), R(0x16e40,0x16e5f), R(0x1e900,0x1e921) + }; + static const unsigned FOLD_MAP_1_DATA[] = { + 0x0061, 0x007a, 0x03bc, 0x00e0, 0x00f6, 0x00f8, 0x00fe, 0x0101, 0x012f, 0x0133, 0x0137, 0x013a, 0x0148, + 0x014b, 0x0177, 0x00ff, 0x017a, 0x017e, 0x0073, 0x0253, 0x0183, 0x0185, 0x0254, 0x0188, 0x0256, 0x0257, + 0x018c, 0x01dd, 0x0259, 0x025b, 0x0192, 0x0260, 0x0263, 0x0269, 0x0268, 0x0199, 0x026f, 0x0272, 0x0275, + 0x01a1, 0x01a5, 0x0280, 0x01a8, 0x0283, 0x01ad, 0x0288, 0x01b0, 0x028a, 0x028b, 0x01b4, 0x01b6, 0x0292, + 0x01b9, 0x01bd, 0x01c6, 0x01c6, 0x01c9, 0x01c9, 0x01cc, 0x01cc, 0x01dc, 0x01df, 0x01ef, 0x01f3, 0x01f3, + 0x01f5, 0x0195, 0x01bf, 0x01f9, 0x021f, 0x019e, 0x0223, 0x0233, 0x2c65, 0x023c, 0x019a, 0x2c66, 0x0242, + 0x0180, 0x0289, 0x028c, 0x0247, 0x024f, 0x03b9, 0x0371, 0x0373, 0x0377, 0x03f3, 0x03ac, 0x03ad, 0x03af, + 0x03cc, 0x03cd, 0x03ce, 0x03b1, 0x03c1, 0x03c3, 0x03cb, 0x03c3, 0x03d7, 0x03b2, 0x03b8, 0x03c6, 0x03c0, + 0x03d9, 0x03ef, 0x03ba, 0x03c1, 0x03b8, 0x03b5, 0x03f8, 0x03f2, 0x03fb, 0x037b, 0x037d, 0x0450, 0x045f, + 0x0430, 0x044f, 0x0461, 0x0481, 0x048b, 0x04bf, 0x04cf, 0x04c2, 0x04ce, 0x04d1, 0x052f, 0x0561, 0x0586, + 0x2d00, 0x2d25, 0x2d27, 0x2d2d, 0x13f0, 0x13f5, 0x0432, 0x0434, 0x043e, 0x0441, 0x0442, 0x0442, 0x044a, + 0x0463, 0xa64b, 0x10d0, 0x10fa, 0x10fd, 0x10ff, 0x1e01, 0x1e95, 0x1e61, 0x1ea1, 0x1eff, 0x1f00, 0x1f07, + 0x1f10, 0x1f15, 0x1f20, 0x1f27, 0x1f30, 0x1f37, 0x1f40, 0x1f45, 0x1f51, 0x1f53, 0x1f55, 0x1f57, 0x1f60, + 0x1f67, 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x03b9, 0x1f72, 0x1f75, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fe0, + 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x03c9, 0x006b, 0x00e5, 0x214e, 0x2170, + 0x217f, 0x2184, 0x24d0, 0x24e9, 0x2c30, 0x2c5e, 0x2c61, 0x026b, 0x1d7d, 0x027d, 0x2c68, 0x2c6c, 0x0251, + 0x0271, 0x0250, 0x0252, 0x2c73, 0x2c76, 0x023f, 0x0240, 0x2c81, 0x2ce3, 0x2cec, 0x2cee, 0x2cf3, 0xa641, + 0xa66d, 0xa681, 0xa69b, 0xa723, 0xa72f, 0xa733, 0xa76f, 0xa77a, 0xa77c, 0x1d79, 0xa77f, 0xa787, 0xa78c, + 0x0265, 0xa791, 0xa793, 0xa797, 0xa7a9, 0x0266, 0x025c, 0x0261, 0x026c, 0x026a, 0x029e, 0x0287, 0x029d, + 0xab53, 0xa7b5, 0xa7bf, 0xa7c3, 0xa794, 0x0282, 0x1d8e, 0xa7c8, 0xa7ca, 0xa7f6, 0x13a0, 0x13ef, 0xff41, + 0xff5a, 0x10428, 0x1044f, 0x104d8, 0x104fb, 0x10cc0, 0x10cf2, 0x118c0, 0x118df, 0x16e60, 0x16e7f, 0x1e922, + 0x1e943 + }; + static const unsigned FOLD_MAP_2[] = { + S(0x00df), S(0x0130), S(0x0149), S(0x01f0), S(0x0587), S(0x1e96), S(0x1e97), S(0x1e98), S(0x1e99), + S(0x1e9a), S(0x1e9e), S(0x1f50), R(0x1f80,0x1f87), R(0x1f88,0x1f8f), R(0x1f90,0x1f97), R(0x1f98,0x1f9f), + R(0x1fa0,0x1fa7), R(0x1fa8,0x1faf), S(0x1fb2), S(0x1fb3), S(0x1fb4), S(0x1fb6), S(0x1fbc), S(0x1fc2), + S(0x1fc3), S(0x1fc4), S(0x1fc6), S(0x1fcc), S(0x1fd6), S(0x1fe4), S(0x1fe6), S(0x1ff2), S(0x1ff3), + S(0x1ff4), S(0x1ff6), S(0x1ffc), S(0xfb00), S(0xfb01), S(0xfb02), S(0xfb05), S(0xfb06), S(0xfb13), + S(0xfb14), S(0xfb15), S(0xfb16), S(0xfb17) + }; + static const unsigned FOLD_MAP_2_DATA[] = { + 0x0073,0x0073, 0x0069,0x0307, 0x02bc,0x006e, 0x006a,0x030c, 0x0565,0x0582, 0x0068,0x0331, 0x0074,0x0308, + 0x0077,0x030a, 0x0079,0x030a, 0x0061,0x02be, 0x0073,0x0073, 0x03c5,0x0313, 0x1f00,0x03b9, 0x1f07,0x03b9, + 0x1f00,0x03b9, 0x1f07,0x03b9, 0x1f20,0x03b9, 0x1f27,0x03b9, 0x1f20,0x03b9, 0x1f27,0x03b9, 0x1f60,0x03b9, + 0x1f67,0x03b9, 0x1f60,0x03b9, 0x1f67,0x03b9, 0x1f70,0x03b9, 0x03b1,0x03b9, 0x03ac,0x03b9, 0x03b1,0x0342, + 0x03b1,0x03b9, 0x1f74,0x03b9, 0x03b7,0x03b9, 0x03ae,0x03b9, 0x03b7,0x0342, 0x03b7,0x03b9, 0x03b9,0x0342, + 0x03c1,0x0313, 0x03c5,0x0342, 0x1f7c,0x03b9, 0x03c9,0x03b9, 0x03ce,0x03b9, 0x03c9,0x0342, 0x03c9,0x03b9, + 0x0066,0x0066, 0x0066,0x0069, 0x0066,0x006c, 0x0073,0x0074, 0x0073,0x0074, 0x0574,0x0576, 0x0574,0x0565, + 0x0574,0x056b, 0x057e,0x0576, 0x0574,0x056d + }; + static const unsigned FOLD_MAP_3[] = { + S(0x0390), S(0x03b0), S(0x1f52), S(0x1f54), S(0x1f56), S(0x1fb7), S(0x1fc7), S(0x1fd2), S(0x1fd3), + S(0x1fd7), S(0x1fe2), S(0x1fe3), S(0x1fe7), S(0x1ff7), S(0xfb03), S(0xfb04) + }; + static const unsigned FOLD_MAP_3_DATA[] = { + 0x03b9,0x0308,0x0301, 0x03c5,0x0308,0x0301, 0x03c5,0x0313,0x0300, 0x03c5,0x0313,0x0301, + 0x03c5,0x0313,0x0342, 0x03b1,0x0342,0x03b9, 0x03b7,0x0342,0x03b9, 0x03b9,0x0308,0x0300, + 0x03b9,0x0308,0x0301, 0x03b9,0x0308,0x0342, 0x03c5,0x0308,0x0300, 0x03c5,0x0308,0x0301, + 0x03c5,0x0308,0x0342, 0x03c9,0x0342,0x03b9, 0x0066,0x0066,0x0069, 0x0066,0x0066,0x006c + }; +#undef R +#undef S + static const struct { + const unsigned* map; + const unsigned* data; + size_t map_size; + unsigned n_codepoints; + } FOLD_MAP_LIST[] = { + { FOLD_MAP_1, FOLD_MAP_1_DATA, SIZEOF_ARRAY(FOLD_MAP_1), 1 }, + { FOLD_MAP_2, FOLD_MAP_2_DATA, SIZEOF_ARRAY(FOLD_MAP_2), 2 }, + { FOLD_MAP_3, FOLD_MAP_3_DATA, SIZEOF_ARRAY(FOLD_MAP_3), 3 } + }; + + int i; + + /* Fast path for ASCII characters. */ + if(codepoint <= 0x7f) { + info->codepoints[0] = codepoint; + if(ISUPPER_(codepoint)) + info->codepoints[0] += 'a' - 'A'; + info->n_codepoints = 1; + return; + } + + /* Try to locate the codepoint in any of the maps. */ + for(i = 0; i < (int) SIZEOF_ARRAY(FOLD_MAP_LIST); i++) { + int index; + + index = md_unicode_bsearch__(codepoint, FOLD_MAP_LIST[i].map, FOLD_MAP_LIST[i].map_size); + if(index >= 0) { + /* Found the mapping. */ + unsigned n_codepoints = FOLD_MAP_LIST[i].n_codepoints; + const unsigned* map = FOLD_MAP_LIST[i].map; + const unsigned* codepoints = FOLD_MAP_LIST[i].data + (index * n_codepoints); + + memcpy(info->codepoints, codepoints, sizeof(unsigned) * n_codepoints); + info->n_codepoints = n_codepoints; + + if(FOLD_MAP_LIST[i].map[index] != codepoint) { + /* The found mapping maps whole range of codepoints, + * i.e. we have to offset info->codepoints[0] accordingly. */ + if((map[index] & 0x00ffffff)+1 == codepoints[0]) { + /* Alternating type of the range. */ + info->codepoints[0] = codepoint + ((codepoint & 0x1) == (map[index] & 0x1) ? 1 : 0); + } else { + /* Range to range kind of mapping. */ + info->codepoints[0] += (codepoint - (map[index] & 0x00ffffff)); + } + } + + return; + } + } + + /* No mapping found. Map the codepoint to itself. */ + info->codepoints[0] = codepoint; + info->n_codepoints = 1; + } +#endif + + +#if defined MD4C_USE_UTF16 + #define IS_UTF16_SURROGATE_HI(word) (((WORD)(word) & 0xfc00) == 0xd800) + #define IS_UTF16_SURROGATE_LO(word) (((WORD)(word) & 0xfc00) == 0xdc00) + #define UTF16_DECODE_SURROGATE(hi, lo) (0x10000 + ((((unsigned)(hi) & 0x3ff) << 10) | (((unsigned)(lo) & 0x3ff) << 0))) + + static unsigned + md_decode_utf16le__(const CHAR* str, SZ str_size, SZ* p_size) + { + if(IS_UTF16_SURROGATE_HI(str[0])) { + if(1 < str_size && IS_UTF16_SURROGATE_LO(str[1])) { + if(p_size != NULL) + *p_size = 2; + return UTF16_DECODE_SURROGATE(str[0], str[1]); + } + } + + if(p_size != NULL) + *p_size = 1; + return str[0]; + } + + static unsigned + md_decode_utf16le_before__(MD_CTX* ctx, OFF off) + { + if(off > 2 && IS_UTF16_SURROGATE_HI(CH(off-2)) && IS_UTF16_SURROGATE_LO(CH(off-1))) + return UTF16_DECODE_SURROGATE(CH(off-2), CH(off-1)); + + return CH(off); + } + + /* No whitespace uses surrogates, so no decoding needed here. */ + #define ISUNICODEWHITESPACE_(codepoint) md_is_unicode_whitespace__(codepoint) + #define ISUNICODEWHITESPACE(off) md_is_unicode_whitespace__(CH(off)) + #define ISUNICODEWHITESPACEBEFORE(off) md_is_unicode_whitespace__(CH((off)-1)) + + #define ISUNICODEPUNCT(off) md_is_unicode_punct__(md_decode_utf16le__(STR(off), ctx->size - (off), NULL)) + #define ISUNICODEPUNCTBEFORE(off) md_is_unicode_punct__(md_decode_utf16le_before__(ctx, off)) + + static inline int + md_decode_unicode(const CHAR* str, OFF off, SZ str_size, SZ* p_char_size) + { + return md_decode_utf16le__(str+off, str_size-off, p_char_size); + } +#elif defined MD4C_USE_UTF8 + #define IS_UTF8_LEAD1(byte) ((unsigned char)(byte) <= 0x7f) + #define IS_UTF8_LEAD2(byte) (((unsigned char)(byte) & 0xe0) == 0xc0) + #define IS_UTF8_LEAD3(byte) (((unsigned char)(byte) & 0xf0) == 0xe0) + #define IS_UTF8_LEAD4(byte) (((unsigned char)(byte) & 0xf8) == 0xf0) + #define IS_UTF8_TAIL(byte) (((unsigned char)(byte) & 0xc0) == 0x80) + + static unsigned + md_decode_utf8__(const CHAR* str, SZ str_size, SZ* p_size) + { + if(!IS_UTF8_LEAD1(str[0])) { + if(IS_UTF8_LEAD2(str[0])) { + if(1 < str_size && IS_UTF8_TAIL(str[1])) { + if(p_size != NULL) + *p_size = 2; + + return (((unsigned int)str[0] & 0x1f) << 6) | + (((unsigned int)str[1] & 0x3f) << 0); + } + } else if(IS_UTF8_LEAD3(str[0])) { + if(2 < str_size && IS_UTF8_TAIL(str[1]) && IS_UTF8_TAIL(str[2])) { + if(p_size != NULL) + *p_size = 3; + + return (((unsigned int)str[0] & 0x0f) << 12) | + (((unsigned int)str[1] & 0x3f) << 6) | + (((unsigned int)str[2] & 0x3f) << 0); + } + } else if(IS_UTF8_LEAD4(str[0])) { + if(3 < str_size && IS_UTF8_TAIL(str[1]) && IS_UTF8_TAIL(str[2]) && IS_UTF8_TAIL(str[3])) { + if(p_size != NULL) + *p_size = 4; + + return (((unsigned int)str[0] & 0x07) << 18) | + (((unsigned int)str[1] & 0x3f) << 12) | + (((unsigned int)str[2] & 0x3f) << 6) | + (((unsigned int)str[3] & 0x3f) << 0); + } + } + } + + if(p_size != NULL) + *p_size = 1; + return (unsigned) str[0]; + } + + static unsigned + md_decode_utf8_before__(MD_CTX* ctx, OFF off) + { + if(!IS_UTF8_LEAD1(CH(off-1))) { + if(off > 1 && IS_UTF8_LEAD2(CH(off-2)) && IS_UTF8_TAIL(CH(off-1))) + return (((unsigned int)CH(off-2) & 0x1f) << 6) | + (((unsigned int)CH(off-1) & 0x3f) << 0); + + if(off > 2 && IS_UTF8_LEAD3(CH(off-3)) && IS_UTF8_TAIL(CH(off-2)) && IS_UTF8_TAIL(CH(off-1))) + return (((unsigned int)CH(off-3) & 0x0f) << 12) | + (((unsigned int)CH(off-2) & 0x3f) << 6) | + (((unsigned int)CH(off-1) & 0x3f) << 0); + + if(off > 3 && IS_UTF8_LEAD4(CH(off-4)) && IS_UTF8_TAIL(CH(off-3)) && IS_UTF8_TAIL(CH(off-2)) && IS_UTF8_TAIL(CH(off-1))) + return (((unsigned int)CH(off-4) & 0x07) << 18) | + (((unsigned int)CH(off-3) & 0x3f) << 12) | + (((unsigned int)CH(off-2) & 0x3f) << 6) | + (((unsigned int)CH(off-1) & 0x3f) << 0); + } + + return (unsigned) CH(off-1); + } + + #define ISUNICODEWHITESPACE_(codepoint) md_is_unicode_whitespace__(codepoint) + #define ISUNICODEWHITESPACE(off) md_is_unicode_whitespace__(md_decode_utf8__(STR(off), ctx->size - (off), NULL)) + #define ISUNICODEWHITESPACEBEFORE(off) md_is_unicode_whitespace__(md_decode_utf8_before__(ctx, off)) + + #define ISUNICODEPUNCT(off) md_is_unicode_punct__(md_decode_utf8__(STR(off), ctx->size - (off), NULL)) + #define ISUNICODEPUNCTBEFORE(off) md_is_unicode_punct__(md_decode_utf8_before__(ctx, off)) + + static inline unsigned + md_decode_unicode(const CHAR* str, OFF off, SZ str_size, SZ* p_char_size) + { + return md_decode_utf8__(str+off, str_size-off, p_char_size); + } +#else + #define ISUNICODEWHITESPACE_(codepoint) ISWHITESPACE_(codepoint) + #define ISUNICODEWHITESPACE(off) ISWHITESPACE(off) + #define ISUNICODEWHITESPACEBEFORE(off) ISWHITESPACE((off)-1) + + #define ISUNICODEPUNCT(off) ISPUNCT(off) + #define ISUNICODEPUNCTBEFORE(off) ISPUNCT((off)-1) + + static inline void + md_get_unicode_fold_info(unsigned codepoint, MD_UNICODE_FOLD_INFO* info) + { + info->codepoints[0] = codepoint; + if(ISUPPER_(codepoint)) + info->codepoints[0] += 'a' - 'A'; + info->n_codepoints = 1; + } + + static inline unsigned + md_decode_unicode(const CHAR* str, OFF off, SZ str_size, SZ* p_size) + { + *p_size = 1; + return (unsigned) str[off]; + } +#endif + + +/************************************* + *** Helper string manipulations *** + *************************************/ + +/* Fill buffer with copy of the string between 'beg' and 'end' but replace any + * line breaks with given replacement character. + * + * NOTE: Caller is responsible to make sure the buffer is large enough. + * (Given the output is always shorter then input, (end - beg) is good idea + * what the caller should allocate.) + */ +static void +md_merge_lines(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, int n_lines, + CHAR line_break_replacement_char, CHAR* buffer, SZ* p_size) +{ + CHAR* ptr = buffer; + int line_index = 0; + OFF off = beg; + + MD_UNUSED(n_lines); + + while(1) { + const MD_LINE* line = &lines[line_index]; + OFF line_end = line->end; + if(end < line_end) + line_end = end; + + while(off < line_end) { + *ptr = CH(off); + ptr++; + off++; + } + + if(off >= end) { + *p_size = (MD_SIZE)(ptr - buffer); + return; + } + + *ptr = line_break_replacement_char; + ptr++; + + line_index++; + off = lines[line_index].beg; + } +} + +/* Wrapper of md_merge_lines() which allocates new buffer for the output string. + */ +static int +md_merge_lines_alloc(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, int n_lines, + CHAR line_break_replacement_char, CHAR** p_str, SZ* p_size) +{ + CHAR* buffer; + + buffer = (CHAR*) malloc(sizeof(CHAR) * (end - beg)); + if(buffer == NULL) { + MD_LOG("malloc() failed."); + return -1; + } + + md_merge_lines(ctx, beg, end, lines, n_lines, + line_break_replacement_char, buffer, p_size); + + *p_str = buffer; + return 0; +} + +static OFF +md_skip_unicode_whitespace(const CHAR* label, OFF off, SZ size) +{ + SZ char_size; + unsigned codepoint; + + while(off < size) { + codepoint = md_decode_unicode(label, off, size, &char_size); + if(!ISUNICODEWHITESPACE_(codepoint) && !ISNEWLINE_(label[off])) + break; + off += char_size; + } + + return off; +} + + +/****************************** + *** Recognizing raw HTML *** + ******************************/ + +/* md_is_html_tag() may be called when processing inlines (inline raw HTML) + * or when breaking document to blocks (checking for start of HTML block type 7). + * + * When breaking document to blocks, we do not yet know line boundaries, but + * in that case the whole tag has to live on a single line. We distinguish this + * by n_lines == 0. + */ +static int +md_is_html_tag(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +{ + int attr_state; + OFF off = beg; + OFF line_end = (n_lines > 0) ? lines[0].end : ctx->size; + int i = 0; + + MD_ASSERT(CH(beg) == _T('<')); + + if(off + 1 >= line_end) + return FALSE; + off++; + + /* For parsing attributes, we need a little state automaton below. + * State -1: no attributes are allowed. + * State 0: attribute could follow after some whitespace. + * State 1: after a whitespace (attribute name may follow). + * State 2: after attribute name ('=' MAY follow). + * State 3: after '=' (value specification MUST follow). + * State 41: in middle of unquoted attribute value. + * State 42: in middle of single-quoted attribute value. + * State 43: in middle of double-quoted attribute value. + */ + attr_state = 0; + + if(CH(off) == _T('/')) { + /* Closer tag "". No attributes may be present. */ + attr_state = -1; + off++; + } + + /* Tag name */ + if(off >= line_end || !ISALPHA(off)) + return FALSE; + off++; + while(off < line_end && (ISALNUM(off) || CH(off) == _T('-'))) + off++; + + /* (Optional) attributes (if not closer), (optional) '/' (if not closer) + * and final '>'. */ + while(1) { + while(off < line_end && !ISNEWLINE(off)) { + if(attr_state > 40) { + if(attr_state == 41 && (ISBLANK(off) || ISANYOF(off, _T("\"'=<>`")))) { + attr_state = 0; + off--; /* Put the char back for re-inspection in the new state. */ + } else if(attr_state == 42 && CH(off) == _T('\'')) { + attr_state = 0; + } else if(attr_state == 43 && CH(off) == _T('"')) { + attr_state = 0; + } + off++; + } else if(ISWHITESPACE(off)) { + if(attr_state == 0) + attr_state = 1; + off++; + } else if(attr_state <= 2 && CH(off) == _T('>')) { + /* End. */ + goto done; + } else if(attr_state <= 2 && CH(off) == _T('/') && off+1 < line_end && CH(off+1) == _T('>')) { + /* End with digraph '/>' */ + off++; + goto done; + } else if((attr_state == 1 || attr_state == 2) && (ISALPHA(off) || CH(off) == _T('_') || CH(off) == _T(':'))) { + off++; + /* Attribute name */ + while(off < line_end && (ISALNUM(off) || ISANYOF(off, _T("_.:-")))) + off++; + attr_state = 2; + } else if(attr_state == 2 && CH(off) == _T('=')) { + /* Attribute assignment sign */ + off++; + attr_state = 3; + } else if(attr_state == 3) { + /* Expecting start of attribute value. */ + if(CH(off) == _T('"')) + attr_state = 43; + else if(CH(off) == _T('\'')) + attr_state = 42; + else if(!ISANYOF(off, _T("\"'=<>`")) && !ISNEWLINE(off)) + attr_state = 41; + else + return FALSE; + off++; + } else { + /* Anything unexpected. */ + return FALSE; + } + } + + /* We have to be on a single line. See definition of start condition + * of HTML block, type 7. */ + if(n_lines == 0) + return FALSE; + + i++; + if(i >= n_lines) + return FALSE; + + off = lines[i].beg; + line_end = lines[i].end; + + if(attr_state == 0 || attr_state == 41) + attr_state = 1; + + if(off >= max_end) + return FALSE; + } + +done: + if(off >= max_end) + return FALSE; + + *p_end = off+1; + return TRUE; +} + +static int +md_scan_for_html_closer(MD_CTX* ctx, const MD_CHAR* str, MD_SIZE len, + const MD_LINE* lines, int n_lines, + OFF beg, OFF max_end, OFF* p_end, + OFF* p_scan_horizon) +{ + OFF off = beg; + int i = 0; + + if(off < *p_scan_horizon && *p_scan_horizon >= max_end - len) { + /* We have already scanned the range up to the max_end so we know + * there is nothing to see. */ + return FALSE; + } + + while(TRUE) { + while(off + len <= lines[i].end && off + len <= max_end) { + if(md_ascii_eq(STR(off), str, len)) { + /* Success. */ + *p_end = off + len; + return TRUE; + } + off++; + } + + i++; + if(off >= max_end || i >= n_lines) { + /* Failure. */ + *p_scan_horizon = off; + return FALSE; + } + + off = lines[i].beg; + } +} + +static int +md_is_html_comment(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +{ + OFF off = beg; + + MD_ASSERT(CH(beg) == _T('<')); + + if(off + 4 >= lines[0].end) + return FALSE; + if(CH(off+1) != _T('!') || CH(off+2) != _T('-') || CH(off+3) != _T('-')) + return FALSE; + off += 4; + + /* ">" and "->" must not follow the opening. */ + if(off < lines[0].end && CH(off) == _T('>')) + return FALSE; + if(off+1 < lines[0].end && CH(off) == _T('-') && CH(off+1) == _T('>')) + return FALSE; + + /* HTML comment must not contain "--", so we scan just for "--" instead + * of "-->" and verify manually that '>' follows. */ + if(md_scan_for_html_closer(ctx, _T("--"), 2, + lines, n_lines, off, max_end, p_end, &ctx->html_comment_horizon)) + { + if(*p_end < max_end && CH(*p_end) == _T('>')) { + *p_end = *p_end + 1; + return TRUE; + } + } + + return FALSE; +} + +static int +md_is_html_processing_instruction(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +{ + OFF off = beg; + + if(off + 2 >= lines[0].end) + return FALSE; + if(CH(off+1) != _T('?')) + return FALSE; + off += 2; + + return md_scan_for_html_closer(ctx, _T("?>"), 2, + lines, n_lines, off, max_end, p_end, &ctx->html_proc_instr_horizon); +} + +static int +md_is_html_declaration(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +{ + OFF off = beg; + + if(off + 2 >= lines[0].end) + return FALSE; + if(CH(off+1) != _T('!')) + return FALSE; + off += 2; + + /* Declaration name. */ + if(off >= lines[0].end || !ISALPHA(off)) + return FALSE; + off++; + while(off < lines[0].end && ISALPHA(off)) + off++; + if(off < lines[0].end && !ISWHITESPACE(off)) + return FALSE; + + return md_scan_for_html_closer(ctx, _T(">"), 1, + lines, n_lines, off, max_end, p_end, &ctx->html_decl_horizon); +} + +static int +md_is_html_cdata(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +{ + static const CHAR open_str[] = _T("= lines[0].end) + return FALSE; + if(memcmp(STR(off), open_str, open_size) != 0) + return FALSE; + off += open_size; + + if(lines[n_lines-1].end < max_end) + max_end = lines[n_lines-1].end - 2; + + return md_scan_for_html_closer(ctx, _T("]]>"), 3, + lines, n_lines, off, max_end, p_end, &ctx->html_cdata_horizon); +} + +static int +md_is_html_any(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +{ + MD_ASSERT(CH(beg) == _T('<')); + return (md_is_html_tag(ctx, lines, n_lines, beg, max_end, p_end) || + md_is_html_comment(ctx, lines, n_lines, beg, max_end, p_end) || + md_is_html_processing_instruction(ctx, lines, n_lines, beg, max_end, p_end) || + md_is_html_declaration(ctx, lines, n_lines, beg, max_end, p_end) || + md_is_html_cdata(ctx, lines, n_lines, beg, max_end, p_end)); +} + + +/**************************** + *** Recognizing Entity *** + ****************************/ + +static int +md_is_hex_entity_contents(MD_CTX* ctx, const CHAR* text, OFF beg, OFF max_end, OFF* p_end) +{ + OFF off = beg; + MD_UNUSED(ctx); + + while(off < max_end && ISXDIGIT_(text[off]) && off - beg <= 8) + off++; + + if(1 <= off - beg && off - beg <= 6) { + *p_end = off; + return TRUE; + } else { + return FALSE; + } +} + +static int +md_is_dec_entity_contents(MD_CTX* ctx, const CHAR* text, OFF beg, OFF max_end, OFF* p_end) +{ + OFF off = beg; + MD_UNUSED(ctx); + + while(off < max_end && ISDIGIT_(text[off]) && off - beg <= 8) + off++; + + if(1 <= off - beg && off - beg <= 7) { + *p_end = off; + return TRUE; + } else { + return FALSE; + } +} + +static int +md_is_named_entity_contents(MD_CTX* ctx, const CHAR* text, OFF beg, OFF max_end, OFF* p_end) +{ + OFF off = beg; + MD_UNUSED(ctx); + + if(off < max_end && ISALPHA_(text[off])) + off++; + else + return FALSE; + + while(off < max_end && ISALNUM_(text[off]) && off - beg <= 48) + off++; + + if(2 <= off - beg && off - beg <= 48) { + *p_end = off; + return TRUE; + } else { + return FALSE; + } +} + +static int +md_is_entity_str(MD_CTX* ctx, const CHAR* text, OFF beg, OFF max_end, OFF* p_end) +{ + int is_contents; + OFF off = beg; + + MD_ASSERT(text[off] == _T('&')); + off++; + + if(off+2 < max_end && text[off] == _T('#') && (text[off+1] == _T('x') || text[off+1] == _T('X'))) + is_contents = md_is_hex_entity_contents(ctx, text, off+2, max_end, &off); + else if(off+1 < max_end && text[off] == _T('#')) + is_contents = md_is_dec_entity_contents(ctx, text, off+1, max_end, &off); + else + is_contents = md_is_named_entity_contents(ctx, text, off, max_end, &off); + + if(is_contents && off < max_end && text[off] == _T(';')) { + *p_end = off+1; + return TRUE; + } else { + return FALSE; + } +} + +static inline int +md_is_entity(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end) +{ + return md_is_entity_str(ctx, ctx->text, beg, max_end, p_end); +} + + +/****************************** + *** Attribute Management *** + ******************************/ + +typedef struct MD_ATTRIBUTE_BUILD_tag MD_ATTRIBUTE_BUILD; +struct MD_ATTRIBUTE_BUILD_tag { + CHAR* text; + MD_TEXTTYPE* substr_types; + OFF* substr_offsets; + int substr_count; + int substr_alloc; + MD_TEXTTYPE trivial_types[1]; + OFF trivial_offsets[2]; +}; + + +#define MD_BUILD_ATTR_NO_ESCAPES 0x0001 + +static int +md_build_attr_append_substr(MD_CTX* ctx, MD_ATTRIBUTE_BUILD* build, + MD_TEXTTYPE type, OFF off) +{ + if(build->substr_count >= build->substr_alloc) { + MD_TEXTTYPE* new_substr_types; + OFF* new_substr_offsets; + + build->substr_alloc = (build->substr_alloc > 0 + ? build->substr_alloc + build->substr_alloc / 2 + : 8); + new_substr_types = (MD_TEXTTYPE*) realloc(build->substr_types, + build->substr_alloc * sizeof(MD_TEXTTYPE)); + if(new_substr_types == NULL) { + MD_LOG("realloc() failed."); + return -1; + } + /* Note +1 to reserve space for final offset (== raw_size). */ + new_substr_offsets = (OFF*) realloc(build->substr_offsets, + (build->substr_alloc+1) * sizeof(OFF)); + if(new_substr_offsets == NULL) { + MD_LOG("realloc() failed."); + free(new_substr_types); + return -1; + } + + build->substr_types = new_substr_types; + build->substr_offsets = new_substr_offsets; + } + + build->substr_types[build->substr_count] = type; + build->substr_offsets[build->substr_count] = off; + build->substr_count++; + return 0; +} + +static void +md_free_attribute(MD_CTX* ctx, MD_ATTRIBUTE_BUILD* build) +{ + MD_UNUSED(ctx); + + if(build->substr_alloc > 0) { + free(build->text); + free(build->substr_types); + free(build->substr_offsets); + } +} + +static int +md_build_attribute(MD_CTX* ctx, const CHAR* raw_text, SZ raw_size, + unsigned flags, MD_ATTRIBUTE* attr, MD_ATTRIBUTE_BUILD* build) +{ + OFF raw_off, off; + int is_trivial; + int ret = 0; + + memset(build, 0, sizeof(MD_ATTRIBUTE_BUILD)); + + /* If there is no backslash and no ampersand, build trivial attribute + * without any malloc(). */ + is_trivial = TRUE; + for(raw_off = 0; raw_off < raw_size; raw_off++) { + if(ISANYOF3_(raw_text[raw_off], _T('\\'), _T('&'), _T('\0'))) { + is_trivial = FALSE; + break; + } + } + + if(is_trivial) { + build->text = (CHAR*) (raw_size ? raw_text : NULL); + build->substr_types = build->trivial_types; + build->substr_offsets = build->trivial_offsets; + build->substr_count = 1; + build->substr_alloc = 0; + build->trivial_types[0] = MD_TEXT_NORMAL; + build->trivial_offsets[0] = 0; + build->trivial_offsets[1] = raw_size; + off = raw_size; + } else { + build->text = (CHAR*) malloc(raw_size * sizeof(CHAR)); + if(build->text == NULL) { + MD_LOG("malloc() failed."); + goto abort; + } + + raw_off = 0; + off = 0; + + while(raw_off < raw_size) { + if(raw_text[raw_off] == _T('\0')) { + MD_CHECK(md_build_attr_append_substr(ctx, build, MD_TEXT_NULLCHAR, off)); + memcpy(build->text + off, raw_text + raw_off, 1); + off++; + raw_off++; + continue; + } + + if(raw_text[raw_off] == _T('&')) { + OFF ent_end; + + if(md_is_entity_str(ctx, raw_text, raw_off, raw_size, &ent_end)) { + MD_CHECK(md_build_attr_append_substr(ctx, build, MD_TEXT_ENTITY, off)); + memcpy(build->text + off, raw_text + raw_off, ent_end - raw_off); + off += ent_end - raw_off; + raw_off = ent_end; + continue; + } + } + + if(build->substr_count == 0 || build->substr_types[build->substr_count-1] != MD_TEXT_NORMAL) + MD_CHECK(md_build_attr_append_substr(ctx, build, MD_TEXT_NORMAL, off)); + + if(!(flags & MD_BUILD_ATTR_NO_ESCAPES) && + raw_text[raw_off] == _T('\\') && raw_off+1 < raw_size && + (ISPUNCT_(raw_text[raw_off+1]) || ISNEWLINE_(raw_text[raw_off+1]))) + raw_off++; + + build->text[off++] = raw_text[raw_off++]; + } + build->substr_offsets[build->substr_count] = off; + } + + attr->text = build->text; + attr->size = off; + attr->substr_offsets = build->substr_offsets; + attr->substr_types = build->substr_types; + return 0; + +abort: + md_free_attribute(ctx, build); + return -1; +} + + +/********************************************* + *** Dictionary of Reference Definitions *** + *********************************************/ + +#define MD_FNV1A_BASE 2166136261U +#define MD_FNV1A_PRIME 16777619U + +static inline unsigned +md_fnv1a(unsigned base, const void* data, size_t n) +{ + const unsigned char* buf = (const unsigned char*) data; + unsigned hash = base; + size_t i; + + for(i = 0; i < n; i++) { + hash ^= buf[i]; + hash *= MD_FNV1A_PRIME; + } + + return hash; +} + + +struct MD_REF_DEF_tag { + CHAR* label; + CHAR* title; + unsigned hash; + SZ label_size; + SZ title_size; + OFF dest_beg; + OFF dest_end; + unsigned char label_needs_free : 1; + unsigned char title_needs_free : 1; +}; + +/* Label equivalence is quite complicated with regards to whitespace and case + * folding. This complicates computing a hash of it as well as direct comparison + * of two labels. */ + +static unsigned +md_link_label_hash(const CHAR* label, SZ size) +{ + unsigned hash = MD_FNV1A_BASE; + OFF off; + unsigned codepoint; + int is_whitespace = FALSE; + + off = md_skip_unicode_whitespace(label, 0, size); + while(off < size) { + SZ char_size; + + codepoint = md_decode_unicode(label, off, size, &char_size); + is_whitespace = ISUNICODEWHITESPACE_(codepoint) || ISNEWLINE_(label[off]); + + if(is_whitespace) { + codepoint = ' '; + hash = md_fnv1a(hash, &codepoint, sizeof(unsigned)); + off = md_skip_unicode_whitespace(label, off, size); + } else { + MD_UNICODE_FOLD_INFO fold_info; + + md_get_unicode_fold_info(codepoint, &fold_info); + hash = md_fnv1a(hash, fold_info.codepoints, fold_info.n_codepoints * sizeof(unsigned)); + off += char_size; + } + } + + return hash; +} + +static OFF +md_link_label_cmp_load_fold_info(const CHAR* label, OFF off, SZ size, + MD_UNICODE_FOLD_INFO* fold_info) +{ + unsigned codepoint; + SZ char_size; + + if(off >= size) { + /* Treat end of a link label as a whitespace. */ + goto whitespace; + } + + codepoint = md_decode_unicode(label, off, size, &char_size); + off += char_size; + if(ISUNICODEWHITESPACE_(codepoint)) { + /* Treat all whitespace as equivalent */ + goto whitespace; + } + + /* Get real folding info. */ + md_get_unicode_fold_info(codepoint, fold_info); + return off; + +whitespace: + fold_info->codepoints[0] = _T(' '); + fold_info->n_codepoints = 1; + return md_skip_unicode_whitespace(label, off, size); +} + +static int +md_link_label_cmp(const CHAR* a_label, SZ a_size, const CHAR* b_label, SZ b_size) +{ + OFF a_off; + OFF b_off; + MD_UNICODE_FOLD_INFO a_fi = { { 0 }, 0 }; + MD_UNICODE_FOLD_INFO b_fi = { { 0 }, 0 }; + OFF a_fi_off = 0; + OFF b_fi_off = 0; + int cmp; + + a_off = md_skip_unicode_whitespace(a_label, 0, a_size); + b_off = md_skip_unicode_whitespace(b_label, 0, b_size); + while(a_off < a_size || a_fi_off < a_fi.n_codepoints || + b_off < b_size || b_fi_off < b_fi.n_codepoints) + { + /* If needed, load fold info for next char. */ + if(a_fi_off >= a_fi.n_codepoints) { + a_fi_off = 0; + a_off = md_link_label_cmp_load_fold_info(a_label, a_off, a_size, &a_fi); + } + if(b_fi_off >= b_fi.n_codepoints) { + b_fi_off = 0; + b_off = md_link_label_cmp_load_fold_info(b_label, b_off, b_size, &b_fi); + } + + cmp = b_fi.codepoints[b_fi_off] - a_fi.codepoints[a_fi_off]; + if(cmp != 0) + return cmp; + + a_fi_off++; + b_fi_off++; + } + + return 0; +} + +typedef struct MD_REF_DEF_LIST_tag MD_REF_DEF_LIST; +struct MD_REF_DEF_LIST_tag { + int n_ref_defs; + int alloc_ref_defs; + MD_REF_DEF* ref_defs[]; /* Valid items always point into ctx->ref_defs[] */ +}; + +static int +md_ref_def_cmp(const void* a, const void* b) +{ + const MD_REF_DEF* a_ref = *(const MD_REF_DEF**)a; + const MD_REF_DEF* b_ref = *(const MD_REF_DEF**)b; + + if(a_ref->hash < b_ref->hash) + return -1; + else if(a_ref->hash > b_ref->hash) + return +1; + else + return md_link_label_cmp(a_ref->label, a_ref->label_size, b_ref->label, b_ref->label_size); +} + +static int +md_ref_def_cmp_for_sort(const void* a, const void* b) +{ + int cmp; + + cmp = md_ref_def_cmp(a, b); + + /* Ensure stability of the sorting. */ + if(cmp == 0) { + const MD_REF_DEF* a_ref = *(const MD_REF_DEF**)a; + const MD_REF_DEF* b_ref = *(const MD_REF_DEF**)b; + + if(a_ref < b_ref) + cmp = -1; + else if(a_ref > b_ref) + cmp = +1; + else + cmp = 0; + } + + return cmp; +} + +static int +md_build_ref_def_hashtable(MD_CTX* ctx) +{ + int i, j; + + if(ctx->n_ref_defs == 0) + return 0; + + ctx->ref_def_hashtable_size = (ctx->n_ref_defs * 5) / 4; + ctx->ref_def_hashtable = malloc(ctx->ref_def_hashtable_size * sizeof(void*)); + if(ctx->ref_def_hashtable == NULL) { + MD_LOG("malloc() failed."); + goto abort; + } + memset(ctx->ref_def_hashtable, 0, ctx->ref_def_hashtable_size * sizeof(void*)); + + /* Each member of ctx->ref_def_hashtable[] can be: + * -- NULL, + * -- pointer to the MD_REF_DEF in ctx->ref_defs[], or + * -- pointer to a MD_REF_DEF_LIST, which holds multiple pointers to + * such MD_REF_DEFs. + */ + for(i = 0; i < ctx->n_ref_defs; i++) { + MD_REF_DEF* def = &ctx->ref_defs[i]; + void* bucket; + MD_REF_DEF_LIST* list; + + def->hash = md_link_label_hash(def->label, def->label_size); + bucket = ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size]; + + if(bucket == NULL) { + /* The bucket is empty. Make it just point to the def. */ + ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = def; + continue; + } + + if(ctx->ref_defs <= (MD_REF_DEF*) bucket && (MD_REF_DEF*) bucket < ctx->ref_defs + ctx->n_ref_defs) { + /* The bucket already contains one ref. def. Lets see whether it + * is the same label (ref. def. duplicate) or different one + * (hash conflict). */ + MD_REF_DEF* old_def = (MD_REF_DEF*) bucket; + + if(md_link_label_cmp(def->label, def->label_size, old_def->label, old_def->label_size) == 0) { + /* Duplicate label: Ignore this ref. def. */ + continue; + } + + /* Make the bucket complex, i.e. able to hold more ref. defs. */ + list = (MD_REF_DEF_LIST*) malloc(sizeof(MD_REF_DEF_LIST) + 2 * sizeof(MD_REF_DEF*)); + if(list == NULL) { + MD_LOG("malloc() failed."); + goto abort; + } + list->ref_defs[0] = old_def; + list->ref_defs[1] = def; + list->n_ref_defs = 2; + list->alloc_ref_defs = 2; + ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list; + continue; + } + + /* Append the def to the complex bucket list. + * + * Note in this case we ignore potential duplicates to avoid expensive + * iterating over the complex bucket. Below, we revisit all the complex + * buckets and handle it more cheaply after the complex bucket contents + * is sorted. */ + list = (MD_REF_DEF_LIST*) bucket; + if(list->n_ref_defs >= list->alloc_ref_defs) { + int alloc_ref_defs = list->alloc_ref_defs + list->alloc_ref_defs / 2; + MD_REF_DEF_LIST* list_tmp = (MD_REF_DEF_LIST*) realloc(list, + sizeof(MD_REF_DEF_LIST) + alloc_ref_defs * sizeof(MD_REF_DEF*)); + if(list_tmp == NULL) { + MD_LOG("realloc() failed."); + goto abort; + } + list = list_tmp; + list->alloc_ref_defs = alloc_ref_defs; + ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list; + } + + list->ref_defs[list->n_ref_defs] = def; + list->n_ref_defs++; + } + + /* Sort the complex buckets so we can use bsearch() with them. */ + for(i = 0; i < ctx->ref_def_hashtable_size; i++) { + void* bucket = ctx->ref_def_hashtable[i]; + MD_REF_DEF_LIST* list; + + if(bucket == NULL) + continue; + if(ctx->ref_defs <= (MD_REF_DEF*) bucket && (MD_REF_DEF*) bucket < ctx->ref_defs + ctx->n_ref_defs) + continue; + + list = (MD_REF_DEF_LIST*) bucket; + qsort(list->ref_defs, list->n_ref_defs, sizeof(MD_REF_DEF*), md_ref_def_cmp_for_sort); + + /* Disable all duplicates in the complex bucket by forcing all such + * records to point to the 1st such ref. def. I.e. no matter which + * record is found during the lookup, it will always point to the right + * ref. def. in ctx->ref_defs[]. */ + for(j = 1; j < list->n_ref_defs; j++) { + if(md_ref_def_cmp(&list->ref_defs[j-1], &list->ref_defs[j]) == 0) + list->ref_defs[j] = list->ref_defs[j-1]; + } + } + + return 0; + +abort: + return -1; +} + +static void +md_free_ref_def_hashtable(MD_CTX* ctx) +{ + if(ctx->ref_def_hashtable != NULL) { + int i; + + for(i = 0; i < ctx->ref_def_hashtable_size; i++) { + void* bucket = ctx->ref_def_hashtable[i]; + if(bucket == NULL) + continue; + if(ctx->ref_defs <= (MD_REF_DEF*) bucket && (MD_REF_DEF*) bucket < ctx->ref_defs + ctx->n_ref_defs) + continue; + free(bucket); + } + + free(ctx->ref_def_hashtable); + } +} + +static const MD_REF_DEF* +md_lookup_ref_def(MD_CTX* ctx, const CHAR* label, SZ label_size) +{ + unsigned hash; + void* bucket; + + if(ctx->ref_def_hashtable_size == 0) + return NULL; + + hash = md_link_label_hash(label, label_size); + bucket = ctx->ref_def_hashtable[hash % ctx->ref_def_hashtable_size]; + + if(bucket == NULL) { + return NULL; + } else if(ctx->ref_defs <= (MD_REF_DEF*) bucket && (MD_REF_DEF*) bucket < ctx->ref_defs + ctx->n_ref_defs) { + const MD_REF_DEF* def = (MD_REF_DEF*) bucket; + + if(md_link_label_cmp(def->label, def->label_size, label, label_size) == 0) + return def; + else + return NULL; + } else { + MD_REF_DEF_LIST* list = (MD_REF_DEF_LIST*) bucket; + MD_REF_DEF key_buf; + const MD_REF_DEF* key = &key_buf; + const MD_REF_DEF** ret; + + key_buf.label = (CHAR*) label; + key_buf.label_size = label_size; + key_buf.hash = md_link_label_hash(key_buf.label, key_buf.label_size); + + ret = (const MD_REF_DEF**) bsearch(&key, list->ref_defs, + list->n_ref_defs, sizeof(MD_REF_DEF*), md_ref_def_cmp); + if(ret != NULL) + return *ret; + else + return NULL; + } +} + + +/*************************** + *** Recognizing Links *** + ***************************/ + +/* Note this code is partially shared between processing inlines and blocks + * as reference definitions and links share some helper parser functions. + */ + +typedef struct MD_LINK_ATTR_tag MD_LINK_ATTR; +struct MD_LINK_ATTR_tag { + OFF dest_beg; + OFF dest_end; + + CHAR* title; + SZ title_size; + int title_needs_free; +}; + + +static int +md_is_link_label(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, + OFF* p_end, int* p_beg_line_index, int* p_end_line_index, + OFF* p_contents_beg, OFF* p_contents_end) +{ + OFF off = beg; + OFF contents_beg = 0; + OFF contents_end = 0; + int line_index = 0; + int len = 0; + + if(CH(off) != _T('[')) + return FALSE; + off++; + + while(1) { + OFF line_end = lines[line_index].end; + + while(off < line_end) { + if(CH(off) == _T('\\') && off+1 < ctx->size && (ISPUNCT(off+1) || ISNEWLINE(off+1))) { + if(contents_end == 0) { + contents_beg = off; + *p_beg_line_index = line_index; + } + contents_end = off + 2; + off += 2; + } else if(CH(off) == _T('[')) { + return FALSE; + } else if(CH(off) == _T(']')) { + if(contents_beg < contents_end) { + /* Success. */ + *p_contents_beg = contents_beg; + *p_contents_end = contents_end; + *p_end = off+1; + *p_end_line_index = line_index; + return TRUE; + } else { + /* Link label must have some non-whitespace contents. */ + return FALSE; + } + } else { + unsigned codepoint; + SZ char_size; + + codepoint = md_decode_unicode(ctx->text, off, ctx->size, &char_size); + if(!ISUNICODEWHITESPACE_(codepoint)) { + if(contents_end == 0) { + contents_beg = off; + *p_beg_line_index = line_index; + } + contents_end = off + char_size; + } + + off += char_size; + } + + len++; + if(len > 999) + return FALSE; + } + + line_index++; + len++; + if(line_index < n_lines) + off = lines[line_index].beg; + else + break; + } + + return FALSE; +} + +static int +md_is_link_destination_A(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, + OFF* p_contents_beg, OFF* p_contents_end) +{ + OFF off = beg; + + if(off >= max_end || CH(off) != _T('<')) + return FALSE; + off++; + + while(off < max_end) { + if(CH(off) == _T('\\') && off+1 < max_end && ISPUNCT(off+1)) { + off += 2; + continue; + } + + if(ISNEWLINE(off) || CH(off) == _T('<')) + return FALSE; + + if(CH(off) == _T('>')) { + /* Success. */ + *p_contents_beg = beg+1; + *p_contents_end = off; + *p_end = off+1; + return TRUE; + } + + off++; + } + + return FALSE; +} + +static int +md_is_link_destination_B(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, + OFF* p_contents_beg, OFF* p_contents_end) +{ + OFF off = beg; + int parenthesis_level = 0; + + while(off < max_end) { + if(CH(off) == _T('\\') && off+1 < max_end && ISPUNCT(off+1)) { + off += 2; + continue; + } + + if(ISWHITESPACE(off) || ISCNTRL(off)) + break; + + /* Link destination may include balanced pairs of unescaped '(' ')'. + * Note we limit the maximal nesting level by 32 to protect us from + * https://github.com/jgm/cmark/issues/214 */ + if(CH(off) == _T('(')) { + parenthesis_level++; + if(parenthesis_level > 32) + return FALSE; + } else if(CH(off) == _T(')')) { + if(parenthesis_level == 0) + break; + parenthesis_level--; + } + + off++; + } + + if(parenthesis_level != 0 || off == beg) + return FALSE; + + /* Success. */ + *p_contents_beg = beg; + *p_contents_end = off; + *p_end = off; + return TRUE; +} + +static inline int +md_is_link_destination(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, + OFF* p_contents_beg, OFF* p_contents_end) +{ + if(CH(beg) == _T('<')) + return md_is_link_destination_A(ctx, beg, max_end, p_end, p_contents_beg, p_contents_end); + else + return md_is_link_destination_B(ctx, beg, max_end, p_end, p_contents_beg, p_contents_end); +} + +static int +md_is_link_title(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, + OFF* p_end, int* p_beg_line_index, int* p_end_line_index, + OFF* p_contents_beg, OFF* p_contents_end) +{ + OFF off = beg; + CHAR closer_char; + int line_index = 0; + + /* White space with up to one line break. */ + while(off < lines[line_index].end && ISWHITESPACE(off)) + off++; + if(off >= lines[line_index].end) { + line_index++; + if(line_index >= n_lines) + return FALSE; + off = lines[line_index].beg; + } + if(off == beg) + return FALSE; + + *p_beg_line_index = line_index; + + /* First char determines how to detect end of it. */ + switch(CH(off)) { + case _T('"'): closer_char = _T('"'); break; + case _T('\''): closer_char = _T('\''); break; + case _T('('): closer_char = _T(')'); break; + default: return FALSE; + } + off++; + + *p_contents_beg = off; + + while(line_index < n_lines) { + OFF line_end = lines[line_index].end; + + while(off < line_end) { + if(CH(off) == _T('\\') && off+1 < ctx->size && (ISPUNCT(off+1) || ISNEWLINE(off+1))) { + off++; + } else if(CH(off) == closer_char) { + /* Success. */ + *p_contents_end = off; + *p_end = off+1; + *p_end_line_index = line_index; + return TRUE; + } else if(closer_char == _T(')') && CH(off) == _T('(')) { + /* ()-style title cannot contain (unescaped '(')) */ + return FALSE; + } + + off++; + } + + line_index++; + } + + return FALSE; +} + +/* Returns 0 if it is not a reference definition. + * + * Returns N > 0 if it is a reference definition. N then corresponds to the + * number of lines forming it). In this case the definition is stored for + * resolving any links referring to it. + * + * Returns -1 in case of an error (out of memory). + */ +static int +md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +{ + OFF label_contents_beg; + OFF label_contents_end; + int label_contents_line_index = -1; + int label_is_multiline = FALSE; + OFF dest_contents_beg; + OFF dest_contents_end; + OFF title_contents_beg; + OFF title_contents_end; + int title_contents_line_index; + int title_is_multiline = FALSE; + OFF off; + int line_index = 0; + int tmp_line_index; + MD_REF_DEF* def = NULL; + int ret = 0; + + /* Link label. */ + if(!md_is_link_label(ctx, lines, n_lines, lines[0].beg, + &off, &label_contents_line_index, &line_index, + &label_contents_beg, &label_contents_end)) + return FALSE; + label_is_multiline = (label_contents_line_index != line_index); + + /* Colon. */ + if(off >= lines[line_index].end || CH(off) != _T(':')) + return FALSE; + off++; + + /* Optional white space with up to one line break. */ + while(off < lines[line_index].end && ISWHITESPACE(off)) + off++; + if(off >= lines[line_index].end) { + line_index++; + if(line_index >= n_lines) + return FALSE; + off = lines[line_index].beg; + } + + /* Link destination. */ + if(!md_is_link_destination(ctx, off, lines[line_index].end, + &off, &dest_contents_beg, &dest_contents_end)) + return FALSE; + + /* (Optional) title. Note we interpret it as an title only if nothing + * more follows on its last line. */ + if(md_is_link_title(ctx, lines + line_index, n_lines - line_index, off, + &off, &title_contents_line_index, &tmp_line_index, + &title_contents_beg, &title_contents_end) + && off >= lines[line_index + tmp_line_index].end) + { + title_is_multiline = (tmp_line_index != title_contents_line_index); + title_contents_line_index += line_index; + line_index += tmp_line_index; + } else { + /* Not a title. */ + title_is_multiline = FALSE; + title_contents_beg = off; + title_contents_end = off; + title_contents_line_index = 0; + } + + /* Nothing more can follow on the last line. */ + if(off < lines[line_index].end) + return FALSE; + + /* So, it _is_ a reference definition. Remember it. */ + if(ctx->n_ref_defs >= ctx->alloc_ref_defs) { + MD_REF_DEF* new_defs; + + ctx->alloc_ref_defs = (ctx->alloc_ref_defs > 0 + ? ctx->alloc_ref_defs + ctx->alloc_ref_defs / 2 + : 16); + new_defs = (MD_REF_DEF*) realloc(ctx->ref_defs, ctx->alloc_ref_defs * sizeof(MD_REF_DEF)); + if(new_defs == NULL) { + MD_LOG("realloc() failed."); + goto abort; + } + + ctx->ref_defs = new_defs; + } + def = &ctx->ref_defs[ctx->n_ref_defs]; + memset(def, 0, sizeof(MD_REF_DEF)); + + if(label_is_multiline) { + MD_CHECK(md_merge_lines_alloc(ctx, label_contents_beg, label_contents_end, + lines + label_contents_line_index, n_lines - label_contents_line_index, + _T(' '), &def->label, &def->label_size)); + def->label_needs_free = TRUE; + } else { + def->label = (CHAR*) STR(label_contents_beg); + def->label_size = label_contents_end - label_contents_beg; + } + + if(title_is_multiline) { + MD_CHECK(md_merge_lines_alloc(ctx, title_contents_beg, title_contents_end, + lines + title_contents_line_index, n_lines - title_contents_line_index, + _T('\n'), &def->title, &def->title_size)); + def->title_needs_free = TRUE; + } else { + def->title = (CHAR*) STR(title_contents_beg); + def->title_size = title_contents_end - title_contents_beg; + } + + def->dest_beg = dest_contents_beg; + def->dest_end = dest_contents_end; + + /* Success. */ + ctx->n_ref_defs++; + return line_index + 1; + +abort: + /* Failure. */ + if(def != NULL && def->label_needs_free) + free(def->label); + if(def != NULL && def->title_needs_free) + free(def->title); + return ret; +} + +static int +md_is_link_reference(MD_CTX* ctx, const MD_LINE* lines, int n_lines, + OFF beg, OFF end, MD_LINK_ATTR* attr) +{ + const MD_REF_DEF* def; + const MD_LINE* beg_line; + int is_multiline; + CHAR* label; + SZ label_size; + int ret; + + MD_ASSERT(CH(beg) == _T('[') || CH(beg) == _T('!')); + MD_ASSERT(CH(end-1) == _T(']')); + + beg += (CH(beg) == _T('!') ? 2 : 1); + end--; + + /* Find lines corresponding to the beg and end positions. */ + beg_line = md_lookup_line(beg, lines, n_lines); + is_multiline = (end > beg_line->end); + + if(is_multiline) { + MD_CHECK(md_merge_lines_alloc(ctx, beg, end, beg_line, + (int)(n_lines - (beg_line - lines)), _T(' '), &label, &label_size)); + } else { + label = (CHAR*) STR(beg); + label_size = end - beg; + } + + def = md_lookup_ref_def(ctx, label, label_size); + if(def != NULL) { + attr->dest_beg = def->dest_beg; + attr->dest_end = def->dest_end; + attr->title = def->title; + attr->title_size = def->title_size; + attr->title_needs_free = FALSE; + } + + if(is_multiline) + free(label); + + ret = (def != NULL); + +abort: + return ret; +} + +static int +md_is_inline_link_spec(MD_CTX* ctx, const MD_LINE* lines, int n_lines, + OFF beg, OFF* p_end, MD_LINK_ATTR* attr) +{ + int line_index = 0; + int tmp_line_index; + OFF title_contents_beg; + OFF title_contents_end; + int title_contents_line_index; + int title_is_multiline; + OFF off = beg; + int ret = FALSE; + + while(off >= lines[line_index].end) + line_index++; + + MD_ASSERT(CH(off) == _T('(')); + off++; + + /* Optional white space with up to one line break. */ + while(off < lines[line_index].end && ISWHITESPACE(off)) + off++; + if(off >= lines[line_index].end && (off >= ctx->size || ISNEWLINE(off))) { + line_index++; + if(line_index >= n_lines) + return FALSE; + off = lines[line_index].beg; + } + + /* Link destination may be omitted, but only when not also having a title. */ + if(off < ctx->size && CH(off) == _T(')')) { + attr->dest_beg = off; + attr->dest_end = off; + attr->title = NULL; + attr->title_size = 0; + attr->title_needs_free = FALSE; + off++; + *p_end = off; + return TRUE; + } + + /* Link destination. */ + if(!md_is_link_destination(ctx, off, lines[line_index].end, + &off, &attr->dest_beg, &attr->dest_end)) + return FALSE; + + /* (Optional) title. */ + if(md_is_link_title(ctx, lines + line_index, n_lines - line_index, off, + &off, &title_contents_line_index, &tmp_line_index, + &title_contents_beg, &title_contents_end)) + { + title_is_multiline = (tmp_line_index != title_contents_line_index); + title_contents_line_index += line_index; + line_index += tmp_line_index; + } else { + /* Not a title. */ + title_is_multiline = FALSE; + title_contents_beg = off; + title_contents_end = off; + title_contents_line_index = 0; + } + + /* Optional whitespace followed with final ')'. */ + while(off < lines[line_index].end && ISWHITESPACE(off)) + off++; + if (off >= lines[line_index].end && (off >= ctx->size || ISNEWLINE(off))) { + line_index++; + if(line_index >= n_lines) + return FALSE; + off = lines[line_index].beg; + } + if(CH(off) != _T(')')) + goto abort; + off++; + + if(title_contents_beg >= title_contents_end) { + attr->title = NULL; + attr->title_size = 0; + attr->title_needs_free = FALSE; + } else if(!title_is_multiline) { + attr->title = (CHAR*) STR(title_contents_beg); + attr->title_size = title_contents_end - title_contents_beg; + attr->title_needs_free = FALSE; + } else { + MD_CHECK(md_merge_lines_alloc(ctx, title_contents_beg, title_contents_end, + lines + title_contents_line_index, n_lines - title_contents_line_index, + _T('\n'), &attr->title, &attr->title_size)); + attr->title_needs_free = TRUE; + } + + *p_end = off; + ret = TRUE; + +abort: + return ret; +} + +static void +md_free_ref_defs(MD_CTX* ctx) +{ + int i; + + for(i = 0; i < ctx->n_ref_defs; i++) { + MD_REF_DEF* def = &ctx->ref_defs[i]; + + if(def->label_needs_free) + free(def->label); + if(def->title_needs_free) + free(def->title); + } + + free(ctx->ref_defs); +} + + +/****************************************** + *** Processing Inlines (a.k.a Spans) *** + ******************************************/ + +/* We process inlines in few phases: + * + * (1) We go through the block text and collect all significant characters + * which may start/end a span or some other significant position into + * ctx->marks[]. Core of this is what md_collect_marks() does. + * + * We also do some very brief preliminary context-less analysis, whether + * it might be opener or closer (e.g. of an emphasis span). + * + * This speeds the other steps as we do not need to re-iterate over all + * characters anymore. + * + * (2) We analyze each potential mark types, in order by their precedence. + * + * In each md_analyze_XXX() function, we re-iterate list of the marks, + * skipping already resolved regions (in preceding precedences) and try to + * resolve them. + * + * (2.1) For trivial marks, which are single (e.g. HTML entity), we just mark + * them as resolved. + * + * (2.2) For range-type marks, we analyze whether the mark could be closer + * and, if yes, whether there is some preceding opener it could satisfy. + * + * If not we check whether it could be really an opener and if yes, we + * remember it so subsequent closers may resolve it. + * + * (3) Finally, when all marks were analyzed, we render the block contents + * by calling MD_RENDERER::text() callback, interrupting by ::enter_span() + * or ::close_span() whenever we reach a resolved mark. + */ + + +/* The mark structure. + * + * '\\': Maybe escape sequence. + * '\0': NULL char. + * '*': Maybe (strong) emphasis start/end. + * '_': Maybe (strong) emphasis start/end. + * '~': Maybe strikethrough start/end (needs MD_FLAG_STRIKETHROUGH). + * '`': Maybe code span start/end. + * '&': Maybe start of entity. + * ';': Maybe end of entity. + * '<': Maybe start of raw HTML or autolink. + * '>': Maybe end of raw HTML or autolink. + * '[': Maybe start of link label or link text. + * '!': Equivalent of '[' for image. + * ']': Maybe end of link label or link text. + * '@': Maybe permissive e-mail auto-link (needs MD_FLAG_PERMISSIVEEMAILAUTOLINKS). + * ':': Maybe permissive URL auto-link (needs MD_FLAG_PERMISSIVEURLAUTOLINKS). + * '.': Maybe permissive WWW auto-link (needs MD_FLAG_PERMISSIVEWWWAUTOLINKS). + * 'D': Dummy mark, it reserves a space for splitting a previous mark + * (e.g. emphasis) or to make more space for storing some special data + * related to the preceding mark (e.g. link). + * + * Note that not all instances of these chars in the text imply creation of the + * structure. Only those which have (or may have, after we see more context) + * the special meaning. + * + * (Keep this struct as small as possible to fit as much of them into CPU + * cache line.) + */ +struct MD_MARK_tag { + OFF beg; + OFF end; + + /* For unresolved openers, 'prev' and 'next' form the chain of open openers + * of given type 'ch'. + * + * During resolving, we disconnect from the chain and point to the + * corresponding counterpart so opener points to its closer and vice versa. + */ + int prev; + int next; + CHAR ch; + unsigned char flags; +}; + +/* Mark flags (these apply to ALL mark types). */ +#define MD_MARK_POTENTIAL_OPENER 0x01 /* Maybe opener. */ +#define MD_MARK_POTENTIAL_CLOSER 0x02 /* Maybe closer. */ +#define MD_MARK_OPENER 0x04 /* Definitely opener. */ +#define MD_MARK_CLOSER 0x08 /* Definitely closer. */ +#define MD_MARK_RESOLVED 0x10 /* Resolved in any definite way. */ + +/* Mark flags specific for various mark types (so they can share bits). */ +#define MD_MARK_EMPH_INTRAWORD 0x20 /* Helper for the "rule of 3". */ +#define MD_MARK_EMPH_MOD3_0 0x40 +#define MD_MARK_EMPH_MOD3_1 0x80 +#define MD_MARK_EMPH_MOD3_2 (0x40 | 0x80) +#define MD_MARK_EMPH_MOD3_MASK (0x40 | 0x80) +#define MD_MARK_AUTOLINK 0x20 /* Distinguisher for '<', '>'. */ +#define MD_MARK_VALIDPERMISSIVEAUTOLINK 0x20 /* For permissive autolinks. */ +#define MD_MARK_HASNESTEDBRACKETS 0x20 /* For '[' to rule out invalid link labels early */ + +static MD_MARKCHAIN* +md_asterisk_chain(MD_CTX* ctx, unsigned flags) +{ + switch(flags & (MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_MASK)) { + case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_0: return &ASTERISK_OPENERS_intraword_mod3_0; + case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_1: return &ASTERISK_OPENERS_intraword_mod3_1; + case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_2: return &ASTERISK_OPENERS_intraword_mod3_2; + case MD_MARK_EMPH_MOD3_0: return &ASTERISK_OPENERS_extraword_mod3_0; + case MD_MARK_EMPH_MOD3_1: return &ASTERISK_OPENERS_extraword_mod3_1; + case MD_MARK_EMPH_MOD3_2: return &ASTERISK_OPENERS_extraword_mod3_2; + default: MD_UNREACHABLE(); + } + return NULL; +} + +static MD_MARKCHAIN* +md_mark_chain(MD_CTX* ctx, int mark_index) +{ + MD_MARK* mark = &ctx->marks[mark_index]; + + switch(mark->ch) { + case _T('*'): return md_asterisk_chain(ctx, mark->flags); + case _T('_'): return &UNDERSCORE_OPENERS; + case _T('~'): return (mark->end - mark->beg == 1) ? &TILDE_OPENERS_1 : &TILDE_OPENERS_2; + case _T('!'): MD_FALLTHROUGH(); + case _T('['): return &BRACKET_OPENERS; + case _T('|'): return &TABLECELLBOUNDARIES; + default: return NULL; + } +} + +static MD_MARK* +md_push_mark(MD_CTX* ctx) +{ + if(ctx->n_marks >= ctx->alloc_marks) { + MD_MARK* new_marks; + + ctx->alloc_marks = (ctx->alloc_marks > 0 + ? ctx->alloc_marks + ctx->alloc_marks / 2 + : 64); + new_marks = realloc(ctx->marks, ctx->alloc_marks * sizeof(MD_MARK)); + if(new_marks == NULL) { + MD_LOG("realloc() failed."); + return NULL; + } + + ctx->marks = new_marks; + } + + return &ctx->marks[ctx->n_marks++]; +} + +#define PUSH_MARK_() \ + do { \ + mark = md_push_mark(ctx); \ + if(mark == NULL) { \ + ret = -1; \ + goto abort; \ + } \ + } while(0) + +#define PUSH_MARK(ch_, beg_, end_, flags_) \ + do { \ + PUSH_MARK_(); \ + mark->beg = (beg_); \ + mark->end = (end_); \ + mark->prev = -1; \ + mark->next = -1; \ + mark->ch = (char)(ch_); \ + mark->flags = (flags_); \ + } while(0) + + +static void +md_mark_chain_append(MD_CTX* ctx, MD_MARKCHAIN* chain, int mark_index) +{ + if(chain->tail >= 0) + ctx->marks[chain->tail].next = mark_index; + else + chain->head = mark_index; + + ctx->marks[mark_index].prev = chain->tail; + ctx->marks[mark_index].next = -1; + chain->tail = mark_index; +} + +/* Sometimes, we need to store a pointer into the mark. It is quite rare + * so we do not bother to make MD_MARK use union, and it can only happen + * for dummy marks. */ +static inline void +md_mark_store_ptr(MD_CTX* ctx, int mark_index, void* ptr) +{ + MD_MARK* mark = &ctx->marks[mark_index]; + MD_ASSERT(mark->ch == 'D'); + + /* Check only members beg and end are misused for this. */ + MD_ASSERT(sizeof(void*) <= 2 * sizeof(OFF)); + memcpy(mark, &ptr, sizeof(void*)); +} + +static inline void* +md_mark_get_ptr(MD_CTX* ctx, int mark_index) +{ + void* ptr; + MD_MARK* mark = &ctx->marks[mark_index]; + MD_ASSERT(mark->ch == 'D'); + memcpy(&ptr, mark, sizeof(void*)); + return ptr; +} + +static void +md_resolve_range(MD_CTX* ctx, MD_MARKCHAIN* chain, int opener_index, int closer_index) +{ + MD_MARK* opener = &ctx->marks[opener_index]; + MD_MARK* closer = &ctx->marks[closer_index]; + + /* Remove opener from the list of openers. */ + if(chain != NULL) { + if(opener->prev >= 0) + ctx->marks[opener->prev].next = opener->next; + else + chain->head = opener->next; + + if(opener->next >= 0) + ctx->marks[opener->next].prev = opener->prev; + else + chain->tail = opener->prev; + } + + /* Interconnect opener and closer and mark both as resolved. */ + opener->next = closer_index; + opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED; + closer->prev = opener_index; + closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED; +} + + +#define MD_ROLLBACK_ALL 0 +#define MD_ROLLBACK_CROSSING 1 + +/* In the range ctx->marks[opener_index] ... [closer_index], undo some or all + * resolvings accordingly to these rules: + * + * (1) All openers BEFORE the range corresponding to any closer inside the + * range are un-resolved and they are re-added to their respective chains + * of unresolved openers. This ensures we can reuse the opener for closers + * AFTER the range. + * + * (2) If 'how' is MD_ROLLBACK_ALL, then ALL resolved marks inside the range + * are discarded. + * + * (3) If 'how' is MD_ROLLBACK_CROSSING, only closers with openers handled + * in (1) are discarded. I.e. pairs of openers and closers which are both + * inside the range are retained as well as any unpaired marks. + */ +static void +md_rollback(MD_CTX* ctx, int opener_index, int closer_index, int how) +{ + int i; + int mark_index; + + /* Cut all unresolved openers at the mark index. */ + for(i = OPENERS_CHAIN_FIRST; i < OPENERS_CHAIN_LAST+1; i++) { + MD_MARKCHAIN* chain = &ctx->mark_chains[i]; + + while(chain->tail >= opener_index) { + int same = chain->tail == opener_index; + chain->tail = ctx->marks[chain->tail].prev; + if (same) break; + } + + if(chain->tail >= 0) + ctx->marks[chain->tail].next = -1; + else + chain->head = -1; + } + + /* Go backwards so that unresolved openers are re-added into their + * respective chains, in the right order. */ + mark_index = closer_index - 1; + while(mark_index > opener_index) { + MD_MARK* mark = &ctx->marks[mark_index]; + int mark_flags = mark->flags; + int discard_flag = (how == MD_ROLLBACK_ALL); + + if(mark->flags & MD_MARK_CLOSER) { + int mark_opener_index = mark->prev; + + /* Undo opener BEFORE the range. */ + if(mark_opener_index < opener_index) { + MD_MARK* mark_opener = &ctx->marks[mark_opener_index]; + MD_MARKCHAIN* chain; + + mark_opener->flags &= ~(MD_MARK_OPENER | MD_MARK_CLOSER | MD_MARK_RESOLVED); + chain = md_mark_chain(ctx, opener_index); + if(chain != NULL) { + md_mark_chain_append(ctx, chain, mark_opener_index); + discard_flag = 1; + } + } + } + + /* And reset our flags. */ + if(discard_flag) { + /* Make zero-length closer a dummy mark as that's how it was born */ + if((mark->flags & MD_MARK_CLOSER) && mark->beg == mark->end) + mark->ch = 'D'; + + mark->flags &= ~(MD_MARK_OPENER | MD_MARK_CLOSER | MD_MARK_RESOLVED); + } + + /* Jump as far as we can over unresolved or non-interesting marks. */ + switch(how) { + case MD_ROLLBACK_CROSSING: + if((mark_flags & MD_MARK_CLOSER) && mark->prev > opener_index) { + /* If we are closer with opener INSIDE the range, there may + * not be any other crosser inside the subrange. */ + mark_index = mark->prev; + break; + } + MD_FALLTHROUGH(); + default: + mark_index--; + break; + } + } +} + +static void +md_build_mark_char_map(MD_CTX* ctx) +{ + memset(ctx->mark_char_map, 0, sizeof(ctx->mark_char_map)); + + ctx->mark_char_map['\\'] = 1; + ctx->mark_char_map['*'] = 1; + ctx->mark_char_map['_'] = 1; + ctx->mark_char_map['`'] = 1; + ctx->mark_char_map['&'] = 1; + ctx->mark_char_map[';'] = 1; + ctx->mark_char_map['<'] = 1; + ctx->mark_char_map['>'] = 1; + ctx->mark_char_map['['] = 1; + ctx->mark_char_map['!'] = 1; + ctx->mark_char_map[']'] = 1; + ctx->mark_char_map['\0'] = 1; + + if(ctx->parser.flags & MD_FLAG_STRIKETHROUGH) + ctx->mark_char_map['~'] = 1; + + if(ctx->parser.flags & MD_FLAG_LATEXMATHSPANS) + ctx->mark_char_map['$'] = 1; + + if(ctx->parser.flags & MD_FLAG_PERMISSIVEEMAILAUTOLINKS) + ctx->mark_char_map['@'] = 1; + + if(ctx->parser.flags & MD_FLAG_PERMISSIVEURLAUTOLINKS) + ctx->mark_char_map[':'] = 1; + + if(ctx->parser.flags & MD_FLAG_PERMISSIVEWWWAUTOLINKS) + ctx->mark_char_map['.'] = 1; + + if((ctx->parser.flags & MD_FLAG_TABLES) || (ctx->parser.flags & MD_FLAG_WIKILINKS)) + ctx->mark_char_map['|'] = 1; + + if(ctx->parser.flags & MD_FLAG_COLLAPSEWHITESPACE) { + int i; + + for(i = 0; i < (int) sizeof(ctx->mark_char_map); i++) { + if(ISWHITESPACE_(i)) + ctx->mark_char_map[i] = 1; + } + } +} + +/* We limit code span marks to lower than 32 backticks. This solves the + * pathologic case of too many openers, each of different length: Their + * resolving would be then O(n^2). */ +#define CODESPAN_MARK_MAXLEN 32 + +static int +md_is_code_span(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, + OFF* p_opener_beg, OFF* p_opener_end, + OFF* p_closer_beg, OFF* p_closer_end, + OFF last_potential_closers[CODESPAN_MARK_MAXLEN], + int* p_reached_paragraph_end) +{ + OFF opener_beg = beg; + OFF opener_end; + OFF closer_beg; + OFF closer_end; + SZ mark_len; + OFF line_end; + int has_space_after_opener = FALSE; + int has_eol_after_opener = FALSE; + int has_space_before_closer = FALSE; + int has_eol_before_closer = FALSE; + int has_only_space = TRUE; + int line_index = 0; + + line_end = lines[0].end; + opener_end = opener_beg; + while(opener_end < line_end && CH(opener_end) == _T('`')) + opener_end++; + has_space_after_opener = (opener_end < line_end && CH(opener_end) == _T(' ')); + has_eol_after_opener = (opener_end == line_end); + + /* The caller needs to know end of the opening mark even if we fail. */ + *p_opener_end = opener_end; + + mark_len = opener_end - opener_beg; + if(mark_len > CODESPAN_MARK_MAXLEN) + return FALSE; + + /* Check whether we already know there is no closer of this length. + * If so, re-scan does no sense. This fixes issue #59. */ + if(last_potential_closers[mark_len-1] >= lines[n_lines-1].end || + (*p_reached_paragraph_end && last_potential_closers[mark_len-1] < opener_end)) + return FALSE; + + closer_beg = opener_end; + closer_end = opener_end; + + /* Find closer mark. */ + while(TRUE) { + while(closer_beg < line_end && CH(closer_beg) != _T('`')) { + if(CH(closer_beg) != _T(' ')) + has_only_space = FALSE; + closer_beg++; + } + closer_end = closer_beg; + while(closer_end < line_end && CH(closer_end) == _T('`')) + closer_end++; + + if(closer_end - closer_beg == mark_len) { + /* Success. */ + has_space_before_closer = (closer_beg > lines[line_index].beg && CH(closer_beg-1) == _T(' ')); + has_eol_before_closer = (closer_beg == lines[line_index].beg); + break; + } + + if(closer_end - closer_beg > 0) { + /* We have found a back-tick which is not part of the closer. */ + has_only_space = FALSE; + + /* But if we eventually fail, remember it as a potential closer + * of its own length for future attempts. This mitigates needs for + * rescans. */ + if(closer_end - closer_beg < CODESPAN_MARK_MAXLEN) { + if(closer_beg > last_potential_closers[closer_end - closer_beg - 1]) + last_potential_closers[closer_end - closer_beg - 1] = closer_beg; + } + } + + if(closer_end >= line_end) { + line_index++; + if(line_index >= n_lines) { + /* Reached end of the paragraph and still nothing. */ + *p_reached_paragraph_end = TRUE; + return FALSE; + } + /* Try on the next line. */ + line_end = lines[line_index].end; + closer_beg = lines[line_index].beg; + } else { + closer_beg = closer_end; + } + } + + /* If there is a space or a new line both after and before the opener + * (and if the code span is not made of spaces only), consume one initial + * and one trailing space as part of the marks. */ + if(!has_only_space && + (has_space_after_opener || has_eol_after_opener) && + (has_space_before_closer || has_eol_before_closer)) + { + if(has_space_after_opener) + opener_end++; + else + opener_end = lines[1].beg; + + if(has_space_before_closer) + closer_beg--; + else { + closer_beg = lines[line_index-1].end; + /* We need to eat the preceding "\r\n" but not any line trailing + * spaces. */ + while(closer_beg < ctx->size && ISBLANK(closer_beg)) + closer_beg++; + } + } + + *p_opener_beg = opener_beg; + *p_opener_end = opener_end; + *p_closer_beg = closer_beg; + *p_closer_end = closer_end; + return TRUE; +} + +static int +md_is_autolink_uri(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end) +{ + OFF off = beg+1; + + MD_ASSERT(CH(beg) == _T('<')); + + /* Check for scheme. */ + if(off >= max_end || !ISASCII(off)) + return FALSE; + off++; + while(1) { + if(off >= max_end) + return FALSE; + if(off - beg > 32) + return FALSE; + if(CH(off) == _T(':') && off - beg >= 3) + break; + if(!ISALNUM(off) && CH(off) != _T('+') && CH(off) != _T('-') && CH(off) != _T('.')) + return FALSE; + off++; + } + + /* Check the path after the scheme. */ + while(off < max_end && CH(off) != _T('>')) { + if(ISWHITESPACE(off) || ISCNTRL(off) || CH(off) == _T('<')) + return FALSE; + off++; + } + + if(off >= max_end) + return FALSE; + + MD_ASSERT(CH(off) == _T('>')); + *p_end = off+1; + return TRUE; +} + +static int +md_is_autolink_email(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end) +{ + OFF off = beg + 1; + int label_len; + + MD_ASSERT(CH(beg) == _T('<')); + + /* The code should correspond to this regexp: + /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+ + @[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])? + (?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ + */ + + /* Username (before '@'). */ + while(off < max_end && (ISALNUM(off) || ISANYOF(off, _T(".!#$%&'*+/=?^_`{|}~-")))) + off++; + if(off <= beg+1) + return FALSE; + + /* '@' */ + if(off >= max_end || CH(off) != _T('@')) + return FALSE; + off++; + + /* Labels delimited with '.'; each label is sequence of 1 - 63 alnum + * characters or '-', but '-' is not allowed as first or last char. */ + label_len = 0; + while(off < max_end) { + if(ISALNUM(off)) + label_len++; + else if(CH(off) == _T('-') && label_len > 0) + label_len++; + else if(CH(off) == _T('.') && label_len > 0 && CH(off-1) != _T('-')) + label_len = 0; + else + break; + + if(label_len > 63) + return FALSE; + + off++; + } + + if(label_len <= 0 || off >= max_end || CH(off) != _T('>') || CH(off-1) == _T('-')) + return FALSE; + + *p_end = off+1; + return TRUE; +} + +static int +md_is_autolink(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, int* p_missing_mailto) +{ + if(md_is_autolink_uri(ctx, beg, max_end, p_end)) { + *p_missing_mailto = FALSE; + return TRUE; + } + + if(md_is_autolink_email(ctx, beg, max_end, p_end)) { + *p_missing_mailto = TRUE; + return TRUE; + } + + return FALSE; +} + +static int +md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) +{ + const MD_LINE* line_term = lines + n_lines; + const MD_LINE* line; + int ret = 0; + MD_MARK* mark; + OFF codespan_last_potential_closers[CODESPAN_MARK_MAXLEN] = { 0 }; + int codespan_scanned_till_paragraph_end = FALSE; + + for(line = lines; line < line_term; line++) { + OFF off = line->beg; + OFF line_end = line->end; + + while(TRUE) { + CHAR ch; + +#ifdef MD4C_USE_UTF16 + /* For UTF-16, mark_char_map[] covers only ASCII. */ + #define IS_MARK_CHAR(off) ((CH(off) < SIZEOF_ARRAY(ctx->mark_char_map)) && \ + (ctx->mark_char_map[(unsigned char) CH(off)])) +#else + /* For 8-bit encodings, mark_char_map[] covers all 256 elements. */ + #define IS_MARK_CHAR(off) (ctx->mark_char_map[(unsigned char) CH(off)]) +#endif + + /* Optimization: Use some loop unrolling. */ + while(off + 3 < line_end && !IS_MARK_CHAR(off+0) && !IS_MARK_CHAR(off+1) + && !IS_MARK_CHAR(off+2) && !IS_MARK_CHAR(off+3)) + off += 4; + while(off < line_end && !IS_MARK_CHAR(off+0)) + off++; + + if(off >= line_end) + break; + + ch = CH(off); + + /* A backslash escape. + * It can go beyond line->end as it may involve escaped new + * line to form a hard break. */ + if(ch == _T('\\') && off+1 < ctx->size && (ISPUNCT(off+1) || ISNEWLINE(off+1))) { + /* Hard-break cannot be on the last line of the block. */ + if(!ISNEWLINE(off+1) || line+1 < line_term) + PUSH_MARK(ch, off, off+2, MD_MARK_RESOLVED); + off += 2; + continue; + } + + /* A potential (string) emphasis start/end. */ + if(ch == _T('*') || ch == _T('_')) { + OFF tmp = off+1; + int left_level; /* What precedes: 0 = whitespace; 1 = punctuation; 2 = other char. */ + int right_level; /* What follows: 0 = whitespace; 1 = punctuation; 2 = other char. */ + + while(tmp < line_end && CH(tmp) == ch) + tmp++; + + if(off == line->beg || ISUNICODEWHITESPACEBEFORE(off)) + left_level = 0; + else if(ISUNICODEPUNCTBEFORE(off)) + left_level = 1; + else + left_level = 2; + + if(tmp == line_end || ISUNICODEWHITESPACE(tmp)) + right_level = 0; + else if(ISUNICODEPUNCT(tmp)) + right_level = 1; + else + right_level = 2; + + /* Intra-word underscore doesn't have special meaning. */ + if(ch == _T('_') && left_level == 2 && right_level == 2) { + left_level = 0; + right_level = 0; + } + + if(left_level != 0 || right_level != 0) { + unsigned flags = 0; + + if(left_level > 0 && left_level >= right_level) + flags |= MD_MARK_POTENTIAL_CLOSER; + if(right_level > 0 && right_level >= left_level) + flags |= MD_MARK_POTENTIAL_OPENER; + if(left_level == 2 && right_level == 2) + flags |= MD_MARK_EMPH_INTRAWORD; + + /* For "the rule of three" we need to remember the original + * size of the mark (modulo three), before we potentially + * split the mark when being later resolved partially by some + * shorter closer. */ + switch((tmp - off) % 3) { + case 0: flags |= MD_MARK_EMPH_MOD3_0; break; + case 1: flags |= MD_MARK_EMPH_MOD3_1; break; + case 2: flags |= MD_MARK_EMPH_MOD3_2; break; + } + + PUSH_MARK(ch, off, tmp, flags); + + /* During resolving, multiple asterisks may have to be + * split into independent span start/ends. Consider e.g. + * "**foo* bar*". Therefore we push also some empty dummy + * marks to have enough space for that. */ + off++; + while(off < tmp) { + PUSH_MARK('D', off, off, 0); + off++; + } + continue; + } + + off = tmp; + continue; + } + + /* A potential code span start/end. */ + if(ch == _T('`')) { + OFF opener_beg, opener_end; + OFF closer_beg, closer_end; + int is_code_span; + + is_code_span = md_is_code_span(ctx, line, line_term - line, off, + &opener_beg, &opener_end, &closer_beg, &closer_end, + codespan_last_potential_closers, + &codespan_scanned_till_paragraph_end); + if(is_code_span) { + PUSH_MARK(_T('`'), opener_beg, opener_end, MD_MARK_OPENER | MD_MARK_RESOLVED); + PUSH_MARK(_T('`'), closer_beg, closer_end, MD_MARK_CLOSER | MD_MARK_RESOLVED); + ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1; + ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2; + + off = closer_end; + + /* Advance the current line accordingly. */ + if(off > line_end) { + line = md_lookup_line(off, line, line_term - line); + line_end = line->end; + } + continue; + } + + off = opener_end; + continue; + } + + /* A potential entity start. */ + if(ch == _T('&')) { + PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER); + off++; + continue; + } + + /* A potential entity end. */ + if(ch == _T(';')) { + /* We surely cannot be entity unless the previous mark is '&'. */ + if(ctx->n_marks > 0 && ctx->marks[ctx->n_marks-1].ch == _T('&')) + PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_CLOSER); + + off++; + continue; + } + + /* A potential autolink or raw HTML start/end. */ + if(ch == _T('<')) { + int is_autolink; + OFF autolink_end; + int missing_mailto; + + if(!(ctx->parser.flags & MD_FLAG_NOHTMLSPANS)) { + int is_html; + OFF html_end; + + /* Given the nature of the raw HTML, we have to recognize + * it here. Doing so later in md_analyze_lt_gt() could + * open can of worms of quadratic complexity. */ + is_html = md_is_html_any(ctx, line, line_term - line, off, + lines[n_lines-1].end, &html_end); + if(is_html) { + PUSH_MARK(_T('<'), off, off, MD_MARK_OPENER | MD_MARK_RESOLVED); + PUSH_MARK(_T('>'), html_end, html_end, MD_MARK_CLOSER | MD_MARK_RESOLVED); + ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1; + ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2; + off = html_end; + + /* Advance the current line accordingly. */ + if(off > line_end) { + line = md_lookup_line(off, line, line_term - line); + line_end = line->end; + } + continue; + } + } + + is_autolink = md_is_autolink(ctx, off, lines[n_lines-1].end, + &autolink_end, &missing_mailto); + if(is_autolink) { + PUSH_MARK((missing_mailto ? _T('@') : _T('<')), off, off+1, + MD_MARK_OPENER | MD_MARK_RESOLVED | MD_MARK_AUTOLINK); + PUSH_MARK(_T('>'), autolink_end-1, autolink_end, + MD_MARK_CLOSER | MD_MARK_RESOLVED | MD_MARK_AUTOLINK); + ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1; + ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2; + off = autolink_end; + continue; + } + + off++; + continue; + } + + /* A potential link or its part. */ + if(ch == _T('[') || (ch == _T('!') && off+1 < line_end && CH(off+1) == _T('['))) { + OFF tmp = (ch == _T('[') ? off+1 : off+2); + PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER); + off = tmp; + /* Two dummies to make enough place for data we need if it is + * a link. */ + PUSH_MARK('D', off, off, 0); + PUSH_MARK('D', off, off, 0); + continue; + } + if(ch == _T(']')) { + PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_CLOSER); + off++; + continue; + } + + /* A potential permissive e-mail autolink. */ + if(ch == _T('@')) { + if(line->beg + 1 <= off && ISALNUM(off-1) && + off + 3 < line->end && ISALNUM(off+1)) + { + PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER); + /* Push a dummy as a reserve for a closer. */ + PUSH_MARK('D', off, off, 0); + } + + off++; + continue; + } + + /* A potential permissive URL autolink. */ + if(ch == _T(':')) { + static struct { + const CHAR* scheme; + SZ scheme_size; + const CHAR* suffix; + SZ suffix_size; + } scheme_map[] = { + /* In the order from the most frequently used, arguably. */ + { _T("http"), 4, _T("//"), 2 }, + { _T("https"), 5, _T("//"), 2 }, + { _T("ftp"), 3, _T("//"), 2 } + }; + int scheme_index; + + for(scheme_index = 0; scheme_index < (int) SIZEOF_ARRAY(scheme_map); scheme_index++) { + const CHAR* scheme = scheme_map[scheme_index].scheme; + const SZ scheme_size = scheme_map[scheme_index].scheme_size; + const CHAR* suffix = scheme_map[scheme_index].suffix; + const SZ suffix_size = scheme_map[scheme_index].suffix_size; + + if(line->beg + scheme_size <= off && md_ascii_eq(STR(off-scheme_size), scheme, scheme_size) && + (line->beg + scheme_size == off || ISWHITESPACE(off-scheme_size-1) || ISANYOF(off-scheme_size-1, _T("*_~(["))) && + off + 1 + suffix_size < line->end && md_ascii_eq(STR(off+1), suffix, suffix_size)) + { + PUSH_MARK(ch, off-scheme_size, off+1+suffix_size, MD_MARK_POTENTIAL_OPENER); + /* Push a dummy as a reserve for a closer. */ + PUSH_MARK('D', off, off, 0); + off += 1 + suffix_size; + break; + } + } + + off++; + continue; + } + + /* A potential permissive WWW autolink. */ + if(ch == _T('.')) { + if(line->beg + 3 <= off && md_ascii_eq(STR(off-3), _T("www"), 3) && + (line->beg + 3 == off || ISWHITESPACE(off-4) || ISANYOF(off-4, _T("*_~(["))) && + off + 1 < line_end) + { + PUSH_MARK(ch, off-3, off+1, MD_MARK_POTENTIAL_OPENER); + /* Push a dummy as a reserve for a closer. */ + PUSH_MARK('D', off, off, 0); + off++; + continue; + } + + off++; + continue; + } + + /* A potential table cell boundary or wiki link label delimiter. */ + if((table_mode || ctx->parser.flags & MD_FLAG_WIKILINKS) && ch == _T('|')) { + PUSH_MARK(ch, off, off+1, 0); + off++; + continue; + } + + /* A potential strikethrough start/end. */ + if(ch == _T('~')) { + OFF tmp = off+1; + + while(tmp < line_end && CH(tmp) == _T('~')) + tmp++; + + if(tmp - off < 3) { + unsigned flags = 0; + + if(tmp < line_end && !ISUNICODEWHITESPACE(tmp)) + flags |= MD_MARK_POTENTIAL_OPENER; + if(off > line->beg && !ISUNICODEWHITESPACEBEFORE(off)) + flags |= MD_MARK_POTENTIAL_CLOSER; + if(flags != 0) + PUSH_MARK(ch, off, tmp, flags); + } + + off = tmp; + continue; + } + + /* A potential equation start/end */ + if(ch == _T('$')) { + /* We can have at most two consecutive $ signs, + * where two dollar signs signify a display equation. */ + OFF tmp = off+1; + + while(tmp < line_end && CH(tmp) == _T('$')) + tmp++; + + if (tmp - off <= 2) + PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER | MD_MARK_POTENTIAL_CLOSER); + off = tmp; + continue; + } + + /* Turn non-trivial whitespace into single space. */ + if(ISWHITESPACE_(ch)) { + OFF tmp = off+1; + + while(tmp < line_end && ISWHITESPACE(tmp)) + tmp++; + + if(tmp - off > 1 || ch != _T(' ')) + PUSH_MARK(ch, off, tmp, MD_MARK_RESOLVED); + + off = tmp; + continue; + } + + /* NULL character. */ + if(ch == _T('\0')) { + PUSH_MARK(ch, off, off+1, MD_MARK_RESOLVED); + off++; + continue; + } + + off++; + } + } + + /* Add a dummy mark at the end of the mark vector to simplify + * process_inlines(). */ + PUSH_MARK(127, ctx->size, ctx->size, MD_MARK_RESOLVED); + +abort: + return ret; +} + +static void +md_analyze_bracket(MD_CTX* ctx, int mark_index) +{ + /* We cannot really resolve links here as for that we would need + * more context. E.g. a following pair of brackets (reference link), + * or enclosing pair of brackets (if the inner is the link, the outer + * one cannot be.) + * + * Therefore we here only construct a list of '[' ']' pairs ordered by + * position of the closer. This allows us to analyze what is or is not + * link in the right order, from inside to outside in case of nested + * brackets. + * + * The resolving itself is deferred to md_resolve_links(). + */ + + MD_MARK* mark = &ctx->marks[mark_index]; + + if(mark->flags & MD_MARK_POTENTIAL_OPENER) { + if(BRACKET_OPENERS.head != -1) + ctx->marks[BRACKET_OPENERS.tail].flags |= MD_MARK_HASNESTEDBRACKETS; + + md_mark_chain_append(ctx, &BRACKET_OPENERS, mark_index); + return; + } + + if(BRACKET_OPENERS.tail >= 0) { + /* Pop the opener from the chain. */ + int opener_index = BRACKET_OPENERS.tail; + MD_MARK* opener = &ctx->marks[opener_index]; + if(opener->prev >= 0) + ctx->marks[opener->prev].next = -1; + else + BRACKET_OPENERS.head = -1; + BRACKET_OPENERS.tail = opener->prev; + + /* Interconnect the opener and closer. */ + opener->next = mark_index; + mark->prev = opener_index; + + /* Add the pair into chain of potential links for md_resolve_links(). + * Note we misuse opener->prev for this as opener->next points to its + * closer. */ + if(ctx->unresolved_link_tail >= 0) + ctx->marks[ctx->unresolved_link_tail].prev = opener_index; + else + ctx->unresolved_link_head = opener_index; + ctx->unresolved_link_tail = opener_index; + opener->prev = -1; + } +} + +/* Forward declaration. */ +static void md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines, + int mark_beg, int mark_end); + +static int +md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +{ + int opener_index = ctx->unresolved_link_head; + OFF last_link_beg = 0; + OFF last_link_end = 0; + OFF last_img_beg = 0; + OFF last_img_end = 0; + + while(opener_index >= 0) { + MD_MARK* opener = &ctx->marks[opener_index]; + int closer_index = opener->next; + MD_MARK* closer = &ctx->marks[closer_index]; + int next_index = opener->prev; + MD_MARK* next_opener; + MD_MARK* next_closer; + MD_LINK_ATTR attr; + int is_link = FALSE; + + if(next_index >= 0) { + next_opener = &ctx->marks[next_index]; + next_closer = &ctx->marks[next_opener->next]; + } else { + next_opener = NULL; + next_closer = NULL; + } + + /* If nested ("[ [ ] ]"), we need to make sure that: + * - The outer does not end inside of (...) belonging to the inner. + * - The outer cannot be link if the inner is link (i.e. not image). + * + * (Note we here analyze from inner to outer as the marks are ordered + * by closer->beg.) + */ + if((opener->beg < last_link_beg && closer->end < last_link_end) || + (opener->beg < last_img_beg && closer->end < last_img_end) || + (opener->beg < last_link_end && opener->ch == '[')) + { + opener_index = next_index; + continue; + } + + /* Recognize and resolve wiki links. + * Wiki-links maybe '[[destination]]' or '[[destination|label]]'. + */ + if ((ctx->parser.flags & MD_FLAG_WIKILINKS) && + (opener->end - opener->beg == 1) && /* not image */ + next_opener != NULL && /* double '[' opener */ + next_opener->ch == '[' && + (next_opener->beg == opener->beg - 1) && + (next_opener->end - next_opener->beg == 1) && + next_closer != NULL && /* double ']' closer */ + next_closer->ch == ']' && + (next_closer->beg == closer->beg + 1) && + (next_closer->end - next_closer->beg == 1)) + { + MD_MARK* delim = NULL; + int delim_index; + OFF dest_beg, dest_end; + + is_link = TRUE; + + /* We don't allow destination to be longer than 100 characters. + * Lets scan to see whether there is '|'. (If not then the whole + * wiki-link has to be below the 100 characters.) */ + delim_index = opener_index + 1; + while(delim_index < closer_index) { + MD_MARK* m = &ctx->marks[delim_index]; + if(m->ch == '|') { + delim = m; + break; + } + if(m->ch != 'D' && m->beg - opener->end > 100) + break; + delim_index++; + } + dest_beg = opener->end; + dest_end = (delim != NULL) ? delim->beg : closer->beg; + if(dest_end - dest_beg == 0 || dest_end - dest_beg > 100) + is_link = FALSE; + + /* There may not be any new line in the destination. */ + if(is_link) { + OFF off; + for(off = dest_beg; off < dest_end; off++) { + if(ISNEWLINE(off)) { + is_link = FALSE; + break; + } + } + } + + if(is_link) { + if(delim != NULL) { + if(delim->end < closer->beg) { + md_rollback(ctx, opener_index, delim_index, MD_ROLLBACK_ALL); + md_rollback(ctx, delim_index, closer_index, MD_ROLLBACK_CROSSING); + delim->flags |= MD_MARK_RESOLVED; + opener->end = delim->beg; + } else { + /* The pipe is just before the closer: [[foo|]] */ + md_rollback(ctx, opener_index, closer_index, MD_ROLLBACK_ALL); + closer->beg = delim->beg; + delim = NULL; + } + } + + opener->beg = next_opener->beg; + opener->next = closer_index; + opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED; + + closer->end = next_closer->end; + closer->prev = opener_index; + closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED; + + last_link_beg = opener->beg; + last_link_end = closer->end; + + if(delim != NULL) + md_analyze_link_contents(ctx, lines, n_lines, delim_index+1, closer_index); + + opener_index = next_opener->prev; + continue; + } + } + + if(next_opener != NULL && next_opener->beg == closer->end) { + if(next_closer->beg > closer->end + 1) { + /* Might be full reference link. */ + if(!(next_opener->flags & MD_MARK_HASNESTEDBRACKETS)) + is_link = md_is_link_reference(ctx, lines, n_lines, next_opener->beg, next_closer->end, &attr); + } else { + /* Might be shortcut reference link. */ + if(!(opener->flags & MD_MARK_HASNESTEDBRACKETS)) + is_link = md_is_link_reference(ctx, lines, n_lines, opener->beg, closer->end, &attr); + } + + if(is_link < 0) + return -1; + + if(is_link) { + /* Eat the 2nd "[...]". */ + closer->end = next_closer->end; + + /* Do not analyze the label as a standalone link in the next + * iteration. */ + next_index = ctx->marks[next_index].prev; + } + } else { + if(closer->end < ctx->size && CH(closer->end) == _T('(')) { + /* Might be inline link. */ + OFF inline_link_end = UINT_MAX; + + is_link = md_is_inline_link_spec(ctx, lines, n_lines, closer->end, &inline_link_end, &attr); + if(is_link < 0) + return -1; + + /* Check the closing ')' is not inside an already resolved range + * (i.e. a range with a higher priority), e.g. a code span. */ + if(is_link) { + int i = closer_index + 1; + + while(i < ctx->n_marks) { + MD_MARK* mark = &ctx->marks[i]; + + if(mark->beg >= inline_link_end) + break; + if((mark->flags & (MD_MARK_OPENER | MD_MARK_RESOLVED)) == (MD_MARK_OPENER | MD_MARK_RESOLVED)) { + if(ctx->marks[mark->next].beg >= inline_link_end) { + /* Cancel the link status. */ + if(attr.title_needs_free) + free(attr.title); + is_link = FALSE; + break; + } + + i = mark->next + 1; + } else { + i++; + } + } + } + + if(is_link) { + /* Eat the "(...)" */ + closer->end = inline_link_end; + } + } + + if(!is_link) { + /* Might be collapsed reference link. */ + if(!(opener->flags & MD_MARK_HASNESTEDBRACKETS)) + is_link = md_is_link_reference(ctx, lines, n_lines, opener->beg, closer->end, &attr); + if(is_link < 0) + return -1; + } + } + + if(is_link) { + /* Resolve the brackets as a link. */ + opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED; + closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED; + + /* If it is a link, we store the destination and title in the two + * dummy marks after the opener. */ + MD_ASSERT(ctx->marks[opener_index+1].ch == 'D'); + ctx->marks[opener_index+1].beg = attr.dest_beg; + ctx->marks[opener_index+1].end = attr.dest_end; + + MD_ASSERT(ctx->marks[opener_index+2].ch == 'D'); + md_mark_store_ptr(ctx, opener_index+2, attr.title); + /* The title might or might not have been allocated for us. */ + if(attr.title_needs_free) + md_mark_chain_append(ctx, &PTR_CHAIN, opener_index+2); + ctx->marks[opener_index+2].prev = attr.title_size; + + if(opener->ch == '[') { + last_link_beg = opener->beg; + last_link_end = closer->end; + } else { + last_img_beg = opener->beg; + last_img_end = closer->end; + } + + md_analyze_link_contents(ctx, lines, n_lines, opener_index+1, closer_index); + + /* If the link text is formed by nothing but permissive autolink, + * suppress the autolink. + * See https://github.com/mity/md4c/issues/152 for more info. */ + if(ctx->parser.flags & MD_FLAG_PERMISSIVEAUTOLINKS) { + MD_MARK* first_nested; + MD_MARK* last_nested; + + first_nested = opener + 1; + while(first_nested->ch == _T('D') && first_nested < closer) + first_nested++; + + last_nested = closer - 1; + while(first_nested->ch == _T('D') && last_nested > opener) + last_nested--; + + if((first_nested->flags & MD_MARK_RESOLVED) && + first_nested->beg == opener->end && + ISANYOF_(first_nested->ch, _T("@:.")) && + first_nested->next == (last_nested - ctx->marks) && + last_nested->end == closer->beg) + { + first_nested->ch = _T('D'); + first_nested->flags &= ~MD_MARK_RESOLVED; + last_nested->ch = _T('D'); + last_nested->flags &= ~MD_MARK_RESOLVED; + } + } + } + + opener_index = next_index; + } + + return 0; +} + +/* Analyze whether the mark '&' starts a HTML entity. + * If so, update its flags as well as flags of corresponding closer ';'. */ +static void +md_analyze_entity(MD_CTX* ctx, int mark_index) +{ + MD_MARK* opener = &ctx->marks[mark_index]; + MD_MARK* closer; + OFF off; + + /* Cannot be entity if there is no closer as the next mark. + * (Any other mark between would mean strange character which cannot be + * part of the entity. + * + * So we can do all the work on '&' and do not call this later for the + * closing mark ';'. + */ + if(mark_index + 1 >= ctx->n_marks) + return; + closer = &ctx->marks[mark_index+1]; + if(closer->ch != ';') + return; + + if(md_is_entity(ctx, opener->beg, closer->end, &off)) { + MD_ASSERT(off == closer->end); + + md_resolve_range(ctx, NULL, mark_index, mark_index+1); + opener->end = closer->end; + } +} + +static void +md_analyze_table_cell_boundary(MD_CTX* ctx, int mark_index) +{ + MD_MARK* mark = &ctx->marks[mark_index]; + mark->flags |= MD_MARK_RESOLVED; + + md_mark_chain_append(ctx, &TABLECELLBOUNDARIES, mark_index); + ctx->n_table_cell_boundaries++; +} + +/* Split a longer mark into two. The new mark takes the given count of + * characters. May only be called if an adequate number of dummy 'D' marks + * follows. + */ +static int +md_split_emph_mark(MD_CTX* ctx, int mark_index, SZ n) +{ + MD_MARK* mark = &ctx->marks[mark_index]; + int new_mark_index = mark_index + (mark->end - mark->beg - n); + MD_MARK* dummy = &ctx->marks[new_mark_index]; + + MD_ASSERT(mark->end - mark->beg > n); + MD_ASSERT(dummy->ch == 'D'); + + memcpy(dummy, mark, sizeof(MD_MARK)); + mark->end -= n; + dummy->beg = mark->end; + + return new_mark_index; +} + +static void +md_analyze_emph(MD_CTX* ctx, int mark_index) +{ + MD_MARK* mark = &ctx->marks[mark_index]; + MD_MARKCHAIN* chain = md_mark_chain(ctx, mark_index); + + /* If we can be a closer, try to resolve with the preceding opener. */ + if(mark->flags & MD_MARK_POTENTIAL_CLOSER) { + MD_MARK* opener = NULL; + int opener_index = 0; + + if(mark->ch == _T('*')) { + MD_MARKCHAIN* opener_chains[6]; + int i, n_opener_chains; + unsigned flags = mark->flags; + + /* Apply the "rule of three". */ + n_opener_chains = 0; + opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_0; + if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2) + opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_1; + if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_1) + opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_2; + opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_0; + if(!(flags & MD_MARK_EMPH_INTRAWORD) || (flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2) + opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_1; + if(!(flags & MD_MARK_EMPH_INTRAWORD) || (flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_1) + opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_2; + + /* Opener is the most recent mark from the allowed chains. */ + for(i = 0; i < n_opener_chains; i++) { + if(opener_chains[i]->tail >= 0) { + int tmp_index = opener_chains[i]->tail; + MD_MARK* tmp_mark = &ctx->marks[tmp_index]; + if(opener == NULL || tmp_mark->end > opener->end) { + opener_index = tmp_index; + opener = tmp_mark; + } + } + } + } else { + /* Simple emph. mark */ + if(chain->tail >= 0) { + opener_index = chain->tail; + opener = &ctx->marks[opener_index]; + } + } + + /* Resolve, if we have found matching opener. */ + if(opener != NULL) { + SZ opener_size = opener->end - opener->beg; + SZ closer_size = mark->end - mark->beg; + MD_MARKCHAIN* opener_chain = md_mark_chain(ctx, opener_index); + + if(opener_size > closer_size) { + opener_index = md_split_emph_mark(ctx, opener_index, closer_size); + md_mark_chain_append(ctx, opener_chain, opener_index); + } else if(opener_size < closer_size) { + md_split_emph_mark(ctx, mark_index, closer_size - opener_size); + } + + md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING); + md_resolve_range(ctx, opener_chain, opener_index, mark_index); + return; + } + } + + /* If we could not resolve as closer, we may be yet be an opener. */ + if(mark->flags & MD_MARK_POTENTIAL_OPENER) + md_mark_chain_append(ctx, chain, mark_index); +} + +static void +md_analyze_tilde(MD_CTX* ctx, int mark_index) +{ + MD_MARK* mark = &ctx->marks[mark_index]; + MD_MARKCHAIN* chain = md_mark_chain(ctx, mark_index); + + /* We attempt to be Github Flavored Markdown compatible here. GFM accepts + * only tildes sequences of length 1 and 2, and the length of the opener + * and closer has to match. */ + + if((mark->flags & MD_MARK_POTENTIAL_CLOSER) && chain->head >= 0) { + int opener_index = chain->head; + + md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING); + md_resolve_range(ctx, chain, opener_index, mark_index); + return; + } + + if(mark->flags & MD_MARK_POTENTIAL_OPENER) + md_mark_chain_append(ctx, chain, mark_index); +} + +static void +md_analyze_dollar(MD_CTX* ctx, int mark_index) +{ + /* This should mimic the way inline equations work in LaTeX, so there + * can only ever be one item in the chain (i.e. the dollars can't be + * nested). This is basically the same as the md_analyze_tilde function, + * except that we require matching openers and closers to be of the same + * length. + * + * E.g.: $abc$$def$$ => abc (display equation) def (end equation) */ + if(DOLLAR_OPENERS.head >= 0) { + /* If the potential closer has a non-matching number of $, discard */ + MD_MARK* open = &ctx->marks[DOLLAR_OPENERS.head]; + MD_MARK* close = &ctx->marks[mark_index]; + + int opener_index = DOLLAR_OPENERS.head; + md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_ALL); + if (open->end - open->beg == close->end - close->beg) { + /* We are the matching closer */ + md_resolve_range(ctx, &DOLLAR_OPENERS, opener_index, mark_index); + return; + } + } + + md_mark_chain_append(ctx, &DOLLAR_OPENERS, mark_index); +} + +static void +md_analyze_permissive_url_autolink(MD_CTX* ctx, int mark_index) +{ + MD_MARK* opener = &ctx->marks[mark_index]; + int closer_index = mark_index + 1; + MD_MARK* closer = &ctx->marks[closer_index]; + MD_MARK* next_resolved_mark; + OFF off = opener->end; + int n_dots = FALSE; + int has_underscore_in_last_seg = FALSE; + int has_underscore_in_next_to_last_seg = FALSE; + int n_opened_parenthesis = 0; + int n_excess_parenthesis = 0; + + /* Check for domain. */ + while(off < ctx->size) { + if(ISALNUM(off) || CH(off) == _T('-')) { + off++; + } else if(CH(off) == _T('.')) { + /* We must see at least one period. */ + n_dots++; + has_underscore_in_next_to_last_seg = has_underscore_in_last_seg; + has_underscore_in_last_seg = FALSE; + off++; + } else if(CH(off) == _T('_')) { + /* No underscore may be present in the last two domain segments. */ + has_underscore_in_last_seg = TRUE; + off++; + } else { + break; + } + } + if(off > opener->end && CH(off-1) == _T('.')) { + off--; + n_dots--; + } + if(off <= opener->end || n_dots == 0 || has_underscore_in_next_to_last_seg || has_underscore_in_last_seg) + return; + + /* Check for path. */ + next_resolved_mark = closer + 1; + while(next_resolved_mark->ch == 'D' || !(next_resolved_mark->flags & MD_MARK_RESOLVED)) + next_resolved_mark++; + while(off < next_resolved_mark->beg && CH(off) != _T('<') && !ISWHITESPACE(off) && !ISNEWLINE(off)) { + /* Parenthesis must be balanced. */ + if(CH(off) == _T('(')) { + n_opened_parenthesis++; + } else if(CH(off) == _T(')')) { + if(n_opened_parenthesis > 0) + n_opened_parenthesis--; + else + n_excess_parenthesis++; + } + + off++; + } + + /* Trim a trailing punctuation from the end. */ + while(TRUE) { + if(ISANYOF(off-1, _T("?!.,:*_~"))) { + off--; + } else if(CH(off-1) == ')' && n_excess_parenthesis > 0) { + /* Unmatched ')' can be in an interior of the path but not at the + * of it, so the auto-link may be safely nested in a parenthesis + * pair. */ + off--; + n_excess_parenthesis--; + } else { + break; + } + } + + /* Ok. Lets call it an auto-link. Adapt opener and create closer to zero + * length so all the contents becomes the link text. */ + MD_ASSERT(closer->ch == 'D' || + ((ctx->parser.flags & MD_FLAG_PERMISSIVEWWWAUTOLINKS) && + (closer->ch == '.' || closer->ch == ':' || closer->ch == '@'))); + opener->end = opener->beg; + closer->ch = opener->ch; + closer->beg = off; + closer->end = off; + md_resolve_range(ctx, NULL, mark_index, closer_index); +} + +/* The permissive autolinks do not have to be enclosed in '<' '>' but we + * instead impose stricter rules what is understood as an e-mail address + * here. Actually any non-alphanumeric characters with exception of '.' + * are prohibited both in username and after '@'. */ +static void +md_analyze_permissive_email_autolink(MD_CTX* ctx, int mark_index) +{ + MD_MARK* opener = &ctx->marks[mark_index]; + int closer_index; + MD_MARK* closer; + OFF beg = opener->beg; + OFF end = opener->end; + int dot_count = 0; + + MD_ASSERT(opener->ch == _T('@')); + + /* Scan for name before '@'. */ + while(beg > 0 && (ISALNUM(beg-1) || ISANYOF(beg-1, _T(".-_+")))) + beg--; + + /* Scan for domain after '@'. */ + while(end < ctx->size && (ISALNUM(end) || ISANYOF(end, _T(".-_")))) { + if(CH(end) == _T('.')) + dot_count++; + end++; + } + if(CH(end-1) == _T('.')) { /* Final '.' not part of it. */ + dot_count--; + end--; + } + else if(ISANYOF2(end-1, _T('-'), _T('_'))) /* These are forbidden at the end. */ + return; + if(CH(end-1) == _T('@') || dot_count == 0) + return; + + /* Ok. Lets call it auto-link. Adapt opener and create closer to zero + * length so all the contents becomes the link text. */ + closer_index = mark_index + 1; + closer = &ctx->marks[closer_index]; + if (closer->ch != 'D') return; + + opener->beg = beg; + opener->end = beg; + closer->ch = opener->ch; + closer->beg = end; + closer->end = end; + md_resolve_range(ctx, NULL, mark_index, closer_index); +} + +static inline void +md_analyze_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, + int mark_beg, int mark_end, const CHAR* mark_chars) +{ + int i = mark_beg; + MD_UNUSED(lines); + MD_UNUSED(n_lines); + + while(i < mark_end) { + MD_MARK* mark = &ctx->marks[i]; + + /* Skip resolved spans. */ + if(mark->flags & MD_MARK_RESOLVED) { + if(mark->flags & MD_MARK_OPENER) { + MD_ASSERT(i < mark->next); + i = mark->next + 1; + } else { + i++; + } + continue; + } + + /* Skip marks we do not want to deal with. */ + if(!ISANYOF_(mark->ch, mark_chars)) { + i++; + continue; + } + + /* Analyze the mark. */ + switch(mark->ch) { + case '[': /* Pass through. */ + case '!': /* Pass through. */ + case ']': md_analyze_bracket(ctx, i); break; + case '&': md_analyze_entity(ctx, i); break; + case '|': md_analyze_table_cell_boundary(ctx, i); break; + case '_': /* Pass through. */ + case '*': md_analyze_emph(ctx, i); break; + case '~': md_analyze_tilde(ctx, i); break; + case '$': md_analyze_dollar(ctx, i); break; + case '.': /* Pass through. */ + case ':': md_analyze_permissive_url_autolink(ctx, i); break; + case '@': md_analyze_permissive_email_autolink(ctx, i); break; + } + + i++; + } +} + +/* Analyze marks (build ctx->marks). */ +static int +md_analyze_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) +{ + int ret; + + /* Reset the previously collected stack of marks. */ + ctx->n_marks = 0; + + /* Collect all marks. */ + MD_CHECK(md_collect_marks(ctx, lines, n_lines, table_mode)); + + /* (1) Links. */ + md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("[]!")); + MD_CHECK(md_resolve_links(ctx, lines, n_lines)); + BRACKET_OPENERS.head = -1; + BRACKET_OPENERS.tail = -1; + ctx->unresolved_link_head = -1; + ctx->unresolved_link_tail = -1; + + if(table_mode) { + /* (2) Analyze table cell boundaries. + * Note we reset TABLECELLBOUNDARIES chain prior to the call md_analyze_marks(), + * not after, because caller may need it. */ + MD_ASSERT(n_lines == 1); + TABLECELLBOUNDARIES.head = -1; + TABLECELLBOUNDARIES.tail = -1; + ctx->n_table_cell_boundaries = 0; + md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("|")); + return ret; + } + + /* (3) Emphasis and strong emphasis; permissive autolinks. */ + md_analyze_link_contents(ctx, lines, n_lines, 0, ctx->n_marks); + +abort: + return ret; +} + +static void +md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines, + int mark_beg, int mark_end) +{ + int i; + + md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("&")); + md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("*_~$@:.")); + + for(i = OPENERS_CHAIN_FIRST; i <= OPENERS_CHAIN_LAST; i++) { + ctx->mark_chains[i].head = -1; + ctx->mark_chains[i].tail = -1; + } +} + +static int +md_enter_leave_span_a(MD_CTX* ctx, int enter, MD_SPANTYPE type, + const CHAR* dest, SZ dest_size, int prohibit_escapes_in_dest, + const CHAR* title, SZ title_size) +{ + MD_ATTRIBUTE_BUILD href_build = { 0 }; + MD_ATTRIBUTE_BUILD title_build = { 0 }; + MD_SPAN_A_DETAIL det; + int ret = 0; + + /* Note we here rely on fact that MD_SPAN_A_DETAIL and + * MD_SPAN_IMG_DETAIL are binary-compatible. */ + memset(&det, 0, sizeof(MD_SPAN_A_DETAIL)); + MD_CHECK(md_build_attribute(ctx, dest, dest_size, + (prohibit_escapes_in_dest ? MD_BUILD_ATTR_NO_ESCAPES : 0), + &det.href, &href_build)); + MD_CHECK(md_build_attribute(ctx, title, title_size, 0, &det.title, &title_build)); + + if(enter) + MD_ENTER_SPAN(type, &det); + else + MD_LEAVE_SPAN(type, &det); + +abort: + md_free_attribute(ctx, &href_build); + md_free_attribute(ctx, &title_build); + return ret; +} + +static int +md_enter_leave_span_wikilink(MD_CTX* ctx, int enter, const CHAR* target, SZ target_size) +{ + MD_ATTRIBUTE_BUILD target_build = { 0 }; + MD_SPAN_WIKILINK_DETAIL det; + int ret = 0; + + memset(&det, 0, sizeof(MD_SPAN_WIKILINK_DETAIL)); + MD_CHECK(md_build_attribute(ctx, target, target_size, 0, &det.target, &target_build)); + + if (enter) + MD_ENTER_SPAN(MD_SPAN_WIKILINK, &det); + else + MD_LEAVE_SPAN(MD_SPAN_WIKILINK, &det); + +abort: + md_free_attribute(ctx, &target_build); + return ret; +} + + +/* Render the output, accordingly to the analyzed ctx->marks. */ +static int +md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +{ + MD_TEXTTYPE text_type; + const MD_LINE* line = lines; + MD_MARK* prev_mark = NULL; + MD_MARK* mark; + OFF off = lines[0].beg; + OFF end = lines[n_lines-1].end; + int enforce_hardbreak = 0; + int ret = 0; + + /* Find first resolved mark. Note there is always at least one resolved + * mark, the dummy last one after the end of the latest line we actually + * never really reach. This saves us of a lot of special checks and cases + * in this function. */ + mark = ctx->marks; + while(!(mark->flags & MD_MARK_RESOLVED)) + mark++; + + text_type = MD_TEXT_NORMAL; + + while(1) { + /* Process the text up to the next mark or end-of-line. */ + OFF tmp = (line->end < mark->beg ? line->end : mark->beg); + if(tmp > off) { + MD_TEXT(text_type, STR(off), tmp - off); + off = tmp; + } + + /* If reached the mark, process it and move to next one. */ + if(off >= mark->beg) { + switch(mark->ch) { + case '\\': /* Backslash escape. */ + if(ISNEWLINE(mark->beg+1)) + enforce_hardbreak = 1; + else + MD_TEXT(text_type, STR(mark->beg+1), 1); + break; + + case ' ': /* Non-trivial space. */ + MD_TEXT(text_type, _T(" "), 1); + break; + + case '`': /* Code span. */ + if(mark->flags & MD_MARK_OPENER) { + MD_ENTER_SPAN(MD_SPAN_CODE, NULL); + text_type = MD_TEXT_CODE; + } else { + MD_LEAVE_SPAN(MD_SPAN_CODE, NULL); + text_type = MD_TEXT_NORMAL; + } + break; + + case '_': /* Underline (or emphasis if we fall through). */ + if(ctx->parser.flags & MD_FLAG_UNDERLINE) { + if(mark->flags & MD_MARK_OPENER) { + while(off < mark->end) { + MD_ENTER_SPAN(MD_SPAN_U, NULL); + off++; + } + } else { + while(off < mark->end) { + MD_LEAVE_SPAN(MD_SPAN_U, NULL); + off++; + } + } + break; + } + MD_FALLTHROUGH(); + + case '*': /* Emphasis, strong emphasis. */ + if(mark->flags & MD_MARK_OPENER) { + if((mark->end - off) % 2) { + MD_ENTER_SPAN(MD_SPAN_EM, NULL); + off++; + } + while(off + 1 < mark->end) { + MD_ENTER_SPAN(MD_SPAN_STRONG, NULL); + off += 2; + } + } else { + while(off + 1 < mark->end) { + MD_LEAVE_SPAN(MD_SPAN_STRONG, NULL); + off += 2; + } + if((mark->end - off) % 2) { + MD_LEAVE_SPAN(MD_SPAN_EM, NULL); + off++; + } + } + break; + + case '~': + if(mark->flags & MD_MARK_OPENER) + MD_ENTER_SPAN(MD_SPAN_DEL, NULL); + else + MD_LEAVE_SPAN(MD_SPAN_DEL, NULL); + break; + + case '$': + if(mark->flags & MD_MARK_OPENER) { + MD_ENTER_SPAN((mark->end - off) % 2 ? MD_SPAN_LATEXMATH : MD_SPAN_LATEXMATH_DISPLAY, NULL); + text_type = MD_TEXT_LATEXMATH; + } else { + MD_LEAVE_SPAN((mark->end - off) % 2 ? MD_SPAN_LATEXMATH : MD_SPAN_LATEXMATH_DISPLAY, NULL); + text_type = MD_TEXT_NORMAL; + } + break; + + case '[': /* Link, wiki link, image. */ + case '!': + case ']': + { + const MD_MARK* opener = (mark->ch != ']' ? mark : &ctx->marks[mark->prev]); + const MD_MARK* closer = &ctx->marks[opener->next]; + const MD_MARK* dest_mark; + const MD_MARK* title_mark; + + if ((opener->ch == '[' && closer->ch == ']') && + opener->end - opener->beg >= 2 && + closer->end - closer->beg >= 2) + { + int has_label = (opener->end - opener->beg > 2); + SZ target_sz; + + if(has_label) + target_sz = opener->end - (opener->beg+2); + else + target_sz = closer->beg - opener->end; + + MD_CHECK(md_enter_leave_span_wikilink(ctx, (mark->ch != ']'), + has_label ? STR(opener->beg+2) : STR(opener->end), + target_sz)); + + break; + } + + dest_mark = opener+1; + MD_ASSERT(dest_mark->ch == 'D'); + title_mark = opener+2; + if (title_mark->ch != 'D') break; + + MD_CHECK(md_enter_leave_span_a(ctx, (mark->ch != ']'), + (opener->ch == '!' ? MD_SPAN_IMG : MD_SPAN_A), + STR(dest_mark->beg), dest_mark->end - dest_mark->beg, FALSE, + md_mark_get_ptr(ctx, (int)(title_mark - ctx->marks)), + title_mark->prev)); + + /* link/image closer may span multiple lines. */ + if(mark->ch == ']') { + while(mark->end > line->end) + line++; + } + + break; + } + + case '<': + case '>': /* Autolink or raw HTML. */ + if(!(mark->flags & MD_MARK_AUTOLINK)) { + /* Raw HTML. */ + if(mark->flags & MD_MARK_OPENER) + text_type = MD_TEXT_HTML; + else + text_type = MD_TEXT_NORMAL; + break; + } + /* Pass through, if auto-link. */ + MD_FALLTHROUGH(); + + case '@': /* Permissive e-mail autolink. */ + case ':': /* Permissive URL autolink. */ + case '.': /* Permissive WWW autolink. */ + { + MD_MARK* opener = ((mark->flags & MD_MARK_OPENER) ? mark : &ctx->marks[mark->prev]); + MD_MARK* closer = &ctx->marks[opener->next]; + const CHAR* dest = STR(opener->end); + SZ dest_size = closer->beg - opener->end; + + /* For permissive auto-links we do not know closer mark + * position at the time of md_collect_marks(), therefore + * it can be out-of-order in ctx->marks[]. + * + * With this flag, we make sure that we output the closer + * only if we processed the opener. */ + if(mark->flags & MD_MARK_OPENER) + closer->flags |= MD_MARK_VALIDPERMISSIVEAUTOLINK; + + if(opener->ch == '@' || opener->ch == '.') { + dest_size += 7; + MD_TEMP_BUFFER(dest_size * sizeof(CHAR)); + memcpy(ctx->buffer, + (opener->ch == '@' ? _T("mailto:") : _T("http://")), + 7 * sizeof(CHAR)); + memcpy(ctx->buffer + 7, dest, (dest_size-7) * sizeof(CHAR)); + dest = ctx->buffer; + } + + if(closer->flags & MD_MARK_VALIDPERMISSIVEAUTOLINK) + MD_CHECK(md_enter_leave_span_a(ctx, (mark->flags & MD_MARK_OPENER), + MD_SPAN_A, dest, dest_size, TRUE, NULL, 0)); + break; + } + + case '&': /* Entity. */ + MD_TEXT(MD_TEXT_ENTITY, STR(mark->beg), mark->end - mark->beg); + break; + + case '\0': + MD_TEXT(MD_TEXT_NULLCHAR, _T(""), 1); + break; + + case 127: + goto abort; + } + + off = mark->end; + + /* Move to next resolved mark. */ + prev_mark = mark; + mark++; + while(!(mark->flags & MD_MARK_RESOLVED) || mark->beg < off) + mark++; + } + + /* If reached end of line, move to next one. */ + if(off >= line->end) { + /* If it is the last line, we are done. */ + if(off >= end) + break; + + if(text_type == MD_TEXT_CODE || text_type == MD_TEXT_LATEXMATH) { + OFF tmp; + + MD_ASSERT(prev_mark != NULL); + MD_ASSERT(ISANYOF2_(prev_mark->ch, '`', '$') && (prev_mark->flags & MD_MARK_OPENER)); + MD_ASSERT(ISANYOF2_(mark->ch, '`', '$') && (mark->flags & MD_MARK_CLOSER)); + + /* Inside a code span, trailing line whitespace has to be + * outputted. */ + tmp = off; + while(off < ctx->size && ISBLANK(off)) + off++; + if(off > tmp) + MD_TEXT(text_type, STR(tmp), off-tmp); + + /* and new lines are transformed into single spaces. */ + if(prev_mark->end < off && off < mark->beg) + MD_TEXT(text_type, _T(" "), 1); + } else if(text_type == MD_TEXT_HTML) { + /* Inside raw HTML, we output the new line verbatim, including + * any trailing spaces. */ + OFF tmp = off; + + while(tmp < end && ISBLANK(tmp)) + tmp++; + if(tmp > off) + MD_TEXT(MD_TEXT_HTML, STR(off), tmp - off); + MD_TEXT(MD_TEXT_HTML, _T("\n"), 1); + } else { + /* Output soft or hard line break. */ + MD_TEXTTYPE break_type = MD_TEXT_SOFTBR; + + if(text_type == MD_TEXT_NORMAL) { + if(enforce_hardbreak) + break_type = MD_TEXT_BR; + else if((CH(line->end) == _T(' ') && CH(line->end+1) == _T(' '))) + break_type = MD_TEXT_BR; + } + + MD_TEXT(break_type, _T("\n"), 1); + } + + /* Move to the next line. */ + line++; + off = line->beg; + + enforce_hardbreak = 0; + } + } + +abort: + return ret; +} + + +/*************************** + *** Processing Tables *** + ***************************/ + +static void +md_analyze_table_alignment(MD_CTX* ctx, OFF beg, OFF end, MD_ALIGN* align, int n_align) +{ + static const MD_ALIGN align_map[] = { MD_ALIGN_DEFAULT, MD_ALIGN_LEFT, MD_ALIGN_RIGHT, MD_ALIGN_CENTER }; + OFF off = beg; + + while(n_align > 0) { + int index = 0; /* index into align_map[] */ + + while(CH(off) != _T('-')) + off++; + if(off > beg && CH(off-1) == _T(':')) + index |= 1; + while(off < end && CH(off) == _T('-')) + off++; + if(off < end && CH(off) == _T(':')) + index |= 2; + + *align = align_map[index]; + align++; + n_align--; + } + +} + +/* Forward declaration. */ +static int md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines); + +static int +md_process_table_cell(MD_CTX* ctx, MD_BLOCKTYPE cell_type, MD_ALIGN align, OFF beg, OFF end) +{ + MD_LINE line; + MD_BLOCK_TD_DETAIL det; + int ret = 0; + + while(beg < end && ISWHITESPACE(beg)) + beg++; + while(end > beg && ISWHITESPACE(end-1)) + end--; + + det.align = align; + line.beg = beg; + line.end = end; + + MD_ENTER_BLOCK(cell_type, &det); + MD_CHECK(md_process_normal_block_contents(ctx, &line, 1)); + MD_LEAVE_BLOCK(cell_type, &det); + +abort: + return ret; +} + +static int +md_process_table_row(MD_CTX* ctx, MD_BLOCKTYPE cell_type, OFF beg, OFF end, + const MD_ALIGN* align, int col_count) +{ + MD_LINE line; + OFF* pipe_offs = NULL; + int i, j, k, n; + int ret = 0; + + line.beg = beg; + line.end = end; + + /* Break the line into table cells by identifying pipe characters who + * form the cell boundary. */ + MD_CHECK(md_analyze_inlines(ctx, &line, 1, TRUE)); + + /* We have to remember the cell boundaries in local buffer because + * ctx->marks[] shall be reused during cell contents processing. */ + n = ctx->n_table_cell_boundaries + 2; + pipe_offs = (OFF*) malloc(n * sizeof(OFF)); + if(pipe_offs == NULL) { + MD_LOG("malloc() failed."); + ret = -1; + goto abort; + } + j = 0; + pipe_offs[j++] = beg; + for(i = TABLECELLBOUNDARIES.head; i >= 0; i = ctx->marks[i].next) { + MD_MARK* mark = &ctx->marks[i]; + pipe_offs[j++] = mark->end; + } + pipe_offs[j++] = end+1; + + /* Process cells. */ + MD_ENTER_BLOCK(MD_BLOCK_TR, NULL); + k = 0; + for(i = 0; i < j-1 && k < col_count; i++) { + if(pipe_offs[i] < pipe_offs[i+1]-1) + MD_CHECK(md_process_table_cell(ctx, cell_type, align[k++], pipe_offs[i], pipe_offs[i+1]-1)); + } + /* Make sure we call enough table cells even if the current table contains + * too few of them. */ + while(k < col_count) + MD_CHECK(md_process_table_cell(ctx, cell_type, align[k++], 0, 0)); + MD_LEAVE_BLOCK(MD_BLOCK_TR, NULL); + +abort: + free(pipe_offs); + + /* Free any temporary memory blocks stored within some dummy marks. */ + for(i = PTR_CHAIN.head; i >= 0; i = ctx->marks[i].next) + free(md_mark_get_ptr(ctx, i)); + PTR_CHAIN.head = -1; + PTR_CHAIN.tail = -1; + + return ret; +} + +static int +md_process_table_block_contents(MD_CTX* ctx, int col_count, const MD_LINE* lines, int n_lines) +{ + MD_ALIGN* align; + int i; + int ret = 0; + + /* At least two lines have to be present: The column headers and the line + * with the underlines. */ + MD_ASSERT(n_lines >= 2); + + align = malloc(col_count * sizeof(MD_ALIGN)); + if(align == NULL) { + MD_LOG("malloc() failed."); + ret = -1; + goto abort; + } + + md_analyze_table_alignment(ctx, lines[1].beg, lines[1].end, align, col_count); + + MD_ENTER_BLOCK(MD_BLOCK_THEAD, NULL); + MD_CHECK(md_process_table_row(ctx, MD_BLOCK_TH, + lines[0].beg, lines[0].end, align, col_count)); + MD_LEAVE_BLOCK(MD_BLOCK_THEAD, NULL); + + if(n_lines > 2) { + MD_ENTER_BLOCK(MD_BLOCK_TBODY, NULL); + for(i = 2; i < n_lines; i++) { + MD_CHECK(md_process_table_row(ctx, MD_BLOCK_TD, + lines[i].beg, lines[i].end, align, col_count)); + } + MD_LEAVE_BLOCK(MD_BLOCK_TBODY, NULL); + } + +abort: + free(align); + return ret; +} + + +/************************** + *** Processing Block *** + **************************/ + +#define MD_BLOCK_CONTAINER_OPENER 0x01 +#define MD_BLOCK_CONTAINER_CLOSER 0x02 +#define MD_BLOCK_CONTAINER (MD_BLOCK_CONTAINER_OPENER | MD_BLOCK_CONTAINER_CLOSER) +#define MD_BLOCK_LOOSE_LIST 0x04 +#define MD_BLOCK_SETEXT_HEADER 0x08 + +struct MD_BLOCK_tag { + MD_BLOCKTYPE type : 8; + unsigned flags : 8; + + /* MD_BLOCK_H: Header level (1 - 6) + * MD_BLOCK_CODE: Non-zero if fenced, zero if indented. + * MD_BLOCK_LI: Task mark character (0 if not task list item, 'x', 'X' or ' '). + * MD_BLOCK_TABLE: Column count (as determined by the table underline). + */ + unsigned data : 16; + + /* Leaf blocks: Count of lines (MD_LINE or MD_VERBATIMLINE) on the block. + * MD_BLOCK_LI: Task mark offset in the input doc. + * MD_BLOCK_OL: Start item number. + */ + unsigned n_lines; +}; + +struct MD_CONTAINER_tag { + CHAR ch; + unsigned is_loose : 8; + unsigned is_task : 8; + unsigned start; + unsigned mark_indent; + unsigned contents_indent; + OFF block_byte_off; + OFF task_mark_off; +}; + + +static int +md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +{ + int i; + int ret; + + MD_CHECK(md_analyze_inlines(ctx, lines, n_lines, FALSE)); + MD_CHECK(md_process_inlines(ctx, lines, n_lines)); + +abort: + /* Free any temporary memory blocks stored within some dummy marks. */ + for(i = PTR_CHAIN.head; i >= 0; i = ctx->marks[i].next) + free(md_mark_get_ptr(ctx, i)); + PTR_CHAIN.head = -1; + PTR_CHAIN.tail = -1; + + return ret; +} + +static int +md_process_verbatim_block_contents(MD_CTX* ctx, MD_TEXTTYPE text_type, const MD_VERBATIMLINE* lines, int n_lines) +{ + static const CHAR indent_chunk_str[] = _T(" "); + static const SZ indent_chunk_size = SIZEOF_ARRAY(indent_chunk_str) - 1; + + int i; + int ret = 0; + + for(i = 0; i < n_lines; i++) { + const MD_VERBATIMLINE* line = &lines[i]; + int indent = line->indent; + + MD_ASSERT(indent >= 0); + + /* Output code indentation. */ + while(indent > (int) indent_chunk_size) { + MD_TEXT(text_type, indent_chunk_str, indent_chunk_size); + indent -= indent_chunk_size; + } + if(indent > 0) + MD_TEXT(text_type, indent_chunk_str, indent); + + /* Output the code line itself. */ + MD_TEXT_INSECURE(text_type, STR(line->beg), line->end - line->beg); + + /* Enforce end-of-line. */ + MD_TEXT(text_type, _T("\n"), 1); + } + +abort: + return ret; +} + +static int +md_process_code_block_contents(MD_CTX* ctx, int is_fenced, const MD_VERBATIMLINE* lines, int n_lines) +{ + if(is_fenced) { + /* Skip the first line in case of fenced code: It is the fence. + * (Only the starting fence is present due to logic in md_analyze_line().) */ + lines++; + n_lines--; + } else { + /* Ignore blank lines at start/end of indented code block. */ + while(n_lines > 0 && lines[0].beg == lines[0].end) { + lines++; + n_lines--; + } + while(n_lines > 0 && lines[n_lines-1].beg == lines[n_lines-1].end) { + n_lines--; + } + } + + if(n_lines == 0) + return 0; + + return md_process_verbatim_block_contents(ctx, MD_TEXT_CODE, lines, n_lines); +} + +static int +md_setup_fenced_code_detail(MD_CTX* ctx, const MD_BLOCK* block, MD_BLOCK_CODE_DETAIL* det, + MD_ATTRIBUTE_BUILD* info_build, MD_ATTRIBUTE_BUILD* lang_build) +{ + const MD_VERBATIMLINE* fence_line = (const MD_VERBATIMLINE*)(block + 1); + OFF beg = fence_line->beg; + OFF end = fence_line->end; + OFF lang_end; + CHAR fence_ch = CH(fence_line->beg); + int ret = 0; + + /* Skip the fence itself. */ + while(beg < ctx->size && CH(beg) == fence_ch) + beg++; + /* Trim initial spaces. */ + while(beg < ctx->size && CH(beg) == _T(' ')) + beg++; + + /* Trim trailing spaces. */ + while(end > beg && CH(end-1) == _T(' ')) + end--; + + /* Build info string attribute. */ + MD_CHECK(md_build_attribute(ctx, STR(beg), end - beg, 0, &det->info, info_build)); + + /* Build info string attribute. */ + lang_end = beg; + while(lang_end < end && !ISWHITESPACE(lang_end)) + lang_end++; + MD_CHECK(md_build_attribute(ctx, STR(beg), lang_end - beg, 0, &det->lang, lang_build)); + + det->fence_char = fence_ch; + +abort: + return ret; +} + +static int +md_process_leaf_block(MD_CTX* ctx, const MD_BLOCK* block) +{ + union { + MD_BLOCK_H_DETAIL header; + MD_BLOCK_CODE_DETAIL code; + MD_BLOCK_TABLE_DETAIL table; + } det; + MD_ATTRIBUTE_BUILD info_build; + MD_ATTRIBUTE_BUILD lang_build; + int is_in_tight_list; + int clean_fence_code_detail = FALSE; + int ret = 0; + + memset(&det, 0, sizeof(det)); + + if(ctx->n_containers == 0) + is_in_tight_list = FALSE; + else + is_in_tight_list = !ctx->containers[ctx->n_containers-1].is_loose; + + switch(block->type) { + case MD_BLOCK_H: + det.header.level = block->data; + break; + + case MD_BLOCK_CODE: + /* For fenced code block, we may need to set the info string. */ + if(block->data != 0) { + memset(&det.code, 0, sizeof(MD_BLOCK_CODE_DETAIL)); + clean_fence_code_detail = TRUE; + MD_CHECK(md_setup_fenced_code_detail(ctx, block, &det.code, &info_build, &lang_build)); + } + break; + + case MD_BLOCK_TABLE: + det.table.col_count = block->data; + det.table.head_row_count = 1; + det.table.body_row_count = block->n_lines - 2; + break; + + default: + /* Noop. */ + break; + } + + if(!is_in_tight_list || block->type != MD_BLOCK_P) + MD_ENTER_BLOCK(block->type, (void*) &det); + + /* Process the block contents accordingly to is type. */ + switch(block->type) { + case MD_BLOCK_HR: + /* noop */ + break; + + case MD_BLOCK_CODE: + MD_CHECK(md_process_code_block_contents(ctx, (block->data != 0), + (const MD_VERBATIMLINE*)(block + 1), block->n_lines)); + break; + + case MD_BLOCK_HTML: + MD_CHECK(md_process_verbatim_block_contents(ctx, MD_TEXT_HTML, + (const MD_VERBATIMLINE*)(block + 1), block->n_lines)); + break; + + case MD_BLOCK_TABLE: + MD_CHECK(md_process_table_block_contents(ctx, block->data, + (const MD_LINE*)(block + 1), block->n_lines)); + break; + + default: + MD_CHECK(md_process_normal_block_contents(ctx, + (const MD_LINE*)(block + 1), block->n_lines)); + break; + } + + if(!is_in_tight_list || block->type != MD_BLOCK_P) + MD_LEAVE_BLOCK(block->type, (void*) &det); + +abort: + if(clean_fence_code_detail) { + md_free_attribute(ctx, &info_build); + md_free_attribute(ctx, &lang_build); + } + return ret; +} + +static int +md_process_all_blocks(MD_CTX* ctx) +{ + int byte_off = 0; + int ret = 0; + + /* ctx->containers now is not needed for detection of lists and list items + * so we reuse it for tracking what lists are loose or tight. We rely + * on the fact the vector is large enough to hold the deepest nesting + * level of lists. */ + ctx->n_containers = 0; + + while(byte_off < ctx->n_block_bytes) { + MD_BLOCK* block = (MD_BLOCK*)((char*)ctx->block_bytes + byte_off); + union { + MD_BLOCK_UL_DETAIL ul; + MD_BLOCK_OL_DETAIL ol; + MD_BLOCK_LI_DETAIL li; + } det; + + switch(block->type) { + case MD_BLOCK_UL: + det.ul.is_tight = (block->flags & MD_BLOCK_LOOSE_LIST) ? FALSE : TRUE; + det.ul.mark = (CHAR) block->data; + break; + + case MD_BLOCK_OL: + det.ol.start = block->n_lines; + det.ol.is_tight = (block->flags & MD_BLOCK_LOOSE_LIST) ? FALSE : TRUE; + det.ol.mark_delimiter = (CHAR) block->data; + break; + + case MD_BLOCK_LI: + det.li.is_task = (block->data != 0); + det.li.task_mark = (CHAR) block->data; + det.li.task_mark_offset = (OFF) block->n_lines; + break; + + default: + /* noop */ + break; + } + + if(block->flags & MD_BLOCK_CONTAINER) { + if(block->flags & MD_BLOCK_CONTAINER_CLOSER) { + MD_LEAVE_BLOCK(block->type, &det); + + if(block->type == MD_BLOCK_UL || block->type == MD_BLOCK_OL || block->type == MD_BLOCK_QUOTE) + ctx->n_containers--; + } + + if(block->flags & MD_BLOCK_CONTAINER_OPENER) { + MD_ENTER_BLOCK(block->type, &det); + + if(block->type == MD_BLOCK_UL || block->type == MD_BLOCK_OL) { + ctx->containers[ctx->n_containers].is_loose = (block->flags & MD_BLOCK_LOOSE_LIST); + ctx->n_containers++; + } else if(block->type == MD_BLOCK_QUOTE) { + /* This causes that any text in a block quote, even if + * nested inside a tight list item, is wrapped with + *

...

. */ + ctx->containers[ctx->n_containers].is_loose = TRUE; + ctx->n_containers++; + } + } + } else { + MD_CHECK(md_process_leaf_block(ctx, block)); + + if(block->type == MD_BLOCK_CODE || block->type == MD_BLOCK_HTML) + byte_off += block->n_lines * sizeof(MD_VERBATIMLINE); + else + byte_off += block->n_lines * sizeof(MD_LINE); + } + + byte_off += sizeof(MD_BLOCK); + } + + ctx->n_block_bytes = 0; + +abort: + return ret; +} + + +/************************************ + *** Grouping Lines into Blocks *** + ************************************/ + +static void* +md_push_block_bytes(MD_CTX* ctx, int n_bytes) +{ + void* ptr; + + if(ctx->n_block_bytes + n_bytes > ctx->alloc_block_bytes) { + void* new_block_bytes; + + ctx->alloc_block_bytes = (ctx->alloc_block_bytes > 0 + ? ctx->alloc_block_bytes + ctx->alloc_block_bytes / 2 + : 512); + new_block_bytes = realloc(ctx->block_bytes, ctx->alloc_block_bytes); + if(new_block_bytes == NULL) { + MD_LOG("realloc() failed."); + return NULL; + } + + /* Fix the ->current_block after the reallocation. */ + if(ctx->current_block != NULL) { + OFF off_current_block = (OFF) ((char*) ctx->current_block - (char*) ctx->block_bytes); + ctx->current_block = (MD_BLOCK*) ((char*) new_block_bytes + off_current_block); + } + + ctx->block_bytes = new_block_bytes; + } + + ptr = (char*)ctx->block_bytes + ctx->n_block_bytes; + ctx->n_block_bytes += n_bytes; + return ptr; +} + +static int +md_start_new_block(MD_CTX* ctx, const MD_LINE_ANALYSIS* line) +{ + MD_BLOCK* block; + + MD_ASSERT(ctx->current_block == NULL); + + block = (MD_BLOCK*) md_push_block_bytes(ctx, sizeof(MD_BLOCK)); + if(block == NULL) + return -1; + + switch(line->type) { + case MD_LINE_HR: + block->type = MD_BLOCK_HR; + break; + + case MD_LINE_ATXHEADER: + case MD_LINE_SETEXTHEADER: + block->type = MD_BLOCK_H; + break; + + case MD_LINE_FENCEDCODE: + case MD_LINE_INDENTEDCODE: + block->type = MD_BLOCK_CODE; + break; + + case MD_LINE_TEXT: + block->type = MD_BLOCK_P; + break; + + case MD_LINE_HTML: + block->type = MD_BLOCK_HTML; + break; + + case MD_LINE_BLANK: + case MD_LINE_SETEXTUNDERLINE: + case MD_LINE_TABLEUNDERLINE: + default: + MD_UNREACHABLE(); + break; + } + + block->flags = 0; + block->data = line->data; + block->n_lines = 0; + + ctx->current_block = block; + return 0; +} + +/* Eat from start of current (textual) block any reference definitions and + * remember them so we can resolve any links referring to them. + * + * (Reference definitions can only be at start of it as they cannot break + * a paragraph.) + */ +static int +md_consume_link_reference_definitions(MD_CTX* ctx) +{ + MD_LINE* lines = (MD_LINE*) (ctx->current_block + 1); + int n_lines = ctx->current_block->n_lines; + int n = 0; + + /* Compute how many lines at the start of the block form one or more + * reference definitions. */ + while(n < n_lines) { + int n_link_ref_lines; + + n_link_ref_lines = md_is_link_reference_definition(ctx, + lines + n, n_lines - n); + /* Not a reference definition? */ + if(n_link_ref_lines == 0) + break; + + /* We fail if it is the ref. def. but it could not be stored due + * a memory allocation error. */ + if(n_link_ref_lines < 0) + return -1; + + n += n_link_ref_lines; + } + + /* If there was at least one reference definition, we need to remove + * its lines from the block, or perhaps even the whole block. */ + if(n > 0) { + if(n == n_lines) { + /* Remove complete block. */ + ctx->n_block_bytes -= n * sizeof(MD_LINE); + ctx->n_block_bytes -= sizeof(MD_BLOCK); + ctx->current_block = NULL; + } else { + /* Remove just some initial lines from the block. */ + memmove(lines, lines + n, (n_lines - n) * sizeof(MD_LINE)); + ctx->current_block->n_lines -= n; + ctx->n_block_bytes -= n * sizeof(MD_LINE); + } + } + + return 0; +} + +static int +md_end_current_block(MD_CTX* ctx) +{ + int ret = 0; + + if(ctx->current_block == NULL) + return ret; + + /* Check whether there is a reference definition. (We do this here instead + * of in md_analyze_line() because reference definition can take multiple + * lines.) */ + if(ctx->current_block->type == MD_BLOCK_P || + (ctx->current_block->type == MD_BLOCK_H && (ctx->current_block->flags & MD_BLOCK_SETEXT_HEADER))) + { + MD_LINE* lines = (MD_LINE*) (ctx->current_block + 1); + if(CH(lines[0].beg) == _T('[')) { + MD_CHECK(md_consume_link_reference_definitions(ctx)); + if(ctx->current_block == NULL) + return ret; + } + } + + if(ctx->current_block->type == MD_BLOCK_H && (ctx->current_block->flags & MD_BLOCK_SETEXT_HEADER)) { + int n_lines = ctx->current_block->n_lines; + + if(n_lines > 1) { + /* Get rid of the underline. */ + ctx->current_block->n_lines--; + ctx->n_block_bytes -= sizeof(MD_LINE); + } else { + /* Only the underline has left after eating the ref. defs. + * Keep the line as beginning of a new ordinary paragraph. */ + ctx->current_block->type = MD_BLOCK_P; + return 0; + } + } + + /* Mark we are not building any block anymore. */ + ctx->current_block = NULL; + +abort: + return ret; +} + +static int +md_add_line_into_current_block(MD_CTX* ctx, const MD_LINE_ANALYSIS* analysis) +{ + MD_ASSERT(ctx->current_block != NULL); + + if(ctx->current_block->type == MD_BLOCK_CODE || ctx->current_block->type == MD_BLOCK_HTML) { + MD_VERBATIMLINE* line; + + line = (MD_VERBATIMLINE*) md_push_block_bytes(ctx, sizeof(MD_VERBATIMLINE)); + if(line == NULL) + return -1; + + line->indent = analysis->indent; + line->beg = analysis->beg; + line->end = analysis->end; + } else { + MD_LINE* line; + + line = (MD_LINE*) md_push_block_bytes(ctx, sizeof(MD_LINE)); + if(line == NULL) + return -1; + + line->beg = analysis->beg; + line->end = analysis->end; + } + ctx->current_block->n_lines++; + + return 0; +} + +static int +md_push_container_bytes(MD_CTX* ctx, MD_BLOCKTYPE type, unsigned start, + unsigned data, unsigned flags) +{ + MD_BLOCK* block; + int ret = 0; + + MD_CHECK(md_end_current_block(ctx)); + + block = (MD_BLOCK*) md_push_block_bytes(ctx, sizeof(MD_BLOCK)); + if(block == NULL) + return -1; + + block->type = type; + block->flags = flags; + block->data = data; + block->n_lines = start; + +abort: + return ret; +} + + + +/*********************** + *** Line Analysis *** + ***********************/ + +static int +md_is_hr_line(MD_CTX* ctx, OFF beg, OFF* p_end, OFF* p_killer) +{ + OFF off = beg + 1; + int n = 1; + + while(off < ctx->size && (CH(off) == CH(beg) || CH(off) == _T(' ') || CH(off) == _T('\t'))) { + if(CH(off) == CH(beg)) + n++; + off++; + } + + if(n < 3) { + *p_killer = off; + return FALSE; + } + + /* Nothing else can be present on the line. */ + if(off < ctx->size && !ISNEWLINE(off)) { + *p_killer = off; + return FALSE; + } + + *p_end = off; + return TRUE; +} + +static int +md_is_atxheader_line(MD_CTX* ctx, OFF beg, OFF* p_beg, OFF* p_end, unsigned* p_level) +{ + int n; + OFF off = beg + 1; + + while(off < ctx->size && CH(off) == _T('#') && off - beg < 7) + off++; + n = off - beg; + + if(n > 6) + return FALSE; + *p_level = n; + + if(!(ctx->parser.flags & MD_FLAG_PERMISSIVEATXHEADERS) && off < ctx->size && + CH(off) != _T(' ') && CH(off) != _T('\t') && !ISNEWLINE(off)) + return FALSE; + + while(off < ctx->size && CH(off) == _T(' ')) + off++; + *p_beg = off; + *p_end = off; + return TRUE; +} + +static int +md_is_setext_underline(MD_CTX* ctx, OFF beg, OFF* p_end, unsigned* p_level) +{ + OFF off = beg + 1; + + while(off < ctx->size && CH(off) == CH(beg)) + off++; + + /* Optionally, space(s) can follow. */ + while(off < ctx->size && CH(off) == _T(' ')) + off++; + + /* But nothing more is allowed on the line. */ + if(off < ctx->size && !ISNEWLINE(off)) + return FALSE; + + *p_level = (CH(beg) == _T('=') ? 1 : 2); + *p_end = off; + return TRUE; +} + +static int +md_is_table_underline(MD_CTX* ctx, OFF beg, OFF* p_end, unsigned* p_col_count) +{ + OFF off = beg; + int found_pipe = FALSE; + unsigned col_count = 0; + + if(off < ctx->size && CH(off) == _T('|')) { + found_pipe = TRUE; + off++; + while(off < ctx->size && ISWHITESPACE(off)) + off++; + } + + while(1) { + int delimited = FALSE; + + /* Cell underline ("-----", ":----", "----:" or ":----:") */ + if(off < ctx->size && CH(off) == _T(':')) + off++; + if(off >= ctx->size || CH(off) != _T('-')) + return FALSE; + while(off < ctx->size && CH(off) == _T('-')) + off++; + if(off < ctx->size && CH(off) == _T(':')) + off++; + + col_count++; + + /* Pipe delimiter (optional at the end of line). */ + while(off < ctx->size && ISWHITESPACE(off)) + off++; + if(off < ctx->size && CH(off) == _T('|')) { + delimited = TRUE; + found_pipe = TRUE; + off++; + while(off < ctx->size && ISWHITESPACE(off)) + off++; + } + + /* Success, if we reach end of line. */ + if(off >= ctx->size || ISNEWLINE(off)) + break; + + if(!delimited) + return FALSE; + } + + if(!found_pipe) + return FALSE; + + *p_end = off; + *p_col_count = col_count; + return TRUE; +} + +static int +md_is_opening_code_fence(MD_CTX* ctx, OFF beg, OFF* p_end) +{ + OFF off = beg; + + while(off < ctx->size && CH(off) == CH(beg)) + off++; + + /* Fence must have at least three characters. */ + if(off - beg < 3) + return FALSE; + + ctx->code_fence_length = off - beg; + + /* Optionally, space(s) can follow. */ + while(off < ctx->size && CH(off) == _T(' ')) + off++; + + /* Optionally, an info string can follow. */ + while(off < ctx->size && !ISNEWLINE(off)) { + /* Backtick-based fence must not contain '`' in the info string. */ + if(CH(beg) == _T('`') && CH(off) == _T('`')) + return FALSE; + off++; + } + + *p_end = off; + return TRUE; +} + +static int +md_is_closing_code_fence(MD_CTX* ctx, CHAR ch, OFF beg, OFF* p_end) +{ + OFF off = beg; + int ret = FALSE; + + /* Closing fence must have at least the same length and use same char as + * opening one. */ + while(off < ctx->size && CH(off) == ch) + off++; + if(off - beg < ctx->code_fence_length) + goto out; + + /* Optionally, space(s) can follow */ + while(off < ctx->size && CH(off) == _T(' ')) + off++; + + /* But nothing more is allowed on the line. */ + if(off < ctx->size && !ISNEWLINE(off)) + goto out; + + ret = TRUE; + +out: + /* Note we set *p_end even on failure: If we are not closing fence, caller + * would eat the line anyway without any parsing. */ + *p_end = off; + return ret; +} + +/* Returns type of the raw HTML block, or FALSE if it is not HTML block. + * (Refer to CommonMark specification for details about the types.) + */ +static int +md_is_html_block_start_condition(MD_CTX* ctx, OFF beg) +{ + typedef struct TAG_tag TAG; + struct TAG_tag { + const CHAR* name; + unsigned len : 8; + }; + + /* Type 6 is started by a long list of allowed tags. We use two-level + * tree to speed-up the search. */ +#ifdef X + #undef X +#endif +#define X(name) { _T(name), (sizeof(name)-1) / sizeof(CHAR) } +#define Xend { NULL, 0 } + static const TAG t1[] = { X("pre"), X("script"), X("style"), X("textarea"), Xend }; + + static const TAG a6[] = { X("address"), X("article"), X("aside"), Xend }; + static const TAG b6[] = { X("base"), X("basefont"), X("blockquote"), X("body"), Xend }; + static const TAG c6[] = { X("caption"), X("center"), X("col"), X("colgroup"), Xend }; + static const TAG d6[] = { X("dd"), X("details"), X("dialog"), X("dir"), + X("div"), X("dl"), X("dt"), Xend }; + static const TAG f6[] = { X("fieldset"), X("figcaption"), X("figure"), X("footer"), + X("form"), X("frame"), X("frameset"), Xend }; + static const TAG h6[] = { X("h1"), X("head"), X("header"), X("hr"), X("html"), Xend }; + static const TAG i6[] = { X("iframe"), Xend }; + static const TAG l6[] = { X("legend"), X("li"), X("link"), Xend }; + static const TAG m6[] = { X("main"), X("menu"), X("menuitem"), Xend }; + static const TAG n6[] = { X("nav"), X("noframes"), Xend }; + static const TAG o6[] = { X("ol"), X("optgroup"), X("option"), Xend }; + static const TAG p6[] = { X("p"), X("param"), Xend }; + static const TAG s6[] = { X("section"), X("source"), X("summary"), Xend }; + static const TAG t6[] = { X("table"), X("tbody"), X("td"), X("tfoot"), X("th"), + X("thead"), X("title"), X("tr"), X("track"), Xend }; + static const TAG u6[] = { X("ul"), Xend }; + static const TAG xx[] = { Xend }; +#undef X + + static const TAG* map6[26] = { + a6, b6, c6, d6, xx, f6, xx, h6, i6, xx, xx, l6, m6, + n6, o6, p6, xx, xx, s6, t6, u6, xx, xx, xx, xx, xx + }; + OFF off = beg + 1; + int i; + + /* Check for type 1: size) { + if(md_ascii_case_eq(STR(off), t1[i].name, t1[i].len)) + return 1; + } + } + + /* Check for type 2: "), 3, p_end) ? 2 : FALSE); + + case 3: + return (md_line_contains(ctx, beg, _T("?>"), 2, p_end) ? 3 : FALSE); + + case 4: + return (md_line_contains(ctx, beg, _T(">"), 1, p_end) ? 4 : FALSE); + + case 5: + return (md_line_contains(ctx, beg, _T("]]>"), 3, p_end) ? 5 : FALSE); + + case 6: /* Pass through */ + case 7: + *p_end = beg; + return (beg >= ctx->size || ISNEWLINE(beg) ? ctx->html_block_type : FALSE); + + default: + MD_UNREACHABLE(); + } + return FALSE; +} + + +static int +md_is_container_compatible(const MD_CONTAINER* pivot, const MD_CONTAINER* container) +{ + /* Block quote has no "items" like lists. */ + if(container->ch == _T('>')) + return FALSE; + + if(container->ch != pivot->ch) + return FALSE; + if(container->mark_indent > pivot->contents_indent) + return FALSE; + + return TRUE; +} + +static int +md_push_container(MD_CTX* ctx, const MD_CONTAINER* container) +{ + if(ctx->n_containers >= ctx->alloc_containers) { + MD_CONTAINER* new_containers; + + ctx->alloc_containers = (ctx->alloc_containers > 0 + ? ctx->alloc_containers + ctx->alloc_containers / 2 + : 16); + new_containers = realloc(ctx->containers, ctx->alloc_containers * sizeof(MD_CONTAINER)); + if(new_containers == NULL) { + MD_LOG("realloc() failed."); + return -1; + } + + ctx->containers = new_containers; + } + + memcpy(&ctx->containers[ctx->n_containers++], container, sizeof(MD_CONTAINER)); + return 0; +} + +static int +md_enter_child_containers(MD_CTX* ctx, int n_children) +{ + int i; + int ret = 0; + + for(i = ctx->n_containers - n_children; i < ctx->n_containers; i++) { + MD_CONTAINER* c = &ctx->containers[i]; + int is_ordered_list = FALSE; + + switch(c->ch) { + case _T(')'): + case _T('.'): + is_ordered_list = TRUE; + MD_FALLTHROUGH(); + + case _T('-'): + case _T('+'): + case _T('*'): + /* Remember offset in ctx->block_bytes so we can revisit the + * block if we detect it is a loose list. */ + md_end_current_block(ctx); + c->block_byte_off = ctx->n_block_bytes; + + MD_CHECK(md_push_container_bytes(ctx, + (is_ordered_list ? MD_BLOCK_OL : MD_BLOCK_UL), + c->start, c->ch, MD_BLOCK_CONTAINER_OPENER)); + MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, + c->task_mark_off, + (c->is_task ? CH(c->task_mark_off) : 0), + MD_BLOCK_CONTAINER_OPENER)); + break; + + case _T('>'): + MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_QUOTE, 0, 0, MD_BLOCK_CONTAINER_OPENER)); + break; + + default: + MD_UNREACHABLE(); + break; + } + } + +abort: + return ret; +} + +static int +md_leave_child_containers(MD_CTX* ctx, int n_keep) +{ + int ret = 0; + + while(ctx->n_containers > n_keep) { + MD_CONTAINER* c = &ctx->containers[ctx->n_containers-1]; + int is_ordered_list = FALSE; + + switch(c->ch) { + case _T(')'): + case _T('.'): + is_ordered_list = TRUE; + MD_FALLTHROUGH(); + + case _T('-'): + case _T('+'): + case _T('*'): + MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, + c->task_mark_off, (c->is_task ? CH(c->task_mark_off) : 0), + MD_BLOCK_CONTAINER_CLOSER)); + MD_CHECK(md_push_container_bytes(ctx, + (is_ordered_list ? MD_BLOCK_OL : MD_BLOCK_UL), 0, + c->ch, MD_BLOCK_CONTAINER_CLOSER)); + break; + + case _T('>'): + MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_QUOTE, 0, + 0, MD_BLOCK_CONTAINER_CLOSER)); + break; + + default: + MD_UNREACHABLE(); + break; + } + + ctx->n_containers--; + } + +abort: + return ret; +} + +static int +md_is_container_mark(MD_CTX* ctx, unsigned indent, OFF beg, OFF* p_end, MD_CONTAINER* p_container) +{ + OFF off = beg; + OFF max_end; + + if(off >= ctx->size || indent >= ctx->code_indent_offset) + return FALSE; + + /* Check for block quote mark. */ + if(CH(off) == _T('>')) { + off++; + p_container->ch = _T('>'); + p_container->is_loose = FALSE; + p_container->is_task = FALSE; + p_container->mark_indent = indent; + p_container->contents_indent = indent + 1; + *p_end = off; + return TRUE; + } + + /* Check for list item bullet mark. */ + if(ISANYOF(off, _T("-+*")) && (off+1 >= ctx->size || ISBLANK(off+1) || ISNEWLINE(off+1))) { + p_container->ch = CH(off); + p_container->is_loose = FALSE; + p_container->is_task = FALSE; + p_container->mark_indent = indent; + p_container->contents_indent = indent + 1; + *p_end = off+1; + return TRUE; + } + + /* Check for ordered list item marks. */ + max_end = off + 9; + if(max_end > ctx->size) + max_end = ctx->size; + p_container->start = 0; + while(off < max_end && ISDIGIT(off)) { + p_container->start = p_container->start * 10 + CH(off) - _T('0'); + off++; + } + if(off > beg && + off < ctx->size && + (CH(off) == _T('.') || CH(off) == _T(')')) && + (off+1 >= ctx->size || ISBLANK(off+1) || ISNEWLINE(off+1))) + { + p_container->ch = CH(off); + p_container->is_loose = FALSE; + p_container->is_task = FALSE; + p_container->mark_indent = indent; + p_container->contents_indent = indent + off - beg + 1; + *p_end = off+1; + return TRUE; + } + + return FALSE; +} + +static unsigned +md_line_indentation(MD_CTX* ctx, unsigned total_indent, OFF beg, OFF* p_end) +{ + OFF off = beg; + unsigned indent = total_indent; + + while(off < ctx->size && ISBLANK(off)) { + if(CH(off) == _T('\t')) + indent = (indent + 4) & ~3; + else + indent++; + off++; + } + + *p_end = off; + return indent - total_indent; +} + +static const MD_LINE_ANALYSIS md_dummy_blank_line = { MD_LINE_BLANK, 0, 0, 0, 0 }; + +/* Analyze type of the line and find some its properties. This serves as a + * main input for determining type and boundaries of a block. */ +static int +md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, + const MD_LINE_ANALYSIS* pivot_line, MD_LINE_ANALYSIS* line) +{ + unsigned total_indent = 0; + int n_parents = 0; + int n_brothers = 0; + int n_children = 0; + MD_CONTAINER container = { 0 }; + int prev_line_has_list_loosening_effect = ctx->last_line_has_list_loosening_effect; + OFF off = beg; + OFF hr_killer = 0; + int ret = 0; + + line->indent = md_line_indentation(ctx, total_indent, off, &off); + total_indent += line->indent; + line->beg = off; + + /* Given the indentation and block quote marks '>', determine how many of + * the current containers are our parents. */ + while(n_parents < ctx->n_containers) { + MD_CONTAINER* c = &ctx->containers[n_parents]; + + if(c->ch == _T('>') && line->indent < ctx->code_indent_offset && + off < ctx->size && CH(off) == _T('>')) + { + /* Block quote mark. */ + off++; + total_indent++; + line->indent = md_line_indentation(ctx, total_indent, off, &off); + total_indent += line->indent; + + /* The optional 1st space after '>' is part of the block quote mark. */ + if(line->indent > 0) + line->indent--; + + line->beg = off; + + } else if(c->ch != _T('>') && line->indent >= c->contents_indent) { + /* List. */ + line->indent -= c->contents_indent; + } else { + break; + } + + n_parents++; + } + + if(off >= ctx->size || ISNEWLINE(off)) { + /* Blank line does not need any real indentation to be nested inside + * a list. */ + if(n_brothers + n_children == 0) { + while(n_parents < ctx->n_containers && ctx->containers[n_parents].ch != _T('>')) + n_parents++; + } + } + + while(TRUE) { + /* Check whether we are fenced code continuation. */ + if(pivot_line->type == MD_LINE_FENCEDCODE) { + line->beg = off; + + /* We are another MD_LINE_FENCEDCODE unless we are closing fence + * which we transform into MD_LINE_BLANK. */ + if(line->indent < ctx->code_indent_offset) { + if(md_is_closing_code_fence(ctx, CH(pivot_line->beg), off, &off)) { + line->type = MD_LINE_BLANK; + ctx->last_line_has_list_loosening_effect = FALSE; + break; + } + } + + /* Change indentation accordingly to the initial code fence. */ + if(n_parents == ctx->n_containers) { + if(line->indent > pivot_line->indent) + line->indent -= pivot_line->indent; + else + line->indent = 0; + + line->type = MD_LINE_FENCEDCODE; + break; + } + } + + /* Check whether we are HTML block continuation. */ + if(pivot_line->type == MD_LINE_HTML && ctx->html_block_type > 0) { + if(n_parents < ctx->n_containers) { + /* HTML block is implicitly ended if the enclosing container + * block ends. */ + ctx->html_block_type = 0; + } else { + int html_block_type; + + html_block_type = md_is_html_block_end_condition(ctx, off, &off); + if(html_block_type > 0) { + MD_ASSERT(html_block_type == ctx->html_block_type); + + /* Make sure this is the last line of the block. */ + ctx->html_block_type = 0; + + /* Some end conditions serve as blank lines at the same time. */ + if(html_block_type == 6 || html_block_type == 7) { + line->type = MD_LINE_BLANK; + line->indent = 0; + break; + } + } + + line->type = MD_LINE_HTML; + n_parents = ctx->n_containers; + break; + } + } + + /* Check for blank line. */ + if(off >= ctx->size || ISNEWLINE(off)) { + if(pivot_line->type == MD_LINE_INDENTEDCODE && n_parents == ctx->n_containers) { + line->type = MD_LINE_INDENTEDCODE; + if(line->indent > ctx->code_indent_offset) + line->indent -= ctx->code_indent_offset; + else + line->indent = 0; + ctx->last_line_has_list_loosening_effect = FALSE; + } else { + line->type = MD_LINE_BLANK; + ctx->last_line_has_list_loosening_effect = (n_parents > 0 && + n_brothers + n_children == 0 && + ctx->containers[n_parents-1].ch != _T('>')); + + #if 1 + /* See https://github.com/mity/md4c/issues/6 + * + * This ugly checking tests we are in (yet empty) list item but + * not its very first line (i.e. not the line with the list + * item mark). + * + * If we are such a blank line, then any following non-blank + * line which would be part of the list item actually has to + * end the list because according to the specification, "a list + * item can begin with at most one blank line." + */ + if(n_parents > 0 && ctx->containers[n_parents-1].ch != _T('>') && + n_brothers + n_children == 0 && ctx->current_block == NULL && + ctx->n_block_bytes > (int) sizeof(MD_BLOCK)) + { + MD_BLOCK* top_block = (MD_BLOCK*) ((char*)ctx->block_bytes + ctx->n_block_bytes - sizeof(MD_BLOCK)); + if(top_block->type == MD_BLOCK_LI) + ctx->last_list_item_starts_with_two_blank_lines = TRUE; + } + #endif + } + break; + } else { + #if 1 + /* This is the 2nd half of the hack. If the flag is set (i.e. there + * was a 2nd blank line at the beginning of the list item) and if + * we would otherwise still belong to the list item, we enforce + * the end of the list. */ + ctx->last_line_has_list_loosening_effect = FALSE; + if(ctx->last_list_item_starts_with_two_blank_lines) { + if(n_parents > 0 && ctx->containers[n_parents-1].ch != _T('>') && + n_brothers + n_children == 0 && ctx->current_block == NULL && + ctx->n_block_bytes > (int) sizeof(MD_BLOCK)) + { + MD_BLOCK* top_block = (MD_BLOCK*) ((char*)ctx->block_bytes + ctx->n_block_bytes - sizeof(MD_BLOCK)); + if(top_block->type == MD_BLOCK_LI) + n_parents--; + } + + ctx->last_list_item_starts_with_two_blank_lines = FALSE; + } + #endif + } + + /* Check whether we are Setext underline. */ + if(line->indent < ctx->code_indent_offset && pivot_line->type == MD_LINE_TEXT + && off < ctx->size && ISANYOF2(off, _T('='), _T('-')) + && (n_parents == ctx->n_containers)) + { + unsigned level; + + if(md_is_setext_underline(ctx, off, &off, &level)) { + line->type = MD_LINE_SETEXTUNDERLINE; + line->data = level; + break; + } + } + + /* Check for thematic break line. */ + if(line->indent < ctx->code_indent_offset + && off < ctx->size && off >= hr_killer + && ISANYOF(off, _T("-_*"))) + { + if(md_is_hr_line(ctx, off, &off, &hr_killer)) { + line->type = MD_LINE_HR; + break; + } + } + + /* Check for "brother" container. I.e. whether we are another list item + * in already started list. */ + if(n_parents < ctx->n_containers && n_brothers + n_children == 0) { + OFF tmp; + + if(md_is_container_mark(ctx, line->indent, off, &tmp, &container) && + md_is_container_compatible(&ctx->containers[n_parents], &container)) + { + pivot_line = &md_dummy_blank_line; + + off = tmp; + + total_indent += container.contents_indent - container.mark_indent; + line->indent = md_line_indentation(ctx, total_indent, off, &off); + total_indent += line->indent; + line->beg = off; + + /* Some of the following whitespace actually still belongs to the mark. */ + if(off >= ctx->size || ISNEWLINE(off)) { + container.contents_indent++; + } else if(line->indent <= ctx->code_indent_offset) { + container.contents_indent += line->indent; + line->indent = 0; + } else { + container.contents_indent += 1; + line->indent--; + } + + ctx->containers[n_parents].mark_indent = container.mark_indent; + ctx->containers[n_parents].contents_indent = container.contents_indent; + + n_brothers++; + continue; + } + } + + /* Check for indented code. + * Note indented code block cannot interrupt a paragraph. */ + if(line->indent >= ctx->code_indent_offset && + (pivot_line->type == MD_LINE_BLANK || pivot_line->type == MD_LINE_INDENTEDCODE)) + { + line->type = MD_LINE_INDENTEDCODE; + MD_ASSERT(line->indent >= ctx->code_indent_offset); + line->indent -= ctx->code_indent_offset; + line->data = 0; + break; + } + + /* Check for start of a new container block. */ + if(line->indent < ctx->code_indent_offset && + md_is_container_mark(ctx, line->indent, off, &off, &container)) + { + if(pivot_line->type == MD_LINE_TEXT && n_parents == ctx->n_containers && + (off >= ctx->size || ISNEWLINE(off)) && container.ch != _T('>')) + { + /* Noop. List mark followed by a blank line cannot interrupt a paragraph. */ + } else if(pivot_line->type == MD_LINE_TEXT && n_parents == ctx->n_containers && + ISANYOF2_(container.ch, _T('.'), _T(')')) && container.start != 1) + { + /* Noop. Ordered list cannot interrupt a paragraph unless the start index is 1. */ + } else { + total_indent += container.contents_indent - container.mark_indent; + line->indent = md_line_indentation(ctx, total_indent, off, &off); + total_indent += line->indent; + + line->beg = off; + line->data = container.ch; + + /* Some of the following whitespace actually still belongs to the mark. */ + if(off >= ctx->size || ISNEWLINE(off)) { + container.contents_indent++; + } else if(line->indent <= ctx->code_indent_offset) { + container.contents_indent += line->indent; + line->indent = 0; + } else { + container.contents_indent += 1; + line->indent--; + } + + if(n_brothers + n_children == 0) + pivot_line = &md_dummy_blank_line; + + if(n_children == 0) + MD_CHECK(md_leave_child_containers(ctx, n_parents + n_brothers)); + + n_children++; + MD_CHECK(md_push_container(ctx, &container)); + continue; + } + } + + /* Check whether we are table continuation. */ + if(pivot_line->type == MD_LINE_TABLE && n_parents == ctx->n_containers) { + line->type = MD_LINE_TABLE; + break; + } + + /* Check for ATX header. */ + if(line->indent < ctx->code_indent_offset && + off < ctx->size && CH(off) == _T('#')) + { + unsigned level; + + if(md_is_atxheader_line(ctx, off, &line->beg, &off, &level)) { + line->type = MD_LINE_ATXHEADER; + line->data = level; + break; + } + } + + /* Check whether we are starting code fence. */ + if(off < ctx->size && ISANYOF2(off, _T('`'), _T('~'))) { + if(md_is_opening_code_fence(ctx, off, &off)) { + line->type = MD_LINE_FENCEDCODE; + line->data = 1; + break; + } + } + + /* Check for start of raw HTML block. */ + if(off < ctx->size && CH(off) == _T('<') + && !(ctx->parser.flags & MD_FLAG_NOHTMLBLOCKS)) + { + ctx->html_block_type = md_is_html_block_start_condition(ctx, off); + + /* HTML block type 7 cannot interrupt paragraph. */ + if(ctx->html_block_type == 7 && pivot_line->type == MD_LINE_TEXT) + ctx->html_block_type = 0; + + if(ctx->html_block_type > 0) { + /* The line itself also may immediately close the block. */ + if(md_is_html_block_end_condition(ctx, off, &off) == ctx->html_block_type) { + /* Make sure this is the last line of the block. */ + ctx->html_block_type = 0; + } + + line->type = MD_LINE_HTML; + break; + } + } + + /* Check for table underline. */ + if((ctx->parser.flags & MD_FLAG_TABLES) && pivot_line->type == MD_LINE_TEXT + && off < ctx->size && ISANYOF3(off, _T('|'), _T('-'), _T(':')) + && n_parents == ctx->n_containers) + { + unsigned col_count; + + if(ctx->current_block != NULL && ctx->current_block->n_lines == 1 && + md_is_table_underline(ctx, off, &off, &col_count)) + { + line->data = col_count; + line->type = MD_LINE_TABLEUNDERLINE; + break; + } + } + + /* By default, we are normal text line. */ + line->type = MD_LINE_TEXT; + if(pivot_line->type == MD_LINE_TEXT && n_brothers + n_children == 0) { + /* Lazy continuation. */ + n_parents = ctx->n_containers; + } + + /* Check for task mark. */ + if((ctx->parser.flags & MD_FLAG_TASKLISTS) && n_brothers + n_children > 0 && + ISANYOF_(ctx->containers[ctx->n_containers-1].ch, _T("-+*.)"))) + { + OFF tmp = off; + + while(tmp < ctx->size && tmp < off + 3 && ISBLANK(tmp)) + tmp++; + if(tmp + 2 < ctx->size && CH(tmp) == _T('[') && + ISANYOF(tmp+1, _T("xX ")) && CH(tmp+2) == _T(']') && + (tmp + 3 == ctx->size || ISBLANK(tmp+3) || ISNEWLINE(tmp+3))) + { + MD_CONTAINER* task_container = (n_children > 0 ? &ctx->containers[ctx->n_containers-1] : &container); + task_container->is_task = TRUE; + task_container->task_mark_off = tmp + 1; + off = tmp + 3; + while(off < ctx->size && ISWHITESPACE(off)) + off++; + if (off == ctx->size) break; + line->beg = off; + } + } + + break; + } + + /* Scan for end of the line. + * + * Note this is quite a bottleneck of the parsing as we here iterate almost + * over compete document. + */ +#if defined __linux__ && !defined MD4C_USE_UTF16 + /* Recent glibc versions have superbly optimized strcspn(), even using + * vectorization if available. */ + if(ctx->doc_ends_with_newline && off < ctx->size) { + while(TRUE) { + off += (OFF) strcspn(STR(off), "\r\n"); + + /* strcspn() can stop on zero terminator; but that can appear + * anywhere in the Markfown input... */ + if(CH(off) == _T('\0')) + off++; + else + break; + } + } else +#endif + { + /* Optimization: Use some loop unrolling. */ + while(off + 3 < ctx->size && !ISNEWLINE(off+0) && !ISNEWLINE(off+1) + && !ISNEWLINE(off+2) && !ISNEWLINE(off+3)) + off += 4; + while(off < ctx->size && !ISNEWLINE(off)) + off++; + } + + /* Set end of the line. */ + line->end = off; + + /* But for ATX header, we should exclude the optional trailing mark. */ + if(line->type == MD_LINE_ATXHEADER) { + OFF tmp = line->end; + while(tmp > line->beg && CH(tmp-1) == _T(' ')) + tmp--; + while(tmp > line->beg && CH(tmp-1) == _T('#')) + tmp--; + if(tmp == line->beg || CH(tmp-1) == _T(' ') || (ctx->parser.flags & MD_FLAG_PERMISSIVEATXHEADERS)) + line->end = tmp; + } + + /* Trim trailing spaces. */ + if(line->type != MD_LINE_INDENTEDCODE && line->type != MD_LINE_FENCEDCODE) { + while(line->end > line->beg && CH(line->end-1) == _T(' ')) + line->end--; + } + + /* Eat also the new line. */ + if(off < ctx->size && CH(off) == _T('\r')) + off++; + if(off < ctx->size && CH(off) == _T('\n')) + off++; + + *p_end = off; + + /* If we belong to a list after seeing a blank line, the list is loose. */ + if(prev_line_has_list_loosening_effect && line->type != MD_LINE_BLANK && n_parents + n_brothers > 0) { + MD_CONTAINER* c = &ctx->containers[n_parents + n_brothers - 1]; + if(c->ch != _T('>')) { + MD_BLOCK* block = (MD_BLOCK*) (((char*)ctx->block_bytes) + c->block_byte_off); + block->flags |= MD_BLOCK_LOOSE_LIST; + } + } + + /* Leave any containers we are not part of anymore. */ + if(n_children == 0 && n_parents + n_brothers < ctx->n_containers) + MD_CHECK(md_leave_child_containers(ctx, n_parents + n_brothers)); + + /* Enter any container we found a mark for. */ + if(n_brothers > 0) { + MD_ASSERT(n_brothers == 1); + MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, + ctx->containers[n_parents].task_mark_off, + (ctx->containers[n_parents].is_task ? CH(ctx->containers[n_parents].task_mark_off) : 0), + MD_BLOCK_CONTAINER_CLOSER)); + MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, + container.task_mark_off, + (container.is_task ? CH(container.task_mark_off) : 0), + MD_BLOCK_CONTAINER_OPENER)); + ctx->containers[n_parents].is_task = container.is_task; + ctx->containers[n_parents].task_mark_off = container.task_mark_off; + } + + if(n_children > 0) + MD_CHECK(md_enter_child_containers(ctx, n_children)); + +abort: + return ret; +} + +static int +md_process_line(MD_CTX* ctx, const MD_LINE_ANALYSIS** p_pivot_line, MD_LINE_ANALYSIS* line) +{ + const MD_LINE_ANALYSIS* pivot_line = *p_pivot_line; + int ret = 0; + + /* Blank line ends current leaf block. */ + if(line->type == MD_LINE_BLANK) { + MD_CHECK(md_end_current_block(ctx)); + *p_pivot_line = &md_dummy_blank_line; + return 0; + } + + /* Some line types form block on their own. */ + if(line->type == MD_LINE_HR || line->type == MD_LINE_ATXHEADER) { + MD_CHECK(md_end_current_block(ctx)); + + /* Add our single-line block. */ + MD_CHECK(md_start_new_block(ctx, line)); + MD_CHECK(md_add_line_into_current_block(ctx, line)); + MD_CHECK(md_end_current_block(ctx)); + *p_pivot_line = &md_dummy_blank_line; + return 0; + } + + /* MD_LINE_SETEXTUNDERLINE changes meaning of the current block and ends it. */ + if(line->type == MD_LINE_SETEXTUNDERLINE) { + MD_ASSERT(ctx->current_block != NULL); + ctx->current_block->type = MD_BLOCK_H; + ctx->current_block->data = line->data; + ctx->current_block->flags |= MD_BLOCK_SETEXT_HEADER; + MD_CHECK(md_add_line_into_current_block(ctx, line)); + MD_CHECK(md_end_current_block(ctx)); + if(ctx->current_block == NULL) { + *p_pivot_line = &md_dummy_blank_line; + } else { + /* This happens if we have consumed all the body as link ref. defs. + * and downgraded the underline into start of a new paragraph block. */ + line->type = MD_LINE_TEXT; + *p_pivot_line = line; + } + return 0; + } + + /* MD_LINE_TABLEUNDERLINE changes meaning of the current block. */ + if(line->type == MD_LINE_TABLEUNDERLINE) { + MD_ASSERT(ctx->current_block != NULL); + MD_ASSERT(ctx->current_block->n_lines == 1); + ctx->current_block->type = MD_BLOCK_TABLE; + ctx->current_block->data = line->data; + MD_ASSERT(pivot_line != &md_dummy_blank_line); + ((MD_LINE_ANALYSIS*)pivot_line)->type = MD_LINE_TABLE; + MD_CHECK(md_add_line_into_current_block(ctx, line)); + return 0; + } + + /* The current block also ends if the line has different type. */ + if(line->type != pivot_line->type) + MD_CHECK(md_end_current_block(ctx)); + + /* The current line may start a new block. */ + if(ctx->current_block == NULL) { + MD_CHECK(md_start_new_block(ctx, line)); + *p_pivot_line = line; + } + + /* In all other cases the line is just a continuation of the current block. */ + MD_CHECK(md_add_line_into_current_block(ctx, line)); + +abort: + return ret; +} + +static int +md_process_doc(MD_CTX *ctx) +{ + const MD_LINE_ANALYSIS* pivot_line = &md_dummy_blank_line; + MD_LINE_ANALYSIS line_buf[2]; + MD_LINE_ANALYSIS* line = &line_buf[0]; + OFF off = 0; + int ret = 0; + + MD_ENTER_BLOCK(MD_BLOCK_DOC, NULL); + + while(off < ctx->size) { + if(line == pivot_line) + line = (line == &line_buf[0] ? &line_buf[1] : &line_buf[0]); + + MD_CHECK(md_analyze_line(ctx, off, &off, pivot_line, line)); + MD_CHECK(md_process_line(ctx, &pivot_line, line)); + } + + md_end_current_block(ctx); + + MD_CHECK(md_build_ref_def_hashtable(ctx)); + + /* Process all blocks. */ + MD_CHECK(md_leave_child_containers(ctx, 0)); + MD_CHECK(md_process_all_blocks(ctx)); + + MD_LEAVE_BLOCK(MD_BLOCK_DOC, NULL); + +abort: + +#if 0 + /* Output some memory consumption statistics. */ + { + char buffer[256]; + sprintf(buffer, "Alloced %u bytes for block buffer.", + (unsigned)(ctx->alloc_block_bytes)); + MD_LOG(buffer); + + sprintf(buffer, "Alloced %u bytes for containers buffer.", + (unsigned)(ctx->alloc_containers * sizeof(MD_CONTAINER))); + MD_LOG(buffer); + + sprintf(buffer, "Alloced %u bytes for marks buffer.", + (unsigned)(ctx->alloc_marks * sizeof(MD_MARK))); + MD_LOG(buffer); + + sprintf(buffer, "Alloced %u bytes for aux. buffer.", + (unsigned)(ctx->alloc_buffer * sizeof(MD_CHAR))); + MD_LOG(buffer); + } +#endif + + return ret; +} + + +/******************** + *** Public API *** + ********************/ + +int +md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata) +{ + MD_CTX ctx; + int i; + int ret; + + if(parser->abi_version != 0) { + if(parser->debug_log != NULL) + parser->debug_log("Unsupported abi_version.", userdata); + return -1; + } + + /* Setup context structure. */ + memset(&ctx, 0, sizeof(MD_CTX)); + ctx.text = text; + ctx.size = size; + memcpy(&ctx.parser, parser, sizeof(MD_PARSER)); + ctx.userdata = userdata; + ctx.code_indent_offset = (ctx.parser.flags & MD_FLAG_NOINDENTEDCODEBLOCKS) ? (OFF)(-1) : 4; + md_build_mark_char_map(&ctx); + ctx.doc_ends_with_newline = (size > 0 && ISNEWLINE_(text[size-1])); + + /* Reset all unresolved opener mark chains. */ + for(i = 0; i < (int) SIZEOF_ARRAY(ctx.mark_chains); i++) { + ctx.mark_chains[i].head = -1; + ctx.mark_chains[i].tail = -1; + } + ctx.unresolved_link_head = -1; + ctx.unresolved_link_tail = -1; + + /* All the work. */ + ret = md_process_doc(&ctx); + + /* Clean-up. */ + md_free_ref_defs(&ctx); + md_free_ref_def_hashtable(&ctx); + free(ctx.buffer); + free(ctx.marks); + free(ctx.block_bytes); + free(ctx.containers); + + return ret; +} diff --git a/src/third-party/md4c/md4c.h b/src/third-party/md4c/md4c.h new file mode 100644 index 00000000..95f78f9b --- /dev/null +++ b/src/third-party/md4c/md4c.h @@ -0,0 +1,405 @@ +/* + * MD4C: Markdown parser for C + * (http://github.com/mity/md4c) + * + * Copyright (c) 2016-2020 Martin Mitas + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef MD4C_H +#define MD4C_H + +#ifdef __cplusplus + extern "C" { +#endif + +#if defined MD4C_USE_UTF16 + /* Magic to support UTF-16. Note that in order to use it, you have to define + * the macro MD4C_USE_UTF16 both when building MD4C as well as when + * including this header in your code. */ + #ifdef _WIN32 + #include + typedef WCHAR MD_CHAR; + #else + #error MD4C_USE_UTF16 is only supported on Windows. + #endif +#else + typedef char MD_CHAR; +#endif + +typedef unsigned MD_SIZE; +typedef unsigned MD_OFFSET; + + +/* Block represents a part of document hierarchy structure like a paragraph + * or list item. + */ +typedef enum MD_BLOCKTYPE { + /* ... */ + MD_BLOCK_DOC = 0, + + /*
...
*/ + MD_BLOCK_QUOTE, + + /*
    ...
+ * Detail: Structure MD_BLOCK_UL_DETAIL. */ + MD_BLOCK_UL, + + /*
    ...
+ * Detail: Structure MD_BLOCK_OL_DETAIL. */ + MD_BLOCK_OL, + + /*
  • ...
  • + * Detail: Structure MD_BLOCK_LI_DETAIL. */ + MD_BLOCK_LI, + + /*
    */ + MD_BLOCK_HR, + + /*

    ...

    (for levels up to 6) + * Detail: Structure MD_BLOCK_H_DETAIL. */ + MD_BLOCK_H, + + /*
    ...
    + * Note the text lines within code blocks are terminated with '\n' + * instead of explicit MD_TEXT_BR. */ + MD_BLOCK_CODE, + + /* Raw HTML block. This itself does not correspond to any particular HTML + * tag. The contents of it _is_ raw HTML source intended to be put + * in verbatim form to the HTML output. */ + MD_BLOCK_HTML, + + /*

    ...

    */ + MD_BLOCK_P, + + /* ...
    and its contents. + * Detail: Structure MD_BLOCK_TABLE_DETAIL (for MD_BLOCK_TABLE), + * structure MD_BLOCK_TD_DETAIL (for MD_BLOCK_TH and MD_BLOCK_TD) + * Note all of these are used only if extension MD_FLAG_TABLES is enabled. */ + MD_BLOCK_TABLE, + MD_BLOCK_THEAD, + MD_BLOCK_TBODY, + MD_BLOCK_TR, + MD_BLOCK_TH, + MD_BLOCK_TD +} MD_BLOCKTYPE; + +/* Span represents an in-line piece of a document which should be rendered with + * the same font, color and other attributes. A sequence of spans forms a block + * like paragraph or list item. */ +typedef enum MD_SPANTYPE { + /* ... */ + MD_SPAN_EM, + + /* ... */ + MD_SPAN_STRONG, + + /* ... + * Detail: Structure MD_SPAN_A_DETAIL. */ + MD_SPAN_A, + + /* ... + * Detail: Structure MD_SPAN_IMG_DETAIL. + * Note: Image text can contain nested spans and even nested images. + * If rendered into ALT attribute of HTML tag, it's responsibility + * of the parser to deal with it. + */ + MD_SPAN_IMG, + + /* ... */ + MD_SPAN_CODE, + + /* ... + * Note: Recognized only when MD_FLAG_STRIKETHROUGH is enabled. + */ + MD_SPAN_DEL, + + /* For recognizing inline ($) and display ($$) equations + * Note: Recognized only when MD_FLAG_LATEXMATHSPANS is enabled. + */ + MD_SPAN_LATEXMATH, + MD_SPAN_LATEXMATH_DISPLAY, + + /* Wiki links + * Note: Recognized only when MD_FLAG_WIKILINKS is enabled. + */ + MD_SPAN_WIKILINK, + + /* ... + * Note: Recognized only when MD_FLAG_UNDERLINE is enabled. */ + MD_SPAN_U +} MD_SPANTYPE; + +/* Text is the actual textual contents of span. */ +typedef enum MD_TEXTTYPE { + /* Normal text. */ + MD_TEXT_NORMAL = 0, + + /* NULL character. CommonMark requires replacing NULL character with + * the replacement char U+FFFD, so this allows caller to do that easily. */ + MD_TEXT_NULLCHAR, + + /* Line breaks. + * Note these are not sent from blocks with verbatim output (MD_BLOCK_CODE + * or MD_BLOCK_HTML). In such cases, '\n' is part of the text itself. */ + MD_TEXT_BR, /*
    (hard break) */ + MD_TEXT_SOFTBR, /* '\n' in source text where it is not semantically meaningful (soft break) */ + + /* Entity. + * (a) Named entity, e.g.   + * (Note MD4C does not have a list of known entities. + * Anything matching the regexp /&[A-Za-z][A-Za-z0-9]{1,47};/ is + * treated as a named entity.) + * (b) Numerical entity, e.g. Ӓ + * (c) Hexadecimal entity, e.g. ካ + * + * As MD4C is mostly encoding agnostic, application gets the verbatim + * entity text into the MD_PARSER::text_callback(). */ + MD_TEXT_ENTITY, + + /* Text in a code block (inside MD_BLOCK_CODE) or inlined code (`code`). + * If it is inside MD_BLOCK_CODE, it includes spaces for indentation and + * '\n' for new lines. MD_TEXT_BR and MD_TEXT_SOFTBR are not sent for this + * kind of text. */ + MD_TEXT_CODE, + + /* Text is a raw HTML. If it is contents of a raw HTML block (i.e. not + * an inline raw HTML), then MD_TEXT_BR and MD_TEXT_SOFTBR are not used. + * The text contains verbatim '\n' for the new lines. */ + MD_TEXT_HTML, + + /* Text is inside an equation. This is processed the same way as inlined code + * spans (`code`). */ + MD_TEXT_LATEXMATH +} MD_TEXTTYPE; + + +/* Alignment enumeration. */ +typedef enum MD_ALIGN { + MD_ALIGN_DEFAULT = 0, /* When unspecified. */ + MD_ALIGN_LEFT, + MD_ALIGN_CENTER, + MD_ALIGN_RIGHT +} MD_ALIGN; + + +/* String attribute. + * + * This wraps strings which are outside of a normal text flow and which are + * propagated within various detailed structures, but which still may contain + * string portions of different types like e.g. entities. + * + * So, for example, lets consider this image: + * + * ![image alt text](http://example.org/image.png 'foo " bar') + * + * The image alt text is propagated as a normal text via the MD_PARSER::text() + * callback. However, the image title ('foo " bar') is propagated as + * MD_ATTRIBUTE in MD_SPAN_IMG_DETAIL::title. + * + * Then the attribute MD_SPAN_IMG_DETAIL::title shall provide the following: + * -- [0]: "foo " (substr_types[0] == MD_TEXT_NORMAL; substr_offsets[0] == 0) + * -- [1]: """ (substr_types[1] == MD_TEXT_ENTITY; substr_offsets[1] == 4) + * -- [2]: " bar" (substr_types[2] == MD_TEXT_NORMAL; substr_offsets[2] == 10) + * -- [3]: (n/a) (n/a ; substr_offsets[3] == 14) + * + * Note that these invariants are always guaranteed: + * -- substr_offsets[0] == 0 + * -- substr_offsets[LAST+1] == size + * -- Currently, only MD_TEXT_NORMAL, MD_TEXT_ENTITY, MD_TEXT_NULLCHAR + * substrings can appear. This could change only of the specification + * changes. + */ +typedef struct MD_ATTRIBUTE { + const MD_CHAR* text; + MD_SIZE size; + const MD_TEXTTYPE* substr_types; + const MD_OFFSET* substr_offsets; +} MD_ATTRIBUTE; + + +/* Detailed info for MD_BLOCK_UL. */ +typedef struct MD_BLOCK_UL_DETAIL { + int is_tight; /* Non-zero if tight list, zero if loose. */ + MD_CHAR mark; /* Item bullet character in MarkDown source of the list, e.g. '-', '+', '*'. */ +} MD_BLOCK_UL_DETAIL; + +/* Detailed info for MD_BLOCK_OL. */ +typedef struct MD_BLOCK_OL_DETAIL { + unsigned start; /* Start index of the ordered list. */ + int is_tight; /* Non-zero if tight list, zero if loose. */ + MD_CHAR mark_delimiter; /* Character delimiting the item marks in MarkDown source, e.g. '.' or ')' */ +} MD_BLOCK_OL_DETAIL; + +/* Detailed info for MD_BLOCK_LI. */ +typedef struct MD_BLOCK_LI_DETAIL { + int is_task; /* Can be non-zero only with MD_FLAG_TASKLISTS */ + MD_CHAR task_mark; /* If is_task, then one of 'x', 'X' or ' '. Undefined otherwise. */ + MD_OFFSET task_mark_offset; /* If is_task, then offset in the input of the char between '[' and ']'. */ +} MD_BLOCK_LI_DETAIL; + +/* Detailed info for MD_BLOCK_H. */ +typedef struct MD_BLOCK_H_DETAIL { + unsigned level; /* Header level (1 - 6) */ +} MD_BLOCK_H_DETAIL; + +/* Detailed info for MD_BLOCK_CODE. */ +typedef struct MD_BLOCK_CODE_DETAIL { + MD_ATTRIBUTE info; + MD_ATTRIBUTE lang; + MD_CHAR fence_char; /* The character used for fenced code block; or zero for indented code block. */ +} MD_BLOCK_CODE_DETAIL; + +/* Detailed info for MD_BLOCK_TABLE. */ +typedef struct MD_BLOCK_TABLE_DETAIL { + unsigned col_count; /* Count of columns in the table. */ + unsigned head_row_count; /* Count of rows in the table header (currently always 1) */ + unsigned body_row_count; /* Count of rows in the table body */ +} MD_BLOCK_TABLE_DETAIL; + +/* Detailed info for MD_BLOCK_TH and MD_BLOCK_TD. */ +typedef struct MD_BLOCK_TD_DETAIL { + MD_ALIGN align; +} MD_BLOCK_TD_DETAIL; + +/* Detailed info for MD_SPAN_A. */ +typedef struct MD_SPAN_A_DETAIL { + MD_ATTRIBUTE href; + MD_ATTRIBUTE title; +} MD_SPAN_A_DETAIL; + +/* Detailed info for MD_SPAN_IMG. */ +typedef struct MD_SPAN_IMG_DETAIL { + MD_ATTRIBUTE src; + MD_ATTRIBUTE title; +} MD_SPAN_IMG_DETAIL; + +/* Detailed info for MD_SPAN_WIKILINK. */ +typedef struct MD_SPAN_WIKILINK { + MD_ATTRIBUTE target; +} MD_SPAN_WIKILINK_DETAIL; + +/* Flags specifying extensions/deviations from CommonMark specification. + * + * By default (when MD_PARSER::flags == 0), we follow CommonMark specification. + * The following flags may allow some extensions or deviations from it. + */ +#define MD_FLAG_COLLAPSEWHITESPACE 0x0001 /* In MD_TEXT_NORMAL, collapse non-trivial whitespace into single ' ' */ +#define MD_FLAG_PERMISSIVEATXHEADERS 0x0002 /* Do not require space in ATX headers ( ###header ) */ +#define MD_FLAG_PERMISSIVEURLAUTOLINKS 0x0004 /* Recognize URLs as autolinks even without '<', '>' */ +#define MD_FLAG_PERMISSIVEEMAILAUTOLINKS 0x0008 /* Recognize e-mails as autolinks even without '<', '>' and 'mailto:' */ +#define MD_FLAG_NOINDENTEDCODEBLOCKS 0x0010 /* Disable indented code blocks. (Only fenced code works.) */ +#define MD_FLAG_NOHTMLBLOCKS 0x0020 /* Disable raw HTML blocks. */ +#define MD_FLAG_NOHTMLSPANS 0x0040 /* Disable raw HTML (inline). */ +#define MD_FLAG_TABLES 0x0100 /* Enable tables extension. */ +#define MD_FLAG_STRIKETHROUGH 0x0200 /* Enable strikethrough extension. */ +#define MD_FLAG_PERMISSIVEWWWAUTOLINKS 0x0400 /* Enable WWW autolinks (even without any scheme prefix, if they begin with 'www.') */ +#define MD_FLAG_TASKLISTS 0x0800 /* Enable task list extension. */ +#define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */ +#define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */ +#define MD_FLAG_UNDERLINE 0x4000 /* Enable underline extension (and disables '_' for normal emphasis). */ + +#define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS) +#define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS) + +/* Convenient sets of flags corresponding to well-known Markdown dialects. + * + * Note we may only support subset of features of the referred dialect. + * The constant just enables those extensions which bring us as close as + * possible given what features we implement. + * + * ABI compatibility note: Meaning of these can change in time as new + * extensions, bringing the dialect closer to the original, are implemented. + */ +#define MD_DIALECT_COMMONMARK 0 +#define MD_DIALECT_GITHUB (MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_TASKLISTS) + +/* Parser structure. + */ +typedef struct MD_PARSER { + /* Reserved. Set to zero. + */ + unsigned abi_version; + + /* Dialect options. Bitmask of MD_FLAG_xxxx values. + */ + unsigned flags; + + /* Caller-provided rendering callbacks. + * + * For some block/span types, more detailed information is provided in a + * type-specific structure pointed by the argument 'detail'. + * + * The last argument of all callbacks, 'userdata', is just propagated from + * md_parse() and is available for any use by the application. + * + * Note any strings provided to the callbacks as their arguments or as + * members of any detail structure are generally not zero-terminated. + * Application has to take the respective size information into account. + * + * Any rendering callback may abort further parsing of the document by + * returning non-zero. + */ + int (*enter_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/); + int (*leave_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/); + + int (*enter_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/); + int (*leave_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/); + + int (*text)(MD_TEXTTYPE /*type*/, const MD_CHAR* /*text*/, MD_SIZE /*size*/, void* /*userdata*/); + + /* Debug callback. Optional (may be NULL). + * + * If provided and something goes wrong, this function gets called. + * This is intended for debugging and problem diagnosis for developers; + * it is not intended to provide any errors suitable for displaying to an + * end user. + */ + void (*debug_log)(const char* /*msg*/, void* /*userdata*/); + + /* Reserved. Set to NULL. + */ + void (*syntax)(void); +} MD_PARSER; + + +/* For backward compatibility. Do not use in new code. + */ +typedef MD_PARSER MD_RENDERER; + + +/* Parse the Markdown document stored in the string 'text' of size 'size'. + * The parser provides callbacks to be called during the parsing so the + * caller can render the document on the screen or convert the Markdown + * to another format. + * + * Zero is returned on success. If a runtime error occurs (e.g. a memory + * fails), -1 is returned. If the processing is aborted due any callback + * returning non-zero, the return value of the callback is returned. + */ +int md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata); + + +#ifdef __cplusplus + } /* extern "C" { */ +#endif + +#endif /* MD4C_H */ diff --git a/src/time-extension-functions.cc b/src/time-extension-functions.cc index 618778f7..449d60c9 100644 --- a/src/time-extension-functions.cc +++ b/src/time-extension-functions.cc @@ -193,7 +193,7 @@ time_extension_functions(struct FuncDef** basic_funcs, "To group log messages into five minute buckets and count " "them", "SELECT timeslice(log_time_msecs, '5m') AS slice, " - "count(1) FROM lnav_example_log GROUP BY slice", + "count(1)\n FROM lnav_example_log GROUP BY slice", }) .with_example({ "To group log messages by those before 4:30am and after", diff --git a/src/view_curses.cc b/src/view_curses.cc index 7f34b7f2..8f916286 100644 --- a/src/view_curses.cc +++ b/src/view_curses.cc @@ -258,6 +258,8 @@ view_curses::mvwattrline(WINDOW* window, short attr_fg = iter->sa_value.get(); if (attr_fg == view_colors::MATCH_COLOR_SEMANTIC) { attr_fg = vc.color_for_ident(al.to_string_fragment(iter)); + } else if (attr_fg < 8) { + attr_fg = vc.ansi_to_theme_color(attr_fg); } std::fill(&fg_color[attr_range.lr_start], &fg_color[attr_range.lr_end], @@ -906,6 +908,15 @@ view_colors::init_roles(const lnav_theme& lt, lt.lt_style_header[5], lt.lt_style_text, reporter); + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_HR)] + = this->to_attrs( + color_pair_base, lt, lt.lt_style_hr, lt.lt_style_text, reporter); + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_HYPERLINK)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_hyperlink, + lt.lt_style_text, + reporter); this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_LIST_GLYPH)] = this->to_attrs(color_pair_base, lt, @@ -918,6 +929,43 @@ view_colors::init_roles(const lnav_theme& lt, lt.lt_style_breadcrumb, lt.lt_style_text, reporter); + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_TABLE_BORDER)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_table_border, + lt.lt_style_text, + reporter); + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_TABLE_HEADER)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_table_header, + lt.lt_style_text, + reporter); + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_QUOTE_BORDER)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_quote_border, + lt.lt_style_text, + reporter); + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_QUOTED_TEXT)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_quoted_text, + lt.lt_style_text, + reporter); + this->vc_role_colors[lnav::enums::to_underlying( + role_t::VCR_FOOTNOTE_BORDER)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_footnote_border, + lt.lt_style_text, + reporter); + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_FOOTNOTE_TEXT)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_footnote_text, + lt.lt_style_text, + reporter); { style_config stitch_sc; @@ -1052,6 +1100,18 @@ view_colors::init_roles(const lnav_theme& lt, color_pair_base, lt, bar_sc, lt.lt_style_warn_status, reporter); } + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_QUOTED_CODE)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_quoted_code, + lt.lt_style_text, + reporter); + this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_CODE_BORDER)] + = this->to_attrs(color_pair_base, + lt, + lt.lt_style_code_border, + lt.lt_style_text, + reporter); this->vc_role_colors[lnav::enums::to_underlying(role_t::VCR_KEYWORD)] = this->to_attrs(color_pair_base, lt, diff --git a/src/view_helpers.cc b/src/view_helpers.cc index 96d3adce..6f4e2e6e 100644 --- a/src/view_helpers.cc +++ b/src/view_helpers.cc @@ -32,11 +32,15 @@ #include "base/humanize.hh" #include "base/itertools.hh" #include "config.h" +#include "document.sections.hh" #include "environ_vtab.hh" +#include "help-md.h" #include "help-txt.h" #include "intervaltree/IntervalTree.h" #include "lnav.hh" #include "lnav.indexing.hh" +#include "md2attr_line.hh" +#include "md4cpp.hh" #include "pretty_printer.hh" #include "shlex.hh" #include "sql_help.hh" @@ -47,6 +51,7 @@ #include "vtab_module.hh" using namespace std::chrono_literals; +using namespace lnav::roles::literals; const char* lnav_view_strings[LNV__MAX + 1] = { "log", @@ -130,7 +135,7 @@ public: const auto& tl = this->tds_lines[line]; const auto initial_size = crumbs.size(); - pretty_printer::hier_node* root_node; + lnav::document::hier_node* root_node{nullptr}; this->pss_hier_tree->template visit_overlapping( tl.tl_offset, @@ -144,7 +149,7 @@ public: | lnav::itertools::append(iv.value); auto poss_provider = [root_node, path]() { std::vector retval; - auto curr_node = pretty_printer::hier_node::lookup_path( + auto curr_node = lnav::document::hier_node::lookup_path( root_node, path); if (curr_node) { auto* parent_node = curr_node.value()->hn_parent; @@ -161,7 +166,7 @@ public: auto path_performer = [this, root_node, path]( const breadcrumb::crumb::key_t& value) { - auto curr_node = pretty_printer::hier_node::lookup_path( + auto curr_node = lnav::document::hier_node::lookup_path( root_node, path); if (!curr_node) { return; @@ -202,13 +207,13 @@ public: std::move(poss_provider), std::move(path_performer)); auto curr_node - = pretty_printer::hier_node::lookup_path(root_node, path); + = lnav::document::hier_node::lookup_path(root_node, path); if (curr_node && curr_node.value()->hn_parent->hn_children.size() != curr_node.value() ->hn_parent->hn_named_children.size()) { - auto node = pretty_printer::hier_node::lookup_path( + auto node = lnav::document::hier_node::lookup_path( root_node, path); crumbs.back().c_expected_input @@ -226,7 +231,7 @@ public: auto path = crumbs | lnav::itertools::skip(initial_size) | lnav::itertools::map(&breadcrumb::crumb::c_key); - auto node = pretty_printer::hier_node::lookup_path(root_node, path); + auto node = lnav::document::hier_node::lookup_path(root_node, path); if (node && !node.value()->hn_children.empty()) { auto poss_provider = [curr_node = node.value()]() { @@ -267,12 +272,12 @@ public: } using hier_tree_t - = interval_tree::IntervalTree; + = interval_tree::IntervalTree; using hier_interval_t - = interval_tree::Interval; + = interval_tree::Interval; - std::shared_ptr pss_interval_tree; - std::vector> pss_hier_nods; + std::shared_ptr pss_interval_tree; + std::vector> pss_hier_nods; std::shared_ptr pss_hier_tree; }; @@ -294,8 +299,8 @@ open_pretty_view() return; } - std::vector all_intervals; - std::vector> hier_nodes; + std::vector all_intervals; + std::vector> hier_nodes; std::vector hier_tree_vec; if (top_tc == log_tc) { logfile_sub_source& lss = lnav_data.ld_log_source; @@ -367,7 +372,7 @@ open_pretty_view() interval.stop += prefix_al.length(); } } - pretty_printer::hier_node::depth_first( + lnav::document::hier_node::depth_first( line_hier_root.get(), [line_off, prefix_len = prefix_al.length()](auto* hn) { if (line_off <= hn->hn_start) { @@ -383,7 +388,7 @@ open_pretty_view() interval.start += start_off; interval.stop += start_off; } - pretty_printer::hier_node::depth_first( + lnav::document::hier_node::depth_first( line_hier_root.get(), [start_off](auto* hn) { hn->hn_start += start_off; }); hier_nodes.emplace_back(std::move(line_hier_root)); @@ -424,7 +429,7 @@ open_pretty_view() } } auto* pts = new pretty_sub_source(); - pts->pss_interval_tree = std::make_shared( + pts->pss_interval_tree = std::make_shared( std::move(all_intervals)); pts->pss_hier_nods = std::move(hier_nodes); pts->pss_hier_tree = std::make_shared( @@ -438,6 +443,12 @@ open_pretty_view() pretty_tc->redo_search(); } +template +static void +ignore_case(const T&) +{ +} + static void build_all_help_text() { @@ -445,13 +456,15 @@ build_all_help_text() return; } - attr_line_t all_help_text; - shlex lexer(help_txt.to_string_fragment()); + shlex lexer(help_md.to_string_fragment()); std::string sub_help_text; lexer.with_ignore_quotes(true).eval( sub_help_text, lnav_data.ld_exec_context.ec_global_vars); - all_help_text.with_ansi_string(sub_help_text); + + md2attr_line mdal; + auto parse_res = md4cpp::parse(sub_help_text, mdal); + attr_line_t all_help_text = parse_res.unwrap(); std::map sql_funcs; std::map sql_keywords; @@ -470,9 +483,26 @@ build_all_help_text() } } + all_help_text.append("\n").append("Command Reference"_h2); + + for (const auto& cmd : lnav_commands) { + if (cmd.second->c_help.ht_summary == nullptr) { + continue; + } + all_help_text.append(2, '\n'); + format_help_text_for_term(cmd.second->c_help, 70, all_help_text); + if (!cmd.second->c_help.ht_example.empty()) { + all_help_text.append("\n"); + format_example_text_for_term( + cmd.second->c_help, eval_example, 90, all_help_text); + } + } + + all_help_text.append("\n").append("SQL Reference"_h2); + for (const auto& iter : sql_funcs) { all_help_text.append(2, '\n'); - format_help_text_for_term(*iter.second, 79, all_help_text); + format_help_text_for_term(*iter.second, 70, all_help_text); if (!iter.second->ht_example.empty()) { all_help_text.append(1, '\n'); format_example_text_for_term( @@ -482,7 +512,7 @@ build_all_help_text() for (const auto& iter : sql_keywords) { all_help_text.append(2, '\n'); - format_help_text_for_term(*iter.second, 79, all_help_text); + format_help_text_for_term(*iter.second, 70, all_help_text); if (!iter.second->ht_example.empty()) { all_help_text.append(1, '\n'); format_example_text_for_term( diff --git a/src/views_vtab.cc b/src/views_vtab.cc index 66d98d50..c185e23e 100644 --- a/src/views_vtab.cc +++ b/src/views_vtab.cc @@ -343,7 +343,7 @@ CREATE TABLE lnav_views ( tab->zErrMsg = sqlite3_mprintf( "Rows cannot be inserted into the lnav_views table"); return SQLITE_ERROR; - }; + } int update_row(sqlite3_vtab* tab, sqlite3_int64& index, @@ -433,7 +433,7 @@ CREATE TABLE lnav_view_stack ( } return SQLITE_OK; - }; + } int delete_row(sqlite3_vtab* tab, sqlite3_int64 rowid) { @@ -446,7 +446,7 @@ CREATE TABLE lnav_view_stack ( lnav_data.ld_last_view = *lnav_data.ld_view_stack.top(); lnav_data.ld_view_stack.pop_back(); return SQLITE_OK; - }; + } int insert_row(sqlite3_vtab* tab, sqlite3_int64& rowid_out, @@ -458,14 +458,14 @@ CREATE TABLE lnav_view_stack ( rowid_out = lnav_data.ld_view_stack.size() - 1; return SQLITE_OK; - }; + } int update_row(sqlite3_vtab* tab, sqlite3_int64& index) { tab->zErrMsg = sqlite3_mprintf("The lnav_view_stack table cannot be updated"); return SQLITE_ERROR; - }; + } }; struct lnav_view_filter_base { @@ -803,7 +803,7 @@ CREATE TABLE lnav_view_filters ( tc.set_needs_update(); return SQLITE_OK; - }; + } }; struct lnav_view_filter_stats diff --git a/src/xml-entities.json b/src/xml-entities.json new file mode 100644 index 00000000..557170b4 --- /dev/null +++ b/src/xml-entities.json @@ -0,0 +1,2233 @@ +{ + "Æ": { "codepoints": [198], "characters": "\u00C6" }, + "Æ": { "codepoints": [198], "characters": "\u00C6" }, + "&": { "codepoints": [38], "characters": "\u0026" }, + "&": { "codepoints": [38], "characters": "\u0026" }, + "Á": { "codepoints": [193], "characters": "\u00C1" }, + "Á": { "codepoints": [193], "characters": "\u00C1" }, + "Ă": { "codepoints": [258], "characters": "\u0102" }, + "Â": { "codepoints": [194], "characters": "\u00C2" }, + "Â": { "codepoints": [194], "characters": "\u00C2" }, + "А": { "codepoints": [1040], "characters": "\u0410" }, + "𝔄": { "codepoints": [120068], "characters": "\uD835\uDD04" }, + "À": { "codepoints": [192], "characters": "\u00C0" }, + "À": { "codepoints": [192], "characters": "\u00C0" }, + "Α": { "codepoints": [913], "characters": "\u0391" }, + "Ā": { "codepoints": [256], "characters": "\u0100" }, + "⩓": { "codepoints": [10835], "characters": "\u2A53" }, + "Ą": { "codepoints": [260], "characters": "\u0104" }, + "𝔸": { "codepoints": [120120], "characters": "\uD835\uDD38" }, + "⁡": { "codepoints": [8289], "characters": "\u2061" }, + "Å": { "codepoints": [197], "characters": "\u00C5" }, + "Å": { "codepoints": [197], "characters": "\u00C5" }, + "𝒜": { "codepoints": [119964], "characters": "\uD835\uDC9C" }, + "≔": { "codepoints": [8788], "characters": "\u2254" }, + "Ã": { "codepoints": [195], "characters": "\u00C3" }, + "Ã": { "codepoints": [195], "characters": "\u00C3" }, + "Ä": { "codepoints": [196], "characters": "\u00C4" }, + "Ä": { "codepoints": [196], "characters": "\u00C4" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "⫧": { "codepoints": [10983], "characters": "\u2AE7" }, + "⌆": { "codepoints": [8966], "characters": "\u2306" }, + "Б": { "codepoints": [1041], "characters": "\u0411" }, + "∵": { "codepoints": [8757], "characters": "\u2235" }, + "ℬ": { "codepoints": [8492], "characters": "\u212C" }, + "Β": { "codepoints": [914], "characters": "\u0392" }, + "𝔅": { "codepoints": [120069], "characters": "\uD835\uDD05" }, + "𝔹": { "codepoints": [120121], "characters": "\uD835\uDD39" }, + "˘": { "codepoints": [728], "characters": "\u02D8" }, + "ℬ": { "codepoints": [8492], "characters": "\u212C" }, + "≎": { "codepoints": [8782], "characters": "\u224E" }, + "Ч": { "codepoints": [1063], "characters": "\u0427" }, + "©": { "codepoints": [169], "characters": "\u00A9" }, + "©": { "codepoints": [169], "characters": "\u00A9" }, + "Ć": { "codepoints": [262], "characters": "\u0106" }, + "⋒": { "codepoints": [8914], "characters": "\u22D2" }, + "ⅅ": { "codepoints": [8517], "characters": "\u2145" }, + "ℭ": { "codepoints": [8493], "characters": "\u212D" }, + "Č": { "codepoints": [268], "characters": "\u010C" }, + "Ç": { "codepoints": [199], "characters": "\u00C7" }, + "Ç": { "codepoints": [199], "characters": "\u00C7" }, + "Ĉ": { "codepoints": [264], "characters": "\u0108" }, + "∰": { "codepoints": [8752], "characters": "\u2230" }, + "Ċ": { "codepoints": [266], "characters": "\u010A" }, + "¸": { "codepoints": [184], "characters": "\u00B8" }, + "·": { "codepoints": [183], "characters": "\u00B7" }, + "ℭ": { "codepoints": [8493], "characters": "\u212D" }, + "Χ": { "codepoints": [935], "characters": "\u03A7" }, + "⊙": { "codepoints": [8857], "characters": "\u2299" }, + "⊖": { "codepoints": [8854], "characters": "\u2296" }, + "⊕": { "codepoints": [8853], "characters": "\u2295" }, + "⊗": { "codepoints": [8855], "characters": "\u2297" }, + "∲": { "codepoints": [8754], "characters": "\u2232" }, + "”": { "codepoints": [8221], "characters": "\u201D" }, + "’": { "codepoints": [8217], "characters": "\u2019" }, + "∷": { "codepoints": [8759], "characters": "\u2237" }, + "⩴": { "codepoints": [10868], "characters": "\u2A74" }, + "≡": { "codepoints": [8801], "characters": "\u2261" }, + "∯": { "codepoints": [8751], "characters": "\u222F" }, + "∮": { "codepoints": [8750], "characters": "\u222E" }, + "ℂ": { "codepoints": [8450], "characters": "\u2102" }, + "∐": { "codepoints": [8720], "characters": "\u2210" }, + "∳": { "codepoints": [8755], "characters": "\u2233" }, + "⨯": { "codepoints": [10799], "characters": "\u2A2F" }, + "𝒞": { "codepoints": [119966], "characters": "\uD835\uDC9E" }, + "⋓": { "codepoints": [8915], "characters": "\u22D3" }, + "≍": { "codepoints": [8781], "characters": "\u224D" }, + "ⅅ": { "codepoints": [8517], "characters": "\u2145" }, + "⤑": { "codepoints": [10513], "characters": "\u2911" }, + "Ђ": { "codepoints": [1026], "characters": "\u0402" }, + "Ѕ": { "codepoints": [1029], "characters": "\u0405" }, + "Џ": { "codepoints": [1039], "characters": "\u040F" }, + "‡": { "codepoints": [8225], "characters": "\u2021" }, + "↡": { "codepoints": [8609], "characters": "\u21A1" }, + "⫤": { "codepoints": [10980], "characters": "\u2AE4" }, + "Ď": { "codepoints": [270], "characters": "\u010E" }, + "Д": { "codepoints": [1044], "characters": "\u0414" }, + "∇": { "codepoints": [8711], "characters": "\u2207" }, + "Δ": { "codepoints": [916], "characters": "\u0394" }, + "𝔇": { "codepoints": [120071], "characters": "\uD835\uDD07" }, + "´": { "codepoints": [180], "characters": "\u00B4" }, + "˙": { "codepoints": [729], "characters": "\u02D9" }, + "˝": { "codepoints": [733], "characters": "\u02DD" }, + "`": { "codepoints": [96], "characters": "\u0060" }, + "˜": { "codepoints": [732], "characters": "\u02DC" }, + "⋄": { "codepoints": [8900], "characters": "\u22C4" }, + "ⅆ": { "codepoints": [8518], "characters": "\u2146" }, + "𝔻": { "codepoints": [120123], "characters": "\uD835\uDD3B" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "⃜": { "codepoints": [8412], "characters": "\u20DC" }, + "≐": { "codepoints": [8784], "characters": "\u2250" }, + "∯": { "codepoints": [8751], "characters": "\u222F" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "⇓": { "codepoints": [8659], "characters": "\u21D3" }, + "⇐": { "codepoints": [8656], "characters": "\u21D0" }, + "⇔": { "codepoints": [8660], "characters": "\u21D4" }, + "⫤": { "codepoints": [10980], "characters": "\u2AE4" }, + "⟸": { "codepoints": [10232], "characters": "\u27F8" }, + "⟺": { "codepoints": [10234], "characters": "\u27FA" }, + "⟹": { "codepoints": [10233], "characters": "\u27F9" }, + "⇒": { "codepoints": [8658], "characters": "\u21D2" }, + "⊨": { "codepoints": [8872], "characters": "\u22A8" }, + "⇑": { "codepoints": [8657], "characters": "\u21D1" }, + "⇕": { "codepoints": [8661], "characters": "\u21D5" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "↓": { "codepoints": [8595], "characters": "\u2193" }, + "⤓": { "codepoints": [10515], "characters": "\u2913" }, + "⇵": { "codepoints": [8693], "characters": "\u21F5" }, + "̑": { "codepoints": [785], "characters": "\u0311" }, + "⥐": { "codepoints": [10576], "characters": "\u2950" }, + "⥞": { "codepoints": [10590], "characters": "\u295E" }, + "↽": { "codepoints": [8637], "characters": "\u21BD" }, + "⥖": { "codepoints": [10582], "characters": "\u2956" }, + "⥟": { "codepoints": [10591], "characters": "\u295F" }, + "⇁": { "codepoints": [8641], "characters": "\u21C1" }, + "⥗": { "codepoints": [10583], "characters": "\u2957" }, + "⊤": { "codepoints": [8868], "characters": "\u22A4" }, + "↧": { "codepoints": [8615], "characters": "\u21A7" }, + "⇓": { "codepoints": [8659], "characters": "\u21D3" }, + "𝒟": { "codepoints": [119967], "characters": "\uD835\uDC9F" }, + "Đ": { "codepoints": [272], "characters": "\u0110" }, + "Ŋ": { "codepoints": [330], "characters": "\u014A" }, + "Ð": { "codepoints": [208], "characters": "\u00D0" }, + "Ð": { "codepoints": [208], "characters": "\u00D0" }, + "É": { "codepoints": [201], "characters": "\u00C9" }, + "É": { "codepoints": [201], "characters": "\u00C9" }, + "Ě": { "codepoints": [282], "characters": "\u011A" }, + "Ê": { "codepoints": [202], "characters": "\u00CA" }, + "Ê": { "codepoints": [202], "characters": "\u00CA" }, + "Э": { "codepoints": [1069], "characters": "\u042D" }, + "Ė": { "codepoints": [278], "characters": "\u0116" }, + "𝔈": { "codepoints": [120072], "characters": "\uD835\uDD08" }, + "È": { "codepoints": [200], "characters": "\u00C8" }, + "È": { "codepoints": [200], "characters": "\u00C8" }, + "∈": { "codepoints": [8712], "characters": "\u2208" }, + "Ē": { "codepoints": [274], "characters": "\u0112" }, + "◻": { "codepoints": [9723], "characters": "\u25FB" }, + "▫": { "codepoints": [9643], "characters": "\u25AB" }, + "Ę": { "codepoints": [280], "characters": "\u0118" }, + "𝔼": { "codepoints": [120124], "characters": "\uD835\uDD3C" }, + "Ε": { "codepoints": [917], "characters": "\u0395" }, + "⩵": { "codepoints": [10869], "characters": "\u2A75" }, + "≂": { "codepoints": [8770], "characters": "\u2242" }, + "⇌": { "codepoints": [8652], "characters": "\u21CC" }, + "ℰ": { "codepoints": [8496], "characters": "\u2130" }, + "⩳": { "codepoints": [10867], "characters": "\u2A73" }, + "Η": { "codepoints": [919], "characters": "\u0397" }, + "Ë": { "codepoints": [203], "characters": "\u00CB" }, + "Ë": { "codepoints": [203], "characters": "\u00CB" }, + "∃": { "codepoints": [8707], "characters": "\u2203" }, + "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, + "Ф": { "codepoints": [1060], "characters": "\u0424" }, + "𝔉": { "codepoints": [120073], "characters": "\uD835\uDD09" }, + "◼": { "codepoints": [9724], "characters": "\u25FC" }, + "▪": { "codepoints": [9642], "characters": "\u25AA" }, + "𝔽": { "codepoints": [120125], "characters": "\uD835\uDD3D" }, + "∀": { "codepoints": [8704], "characters": "\u2200" }, + "ℱ": { "codepoints": [8497], "characters": "\u2131" }, + "ℱ": { "codepoints": [8497], "characters": "\u2131" }, + "Ѓ": { "codepoints": [1027], "characters": "\u0403" }, + ">": { "codepoints": [62], "characters": "\u003E" }, + ">": { "codepoints": [62], "characters": "\u003E" }, + "Γ": { "codepoints": [915], "characters": "\u0393" }, + "Ϝ": { "codepoints": [988], "characters": "\u03DC" }, + "Ğ": { "codepoints": [286], "characters": "\u011E" }, + "Ģ": { "codepoints": [290], "characters": "\u0122" }, + "Ĝ": { "codepoints": [284], "characters": "\u011C" }, + "Г": { "codepoints": [1043], "characters": "\u0413" }, + "Ġ": { "codepoints": [288], "characters": "\u0120" }, + "𝔊": { "codepoints": [120074], "characters": "\uD835\uDD0A" }, + "⋙": { "codepoints": [8921], "characters": "\u22D9" }, + "𝔾": { "codepoints": [120126], "characters": "\uD835\uDD3E" }, + "≥": { "codepoints": [8805], "characters": "\u2265" }, + "⋛": { "codepoints": [8923], "characters": "\u22DB" }, + "≧": { "codepoints": [8807], "characters": "\u2267" }, + "⪢": { "codepoints": [10914], "characters": "\u2AA2" }, + "≷": { "codepoints": [8823], "characters": "\u2277" }, + "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, + "≳": { "codepoints": [8819], "characters": "\u2273" }, + "𝒢": { "codepoints": [119970], "characters": "\uD835\uDCA2" }, + "≫": { "codepoints": [8811], "characters": "\u226B" }, + "Ъ": { "codepoints": [1066], "characters": "\u042A" }, + "ˇ": { "codepoints": [711], "characters": "\u02C7" }, + "^": { "codepoints": [94], "characters": "\u005E" }, + "Ĥ": { "codepoints": [292], "characters": "\u0124" }, + "ℌ": { "codepoints": [8460], "characters": "\u210C" }, + "ℋ": { "codepoints": [8459], "characters": "\u210B" }, + "ℍ": { "codepoints": [8461], "characters": "\u210D" }, + "─": { "codepoints": [9472], "characters": "\u2500" }, + "ℋ": { "codepoints": [8459], "characters": "\u210B" }, + "Ħ": { "codepoints": [294], "characters": "\u0126" }, + "≎": { "codepoints": [8782], "characters": "\u224E" }, + "≏": { "codepoints": [8783], "characters": "\u224F" }, + "Е": { "codepoints": [1045], "characters": "\u0415" }, + "IJ": { "codepoints": [306], "characters": "\u0132" }, + "Ё": { "codepoints": [1025], "characters": "\u0401" }, + "Í": { "codepoints": [205], "characters": "\u00CD" }, + "Í": { "codepoints": [205], "characters": "\u00CD" }, + "Î": { "codepoints": [206], "characters": "\u00CE" }, + "Î": { "codepoints": [206], "characters": "\u00CE" }, + "И": { "codepoints": [1048], "characters": "\u0418" }, + "İ": { "codepoints": [304], "characters": "\u0130" }, + "ℑ": { "codepoints": [8465], "characters": "\u2111" }, + "Ì": { "codepoints": [204], "characters": "\u00CC" }, + "Ì": { "codepoints": [204], "characters": "\u00CC" }, + "ℑ": { "codepoints": [8465], "characters": "\u2111" }, + "Ī": { "codepoints": [298], "characters": "\u012A" }, + "ⅈ": { "codepoints": [8520], "characters": "\u2148" }, + "⇒": { "codepoints": [8658], "characters": "\u21D2" }, + "∬": { "codepoints": [8748], "characters": "\u222C" }, + "∫": { "codepoints": [8747], "characters": "\u222B" }, + "⋂": { "codepoints": [8898], "characters": "\u22C2" }, + "⁣": { "codepoints": [8291], "characters": "\u2063" }, + "⁢": { "codepoints": [8290], "characters": "\u2062" }, + "Į": { "codepoints": [302], "characters": "\u012E" }, + "𝕀": { "codepoints": [120128], "characters": "\uD835\uDD40" }, + "Ι": { "codepoints": [921], "characters": "\u0399" }, + "ℐ": { "codepoints": [8464], "characters": "\u2110" }, + "Ĩ": { "codepoints": [296], "characters": "\u0128" }, + "І": { "codepoints": [1030], "characters": "\u0406" }, + "Ï": { "codepoints": [207], "characters": "\u00CF" }, + "Ï": { "codepoints": [207], "characters": "\u00CF" }, + "Ĵ": { "codepoints": [308], "characters": "\u0134" }, + "Й": { "codepoints": [1049], "characters": "\u0419" }, + "𝔍": { "codepoints": [120077], "characters": "\uD835\uDD0D" }, + "𝕁": { "codepoints": [120129], "characters": "\uD835\uDD41" }, + "𝒥": { "codepoints": [119973], "characters": "\uD835\uDCA5" }, + "Ј": { "codepoints": [1032], "characters": "\u0408" }, + "Є": { "codepoints": [1028], "characters": "\u0404" }, + "Х": { "codepoints": [1061], "characters": "\u0425" }, + "Ќ": { "codepoints": [1036], "characters": "\u040C" }, + "Κ": { "codepoints": [922], "characters": "\u039A" }, + "Ķ": { "codepoints": [310], "characters": "\u0136" }, + "К": { "codepoints": [1050], "characters": "\u041A" }, + "𝔎": { "codepoints": [120078], "characters": "\uD835\uDD0E" }, + "𝕂": { "codepoints": [120130], "characters": "\uD835\uDD42" }, + "𝒦": { "codepoints": [119974], "characters": "\uD835\uDCA6" }, + "Љ": { "codepoints": [1033], "characters": "\u0409" }, + "<": { "codepoints": [60], "characters": "\u003C" }, + "<": { "codepoints": [60], "characters": "\u003C" }, + "Ĺ": { "codepoints": [313], "characters": "\u0139" }, + "Λ": { "codepoints": [923], "characters": "\u039B" }, + "⟪": { "codepoints": [10218], "characters": "\u27EA" }, + "ℒ": { "codepoints": [8466], "characters": "\u2112" }, + "↞": { "codepoints": [8606], "characters": "\u219E" }, + "Ľ": { "codepoints": [317], "characters": "\u013D" }, + "Ļ": { "codepoints": [315], "characters": "\u013B" }, + "Л": { "codepoints": [1051], "characters": "\u041B" }, + "⟨": { "codepoints": [10216], "characters": "\u27E8" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "⇤": { "codepoints": [8676], "characters": "\u21E4" }, + "⇆": { "codepoints": [8646], "characters": "\u21C6" }, + "⌈": { "codepoints": [8968], "characters": "\u2308" }, + "⟦": { "codepoints": [10214], "characters": "\u27E6" }, + "⥡": { "codepoints": [10593], "characters": "\u2961" }, + "⇃": { "codepoints": [8643], "characters": "\u21C3" }, + "⥙": { "codepoints": [10585], "characters": "\u2959" }, + "⌊": { "codepoints": [8970], "characters": "\u230A" }, + "↔": { "codepoints": [8596], "characters": "\u2194" }, + "⥎": { "codepoints": [10574], "characters": "\u294E" }, + "⊣": { "codepoints": [8867], "characters": "\u22A3" }, + "↤": { "codepoints": [8612], "characters": "\u21A4" }, + "⥚": { "codepoints": [10586], "characters": "\u295A" }, + "⊲": { "codepoints": [8882], "characters": "\u22B2" }, + "⧏": { "codepoints": [10703], "characters": "\u29CF" }, + "⊴": { "codepoints": [8884], "characters": "\u22B4" }, + "⥑": { "codepoints": [10577], "characters": "\u2951" }, + "⥠": { "codepoints": [10592], "characters": "\u2960" }, + "↿": { "codepoints": [8639], "characters": "\u21BF" }, + "⥘": { "codepoints": [10584], "characters": "\u2958" }, + "↼": { "codepoints": [8636], "characters": "\u21BC" }, + "⥒": { "codepoints": [10578], "characters": "\u2952" }, + "⇐": { "codepoints": [8656], "characters": "\u21D0" }, + "⇔": { "codepoints": [8660], "characters": "\u21D4" }, + "⋚": { "codepoints": [8922], "characters": "\u22DA" }, + "≦": { "codepoints": [8806], "characters": "\u2266" }, + "≶": { "codepoints": [8822], "characters": "\u2276" }, + "⪡": { "codepoints": [10913], "characters": "\u2AA1" }, + "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, + "≲": { "codepoints": [8818], "characters": "\u2272" }, + "𝔏": { "codepoints": [120079], "characters": "\uD835\uDD0F" }, + "⋘": { "codepoints": [8920], "characters": "\u22D8" }, + "⇚": { "codepoints": [8666], "characters": "\u21DA" }, + "Ŀ": { "codepoints": [319], "characters": "\u013F" }, + "⟵": { "codepoints": [10229], "characters": "\u27F5" }, + "⟷": { "codepoints": [10231], "characters": "\u27F7" }, + "⟶": { "codepoints": [10230], "characters": "\u27F6" }, + "⟸": { "codepoints": [10232], "characters": "\u27F8" }, + "⟺": { "codepoints": [10234], "characters": "\u27FA" }, + "⟹": { "codepoints": [10233], "characters": "\u27F9" }, + "𝕃": { "codepoints": [120131], "characters": "\uD835\uDD43" }, + "↙": { "codepoints": [8601], "characters": "\u2199" }, + "↘": { "codepoints": [8600], "characters": "\u2198" }, + "ℒ": { "codepoints": [8466], "characters": "\u2112" }, + "↰": { "codepoints": [8624], "characters": "\u21B0" }, + "Ł": { "codepoints": [321], "characters": "\u0141" }, + "≪": { "codepoints": [8810], "characters": "\u226A" }, + "⤅": { "codepoints": [10501], "characters": "\u2905" }, + "М": { "codepoints": [1052], "characters": "\u041C" }, + " ": { "codepoints": [8287], "characters": "\u205F" }, + "ℳ": { "codepoints": [8499], "characters": "\u2133" }, + "𝔐": { "codepoints": [120080], "characters": "\uD835\uDD10" }, + "∓": { "codepoints": [8723], "characters": "\u2213" }, + "𝕄": { "codepoints": [120132], "characters": "\uD835\uDD44" }, + "ℳ": { "codepoints": [8499], "characters": "\u2133" }, + "Μ": { "codepoints": [924], "characters": "\u039C" }, + "Њ": { "codepoints": [1034], "characters": "\u040A" }, + "Ń": { "codepoints": [323], "characters": "\u0143" }, + "Ň": { "codepoints": [327], "characters": "\u0147" }, + "Ņ": { "codepoints": [325], "characters": "\u0145" }, + "Н": { "codepoints": [1053], "characters": "\u041D" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "≫": { "codepoints": [8811], "characters": "\u226B" }, + "≪": { "codepoints": [8810], "characters": "\u226A" }, + " ": { "codepoints": [10], "characters": "\u000A" }, + "𝔑": { "codepoints": [120081], "characters": "\uD835\uDD11" }, + "⁠": { "codepoints": [8288], "characters": "\u2060" }, + " ": { "codepoints": [160], "characters": "\u00A0" }, + "ℕ": { "codepoints": [8469], "characters": "\u2115" }, + "⫬": { "codepoints": [10988], "characters": "\u2AEC" }, + "≢": { "codepoints": [8802], "characters": "\u2262" }, + "≭": { "codepoints": [8813], "characters": "\u226D" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "∉": { "codepoints": [8713], "characters": "\u2209" }, + "≠": { "codepoints": [8800], "characters": "\u2260" }, + "≂̸": { "codepoints": [8770, 824], "characters": "\u2242\u0338" }, + "∄": { "codepoints": [8708], "characters": "\u2204" }, + "≯": { "codepoints": [8815], "characters": "\u226F" }, + "≱": { "codepoints": [8817], "characters": "\u2271" }, + "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, + "≫̸": { "codepoints": [8811, 824], "characters": "\u226B\u0338" }, + "≹": { "codepoints": [8825], "characters": "\u2279" }, + "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, + "≵": { "codepoints": [8821], "characters": "\u2275" }, + "≎̸": { "codepoints": [8782, 824], "characters": "\u224E\u0338" }, + "≏̸": { "codepoints": [8783, 824], "characters": "\u224F\u0338" }, + "⋪": { "codepoints": [8938], "characters": "\u22EA" }, + "⧏̸": { "codepoints": [10703, 824], "characters": "\u29CF\u0338" }, + "⋬": { "codepoints": [8940], "characters": "\u22EC" }, + "≮": { "codepoints": [8814], "characters": "\u226E" }, + "≰": { "codepoints": [8816], "characters": "\u2270" }, + "≸": { "codepoints": [8824], "characters": "\u2278" }, + "≪̸": { "codepoints": [8810, 824], "characters": "\u226A\u0338" }, + "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, + "≴": { "codepoints": [8820], "characters": "\u2274" }, + "⪢̸": { "codepoints": [10914, 824], "characters": "\u2AA2\u0338" }, + "⪡̸": { "codepoints": [10913, 824], "characters": "\u2AA1\u0338" }, + "⊀": { "codepoints": [8832], "characters": "\u2280" }, + "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, + "⋠": { "codepoints": [8928], "characters": "\u22E0" }, + "∌": { "codepoints": [8716], "characters": "\u220C" }, + "⋫": { "codepoints": [8939], "characters": "\u22EB" }, + "⧐̸": { "codepoints": [10704, 824], "characters": "\u29D0\u0338" }, + "⋭": { "codepoints": [8941], "characters": "\u22ED" }, + "⊏̸": { "codepoints": [8847, 824], "characters": "\u228F\u0338" }, + "⋢": { "codepoints": [8930], "characters": "\u22E2" }, + "⊐̸": { "codepoints": [8848, 824], "characters": "\u2290\u0338" }, + "⋣": { "codepoints": [8931], "characters": "\u22E3" }, + "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, + "⊈": { "codepoints": [8840], "characters": "\u2288" }, + "⊁": { "codepoints": [8833], "characters": "\u2281" }, + "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, + "⋡": { "codepoints": [8929], "characters": "\u22E1" }, + "≿̸": { "codepoints": [8831, 824], "characters": "\u227F\u0338" }, + "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, + "⊉": { "codepoints": [8841], "characters": "\u2289" }, + "≁": { "codepoints": [8769], "characters": "\u2241" }, + "≄": { "codepoints": [8772], "characters": "\u2244" }, + "≇": { "codepoints": [8775], "characters": "\u2247" }, + "≉": { "codepoints": [8777], "characters": "\u2249" }, + "∤": { "codepoints": [8740], "characters": "\u2224" }, + "𝒩": { "codepoints": [119977], "characters": "\uD835\uDCA9" }, + "Ñ": { "codepoints": [209], "characters": "\u00D1" }, + "Ñ": { "codepoints": [209], "characters": "\u00D1" }, + "Ν": { "codepoints": [925], "characters": "\u039D" }, + "Œ": { "codepoints": [338], "characters": "\u0152" }, + "Ó": { "codepoints": [211], "characters": "\u00D3" }, + "Ó": { "codepoints": [211], "characters": "\u00D3" }, + "Ô": { "codepoints": [212], "characters": "\u00D4" }, + "Ô": { "codepoints": [212], "characters": "\u00D4" }, + "О": { "codepoints": [1054], "characters": "\u041E" }, + "Ő": { "codepoints": [336], "characters": "\u0150" }, + "𝔒": { "codepoints": [120082], "characters": "\uD835\uDD12" }, + "Ò": { "codepoints": [210], "characters": "\u00D2" }, + "Ò": { "codepoints": [210], "characters": "\u00D2" }, + "Ō": { "codepoints": [332], "characters": "\u014C" }, + "Ω": { "codepoints": [937], "characters": "\u03A9" }, + "Ο": { "codepoints": [927], "characters": "\u039F" }, + "𝕆": { "codepoints": [120134], "characters": "\uD835\uDD46" }, + "“": { "codepoints": [8220], "characters": "\u201C" }, + "‘": { "codepoints": [8216], "characters": "\u2018" }, + "⩔": { "codepoints": [10836], "characters": "\u2A54" }, + "𝒪": { "codepoints": [119978], "characters": "\uD835\uDCAA" }, + "Ø": { "codepoints": [216], "characters": "\u00D8" }, + "Ø": { "codepoints": [216], "characters": "\u00D8" }, + "Õ": { "codepoints": [213], "characters": "\u00D5" }, + "Õ": { "codepoints": [213], "characters": "\u00D5" }, + "⨷": { "codepoints": [10807], "characters": "\u2A37" }, + "Ö": { "codepoints": [214], "characters": "\u00D6" }, + "Ö": { "codepoints": [214], "characters": "\u00D6" }, + "‾": { "codepoints": [8254], "characters": "\u203E" }, + "⏞": { "codepoints": [9182], "characters": "\u23DE" }, + "⎴": { "codepoints": [9140], "characters": "\u23B4" }, + "⏜": { "codepoints": [9180], "characters": "\u23DC" }, + "∂": { "codepoints": [8706], "characters": "\u2202" }, + "П": { "codepoints": [1055], "characters": "\u041F" }, + "𝔓": { "codepoints": [120083], "characters": "\uD835\uDD13" }, + "Φ": { "codepoints": [934], "characters": "\u03A6" }, + "Π": { "codepoints": [928], "characters": "\u03A0" }, + "±": { "codepoints": [177], "characters": "\u00B1" }, + "ℌ": { "codepoints": [8460], "characters": "\u210C" }, + "ℙ": { "codepoints": [8473], "characters": "\u2119" }, + "⪻": { "codepoints": [10939], "characters": "\u2ABB" }, + "≺": { "codepoints": [8826], "characters": "\u227A" }, + "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, + "≼": { "codepoints": [8828], "characters": "\u227C" }, + "≾": { "codepoints": [8830], "characters": "\u227E" }, + "″": { "codepoints": [8243], "characters": "\u2033" }, + "∏": { "codepoints": [8719], "characters": "\u220F" }, + "∷": { "codepoints": [8759], "characters": "\u2237" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "𝒫": { "codepoints": [119979], "characters": "\uD835\uDCAB" }, + "Ψ": { "codepoints": [936], "characters": "\u03A8" }, + """: { "codepoints": [34], "characters": "\u0022" }, + """: { "codepoints": [34], "characters": "\u0022" }, + "𝔔": { "codepoints": [120084], "characters": "\uD835\uDD14" }, + "ℚ": { "codepoints": [8474], "characters": "\u211A" }, + "𝒬": { "codepoints": [119980], "characters": "\uD835\uDCAC" }, + "⤐": { "codepoints": [10512], "characters": "\u2910" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "Ŕ": { "codepoints": [340], "characters": "\u0154" }, + "⟫": { "codepoints": [10219], "characters": "\u27EB" }, + "↠": { "codepoints": [8608], "characters": "\u21A0" }, + "⤖": { "codepoints": [10518], "characters": "\u2916" }, + "Ř": { "codepoints": [344], "characters": "\u0158" }, + "Ŗ": { "codepoints": [342], "characters": "\u0156" }, + "Р": { "codepoints": [1056], "characters": "\u0420" }, + "ℜ": { "codepoints": [8476], "characters": "\u211C" }, + "∋": { "codepoints": [8715], "characters": "\u220B" }, + "⇋": { "codepoints": [8651], "characters": "\u21CB" }, + "⥯": { "codepoints": [10607], "characters": "\u296F" }, + "ℜ": { "codepoints": [8476], "characters": "\u211C" }, + "Ρ": { "codepoints": [929], "characters": "\u03A1" }, + "⟩": { "codepoints": [10217], "characters": "\u27E9" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "⇥": { "codepoints": [8677], "characters": "\u21E5" }, + "⇄": { "codepoints": [8644], "characters": "\u21C4" }, + "⌉": { "codepoints": [8969], "characters": "\u2309" }, + "⟧": { "codepoints": [10215], "characters": "\u27E7" }, + "⥝": { "codepoints": [10589], "characters": "\u295D" }, + "⇂": { "codepoints": [8642], "characters": "\u21C2" }, + "⥕": { "codepoints": [10581], "characters": "\u2955" }, + "⌋": { "codepoints": [8971], "characters": "\u230B" }, + "⊢": { "codepoints": [8866], "characters": "\u22A2" }, + "↦": { "codepoints": [8614], "characters": "\u21A6" }, + "⥛": { "codepoints": [10587], "characters": "\u295B" }, + "⊳": { "codepoints": [8883], "characters": "\u22B3" }, + "⧐": { "codepoints": [10704], "characters": "\u29D0" }, + "⊵": { "codepoints": [8885], "characters": "\u22B5" }, + "⥏": { "codepoints": [10575], "characters": "\u294F" }, + "⥜": { "codepoints": [10588], "characters": "\u295C" }, + "↾": { "codepoints": [8638], "characters": "\u21BE" }, + "⥔": { "codepoints": [10580], "characters": "\u2954" }, + "⇀": { "codepoints": [8640], "characters": "\u21C0" }, + "⥓": { "codepoints": [10579], "characters": "\u2953" }, + "⇒": { "codepoints": [8658], "characters": "\u21D2" }, + "ℝ": { "codepoints": [8477], "characters": "\u211D" }, + "⥰": { "codepoints": [10608], "characters": "\u2970" }, + "⇛": { "codepoints": [8667], "characters": "\u21DB" }, + "ℛ": { "codepoints": [8475], "characters": "\u211B" }, + "↱": { "codepoints": [8625], "characters": "\u21B1" }, + "⧴": { "codepoints": [10740], "characters": "\u29F4" }, + "Щ": { "codepoints": [1065], "characters": "\u0429" }, + "Ш": { "codepoints": [1064], "characters": "\u0428" }, + "Ь": { "codepoints": [1068], "characters": "\u042C" }, + "Ś": { "codepoints": [346], "characters": "\u015A" }, + "⪼": { "codepoints": [10940], "characters": "\u2ABC" }, + "Š": { "codepoints": [352], "characters": "\u0160" }, + "Ş": { "codepoints": [350], "characters": "\u015E" }, + "Ŝ": { "codepoints": [348], "characters": "\u015C" }, + "С": { "codepoints": [1057], "characters": "\u0421" }, + "𝔖": { "codepoints": [120086], "characters": "\uD835\uDD16" }, + "↓": { "codepoints": [8595], "characters": "\u2193" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "↑": { "codepoints": [8593], "characters": "\u2191" }, + "Σ": { "codepoints": [931], "characters": "\u03A3" }, + "∘": { "codepoints": [8728], "characters": "\u2218" }, + "𝕊": { "codepoints": [120138], "characters": "\uD835\uDD4A" }, + "√": { "codepoints": [8730], "characters": "\u221A" }, + "□": { "codepoints": [9633], "characters": "\u25A1" }, + "⊓": { "codepoints": [8851], "characters": "\u2293" }, + "⊏": { "codepoints": [8847], "characters": "\u228F" }, + "⊑": { "codepoints": [8849], "characters": "\u2291" }, + "⊐": { "codepoints": [8848], "characters": "\u2290" }, + "⊒": { "codepoints": [8850], "characters": "\u2292" }, + "⊔": { "codepoints": [8852], "characters": "\u2294" }, + "𝒮": { "codepoints": [119982], "characters": "\uD835\uDCAE" }, + "⋆": { "codepoints": [8902], "characters": "\u22C6" }, + "⋐": { "codepoints": [8912], "characters": "\u22D0" }, + "⋐": { "codepoints": [8912], "characters": "\u22D0" }, + "⊆": { "codepoints": [8838], "characters": "\u2286" }, + "≻": { "codepoints": [8827], "characters": "\u227B" }, + "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, + "≽": { "codepoints": [8829], "characters": "\u227D" }, + "≿": { "codepoints": [8831], "characters": "\u227F" }, + "∋": { "codepoints": [8715], "characters": "\u220B" }, + "∑": { "codepoints": [8721], "characters": "\u2211" }, + "⋑": { "codepoints": [8913], "characters": "\u22D1" }, + "⊃": { "codepoints": [8835], "characters": "\u2283" }, + "⊇": { "codepoints": [8839], "characters": "\u2287" }, + "⋑": { "codepoints": [8913], "characters": "\u22D1" }, + "Þ": { "codepoints": [222], "characters": "\u00DE" }, + "Þ": { "codepoints": [222], "characters": "\u00DE" }, + "™": { "codepoints": [8482], "characters": "\u2122" }, + "Ћ": { "codepoints": [1035], "characters": "\u040B" }, + "Ц": { "codepoints": [1062], "characters": "\u0426" }, + " ": { "codepoints": [9], "characters": "\u0009" }, + "Τ": { "codepoints": [932], "characters": "\u03A4" }, + "Ť": { "codepoints": [356], "characters": "\u0164" }, + "Ţ": { "codepoints": [354], "characters": "\u0162" }, + "Т": { "codepoints": [1058], "characters": "\u0422" }, + "𝔗": { "codepoints": [120087], "characters": "\uD835\uDD17" }, + "∴": { "codepoints": [8756], "characters": "\u2234" }, + "Θ": { "codepoints": [920], "characters": "\u0398" }, + "  ": { "codepoints": [8287, 8202], "characters": "\u205F\u200A" }, + " ": { "codepoints": [8201], "characters": "\u2009" }, + "∼": { "codepoints": [8764], "characters": "\u223C" }, + "≃": { "codepoints": [8771], "characters": "\u2243" }, + "≅": { "codepoints": [8773], "characters": "\u2245" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "𝕋": { "codepoints": [120139], "characters": "\uD835\uDD4B" }, + "⃛": { "codepoints": [8411], "characters": "\u20DB" }, + "𝒯": { "codepoints": [119983], "characters": "\uD835\uDCAF" }, + "Ŧ": { "codepoints": [358], "characters": "\u0166" }, + "Ú": { "codepoints": [218], "characters": "\u00DA" }, + "Ú": { "codepoints": [218], "characters": "\u00DA" }, + "↟": { "codepoints": [8607], "characters": "\u219F" }, + "⥉": { "codepoints": [10569], "characters": "\u2949" }, + "Ў": { "codepoints": [1038], "characters": "\u040E" }, + "Ŭ": { "codepoints": [364], "characters": "\u016C" }, + "Û": { "codepoints": [219], "characters": "\u00DB" }, + "Û": { "codepoints": [219], "characters": "\u00DB" }, + "У": { "codepoints": [1059], "characters": "\u0423" }, + "Ű": { "codepoints": [368], "characters": "\u0170" }, + "𝔘": { "codepoints": [120088], "characters": "\uD835\uDD18" }, + "Ù": { "codepoints": [217], "characters": "\u00D9" }, + "Ù": { "codepoints": [217], "characters": "\u00D9" }, + "Ū": { "codepoints": [362], "characters": "\u016A" }, + "_": { "codepoints": [95], "characters": "\u005F" }, + "⏟": { "codepoints": [9183], "characters": "\u23DF" }, + "⎵": { "codepoints": [9141], "characters": "\u23B5" }, + "⏝": { "codepoints": [9181], "characters": "\u23DD" }, + "⋃": { "codepoints": [8899], "characters": "\u22C3" }, + "⊎": { "codepoints": [8846], "characters": "\u228E" }, + "Ų": { "codepoints": [370], "characters": "\u0172" }, + "𝕌": { "codepoints": [120140], "characters": "\uD835\uDD4C" }, + "↑": { "codepoints": [8593], "characters": "\u2191" }, + "⤒": { "codepoints": [10514], "characters": "\u2912" }, + "⇅": { "codepoints": [8645], "characters": "\u21C5" }, + "↕": { "codepoints": [8597], "characters": "\u2195" }, + "⥮": { "codepoints": [10606], "characters": "\u296E" }, + "⊥": { "codepoints": [8869], "characters": "\u22A5" }, + "↥": { "codepoints": [8613], "characters": "\u21A5" }, + "⇑": { "codepoints": [8657], "characters": "\u21D1" }, + "⇕": { "codepoints": [8661], "characters": "\u21D5" }, + "↖": { "codepoints": [8598], "characters": "\u2196" }, + "↗": { "codepoints": [8599], "characters": "\u2197" }, + "ϒ": { "codepoints": [978], "characters": "\u03D2" }, + "Υ": { "codepoints": [933], "characters": "\u03A5" }, + "Ů": { "codepoints": [366], "characters": "\u016E" }, + "𝒰": { "codepoints": [119984], "characters": "\uD835\uDCB0" }, + "Ũ": { "codepoints": [360], "characters": "\u0168" }, + "Ü": { "codepoints": [220], "characters": "\u00DC" }, + "Ü": { "codepoints": [220], "characters": "\u00DC" }, + "⊫": { "codepoints": [8875], "characters": "\u22AB" }, + "⫫": { "codepoints": [10987], "characters": "\u2AEB" }, + "В": { "codepoints": [1042], "characters": "\u0412" }, + "⊩": { "codepoints": [8873], "characters": "\u22A9" }, + "⫦": { "codepoints": [10982], "characters": "\u2AE6" }, + "⋁": { "codepoints": [8897], "characters": "\u22C1" }, + "‖": { "codepoints": [8214], "characters": "\u2016" }, + "‖": { "codepoints": [8214], "characters": "\u2016" }, + "∣": { "codepoints": [8739], "characters": "\u2223" }, + "|": { "codepoints": [124], "characters": "\u007C" }, + "❘": { "codepoints": [10072], "characters": "\u2758" }, + "≀": { "codepoints": [8768], "characters": "\u2240" }, + " ": { "codepoints": [8202], "characters": "\u200A" }, + "𝔙": { "codepoints": [120089], "characters": "\uD835\uDD19" }, + "𝕍": { "codepoints": [120141], "characters": "\uD835\uDD4D" }, + "𝒱": { "codepoints": [119985], "characters": "\uD835\uDCB1" }, + "⊪": { "codepoints": [8874], "characters": "\u22AA" }, + "Ŵ": { "codepoints": [372], "characters": "\u0174" }, + "⋀": { "codepoints": [8896], "characters": "\u22C0" }, + "𝔚": { "codepoints": [120090], "characters": "\uD835\uDD1A" }, + "𝕎": { "codepoints": [120142], "characters": "\uD835\uDD4E" }, + "𝒲": { "codepoints": [119986], "characters": "\uD835\uDCB2" }, + "𝔛": { "codepoints": [120091], "characters": "\uD835\uDD1B" }, + "Ξ": { "codepoints": [926], "characters": "\u039E" }, + "𝕏": { "codepoints": [120143], "characters": "\uD835\uDD4F" }, + "𝒳": { "codepoints": [119987], "characters": "\uD835\uDCB3" }, + "Я": { "codepoints": [1071], "characters": "\u042F" }, + "Ї": { "codepoints": [1031], "characters": "\u0407" }, + "Ю": { "codepoints": [1070], "characters": "\u042E" }, + "Ý": { "codepoints": [221], "characters": "\u00DD" }, + "Ý": { "codepoints": [221], "characters": "\u00DD" }, + "Ŷ": { "codepoints": [374], "characters": "\u0176" }, + "Ы": { "codepoints": [1067], "characters": "\u042B" }, + "𝔜": { "codepoints": [120092], "characters": "\uD835\uDD1C" }, + "𝕐": { "codepoints": [120144], "characters": "\uD835\uDD50" }, + "𝒴": { "codepoints": [119988], "characters": "\uD835\uDCB4" }, + "Ÿ": { "codepoints": [376], "characters": "\u0178" }, + "Ж": { "codepoints": [1046], "characters": "\u0416" }, + "Ź": { "codepoints": [377], "characters": "\u0179" }, + "Ž": { "codepoints": [381], "characters": "\u017D" }, + "З": { "codepoints": [1047], "characters": "\u0417" }, + "Ż": { "codepoints": [379], "characters": "\u017B" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "Ζ": { "codepoints": [918], "characters": "\u0396" }, + "ℨ": { "codepoints": [8488], "characters": "\u2128" }, + "ℤ": { "codepoints": [8484], "characters": "\u2124" }, + "𝒵": { "codepoints": [119989], "characters": "\uD835\uDCB5" }, + "á": { "codepoints": [225], "characters": "\u00E1" }, + "á": { "codepoints": [225], "characters": "\u00E1" }, + "ă": { "codepoints": [259], "characters": "\u0103" }, + "∾": { "codepoints": [8766], "characters": "\u223E" }, + "∾̳": { "codepoints": [8766, 819], "characters": "\u223E\u0333" }, + "∿": { "codepoints": [8767], "characters": "\u223F" }, + "â": { "codepoints": [226], "characters": "\u00E2" }, + "â": { "codepoints": [226], "characters": "\u00E2" }, + "´": { "codepoints": [180], "characters": "\u00B4" }, + "´": { "codepoints": [180], "characters": "\u00B4" }, + "а": { "codepoints": [1072], "characters": "\u0430" }, + "æ": { "codepoints": [230], "characters": "\u00E6" }, + "æ": { "codepoints": [230], "characters": "\u00E6" }, + "⁡": { "codepoints": [8289], "characters": "\u2061" }, + "𝔞": { "codepoints": [120094], "characters": "\uD835\uDD1E" }, + "à": { "codepoints": [224], "characters": "\u00E0" }, + "à": { "codepoints": [224], "characters": "\u00E0" }, + "ℵ": { "codepoints": [8501], "characters": "\u2135" }, + "ℵ": { "codepoints": [8501], "characters": "\u2135" }, + "α": { "codepoints": [945], "characters": "\u03B1" }, + "ā": { "codepoints": [257], "characters": "\u0101" }, + "⨿": { "codepoints": [10815], "characters": "\u2A3F" }, + "&": { "codepoints": [38], "characters": "\u0026" }, + "&": { "codepoints": [38], "characters": "\u0026" }, + "∧": { "codepoints": [8743], "characters": "\u2227" }, + "⩕": { "codepoints": [10837], "characters": "\u2A55" }, + "⩜": { "codepoints": [10844], "characters": "\u2A5C" }, + "⩘": { "codepoints": [10840], "characters": "\u2A58" }, + "⩚": { "codepoints": [10842], "characters": "\u2A5A" }, + "∠": { "codepoints": [8736], "characters": "\u2220" }, + "⦤": { "codepoints": [10660], "characters": "\u29A4" }, + "∠": { "codepoints": [8736], "characters": "\u2220" }, + "∡": { "codepoints": [8737], "characters": "\u2221" }, + "⦨": { "codepoints": [10664], "characters": "\u29A8" }, + "⦩": { "codepoints": [10665], "characters": "\u29A9" }, + "⦪": { "codepoints": [10666], "characters": "\u29AA" }, + "⦫": { "codepoints": [10667], "characters": "\u29AB" }, + "⦬": { "codepoints": [10668], "characters": "\u29AC" }, + "⦭": { "codepoints": [10669], "characters": "\u29AD" }, + "⦮": { "codepoints": [10670], "characters": "\u29AE" }, + "⦯": { "codepoints": [10671], "characters": "\u29AF" }, + "∟": { "codepoints": [8735], "characters": "\u221F" }, + "⊾": { "codepoints": [8894], "characters": "\u22BE" }, + "⦝": { "codepoints": [10653], "characters": "\u299D" }, + "∢": { "codepoints": [8738], "characters": "\u2222" }, + "Å": { "codepoints": [197], "characters": "\u00C5" }, + "⍼": { "codepoints": [9084], "characters": "\u237C" }, + "ą": { "codepoints": [261], "characters": "\u0105" }, + "𝕒": { "codepoints": [120146], "characters": "\uD835\uDD52" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "⩰": { "codepoints": [10864], "characters": "\u2A70" }, + "⩯": { "codepoints": [10863], "characters": "\u2A6F" }, + "≊": { "codepoints": [8778], "characters": "\u224A" }, + "≋": { "codepoints": [8779], "characters": "\u224B" }, + "'": { "codepoints": [39], "characters": "\u0027" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "≊": { "codepoints": [8778], "characters": "\u224A" }, + "å": { "codepoints": [229], "characters": "\u00E5" }, + "å": { "codepoints": [229], "characters": "\u00E5" }, + "𝒶": { "codepoints": [119990], "characters": "\uD835\uDCB6" }, + "*": { "codepoints": [42], "characters": "\u002A" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "≍": { "codepoints": [8781], "characters": "\u224D" }, + "ã": { "codepoints": [227], "characters": "\u00E3" }, + "ã": { "codepoints": [227], "characters": "\u00E3" }, + "ä": { "codepoints": [228], "characters": "\u00E4" }, + "ä": { "codepoints": [228], "characters": "\u00E4" }, + "∳": { "codepoints": [8755], "characters": "\u2233" }, + "⨑": { "codepoints": [10769], "characters": "\u2A11" }, + "⫭": { "codepoints": [10989], "characters": "\u2AED" }, + "≌": { "codepoints": [8780], "characters": "\u224C" }, + "϶": { "codepoints": [1014], "characters": "\u03F6" }, + "‵": { "codepoints": [8245], "characters": "\u2035" }, + "∽": { "codepoints": [8765], "characters": "\u223D" }, + "⋍": { "codepoints": [8909], "characters": "\u22CD" }, + "⊽": { "codepoints": [8893], "characters": "\u22BD" }, + "⌅": { "codepoints": [8965], "characters": "\u2305" }, + "⌅": { "codepoints": [8965], "characters": "\u2305" }, + "⎵": { "codepoints": [9141], "characters": "\u23B5" }, + "⎶": { "codepoints": [9142], "characters": "\u23B6" }, + "≌": { "codepoints": [8780], "characters": "\u224C" }, + "б": { "codepoints": [1073], "characters": "\u0431" }, + "„": { "codepoints": [8222], "characters": "\u201E" }, + "∵": { "codepoints": [8757], "characters": "\u2235" }, + "∵": { "codepoints": [8757], "characters": "\u2235" }, + "⦰": { "codepoints": [10672], "characters": "\u29B0" }, + "϶": { "codepoints": [1014], "characters": "\u03F6" }, + "ℬ": { "codepoints": [8492], "characters": "\u212C" }, + "β": { "codepoints": [946], "characters": "\u03B2" }, + "ℶ": { "codepoints": [8502], "characters": "\u2136" }, + "≬": { "codepoints": [8812], "characters": "\u226C" }, + "𝔟": { "codepoints": [120095], "characters": "\uD835\uDD1F" }, + "⋂": { "codepoints": [8898], "characters": "\u22C2" }, + "◯": { "codepoints": [9711], "characters": "\u25EF" }, + "⋃": { "codepoints": [8899], "characters": "\u22C3" }, + "⨀": { "codepoints": [10752], "characters": "\u2A00" }, + "⨁": { "codepoints": [10753], "characters": "\u2A01" }, + "⨂": { "codepoints": [10754], "characters": "\u2A02" }, + "⨆": { "codepoints": [10758], "characters": "\u2A06" }, + "★": { "codepoints": [9733], "characters": "\u2605" }, + "▽": { "codepoints": [9661], "characters": "\u25BD" }, + "△": { "codepoints": [9651], "characters": "\u25B3" }, + "⨄": { "codepoints": [10756], "characters": "\u2A04" }, + "⋁": { "codepoints": [8897], "characters": "\u22C1" }, + "⋀": { "codepoints": [8896], "characters": "\u22C0" }, + "⤍": { "codepoints": [10509], "characters": "\u290D" }, + "⧫": { "codepoints": [10731], "characters": "\u29EB" }, + "▪": { "codepoints": [9642], "characters": "\u25AA" }, + "▴": { "codepoints": [9652], "characters": "\u25B4" }, + "▾": { "codepoints": [9662], "characters": "\u25BE" }, + "◂": { "codepoints": [9666], "characters": "\u25C2" }, + "▸": { "codepoints": [9656], "characters": "\u25B8" }, + "␣": { "codepoints": [9251], "characters": "\u2423" }, + "▒": { "codepoints": [9618], "characters": "\u2592" }, + "░": { "codepoints": [9617], "characters": "\u2591" }, + "▓": { "codepoints": [9619], "characters": "\u2593" }, + "█": { "codepoints": [9608], "characters": "\u2588" }, + "=⃥": { "codepoints": [61, 8421], "characters": "\u003D\u20E5" }, + "≡⃥": { "codepoints": [8801, 8421], "characters": "\u2261\u20E5" }, + "⌐": { "codepoints": [8976], "characters": "\u2310" }, + "𝕓": { "codepoints": [120147], "characters": "\uD835\uDD53" }, + "⊥": { "codepoints": [8869], "characters": "\u22A5" }, + "⊥": { "codepoints": [8869], "characters": "\u22A5" }, + "⋈": { "codepoints": [8904], "characters": "\u22C8" }, + "╗": { "codepoints": [9559], "characters": "\u2557" }, + "╔": { "codepoints": [9556], "characters": "\u2554" }, + "╖": { "codepoints": [9558], "characters": "\u2556" }, + "╓": { "codepoints": [9555], "characters": "\u2553" }, + "═": { "codepoints": [9552], "characters": "\u2550" }, + "╦": { "codepoints": [9574], "characters": "\u2566" }, + "╩": { "codepoints": [9577], "characters": "\u2569" }, + "╤": { "codepoints": [9572], "characters": "\u2564" }, + "╧": { "codepoints": [9575], "characters": "\u2567" }, + "╝": { "codepoints": [9565], "characters": "\u255D" }, + "╚": { "codepoints": [9562], "characters": "\u255A" }, + "╜": { "codepoints": [9564], "characters": "\u255C" }, + "╙": { "codepoints": [9561], "characters": "\u2559" }, + "║": { "codepoints": [9553], "characters": "\u2551" }, + "╬": { "codepoints": [9580], "characters": "\u256C" }, + "╣": { "codepoints": [9571], "characters": "\u2563" }, + "╠": { "codepoints": [9568], "characters": "\u2560" }, + "╫": { "codepoints": [9579], "characters": "\u256B" }, + "╢": { "codepoints": [9570], "characters": "\u2562" }, + "╟": { "codepoints": [9567], "characters": "\u255F" }, + "⧉": { "codepoints": [10697], "characters": "\u29C9" }, + "╕": { "codepoints": [9557], "characters": "\u2555" }, + "╒": { "codepoints": [9554], "characters": "\u2552" }, + "┐": { "codepoints": [9488], "characters": "\u2510" }, + "┌": { "codepoints": [9484], "characters": "\u250C" }, + "─": { "codepoints": [9472], "characters": "\u2500" }, + "╥": { "codepoints": [9573], "characters": "\u2565" }, + "╨": { "codepoints": [9576], "characters": "\u2568" }, + "┬": { "codepoints": [9516], "characters": "\u252C" }, + "┴": { "codepoints": [9524], "characters": "\u2534" }, + "⊟": { "codepoints": [8863], "characters": "\u229F" }, + "⊞": { "codepoints": [8862], "characters": "\u229E" }, + "⊠": { "codepoints": [8864], "characters": "\u22A0" }, + "╛": { "codepoints": [9563], "characters": "\u255B" }, + "╘": { "codepoints": [9560], "characters": "\u2558" }, + "┘": { "codepoints": [9496], "characters": "\u2518" }, + "└": { "codepoints": [9492], "characters": "\u2514" }, + "│": { "codepoints": [9474], "characters": "\u2502" }, + "╪": { "codepoints": [9578], "characters": "\u256A" }, + "╡": { "codepoints": [9569], "characters": "\u2561" }, + "╞": { "codepoints": [9566], "characters": "\u255E" }, + "┼": { "codepoints": [9532], "characters": "\u253C" }, + "┤": { "codepoints": [9508], "characters": "\u2524" }, + "├": { "codepoints": [9500], "characters": "\u251C" }, + "‵": { "codepoints": [8245], "characters": "\u2035" }, + "˘": { "codepoints": [728], "characters": "\u02D8" }, + "¦": { "codepoints": [166], "characters": "\u00A6" }, + "¦": { "codepoints": [166], "characters": "\u00A6" }, + "𝒷": { "codepoints": [119991], "characters": "\uD835\uDCB7" }, + "⁏": { "codepoints": [8271], "characters": "\u204F" }, + "∽": { "codepoints": [8765], "characters": "\u223D" }, + "⋍": { "codepoints": [8909], "characters": "\u22CD" }, + "\": { "codepoints": [92], "characters": "\u005C" }, + "⧅": { "codepoints": [10693], "characters": "\u29C5" }, + "⟈": { "codepoints": [10184], "characters": "\u27C8" }, + "•": { "codepoints": [8226], "characters": "\u2022" }, + "•": { "codepoints": [8226], "characters": "\u2022" }, + "≎": { "codepoints": [8782], "characters": "\u224E" }, + "⪮": { "codepoints": [10926], "characters": "\u2AAE" }, + "≏": { "codepoints": [8783], "characters": "\u224F" }, + "≏": { "codepoints": [8783], "characters": "\u224F" }, + "ć": { "codepoints": [263], "characters": "\u0107" }, + "∩": { "codepoints": [8745], "characters": "\u2229" }, + "⩄": { "codepoints": [10820], "characters": "\u2A44" }, + "⩉": { "codepoints": [10825], "characters": "\u2A49" }, + "⩋": { "codepoints": [10827], "characters": "\u2A4B" }, + "⩇": { "codepoints": [10823], "characters": "\u2A47" }, + "⩀": { "codepoints": [10816], "characters": "\u2A40" }, + "∩︀": { "codepoints": [8745, 65024], "characters": "\u2229\uFE00" }, + "⁁": { "codepoints": [8257], "characters": "\u2041" }, + "ˇ": { "codepoints": [711], "characters": "\u02C7" }, + "⩍": { "codepoints": [10829], "characters": "\u2A4D" }, + "č": { "codepoints": [269], "characters": "\u010D" }, + "ç": { "codepoints": [231], "characters": "\u00E7" }, + "ç": { "codepoints": [231], "characters": "\u00E7" }, + "ĉ": { "codepoints": [265], "characters": "\u0109" }, + "⩌": { "codepoints": [10828], "characters": "\u2A4C" }, + "⩐": { "codepoints": [10832], "characters": "\u2A50" }, + "ċ": { "codepoints": [267], "characters": "\u010B" }, + "¸": { "codepoints": [184], "characters": "\u00B8" }, + "¸": { "codepoints": [184], "characters": "\u00B8" }, + "⦲": { "codepoints": [10674], "characters": "\u29B2" }, + "¢": { "codepoints": [162], "characters": "\u00A2" }, + "¢": { "codepoints": [162], "characters": "\u00A2" }, + "·": { "codepoints": [183], "characters": "\u00B7" }, + "𝔠": { "codepoints": [120096], "characters": "\uD835\uDD20" }, + "ч": { "codepoints": [1095], "characters": "\u0447" }, + "✓": { "codepoints": [10003], "characters": "\u2713" }, + "✓": { "codepoints": [10003], "characters": "\u2713" }, + "χ": { "codepoints": [967], "characters": "\u03C7" }, + "○": { "codepoints": [9675], "characters": "\u25CB" }, + "⧃": { "codepoints": [10691], "characters": "\u29C3" }, + "ˆ": { "codepoints": [710], "characters": "\u02C6" }, + "≗": { "codepoints": [8791], "characters": "\u2257" }, + "↺": { "codepoints": [8634], "characters": "\u21BA" }, + "↻": { "codepoints": [8635], "characters": "\u21BB" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "Ⓢ": { "codepoints": [9416], "characters": "\u24C8" }, + "⊛": { "codepoints": [8859], "characters": "\u229B" }, + "⊚": { "codepoints": [8858], "characters": "\u229A" }, + "⊝": { "codepoints": [8861], "characters": "\u229D" }, + "≗": { "codepoints": [8791], "characters": "\u2257" }, + "⨐": { "codepoints": [10768], "characters": "\u2A10" }, + "⫯": { "codepoints": [10991], "characters": "\u2AEF" }, + "⧂": { "codepoints": [10690], "characters": "\u29C2" }, + "♣": { "codepoints": [9827], "characters": "\u2663" }, + "♣": { "codepoints": [9827], "characters": "\u2663" }, + ":": { "codepoints": [58], "characters": "\u003A" }, + "≔": { "codepoints": [8788], "characters": "\u2254" }, + "≔": { "codepoints": [8788], "characters": "\u2254" }, + ",": { "codepoints": [44], "characters": "\u002C" }, + "@": { "codepoints": [64], "characters": "\u0040" }, + "∁": { "codepoints": [8705], "characters": "\u2201" }, + "∘": { "codepoints": [8728], "characters": "\u2218" }, + "∁": { "codepoints": [8705], "characters": "\u2201" }, + "ℂ": { "codepoints": [8450], "characters": "\u2102" }, + "≅": { "codepoints": [8773], "characters": "\u2245" }, + "⩭": { "codepoints": [10861], "characters": "\u2A6D" }, + "∮": { "codepoints": [8750], "characters": "\u222E" }, + "𝕔": { "codepoints": [120148], "characters": "\uD835\uDD54" }, + "∐": { "codepoints": [8720], "characters": "\u2210" }, + "©": { "codepoints": [169], "characters": "\u00A9" }, + "©": { "codepoints": [169], "characters": "\u00A9" }, + "℗": { "codepoints": [8471], "characters": "\u2117" }, + "↵": { "codepoints": [8629], "characters": "\u21B5" }, + "✗": { "codepoints": [10007], "characters": "\u2717" }, + "𝒸": { "codepoints": [119992], "characters": "\uD835\uDCB8" }, + "⫏": { "codepoints": [10959], "characters": "\u2ACF" }, + "⫑": { "codepoints": [10961], "characters": "\u2AD1" }, + "⫐": { "codepoints": [10960], "characters": "\u2AD0" }, + "⫒": { "codepoints": [10962], "characters": "\u2AD2" }, + "⋯": { "codepoints": [8943], "characters": "\u22EF" }, + "⤸": { "codepoints": [10552], "characters": "\u2938" }, + "⤵": { "codepoints": [10549], "characters": "\u2935" }, + "⋞": { "codepoints": [8926], "characters": "\u22DE" }, + "⋟": { "codepoints": [8927], "characters": "\u22DF" }, + "↶": { "codepoints": [8630], "characters": "\u21B6" }, + "⤽": { "codepoints": [10557], "characters": "\u293D" }, + "∪": { "codepoints": [8746], "characters": "\u222A" }, + "⩈": { "codepoints": [10824], "characters": "\u2A48" }, + "⩆": { "codepoints": [10822], "characters": "\u2A46" }, + "⩊": { "codepoints": [10826], "characters": "\u2A4A" }, + "⊍": { "codepoints": [8845], "characters": "\u228D" }, + "⩅": { "codepoints": [10821], "characters": "\u2A45" }, + "∪︀": { "codepoints": [8746, 65024], "characters": "\u222A\uFE00" }, + "↷": { "codepoints": [8631], "characters": "\u21B7" }, + "⤼": { "codepoints": [10556], "characters": "\u293C" }, + "⋞": { "codepoints": [8926], "characters": "\u22DE" }, + "⋟": { "codepoints": [8927], "characters": "\u22DF" }, + "⋎": { "codepoints": [8910], "characters": "\u22CE" }, + "⋏": { "codepoints": [8911], "characters": "\u22CF" }, + "¤": { "codepoints": [164], "characters": "\u00A4" }, + "¤": { "codepoints": [164], "characters": "\u00A4" }, + "↶": { "codepoints": [8630], "characters": "\u21B6" }, + "↷": { "codepoints": [8631], "characters": "\u21B7" }, + "⋎": { "codepoints": [8910], "characters": "\u22CE" }, + "⋏": { "codepoints": [8911], "characters": "\u22CF" }, + "∲": { "codepoints": [8754], "characters": "\u2232" }, + "∱": { "codepoints": [8753], "characters": "\u2231" }, + "⌭": { "codepoints": [9005], "characters": "\u232D" }, + "⇓": { "codepoints": [8659], "characters": "\u21D3" }, + "⥥": { "codepoints": [10597], "characters": "\u2965" }, + "†": { "codepoints": [8224], "characters": "\u2020" }, + "ℸ": { "codepoints": [8504], "characters": "\u2138" }, + "↓": { "codepoints": [8595], "characters": "\u2193" }, + "‐": { "codepoints": [8208], "characters": "\u2010" }, + "⊣": { "codepoints": [8867], "characters": "\u22A3" }, + "⤏": { "codepoints": [10511], "characters": "\u290F" }, + "˝": { "codepoints": [733], "characters": "\u02DD" }, + "ď": { "codepoints": [271], "characters": "\u010F" }, + "д": { "codepoints": [1076], "characters": "\u0434" }, + "ⅆ": { "codepoints": [8518], "characters": "\u2146" }, + "‡": { "codepoints": [8225], "characters": "\u2021" }, + "⇊": { "codepoints": [8650], "characters": "\u21CA" }, + "⩷": { "codepoints": [10871], "characters": "\u2A77" }, + "°": { "codepoints": [176], "characters": "\u00B0" }, + "°": { "codepoints": [176], "characters": "\u00B0" }, + "δ": { "codepoints": [948], "characters": "\u03B4" }, + "⦱": { "codepoints": [10673], "characters": "\u29B1" }, + "⥿": { "codepoints": [10623], "characters": "\u297F" }, + "𝔡": { "codepoints": [120097], "characters": "\uD835\uDD21" }, + "⇃": { "codepoints": [8643], "characters": "\u21C3" }, + "⇂": { "codepoints": [8642], "characters": "\u21C2" }, + "⋄": { "codepoints": [8900], "characters": "\u22C4" }, + "⋄": { "codepoints": [8900], "characters": "\u22C4" }, + "♦": { "codepoints": [9830], "characters": "\u2666" }, + "♦": { "codepoints": [9830], "characters": "\u2666" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "ϝ": { "codepoints": [989], "characters": "\u03DD" }, + "⋲": { "codepoints": [8946], "characters": "\u22F2" }, + "÷": { "codepoints": [247], "characters": "\u00F7" }, + "÷": { "codepoints": [247], "characters": "\u00F7" }, + "÷": { "codepoints": [247], "characters": "\u00F7" }, + "⋇": { "codepoints": [8903], "characters": "\u22C7" }, + "⋇": { "codepoints": [8903], "characters": "\u22C7" }, + "ђ": { "codepoints": [1106], "characters": "\u0452" }, + "⌞": { "codepoints": [8990], "characters": "\u231E" }, + "⌍": { "codepoints": [8973], "characters": "\u230D" }, + "$": { "codepoints": [36], "characters": "\u0024" }, + "𝕕": { "codepoints": [120149], "characters": "\uD835\uDD55" }, + "˙": { "codepoints": [729], "characters": "\u02D9" }, + "≐": { "codepoints": [8784], "characters": "\u2250" }, + "≑": { "codepoints": [8785], "characters": "\u2251" }, + "∸": { "codepoints": [8760], "characters": "\u2238" }, + "∔": { "codepoints": [8724], "characters": "\u2214" }, + "⊡": { "codepoints": [8865], "characters": "\u22A1" }, + "⌆": { "codepoints": [8966], "characters": "\u2306" }, + "↓": { "codepoints": [8595], "characters": "\u2193" }, + "⇊": { "codepoints": [8650], "characters": "\u21CA" }, + "⇃": { "codepoints": [8643], "characters": "\u21C3" }, + "⇂": { "codepoints": [8642], "characters": "\u21C2" }, + "⤐": { "codepoints": [10512], "characters": "\u2910" }, + "⌟": { "codepoints": [8991], "characters": "\u231F" }, + "⌌": { "codepoints": [8972], "characters": "\u230C" }, + "𝒹": { "codepoints": [119993], "characters": "\uD835\uDCB9" }, + "ѕ": { "codepoints": [1109], "characters": "\u0455" }, + "⧶": { "codepoints": [10742], "characters": "\u29F6" }, + "đ": { "codepoints": [273], "characters": "\u0111" }, + "⋱": { "codepoints": [8945], "characters": "\u22F1" }, + "▿": { "codepoints": [9663], "characters": "\u25BF" }, + "▾": { "codepoints": [9662], "characters": "\u25BE" }, + "⇵": { "codepoints": [8693], "characters": "\u21F5" }, + "⥯": { "codepoints": [10607], "characters": "\u296F" }, + "⦦": { "codepoints": [10662], "characters": "\u29A6" }, + "џ": { "codepoints": [1119], "characters": "\u045F" }, + "⟿": { "codepoints": [10239], "characters": "\u27FF" }, + "⩷": { "codepoints": [10871], "characters": "\u2A77" }, + "≑": { "codepoints": [8785], "characters": "\u2251" }, + "é": { "codepoints": [233], "characters": "\u00E9" }, + "é": { "codepoints": [233], "characters": "\u00E9" }, + "⩮": { "codepoints": [10862], "characters": "\u2A6E" }, + "ě": { "codepoints": [283], "characters": "\u011B" }, + "≖": { "codepoints": [8790], "characters": "\u2256" }, + "ê": { "codepoints": [234], "characters": "\u00EA" }, + "ê": { "codepoints": [234], "characters": "\u00EA" }, + "≕": { "codepoints": [8789], "characters": "\u2255" }, + "э": { "codepoints": [1101], "characters": "\u044D" }, + "ė": { "codepoints": [279], "characters": "\u0117" }, + "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, + "≒": { "codepoints": [8786], "characters": "\u2252" }, + "𝔢": { "codepoints": [120098], "characters": "\uD835\uDD22" }, + "⪚": { "codepoints": [10906], "characters": "\u2A9A" }, + "è": { "codepoints": [232], "characters": "\u00E8" }, + "è": { "codepoints": [232], "characters": "\u00E8" }, + "⪖": { "codepoints": [10902], "characters": "\u2A96" }, + "⪘": { "codepoints": [10904], "characters": "\u2A98" }, + "⪙": { "codepoints": [10905], "characters": "\u2A99" }, + "⏧": { "codepoints": [9191], "characters": "\u23E7" }, + "ℓ": { "codepoints": [8467], "characters": "\u2113" }, + "⪕": { "codepoints": [10901], "characters": "\u2A95" }, + "⪗": { "codepoints": [10903], "characters": "\u2A97" }, + "ē": { "codepoints": [275], "characters": "\u0113" }, + "∅": { "codepoints": [8709], "characters": "\u2205" }, + "∅": { "codepoints": [8709], "characters": "\u2205" }, + "∅": { "codepoints": [8709], "characters": "\u2205" }, + " ": { "codepoints": [8196], "characters": "\u2004" }, + " ": { "codepoints": [8197], "characters": "\u2005" }, + " ": { "codepoints": [8195], "characters": "\u2003" }, + "ŋ": { "codepoints": [331], "characters": "\u014B" }, + " ": { "codepoints": [8194], "characters": "\u2002" }, + "ę": { "codepoints": [281], "characters": "\u0119" }, + "𝕖": { "codepoints": [120150], "characters": "\uD835\uDD56" }, + "⋕": { "codepoints": [8917], "characters": "\u22D5" }, + "⧣": { "codepoints": [10723], "characters": "\u29E3" }, + "⩱": { "codepoints": [10865], "characters": "\u2A71" }, + "ε": { "codepoints": [949], "characters": "\u03B5" }, + "ε": { "codepoints": [949], "characters": "\u03B5" }, + "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, + "≖": { "codepoints": [8790], "characters": "\u2256" }, + "≕": { "codepoints": [8789], "characters": "\u2255" }, + "≂": { "codepoints": [8770], "characters": "\u2242" }, + "⪖": { "codepoints": [10902], "characters": "\u2A96" }, + "⪕": { "codepoints": [10901], "characters": "\u2A95" }, + "=": { "codepoints": [61], "characters": "\u003D" }, + "≟": { "codepoints": [8799], "characters": "\u225F" }, + "≡": { "codepoints": [8801], "characters": "\u2261" }, + "⩸": { "codepoints": [10872], "characters": "\u2A78" }, + "⧥": { "codepoints": [10725], "characters": "\u29E5" }, + "≓": { "codepoints": [8787], "characters": "\u2253" }, + "⥱": { "codepoints": [10609], "characters": "\u2971" }, + "ℯ": { "codepoints": [8495], "characters": "\u212F" }, + "≐": { "codepoints": [8784], "characters": "\u2250" }, + "≂": { "codepoints": [8770], "characters": "\u2242" }, + "η": { "codepoints": [951], "characters": "\u03B7" }, + "ð": { "codepoints": [240], "characters": "\u00F0" }, + "ð": { "codepoints": [240], "characters": "\u00F0" }, + "ë": { "codepoints": [235], "characters": "\u00EB" }, + "ë": { "codepoints": [235], "characters": "\u00EB" }, + "€": { "codepoints": [8364], "characters": "\u20AC" }, + "!": { "codepoints": [33], "characters": "\u0021" }, + "∃": { "codepoints": [8707], "characters": "\u2203" }, + "ℰ": { "codepoints": [8496], "characters": "\u2130" }, + "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, + "≒": { "codepoints": [8786], "characters": "\u2252" }, + "ф": { "codepoints": [1092], "characters": "\u0444" }, + "♀": { "codepoints": [9792], "characters": "\u2640" }, + "ffi": { "codepoints": [64259], "characters": "\uFB03" }, + "ff": { "codepoints": [64256], "characters": "\uFB00" }, + "ffl": { "codepoints": [64260], "characters": "\uFB04" }, + "𝔣": { "codepoints": [120099], "characters": "\uD835\uDD23" }, + "fi": { "codepoints": [64257], "characters": "\uFB01" }, + "fj": { "codepoints": [102, 106], "characters": "\u0066\u006A" }, + "♭": { "codepoints": [9837], "characters": "\u266D" }, + "fl": { "codepoints": [64258], "characters": "\uFB02" }, + "▱": { "codepoints": [9649], "characters": "\u25B1" }, + "ƒ": { "codepoints": [402], "characters": "\u0192" }, + "𝕗": { "codepoints": [120151], "characters": "\uD835\uDD57" }, + "∀": { "codepoints": [8704], "characters": "\u2200" }, + "⋔": { "codepoints": [8916], "characters": "\u22D4" }, + "⫙": { "codepoints": [10969], "characters": "\u2AD9" }, + "⨍": { "codepoints": [10765], "characters": "\u2A0D" }, + "½": { "codepoints": [189], "characters": "\u00BD" }, + "½": { "codepoints": [189], "characters": "\u00BD" }, + "⅓": { "codepoints": [8531], "characters": "\u2153" }, + "¼": { "codepoints": [188], "characters": "\u00BC" }, + "¼": { "codepoints": [188], "characters": "\u00BC" }, + "⅕": { "codepoints": [8533], "characters": "\u2155" }, + "⅙": { "codepoints": [8537], "characters": "\u2159" }, + "⅛": { "codepoints": [8539], "characters": "\u215B" }, + "⅔": { "codepoints": [8532], "characters": "\u2154" }, + "⅖": { "codepoints": [8534], "characters": "\u2156" }, + "¾": { "codepoints": [190], "characters": "\u00BE" }, + "¾": { "codepoints": [190], "characters": "\u00BE" }, + "⅗": { "codepoints": [8535], "characters": "\u2157" }, + "⅜": { "codepoints": [8540], "characters": "\u215C" }, + "⅘": { "codepoints": [8536], "characters": "\u2158" }, + "⅚": { "codepoints": [8538], "characters": "\u215A" }, + "⅝": { "codepoints": [8541], "characters": "\u215D" }, + "⅞": { "codepoints": [8542], "characters": "\u215E" }, + "⁄": { "codepoints": [8260], "characters": "\u2044" }, + "⌢": { "codepoints": [8994], "characters": "\u2322" }, + "𝒻": { "codepoints": [119995], "characters": "\uD835\uDCBB" }, + "≧": { "codepoints": [8807], "characters": "\u2267" }, + "⪌": { "codepoints": [10892], "characters": "\u2A8C" }, + "ǵ": { "codepoints": [501], "characters": "\u01F5" }, + "γ": { "codepoints": [947], "characters": "\u03B3" }, + "ϝ": { "codepoints": [989], "characters": "\u03DD" }, + "⪆": { "codepoints": [10886], "characters": "\u2A86" }, + "ğ": { "codepoints": [287], "characters": "\u011F" }, + "ĝ": { "codepoints": [285], "characters": "\u011D" }, + "г": { "codepoints": [1075], "characters": "\u0433" }, + "ġ": { "codepoints": [289], "characters": "\u0121" }, + "≥": { "codepoints": [8805], "characters": "\u2265" }, + "⋛": { "codepoints": [8923], "characters": "\u22DB" }, + "≥": { "codepoints": [8805], "characters": "\u2265" }, + "≧": { "codepoints": [8807], "characters": "\u2267" }, + "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, + "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, + "⪩": { "codepoints": [10921], "characters": "\u2AA9" }, + "⪀": { "codepoints": [10880], "characters": "\u2A80" }, + "⪂": { "codepoints": [10882], "characters": "\u2A82" }, + "⪄": { "codepoints": [10884], "characters": "\u2A84" }, + "⋛︀": { "codepoints": [8923, 65024], "characters": "\u22DB\uFE00" }, + "⪔": { "codepoints": [10900], "characters": "\u2A94" }, + "𝔤": { "codepoints": [120100], "characters": "\uD835\uDD24" }, + "≫": { "codepoints": [8811], "characters": "\u226B" }, + "⋙": { "codepoints": [8921], "characters": "\u22D9" }, + "ℷ": { "codepoints": [8503], "characters": "\u2137" }, + "ѓ": { "codepoints": [1107], "characters": "\u0453" }, + "≷": { "codepoints": [8823], "characters": "\u2277" }, + "⪒": { "codepoints": [10898], "characters": "\u2A92" }, + "⪥": { "codepoints": [10917], "characters": "\u2AA5" }, + "⪤": { "codepoints": [10916], "characters": "\u2AA4" }, + "≩": { "codepoints": [8809], "characters": "\u2269" }, + "⪊": { "codepoints": [10890], "characters": "\u2A8A" }, + "⪊": { "codepoints": [10890], "characters": "\u2A8A" }, + "⪈": { "codepoints": [10888], "characters": "\u2A88" }, + "⪈": { "codepoints": [10888], "characters": "\u2A88" }, + "≩": { "codepoints": [8809], "characters": "\u2269" }, + "⋧": { "codepoints": [8935], "characters": "\u22E7" }, + "𝕘": { "codepoints": [120152], "characters": "\uD835\uDD58" }, + "`": { "codepoints": [96], "characters": "\u0060" }, + "ℊ": { "codepoints": [8458], "characters": "\u210A" }, + "≳": { "codepoints": [8819], "characters": "\u2273" }, + "⪎": { "codepoints": [10894], "characters": "\u2A8E" }, + "⪐": { "codepoints": [10896], "characters": "\u2A90" }, + ">": { "codepoints": [62], "characters": "\u003E" }, + ">": { "codepoints": [62], "characters": "\u003E" }, + "⪧": { "codepoints": [10919], "characters": "\u2AA7" }, + "⩺": { "codepoints": [10874], "characters": "\u2A7A" }, + "⋗": { "codepoints": [8919], "characters": "\u22D7" }, + "⦕": { "codepoints": [10645], "characters": "\u2995" }, + "⩼": { "codepoints": [10876], "characters": "\u2A7C" }, + "⪆": { "codepoints": [10886], "characters": "\u2A86" }, + "⥸": { "codepoints": [10616], "characters": "\u2978" }, + "⋗": { "codepoints": [8919], "characters": "\u22D7" }, + "⋛": { "codepoints": [8923], "characters": "\u22DB" }, + "⪌": { "codepoints": [10892], "characters": "\u2A8C" }, + "≷": { "codepoints": [8823], "characters": "\u2277" }, + "≳": { "codepoints": [8819], "characters": "\u2273" }, + "≩︀": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" }, + "≩︀": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" }, + "⇔": { "codepoints": [8660], "characters": "\u21D4" }, + " ": { "codepoints": [8202], "characters": "\u200A" }, + "½": { "codepoints": [189], "characters": "\u00BD" }, + "ℋ": { "codepoints": [8459], "characters": "\u210B" }, + "ъ": { "codepoints": [1098], "characters": "\u044A" }, + "↔": { "codepoints": [8596], "characters": "\u2194" }, + "⥈": { "codepoints": [10568], "characters": "\u2948" }, + "↭": { "codepoints": [8621], "characters": "\u21AD" }, + "ℏ": { "codepoints": [8463], "characters": "\u210F" }, + "ĥ": { "codepoints": [293], "characters": "\u0125" }, + "♥": { "codepoints": [9829], "characters": "\u2665" }, + "♥": { "codepoints": [9829], "characters": "\u2665" }, + "…": { "codepoints": [8230], "characters": "\u2026" }, + "⊹": { "codepoints": [8889], "characters": "\u22B9" }, + "𝔥": { "codepoints": [120101], "characters": "\uD835\uDD25" }, + "⤥": { "codepoints": [10533], "characters": "\u2925" }, + "⤦": { "codepoints": [10534], "characters": "\u2926" }, + "⇿": { "codepoints": [8703], "characters": "\u21FF" }, + "∻": { "codepoints": [8763], "characters": "\u223B" }, + "↩": { "codepoints": [8617], "characters": "\u21A9" }, + "↪": { "codepoints": [8618], "characters": "\u21AA" }, + "𝕙": { "codepoints": [120153], "characters": "\uD835\uDD59" }, + "―": { "codepoints": [8213], "characters": "\u2015" }, + "𝒽": { "codepoints": [119997], "characters": "\uD835\uDCBD" }, + "ℏ": { "codepoints": [8463], "characters": "\u210F" }, + "ħ": { "codepoints": [295], "characters": "\u0127" }, + "⁃": { "codepoints": [8259], "characters": "\u2043" }, + "‐": { "codepoints": [8208], "characters": "\u2010" }, + "í": { "codepoints": [237], "characters": "\u00ED" }, + "í": { "codepoints": [237], "characters": "\u00ED" }, + "⁣": { "codepoints": [8291], "characters": "\u2063" }, + "î": { "codepoints": [238], "characters": "\u00EE" }, + "î": { "codepoints": [238], "characters": "\u00EE" }, + "и": { "codepoints": [1080], "characters": "\u0438" }, + "е": { "codepoints": [1077], "characters": "\u0435" }, + "¡": { "codepoints": [161], "characters": "\u00A1" }, + "¡": { "codepoints": [161], "characters": "\u00A1" }, + "⇔": { "codepoints": [8660], "characters": "\u21D4" }, + "𝔦": { "codepoints": [120102], "characters": "\uD835\uDD26" }, + "ì": { "codepoints": [236], "characters": "\u00EC" }, + "ì": { "codepoints": [236], "characters": "\u00EC" }, + "ⅈ": { "codepoints": [8520], "characters": "\u2148" }, + "⨌": { "codepoints": [10764], "characters": "\u2A0C" }, + "∭": { "codepoints": [8749], "characters": "\u222D" }, + "⧜": { "codepoints": [10716], "characters": "\u29DC" }, + "℩": { "codepoints": [8489], "characters": "\u2129" }, + "ij": { "codepoints": [307], "characters": "\u0133" }, + "ī": { "codepoints": [299], "characters": "\u012B" }, + "ℑ": { "codepoints": [8465], "characters": "\u2111" }, + "ℐ": { "codepoints": [8464], "characters": "\u2110" }, + "ℑ": { "codepoints": [8465], "characters": "\u2111" }, + "ı": { "codepoints": [305], "characters": "\u0131" }, + "⊷": { "codepoints": [8887], "characters": "\u22B7" }, + "Ƶ": { "codepoints": [437], "characters": "\u01B5" }, + "∈": { "codepoints": [8712], "characters": "\u2208" }, + "℅": { "codepoints": [8453], "characters": "\u2105" }, + "∞": { "codepoints": [8734], "characters": "\u221E" }, + "⧝": { "codepoints": [10717], "characters": "\u29DD" }, + "ı": { "codepoints": [305], "characters": "\u0131" }, + "∫": { "codepoints": [8747], "characters": "\u222B" }, + "⊺": { "codepoints": [8890], "characters": "\u22BA" }, + "ℤ": { "codepoints": [8484], "characters": "\u2124" }, + "⊺": { "codepoints": [8890], "characters": "\u22BA" }, + "⨗": { "codepoints": [10775], "characters": "\u2A17" }, + "⨼": { "codepoints": [10812], "characters": "\u2A3C" }, + "ё": { "codepoints": [1105], "characters": "\u0451" }, + "į": { "codepoints": [303], "characters": "\u012F" }, + "𝕚": { "codepoints": [120154], "characters": "\uD835\uDD5A" }, + "ι": { "codepoints": [953], "characters": "\u03B9" }, + "⨼": { "codepoints": [10812], "characters": "\u2A3C" }, + "¿": { "codepoints": [191], "characters": "\u00BF" }, + "¿": { "codepoints": [191], "characters": "\u00BF" }, + "𝒾": { "codepoints": [119998], "characters": "\uD835\uDCBE" }, + "∈": { "codepoints": [8712], "characters": "\u2208" }, + "⋹": { "codepoints": [8953], "characters": "\u22F9" }, + "⋵": { "codepoints": [8949], "characters": "\u22F5" }, + "⋴": { "codepoints": [8948], "characters": "\u22F4" }, + "⋳": { "codepoints": [8947], "characters": "\u22F3" }, + "∈": { "codepoints": [8712], "characters": "\u2208" }, + "⁢": { "codepoints": [8290], "characters": "\u2062" }, + "ĩ": { "codepoints": [297], "characters": "\u0129" }, + "і": { "codepoints": [1110], "characters": "\u0456" }, + "ï": { "codepoints": [239], "characters": "\u00EF" }, + "ï": { "codepoints": [239], "characters": "\u00EF" }, + "ĵ": { "codepoints": [309], "characters": "\u0135" }, + "й": { "codepoints": [1081], "characters": "\u0439" }, + "𝔧": { "codepoints": [120103], "characters": "\uD835\uDD27" }, + "ȷ": { "codepoints": [567], "characters": "\u0237" }, + "𝕛": { "codepoints": [120155], "characters": "\uD835\uDD5B" }, + "𝒿": { "codepoints": [119999], "characters": "\uD835\uDCBF" }, + "ј": { "codepoints": [1112], "characters": "\u0458" }, + "є": { "codepoints": [1108], "characters": "\u0454" }, + "κ": { "codepoints": [954], "characters": "\u03BA" }, + "ϰ": { "codepoints": [1008], "characters": "\u03F0" }, + "ķ": { "codepoints": [311], "characters": "\u0137" }, + "к": { "codepoints": [1082], "characters": "\u043A" }, + "𝔨": { "codepoints": [120104], "characters": "\uD835\uDD28" }, + "ĸ": { "codepoints": [312], "characters": "\u0138" }, + "х": { "codepoints": [1093], "characters": "\u0445" }, + "ќ": { "codepoints": [1116], "characters": "\u045C" }, + "𝕜": { "codepoints": [120156], "characters": "\uD835\uDD5C" }, + "𝓀": { "codepoints": [120000], "characters": "\uD835\uDCC0" }, + "⇚": { "codepoints": [8666], "characters": "\u21DA" }, + "⇐": { "codepoints": [8656], "characters": "\u21D0" }, + "⤛": { "codepoints": [10523], "characters": "\u291B" }, + "⤎": { "codepoints": [10510], "characters": "\u290E" }, + "≦": { "codepoints": [8806], "characters": "\u2266" }, + "⪋": { "codepoints": [10891], "characters": "\u2A8B" }, + "⥢": { "codepoints": [10594], "characters": "\u2962" }, + "ĺ": { "codepoints": [314], "characters": "\u013A" }, + "⦴": { "codepoints": [10676], "characters": "\u29B4" }, + "ℒ": { "codepoints": [8466], "characters": "\u2112" }, + "λ": { "codepoints": [955], "characters": "\u03BB" }, + "⟨": { "codepoints": [10216], "characters": "\u27E8" }, + "⦑": { "codepoints": [10641], "characters": "\u2991" }, + "⟨": { "codepoints": [10216], "characters": "\u27E8" }, + "⪅": { "codepoints": [10885], "characters": "\u2A85" }, + "«": { "codepoints": [171], "characters": "\u00AB" }, + "«": { "codepoints": [171], "characters": "\u00AB" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "⇤": { "codepoints": [8676], "characters": "\u21E4" }, + "⤟": { "codepoints": [10527], "characters": "\u291F" }, + "⤝": { "codepoints": [10525], "characters": "\u291D" }, + "↩": { "codepoints": [8617], "characters": "\u21A9" }, + "↫": { "codepoints": [8619], "characters": "\u21AB" }, + "⤹": { "codepoints": [10553], "characters": "\u2939" }, + "⥳": { "codepoints": [10611], "characters": "\u2973" }, + "↢": { "codepoints": [8610], "characters": "\u21A2" }, + "⪫": { "codepoints": [10923], "characters": "\u2AAB" }, + "⤙": { "codepoints": [10521], "characters": "\u2919" }, + "⪭": { "codepoints": [10925], "characters": "\u2AAD" }, + "⪭︀": { "codepoints": [10925, 65024], "characters": "\u2AAD\uFE00" }, + "⤌": { "codepoints": [10508], "characters": "\u290C" }, + "❲": { "codepoints": [10098], "characters": "\u2772" }, + "{": { "codepoints": [123], "characters": "\u007B" }, + "[": { "codepoints": [91], "characters": "\u005B" }, + "⦋": { "codepoints": [10635], "characters": "\u298B" }, + "⦏": { "codepoints": [10639], "characters": "\u298F" }, + "⦍": { "codepoints": [10637], "characters": "\u298D" }, + "ľ": { "codepoints": [318], "characters": "\u013E" }, + "ļ": { "codepoints": [316], "characters": "\u013C" }, + "⌈": { "codepoints": [8968], "characters": "\u2308" }, + "{": { "codepoints": [123], "characters": "\u007B" }, + "л": { "codepoints": [1083], "characters": "\u043B" }, + "⤶": { "codepoints": [10550], "characters": "\u2936" }, + "“": { "codepoints": [8220], "characters": "\u201C" }, + "„": { "codepoints": [8222], "characters": "\u201E" }, + "⥧": { "codepoints": [10599], "characters": "\u2967" }, + "⥋": { "codepoints": [10571], "characters": "\u294B" }, + "↲": { "codepoints": [8626], "characters": "\u21B2" }, + "≤": { "codepoints": [8804], "characters": "\u2264" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "↢": { "codepoints": [8610], "characters": "\u21A2" }, + "↽": { "codepoints": [8637], "characters": "\u21BD" }, + "↼": { "codepoints": [8636], "characters": "\u21BC" }, + "⇇": { "codepoints": [8647], "characters": "\u21C7" }, + "↔": { "codepoints": [8596], "characters": "\u2194" }, + "⇆": { "codepoints": [8646], "characters": "\u21C6" }, + "⇋": { "codepoints": [8651], "characters": "\u21CB" }, + "↭": { "codepoints": [8621], "characters": "\u21AD" }, + "⋋": { "codepoints": [8907], "characters": "\u22CB" }, + "⋚": { "codepoints": [8922], "characters": "\u22DA" }, + "≤": { "codepoints": [8804], "characters": "\u2264" }, + "≦": { "codepoints": [8806], "characters": "\u2266" }, + "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, + "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, + "⪨": { "codepoints": [10920], "characters": "\u2AA8" }, + "⩿": { "codepoints": [10879], "characters": "\u2A7F" }, + "⪁": { "codepoints": [10881], "characters": "\u2A81" }, + "⪃": { "codepoints": [10883], "characters": "\u2A83" }, + "⋚︀": { "codepoints": [8922, 65024], "characters": "\u22DA\uFE00" }, + "⪓": { "codepoints": [10899], "characters": "\u2A93" }, + "⪅": { "codepoints": [10885], "characters": "\u2A85" }, + "⋖": { "codepoints": [8918], "characters": "\u22D6" }, + "⋚": { "codepoints": [8922], "characters": "\u22DA" }, + "⪋": { "codepoints": [10891], "characters": "\u2A8B" }, + "≶": { "codepoints": [8822], "characters": "\u2276" }, + "≲": { "codepoints": [8818], "characters": "\u2272" }, + "⥼": { "codepoints": [10620], "characters": "\u297C" }, + "⌊": { "codepoints": [8970], "characters": "\u230A" }, + "𝔩": { "codepoints": [120105], "characters": "\uD835\uDD29" }, + "≶": { "codepoints": [8822], "characters": "\u2276" }, + "⪑": { "codepoints": [10897], "characters": "\u2A91" }, + "↽": { "codepoints": [8637], "characters": "\u21BD" }, + "↼": { "codepoints": [8636], "characters": "\u21BC" }, + "⥪": { "codepoints": [10602], "characters": "\u296A" }, + "▄": { "codepoints": [9604], "characters": "\u2584" }, + "љ": { "codepoints": [1113], "characters": "\u0459" }, + "≪": { "codepoints": [8810], "characters": "\u226A" }, + "⇇": { "codepoints": [8647], "characters": "\u21C7" }, + "⌞": { "codepoints": [8990], "characters": "\u231E" }, + "⥫": { "codepoints": [10603], "characters": "\u296B" }, + "◺": { "codepoints": [9722], "characters": "\u25FA" }, + "ŀ": { "codepoints": [320], "characters": "\u0140" }, + "⎰": { "codepoints": [9136], "characters": "\u23B0" }, + "⎰": { "codepoints": [9136], "characters": "\u23B0" }, + "≨": { "codepoints": [8808], "characters": "\u2268" }, + "⪉": { "codepoints": [10889], "characters": "\u2A89" }, + "⪉": { "codepoints": [10889], "characters": "\u2A89" }, + "⪇": { "codepoints": [10887], "characters": "\u2A87" }, + "⪇": { "codepoints": [10887], "characters": "\u2A87" }, + "≨": { "codepoints": [8808], "characters": "\u2268" }, + "⋦": { "codepoints": [8934], "characters": "\u22E6" }, + "⟬": { "codepoints": [10220], "characters": "\u27EC" }, + "⇽": { "codepoints": [8701], "characters": "\u21FD" }, + "⟦": { "codepoints": [10214], "characters": "\u27E6" }, + "⟵": { "codepoints": [10229], "characters": "\u27F5" }, + "⟷": { "codepoints": [10231], "characters": "\u27F7" }, + "⟼": { "codepoints": [10236], "characters": "\u27FC" }, + "⟶": { "codepoints": [10230], "characters": "\u27F6" }, + "↫": { "codepoints": [8619], "characters": "\u21AB" }, + "↬": { "codepoints": [8620], "characters": "\u21AC" }, + "⦅": { "codepoints": [10629], "characters": "\u2985" }, + "𝕝": { "codepoints": [120157], "characters": "\uD835\uDD5D" }, + "⨭": { "codepoints": [10797], "characters": "\u2A2D" }, + "⨴": { "codepoints": [10804], "characters": "\u2A34" }, + "∗": { "codepoints": [8727], "characters": "\u2217" }, + "_": { "codepoints": [95], "characters": "\u005F" }, + "◊": { "codepoints": [9674], "characters": "\u25CA" }, + "◊": { "codepoints": [9674], "characters": "\u25CA" }, + "⧫": { "codepoints": [10731], "characters": "\u29EB" }, + "(": { "codepoints": [40], "characters": "\u0028" }, + "⦓": { "codepoints": [10643], "characters": "\u2993" }, + "⇆": { "codepoints": [8646], "characters": "\u21C6" }, + "⌟": { "codepoints": [8991], "characters": "\u231F" }, + "⇋": { "codepoints": [8651], "characters": "\u21CB" }, + "⥭": { "codepoints": [10605], "characters": "\u296D" }, + "‎": { "codepoints": [8206], "characters": "\u200E" }, + "⊿": { "codepoints": [8895], "characters": "\u22BF" }, + "‹": { "codepoints": [8249], "characters": "\u2039" }, + "𝓁": { "codepoints": [120001], "characters": "\uD835\uDCC1" }, + "↰": { "codepoints": [8624], "characters": "\u21B0" }, + "≲": { "codepoints": [8818], "characters": "\u2272" }, + "⪍": { "codepoints": [10893], "characters": "\u2A8D" }, + "⪏": { "codepoints": [10895], "characters": "\u2A8F" }, + "[": { "codepoints": [91], "characters": "\u005B" }, + "‘": { "codepoints": [8216], "characters": "\u2018" }, + "‚": { "codepoints": [8218], "characters": "\u201A" }, + "ł": { "codepoints": [322], "characters": "\u0142" }, + "<": { "codepoints": [60], "characters": "\u003C" }, + "<": { "codepoints": [60], "characters": "\u003C" }, + "⪦": { "codepoints": [10918], "characters": "\u2AA6" }, + "⩹": { "codepoints": [10873], "characters": "\u2A79" }, + "⋖": { "codepoints": [8918], "characters": "\u22D6" }, + "⋋": { "codepoints": [8907], "characters": "\u22CB" }, + "⋉": { "codepoints": [8905], "characters": "\u22C9" }, + "⥶": { "codepoints": [10614], "characters": "\u2976" }, + "⩻": { "codepoints": [10875], "characters": "\u2A7B" }, + "⦖": { "codepoints": [10646], "characters": "\u2996" }, + "◃": { "codepoints": [9667], "characters": "\u25C3" }, + "⊴": { "codepoints": [8884], "characters": "\u22B4" }, + "◂": { "codepoints": [9666], "characters": "\u25C2" }, + "⥊": { "codepoints": [10570], "characters": "\u294A" }, + "⥦": { "codepoints": [10598], "characters": "\u2966" }, + "≨︀": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" }, + "≨︀": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" }, + "∺": { "codepoints": [8762], "characters": "\u223A" }, + "¯": { "codepoints": [175], "characters": "\u00AF" }, + "¯": { "codepoints": [175], "characters": "\u00AF" }, + "♂": { "codepoints": [9794], "characters": "\u2642" }, + "✠": { "codepoints": [10016], "characters": "\u2720" }, + "✠": { "codepoints": [10016], "characters": "\u2720" }, + "↦": { "codepoints": [8614], "characters": "\u21A6" }, + "↦": { "codepoints": [8614], "characters": "\u21A6" }, + "↧": { "codepoints": [8615], "characters": "\u21A7" }, + "↤": { "codepoints": [8612], "characters": "\u21A4" }, + "↥": { "codepoints": [8613], "characters": "\u21A5" }, + "▮": { "codepoints": [9646], "characters": "\u25AE" }, + "⨩": { "codepoints": [10793], "characters": "\u2A29" }, + "м": { "codepoints": [1084], "characters": "\u043C" }, + "—": { "codepoints": [8212], "characters": "\u2014" }, + "∡": { "codepoints": [8737], "characters": "\u2221" }, + "𝔪": { "codepoints": [120106], "characters": "\uD835\uDD2A" }, + "℧": { "codepoints": [8487], "characters": "\u2127" }, + "µ": { "codepoints": [181], "characters": "\u00B5" }, + "µ": { "codepoints": [181], "characters": "\u00B5" }, + "∣": { "codepoints": [8739], "characters": "\u2223" }, + "*": { "codepoints": [42], "characters": "\u002A" }, + "⫰": { "codepoints": [10992], "characters": "\u2AF0" }, + "·": { "codepoints": [183], "characters": "\u00B7" }, + "·": { "codepoints": [183], "characters": "\u00B7" }, + "−": { "codepoints": [8722], "characters": "\u2212" }, + "⊟": { "codepoints": [8863], "characters": "\u229F" }, + "∸": { "codepoints": [8760], "characters": "\u2238" }, + "⨪": { "codepoints": [10794], "characters": "\u2A2A" }, + "⫛": { "codepoints": [10971], "characters": "\u2ADB" }, + "…": { "codepoints": [8230], "characters": "\u2026" }, + "∓": { "codepoints": [8723], "characters": "\u2213" }, + "⊧": { "codepoints": [8871], "characters": "\u22A7" }, + "𝕞": { "codepoints": [120158], "characters": "\uD835\uDD5E" }, + "∓": { "codepoints": [8723], "characters": "\u2213" }, + "𝓂": { "codepoints": [120002], "characters": "\uD835\uDCC2" }, + "∾": { "codepoints": [8766], "characters": "\u223E" }, + "μ": { "codepoints": [956], "characters": "\u03BC" }, + "⊸": { "codepoints": [8888], "characters": "\u22B8" }, + "⊸": { "codepoints": [8888], "characters": "\u22B8" }, + "⋙̸": { "codepoints": [8921, 824], "characters": "\u22D9\u0338" }, + "≫⃒": { "codepoints": [8811, 8402], "characters": "\u226B\u20D2" }, + "≫̸": { "codepoints": [8811, 824], "characters": "\u226B\u0338" }, + "⇍": { "codepoints": [8653], "characters": "\u21CD" }, + "⇎": { "codepoints": [8654], "characters": "\u21CE" }, + "⋘̸": { "codepoints": [8920, 824], "characters": "\u22D8\u0338" }, + "≪⃒": { "codepoints": [8810, 8402], "characters": "\u226A\u20D2" }, + "≪̸": { "codepoints": [8810, 824], "characters": "\u226A\u0338" }, + "⇏": { "codepoints": [8655], "characters": "\u21CF" }, + "⊯": { "codepoints": [8879], "characters": "\u22AF" }, + "⊮": { "codepoints": [8878], "characters": "\u22AE" }, + "∇": { "codepoints": [8711], "characters": "\u2207" }, + "ń": { "codepoints": [324], "characters": "\u0144" }, + "∠⃒": { "codepoints": [8736, 8402], "characters": "\u2220\u20D2" }, + "≉": { "codepoints": [8777], "characters": "\u2249" }, + "⩰̸": { "codepoints": [10864, 824], "characters": "\u2A70\u0338" }, + "≋̸": { "codepoints": [8779, 824], "characters": "\u224B\u0338" }, + "ʼn": { "codepoints": [329], "characters": "\u0149" }, + "≉": { "codepoints": [8777], "characters": "\u2249" }, + "♮": { "codepoints": [9838], "characters": "\u266E" }, + "♮": { "codepoints": [9838], "characters": "\u266E" }, + "ℕ": { "codepoints": [8469], "characters": "\u2115" }, + " ": { "codepoints": [160], "characters": "\u00A0" }, + " ": { "codepoints": [160], "characters": "\u00A0" }, + "≎̸": { "codepoints": [8782, 824], "characters": "\u224E\u0338" }, + "≏̸": { "codepoints": [8783, 824], "characters": "\u224F\u0338" }, + "⩃": { "codepoints": [10819], "characters": "\u2A43" }, + "ň": { "codepoints": [328], "characters": "\u0148" }, + "ņ": { "codepoints": [326], "characters": "\u0146" }, + "≇": { "codepoints": [8775], "characters": "\u2247" }, + "⩭̸": { "codepoints": [10861, 824], "characters": "\u2A6D\u0338" }, + "⩂": { "codepoints": [10818], "characters": "\u2A42" }, + "н": { "codepoints": [1085], "characters": "\u043D" }, + "–": { "codepoints": [8211], "characters": "\u2013" }, + "≠": { "codepoints": [8800], "characters": "\u2260" }, + "⇗": { "codepoints": [8663], "characters": "\u21D7" }, + "⤤": { "codepoints": [10532], "characters": "\u2924" }, + "↗": { "codepoints": [8599], "characters": "\u2197" }, + "↗": { "codepoints": [8599], "characters": "\u2197" }, + "≐̸": { "codepoints": [8784, 824], "characters": "\u2250\u0338" }, + "≢": { "codepoints": [8802], "characters": "\u2262" }, + "⤨": { "codepoints": [10536], "characters": "\u2928" }, + "≂̸": { "codepoints": [8770, 824], "characters": "\u2242\u0338" }, + "∄": { "codepoints": [8708], "characters": "\u2204" }, + "∄": { "codepoints": [8708], "characters": "\u2204" }, + "𝔫": { "codepoints": [120107], "characters": "\uD835\uDD2B" }, + "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, + "≱": { "codepoints": [8817], "characters": "\u2271" }, + "≱": { "codepoints": [8817], "characters": "\u2271" }, + "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, + "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, + "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, + "≵": { "codepoints": [8821], "characters": "\u2275" }, + "≯": { "codepoints": [8815], "characters": "\u226F" }, + "≯": { "codepoints": [8815], "characters": "\u226F" }, + "⇎": { "codepoints": [8654], "characters": "\u21CE" }, + "↮": { "codepoints": [8622], "characters": "\u21AE" }, + "⫲": { "codepoints": [10994], "characters": "\u2AF2" }, + "∋": { "codepoints": [8715], "characters": "\u220B" }, + "⋼": { "codepoints": [8956], "characters": "\u22FC" }, + "⋺": { "codepoints": [8954], "characters": "\u22FA" }, + "∋": { "codepoints": [8715], "characters": "\u220B" }, + "њ": { "codepoints": [1114], "characters": "\u045A" }, + "⇍": { "codepoints": [8653], "characters": "\u21CD" }, + "≦̸": { "codepoints": [8806, 824], "characters": "\u2266\u0338" }, + "↚": { "codepoints": [8602], "characters": "\u219A" }, + "‥": { "codepoints": [8229], "characters": "\u2025" }, + "≰": { "codepoints": [8816], "characters": "\u2270" }, + "↚": { "codepoints": [8602], "characters": "\u219A" }, + "↮": { "codepoints": [8622], "characters": "\u21AE" }, + "≰": { "codepoints": [8816], "characters": "\u2270" }, + "≦̸": { "codepoints": [8806, 824], "characters": "\u2266\u0338" }, + "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, + "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, + "≮": { "codepoints": [8814], "characters": "\u226E" }, + "≴": { "codepoints": [8820], "characters": "\u2274" }, + "≮": { "codepoints": [8814], "characters": "\u226E" }, + "⋪": { "codepoints": [8938], "characters": "\u22EA" }, + "⋬": { "codepoints": [8940], "characters": "\u22EC" }, + "∤": { "codepoints": [8740], "characters": "\u2224" }, + "𝕟": { "codepoints": [120159], "characters": "\uD835\uDD5F" }, + "¬": { "codepoints": [172], "characters": "\u00AC" }, + "¬": { "codepoints": [172], "characters": "\u00AC" }, + "∉": { "codepoints": [8713], "characters": "\u2209" }, + "⋹̸": { "codepoints": [8953, 824], "characters": "\u22F9\u0338" }, + "⋵̸": { "codepoints": [8949, 824], "characters": "\u22F5\u0338" }, + "∉": { "codepoints": [8713], "characters": "\u2209" }, + "⋷": { "codepoints": [8951], "characters": "\u22F7" }, + "⋶": { "codepoints": [8950], "characters": "\u22F6" }, + "∌": { "codepoints": [8716], "characters": "\u220C" }, + "∌": { "codepoints": [8716], "characters": "\u220C" }, + "⋾": { "codepoints": [8958], "characters": "\u22FE" }, + "⋽": { "codepoints": [8957], "characters": "\u22FD" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "⫽⃥": { "codepoints": [11005, 8421], "characters": "\u2AFD\u20E5" }, + "∂̸": { "codepoints": [8706, 824], "characters": "\u2202\u0338" }, + "⨔": { "codepoints": [10772], "characters": "\u2A14" }, + "⊀": { "codepoints": [8832], "characters": "\u2280" }, + "⋠": { "codepoints": [8928], "characters": "\u22E0" }, + "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, + "⊀": { "codepoints": [8832], "characters": "\u2280" }, + "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, + "⇏": { "codepoints": [8655], "characters": "\u21CF" }, + "↛": { "codepoints": [8603], "characters": "\u219B" }, + "⤳̸": { "codepoints": [10547, 824], "characters": "\u2933\u0338" }, + "↝̸": { "codepoints": [8605, 824], "characters": "\u219D\u0338" }, + "↛": { "codepoints": [8603], "characters": "\u219B" }, + "⋫": { "codepoints": [8939], "characters": "\u22EB" }, + "⋭": { "codepoints": [8941], "characters": "\u22ED" }, + "⊁": { "codepoints": [8833], "characters": "\u2281" }, + "⋡": { "codepoints": [8929], "characters": "\u22E1" }, + "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, + "𝓃": { "codepoints": [120003], "characters": "\uD835\uDCC3" }, + "∤": { "codepoints": [8740], "characters": "\u2224" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "≁": { "codepoints": [8769], "characters": "\u2241" }, + "≄": { "codepoints": [8772], "characters": "\u2244" }, + "≄": { "codepoints": [8772], "characters": "\u2244" }, + "∤": { "codepoints": [8740], "characters": "\u2224" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "⋢": { "codepoints": [8930], "characters": "\u22E2" }, + "⋣": { "codepoints": [8931], "characters": "\u22E3" }, + "⊄": { "codepoints": [8836], "characters": "\u2284" }, + "⫅̸": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" }, + "⊈": { "codepoints": [8840], "characters": "\u2288" }, + "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, + "⊈": { "codepoints": [8840], "characters": "\u2288" }, + "⫅̸": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" }, + "⊁": { "codepoints": [8833], "characters": "\u2281" }, + "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, + "⊅": { "codepoints": [8837], "characters": "\u2285" }, + "⫆̸": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" }, + "⊉": { "codepoints": [8841], "characters": "\u2289" }, + "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, + "⊉": { "codepoints": [8841], "characters": "\u2289" }, + "⫆̸": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" }, + "≹": { "codepoints": [8825], "characters": "\u2279" }, + "ñ": { "codepoints": [241], "characters": "\u00F1" }, + "ñ": { "codepoints": [241], "characters": "\u00F1" }, + "≸": { "codepoints": [8824], "characters": "\u2278" }, + "⋪": { "codepoints": [8938], "characters": "\u22EA" }, + "⋬": { "codepoints": [8940], "characters": "\u22EC" }, + "⋫": { "codepoints": [8939], "characters": "\u22EB" }, + "⋭": { "codepoints": [8941], "characters": "\u22ED" }, + "ν": { "codepoints": [957], "characters": "\u03BD" }, + "#": { "codepoints": [35], "characters": "\u0023" }, + "№": { "codepoints": [8470], "characters": "\u2116" }, + " ": { "codepoints": [8199], "characters": "\u2007" }, + "⊭": { "codepoints": [8877], "characters": "\u22AD" }, + "⤄": { "codepoints": [10500], "characters": "\u2904" }, + "≍⃒": { "codepoints": [8781, 8402], "characters": "\u224D\u20D2" }, + "⊬": { "codepoints": [8876], "characters": "\u22AC" }, + "≥⃒": { "codepoints": [8805, 8402], "characters": "\u2265\u20D2" }, + ">⃒": { "codepoints": [62, 8402], "characters": "\u003E\u20D2" }, + "⧞": { "codepoints": [10718], "characters": "\u29DE" }, + "⤂": { "codepoints": [10498], "characters": "\u2902" }, + "≤⃒": { "codepoints": [8804, 8402], "characters": "\u2264\u20D2" }, + "<⃒": { "codepoints": [60, 8402], "characters": "\u003C\u20D2" }, + "⊴⃒": { "codepoints": [8884, 8402], "characters": "\u22B4\u20D2" }, + "⤃": { "codepoints": [10499], "characters": "\u2903" }, + "⊵⃒": { "codepoints": [8885, 8402], "characters": "\u22B5\u20D2" }, + "∼⃒": { "codepoints": [8764, 8402], "characters": "\u223C\u20D2" }, + "⇖": { "codepoints": [8662], "characters": "\u21D6" }, + "⤣": { "codepoints": [10531], "characters": "\u2923" }, + "↖": { "codepoints": [8598], "characters": "\u2196" }, + "↖": { "codepoints": [8598], "characters": "\u2196" }, + "⤧": { "codepoints": [10535], "characters": "\u2927" }, + "Ⓢ": { "codepoints": [9416], "characters": "\u24C8" }, + "ó": { "codepoints": [243], "characters": "\u00F3" }, + "ó": { "codepoints": [243], "characters": "\u00F3" }, + "⊛": { "codepoints": [8859], "characters": "\u229B" }, + "⊚": { "codepoints": [8858], "characters": "\u229A" }, + "ô": { "codepoints": [244], "characters": "\u00F4" }, + "ô": { "codepoints": [244], "characters": "\u00F4" }, + "о": { "codepoints": [1086], "characters": "\u043E" }, + "⊝": { "codepoints": [8861], "characters": "\u229D" }, + "ő": { "codepoints": [337], "characters": "\u0151" }, + "⨸": { "codepoints": [10808], "characters": "\u2A38" }, + "⊙": { "codepoints": [8857], "characters": "\u2299" }, + "⦼": { "codepoints": [10684], "characters": "\u29BC" }, + "œ": { "codepoints": [339], "characters": "\u0153" }, + "⦿": { "codepoints": [10687], "characters": "\u29BF" }, + "𝔬": { "codepoints": [120108], "characters": "\uD835\uDD2C" }, + "˛": { "codepoints": [731], "characters": "\u02DB" }, + "ò": { "codepoints": [242], "characters": "\u00F2" }, + "ò": { "codepoints": [242], "characters": "\u00F2" }, + "⧁": { "codepoints": [10689], "characters": "\u29C1" }, + "⦵": { "codepoints": [10677], "characters": "\u29B5" }, + "Ω": { "codepoints": [937], "characters": "\u03A9" }, + "∮": { "codepoints": [8750], "characters": "\u222E" }, + "↺": { "codepoints": [8634], "characters": "\u21BA" }, + "⦾": { "codepoints": [10686], "characters": "\u29BE" }, + "⦻": { "codepoints": [10683], "characters": "\u29BB" }, + "‾": { "codepoints": [8254], "characters": "\u203E" }, + "⧀": { "codepoints": [10688], "characters": "\u29C0" }, + "ō": { "codepoints": [333], "characters": "\u014D" }, + "ω": { "codepoints": [969], "characters": "\u03C9" }, + "ο": { "codepoints": [959], "characters": "\u03BF" }, + "⦶": { "codepoints": [10678], "characters": "\u29B6" }, + "⊖": { "codepoints": [8854], "characters": "\u2296" }, + "𝕠": { "codepoints": [120160], "characters": "\uD835\uDD60" }, + "⦷": { "codepoints": [10679], "characters": "\u29B7" }, + "⦹": { "codepoints": [10681], "characters": "\u29B9" }, + "⊕": { "codepoints": [8853], "characters": "\u2295" }, + "∨": { "codepoints": [8744], "characters": "\u2228" }, + "↻": { "codepoints": [8635], "characters": "\u21BB" }, + "⩝": { "codepoints": [10845], "characters": "\u2A5D" }, + "ℴ": { "codepoints": [8500], "characters": "\u2134" }, + "ℴ": { "codepoints": [8500], "characters": "\u2134" }, + "ª": { "codepoints": [170], "characters": "\u00AA" }, + "ª": { "codepoints": [170], "characters": "\u00AA" }, + "º": { "codepoints": [186], "characters": "\u00BA" }, + "º": { "codepoints": [186], "characters": "\u00BA" }, + "⊶": { "codepoints": [8886], "characters": "\u22B6" }, + "⩖": { "codepoints": [10838], "characters": "\u2A56" }, + "⩗": { "codepoints": [10839], "characters": "\u2A57" }, + "⩛": { "codepoints": [10843], "characters": "\u2A5B" }, + "ℴ": { "codepoints": [8500], "characters": "\u2134" }, + "ø": { "codepoints": [248], "characters": "\u00F8" }, + "ø": { "codepoints": [248], "characters": "\u00F8" }, + "⊘": { "codepoints": [8856], "characters": "\u2298" }, + "õ": { "codepoints": [245], "characters": "\u00F5" }, + "õ": { "codepoints": [245], "characters": "\u00F5" }, + "⊗": { "codepoints": [8855], "characters": "\u2297" }, + "⨶": { "codepoints": [10806], "characters": "\u2A36" }, + "ö": { "codepoints": [246], "characters": "\u00F6" }, + "ö": { "codepoints": [246], "characters": "\u00F6" }, + "⌽": { "codepoints": [9021], "characters": "\u233D" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "¶": { "codepoints": [182], "characters": "\u00B6" }, + "¶": { "codepoints": [182], "characters": "\u00B6" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "⫳": { "codepoints": [10995], "characters": "\u2AF3" }, + "⫽": { "codepoints": [11005], "characters": "\u2AFD" }, + "∂": { "codepoints": [8706], "characters": "\u2202" }, + "п": { "codepoints": [1087], "characters": "\u043F" }, + "%": { "codepoints": [37], "characters": "\u0025" }, + ".": { "codepoints": [46], "characters": "\u002E" }, + "‰": { "codepoints": [8240], "characters": "\u2030" }, + "⊥": { "codepoints": [8869], "characters": "\u22A5" }, + "‱": { "codepoints": [8241], "characters": "\u2031" }, + "𝔭": { "codepoints": [120109], "characters": "\uD835\uDD2D" }, + "φ": { "codepoints": [966], "characters": "\u03C6" }, + "ϕ": { "codepoints": [981], "characters": "\u03D5" }, + "ℳ": { "codepoints": [8499], "characters": "\u2133" }, + "☎": { "codepoints": [9742], "characters": "\u260E" }, + "π": { "codepoints": [960], "characters": "\u03C0" }, + "⋔": { "codepoints": [8916], "characters": "\u22D4" }, + "ϖ": { "codepoints": [982], "characters": "\u03D6" }, + "ℏ": { "codepoints": [8463], "characters": "\u210F" }, + "ℎ": { "codepoints": [8462], "characters": "\u210E" }, + "ℏ": { "codepoints": [8463], "characters": "\u210F" }, + "+": { "codepoints": [43], "characters": "\u002B" }, + "⨣": { "codepoints": [10787], "characters": "\u2A23" }, + "⊞": { "codepoints": [8862], "characters": "\u229E" }, + "⨢": { "codepoints": [10786], "characters": "\u2A22" }, + "∔": { "codepoints": [8724], "characters": "\u2214" }, + "⨥": { "codepoints": [10789], "characters": "\u2A25" }, + "⩲": { "codepoints": [10866], "characters": "\u2A72" }, + "±": { "codepoints": [177], "characters": "\u00B1" }, + "±": { "codepoints": [177], "characters": "\u00B1" }, + "⨦": { "codepoints": [10790], "characters": "\u2A26" }, + "⨧": { "codepoints": [10791], "characters": "\u2A27" }, + "±": { "codepoints": [177], "characters": "\u00B1" }, + "⨕": { "codepoints": [10773], "characters": "\u2A15" }, + "𝕡": { "codepoints": [120161], "characters": "\uD835\uDD61" }, + "£": { "codepoints": [163], "characters": "\u00A3" }, + "£": { "codepoints": [163], "characters": "\u00A3" }, + "≺": { "codepoints": [8826], "characters": "\u227A" }, + "⪳": { "codepoints": [10931], "characters": "\u2AB3" }, + "⪷": { "codepoints": [10935], "characters": "\u2AB7" }, + "≼": { "codepoints": [8828], "characters": "\u227C" }, + "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, + "≺": { "codepoints": [8826], "characters": "\u227A" }, + "⪷": { "codepoints": [10935], "characters": "\u2AB7" }, + "≼": { "codepoints": [8828], "characters": "\u227C" }, + "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, + "⪹": { "codepoints": [10937], "characters": "\u2AB9" }, + "⪵": { "codepoints": [10933], "characters": "\u2AB5" }, + "⋨": { "codepoints": [8936], "characters": "\u22E8" }, + "≾": { "codepoints": [8830], "characters": "\u227E" }, + "′": { "codepoints": [8242], "characters": "\u2032" }, + "ℙ": { "codepoints": [8473], "characters": "\u2119" }, + "⪵": { "codepoints": [10933], "characters": "\u2AB5" }, + "⪹": { "codepoints": [10937], "characters": "\u2AB9" }, + "⋨": { "codepoints": [8936], "characters": "\u22E8" }, + "∏": { "codepoints": [8719], "characters": "\u220F" }, + "⌮": { "codepoints": [9006], "characters": "\u232E" }, + "⌒": { "codepoints": [8978], "characters": "\u2312" }, + "⌓": { "codepoints": [8979], "characters": "\u2313" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "≾": { "codepoints": [8830], "characters": "\u227E" }, + "⊰": { "codepoints": [8880], "characters": "\u22B0" }, + "𝓅": { "codepoints": [120005], "characters": "\uD835\uDCC5" }, + "ψ": { "codepoints": [968], "characters": "\u03C8" }, + " ": { "codepoints": [8200], "characters": "\u2008" }, + "𝔮": { "codepoints": [120110], "characters": "\uD835\uDD2E" }, + "⨌": { "codepoints": [10764], "characters": "\u2A0C" }, + "𝕢": { "codepoints": [120162], "characters": "\uD835\uDD62" }, + "⁗": { "codepoints": [8279], "characters": "\u2057" }, + "𝓆": { "codepoints": [120006], "characters": "\uD835\uDCC6" }, + "ℍ": { "codepoints": [8461], "characters": "\u210D" }, + "⨖": { "codepoints": [10774], "characters": "\u2A16" }, + "?": { "codepoints": [63], "characters": "\u003F" }, + "≟": { "codepoints": [8799], "characters": "\u225F" }, + """: { "codepoints": [34], "characters": "\u0022" }, + """: { "codepoints": [34], "characters": "\u0022" }, + "⇛": { "codepoints": [8667], "characters": "\u21DB" }, + "⇒": { "codepoints": [8658], "characters": "\u21D2" }, + "⤜": { "codepoints": [10524], "characters": "\u291C" }, + "⤏": { "codepoints": [10511], "characters": "\u290F" }, + "⥤": { "codepoints": [10596], "characters": "\u2964" }, + "∽̱": { "codepoints": [8765, 817], "characters": "\u223D\u0331" }, + "ŕ": { "codepoints": [341], "characters": "\u0155" }, + "√": { "codepoints": [8730], "characters": "\u221A" }, + "⦳": { "codepoints": [10675], "characters": "\u29B3" }, + "⟩": { "codepoints": [10217], "characters": "\u27E9" }, + "⦒": { "codepoints": [10642], "characters": "\u2992" }, + "⦥": { "codepoints": [10661], "characters": "\u29A5" }, + "⟩": { "codepoints": [10217], "characters": "\u27E9" }, + "»": { "codepoints": [187], "characters": "\u00BB" }, + "»": { "codepoints": [187], "characters": "\u00BB" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "⥵": { "codepoints": [10613], "characters": "\u2975" }, + "⇥": { "codepoints": [8677], "characters": "\u21E5" }, + "⤠": { "codepoints": [10528], "characters": "\u2920" }, + "⤳": { "codepoints": [10547], "characters": "\u2933" }, + "⤞": { "codepoints": [10526], "characters": "\u291E" }, + "↪": { "codepoints": [8618], "characters": "\u21AA" }, + "↬": { "codepoints": [8620], "characters": "\u21AC" }, + "⥅": { "codepoints": [10565], "characters": "\u2945" }, + "⥴": { "codepoints": [10612], "characters": "\u2974" }, + "↣": { "codepoints": [8611], "characters": "\u21A3" }, + "↝": { "codepoints": [8605], "characters": "\u219D" }, + "⤚": { "codepoints": [10522], "characters": "\u291A" }, + "∶": { "codepoints": [8758], "characters": "\u2236" }, + "ℚ": { "codepoints": [8474], "characters": "\u211A" }, + "⤍": { "codepoints": [10509], "characters": "\u290D" }, + "❳": { "codepoints": [10099], "characters": "\u2773" }, + "}": { "codepoints": [125], "characters": "\u007D" }, + "]": { "codepoints": [93], "characters": "\u005D" }, + "⦌": { "codepoints": [10636], "characters": "\u298C" }, + "⦎": { "codepoints": [10638], "characters": "\u298E" }, + "⦐": { "codepoints": [10640], "characters": "\u2990" }, + "ř": { "codepoints": [345], "characters": "\u0159" }, + "ŗ": { "codepoints": [343], "characters": "\u0157" }, + "⌉": { "codepoints": [8969], "characters": "\u2309" }, + "}": { "codepoints": [125], "characters": "\u007D" }, + "р": { "codepoints": [1088], "characters": "\u0440" }, + "⤷": { "codepoints": [10551], "characters": "\u2937" }, + "⥩": { "codepoints": [10601], "characters": "\u2969" }, + "”": { "codepoints": [8221], "characters": "\u201D" }, + "”": { "codepoints": [8221], "characters": "\u201D" }, + "↳": { "codepoints": [8627], "characters": "\u21B3" }, + "ℜ": { "codepoints": [8476], "characters": "\u211C" }, + "ℛ": { "codepoints": [8475], "characters": "\u211B" }, + "ℜ": { "codepoints": [8476], "characters": "\u211C" }, + "ℝ": { "codepoints": [8477], "characters": "\u211D" }, + "▭": { "codepoints": [9645], "characters": "\u25AD" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "⥽": { "codepoints": [10621], "characters": "\u297D" }, + "⌋": { "codepoints": [8971], "characters": "\u230B" }, + "𝔯": { "codepoints": [120111], "characters": "\uD835\uDD2F" }, + "⇁": { "codepoints": [8641], "characters": "\u21C1" }, + "⇀": { "codepoints": [8640], "characters": "\u21C0" }, + "⥬": { "codepoints": [10604], "characters": "\u296C" }, + "ρ": { "codepoints": [961], "characters": "\u03C1" }, + "ϱ": { "codepoints": [1009], "characters": "\u03F1" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "↣": { "codepoints": [8611], "characters": "\u21A3" }, + "⇁": { "codepoints": [8641], "characters": "\u21C1" }, + "⇀": { "codepoints": [8640], "characters": "\u21C0" }, + "⇄": { "codepoints": [8644], "characters": "\u21C4" }, + "⇌": { "codepoints": [8652], "characters": "\u21CC" }, + "⇉": { "codepoints": [8649], "characters": "\u21C9" }, + "↝": { "codepoints": [8605], "characters": "\u219D" }, + "⋌": { "codepoints": [8908], "characters": "\u22CC" }, + "˚": { "codepoints": [730], "characters": "\u02DA" }, + "≓": { "codepoints": [8787], "characters": "\u2253" }, + "⇄": { "codepoints": [8644], "characters": "\u21C4" }, + "⇌": { "codepoints": [8652], "characters": "\u21CC" }, + "‏": { "codepoints": [8207], "characters": "\u200F" }, + "⎱": { "codepoints": [9137], "characters": "\u23B1" }, + "⎱": { "codepoints": [9137], "characters": "\u23B1" }, + "⫮": { "codepoints": [10990], "characters": "\u2AEE" }, + "⟭": { "codepoints": [10221], "characters": "\u27ED" }, + "⇾": { "codepoints": [8702], "characters": "\u21FE" }, + "⟧": { "codepoints": [10215], "characters": "\u27E7" }, + "⦆": { "codepoints": [10630], "characters": "\u2986" }, + "𝕣": { "codepoints": [120163], "characters": "\uD835\uDD63" }, + "⨮": { "codepoints": [10798], "characters": "\u2A2E" }, + "⨵": { "codepoints": [10805], "characters": "\u2A35" }, + ")": { "codepoints": [41], "characters": "\u0029" }, + "⦔": { "codepoints": [10644], "characters": "\u2994" }, + "⨒": { "codepoints": [10770], "characters": "\u2A12" }, + "⇉": { "codepoints": [8649], "characters": "\u21C9" }, + "›": { "codepoints": [8250], "characters": "\u203A" }, + "𝓇": { "codepoints": [120007], "characters": "\uD835\uDCC7" }, + "↱": { "codepoints": [8625], "characters": "\u21B1" }, + "]": { "codepoints": [93], "characters": "\u005D" }, + "’": { "codepoints": [8217], "characters": "\u2019" }, + "’": { "codepoints": [8217], "characters": "\u2019" }, + "⋌": { "codepoints": [8908], "characters": "\u22CC" }, + "⋊": { "codepoints": [8906], "characters": "\u22CA" }, + "▹": { "codepoints": [9657], "characters": "\u25B9" }, + "⊵": { "codepoints": [8885], "characters": "\u22B5" }, + "▸": { "codepoints": [9656], "characters": "\u25B8" }, + "⧎": { "codepoints": [10702], "characters": "\u29CE" }, + "⥨": { "codepoints": [10600], "characters": "\u2968" }, + "℞": { "codepoints": [8478], "characters": "\u211E" }, + "ś": { "codepoints": [347], "characters": "\u015B" }, + "‚": { "codepoints": [8218], "characters": "\u201A" }, + "≻": { "codepoints": [8827], "characters": "\u227B" }, + "⪴": { "codepoints": [10932], "characters": "\u2AB4" }, + "⪸": { "codepoints": [10936], "characters": "\u2AB8" }, + "š": { "codepoints": [353], "characters": "\u0161" }, + "≽": { "codepoints": [8829], "characters": "\u227D" }, + "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, + "ş": { "codepoints": [351], "characters": "\u015F" }, + "ŝ": { "codepoints": [349], "characters": "\u015D" }, + "⪶": { "codepoints": [10934], "characters": "\u2AB6" }, + "⪺": { "codepoints": [10938], "characters": "\u2ABA" }, + "⋩": { "codepoints": [8937], "characters": "\u22E9" }, + "⨓": { "codepoints": [10771], "characters": "\u2A13" }, + "≿": { "codepoints": [8831], "characters": "\u227F" }, + "с": { "codepoints": [1089], "characters": "\u0441" }, + "⋅": { "codepoints": [8901], "characters": "\u22C5" }, + "⊡": { "codepoints": [8865], "characters": "\u22A1" }, + "⩦": { "codepoints": [10854], "characters": "\u2A66" }, + "⇘": { "codepoints": [8664], "characters": "\u21D8" }, + "⤥": { "codepoints": [10533], "characters": "\u2925" }, + "↘": { "codepoints": [8600], "characters": "\u2198" }, + "↘": { "codepoints": [8600], "characters": "\u2198" }, + "§": { "codepoints": [167], "characters": "\u00A7" }, + "§": { "codepoints": [167], "characters": "\u00A7" }, + ";": { "codepoints": [59], "characters": "\u003B" }, + "⤩": { "codepoints": [10537], "characters": "\u2929" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "✶": { "codepoints": [10038], "characters": "\u2736" }, + "𝔰": { "codepoints": [120112], "characters": "\uD835\uDD30" }, + "⌢": { "codepoints": [8994], "characters": "\u2322" }, + "♯": { "codepoints": [9839], "characters": "\u266F" }, + "щ": { "codepoints": [1097], "characters": "\u0449" }, + "ш": { "codepoints": [1096], "characters": "\u0448" }, + "∣": { "codepoints": [8739], "characters": "\u2223" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "­": { "codepoints": [173], "characters": "\u00AD" }, + "­": { "codepoints": [173], "characters": "\u00AD" }, + "σ": { "codepoints": [963], "characters": "\u03C3" }, + "ς": { "codepoints": [962], "characters": "\u03C2" }, + "ς": { "codepoints": [962], "characters": "\u03C2" }, + "∼": { "codepoints": [8764], "characters": "\u223C" }, + "⩪": { "codepoints": [10858], "characters": "\u2A6A" }, + "≃": { "codepoints": [8771], "characters": "\u2243" }, + "≃": { "codepoints": [8771], "characters": "\u2243" }, + "⪞": { "codepoints": [10910], "characters": "\u2A9E" }, + "⪠": { "codepoints": [10912], "characters": "\u2AA0" }, + "⪝": { "codepoints": [10909], "characters": "\u2A9D" }, + "⪟": { "codepoints": [10911], "characters": "\u2A9F" }, + "≆": { "codepoints": [8774], "characters": "\u2246" }, + "⨤": { "codepoints": [10788], "characters": "\u2A24" }, + "⥲": { "codepoints": [10610], "characters": "\u2972" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "⨳": { "codepoints": [10803], "characters": "\u2A33" }, + "⧤": { "codepoints": [10724], "characters": "\u29E4" }, + "∣": { "codepoints": [8739], "characters": "\u2223" }, + "⌣": { "codepoints": [8995], "characters": "\u2323" }, + "⪪": { "codepoints": [10922], "characters": "\u2AAA" }, + "⪬": { "codepoints": [10924], "characters": "\u2AAC" }, + "⪬︀": { "codepoints": [10924, 65024], "characters": "\u2AAC\uFE00" }, + "ь": { "codepoints": [1100], "characters": "\u044C" }, + "/": { "codepoints": [47], "characters": "\u002F" }, + "⧄": { "codepoints": [10692], "characters": "\u29C4" }, + "⌿": { "codepoints": [9023], "characters": "\u233F" }, + "𝕤": { "codepoints": [120164], "characters": "\uD835\uDD64" }, + "♠": { "codepoints": [9824], "characters": "\u2660" }, + "♠": { "codepoints": [9824], "characters": "\u2660" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "⊓": { "codepoints": [8851], "characters": "\u2293" }, + "⊓︀": { "codepoints": [8851, 65024], "characters": "\u2293\uFE00" }, + "⊔": { "codepoints": [8852], "characters": "\u2294" }, + "⊔︀": { "codepoints": [8852, 65024], "characters": "\u2294\uFE00" }, + "⊏": { "codepoints": [8847], "characters": "\u228F" }, + "⊑": { "codepoints": [8849], "characters": "\u2291" }, + "⊏": { "codepoints": [8847], "characters": "\u228F" }, + "⊑": { "codepoints": [8849], "characters": "\u2291" }, + "⊐": { "codepoints": [8848], "characters": "\u2290" }, + "⊒": { "codepoints": [8850], "characters": "\u2292" }, + "⊐": { "codepoints": [8848], "characters": "\u2290" }, + "⊒": { "codepoints": [8850], "characters": "\u2292" }, + "□": { "codepoints": [9633], "characters": "\u25A1" }, + "□": { "codepoints": [9633], "characters": "\u25A1" }, + "▪": { "codepoints": [9642], "characters": "\u25AA" }, + "▪": { "codepoints": [9642], "characters": "\u25AA" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "𝓈": { "codepoints": [120008], "characters": "\uD835\uDCC8" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "⌣": { "codepoints": [8995], "characters": "\u2323" }, + "⋆": { "codepoints": [8902], "characters": "\u22C6" }, + "☆": { "codepoints": [9734], "characters": "\u2606" }, + "★": { "codepoints": [9733], "characters": "\u2605" }, + "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, + "ϕ": { "codepoints": [981], "characters": "\u03D5" }, + "¯": { "codepoints": [175], "characters": "\u00AF" }, + "⊂": { "codepoints": [8834], "characters": "\u2282" }, + "⫅": { "codepoints": [10949], "characters": "\u2AC5" }, + "⪽": { "codepoints": [10941], "characters": "\u2ABD" }, + "⊆": { "codepoints": [8838], "characters": "\u2286" }, + "⫃": { "codepoints": [10947], "characters": "\u2AC3" }, + "⫁": { "codepoints": [10945], "characters": "\u2AC1" }, + "⫋": { "codepoints": [10955], "characters": "\u2ACB" }, + "⊊": { "codepoints": [8842], "characters": "\u228A" }, + "⪿": { "codepoints": [10943], "characters": "\u2ABF" }, + "⥹": { "codepoints": [10617], "characters": "\u2979" }, + "⊂": { "codepoints": [8834], "characters": "\u2282" }, + "⊆": { "codepoints": [8838], "characters": "\u2286" }, + "⫅": { "codepoints": [10949], "characters": "\u2AC5" }, + "⊊": { "codepoints": [8842], "characters": "\u228A" }, + "⫋": { "codepoints": [10955], "characters": "\u2ACB" }, + "⫇": { "codepoints": [10951], "characters": "\u2AC7" }, + "⫕": { "codepoints": [10965], "characters": "\u2AD5" }, + "⫓": { "codepoints": [10963], "characters": "\u2AD3" }, + "≻": { "codepoints": [8827], "characters": "\u227B" }, + "⪸": { "codepoints": [10936], "characters": "\u2AB8" }, + "≽": { "codepoints": [8829], "characters": "\u227D" }, + "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, + "⪺": { "codepoints": [10938], "characters": "\u2ABA" }, + "⪶": { "codepoints": [10934], "characters": "\u2AB6" }, + "⋩": { "codepoints": [8937], "characters": "\u22E9" }, + "≿": { "codepoints": [8831], "characters": "\u227F" }, + "∑": { "codepoints": [8721], "characters": "\u2211" }, + "♪": { "codepoints": [9834], "characters": "\u266A" }, + "¹": { "codepoints": [185], "characters": "\u00B9" }, + "¹": { "codepoints": [185], "characters": "\u00B9" }, + "²": { "codepoints": [178], "characters": "\u00B2" }, + "²": { "codepoints": [178], "characters": "\u00B2" }, + "³": { "codepoints": [179], "characters": "\u00B3" }, + "³": { "codepoints": [179], "characters": "\u00B3" }, + "⊃": { "codepoints": [8835], "characters": "\u2283" }, + "⫆": { "codepoints": [10950], "characters": "\u2AC6" }, + "⪾": { "codepoints": [10942], "characters": "\u2ABE" }, + "⫘": { "codepoints": [10968], "characters": "\u2AD8" }, + "⊇": { "codepoints": [8839], "characters": "\u2287" }, + "⫄": { "codepoints": [10948], "characters": "\u2AC4" }, + "⟉": { "codepoints": [10185], "characters": "\u27C9" }, + "⫗": { "codepoints": [10967], "characters": "\u2AD7" }, + "⥻": { "codepoints": [10619], "characters": "\u297B" }, + "⫂": { "codepoints": [10946], "characters": "\u2AC2" }, + "⫌": { "codepoints": [10956], "characters": "\u2ACC" }, + "⊋": { "codepoints": [8843], "characters": "\u228B" }, + "⫀": { "codepoints": [10944], "characters": "\u2AC0" }, + "⊃": { "codepoints": [8835], "characters": "\u2283" }, + "⊇": { "codepoints": [8839], "characters": "\u2287" }, + "⫆": { "codepoints": [10950], "characters": "\u2AC6" }, + "⊋": { "codepoints": [8843], "characters": "\u228B" }, + "⫌": { "codepoints": [10956], "characters": "\u2ACC" }, + "⫈": { "codepoints": [10952], "characters": "\u2AC8" }, + "⫔": { "codepoints": [10964], "characters": "\u2AD4" }, + "⫖": { "codepoints": [10966], "characters": "\u2AD6" }, + "⇙": { "codepoints": [8665], "characters": "\u21D9" }, + "⤦": { "codepoints": [10534], "characters": "\u2926" }, + "↙": { "codepoints": [8601], "characters": "\u2199" }, + "↙": { "codepoints": [8601], "characters": "\u2199" }, + "⤪": { "codepoints": [10538], "characters": "\u292A" }, + "ß": { "codepoints": [223], "characters": "\u00DF" }, + "ß": { "codepoints": [223], "characters": "\u00DF" }, + "⌖": { "codepoints": [8982], "characters": "\u2316" }, + "τ": { "codepoints": [964], "characters": "\u03C4" }, + "⎴": { "codepoints": [9140], "characters": "\u23B4" }, + "ť": { "codepoints": [357], "characters": "\u0165" }, + "ţ": { "codepoints": [355], "characters": "\u0163" }, + "т": { "codepoints": [1090], "characters": "\u0442" }, + "⃛": { "codepoints": [8411], "characters": "\u20DB" }, + "⌕": { "codepoints": [8981], "characters": "\u2315" }, + "𝔱": { "codepoints": [120113], "characters": "\uD835\uDD31" }, + "∴": { "codepoints": [8756], "characters": "\u2234" }, + "∴": { "codepoints": [8756], "characters": "\u2234" }, + "θ": { "codepoints": [952], "characters": "\u03B8" }, + "ϑ": { "codepoints": [977], "characters": "\u03D1" }, + "ϑ": { "codepoints": [977], "characters": "\u03D1" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "∼": { "codepoints": [8764], "characters": "\u223C" }, + " ": { "codepoints": [8201], "characters": "\u2009" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "∼": { "codepoints": [8764], "characters": "\u223C" }, + "þ": { "codepoints": [254], "characters": "\u00FE" }, + "þ": { "codepoints": [254], "characters": "\u00FE" }, + "˜": { "codepoints": [732], "characters": "\u02DC" }, + "×": { "codepoints": [215], "characters": "\u00D7" }, + "×": { "codepoints": [215], "characters": "\u00D7" }, + "⊠": { "codepoints": [8864], "characters": "\u22A0" }, + "⨱": { "codepoints": [10801], "characters": "\u2A31" }, + "⨰": { "codepoints": [10800], "characters": "\u2A30" }, + "∭": { "codepoints": [8749], "characters": "\u222D" }, + "⤨": { "codepoints": [10536], "characters": "\u2928" }, + "⊤": { "codepoints": [8868], "characters": "\u22A4" }, + "⌶": { "codepoints": [9014], "characters": "\u2336" }, + "⫱": { "codepoints": [10993], "characters": "\u2AF1" }, + "𝕥": { "codepoints": [120165], "characters": "\uD835\uDD65" }, + "⫚": { "codepoints": [10970], "characters": "\u2ADA" }, + "⤩": { "codepoints": [10537], "characters": "\u2929" }, + "‴": { "codepoints": [8244], "characters": "\u2034" }, + "™": { "codepoints": [8482], "characters": "\u2122" }, + "▵": { "codepoints": [9653], "characters": "\u25B5" }, + "▿": { "codepoints": [9663], "characters": "\u25BF" }, + "◃": { "codepoints": [9667], "characters": "\u25C3" }, + "⊴": { "codepoints": [8884], "characters": "\u22B4" }, + "≜": { "codepoints": [8796], "characters": "\u225C" }, + "▹": { "codepoints": [9657], "characters": "\u25B9" }, + "⊵": { "codepoints": [8885], "characters": "\u22B5" }, + "◬": { "codepoints": [9708], "characters": "\u25EC" }, + "≜": { "codepoints": [8796], "characters": "\u225C" }, + "⨺": { "codepoints": [10810], "characters": "\u2A3A" }, + "⨹": { "codepoints": [10809], "characters": "\u2A39" }, + "⧍": { "codepoints": [10701], "characters": "\u29CD" }, + "⨻": { "codepoints": [10811], "characters": "\u2A3B" }, + "⏢": { "codepoints": [9186], "characters": "\u23E2" }, + "𝓉": { "codepoints": [120009], "characters": "\uD835\uDCC9" }, + "ц": { "codepoints": [1094], "characters": "\u0446" }, + "ћ": { "codepoints": [1115], "characters": "\u045B" }, + "ŧ": { "codepoints": [359], "characters": "\u0167" }, + "≬": { "codepoints": [8812], "characters": "\u226C" }, + "↞": { "codepoints": [8606], "characters": "\u219E" }, + "↠": { "codepoints": [8608], "characters": "\u21A0" }, + "⇑": { "codepoints": [8657], "characters": "\u21D1" }, + "⥣": { "codepoints": [10595], "characters": "\u2963" }, + "ú": { "codepoints": [250], "characters": "\u00FA" }, + "ú": { "codepoints": [250], "characters": "\u00FA" }, + "↑": { "codepoints": [8593], "characters": "\u2191" }, + "ў": { "codepoints": [1118], "characters": "\u045E" }, + "ŭ": { "codepoints": [365], "characters": "\u016D" }, + "û": { "codepoints": [251], "characters": "\u00FB" }, + "û": { "codepoints": [251], "characters": "\u00FB" }, + "у": { "codepoints": [1091], "characters": "\u0443" }, + "⇅": { "codepoints": [8645], "characters": "\u21C5" }, + "ű": { "codepoints": [369], "characters": "\u0171" }, + "⥮": { "codepoints": [10606], "characters": "\u296E" }, + "⥾": { "codepoints": [10622], "characters": "\u297E" }, + "𝔲": { "codepoints": [120114], "characters": "\uD835\uDD32" }, + "ù": { "codepoints": [249], "characters": "\u00F9" }, + "ù": { "codepoints": [249], "characters": "\u00F9" }, + "↿": { "codepoints": [8639], "characters": "\u21BF" }, + "↾": { "codepoints": [8638], "characters": "\u21BE" }, + "▀": { "codepoints": [9600], "characters": "\u2580" }, + "⌜": { "codepoints": [8988], "characters": "\u231C" }, + "⌜": { "codepoints": [8988], "characters": "\u231C" }, + "⌏": { "codepoints": [8975], "characters": "\u230F" }, + "◸": { "codepoints": [9720], "characters": "\u25F8" }, + "ū": { "codepoints": [363], "characters": "\u016B" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "ų": { "codepoints": [371], "characters": "\u0173" }, + "𝕦": { "codepoints": [120166], "characters": "\uD835\uDD66" }, + "↑": { "codepoints": [8593], "characters": "\u2191" }, + "↕": { "codepoints": [8597], "characters": "\u2195" }, + "↿": { "codepoints": [8639], "characters": "\u21BF" }, + "↾": { "codepoints": [8638], "characters": "\u21BE" }, + "⊎": { "codepoints": [8846], "characters": "\u228E" }, + "υ": { "codepoints": [965], "characters": "\u03C5" }, + "ϒ": { "codepoints": [978], "characters": "\u03D2" }, + "υ": { "codepoints": [965], "characters": "\u03C5" }, + "⇈": { "codepoints": [8648], "characters": "\u21C8" }, + "⌝": { "codepoints": [8989], "characters": "\u231D" }, + "⌝": { "codepoints": [8989], "characters": "\u231D" }, + "⌎": { "codepoints": [8974], "characters": "\u230E" }, + "ů": { "codepoints": [367], "characters": "\u016F" }, + "◹": { "codepoints": [9721], "characters": "\u25F9" }, + "𝓊": { "codepoints": [120010], "characters": "\uD835\uDCCA" }, + "⋰": { "codepoints": [8944], "characters": "\u22F0" }, + "ũ": { "codepoints": [361], "characters": "\u0169" }, + "▵": { "codepoints": [9653], "characters": "\u25B5" }, + "▴": { "codepoints": [9652], "characters": "\u25B4" }, + "⇈": { "codepoints": [8648], "characters": "\u21C8" }, + "ü": { "codepoints": [252], "characters": "\u00FC" }, + "ü": { "codepoints": [252], "characters": "\u00FC" }, + "⦧": { "codepoints": [10663], "characters": "\u29A7" }, + "⇕": { "codepoints": [8661], "characters": "\u21D5" }, + "⫨": { "codepoints": [10984], "characters": "\u2AE8" }, + "⫩": { "codepoints": [10985], "characters": "\u2AE9" }, + "⊨": { "codepoints": [8872], "characters": "\u22A8" }, + "⦜": { "codepoints": [10652], "characters": "\u299C" }, + "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, + "ϰ": { "codepoints": [1008], "characters": "\u03F0" }, + "∅": { "codepoints": [8709], "characters": "\u2205" }, + "ϕ": { "codepoints": [981], "characters": "\u03D5" }, + "ϖ": { "codepoints": [982], "characters": "\u03D6" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "↕": { "codepoints": [8597], "characters": "\u2195" }, + "ϱ": { "codepoints": [1009], "characters": "\u03F1" }, + "ς": { "codepoints": [962], "characters": "\u03C2" }, + "⊊︀": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" }, + "⫋︀": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" }, + "⊋︀": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" }, + "⫌︀": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" }, + "ϑ": { "codepoints": [977], "characters": "\u03D1" }, + "⊲": { "codepoints": [8882], "characters": "\u22B2" }, + "⊳": { "codepoints": [8883], "characters": "\u22B3" }, + "в": { "codepoints": [1074], "characters": "\u0432" }, + "⊢": { "codepoints": [8866], "characters": "\u22A2" }, + "∨": { "codepoints": [8744], "characters": "\u2228" }, + "⊻": { "codepoints": [8891], "characters": "\u22BB" }, + "≚": { "codepoints": [8794], "characters": "\u225A" }, + "⋮": { "codepoints": [8942], "characters": "\u22EE" }, + "|": { "codepoints": [124], "characters": "\u007C" }, + "|": { "codepoints": [124], "characters": "\u007C" }, + "𝔳": { "codepoints": [120115], "characters": "\uD835\uDD33" }, + "⊲": { "codepoints": [8882], "characters": "\u22B2" }, + "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, + "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, + "𝕧": { "codepoints": [120167], "characters": "\uD835\uDD67" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "⊳": { "codepoints": [8883], "characters": "\u22B3" }, + "𝓋": { "codepoints": [120011], "characters": "\uD835\uDCCB" }, + "⫋︀": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" }, + "⊊︀": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" }, + "⫌︀": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" }, + "⊋︀": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" }, + "⦚": { "codepoints": [10650], "characters": "\u299A" }, + "ŵ": { "codepoints": [373], "characters": "\u0175" }, + "⩟": { "codepoints": [10847], "characters": "\u2A5F" }, + "∧": { "codepoints": [8743], "characters": "\u2227" }, + "≙": { "codepoints": [8793], "characters": "\u2259" }, + "℘": { "codepoints": [8472], "characters": "\u2118" }, + "𝔴": { "codepoints": [120116], "characters": "\uD835\uDD34" }, + "𝕨": { "codepoints": [120168], "characters": "\uD835\uDD68" }, + "℘": { "codepoints": [8472], "characters": "\u2118" }, + "≀": { "codepoints": [8768], "characters": "\u2240" }, + "≀": { "codepoints": [8768], "characters": "\u2240" }, + "𝓌": { "codepoints": [120012], "characters": "\uD835\uDCCC" }, + "⋂": { "codepoints": [8898], "characters": "\u22C2" }, + "◯": { "codepoints": [9711], "characters": "\u25EF" }, + "⋃": { "codepoints": [8899], "characters": "\u22C3" }, + "▽": { "codepoints": [9661], "characters": "\u25BD" }, + "𝔵": { "codepoints": [120117], "characters": "\uD835\uDD35" }, + "⟺": { "codepoints": [10234], "characters": "\u27FA" }, + "⟷": { "codepoints": [10231], "characters": "\u27F7" }, + "ξ": { "codepoints": [958], "characters": "\u03BE" }, + "⟸": { "codepoints": [10232], "characters": "\u27F8" }, + "⟵": { "codepoints": [10229], "characters": "\u27F5" }, + "⟼": { "codepoints": [10236], "characters": "\u27FC" }, + "⋻": { "codepoints": [8955], "characters": "\u22FB" }, + "⨀": { "codepoints": [10752], "characters": "\u2A00" }, + "𝕩": { "codepoints": [120169], "characters": "\uD835\uDD69" }, + "⨁": { "codepoints": [10753], "characters": "\u2A01" }, + "⨂": { "codepoints": [10754], "characters": "\u2A02" }, + "⟹": { "codepoints": [10233], "characters": "\u27F9" }, + "⟶": { "codepoints": [10230], "characters": "\u27F6" }, + "𝓍": { "codepoints": [120013], "characters": "\uD835\uDCCD" }, + "⨆": { "codepoints": [10758], "characters": "\u2A06" }, + "⨄": { "codepoints": [10756], "characters": "\u2A04" }, + "△": { "codepoints": [9651], "characters": "\u25B3" }, + "⋁": { "codepoints": [8897], "characters": "\u22C1" }, + "⋀": { "codepoints": [8896], "characters": "\u22C0" }, + "ý": { "codepoints": [253], "characters": "\u00FD" }, + "ý": { "codepoints": [253], "characters": "\u00FD" }, + "я": { "codepoints": [1103], "characters": "\u044F" }, + "ŷ": { "codepoints": [375], "characters": "\u0177" }, + "ы": { "codepoints": [1099], "characters": "\u044B" }, + "¥": { "codepoints": [165], "characters": "\u00A5" }, + "¥": { "codepoints": [165], "characters": "\u00A5" }, + "𝔶": { "codepoints": [120118], "characters": "\uD835\uDD36" }, + "ї": { "codepoints": [1111], "characters": "\u0457" }, + "𝕪": { "codepoints": [120170], "characters": "\uD835\uDD6A" }, + "𝓎": { "codepoints": [120014], "characters": "\uD835\uDCCE" }, + "ю": { "codepoints": [1102], "characters": "\u044E" }, + "ÿ": { "codepoints": [255], "characters": "\u00FF" }, + "ÿ": { "codepoints": [255], "characters": "\u00FF" }, + "ź": { "codepoints": [378], "characters": "\u017A" }, + "ž": { "codepoints": [382], "characters": "\u017E" }, + "з": { "codepoints": [1079], "characters": "\u0437" }, + "ż": { "codepoints": [380], "characters": "\u017C" }, + "ℨ": { "codepoints": [8488], "characters": "\u2128" }, + "ζ": { "codepoints": [950], "characters": "\u03B6" }, + "𝔷": { "codepoints": [120119], "characters": "\uD835\uDD37" }, + "ж": { "codepoints": [1078], "characters": "\u0436" }, + "⇝": { "codepoints": [8669], "characters": "\u21DD" }, + "𝕫": { "codepoints": [120171], "characters": "\uD835\uDD6B" }, + "𝓏": { "codepoints": [120015], "characters": "\uD835\uDCCF" }, + "‍": { "codepoints": [8205], "characters": "\u200D" }, + "‌": { "codepoints": [8204], "characters": "\u200C" } +} diff --git a/src/yajlpp/yajlpp.cc b/src/yajlpp/yajlpp.cc index 47f5d907..fb8c4533 100644 --- a/src/yajlpp/yajlpp.cc +++ b/src/yajlpp/yajlpp.cc @@ -1139,6 +1139,7 @@ yajlpp_parse_context::get_snippet() const } } + content.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE)); return lnav::console::snippet::from(this->ypc_source, content) .with_line(line_number); } diff --git a/src/yajlpp/yajlpp_def.hh b/src/yajlpp/yajlpp_def.hh index df391dfb..cc6ca3f9 100644 --- a/src/yajlpp/yajlpp_def.hh +++ b/src/yajlpp/yajlpp_def.hh @@ -1260,6 +1260,13 @@ public: }); } + yajlpp_parser& with_ignore_unused(bool value) + { + this->yp_parse_context.with_ignore_unused(value); + + return *this; + } + Result> of( const string_fragment& json) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ca3ee7fe..d5f5df26 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,6 +20,11 @@ add_executable(test_auto_mem test_auto_mem.cc test_stubs.cc) target_link_libraries(test_auto_mem diag) add_test(NAME test_auto_mem COMMAND test_auto_mem) +add_executable(document.sections.tests document.sections.tests.cc test_stubs.cc) +target_include_directories(document.sections.tests PUBLIC ../src/third-party/doctest-root) +target_link_libraries(document.sections.tests diag) +add_test(NAME document.sections.tests COMMAND document.sections.tests) + add_executable(test_bookmarks test_bookmarks.cc test_stubs.cc) target_link_libraries(test_bookmarks diag) add_test(NAME test_bookmarks COMMAND test_bookmarks) diff --git a/test/Makefile.am b/test/Makefile.am index abc9d81a..0f29f242 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -101,6 +101,7 @@ LDADD = \ $(TEXT2C_OBJS) \ $(DUMMY_OBJS) \ $(top_builddir)/src/libdiag.a \ + $(top_builddir)/src/libdatascanner.a \ $(top_builddir)/src/formats/logfmt/liblogfmt.a \ $(top_builddir)/src/fmtlib/libcppfmt.a \ $(top_builddir)/src/pcrepp/libpcrepp.a \ diff --git a/test/document.sections.tests.cc b/test/document.sections.tests.cc new file mode 100644 index 00000000..bdb4f7a3 --- /dev/null +++ b/test/document.sections.tests.cc @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "doctest/doctest.h" +#include "document.sections.hh" + +TEST_CASE("lnav::document::sections::basics") +{ + static const std::string INPUT = R"( +{ + "msg": "Hello, World!", + "obj": { + "a": 1, + "b": "Two", + "c": 3.0 + }, + "arr": [1, 2, 3], + "arr2": [ + 456, + 789, + { + "def": 123, + "ghi": null, + "jkl": "other" + }, + { + "def": 456, + "ghi": null, + "jkl": "OTHER" + }, + { + "def": 789, + "ghi": null, + "jkl": "OtHeR" + } + ] +} +)"; + + auto meta = lnav::document::discover_structure(INPUT); + + meta.m_sections_tree.visit_all([](const auto& intv) { + auto ser = intv.value.match( + [](const std::string& name) { return name; }, + [](const size_t index) { return fmt::format("{}", index); }); + printf("interval %d:%d %s\n", intv.start, intv.stop, ser.c_str()); + }); + lnav::document::hier_node::depth_first( + meta.m_sections_root.get(), [](const auto* node) { + printf("node %p %d\n", node, node->hn_start); + for (const auto& pair : node->hn_named_children) { + printf(" child: %p %s\n", pair.second, pair.first.c_str()); + } + }); +} diff --git a/test/expected/expected.am b/test/expected/expected.am index 475159b5..8d4804a3 100644 --- a/test/expected/expected.am +++ b/test/expected/expected.am @@ -6,6 +6,200 @@ EXPECTED_FILES = \ $(srcdir)/%reldir%/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.out \ $(srcdir)/%reldir%/test_cli.sh_a1a09f890f4604309d0a81bbbec8e50fb7d5e887.err \ $(srcdir)/%reldir%/test_cli.sh_a1a09f890f4604309d0a81bbbec8e50fb7d5e887.out \ + $(srcdir)/%reldir%/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.err \ + $(srcdir)/%reldir%/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.out \ + $(srcdir)/%reldir%/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.err \ + $(srcdir)/%reldir%/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.out \ + $(srcdir)/%reldir%/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.err \ + $(srcdir)/%reldir%/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.out \ + $(srcdir)/%reldir%/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.err \ + $(srcdir)/%reldir%/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.out \ + $(srcdir)/%reldir%/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.err \ + $(srcdir)/%reldir%/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.out \ + $(srcdir)/%reldir%/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.err \ + $(srcdir)/%reldir%/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.out \ + $(srcdir)/%reldir%/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.err \ + $(srcdir)/%reldir%/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.out \ + $(srcdir)/%reldir%/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.err \ + $(srcdir)/%reldir%/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.out \ + $(srcdir)/%reldir%/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.err \ + $(srcdir)/%reldir%/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.out \ + $(srcdir)/%reldir%/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.err \ + $(srcdir)/%reldir%/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.out \ + $(srcdir)/%reldir%/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.err \ + $(srcdir)/%reldir%/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.out \ + $(srcdir)/%reldir%/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.err \ + $(srcdir)/%reldir%/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.out \ + $(srcdir)/%reldir%/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.err \ + $(srcdir)/%reldir%/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.out \ + $(srcdir)/%reldir%/test_cmds.sh_1ffb06d63469f0e6dae94af026f77d5b255386c2.err \ + $(srcdir)/%reldir%/test_cmds.sh_1ffb06d63469f0e6dae94af026f77d5b255386c2.out \ + $(srcdir)/%reldir%/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.err \ + $(srcdir)/%reldir%/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.out \ + $(srcdir)/%reldir%/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.err \ + $(srcdir)/%reldir%/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.out \ + $(srcdir)/%reldir%/test_cmds.sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.err \ + $(srcdir)/%reldir%/test_cmds.sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.out \ + $(srcdir)/%reldir%/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.err \ + $(srcdir)/%reldir%/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.out \ + $(srcdir)/%reldir%/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.err \ + $(srcdir)/%reldir%/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.out \ + $(srcdir)/%reldir%/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.err \ + $(srcdir)/%reldir%/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.out \ + $(srcdir)/%reldir%/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.err \ + $(srcdir)/%reldir%/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.out \ + $(srcdir)/%reldir%/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.err \ + $(srcdir)/%reldir%/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.out \ + $(srcdir)/%reldir%/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.err \ + $(srcdir)/%reldir%/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.out \ + $(srcdir)/%reldir%/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.err \ + $(srcdir)/%reldir%/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.out \ + $(srcdir)/%reldir%/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.err \ + $(srcdir)/%reldir%/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.out \ + $(srcdir)/%reldir%/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.err \ + $(srcdir)/%reldir%/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.out \ + $(srcdir)/%reldir%/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.err \ + $(srcdir)/%reldir%/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.out \ + $(srcdir)/%reldir%/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.err \ + $(srcdir)/%reldir%/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.out \ + $(srcdir)/%reldir%/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.err \ + $(srcdir)/%reldir%/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.out \ + $(srcdir)/%reldir%/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.err \ + $(srcdir)/%reldir%/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.out \ + $(srcdir)/%reldir%/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.err \ + $(srcdir)/%reldir%/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.out \ + $(srcdir)/%reldir%/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.err \ + $(srcdir)/%reldir%/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.out \ + $(srcdir)/%reldir%/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.err \ + $(srcdir)/%reldir%/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.out \ + $(srcdir)/%reldir%/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.err \ + $(srcdir)/%reldir%/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.out \ + $(srcdir)/%reldir%/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.err \ + $(srcdir)/%reldir%/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.out \ + $(srcdir)/%reldir%/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.err \ + $(srcdir)/%reldir%/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.out \ + $(srcdir)/%reldir%/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.err \ + $(srcdir)/%reldir%/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.out \ + $(srcdir)/%reldir%/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.err \ + $(srcdir)/%reldir%/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.out \ + $(srcdir)/%reldir%/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.err \ + $(srcdir)/%reldir%/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.out \ + $(srcdir)/%reldir%/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.err \ + $(srcdir)/%reldir%/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.out \ + $(srcdir)/%reldir%/test_cmds.sh_63e1925e9d7aadcdaa54a35a29711d7ecaad8d27.err \ + $(srcdir)/%reldir%/test_cmds.sh_63e1925e9d7aadcdaa54a35a29711d7ecaad8d27.out \ + $(srcdir)/%reldir%/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.err \ + $(srcdir)/%reldir%/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.out \ + $(srcdir)/%reldir%/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.err \ + $(srcdir)/%reldir%/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.out \ + $(srcdir)/%reldir%/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.err \ + $(srcdir)/%reldir%/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.out \ + $(srcdir)/%reldir%/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.err \ + $(srcdir)/%reldir%/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.out \ + $(srcdir)/%reldir%/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.err \ + $(srcdir)/%reldir%/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.out \ + $(srcdir)/%reldir%/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.err \ + $(srcdir)/%reldir%/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.out \ + $(srcdir)/%reldir%/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.err \ + $(srcdir)/%reldir%/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.out \ + $(srcdir)/%reldir%/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.err \ + $(srcdir)/%reldir%/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.out \ + $(srcdir)/%reldir%/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.err \ + $(srcdir)/%reldir%/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.out \ + $(srcdir)/%reldir%/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.err \ + $(srcdir)/%reldir%/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.out \ + $(srcdir)/%reldir%/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.err \ + $(srcdir)/%reldir%/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.out \ + $(srcdir)/%reldir%/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.err \ + $(srcdir)/%reldir%/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.out \ + $(srcdir)/%reldir%/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.err \ + $(srcdir)/%reldir%/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.out \ + $(srcdir)/%reldir%/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.err \ + $(srcdir)/%reldir%/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.out \ + $(srcdir)/%reldir%/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.err \ + $(srcdir)/%reldir%/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.out \ + $(srcdir)/%reldir%/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.err \ + $(srcdir)/%reldir%/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.out \ + $(srcdir)/%reldir%/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.err \ + $(srcdir)/%reldir%/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.out \ + $(srcdir)/%reldir%/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.err \ + $(srcdir)/%reldir%/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.out \ + $(srcdir)/%reldir%/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.err \ + $(srcdir)/%reldir%/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.out \ + $(srcdir)/%reldir%/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.err \ + $(srcdir)/%reldir%/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.out \ + $(srcdir)/%reldir%/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.err \ + $(srcdir)/%reldir%/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.out \ + $(srcdir)/%reldir%/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.err \ + $(srcdir)/%reldir%/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.out \ + $(srcdir)/%reldir%/test_cmds.sh_a2690827f648d15ee64d1bdc8638f0277f175b95.err \ + $(srcdir)/%reldir%/test_cmds.sh_a2690827f648d15ee64d1bdc8638f0277f175b95.out \ + $(srcdir)/%reldir%/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.err \ + $(srcdir)/%reldir%/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.out \ + $(srcdir)/%reldir%/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.err \ + $(srcdir)/%reldir%/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.out \ + $(srcdir)/%reldir%/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.err \ + $(srcdir)/%reldir%/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.out \ + $(srcdir)/%reldir%/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.err \ + $(srcdir)/%reldir%/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.out \ + $(srcdir)/%reldir%/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.err \ + $(srcdir)/%reldir%/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.out \ + $(srcdir)/%reldir%/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.err \ + $(srcdir)/%reldir%/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.out \ + $(srcdir)/%reldir%/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.err \ + $(srcdir)/%reldir%/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out \ + $(srcdir)/%reldir%/test_cmds.sh_b755a8b48c0f602f0270500b0117b76e11db546e.err \ + $(srcdir)/%reldir%/test_cmds.sh_b755a8b48c0f602f0270500b0117b76e11db546e.out \ + $(srcdir)/%reldir%/test_cmds.sh_b7fcd26c45c850c3d43ce25b1f610a311eb898c5.err \ + $(srcdir)/%reldir%/test_cmds.sh_b7fcd26c45c850c3d43ce25b1f610a311eb898c5.out \ + $(srcdir)/%reldir%/test_cmds.sh_b9f8bf53ec2736432eb048d94a391175eb4dc5bf.err \ + $(srcdir)/%reldir%/test_cmds.sh_b9f8bf53ec2736432eb048d94a391175eb4dc5bf.out \ + $(srcdir)/%reldir%/test_cmds.sh_bc60341827636715c14c562863da9733cbde7e68.err \ + $(srcdir)/%reldir%/test_cmds.sh_bc60341827636715c14c562863da9733cbde7e68.out \ + $(srcdir)/%reldir%/test_cmds.sh_be1d9628fc447b6f17121d9457ea1602afe8f3f3.err \ + $(srcdir)/%reldir%/test_cmds.sh_be1d9628fc447b6f17121d9457ea1602afe8f3f3.out \ + $(srcdir)/%reldir%/test_cmds.sh_be3b7c5874b5f4d86cc230bd2f9802c98909e148.err \ + $(srcdir)/%reldir%/test_cmds.sh_be3b7c5874b5f4d86cc230bd2f9802c98909e148.out \ + $(srcdir)/%reldir%/test_cmds.sh_c01e10f7cae8d36fa79ae03be887cb5477025f6d.err \ + $(srcdir)/%reldir%/test_cmds.sh_c01e10f7cae8d36fa79ae03be887cb5477025f6d.out \ + $(srcdir)/%reldir%/test_cmds.sh_c2b4431dd0cc36c6201d263b727b3305e8cda6b1.err \ + $(srcdir)/%reldir%/test_cmds.sh_c2b4431dd0cc36c6201d263b727b3305e8cda6b1.out \ + $(srcdir)/%reldir%/test_cmds.sh_c4777849c39a6c34dea5b0279cd7400692f1ab5f.err \ + $(srcdir)/%reldir%/test_cmds.sh_c4777849c39a6c34dea5b0279cd7400692f1ab5f.out \ + $(srcdir)/%reldir%/test_cmds.sh_c4a15771f7e1487bf73b2e9d1564ad8ecfd76c7e.err \ + $(srcdir)/%reldir%/test_cmds.sh_c4a15771f7e1487bf73b2e9d1564ad8ecfd76c7e.out \ + $(srcdir)/%reldir%/test_cmds.sh_c72aed622c19d493968e33f20d5dde3838a4258f.err \ + $(srcdir)/%reldir%/test_cmds.sh_c72aed622c19d493968e33f20d5dde3838a4258f.out \ + $(srcdir)/%reldir%/test_cmds.sh_c7fabc25374ff47c47931f63b1d697061b816a28.err \ + $(srcdir)/%reldir%/test_cmds.sh_c7fabc25374ff47c47931f63b1d697061b816a28.out \ + $(srcdir)/%reldir%/test_cmds.sh_ca66660c973f76a3c2a147c7f5035bcb4e8a8bbc.err \ + $(srcdir)/%reldir%/test_cmds.sh_ca66660c973f76a3c2a147c7f5035bcb4e8a8bbc.out \ + $(srcdir)/%reldir%/test_cmds.sh_ccd326da92d1cacda63501cd1a3077381a18e8f2.err \ + $(srcdir)/%reldir%/test_cmds.sh_ccd326da92d1cacda63501cd1a3077381a18e8f2.out \ + $(srcdir)/%reldir%/test_cmds.sh_d76d77ad95b9f120825417a6a8220c13df9541fc.err \ + $(srcdir)/%reldir%/test_cmds.sh_d76d77ad95b9f120825417a6a8220c13df9541fc.out \ + $(srcdir)/%reldir%/test_cmds.sh_d8eeef53a58bdeddbc1028d7c525413e3ca1c8df.err \ + $(srcdir)/%reldir%/test_cmds.sh_d8eeef53a58bdeddbc1028d7c525413e3ca1c8df.out \ + $(srcdir)/%reldir%/test_cmds.sh_dbdd62995fdefc8318053af05a32416eccfa79fc.err \ + $(srcdir)/%reldir%/test_cmds.sh_dbdd62995fdefc8318053af05a32416eccfa79fc.out \ + $(srcdir)/%reldir%/test_cmds.sh_dd41fbbcd71699314af232156d4155fbdf849131.err \ + $(srcdir)/%reldir%/test_cmds.sh_dd41fbbcd71699314af232156d4155fbdf849131.out \ + $(srcdir)/%reldir%/test_cmds.sh_e495cf059477e3f80c3241c6f8d5808b6f1d19c7.err \ + $(srcdir)/%reldir%/test_cmds.sh_e495cf059477e3f80c3241c6f8d5808b6f1d19c7.out \ + $(srcdir)/%reldir%/test_cmds.sh_e7e8244fac65bc51dbd5af31be476fe3b8776bfc.err \ + $(srcdir)/%reldir%/test_cmds.sh_e7e8244fac65bc51dbd5af31be476fe3b8776bfc.out \ + $(srcdir)/%reldir%/test_cmds.sh_e911aebcb2defb7471aa620c45a86cad449ad505.err \ + $(srcdir)/%reldir%/test_cmds.sh_e911aebcb2defb7471aa620c45a86cad449ad505.out \ + $(srcdir)/%reldir%/test_cmds.sh_eb22c3e94c536a1bfaeae0c40d271b5b4b08f4fc.err \ + $(srcdir)/%reldir%/test_cmds.sh_eb22c3e94c536a1bfaeae0c40d271b5b4b08f4fc.out \ + $(srcdir)/%reldir%/test_cmds.sh_ec2b28c6ea328e3ea56b13ab8ca3d9ee856a9dda.err \ + $(srcdir)/%reldir%/test_cmds.sh_ec2b28c6ea328e3ea56b13ab8ca3d9ee856a9dda.out \ + $(srcdir)/%reldir%/test_cmds.sh_ed5b73be0b991e0e8d6735e31df5b37c4286321b.err \ + $(srcdir)/%reldir%/test_cmds.sh_ed5b73be0b991e0e8d6735e31df5b37c4286321b.out \ + $(srcdir)/%reldir%/test_cmds.sh_f788d5f5932905d09ecbd581040ec5ce76459da5.err \ + $(srcdir)/%reldir%/test_cmds.sh_f788d5f5932905d09ecbd581040ec5ce76459da5.out \ + $(srcdir)/%reldir%/test_cmds.sh_ff6faebbde8586e04bfadba14a3d2bb4451784ad.err \ + $(srcdir)/%reldir%/test_cmds.sh_ff6faebbde8586e04bfadba14a3d2bb4451784ad.out \ $(srcdir)/%reldir%/test_config.sh_2765ea0d4c037b8c935840604edb0ae796c97a04.err \ $(srcdir)/%reldir%/test_config.sh_2765ea0d4c037b8c935840604edb0ae796c97a04.out \ $(srcdir)/%reldir%/test_config.sh_5105c29004e297521310ca0bd0fd560b01c2c549.err \ @@ -100,58 +294,6 @@ EXPECTED_FILES = \ $(srcdir)/%reldir%/test_pretty_print.sh_a6d9042e5e95f2a49194bd80c1eed154813ddf41.out \ $(srcdir)/%reldir%/test_pretty_print.sh_cd361eeca7e91bfab942b75d6c3422c7a456a111.err \ $(srcdir)/%reldir%/test_pretty_print.sh_cd361eeca7e91bfab942b75d6c3422c7a456a111.out \ - $(srcdir)/%reldir%/test_regex101.sh_0fa3663a45aca6a328cb728872af7ed7ee896f1c.err \ - $(srcdir)/%reldir%/test_regex101.sh_0fa3663a45aca6a328cb728872af7ed7ee896f1c.out \ - $(srcdir)/%reldir%/test_regex101.sh_182ae9244db314a953af2bee969726e381bc5a32.err \ - $(srcdir)/%reldir%/test_regex101.sh_182ae9244db314a953af2bee969726e381bc5a32.out \ - $(srcdir)/%reldir%/test_regex101.sh_2158f1f011ba8e1b152396c072790c076fdb8ce8.err \ - $(srcdir)/%reldir%/test_regex101.sh_2158f1f011ba8e1b152396c072790c076fdb8ce8.out \ - $(srcdir)/%reldir%/test_regex101.sh_281af24141680330791db7f7c5fa70833ce08a6b.err \ - $(srcdir)/%reldir%/test_regex101.sh_281af24141680330791db7f7c5fa70833ce08a6b.out \ - $(srcdir)/%reldir%/test_regex101.sh_35703b13990785632cca82123fb3883797959c0b.err \ - $(srcdir)/%reldir%/test_regex101.sh_35703b13990785632cca82123fb3883797959c0b.out \ - $(srcdir)/%reldir%/test_regex101.sh_366730cac50b4a09b7de4b84641791470b1cb9a3.err \ - $(srcdir)/%reldir%/test_regex101.sh_366730cac50b4a09b7de4b84641791470b1cb9a3.out \ - $(srcdir)/%reldir%/test_regex101.sh_3d18474a3e472fff6e23e0c41337ec9188fee591.err \ - $(srcdir)/%reldir%/test_regex101.sh_3d18474a3e472fff6e23e0c41337ec9188fee591.out \ - $(srcdir)/%reldir%/test_regex101.sh_442cc58676590a3604d5c2183f5fe0a75c98351a.err \ - $(srcdir)/%reldir%/test_regex101.sh_442cc58676590a3604d5c2183f5fe0a75c98351a.out \ - $(srcdir)/%reldir%/test_regex101.sh_566fd88d216a44bc1c6e23f2d6f2d0caf99d42f9.err \ - $(srcdir)/%reldir%/test_regex101.sh_566fd88d216a44bc1c6e23f2d6f2d0caf99d42f9.out \ - $(srcdir)/%reldir%/test_regex101.sh_5f2f7ecb6ab9cbec4b41385b91bd038906b8a7b2.err \ - $(srcdir)/%reldir%/test_regex101.sh_5f2f7ecb6ab9cbec4b41385b91bd038906b8a7b2.out \ - $(srcdir)/%reldir%/test_regex101.sh_629bde30483e0a6461076e9058f3a5eb81ae0425.err \ - $(srcdir)/%reldir%/test_regex101.sh_629bde30483e0a6461076e9058f3a5eb81ae0425.out \ - $(srcdir)/%reldir%/test_regex101.sh_630db454054cf92ec9bd0f4e3e83300047f583ff.err \ - $(srcdir)/%reldir%/test_regex101.sh_630db454054cf92ec9bd0f4e3e83300047f583ff.out \ - $(srcdir)/%reldir%/test_regex101.sh_771af6f3d29b8350542d5c6e98bdbf4c223cd531.err \ - $(srcdir)/%reldir%/test_regex101.sh_771af6f3d29b8350542d5c6e98bdbf4c223cd531.out \ - $(srcdir)/%reldir%/test_regex101.sh_7991a5b617867cf37c9f7baa85ffa425f7d455a2.err \ - $(srcdir)/%reldir%/test_regex101.sh_7991a5b617867cf37c9f7baa85ffa425f7d455a2.out \ - $(srcdir)/%reldir%/test_regex101.sh_79ee3f5fe71ccec97b2619d8c1f74ca97ffd2243.err \ - $(srcdir)/%reldir%/test_regex101.sh_79ee3f5fe71ccec97b2619d8c1f74ca97ffd2243.out \ - $(srcdir)/%reldir%/test_regex101.sh_7de76c174c58d67bf93e8f01d6d55ebb6a023f10.err \ - $(srcdir)/%reldir%/test_regex101.sh_7de76c174c58d67bf93e8f01d6d55ebb6a023f10.out \ - $(srcdir)/%reldir%/test_regex101.sh_8a43e6657d4f60e68d31eb8302542ca28e80d077.err \ - $(srcdir)/%reldir%/test_regex101.sh_8a43e6657d4f60e68d31eb8302542ca28e80d077.out \ - $(srcdir)/%reldir%/test_regex101.sh_8e93a3b6b941847c71409a297779fbb0a6666a51.err \ - $(srcdir)/%reldir%/test_regex101.sh_8e93a3b6b941847c71409a297779fbb0a6666a51.out \ - $(srcdir)/%reldir%/test_regex101.sh_95c56a9d146ec9a7c2196559d316f928b2ae6ae9.err \ - $(srcdir)/%reldir%/test_regex101.sh_95c56a9d146ec9a7c2196559d316f928b2ae6ae9.out \ - $(srcdir)/%reldir%/test_regex101.sh_9d101ee29c45cdb8c0f117ad736c9a5dd5da5839.err \ - $(srcdir)/%reldir%/test_regex101.sh_9d101ee29c45cdb8c0f117ad736c9a5dd5da5839.out \ - $(srcdir)/%reldir%/test_regex101.sh_c43e07df9b3068696fdc8759c7561135db981b38.err \ - $(srcdir)/%reldir%/test_regex101.sh_c43e07df9b3068696fdc8759c7561135db981b38.out \ - $(srcdir)/%reldir%/test_regex101.sh_cbd859487e4ea011cd6e0f0f114d70158bfd8b43.err \ - $(srcdir)/%reldir%/test_regex101.sh_cbd859487e4ea011cd6e0f0f114d70158bfd8b43.out \ - $(srcdir)/%reldir%/test_regex101.sh_cf6c0a9f0f04e24ce1fae7a0a434830b14447f83.err \ - $(srcdir)/%reldir%/test_regex101.sh_cf6c0a9f0f04e24ce1fae7a0a434830b14447f83.out \ - $(srcdir)/%reldir%/test_regex101.sh_d84597760285c3964b258726341e018f6cd49954.err \ - $(srcdir)/%reldir%/test_regex101.sh_d84597760285c3964b258726341e018f6cd49954.out \ - $(srcdir)/%reldir%/test_regex101.sh_f23e393dbf23d0d8e276e9b7610c7b74d79980f8.err \ - $(srcdir)/%reldir%/test_regex101.sh_f23e393dbf23d0d8e276e9b7610c7b74d79980f8.out \ - $(srcdir)/%reldir%/test_regex101.sh_fc41b6ee90cbf038620151f16d164b361acf82dd.err \ - $(srcdir)/%reldir%/test_regex101.sh_fc41b6ee90cbf038620151f16d164b361acf82dd.out \ $(srcdir)/%reldir%/test_sessions.sh_0300a1391c33b1c45ddfa90198a6bd0a5404a77f.err \ $(srcdir)/%reldir%/test_sessions.sh_0300a1391c33b1c45ddfa90198a6bd0a5404a77f.out \ $(srcdir)/%reldir%/test_sessions.sh_17b85654b929b2a8fc1705a170ced544783292fa.err \ @@ -194,8 +336,6 @@ EXPECTED_FILES = \ $(srcdir)/%reldir%/test_shlexer.sh_90961e6728e96d0a44535a6c9907cc990c10316c.out \ $(srcdir)/%reldir%/test_shlexer.sh_95c4e861804a5434900fdb4d67b149d1baa2edf4.err \ $(srcdir)/%reldir%/test_shlexer.sh_95c4e861804a5434900fdb4d67b149d1baa2edf4.out \ - $(srcdir)/%reldir%/test_shlexer.sh_d5abaa610808635cebb8031a686274d44fcae1b3.err \ - $(srcdir)/%reldir%/test_shlexer.sh_d5abaa610808635cebb8031a686274d44fcae1b3.out \ $(srcdir)/%reldir%/test_shlexer.sh_d7fe5f6b8fc9ba00539fad0fa0bfb08319d8b04b.err \ $(srcdir)/%reldir%/test_shlexer.sh_d7fe5f6b8fc9ba00539fad0fa0bfb08319d8b04b.out \ $(srcdir)/%reldir%/test_shlexer.sh_d9d46422a913e3a06ddbd262933ef5352c30e68f.err \ @@ -274,8 +414,6 @@ EXPECTED_FILES = \ $(srcdir)/%reldir%/test_sql_fs_func.sh_3ed11101a413e47c3dfe219557b7a6df04a64253.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_469380561dccd79c7249562067107c330838eaad.err \ $(srcdir)/%reldir%/test_sql_fs_func.sh_469380561dccd79c7249562067107c330838eaad.out \ - $(srcdir)/%reldir%/test_sql_fs_func.sh_549647b59b4fec38734fa58842c0e720c21af120.err \ - $(srcdir)/%reldir%/test_sql_fs_func.sh_549647b59b4fec38734fa58842c0e720c21af120.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_54b004f301907860d360434b37fd6c81fcc12f99.err \ $(srcdir)/%reldir%/test_sql_fs_func.sh_54b004f301907860d360434b37fd6c81fcc12f99.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_73df81c6889d1f06fb3f3b6bf30c6046b3f52c8b.err \ diff --git a/test/expected/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.err b/test/expected/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.err index fb2dc946..2b495a85 100644 --- a/test/expected/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.err +++ b/test/expected/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.err @@ -1,7 +1,7 @@ ✘ error: invalid value for “-c” option - --> arg - |  -c foo - |  ^ command type prefix is missing + --> command-line argument + |  -c foo  + |  ^ command type prefix is missing  = help: command arguments must start with one of the following symbols to denote the type of command: : - an lnav command (e.g. :goto 42) ; - an SQL statement (e.g. ;SELECT * FROM syslog_log) diff --git a/test/expected/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.err b/test/expected/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.err new file mode 100644 index 00000000..d604bc51 --- /dev/null +++ b/test/expected/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.err @@ -0,0 +1,6 @@ +✘ error: no log files loaded + --> command-option:2 + | :close  + = help: :close + ══════════════════════════════════════════════════════════════════════ + Close the top file in the view diff --git a/test/expected/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.out b/test/expected/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.err b/test/expected/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.out b/test/expected/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.out new file mode 100644 index 00000000..f8b9bd37 --- /dev/null +++ b/test/expected/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:21:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:21:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:21:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.err b/test/expected/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.out b/test/expected/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.out new file mode 100644 index 00000000..ccd015e3 --- /dev/null +++ b/test/expected/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.out @@ -0,0 +1,3 @@ +c1,c2 +1,"Hello +World!" diff --git a/test/expected/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.err b/test/expected/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.out b/test/expected/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.out new file mode 100644 index 00000000..a90f29fd --- /dev/null +++ b/test/expected/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.err b/test/expected/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.out b/test/expected/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.err b/test/expected/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.out b/test/expected/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.out new file mode 100644 index 00000000..d18c6b11 --- /dev/null +++ b/test/expected/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.out @@ -0,0 +1 @@ +How are you? diff --git a/test/expected/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.err b/test/expected/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.err new file mode 100644 index 00000000..7b3f55d6 --- /dev/null +++ b/test/expected/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.err @@ -0,0 +1,6 @@ +✘ error: filter expression failed with: unable to parse time slice value: bad -- Unrecognized input + --> command-option:1 + | :filter-expr timeslice(:log_time_msecs, 'bad') is not null + = help: :filter-expr expr + ══════════════════════════════════════════════════════════════════════ + Set the filter expression diff --git a/test/expected/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.out b/test/expected/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.err b/test/expected/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.out b/test/expected/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.err b/test/expected/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.out b/test/expected/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.out new file mode 100644 index 00000000..6ebd88be --- /dev/null +++ b/test/expected/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.out @@ -0,0 +1,2 @@ +Hello, World! +-07-20 22:59:30,221:ERROR:Goodbye, Bork! diff --git a/test/expected/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.err b/test/expected/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.out b/test/expected/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.out new file mode 100644 index 00000000..45e6c41a --- /dev/null +++ b/test/expected/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.out @@ -0,0 +1,3 @@ +-07-20 22:59:27,672:DEBUG:Hello, Bork! + How are you today? +-07-20 22:59:30,221:ERROR:Goodbye, Bork! diff --git a/test/expected/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.err b/test/expected/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.out b/test/expected/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.out new file mode 100644 index 00000000..6dfcc1cc --- /dev/null +++ b/test/expected/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.out @@ -0,0 +1,68 @@ +[ + { + "log_line": 0, + "log_part": null, + "log_time": "2009-07-20 22:59:26.000", + "log_idle_msecs": 0, + "log_level": "info", + "log_mark": 0, + "log_comment": null, + "log_tags": null, + "log_filters": null, + "c_ip": "192.168.202.254", + "cs_method": "GET", + "cs_referer": "-", + "cs_uri_query": null, + "cs_uri_stem": "/vmw/cgi/tramp", + "cs_user_agent": "gPXE/0.9.7", + "cs_username": "-", + "cs_version": "HTTP/1.0", + "sc_bytes": 134, + "sc_status": 200, + "cs_host": null + }, + { + "log_line": 1, + "log_part": null, + "log_time": "2009-07-20 22:59:29.000", + "log_idle_msecs": 3000, + "log_level": "error", + "log_mark": 0, + "log_comment": null, + "log_tags": null, + "log_filters": null, + "c_ip": "192.168.202.254", + "cs_method": "GET", + "cs_referer": "-", + "cs_uri_query": null, + "cs_uri_stem": "/vmw/vSphere/default/vmkboot.gz", + "cs_user_agent": "gPXE/0.9.7", + "cs_username": "-", + "cs_version": "HTTP/1.0", + "sc_bytes": 46210, + "sc_status": 404, + "cs_host": null + }, + { + "log_line": 2, + "log_part": null, + "log_time": "2009-07-20 22:59:29.000", + "log_idle_msecs": 0, + "log_level": "info", + "log_mark": 0, + "log_comment": null, + "log_tags": null, + "log_filters": null, + "c_ip": "192.168.202.254", + "cs_method": "GET", + "cs_referer": "-", + "cs_uri_query": null, + "cs_uri_stem": "/vmw/vSphere/default/vmkernel.gz", + "cs_user_agent": "gPXE/0.9.7", + "cs_username": "-", + "cs_version": "HTTP/1.0", + "sc_bytes": 78929, + "sc_status": 200, + "cs_host": null + } +] diff --git a/test/expected/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.err b/test/expected/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.err new file mode 100644 index 00000000..68ef36e9 --- /dev/null +++ b/test/expected/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.err @@ -0,0 +1,6 @@ +✘ error: highlight does not exist -- foobar + --> command-option:1 + | :clear-highlight foobar  + = help: :clear-highlight pattern + ══════════════════════════════════════════════════════════════════════ + Remove a previously set highlight regular expression diff --git a/test/expected/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.out b/test/expected/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.err b/test/expected/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.out b/test/expected/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.out new file mode 100644 index 00000000..a90f29fd --- /dev/null +++ b/test/expected/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_1ffb06d63469f0e6dae94af026f77d5b255386c2.err b/test/expected/test_cmds.sh_1ffb06d63469f0e6dae94af026f77d5b255386c2.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_1ffb06d63469f0e6dae94af026f77d5b255386c2.out b/test/expected/test_cmds.sh_1ffb06d63469f0e6dae94af026f77d5b255386c2.out new file mode 100644 index 00000000..a90f29fd --- /dev/null +++ b/test/expected/test_cmds.sh_1ffb06d63469f0e6dae94af026f77d5b255386c2.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.err b/test/expected/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.out b/test/expected/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.err b/test/expected/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.out b/test/expected/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.out new file mode 100644 index 00000000..83f15c52 --- /dev/null +++ b/test/expected/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.out @@ -0,0 +1,12 @@ +Dec 6 13:01:34 ubu-mac avahi-daemon[786]: Joining mDNS multicast group on interface virbr0.IPv4 with address 192.168.122.1. +Dec 6 13:01:34 ubu-mac avahi-daemon[786]: New relevant interface virbr0.IPv4 for mDNS. +Dec 6 13:01:34 ubu-mac avahi-daemon[786]: Registering new address record for 192.168.122.1 on virbr0.IPv4. +Dec 6 13:01:34 ubu-mac dnsmasq[1840]: started, version 2.68 cachesize 150 +Dec 6 13:01:34 ubu-mac dnsmasq[1840]: compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth +Dec 6 13:01:34 ubu-mac dnsmasq-dhcp[1840]: DHCP, IP range 192.168.122.2 -- 192.168.122.254, lease time 1h +Dec 6 13:01:34 ubu-mac dnsmasq-dhcp[1840]: DHCP, sockets bound exclusively to interface virbr0 +Dec 6 13:01:34 ubu-mac dnsmasq[1840]: reading /etc/resolv.conf +Dec 6 13:01:34 ubu-mac dnsmasq[1840]: using nameserver 192.168.1.1#53 +Dec 6 13:01:34 ubu-mac dnsmasq[1840]: read /etc/hosts - 5 addresses +Dec 6 13:01:34 ubu-mac dnsmasq[1840]: read /var/lib/libvirt/dnsmasq/default.addnhosts - 0 addresses +Dec 6 13:01:34 ubu-mac dnsmasq-dhcp[1840]: read /var/lib/libvirt/dnsmasq/default.hostsfile diff --git a/test/expected/test_cmds.sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.err b/test/expected/test_cmds.sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.out b/test/expected/test_cmds.sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.err b/test/expected/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.out b/test/expected/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.out new file mode 100644 index 00000000..721d34ff --- /dev/null +++ b/test/expected/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.out @@ -0,0 +1 @@ +Hello, $XYZ! diff --git a/test/expected/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.err b/test/expected/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.out b/test/expected/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.out new file mode 100644 index 00000000..8241dd29 --- /dev/null +++ b/test/expected/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.out @@ -0,0 +1 @@ +/ui/clock-format = "%a %b %d %H:%M:%S %Z" diff --git a/test/expected/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.err b/test/expected/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.out b/test/expected/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.out new file mode 100644 index 00000000..6ae2e705 --- /dev/null +++ b/test/expected/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.out @@ -0,0 +1,2 @@ +2009-07-20 22:59:27,672:DEBUG:Hello, World! + How are you today? diff --git a/test/expected/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.err b/test/expected/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.err new file mode 100644 index 00000000..41e26940 --- /dev/null +++ b/test/expected/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.err @@ -0,0 +1,6 @@ +✘ error: expecting file name or '-' to write to the terminal + --> command-option:1 + | :write-to  + = help: :write-to path + ══════════════════════════════════════════════════════════════════════ + Overwrite the given file with any marked lines in the current view diff --git a/test/expected/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.out b/test/expected/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.err b/test/expected/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.out b/test/expected/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.out new file mode 100644 index 00000000..8d4265fe --- /dev/null +++ b/test/expected/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.out @@ -0,0 +1,2 @@ +Hello, World! +Goodbye, World! diff --git a/test/expected/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.err b/test/expected/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.out b/test/expected/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.out new file mode 100644 index 00000000..7cb26de2 --- /dev/null +++ b/test/expected/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.out @@ -0,0 +1 @@ +2009-07-20 22:59:30,221:ERROR:Goodbye, World! diff --git a/test/expected/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.err b/test/expected/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.out b/test/expected/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.out new file mode 100644 index 00000000..7cb26de2 --- /dev/null +++ b/test/expected/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.out @@ -0,0 +1 @@ +2009-07-20 22:59:30,221:ERROR:Goodbye, World! diff --git a/test/expected/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.err b/test/expected/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.out b/test/expected/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.out new file mode 100644 index 00000000..232301d3 --- /dev/null +++ b/test/expected/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.out @@ -0,0 +1 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.err b/test/expected/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.out b/test/expected/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.err b/test/expected/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.out b/test/expected/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.out new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/test/expected/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.out @@ -0,0 +1 @@ +Hello, World! diff --git a/test/expected/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.err b/test/expected/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.out b/test/expected/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.out new file mode 100644 index 00000000..c5fa5ddb --- /dev/null +++ b/test/expected/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.out @@ -0,0 +1,3 @@ +/ui/clock-format = "%a %b %d %H:%M:%S %Z" +info: changed config option -- /ui/clock-format +/ui/clock-format = "abc" diff --git a/test/expected/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.err b/test/expected/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.err new file mode 100644 index 00000000..8ca02934 --- /dev/null +++ b/test/expected/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.err @@ -0,0 +1,6 @@ +✘ error: unrecognized token: "#" + --> command-option:1 + | :filter-expr :sc_bytes # ff  + = help: :filter-expr expr + ══════════════════════════════════════════════════════════════════════ + Set the filter expression diff --git a/test/expected/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.out b/test/expected/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.err b/test/expected/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.out b/test/expected/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.out new file mode 100644 index 00000000..2e600e41 --- /dev/null +++ b/test/expected/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET ⋮ HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET ⋮ HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET ⋮ HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.err b/test/expected/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.out b/test/expected/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.err b/test/expected/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.out b/test/expected/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.out new file mode 100644 index 00000000..32372ddd --- /dev/null +++ b/test/expected/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.out @@ -0,0 +1,2 @@ +2009-07-20 22:59:30,221:ERROR:Goodbye, World! +2009-07-20 22:59:30,221:ERROR:Goodbye, World! diff --git a/test/expected/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.err b/test/expected/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.out b/test/expected/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.out new file mode 100644 index 00000000..8d4265fe --- /dev/null +++ b/test/expected/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.out @@ -0,0 +1,2 @@ +Hello, World! +Goodbye, World! diff --git a/test/expected/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.err b/test/expected/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.out b/test/expected/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.out new file mode 100644 index 00000000..b1c7de06 --- /dev/null +++ b/test/expected/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.out @@ -0,0 +1,3 @@ +2009-07-20 22:59:27,672:DEBUG:Hello, World! + How are you today? +2009-07-20 22:59:30,221:ERROR:Goodbye, World! diff --git a/test/expected/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.err b/test/expected/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.err new file mode 100644 index 00000000..42512a1c --- /dev/null +++ b/test/expected/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.err @@ -0,0 +1,8 @@ +✘ error: cannot stat file: /non-existent -- No such file or directory + --> command-option:2 + | :open /non-existent  + = help: :open path1 [... pathN] + ══════════════════════════════════════════════════════════════════════ + Open the given file(s) in lnav. Opening files on machines + accessible via SSH can be done using the syntax: + [user@]host:/path/to/logs diff --git a/test/expected/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.out b/test/expected/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.err b/test/expected/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.out b/test/expected/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.out new file mode 100644 index 00000000..39589f4b --- /dev/null +++ b/test/expected/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.out @@ -0,0 +1,2 @@ +toplevel here 123 456 +nested here nested.lnav abc 789 diff --git a/test/expected/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.err b/test/expected/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.err new file mode 100644 index 00000000..a00dc37a --- /dev/null +++ b/test/expected/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.err @@ -0,0 +1,6 @@ +✘ error: unknown bookmark type: foobar + --> command-option:2 + | :next-mark foobar  + = help: :next-mark type1 [... typeN] + ══════════════════════════════════════════════════════════════════════ + Move to the next bookmark of the given type in the current view diff --git a/test/expected/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.out b/test/expected/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.err b/test/expected/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.out b/test/expected/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.err b/test/expected/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.out b/test/expected/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.err b/test/expected/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.out b/test/expected/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.out new file mode 100644 index 00000000..493283c0 --- /dev/null +++ b/test/expected/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.out @@ -0,0 +1 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_63e1925e9d7aadcdaa54a35a29711d7ecaad8d27.err b/test/expected/test_cmds.sh_63e1925e9d7aadcdaa54a35a29711d7ecaad8d27.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_63e1925e9d7aadcdaa54a35a29711d7ecaad8d27.out b/test/expected/test_cmds.sh_63e1925e9d7aadcdaa54a35a29711d7ecaad8d27.out new file mode 100644 index 00000000..a90f29fd --- /dev/null +++ b/test/expected/test_cmds.sh_63e1925e9d7aadcdaa54a35a29711d7ecaad8d27.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.err b/test/expected/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.out b/test/expected/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.out new file mode 100644 index 00000000..06749a81 --- /dev/null +++ b/test/expected/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.out @@ -0,0 +1,14 @@ +'' +{ + "foo bar" : null, + "array" : [ + 1, + 2, + 3 + ], + "obj" : { + "one" : 1, + "two" : true + } +} +'' diff --git a/test/expected/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.err b/test/expected/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.err new file mode 100644 index 00000000..b09d8644 --- /dev/null +++ b/test/expected/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.err @@ -0,0 +1,8 @@ +✘ error: expecting file name to open + --> command-option:1 + | :open  + = help: :open path1 [... pathN] + ══════════════════════════════════════════════════════════════════════ + Open the given file(s) in lnav. Opening files on machines + accessible via SSH can be done using the syntax: + [user@]host:/path/to/logs diff --git a/test/expected/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.out b/test/expected/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.err b/test/expected/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.err new file mode 100644 index 00000000..97997e61 --- /dev/null +++ b/test/expected/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.err @@ -0,0 +1,6 @@ +✘ error: invalid zoom level -- bad + --> command-option:1 + | :zoom-to bad  + = help: :zoom-to zoom-level + ══════════════════════════════════════════════════════════════════════ + Zoom the histogram view to the given level diff --git a/test/expected/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.out b/test/expected/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.err b/test/expected/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.out b/test/expected/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.out new file mode 100644 index 00000000..bccea868 --- /dev/null +++ b/test/expected/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.out @@ -0,0 +1 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.err b/test/expected/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.out b/test/expected/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.out new file mode 100644 index 00000000..a90f29fd --- /dev/null +++ b/test/expected/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.err b/test/expected/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.out b/test/expected/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.out new file mode 100644 index 00000000..7ad9d781 --- /dev/null +++ b/test/expected/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.out @@ -0,0 +1,5 @@ +┏━━┳━━━━━━━━━━━━━┓ +┃c1┃ c2 ┃ +┡━━╇━━━━━━━━━━━━━┩ +│ 1│Hello, World!│ +└━━┴━━━━━━━━━━━━━┘ diff --git a/test/expected/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.err b/test/expected/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.err new file mode 100644 index 00000000..0a5e05e7 --- /dev/null +++ b/test/expected/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.err @@ -0,0 +1,6 @@ +✘ error: expecting an SQL expression + --> command-option:1 + | :mark-expr  + = help: :mark-expr expr + ══════════════════════════════════════════════════════════════════════ + Set the bookmark expression diff --git a/test/expected/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.out b/test/expected/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.err b/test/expected/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.out b/test/expected/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.out new file mode 100644 index 00000000..67f60937 --- /dev/null +++ b/test/expected/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.out @@ -0,0 +1 @@ +Hello: Jules diff --git a/test/expected/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.err b/test/expected/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.out b/test/expected/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.out new file mode 100644 index 00000000..0c154aba --- /dev/null +++ b/test/expected/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.out @@ -0,0 +1,3 @@ +⋮ - - [20/Jul/2009:22:59:26 +0000] "GET ⋮ HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +⋮ - - [20/Jul/2009:22:59:29 +0000] "GET ⋮ HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +⋮ - - [20/Jul/2009:22:59:29 +0000] "GET ⋮ HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.err b/test/expected/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.out b/test/expected/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.out new file mode 100644 index 00000000..a90f29fd --- /dev/null +++ b/test/expected/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.err b/test/expected/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.out b/test/expected/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.out new file mode 100644 index 00000000..6be811e3 --- /dev/null +++ b/test/expected/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.out @@ -0,0 +1 @@ +1Hello, World! diff --git a/test/expected/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.err b/test/expected/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.out b/test/expected/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.out new file mode 100644 index 00000000..232301d3 --- /dev/null +++ b/test/expected/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.out @@ -0,0 +1 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.err b/test/expected/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.out b/test/expected/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.out new file mode 100644 index 00000000..493283c0 --- /dev/null +++ b/test/expected/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.out @@ -0,0 +1 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.err b/test/expected/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.err new file mode 100644 index 00000000..55a3d6c3 --- /dev/null +++ b/test/expected/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.err @@ -0,0 +1,6 @@ +✘ error: expecting a unix time value + --> command-option:1 + | :unix-time  + = help: :unix-time seconds + ══════════════════════════════════════════════════════════════════════ + Convert epoch time to a human-readable form diff --git a/test/expected/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.out b/test/expected/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.err b/test/expected/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.out b/test/expected/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.out new file mode 100644 index 00000000..330e0419 --- /dev/null +++ b/test/expected/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.out @@ -0,0 +1,37 @@ +2015-03-12T23:16:52.071:INFO:com.root:Response : + + + en-US + ipInfo + 198.51.100.253 + + true + true + nic1 + + 0.0.0.0 + True + 22 + dhcp + 198.51.100.110 + nic1 + + + fe80::214:f609:19f7:6bf1 + True + nic1 + False + False + + other + preferred + 64 + fe80::250:56ff:feaa:5abf + + + + up + 00:50:56:aa:5a:bf + nic1 + + diff --git a/test/expected/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.err b/test/expected/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.out b/test/expected/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.out new file mode 100644 index 00000000..3cc484d9 --- /dev/null +++ b/test/expected/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.out @@ -0,0 +1,2 @@ +info: hiding lines before 2009-07-20 22:59:29.000 +info: hiding lines before 2009-07-20 22:59:29.000 diff --git a/test/expected/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.err b/test/expected/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.out b/test/expected/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.out new file mode 100644 index 00000000..a90f29fd --- /dev/null +++ b/test/expected/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.out @@ -0,0 +1,3 @@ +192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.err b/test/expected/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.out b/test/expected/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.err b/test/expected/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.out b/test/expected/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.out new file mode 100644 index 00000000..ad2b37e2 --- /dev/null +++ b/test/expected/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.out @@ -0,0 +1 @@ +10.112.81.15 - - [15/Feb/2013:06:00:31 +0000] "-" 400 0 "-" "-" diff --git a/test/expected/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.err b/test/expected/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.out b/test/expected/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.out new file mode 100644 index 00000000..5957830e --- /dev/null +++ b/test/expected/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.out @@ -0,0 +1,4 @@ + Thu Nov 03 09:20:00 1 normal 2 errors 0 warnings 0 marks + Thu Nov 03 09:45:00 1 normal 0 errors 0 warnings 0 marks + Fri Feb 03 09:20:00 0 normal 1 errors 0 warnings 0 marks + Wed Jan 03 09:20:00 1 normal 0 errors 0 warnings 0 marks diff --git a/test/expected/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.err b/test/expected/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.out b/test/expected/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.out new file mode 100644 index 00000000..a9dd3d00 --- /dev/null +++ b/test/expected/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.out @@ -0,0 +1,2 @@ +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" +192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" diff --git a/test/expected/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.err b/test/expected/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.err new file mode 100644 index 00000000..f0302bfd --- /dev/null +++ b/test/expected/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.err @@ -0,0 +1,3 @@ +✘ error: unknown script -- nonexistent.lnav -- file not found + --> command-option:1 + | |nonexistent.lnav  diff --git a/test/expected/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.out b/test/expected/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_a2690827f648d15ee64d1bdc8638f0277f175b95.err b/test/expected/test_cmds.sh_a2690827f648d15ee64d1bdc8638f0277f175b95.err new file mode 100644 index 00000000..485656e1 --- /dev/null +++ b/test/expected/test_cmds.sh_a2690827f648d15ee64d1bdc8638f0277f175b95.err @@ -0,0 +1,6 @@ +✘ error: write-json-to -- unavailable in secure mode + --> command-option:2 + | :write-json-to -  + = help: :write-json-to path + ══════════════════════════════════════════════════════════════════════ + Write SQL results to the given file in JSON format diff --git a/test/expected/test_cmds.sh_a2690827f648d15ee64d1bdc8638f0277f175b95.out b/test/expected/test_cmds.sh_a2690827f648d15ee64d1bdc8638f0277f175b95.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.err b/test/expected/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.err new file mode 100644 index 00000000..1ffc9281 --- /dev/null +++ b/test/expected/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.err @@ -0,0 +1,6 @@ +✘ error: unknown configuration option -- /bad/option + --> command-option:1 + | :config /bad/option  + = help: :config option [value] + ══════════════════════════════════════════════════════════════════════ + Read or write a configuration option diff --git a/test/expected/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.out b/test/expected/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.err b/test/expected/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.out b/test/expected/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.out new file mode 100644 index 00000000..c3813a52 --- /dev/null +++ b/test/expected/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.out @@ -0,0 +1,10 @@ +Sep 13 03:12:04 Tim-Stacks-iMac kernel[0]: vm_compressor_record_warmup (9478314 - 9492476) +Sep 13 03:12:04 Tim-Stacks-iMac kernel[0]: AppleBCM5701Ethernet [en0]: 0 0 memWrInd fBJP_Wakeup_Timer +Sep 13 01:25:39 Tim-Stacks-iMac kernel[0]: AppleThunderboltNHIType2::waitForOk2Go2Sx - retries = 60000 +Sep 13 03:12:04 Tim-Stacks-iMac kernel[0]: hibernate_page_list_setall(preflight 0) start 0xffffff8428276000, 0xffffff8428336000 +Sep 13 03:12:58 Tim-Stacks-iMac kernel[0]: *** kernel exceeded 500 log message per second limit - remaining messages this second discarded *** +Sep 13 03:46:03 Tim-Stacks-iMac kernel[0]: IOThunderboltSwitch<0xffffff803f4b3000>(0x0)::listenerCallback - Thunderbolt HPD packet for route = 0x0 port = 11 unplug = 0 +Sep 13 03:46:03 Tim-Stacks-iMac kernel[0]: vm_compressor_flush - starting +Sep 13 03:46:03 Tim-Stacks-iMac kernel[0]: AppleBCM5701Ethernet [en0]: 0 0 memWrInd fBJP_Wakeup_Timer +Sep 13 03:13:16 Tim-Stacks-iMac kernel[0]: AppleThunderboltNHIType2::waitForOk2Go2Sx - retries = 60000 +Sep 13 03:46:03 Tim-Stacks-iMac kernel[0]: hibernate_page_list_setall(preflight 0) start 0xffffff838f1fc000, 0xffffff838f2bc000 diff --git a/test/expected/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.err b/test/expected/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.err new file mode 100644 index 00000000..d34140e1 --- /dev/null +++ b/test/expected/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.err @@ -0,0 +1,7 @@ +✘ error: filter limit reached, try combining filters with a pipe symbol (e.g. foo|bar) + --> command-option:32 + | :filter-out 32  + = help: :filter-out pattern + ══════════════════════════════════════════════════════════════════════ + Remove lines that match the given regular expression in the current + view diff --git a/test/expected/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.out b/test/expected/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.out new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.err b/test/expected/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.out b/test/expected/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.out new file mode 100644 index 00000000..80b5fb6f --- /dev/null +++ b/test/expected/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.out @@ -0,0 +1,12 @@ +{ + "foo bar": null, + "array": [ + 1, + 2, + 3 + ], + "obj": { + "one": 1, + "two": true + } +} diff --git a/test/expected/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.err b/test/expected/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.out b/test/expected/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.out new file mode 100644 index 00000000..a5568383 --- /dev/null +++ b/test/expected/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.out @@ -0,0 +1 @@ + Sat Nov 03 08:00:00 1 normal 0 errors 0 warnings 0 marks diff --git a/test/expected/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.err b/test/expected/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.out b/test/expected/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.out new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/test/expected/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.out @@ -0,0 +1 @@ +Hello, World! diff --git a/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.err b/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out b/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out new file mode 100644 index 00000000..dec48f3a --- /dev/null +++ b/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out @@ -0,0 +1,4057 @@ + +lnav + +A fancy log file viewer for the terminal. + +Overview + +The Logfile Navigator, lnav, is an enhanced log file viewer that takes +advantage of any semantic information that can be gleaned from the +files being viewed, such as timestamps and log levels. Using this +extra semantic information, lnav can do things like interleaving +messages from different files, generate histograms of messages over +time, and providing hotkeys for navigating through the file. It is +hoped that these features will allow the user to quickly and +efficiently zero in on problems. + +Opening Paths/URLs + +The main arguments to lnav are the files, directories, glob patterns, +or URLs to be viewed. If no arguments are given, the default syslog +file for your system will be opened. These arguments will be polled +periodically so that any new data or files will be automatically +loaded. If a previously loaded file is removed or replaced, it will be +closed and the replacement opened. + +Note: When opening SFTP URLs, if the password is not provided for the +host, the SSH agent can be used to do authentication. + +Options + +Lnav takes a list of files to view and/or you can use the flag +arguments to load well-known log files, such as the syslog log files. +The flag arguments are: + + •  -a  Load all of the most recent log file types. + •  -r  Recursively load files from the given directory + hierarchies. + •  -R  Load older rotated log files as well. + +When using the flag arguments, lnav will look for the files relative +to the current directory and its parent directories. In other words, +if you are working within a directory that has the well-known log +files, those will be preferred over any others. + +If you do not want the default syslog file to be loaded when no files +are specified, you can pass the  -N  flag. + +Any files given on the command-line are scanned to determine their log +file format and to create an index for each line in the file. You do +not have to manually specify the log file format. The currently +supported formats are: syslog, apache, strace, tcsh history, and +generic log files with timestamps. + +Lnav will also display data piped in on the standard input. The +following options are available when doing so: + + •  -t  Prepend timestamps to the lines of data being read + in on the standard input. + •  -w file  Write the contents of the standard input to + this file. + +To automatically execute queries or lnav commands after the files have +been loaded, you can use the following options: + + •  -c cmd  A command, query, or file to execute. The + first character determines the type of operation: a colon + ( : ) is used for the built-in commands; a semi-colon ( ; + ) for SQL queries; and a pipe symbol ( | ) for executing + a file containing other commands. For example, to open + the file "foo.log" and go to the tenth line in the file, + you can do: + + ▌lnav -c ':goto 10' foo.log  + + This option can be given multiple times to execute + multiple operations in sequence. + •  -f file  A file that contains commands, queries, or + files to execute. This option is a shortcut for  -c '|file' + . You can use a dash ( - ) to execute commands from the + standard input. + +To execute commands/queries without opening the interactive text UI, +you can pass the  -n  option. This combination of options allows you +to write scripts for processing logs with lnav. For example, to get a +list of IP addresses that dhclient has bound to in CSV format: + + ▌#! /usr/bin/lnav -nf  + ▌  + ▌# Usage: dhcp_ip.lnav /var/log/messages  + ▌# Only include lines that look like:  + ▌# Apr 29 00:31:56 example-centos5 dhclient: bound to 10.1.10.103 -- renewal in 9938 seconds.  + ▌  + ▌:filter-in dhclient: bound to  + ▌  + ▌# The log message parser will extract the IP address  + ▌# as col_0, so we select that and alias it to "dhcp_ip".  + ▌;SELECT DISTINCT col_0 AS dhcp_ip FROM logline;  + ▌  + ▌# Finally, write the results of the query to stdout.  + ▌:write-csv-to -  + +Display + +The main part of the display shows the log lines from the files +interleaved based on time-of-day. New lines are automatically loaded +as they are appended to the files and, if you are viewing the bottom +of the files, lnav will scroll down to display the new lines, much +like  tail -f . + +On color displays, the lines will be highlighted as follows: + + • Errors will be colored in red; + • warnings will be yellow; + • boundaries between days will be underlined; and + • various color highlights will be applied to: IP + addresses, SQL keywords, XML tags, file and line numbers + in Java backtraces, and quoted strings. + +To give you an idea of where you are spatially, the right side of the +display has a proportionally sized 'scroll bar' that indicates your +current position in the files. The scroll bar will also show areas of +the file where warnings or errors are detected by coloring the bar +yellow or red, respectively. Tick marks will also be added to the left +and right hand side of the bar, for search hits and bookmarks. + +A bar on the left side is color coded and broken up to indicate which +messages are from the same file. Pressing the left-arrow or  h  will +reveal the source file names for each message and pressing again will +show the full paths. + +When at the bottom of the log view, a summary line will be displayed +on the right-hand-side to give you some more information about your +logs, including: how long ago the last message was generated, the +number of log files, the error rate, and how much time the logs cover. +The error rate display shows the errors-per-minute over the last five +minutes. A bar chart is also overlaid on the "Error rate" label to +show the error rate over the past ten seconds. For example, if there +have not been many errors in the past five minutes and there is a +sudden spike, the bar chart will fill up completely. But, if there has +been a steady stream of errors, then the chart will only partially +fill based on the recent error frequency. + +Above and below the main body are status lines that display: + + • the current time; + • the name of the file the top line was pulled from; + • the log format for the top line; + • the current view; + • the line number for the top line in the display; + • the current search hit, the total number of hits, and + the search term; + +If the view supports filtering, there will be a status line showing +the following: + + • the number of enabled filters and the total number of + filters; + • the number of lines not displayed because of filtering. + +To edit the filters, you can press TAB to change the focus from the +main view to the filter editor. The editor allows you to create, +enable/disable, and delete filters easily. + +Finally, the last line on the display is where you can enter search +patterns and execute internal commands, such as converting a +unix-timestamp into a human-readable date. The command-line is +implemented using the readline library, so the usual set of keyboard +shortcuts are available. Most commands and searches also support +tab-completion. + +The body of the display is also used to display other content, such +as: the help file, histograms of the log messages over time, and SQL +results. The views are organized into a stack so that any time you +activate a new view with a key press or command, the new view is +pushed onto the stack. Pressing the same key again will pop the view +off of the stack and return you to the previous view. Note that you +can always use  q  to pop the top view off of the stack. + +Default Key Bindings + +Views + + Key(s) Action +═══════════════════════════════════════════════════════════ + ? View/leave this help message. + q Leave the current view or quit the program when in + the log file view. + Q Similar to  q , except it will try to sync the top + time between the current and former views. For + example, when leaving the spectrogram view with  Q + , the top time in that view will be matched to the + top time in the log view. + TAB Toggle focusing on the filter editor or the main + view. + a/A Restore the view that was previously popped with  q + / Q . The  A  hotkey will try to match the top + times between the two views. + X Close the current text file or log file. + +Spatial Navigation + + Key(s) Action +═══════════════════════════════════════════════════════════════ + g/Home Move to the top of the file. + G/End Move to the end of the file. If the view is + already at the end, it will move to the last line. + SPACE/PgDn Move down a page. + b/PgUp Move up a page. + j/↓ Move down a line. + k/↑ Move up a line. + h/← Move to the left. In the log view, moving left + will reveal the source log file names for each + line. Pressing again will reveal the full path. + l/→ Move to the right. + H/Shift ← Move to the left by a smaller increment. + L/Shift → Move to the right by a smaller increment. + e/E Move to the next/previous error. + w/W Move to the next/previous warning. + n/N Move to the next/previous search hit. When pressed + repeatedly within a short time, the view will move + at least a full page at a time instead of moving + to the next hit. + f/F Move to the next/previous file. In the log view, + this moves to the next line from a different file. + In the text view, this rotates the view to the + next file. + >/< Move horizontally to the next/previous search hit. + o/O Move forward/backward to the log message with a + matching 'operation ID' (opid) field. + u/U Move forward/backward through any user bookmarks + you have added using the 'm' key. This hotkey will + also jump to the start of any log partitions that + have been created with the 'partition-name' + command. + s/S Move to the next/previous "slow down" in the log + message rate. A slow down is detected by measuring + how quickly the message rate has changed over the + previous several messages. For example, if one + message is logged every second for five seconds + and then the last message arrives five seconds + later, the last message will be highlighted as a + slow down. + {/} Move to the previous/next location in history. + Whenever you jump to a new location in the view, + the location will be added to the history. The + history is not updated when using only the arrow + keys. + +Chronological Navigation + + Key(s) Action +══════════════════════════════════════════════════════════════════ + d/D Move forward/backward 24 hours from the current + position in the log file. + 1-6/Shift 1-6 Move to the next/previous n'th ten minute of the + hour. For example, '4' would move to the first log + line in the fortieth minute of the current hour in + the log. And, '6' would move to the next hour + boundary. + 7/8 Move to the previous/next minute. + 0/Shift 0 Move to the next/previous day boundary. + r/R Move forward/backward based on the relative time + that was last used with the 'goto' command. For + example, executing ':goto a minute later' will + move the log view forward a minute and then + pressing 'r' will move it forward a minute again. + Pressing 'R' will then move the view in the + opposite direction, so backwards a minute. + +Bookmarks + + Key(s) Action +═══════════════════════════════════════════════════════════ + m Mark/unmark the line at the top of the display. + The line will be highlighted with reverse video to + indicate that it is a user bookmark. You can use + the  u  hotkey to iterate through marks you have + added. + M Mark/unmark all the lines between the top of the + display and the last line marked/unmarked. + J Mark/unmark the next line after the previously + marked line. + K Like  J  except it toggles the mark on the + previous line. + c Copy the marked text to the X11 selection buffer + or OS X clipboard. + C Clear all marked lines. + +Display options + + Key(s) Action +══════════════════════════════════════════════════════════════════ + P Switch to/from the pretty-printed view of the log + or text files currently displayed. In this view, + structured data, such as XML, will be reformatted + to make it easier to read. + t Switch to/from the text file view. The text file + view is for any files that are not recognized as + log files. + = Pause/unpause loading of new file data. + Ctrl-L (Lo-fi mode) Exit screen-mode and write the + displayed log lines in plain text to the terminal + until a key is pressed. Useful for copying long + lines from the terminal without picking up any of + the extra decorations. + T Toggle the display of the "elapsed time" column + that shows the time elapsed since the beginning of + the logs or the offset from the previous bookmark. + Sharp changes in the message rate are highlighted + by coloring the separator between the time column + and the log message. A red highlight means the + message rate has slowed down and green means it + has sped up. You can use the "s/S" hotkeys to scan + through the slow downs. + i View/leave a histogram of the log messages over + time. The histogram counts the number of displayed + log lines for each bucket of time. The bars are + layed out horizontally with colored segments + representing the different log levels. You can use + the  z  hotkey to change the size of the time + buckets (e.g. ten minutes, one hour, one day). + I Switch between the log and histogram views while + keeping the time displayed at the top of each view + in sync. For example, if the top line in the log + view is "11:40", hitting  I  will switch to the + histogram view and scrolled to display "11:00" at + the top (if the zoom level is hours). + z/Shift Z Zoom in or out one step in the histogram view. + v Switch to/from the SQL result view. + V Switch between the log and SQL result views while + keeping the top line number in the log view in + sync with the log_line column in the SQL view. For + example, doing a query that selects for + "log_idle_msecs" and "log_line", you can move the + top of the SQL view to a line and hit 'V' to + switch to the log view and move to the line number + that was selected in the "log_line" column. If + there is no "log_line" column, lnav will find the + first column with a timestamp and move to + corresponding time in the log view. + TAB/Shift TAB In the SQL result view, cycle through the columns + that are graphed. Initially, all number values are + displayed in a stacked graph. Pressing TAB will + change the display to only graph the first column. + Repeatedly pressing TAB will cycle through the + columns until they are all graphed again. + p In the log view: enable or disable the display of + the fields that the log message parser knows about + or has discovered. This overlay is temporarily + enabled when the semicolon key (;) is pressed so + that it is easier to write queries. + In the DB view: enable or disable the display of + values in columns containing JSON-encoded values + in the top row. The overlay will display the + JSON-Pointer reference and value for all fields in + the JSON data. + CTRL-W Toggle word-wrapping. + CTRL-P Show/hide the data preview panel that may be + opened when entering commands or SQL queries. + CTRL-F Toggle the enabled/disabled state of all filters + in the current view. + x Toggle the hiding of log message fields. The + hidden fields will be replaced with three bullets + and highlighted in yellow. + F2 Toggle mouse support. + +Query + + Key(s) Action +════════════════════════════════════════════════════════════════════════ + /regexp Start a search for the given regular expression. + The search is live, so when there is a pause in + typing, the currently running search will be + canceled and a new one started. The first ten + lines that match the search will be displayed in + the preview window at the bottom of the view. + History is maintained for your searches so you can + rerun them easily. Words that are currently + displayed are also available for tab-completion, + so you can easily search for values without + needing to copy-and-paste the string. If there is + an error encountered while trying to interpret the + expression, the error will be displayed in red on + the status line. While the search is active, the + 'hits' field in the status line will be green, + when finished it will turn back to black. + : Execute an internal command. The commands are + listed below. History is also supported in this + context as well as tab-completion for commands and + some arguments. The result of the command replaces + the command you typed. + ; Execute an SQL query. Most supported log file + formats provide a sqlite virtual table backend + that can be used in queries. See the SQL section + below for more information. + |