From 0ea9dd881118a5051414d4f6e6afcb0f59451a75 Mon Sep 17 00:00:00 2001 From: Noorhteen Raja NJ Date: Tue, 18 Jan 2022 21:41:16 +0530 Subject: [PATCH] fix: xonfig web is not upto-date (#4606) * fix: xonfig web is not upto-date it should load data from backend * refactor: update elm-compile module * feat: update `xonfig web` * todo: * style: * tmp * feat: implements colors page * feat: implement prompts page * feat: implement xontribs page * refactor: remove elm from project * fix: qa errors * docs: * test: add test for xonfig.web * fix: lru-cache call * feat: add env variable for sys level config dir * refactor: add method to handle post * feat: implement updating prompts * feat: implement xontribs update page * style: * fix: tests failure * feat: add variables page * feat: add abbrevs,aliases pages * feat: run xonfig web in main process this way we can update the current session * style: optimize imports * docs: * refactor: write .xonshrc as the old code * refactor: split file write functions --- .github/workflows/elm.yml | 36 - ci/environment-elm.yml | 10 - news/webconfig.rst | 23 + tests/test_xonfig.py | 62 +- xonsh/environ.py | 16 +- xonsh/webconfig/elm-compile.xsh | 281 -- xonsh/webconfig/elm-src/App.elm | 228 -- xonsh/webconfig/elm-src/XonshData.elm | 129 - xonsh/webconfig/elm.json | 32 - xonsh/webconfig/file_writes.py | 69 + xonsh/webconfig/index.html | 35 +- xonsh/webconfig/js/app.min.js | 1 - xonsh/webconfig/js/xonsh_sticker.svg | 3827 +++++++++++++++++++++++++ xonsh/webconfig/main.py | 234 +- xonsh/webconfig/routes.py | 432 +++ xonsh/webconfig/tags.py | 141 + xonsh/webconfig/xonsh_data.py | 215 ++ xonsh/xonfig.py | 8 +- xonsh/xontribs.py | 8 +- 19 files changed, 4952 insertions(+), 835 deletions(-) delete mode 100644 .github/workflows/elm.yml delete mode 100644 ci/environment-elm.yml create mode 100644 news/webconfig.rst delete mode 100755 xonsh/webconfig/elm-compile.xsh delete mode 100644 xonsh/webconfig/elm-src/App.elm delete mode 100644 xonsh/webconfig/elm-src/XonshData.elm delete mode 100644 xonsh/webconfig/elm.json create mode 100644 xonsh/webconfig/file_writes.py delete mode 100644 xonsh/webconfig/js/app.min.js create mode 100644 xonsh/webconfig/js/xonsh_sticker.svg create mode 100644 xonsh/webconfig/routes.py create mode 100644 xonsh/webconfig/tags.py create mode 100644 xonsh/webconfig/xonsh_data.py diff --git a/.github/workflows/elm.yml b/.github/workflows/elm.yml deleted file mode 100644 index c10bc582f0..0000000000 --- a/.github/workflows/elm.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Check Elm - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - name: Python 3.7 Elm Check Ubuntu - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: ~/conda_pkgs_dir - key: elm-env-${{ hashFiles('ci/environment-elm.yml') }} - restore-keys: | - elm-env- - - name: Setup conda - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: elm-xonsh-test - environment-file: ci/environment-elm.yml - auto-update-conda: true - python-version: 3.7 - condarc-file: ci/condarc.yml - - shell: bash -l {0} - run: | - python -m pip install . --no-deps - pushd xonsh/webconfig - python -m xonsh elm-compile.xsh - popd diff --git a/ci/environment-elm.yml b/ci/environment-elm.yml deleted file mode 100644 index 3ee9690edd..0000000000 --- a/ci/environment-elm.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: elm-xonsh-test -channels: - - conda-forge - - defaults -dependencies: - - python=3.7 - - pygments - - elm - - uglify-js - - docutils diff --git a/news/webconfig.rst b/news/webconfig.rst new file mode 100644 index 0000000000..4f483900ad --- /dev/null +++ b/news/webconfig.rst @@ -0,0 +1,23 @@ +**Added:** + +* ``xonfig web`` can now update ``abbrevs/aliases/env-variables``. + +**Changed:** + +* ``xonfig web`` now shows latest xontribs available from ``xonsh.xontribs_meta`` + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/tests/test_xonfig.py b/tests/test_xonfig.py index 9605757d95..7df8bc54cd 100644 --- a/tests/test_xonfig.py +++ b/tests/test_xonfig.py @@ -10,9 +10,10 @@ import sys import json import pytest # noqa F401 - +import io from xonsh.tools import ON_WINDOWS from xonsh.xonfig import xonfig_main +from xonsh.webconfig import main as web_main def test_xonfg_help(capsys, xession): @@ -132,3 +133,62 @@ def mock_get_kernel_spec(*args, **kwargs): def test_xonfig_kernel_no_jupyter(capsys, xession): with pytest.raises(ImportError): rc = xonfig_main(["jupyter-kernel"]) # noqa F841 + + +@pytest.fixture +def request_factory(): + class MockSocket: + def getsockname(self): + return ("sockname",) + + def sendall(self, data): + self.data = data + + class MockRequest: + _sock = MockSocket() + + def __init__(self, path: str, method: str): + self._path = path + self.data = b"" + self.method = method.upper() + + def makefile(self, *args, **kwargs): + if args[0] == "rb": + return io.BytesIO(f"{self.method} {self._path} HTTP/1.0".encode()) + elif args[0] == "wb": + return io.BytesIO(b"") + else: + raise ValueError("Unknown file type to make", args, kwargs) + + def sendall(self, data): + self.data = data + + return MockRequest + + +@pytest.fixture +def get_req(request_factory): + from urllib import parse + + def factory(path, data: "dict[str, str]|None" = None): + if data: + path = path + "?" + parse.urlencode(data) + request = request_factory(path, "get") + handle = web_main.XonshConfigHTTPRequestHandler(request, (0, 0), None) + return request, handle, request.data.decode() + + return factory + + +class TestXonfigWeb: + def test_colors_get(self, get_req): + _, _, resp = get_req("/") + assert "Colors" in resp + + def test_xontribs_get(self, get_req): + _, _, resp = get_req("/xontribs") + assert "Xontribs" in resp + + def test_prompts_get(self, get_req): + _, _, resp = get_req("/prompts") + assert "Prompts" in resp diff --git a/xonsh/environ.py b/xonsh/environ.py index a0e2391cad..1cd1b5c2be 100644 --- a/xonsh/environ.py +++ b/xonsh/environ.py @@ -667,12 +667,6 @@ def default_completer_dirs(env): ] -@default_value -def xonfig_data_files(env): - """``['$XONSH_SYS_CONFIG_DIR/xonfig-data.json', '$XONSH_CONFIG_DIR/xonfig-data.json']``\n""" - return get_config_paths(env, "xonfig-data.json") - - @default_value def xonsh_append_newline(env): """Appends a newline if we are in interactive mode""" @@ -2156,6 +2150,11 @@ def get(self, key, default=None): else: return default + def get_stringified(self, key, default=None): + value = self.get(key, default) + detyper = self.get_detyper(key) + return detyper(value) + def rawkeys(self): """An iterator that returns all environment keys in their original form. This include string & compiled regular expression keys. @@ -2281,6 +2280,11 @@ def deregister(self, name): """ self._vars.pop(name) + def is_configurable(self, name): + if name not in self._vars: + return False + return self._vars[name].is_configurable + class InternalEnvironDict(ChainMap): """A dictionary which supports thread-local overrides. diff --git a/xonsh/webconfig/elm-compile.xsh b/xonsh/webconfig/elm-compile.xsh deleted file mode 100755 index 1ba30fa6f0..0000000000 --- a/xonsh/webconfig/elm-compile.xsh +++ /dev/null @@ -1,281 +0,0 @@ -#!/usr/bin/env xonsh -"""script for compiling elm source and dumping it to the js folder.""" -import os -import io -import tempfile -from pprint import pprint - -import pygments -from pygments.lexers import Python3Lexer -from pygments.formatters.html import HtmlFormatter - -from xonsh.tools import print_color, format_color -from xonsh.style_tools import partial_color_tokenize -from xonsh.color_tools import rgb_to_ints -from xonsh.pygments_cache import get_all_styles -from xonsh.pyghooks import XonshStyle, xonsh_style_proxy, XonshHtmlFormatter, Token, XonshLexer -from xonsh.prompt.base import PromptFormatter -from xonsh.xontribs_meta import get_xontribs, Xontrib - - -$RAISE_SUBPROC_ERROR = True -$XONSH_SHOW_TRACEBACK = False - -# -# helper funcs -# - -def escape(s): - return s.replace("\n", "").replace('"', '\\"') - - -def invert_color(orig): - r, g, b = rgb_to_ints(orig) - inverted = [255 - r, 255 - g, 255 - b] - new = [hex(n)[2:] for n in inverted] - new = [n if len(n) == 2 else '0' + n for n in new] - return ''.join(new) - - -def html_format(s, style="default"): - buf = io.StringIO() - proxy_style = xonsh_style_proxy(XonshStyle(style)) - # make sure we have a foreground color - fgcolor = proxy_style._styles[Token.Text][0] - if not fgcolor: - fgcolor = invert_color(proxy_style.background_color[1:].strip('#')) - # need to generate stream before creating formatter so that all tokens actually exist - if isinstance(s, str): - token_stream = partial_color_tokenize(s) - else: - token_stream = s - formatter = XonshHtmlFormatter( - wrapcode=True, - noclasses=True, - style=proxy_style, - prestyles="margin: 0em; padding: 0.5em 0.1em; color: #" + fgcolor, - cssstyles="border-style: solid; border-radius: 5px", - ) - formatter.format(token_stream, buf) - return buf.getvalue() - - -def rst_to_html(s): - template = "%(body)s" - with tempfile.NamedTemporaryFile('w+') as t, tempfile.NamedTemporaryFile('w+') as f: - t.write(template) - t.flush() - f.write(s) - f.flush() - html = $(rst2html5.py --template @(t.name) @(f.name)) - return html - - -# -# first, write out elm-src/XonshData.elm -# -XONSH_DATA_HEADER = """-- A collection of xonsh values for the web-ui --- This file has been auto-generated by elm-compile.xsh -module XonshData exposing (..) - -import List -import String - -""" - -# render prompts -PROMPTS = [ - ("default", '{env_name}{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}' - '{branch_color}{curr_branch: {}}{RESET} {BOLD_BLUE}' - '{prompt_end}{RESET} '), - ("debian chroot", '{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}{RESET}> '), - ("minimalist", '{BOLD_GREEN}{cwd_base}{RESET} ) '), - ("terlar", '{env_name}{BOLD_GREEN}{user}{RESET}@{hostname}:' - '{BOLD_GREEN}{cwd}{RESET}|{gitstatus}\\n{BOLD_INTENSE_RED}➤{RESET} '), - ("default with git status", '{env_name}{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}' - '{branch_color}{gitstatus: {}}{RESET} {BOLD_BLUE}' - '{prompt_end}{RESET} '), - ("robbyrussell", '{BOLD_INTENSE_RED}➜ {CYAN}{cwd_base} {gitstatus}{RESET} '), - ("just a dollar", "$ "), - ("simple pythonista", "{INTENSE_RED}{user}{RESET} at {INTENSE_PURPLE}{hostname}{RESET} " - "in {BOLD_GREEN}{cwd}{RESET}\\n↪ "), - ("informative", "[{localtime}] {YELLOW}{env_name} {BOLD_BLUE}{user}@{hostname} " - "{BOLD_GREEN}{cwd} {gitstatus}{RESET}\\n> "), - ("informative Version Control", "{YELLOW}{env_name} " - "{BOLD_GREEN}{cwd} {gitstatus}{RESET} {prompt_end} "), - ("classic", "{user}@{hostname} {BOLD_GREEN}{cwd}{RESET}> "), - ("classic with git status", "{gitstatus} {RESET}{user}@{hostname} {BOLD_GREEN}{cwd}{RESET}> "), - ("screen savvy", "{YELLOW}{user}@{PURPLE}{hostname}{BOLD_GREEN}{cwd}{RESET}> "), - ("sorin", "{CYAN}{cwd} {INTENSE_RED}❯{INTENSE_YELLOW}❯{INTENSE_GREEN}❯{RESET} "), - ("acidhub", "❰{INTENSE_GREEN}{user}{RESET}❙{YELLOW}{cwd}{RESET}{env_name}❱{gitstatus}≻ "), - ("nim", "{INTENSE_GREEN}┬─[{YELLOW}{user}{RESET}@{BLUE}{hostname}{RESET}:{cwd}" - "{INTENSE_GREEN}]─[{localtime}]─[{RESET}G:{INTENSE_GREEN}{curr_branch}=]" - "\\n{INTENSE_GREEN}╰─>{INTENSE_RED}{prompt_end}{RESET} "), -] - -prompt_header = """type alias PromptData = - { name : String - , value : String - , display : String - } - -prompts : List PromptData -prompts =""" - -def render_prompts(lines): - print_color("Rendering {GREEN}prompts{RESET}") - prompt_format = PromptFormatter() - fields = dict($PROMPT_FIELDS) - fields.update( - cwd="~/snail/stuff", - cwd_base="stuff", - user="lou", - hostname="carcolh", - env_name=fields['env_prefix'] + "env" + fields["env_postfix"], - curr_branch="branch", - gitstatus="{CYAN}branch|{BOLD_BLUE}+2{RESET}⚑7", - branch_color="{BOLD_INTENSE_RED}", - localtime="15:56:07", - ) - lines.append(prompt_header) - for i, (name, template) in enumerate(PROMPTS): - display = html_format(prompt_format(template, fields=fields)) - item = 'name = "' + name + '", ' - item += 'value = "' + escape(template) + '", ' - item += 'display = "' + escape(display) + '"' - pre = " [ " if i == 0 else " , " - lines.append(pre + "{ " + item + " }") - lines.append(" ]") - - -# render color data - -color_header = """ -type alias ColorData = - { name : String - , display : String - } - -colors : List ColorData -colors =""" - -def render_colors(lines): - print_color("Rendering {GREEN}color styles{RESET}") - source = ( - 'import sys\n' - 'echo "Welcome $USER on" @(sys.platform)\n\n' - 'def func(x=42):\n' - ' d = {"xonsh": True}\n' - ' return d.get("xonsh") and you\n\n' - '# This is a comment\n' - '![env | uniq | sort | grep PATH]\n' - ) - lexer = XonshLexer() - lexer.add_filter('tokenmerge') - token_stream = list(pygments.lex(source, lexer=lexer)) - token_stream = [(t, s.replace("\n", "\\n")) for t, s in token_stream] - lines.append(color_header) - styles = sorted(get_all_styles()) - styles.insert(0, styles.pop(styles.index("default"))) - for i, style in enumerate(styles): - display = html_format(token_stream, style=style) - item = 'name = "' + style + '", ' - item += 'display = "' + escape(display) + '"' - pre = " [ " if i == 0 else " , " - lines.append(pre + "{ " + item + " }") - lines.append(" ]") - - -# render xontrib data - -xontrib_header = """ -type alias XontribData = - { name : String - , url : String - , license : String - , description : String - } - -xontribs : List XontribData -xontribs =""" - - -def get_xontrib_item(xontrib_name: str, xontrib: Xontrib): - item = 'name = "' + xontrib_name + '", ' - item += 'url = "' + xontrib.url + '", ' - item += 'license = "' + (xontrib.package.license if xontrib.package else "") + '", ' - d = rst_to_html("".join(xontrib.description)).replace("\n", "\\n") - item += 'description = "' + escape(d) + '"' - return item - - -def render_xontribs(lines): - print_color("Rendering {GREEN}xontribs{RESET}") - lines.append(xontrib_header) - md = get_xontribs() - for i, xontrib_name in enumerate(md): - xontrib = md[xontrib_name] - item = get_xontrib_item(xontrib_name, xontrib) - pre = " [ " if i == 0 else " , " - lines.append(pre + "{ " + item + " }") - lines.append(" ]") - - -def write_xonsh_data(): - # write XonshData.elm - lines = [XONSH_DATA_HEADER] - render_prompts(lines) - render_colors(lines) - render_xontribs(lines) - src = "\n".join(lines) + "\n" - xdelm = os.path.join('elm-src', 'XonshData.elm') - with open(xdelm, 'w') as f: - f.write(src) - - -# -# now compile the sources -# -SOURCES = [ - 'App.elm', -] -with ${...}.swap(RAISE_SUBPROC_ERROR=False): - HAVE_UGLIFY = bool(!(which uglifyjs e>o)) - -UGLIFY_FLAGS = ('pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",' - 'pure_getters,keep_fargs=false,unsafe_comps,unsafe') - - -def compile(): - for source in SOURCES: - base = os.path.splitext(source.lower())[0] - src = os.path.join('elm-src', source) - js_target = os.path.join('js', base + '.js') - print_color('Compiling {YELLOW}' + src + '{RESET} -> {GREEN}' + - js_target + '{RESET}') - $XONSH_SHOW_TRACEBACK = False - try: - ![elm make --optimize --output @(js_target) @(src)] - except Exception: - import sys - sys.exit(1) - new_files = [js_target] - min_target = os.path.join('js', base + '.min.js') - if os.path.exists(min_target): - ![rm -v @(min_target)] - if HAVE_UGLIFY: - print_color('Minifying {YELLOW}' + js_target + '{RESET} -> {GREEN}' + - min_target + '{RESET}') - ![uglifyjs @(js_target) --compress @(UGLIFY_FLAGS) | - uglifyjs --mangle --output @(min_target)] - new_files.append(min_target) - ![ls -l @(new_files)] - - -def main(): - write_xonsh_data() - compile() - - -if __name__ == "__main__": - main() diff --git a/xonsh/webconfig/elm-src/App.elm b/xonsh/webconfig/elm-src/App.elm deleted file mode 100644 index ccac27e7e2..0000000000 --- a/xonsh/webconfig/elm-src/App.elm +++ /dev/null @@ -1,228 +0,0 @@ -import Browser - -import Html exposing (..) -import Html.Events exposing (onClick) -import Html.Attributes exposing (class, style, href) -import Html.Parser -import Html.Parser.Util -import Http -import Maybe exposing (withDefault) -import Set -import Set exposing (Set) -import List -import String -import Json.Decode -import Json.Decode as Decode -import Json.Encode as Encode -import Bootstrap.Tab as Tab -import Bootstrap.CDN as CDN -import Bootstrap.Card as Card -import Bootstrap.Text as Text -import Bootstrap.Grid as Grid -import Bootstrap.Grid.Row as Row -import Bootstrap.Card.Block as Block -import Bootstrap.Button as Button -import Bootstrap.ListGroup as ListGroup -import XonshData -import XonshData exposing (PromptData, ColorData, XontribData) - - --- example with animation, you can drop the subscription part when not using animations -type alias Model = - { tabState : Tab.State - , promptValue : PromptData - , colorValue : ColorData - , xontribs : (Set String) - --, response : Maybe PostResponse - , error : Maybe Http.Error - } - -init : ( Model, Cmd Msg ) -init = - ( { tabState = Tab.initialState - , promptValue = withDefault {name = "unknown", value = "$ ", display = ""} - (List.head XonshData.prompts) - , colorValue = withDefault {name = "unknown", display = ""} - (List.head XonshData.colors) - , xontribs = Set.empty - --, response = Nothing - , error = Nothing - } - , Cmd.none ) - -type Msg - = TabMsg Tab.State - | PromptSelect PromptData - | ColorSelect ColorData - | XontribAdded XontribData - | XontribRemoved XontribData - | SaveClicked - | Response (Result Http.Error ()) - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - TabMsg state -> - ( { model | tabState = state } - , Cmd.none - ) - PromptSelect value -> - ( { model | promptValue = value } - , Cmd.none - ) - ColorSelect value -> - ( { model | colorValue = value } - , Cmd.none - ) - XontribAdded value -> - ( { model | xontribs = Set.insert value.name model.xontribs } - , Cmd.none - ) - XontribRemoved value -> - ( { model | xontribs = Set.remove value.name model.xontribs } - , Cmd.none - ) - SaveClicked -> - -- ( { model | error = Nothing, response = Nothing } - ( { model | error = Nothing} - , saveSettings model - ) - Response (Ok response) -> - --( { model | error = Nothing, response = Just response }, Cmd.none ) - ( { model | error = Nothing }, Cmd.none ) - Response (Err error) -> - --( { model | error = Just error, response = Nothing }, Cmd.none ) - ( { model | error = Just error }, Cmd.none ) - -encodeModel : Model -> Encode.Value -encodeModel model = - Encode.object - [ ("prompt", Encode.string model.promptValue.value) - , ("colors", Encode.string model.colorValue.name) - , ("xontribs", Encode.set Encode.string model.xontribs) - ] - -saveSettings : Model -> Cmd Msg -saveSettings model = - Http.post - { url = "/save" - , body = Http.stringBody "application/json" (Encode.encode 0 (encodeModel model)) - , expect = Http.expectWhatever Response - } - --- VIEWS - -textHtml : String -> List (Html.Html msg) -textHtml t = - case Html.Parser.run t of - Ok nodes -> - Html.Parser.Util.toVirtualDom nodes - - Err _ -> - [] - -promptButton : PromptData -> (ListGroup.CustomItem Msg) -promptButton pd = - ListGroup.button - [ ListGroup.attrs [ onClick (PromptSelect pd) ] - , ListGroup.info - ] - [ text pd.name - , p [] [] - , span [] (textHtml pd.display) - ] - -colorButton : ColorData -> (ListGroup.CustomItem Msg) -colorButton cd = - ListGroup.button - [ ListGroup.attrs [ onClick (ColorSelect cd) ] - , ListGroup.info - ] - [ text cd.name - , p [] [] - , span [] (textHtml cd.display) - ] - - -centeredDeck : List (Card.Config msg) -> Html.Html msg -centeredDeck cards = - Html.div - [ class "card-deck justify-content-center" ] - (List.map Card.view cards) - -xontribCard : Model -> XontribData -> Card.Config Msg -xontribCard model xd = - Card.config [ Card.attrs - [ style "min-width" "20em" - , style "max-width" "20em" - , style "padding" "0.25em" - , style "margin" "0.5em" - ] ] - |> Card.headerH3 [] [ a [href xd.url] [ text xd.name ] ] - |> Card.block [] [ Block.text [] ( textHtml xd.description ) ] - |> Card.footer [] - [ if Set.member xd.name model.xontribs then - Button.button [ Button.danger - , Button.attrs [ onClick (XontribRemoved xd) ] - ] [ text "Remove" ] - else - Button.button [ Button.success - , Button.attrs [ onClick (XontribAdded xd) ] - ] [ text "Add" ] - ] - -view : Model -> Html Msg -view model = - div [style "padding" "0.75em 1.25em"] - [ Grid.container [] - [ Grid.row [] - [ Grid.col [] [ div [ style "text-align" "left"] [ h2 [] [text "xonsh"] ] ] - , Grid.col [] [ div [ style "text-align" "right"] - [ Button.button [ Button.success - , Button.attrs [ onClick SaveClicked ] - ] [ text "Save" ] - ] ] - ] - ] - , p [] [] - , Tab.config TabMsg - |> Tab.center - |> Tab.items - [ Tab.item - { id = "tabItemColors" - , link = Tab.link [] [ text "Colors" ] - , pane = Tab.pane [] - [ text ("Current Selection: " ++ model.colorValue.name) - , p [] [] - , div [style "padding" "0.75em 1.25em"] (textHtml model.colorValue.display) - , ListGroup.custom (List.map colorButton XonshData.colors) - ] - } - , Tab.item - { id = "tabItemPrompt" - , link = Tab.link [] [ text "Prompt" ] - , pane = Tab.pane [] - [ text ("Current Selection: " ++ model.promptValue.name) - , p [] [] - , div [style "padding" "0.75em 1.25em"] (textHtml model.promptValue.display) - , ListGroup.custom (List.map promptButton XonshData.prompts) - ] - } - , Tab.item - { id = "tabItemXontribs" - , link = Tab.link [] [ text "Xontribs" ] - , pane = Tab.pane [] [ centeredDeck (List.map (xontribCard model) XonshData.xontribs) ] - } - ] - |> Tab.view model.tabState - ] - - -main : Program Decode.Value Model Msg -main = - Browser.element - { init = \_ -> init - , view = view - , update = update - , subscriptions = \_ -> Sub.none - } diff --git a/xonsh/webconfig/elm-src/XonshData.elm b/xonsh/webconfig/elm-src/XonshData.elm deleted file mode 100644 index 643c79ea7e..0000000000 --- a/xonsh/webconfig/elm-src/XonshData.elm +++ /dev/null @@ -1,129 +0,0 @@ --- A collection of xonsh values for the web-ui --- This file has been auto-generated by elm-compile.xsh -module XonshData exposing (..) - -import List -import String - - -type alias PromptData = - { name : String - , value : String - , display : String - } - -prompts : List PromptData -prompts = - [ { name = "default", value = "{env_name}{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}{branch_color}{curr_branch: {}}{RESET} {BOLD_BLUE}{prompt_end}{RESET} ", display = "
(env) lou@carcolh ~/snail/stuff branch $ 
" } - , { name = "debian chroot", value = "{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}{RESET}> ", display = "
lou@carcolh ~/snail/stuff> 
" } - , { name = "minimalist", value = "{BOLD_GREEN}{cwd_base}{RESET} ) ", display = "
stuff ) 
" } - , { name = "terlar", value = "{env_name}{BOLD_GREEN}{user}{RESET}@{hostname}:{BOLD_GREEN}{cwd}{RESET}|{gitstatus}\n{BOLD_INTENSE_RED}➤{RESET} ", display = "
(env) lou@carcolh:~/snail/stuff|branch|+2⚑7\n 
" } - , { name = "default with git status", value = "{env_name}{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}{branch_color}{gitstatus: {}}{RESET} {BOLD_BLUE}{prompt_end}{RESET} ", display = "
(env) lou@carcolh ~/snail/stuff branch|+2⚑7 $ 
" } - , { name = "robbyrussell", value = "{BOLD_INTENSE_RED}➜ {CYAN}{cwd_base} {gitstatus}{RESET} ", display = "
stuff branch|+2⚑7 
" } - , { name = "just a dollar", value = "$ ", display = "
$ 
" } - , { name = "simple pythonista", value = "{INTENSE_RED}{user}{RESET} at {INTENSE_PURPLE}{hostname}{RESET} in {BOLD_GREEN}{cwd}{RESET}\n↪ ", display = "
lou at carcolh in ~/snail/stuff\n↪ 
" } - , { name = "informative", value = "[{localtime}] {YELLOW}{env_name} {BOLD_BLUE}{user}@{hostname} {BOLD_GREEN}{cwd} {gitstatus}{RESET}\n> ", display = "
[15:56:07] (env)  lou@carcolh ~/snail/stuff branch|+2⚑7\n> 
" } - , { name = "informative Version Control", value = "{YELLOW}{env_name} {BOLD_GREEN}{cwd} {gitstatus}{RESET} {prompt_end} ", display = "
(env)  ~/snail/stuff branch|+2⚑7 $ 
" } - , { name = "classic", value = "{user}@{hostname} {BOLD_GREEN}{cwd}{RESET}> ", display = "
lou@carcolh ~/snail/stuff> 
" } - , { name = "classic with git status", value = "{gitstatus} {RESET}{user}@{hostname} {BOLD_GREEN}{cwd}{RESET}> ", display = "
branch|+2⚑7 lou@carcolh ~/snail/stuff> 
" } - , { name = "screen savvy", value = "{YELLOW}{user}@{PURPLE}{hostname}{BOLD_GREEN}{cwd}{RESET}> ", display = "
lou@carcolh~/snail/stuff> 
" } - , { name = "sorin", value = "{CYAN}{cwd} {INTENSE_RED}❯{INTENSE_YELLOW}❯{INTENSE_GREEN}❯{RESET} ", display = "
~/snail/stuff  
" } - , { name = "acidhub", value = "❰{INTENSE_GREEN}{user}{RESET}❙{YELLOW}{cwd}{RESET}{env_name}❱{gitstatus}≻ ", display = "
lou~/snail/stuff(env) ❱branch|+2⚑7≻ 
" } - , { name = "nim", value = "{INTENSE_GREEN}┬─[{YELLOW}{user}{RESET}@{BLUE}{hostname}{RESET}:{cwd}{INTENSE_GREEN}]─[{localtime}]─[{RESET}G:{INTENSE_GREEN}{curr_branch}=]\n{INTENSE_GREEN}╰─>{INTENSE_RED}{prompt_end}{RESET} ", display = "
┬─[lou@carcolh:~/snail/stuff]─[15:56:07]─[G:branch=]\n╰─>$ 
" } - ] - -type alias ColorData = - { name : String - , display : String - } - -colors : List ColorData -colors = - [ { name = "default", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "abap", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "algol", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "algol_nu", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "arduino", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "autumn", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "borland", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "bw", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "colorful", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "emacs", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "friendly", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "fruity", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "igor", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "inkpot", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "lovelace", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "manni", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "monokai", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "murphy", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "native", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "paraiso-dark", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "paraiso-light", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "pastie", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "perldoc", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "rainbow_dash", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "rrt", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "sas", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "solarized-dark", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "solarized-light", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "stata", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "stata-dark", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "stata-light", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "tango", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "trac", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "vim", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "vs", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - , { name = "xcode", display = "
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
" } - ] - -type alias XontribData = - { name : String - , url : String - , license : String - , description : String - } - -xontribs : List XontribData -xontribs = - [ { name = "abbrevs", url = "http://xon.sh", license = "BSD 3-clause", description = "

Adds abbrevs dictionary to hold user-defined command abbreviations. The dictionary is searched as you type and the matching words are replaced at the command line by the corresponding dictionary contents once you hit 'Space' or 'Return' key. For instance a frequently used command such as git status can be abbreviated to gst as follows:

\n
$ xontrib load abbrevs\n$ abbrevs['gst'] = 'git status'\n$ gst # Once you hit <space> or <return>, 'gst' gets expanded to 'git status'.
" } - , { name = "apt_tabcomplete", url = "https://github.com/DangerOnTheRanger/xonsh-apt-tabcomplete", license = "BSD 2-clause", description = "

Adds tabcomplete functionality to apt-get/apt-cache inside of xonsh.

" } - , { name = "argcomplete", url = "https://github.com/anki-code/xontrib-argcomplete", license = "", description = "

Argcomplete support to tab completion of python and xonsh scripts in xonsh.

" } - , { name = "autojump", url = "https://github.com/wshanks/xontrib-autojump", license = "", description = "

autojump support for xonsh

" } - , { name = "autovox", url = "http://xon.sh", license = "BSD 3-clause", description = "

Manages automatic activation of virtual environments.

" } - , { name = "autoxsh", url = "https://github.com/Granitas/xonsh-autoxsh", license = "GPLv3", description = "

Adds automatic execution of xonsh script files called .autoxsh when enterting a directory with cd function

" } - , { name = "avox", url = "https://github.com/AstraLuma/xontrib-avox", license = "GPLv3", description = "

Automatic (de)activation of virtual environments as you cd around

" } - , { name = "bashisms", url = "http://xon.sh", license = "BSD 3-clause", description = "

Enables additional Bash-like syntax while at the command prompt. For example, the !! syntax for running the previous command is now usable. Note that these features are implemented as precommand events and these additions do not affect the xonsh language when run as script. That said, you might find them useful if you have strong muscle memory.

\n

Warning: This xontrib may modify user command line input to implement its behavior. To see the modifications as they are applied (in unified diffformat), please set $XONSH_DEBUG to 2 or higher.

\n

The xontrib also adds commands: alias, export, unset, set, shopt, complete.

" } - , { name = "base16_shell", url = "https://github.com/ErickTucto/xontrib-base16-shell", license = "", description = "

Change base16 shell themes

" } - , { name = "coreutils", url = "http://xon.sh", license = "BSD 3-clause", description = "

Additional core utilities that are implemented in xonsh. The current list includes:

\n
    \n
  • cat

  • \n
  • echo

  • \n
  • pwd

  • \n
  • tee

  • \n
  • tty

  • \n
  • yes

  • \n
\n

In many cases, these may have a lower performance overhead than the posix command line utility with the same name. This is because these tools avoid the need for a full subprocess call. Additionally, these tools are cross-platform.

" } - , { name = "direnv", url = "https://github.com/74th/xonsh-direnv", license = "MIT", description = "

Supports direnv.

" } - , { name = "hist_navigator", url = "https://github.com/jnoortheen/xontrib-hist-navigator", license = "", description = "

Move through directory history with nextd and prevd also with keybindings.

" } - , { name = "distributed", url = "http://xon.sh", license = "BSD 3-clause", description = "

The distributed parallel computing library hooks for xonsh. Importantly this provides a substitute 'dworker' command which enables distributed workers to have access to xonsh builtins.

\n

Furthermore, this xontrib adds a 'DSubmitter' context manager for executing a block remotely. Moreover, this also adds a convenience function 'dsubmit()' for creating DSubmitter and Executor instances at the same time. Thus users may submit distributed jobs with:

\n
with dsubmit('127.0.0.1:8786', rtn='x') as dsub:\n    x = $(echo I am elsewhere)\n\nres = dsub.future.result()\nprint(res)
\n

This is useful for long running or non-blocking jobs.

" } - , { name = "docker_tabcomplete", url = "https://github.com/xsteadfastx/xonsh-docker-tabcomplete", license = "MIT", description = "

Adds tabcomplete functionality to docker inside of xonsh.

" } - , { name = "free_cwd", url = "http://xon.sh", license = "BSD 3-clause", description = "

Windows only xontrib, to release the lock on the current directory whenever the prompt is shown. Enabling this will allow the other programs or Windows Explorer to delete or rename the current or parent directories. Internally, it is accomplished by temporarily resetting CWD to the root drive folder while waiting at the prompt. This only works with the prompt_toolkit backend and can cause cause issues if any extensions are enabled that hook the prompt and relies on os.getcwd()

" } - , { name = "fzf-widgets", url = "https://github.com/laloch/xontrib-fzf-widgets", license = "GPLv3", description = "

Adds some fzf widgets to your xonsh shell.

" } - , { name = "histcpy", url = "https://github.com/con-f-use/xontrib-histcpy", license = "GPLv3", description = "

Useful aliases and shortcuts for extracting links and textfrom command output history and putting them into the clipboard.

" } - , { name = "jedi", url = "http://xon.sh", license = "BSD 3-clause", description = "

Use Jedi as xonsh's python completer.

" } - , { name = "kitty", url = "https://github.com/scopatz/xontrib-kitty", license = "BSD-3-Clause", description = "

Xonsh hooks for the Kitty terminal emulator.

" } - , { name = "mpl", url = "http://xon.sh", license = "BSD 3-clause", description = "

Matplotlib hooks for xonsh, including the new 'mpl' alias that displays the current figure on the screen.

" } - , { name = "output_search", url = "https://github.com/anki-code/xontrib-output-search", license = "", description = "

Get identifiers, names, paths, URLs and words from the previous command output and use them for the next command.

" } - , { name = "onepath", url = "https://github.com/anki-code/xontrib-onepath", license = "", description = "

When you click to a file or folder in graphical OS they will be opened in associated app.The xontrib-onepath brings the same logic for the xonsh shell. Type the filename or pathwithout preceding command and an associated action will be executed. The actions are customizable.

" } - , { name = "pdb", url = "http://xon.sh", license = "BSD 3-clause", description = "

Simple built-in debugger. Runs pdb on reception of SIGUSR1 signal.

" } - , { name = "pipeliner", url = "https://github.com/anki-code/xontrib-pipeliner", license = "", description = "

Let your pipe lines flow thru the Python code in xonsh.

" } - , { name = "powerline", url = "https://github.com/santagada/xontrib-powerline", license = "MIT", description = "

Powerline for Xonsh shell

" } - , { name = "prompt_bar", url = "https://github.com/anki-code/xontrib-prompt-bar", license = "", description = "

An elegance bar style for prompt.

" } - , { name = "powerline-binding", url = "https://github.com/dyuri/xontrib-powerline-binding", license = "MIT", description = "

Uses powerline to render the xonsh prompt

" } - , { name = "prompt_ret_code", url = "http://xon.sh", license = "BSD 3-clause", description = "

Adds return code info to the prompt

" } - , { name = "prompt_vi_mode", url = "https://github.com/t184256/xontrib-prompt-vi-mode", license = "MIT", description = "

vi-mode status formatter for xonsh prompt

" } - , { name = "pyenv", url = "https://github.com/dyuri/xontrib-pyenv", license = "MIT", description = "

pyenv integration for xonsh.

" } - , { name = "readable-traceback", url = "https://github.com/6syun9/xontrib-readable-traceback", license = "MIT", description = "

Make traceback easier to see for xonsh.

" } - , { name = "schedule", url = "https://github.com/AstraLuma/xontrib-schedule", license = "MIT", description = "

Xonsh Task Scheduling

" } - , { name = "scrapy_tabcomplete", url = "https://github.com/Granitas/xonsh-scrapy-tabcomplete", license = "GPLv3", description = "

Adds tabcomplete functionality to scrapy inside of xonsh.

" } - , { name = "ssh_agent", url = "https://github.com/dyuri/xontrib-ssh-agent", license = "MIT", description = "

ssh-agent integration

" } - , { name = "vox", url = "http://xon.sh", license = "BSD 3-clause", description = "

Python virtual environment manager for xonsh.

" } - , { name = "vox_tabcomplete", url = "https://github.com/Granitosaurus/xonsh-vox-tabcomplete", license = "GPLv3", description = "

Adds tabcomplete functionality to vox inside of xonsh.

" } - , { name = "whole_word_jumping", url = "http://xon.sh", license = "BSD 3-clause", description = "

Jumping across whole words (non-whitespace) with Ctrl+Left/Right. Alt+Left/Right remains unmodified to jump over smaller word segments. Shift+Delete removes the whole word.

" } - , { name = "xo", url = "https://github.com/scopatz/xo", license = "WTFPL", description = "

Adds an 'xo' alias to run the exofrills text editor in the current Python interpreter session. This shaves off a bit of the startup time when running your favorite, minimal text editor.

" } - , { name = "xpg", url = "https://github.com/fengttt/xsh/tree/master/py", license = "Apache", description = "

Run/plot/explain sql query for PostgreSQL.

" } - , { name = "z", url = "https://github.com/AstraLuma/xontrib-z", license = "GPLv3", description = "

Tracks your most used directories, based on 'frecency'.

" } - ] diff --git a/xonsh/webconfig/elm.json b/xonsh/webconfig/elm.json deleted file mode 100644 index 982f5f2196..0000000000 --- a/xonsh/webconfig/elm.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "type": "application", - "source-directories": [ - "elm-src" - ], - "elm-version": "0.19.0", - "dependencies": { - "direct": { - "elm/browser": "1.0.1", - "elm/core": "1.0.2", - "elm/html": "1.0.0", - "elm/http": "2.0.0", - "elm/json": "1.1.2", - "elm/url": "1.0.0", - "hecrj/html-parser": "2.3.4", - "rundis/elm-bootstrap": "5.2.0" - }, - "indirect": { - "avh4/elm-color": "1.0.0", - "elm/bytes": "1.0.7", - "elm/file": "1.0.3", - "elm/parser": "1.1.0", - "elm/time": "1.0.0", - "elm/virtual-dom": "1.0.2", - "rtfeldman/elm-hex": "1.0.0" - } - }, - "test-dependencies": { - "direct": {}, - "indirect": {} - } -} \ No newline at end of file diff --git a/xonsh/webconfig/file_writes.py b/xonsh/webconfig/file_writes.py new file mode 100644 index 0000000000..1e4b4aec21 --- /dev/null +++ b/xonsh/webconfig/file_writes.py @@ -0,0 +1,69 @@ +"""functions to update rc files""" +import os +import typing as tp + +RENDERERS: tp.List[tp.Callable] = [] + + +def renderer(f): + """Adds decorated function to renderers list.""" + RENDERERS.append(f) + + +@renderer +def prompt(config): + prompt = config.get("prompt") + if prompt: + yield f"$PROMPT = {prompt!r}" + + +@renderer +def colors(config): + style = config.get("color") + if style: + yield f"$XONSH_COLOR_STYLE = {style!r}" + + +@renderer +def xontribs(config): + xtribs = config.get("xontribs") + if xtribs: + yield "xontrib load " + " ".join(xtribs) + + +def config_to_xonsh( + config, prefix="# XONSH WEBCONFIG START", suffix="# XONSH WEBCONFIG END" +): + """Turns config dict into xonsh code (str).""" + lines = [prefix] + for func in RENDERERS: + lines.extend(func(config)) + lines.append(suffix) + return "\n".join(lines) + + +def insert_into_xonshrc( + config, + xonshrc="~/.xonshrc", + prefix="# XONSH WEBCONFIG START", + suffix="# XONSH WEBCONFIG END", +): + """Places a config dict into the xonshrc.""" + # get current contents + fname = os.path.expanduser(xonshrc) + if os.path.isfile(fname): + with open(fname) as f: + s = f.read() + before, _, s = s.partition(prefix) + _, _, after = s.partition(suffix) + else: + before = after = "" + dname = os.path.dirname(fname) + if dname: + os.makedirs(dname, exist_ok=True) + # compute new values + new = config_to_xonsh(config, prefix=prefix, suffix=suffix) + # write out the file + with open(fname, "w", encoding="utf-8") as f: + f.write(before + new + after) + return fname diff --git a/xonsh/webconfig/index.html b/xonsh/webconfig/index.html index 13b3926712..af66f177f4 100644 --- a/xonsh/webconfig/index.html +++ b/xonsh/webconfig/index.html @@ -1,19 +1,32 @@ - - Xonsh Web Config - - - + + Xonsh Web Config + + + -
- + +
+ $body +
diff --git a/xonsh/webconfig/js/app.min.js b/xonsh/webconfig/js/app.min.js deleted file mode 100644 index a037a2d277..0000000000 --- a/xonsh/webconfig/js/app.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(a){"use strict";function n(a,n,e){return e.a=a,e.f=n,e}function l(e){return n(2,e,function(n){return function(a){return e(n,a)}})}function e(r){return n(3,r,function(e){return function(n){return function(a){return r(e,n,a)}}})}function r(t){return n(4,t,function(r){return function(e){return function(n){return function(a){return t(r,e,n,a)}}}})}function t(o){return n(5,o,function(t){return function(r){return function(e){return function(n){return function(a){return o(t,r,e,n,a)}}}}})}function o(s){return n(6,s,function(o){return function(t){return function(r){return function(e){return function(n){return function(a){return s(o,t,r,e,n,a)}}}}}})}function s(l){return n(7,l,function(s){return function(o){return function(t){return function(r){return function(e){return function(n){return function(a){return l(s,o,t,r,e,n,a)}}}}}}})}function c(c){return n(8,c,function(l){return function(s){return function(o){return function(t){return function(r){return function(e){return function(n){return function(a){return c(l,s,o,t,r,e,n,a)}}}}}}}})}function b(b){return n(9,b,function(c){return function(l){return function(s){return function(o){return function(t){return function(r){return function(e){return function(n){return function(a){return b(c,l,s,o,t,r,e,n,a)}}}}}}}}})}function i(a,n,e){return 2===a.a?a.f(n,e):a(n)(e)}function u(a,n,e,r){return 3===a.a?a.f(n,e,r):a(n)(e)(r)}function f(a,n,e,r,t){return 4===a.a?a.f(n,e,r,t):a(n)(e)(r)(t)}function p(a,n,e,r,t,o){return 5===a.a?a.f(n,e,r,t,o):a(n)(e)(r)(t)(o)}function d(a,n,e,r,t,o,s){return 6===a.a?a.f(n,e,r,t,o,s):a(n)(e)(r)(t)(o)(s)}function y(a,n,e,r,t,o,s,l){return 7===a.a?a.f(n,e,r,t,o,s,l):a(n)(e)(r)(t)(o)(s)(l)}var h=e(function(a,n,e){for(var r=Array(a),t=0;t"),n});function v(a){throw Error("https://github.com/elm/core/blob/1.0.0/hints/"+a+".md")}function w(a,n){for(var e,r=[],t=q(a,n,0,r);t&&(e=r.pop());t=q(e.a,e.b,0,r));return t}function q(a,n,e,r){if(100s)return t}var u=e.$;if(4===u){for(var f=e.k;4===f.$;)f=f.k;return a(n,f,r,t,o+1,s,n.elm_event_node_ref)}var d=e.e;var y=n.childNodes;for(var h=0;hs))return t;o=m}return t}(a,n,e,0,0,n.b,r)}function on(a,n,e,r){return 0===e.length?a:(tn(a,n,e,r),sn(a,e))}function sn(a,n){for(var e=0;er||r>57)if(65>r||r>70){if(r<97||102
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"default"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"abap"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"algol"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"algol_nu"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"arduino"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"autumn"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"borland"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"bw"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"colorful"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"emacs"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"friendly"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"fruity"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"igor"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"inkpot"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"lovelace"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"manni"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"monokai"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"murphy"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"native"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"paraiso-dark"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"paraiso-light"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"pastie"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"perldoc"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"rainbow_dash"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"rrt"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"sas"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"solarized-dark"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"solarized-light"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"stata"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"stata-dark"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"stata-light"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"tango"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"trac"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"vim"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"vs"},{W:'
import sys\necho "Welcome $USER on" @(sys.platform)\n\ndef func(x=42):\n    d = {"xonsh": True}\n    return d.get("xonsh") and you\n\n# This is a comment\n![env | uniq | sort | grep PATH]\n
',s:"xcode"}]),Vn=N([{W:'
(env) lou@carcolh ~/snail/stuff branch $ 
',s:"default",dz:"{env_name}{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}{branch_color}{curr_branch: {}}{RESET} {BOLD_BLUE}{prompt_end}{RESET} "},{W:'
lou@carcolh ~/snail/stuff> 
',s:"debian chroot",dz:"{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}{RESET}> "},{W:'
stuff ) 
',s:"minimalist",dz:"{BOLD_GREEN}{cwd_base}{RESET} ) "},{W:'
(env) lou@carcolh:~/snail/stuff|branch|+2⚑7\n 
',s:"terlar",dz:"{env_name}{BOLD_GREEN}{user}{RESET}@{hostname}:{BOLD_GREEN}{cwd}{RESET}|{gitstatus}\n{BOLD_INTENSE_RED}➤{RESET} "},{W:'
(env) lou@carcolh ~/snail/stuff branch|+2⚑7 $ 
',s:"default with git status",dz:"{env_name}{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}{branch_color}{gitstatus: {}}{RESET} {BOLD_BLUE}{prompt_end}{RESET} "},{W:'
stuff branch|+2⚑7 
',s:"robbyrussell",dz:"{BOLD_INTENSE_RED}➜ {CYAN}{cwd_base} {gitstatus}{RESET} "},{W:'
$ 
',s:"just a dollar",dz:"$ "},{W:'
lou at carcolh in ~/snail/stuff\n↪ 
',s:"simple pythonista",dz:"{INTENSE_RED}{user}{RESET} at {INTENSE_PURPLE}{hostname}{RESET} in {BOLD_GREEN}{cwd}{RESET}\n↪ "},{W:'
[15:56:07] (env)  lou@carcolh ~/snail/stuff branch|+2⚑7\n> 
',s:"informative",dz:"[{localtime}] {YELLOW}{env_name} {BOLD_BLUE}{user}@{hostname} {BOLD_GREEN}{cwd} {gitstatus}{RESET}\n> "},{W:'
(env)  ~/snail/stuff branch|+2⚑7 $ 
',s:"informative Version Control",dz:"{YELLOW}{env_name} {BOLD_GREEN}{cwd} {gitstatus}{RESET} {prompt_end} "},{W:'
lou@carcolh ~/snail/stuff> 
',s:"classic",dz:"{user}@{hostname} {BOLD_GREEN}{cwd}{RESET}> "},{W:'
branch|+2⚑7 lou@carcolh ~/snail/stuff> 
',s:"classic with git status",dz:"{gitstatus} {RESET}{user}@{hostname} {BOLD_GREEN}{cwd}{RESET}> "},{W:'
lou@carcolh~/snail/stuff> 
',s:"screen savvy",dz:"{YELLOW}{user}@{PURPLE}{hostname}{BOLD_GREEN}{cwd}{RESET}> "},{W:'
~/snail/stuff  
',s:"sorin",dz:"{CYAN}{cwd} {INTENSE_RED}❯{INTENSE_YELLOW}❯{INTENSE_GREEN}❯{RESET} "},{W:'
lou~/snail/stuff(env) ❱branch|+2⚑7≻ 
',s:"acidhub",dz:"❰{INTENSE_GREEN}{user}{RESET}❙{YELLOW}{cwd}{RESET}{env_name}❱{gitstatus}≻ "},{W:'
┬─[lou@carcolh:~/snail/stuff]─[15:56:07]─[G:branch=]\n╰─>$ 
',s:"nim",dz:"{INTENSE_GREEN}┬─[{YELLOW}{user}{RESET}@{BLUE}{hostname}{RESET}:{cwd}{INTENSE_GREEN}]─[{localtime}]─[{RESET}G:{INTENSE_GREEN}{curr_branch}=]\n{INTENSE_GREEN}╰─>{INTENSE_RED}{prompt_end}{RESET} "}]),zn=function(a){return{$:0,a:a}},Mn={$:1},Jn=l(function(a,n){return n.$?a:n.a}),Kn=function(a){return!a.$},Yn=r(function(a,n,e,r){return{$:0,a:a,b:n,c:e,d:r}}),Xn=U,Zn=l(function(a,n){return F(n)/F(a)}),Qn=Xn(i(Zn,2,32)),ae=[],ne=f(Yn,0,Qn,ae,ae),ee=g,re=e(function(a,n,e){for(;;){if(!e.b)return n;var r=e.b,t=a,o=i(a,e.a,n);a=t,n=o,e=r}}),te=function(a){return u(re,Pn,D,a)},oe=l(function(a,n){for(;;){var e=i(ee,32,a),r=e.b,e=i(Pn,{$:0,a:e.a},n);if(!r.b)return te(e);a=r,n=e}}),se=(l(function(a,n){return n(a)}),x),le=l(function(a,n){for(;;){var e=Xn(n/32);if(1===e)return i(ee,32,a).a;a=i(oe,a,D),n=e}}),ce=l(function(a,n){return a(n)}),be=I,pe=l(function(a,n){return 0<$(a,n)?a:n}),ie=function(a){return a.length},ue=l(function(a,n){if(n.f){var e=32*n.f,r=be(i(Zn,32,e-1)),a=a?te(n.i):n.i,a=i(le,a,n.f);return f(Yn,ie(n.h)+e,i(pe,5,r*Qn),a,n.h)}return f(Yn,ie(n.h),Qn,ae,n.h)}),fe=h,de=t(function(a,n,e,r,t){for(;;){if(n<0)return i(ue,!1,{i:r,f:e/32|0,h:t});var o={$:1,a:u(fe,32,n,a)};a=a,n=n-32,e=e,r=i(Pn,o,r),t=t}}),ye=l(function(a,n){if(0"!==a})));return i(Wt,i(Wt,i(Wt,i(Wt,St(se("<")),St(se("/"))),a),Rt(jt)),St(se(">")))},zt=l(function(a,n){return{$:0,a:a,b:n}}),Mt=yn,Jt=r(function(a,n,e,r){return i(xt,$t,f(Et,a,n,e,r))}),Kt=cn,E=function(a){return function(a){var t=a.a,o=a.b,s=!(""===t);return function(a){var n=p(Kt,t,a.b,a.dh,a.cF,a.a),e=n.a,r=n.b,n=n.c;return w(e,-1)?i(gt,!1,i(kt,a,o)):u(mt,s,0,{cF:n,d:a.d,e:a.e,b:e,dh:r,a:a.a})}}(Or(a))},Yt=i(Bt,i(Wt,i(Wt,Ft(function(a){return{$:2,a:a}}),E(""!==a&&"/"!==a&&"="!==a}))),yn=St(se(";")),so=(Jr=N([{a:"Aacute",b:"Á"},{a:"aacute",b:"á"},{a:"Abreve",b:"Ă"},{a:"abreve",b:"ă"},{a:"ac",b:"∾"},{a:"acd",b:"∿"},{a:"acE",b:"∾̳"},{a:"Acirc",b:"Â"},{a:"acirc",b:"â"},{a:"acute",b:"´"},{a:"Acy",b:"А"},{a:"acy",b:"а"},{a:"AElig",b:"Æ"},{a:"aelig",b:"æ"},{a:"af",b:"⁡"},{a:"Afr",b:"𝔄"},{a:"afr",b:"𝔞"},{a:"Agrave",b:"À"},{a:"agrave",b:"à"},{a:"alefsym",b:"ℵ"},{a:"aleph",b:"ℵ"},{a:"Alpha",b:"Α"},{a:"alpha",b:"α"},{a:"Amacr",b:"Ā"},{a:"amacr",b:"ā"},{a:"amalg",b:"⨿"},{a:"amp",b:"&"},{a:"AMP",b:"&"},{a:"andand",b:"⩕"},{a:"And",b:"⩓"},{a:"and",b:"∧"},{a:"andd",b:"⩜"},{a:"andslope",b:"⩘"},{a:"andv",b:"⩚"},{a:"ang",b:"∠"},{a:"ange",b:"⦤"},{a:"angle",b:"∠"},{a:"angmsdaa",b:"⦨"},{a:"angmsdab",b:"⦩"},{a:"angmsdac",b:"⦪"},{a:"angmsdad",b:"⦫"},{a:"angmsdae",b:"⦬"},{a:"angmsdaf",b:"⦭"},{a:"angmsdag",b:"⦮"},{a:"angmsdah",b:"⦯"},{a:"angmsd",b:"∡"},{a:"angrt",b:"∟"},{a:"angrtvb",b:"⊾"},{a:"angrtvbd",b:"⦝"},{a:"angsph",b:"∢"},{a:"angst",b:"Å"},{a:"angzarr",b:"⍼"},{a:"Aogon",b:"Ą"},{a:"aogon",b:"ą"},{a:"Aopf",b:"𝔸"},{a:"aopf",b:"𝕒"},{a:"apacir",b:"⩯"},{a:"ap",b:"≈"},{a:"apE",b:"⩰"},{a:"ape",b:"≊"},{a:"apid",b:"≋"},{a:"apos",b:"'"},{a:"ApplyFunction",b:"⁡"},{a:"approx",b:"≈"},{a:"approxeq",b:"≊"},{a:"Aring",b:"Å"},{a:"aring",b:"å"},{a:"Ascr",b:"𝒜"},{a:"ascr",b:"𝒶"},{a:"Assign",b:"≔"},{a:"ast",b:"*"},{a:"asymp",b:"≈"},{a:"asympeq",b:"≍"},{a:"Atilde",b:"Ã"},{a:"atilde",b:"ã"},{a:"Auml",b:"Ä"},{a:"auml",b:"ä"},{a:"awconint",b:"∳"},{a:"awint",b:"⨑"},{a:"backcong",b:"≌"},{a:"backepsilon",b:"϶"},{a:"backprime",b:"‵"},{a:"backsim",b:"∽"},{a:"backsimeq",b:"⋍"},{a:"Backslash",b:"∖"},{a:"Barv",b:"⫧"},{a:"barvee",b:"⊽"},{a:"barwed",b:"⌅"},{a:"Barwed",b:"⌆"},{a:"barwedge",b:"⌅"},{a:"bbrk",b:"⎵"},{a:"bbrktbrk",b:"⎶"},{a:"bcong",b:"≌"},{a:"Bcy",b:"Б"},{a:"bcy",b:"б"},{a:"bdquo",b:"„"},{a:"becaus",b:"∵"},{a:"because",b:"∵"},{a:"Because",b:"∵"},{a:"bemptyv",b:"⦰"},{a:"bepsi",b:"϶"},{a:"bernou",b:"ℬ"},{a:"Bernoullis",b:"ℬ"},{a:"Beta",b:"Β"},{a:"beta",b:"β"},{a:"beth",b:"ℶ"},{a:"between",b:"≬"},{a:"Bfr",b:"𝔅"},{a:"bfr",b:"𝔟"},{a:"bigcap",b:"⋂"},{a:"bigcirc",b:"◯"},{a:"bigcup",b:"⋃"},{a:"bigodot",b:"⨀"},{a:"bigoplus",b:"⨁"},{a:"bigotimes",b:"⨂"},{a:"bigsqcup",b:"⨆"},{a:"bigstar",b:"★"},{a:"bigtriangledown",b:"▽"},{a:"bigtriangleup",b:"△"},{a:"biguplus",b:"⨄"},{a:"bigvee",b:"⋁"},{a:"bigwedge",b:"⋀"},{a:"bkarow",b:"⤍"},{a:"blacklozenge",b:"⧫"},{a:"blacksquare",b:"▪"},{a:"blacktriangle",b:"▴"},{a:"blacktriangledown",b:"▾"},{a:"blacktriangleleft",b:"◂"},{a:"blacktriangleright",b:"▸"},{a:"blank",b:"␣"},{a:"blk12",b:"▒"},{a:"blk14",b:"░"},{a:"blk34",b:"▓"},{a:"block",b:"█"},{a:"bne",b:"=⃥"},{a:"bnequiv",b:"≡⃥"},{a:"bNot",b:"⫭"},{a:"bnot",b:"⌐"},{a:"Bopf",b:"𝔹"},{a:"bopf",b:"𝕓"},{a:"bot",b:"⊥"},{a:"bottom",b:"⊥"},{a:"bowtie",b:"⋈"},{a:"boxbox",b:"⧉"},{a:"boxdl",b:"┐"},{a:"boxdL",b:"╕"},{a:"boxDl",b:"╖"},{a:"boxDL",b:"╗"},{a:"boxdr",b:"┌"},{a:"boxdR",b:"╒"},{a:"boxDr",b:"╓"},{a:"boxDR",b:"╔"},{a:"boxh",b:"─"},{a:"boxH",b:"═"},{a:"boxhd",b:"┬"},{a:"boxHd",b:"╤"},{a:"boxhD",b:"╥"},{a:"boxHD",b:"╦"},{a:"boxhu",b:"┴"},{a:"boxHu",b:"╧"},{a:"boxhU",b:"╨"},{a:"boxHU",b:"╩"},{a:"boxminus",b:"⊟"},{a:"boxplus",b:"⊞"},{a:"boxtimes",b:"⊠"},{a:"boxul",b:"┘"},{a:"boxuL",b:"╛"},{a:"boxUl",b:"╜"},{a:"boxUL",b:"╝"},{a:"boxur",b:"└"},{a:"boxuR",b:"╘"},{a:"boxUr",b:"╙"},{a:"boxUR",b:"╚"},{a:"boxv",b:"│"},{a:"boxV",b:"║"},{a:"boxvh",b:"┼"},{a:"boxvH",b:"╪"},{a:"boxVh",b:"╫"},{a:"boxVH",b:"╬"},{a:"boxvl",b:"┤"},{a:"boxvL",b:"╡"},{a:"boxVl",b:"╢"},{a:"boxVL",b:"╣"},{a:"boxvr",b:"├"},{a:"boxvR",b:"╞"},{a:"boxVr",b:"╟"},{a:"boxVR",b:"╠"},{a:"bprime",b:"‵"},{a:"breve",b:"˘"},{a:"Breve",b:"˘"},{a:"brvbar",b:"¦"},{a:"bscr",b:"𝒷"},{a:"Bscr",b:"ℬ"},{a:"bsemi",b:"⁏"},{a:"bsim",b:"∽"},{a:"bsime",b:"⋍"},{a:"bsolb",b:"⧅"},{a:"bsol",b:"\\"},{a:"bsolhsub",b:"⟈"},{a:"bull",b:"•"},{a:"bullet",b:"•"},{a:"bump",b:"≎"},{a:"bumpE",b:"⪮"},{a:"bumpe",b:"≏"},{a:"Bumpeq",b:"≎"},{a:"bumpeq",b:"≏"},{a:"Cacute",b:"Ć"},{a:"cacute",b:"ć"},{a:"capand",b:"⩄"},{a:"capbrcup",b:"⩉"},{a:"capcap",b:"⩋"},{a:"cap",b:"∩"},{a:"Cap",b:"⋒"},{a:"capcup",b:"⩇"},{a:"capdot",b:"⩀"},{a:"CapitalDifferentialD",b:"ⅅ"},{a:"caps",b:"∩︀"},{a:"caret",b:"⁁"},{a:"caron",b:"ˇ"},{a:"Cayleys",b:"ℭ"},{a:"ccaps",b:"⩍"},{a:"Ccaron",b:"Č"},{a:"ccaron",b:"č"},{a:"Ccedil",b:"Ç"},{a:"ccedil",b:"ç"},{a:"Ccirc",b:"Ĉ"},{a:"ccirc",b:"ĉ"},{a:"Cconint",b:"∰"},{a:"ccups",b:"⩌"},{a:"ccupssm",b:"⩐"},{a:"Cdot",b:"Ċ"},{a:"cdot",b:"ċ"},{a:"cedil",b:"¸"},{a:"Cedilla",b:"¸"},{a:"cemptyv",b:"⦲"},{a:"cent",b:"¢"},{a:"centerdot",b:"·"},{a:"CenterDot",b:"·"},{a:"cfr",b:"𝔠"},{a:"Cfr",b:"ℭ"},{a:"CHcy",b:"Ч"},{a:"chcy",b:"ч"},{a:"check",b:"✓"},{a:"checkmark",b:"✓"},{a:"Chi",b:"Χ"},{a:"chi",b:"χ"},{a:"circ",b:"ˆ"},{a:"circeq",b:"≗"},{a:"circlearrowleft",b:"↺"},{a:"circlearrowright",b:"↻"},{a:"circledast",b:"⊛"},{a:"circledcirc",b:"⊚"},{a:"circleddash",b:"⊝"},{a:"CircleDot",b:"⊙"},{a:"circledR",b:"®"},{a:"circledS",b:"Ⓢ"},{a:"CircleMinus",b:"⊖"},{a:"CirclePlus",b:"⊕"},{a:"CircleTimes",b:"⊗"},{a:"cir",b:"○"},{a:"cirE",b:"⧃"},{a:"cire",b:"≗"},{a:"cirfnint",b:"⨐"},{a:"cirmid",b:"⫯"},{a:"cirscir",b:"⧂"},{a:"ClockwiseContourIntegral",b:"∲"},{a:"CloseCurlyDoubleQuote",b:"”"},{a:"CloseCurlyQuote",b:"’"},{a:"clubs",b:"♣"},{a:"clubsuit",b:"♣"},{a:"colon",b:":"},{a:"Colon",b:"∷"},{a:"Colone",b:"⩴"},{a:"colone",b:"≔"},{a:"coloneq",b:"≔"},{a:"comma",b:","},{a:"commat",b:"@"},{a:"comp",b:"∁"},{a:"compfn",b:"∘"},{a:"complement",b:"∁"},{a:"complexes",b:"ℂ"},{a:"cong",b:"≅"},{a:"congdot",b:"⩭"},{a:"Congruent",b:"≡"},{a:"conint",b:"∮"},{a:"Conint",b:"∯"},{a:"ContourIntegral",b:"∮"},{a:"copf",b:"𝕔"},{a:"Copf",b:"ℂ"},{a:"coprod",b:"∐"},{a:"Coproduct",b:"∐"},{a:"copy",b:"©"},{a:"COPY",b:"©"},{a:"copysr",b:"℗"},{a:"CounterClockwiseContourIntegral",b:"∳"},{a:"crarr",b:"↵"},{a:"cross",b:"✗"},{a:"Cross",b:"⨯"},{a:"Cscr",b:"𝒞"},{a:"cscr",b:"𝒸"},{a:"csub",b:"⫏"},{a:"csube",b:"⫑"},{a:"csup",b:"⫐"},{a:"csupe",b:"⫒"},{a:"ctdot",b:"⋯"},{a:"cudarrl",b:"⤸"},{a:"cudarrr",b:"⤵"},{a:"cuepr",b:"⋞"},{a:"cuesc",b:"⋟"},{a:"cularr",b:"↶"},{a:"cularrp",b:"⤽"},{a:"cupbrcap",b:"⩈"},{a:"cupcap",b:"⩆"},{a:"CupCap",b:"≍"},{a:"cup",b:"∪"},{a:"Cup",b:"⋓"},{a:"cupcup",b:"⩊"},{a:"cupdot",b:"⊍"},{a:"cupor",b:"⩅"},{a:"cups",b:"∪︀"},{a:"curarr",b:"↷"},{a:"curarrm",b:"⤼"},{a:"curlyeqprec",b:"⋞"},{a:"curlyeqsucc",b:"⋟"},{a:"curlyvee",b:"⋎"},{a:"curlywedge",b:"⋏"},{a:"curren",b:"¤"},{a:"curvearrowleft",b:"↶"},{a:"curvearrowright",b:"↷"},{a:"cuvee",b:"⋎"},{a:"cuwed",b:"⋏"},{a:"cwconint",b:"∲"},{a:"cwint",b:"∱"},{a:"cylcty",b:"⌭"},{a:"dagger",b:"†"},{a:"Dagger",b:"‡"},{a:"daleth",b:"ℸ"},{a:"darr",b:"↓"},{a:"Darr",b:"↡"},{a:"dArr",b:"⇓"},{a:"dash",b:"‐"},{a:"Dashv",b:"⫤"},{a:"dashv",b:"⊣"},{a:"dbkarow",b:"⤏"},{a:"dblac",b:"˝"},{a:"Dcaron",b:"Ď"},{a:"dcaron",b:"ď"},{a:"Dcy",b:"Д"},{a:"dcy",b:"д"},{a:"ddagger",b:"‡"},{a:"ddarr",b:"⇊"},{a:"DD",b:"ⅅ"},{a:"dd",b:"ⅆ"},{a:"DDotrahd",b:"⤑"},{a:"ddotseq",b:"⩷"},{a:"deg",b:"°"},{a:"Del",b:"∇"},{a:"Delta",b:"Δ"},{a:"delta",b:"δ"},{a:"demptyv",b:"⦱"},{a:"dfisht",b:"⥿"},{a:"Dfr",b:"𝔇"},{a:"dfr",b:"𝔡"},{a:"dHar",b:"⥥"},{a:"dharl",b:"⇃"},{a:"dharr",b:"⇂"},{a:"DiacriticalAcute",b:"´"},{a:"DiacriticalDot",b:"˙"},{a:"DiacriticalDoubleAcute",b:"˝"},{a:"DiacriticalGrave",b:"`"},{a:"DiacriticalTilde",b:"˜"},{a:"diam",b:"⋄"},{a:"diamond",b:"⋄"},{a:"Diamond",b:"⋄"},{a:"diamondsuit",b:"♦"},{a:"diams",b:"♦"},{a:"die",b:"¨"},{a:"DifferentialD",b:"ⅆ"},{a:"digamma",b:"ϝ"},{a:"disin",b:"⋲"},{a:"div",b:"÷"},{a:"divide",b:"÷"},{a:"divideontimes",b:"⋇"},{a:"divonx",b:"⋇"},{a:"DJcy",b:"Ђ"},{a:"djcy",b:"ђ"},{a:"dlcorn",b:"⌞"},{a:"dlcrop",b:"⌍"},{a:"dollar",b:"$"},{a:"Dopf",b:"𝔻"},{a:"dopf",b:"𝕕"},{a:"Dot",b:"¨"},{a:"dot",b:"˙"},{a:"DotDot",b:"⃜"},{a:"doteq",b:"≐"},{a:"doteqdot",b:"≑"},{a:"DotEqual",b:"≐"},{a:"dotminus",b:"∸"},{a:"dotplus",b:"∔"},{a:"dotsquare",b:"⊡"},{a:"doublebarwedge",b:"⌆"},{a:"DoubleContourIntegral",b:"∯"},{a:"DoubleDot",b:"¨"},{a:"DoubleDownArrow",b:"⇓"},{a:"DoubleLeftArrow",b:"⇐"},{a:"DoubleLeftRightArrow",b:"⇔"},{a:"DoubleLeftTee",b:"⫤"},{a:"DoubleLongLeftArrow",b:"⟸"},{a:"DoubleLongLeftRightArrow",b:"⟺"},{a:"DoubleLongRightArrow",b:"⟹"},{a:"DoubleRightArrow",b:"⇒"},{a:"DoubleRightTee",b:"⊨"},{a:"DoubleUpArrow",b:"⇑"},{a:"DoubleUpDownArrow",b:"⇕"},{a:"DoubleVerticalBar",b:"∥"},{a:"DownArrowBar",b:"⤓"},{a:"downarrow",b:"↓"},{a:"DownArrow",b:"↓"},{a:"Downarrow",b:"⇓"},{a:"DownArrowUpArrow",b:"⇵"},{a:"DownBreve",b:"̑"},{a:"downdownarrows",b:"⇊"},{a:"downharpoonleft",b:"⇃"},{a:"downharpoonright",b:"⇂"},{a:"DownLeftRightVector",b:"⥐"},{a:"DownLeftTeeVector",b:"⥞"},{a:"DownLeftVectorBar",b:"⥖"},{a:"DownLeftVector",b:"↽"},{a:"DownRightTeeVector",b:"⥟"},{a:"DownRightVectorBar",b:"⥗"},{a:"DownRightVector",b:"⇁"},{a:"DownTeeArrow",b:"↧"},{a:"DownTee",b:"⊤"},{a:"drbkarow",b:"⤐"},{a:"drcorn",b:"⌟"},{a:"drcrop",b:"⌌"},{a:"Dscr",b:"𝒟"},{a:"dscr",b:"𝒹"},{a:"DScy",b:"Ѕ"},{a:"dscy",b:"ѕ"},{a:"dsol",b:"⧶"},{a:"Dstrok",b:"Đ"},{a:"dstrok",b:"đ"},{a:"dtdot",b:"⋱"},{a:"dtri",b:"▿"},{a:"dtrif",b:"▾"},{a:"duarr",b:"⇵"},{a:"duhar",b:"⥯"},{a:"dwangle",b:"⦦"},{a:"DZcy",b:"Џ"},{a:"dzcy",b:"џ"},{a:"dzigrarr",b:"⟿"},{a:"Eacute",b:"É"},{a:"eacute",b:"é"},{a:"easter",b:"⩮"},{a:"Ecaron",b:"Ě"},{a:"ecaron",b:"ě"},{a:"Ecirc",b:"Ê"},{a:"ecirc",b:"ê"},{a:"ecir",b:"≖"},{a:"ecolon",b:"≕"},{a:"Ecy",b:"Э"},{a:"ecy",b:"э"},{a:"eDDot",b:"⩷"},{a:"Edot",b:"Ė"},{a:"edot",b:"ė"},{a:"eDot",b:"≑"},{a:"ee",b:"ⅇ"},{a:"efDot",b:"≒"},{a:"Efr",b:"𝔈"},{a:"efr",b:"𝔢"},{a:"eg",b:"⪚"},{a:"Egrave",b:"È"},{a:"egrave",b:"è"},{a:"egs",b:"⪖"},{a:"egsdot",b:"⪘"},{a:"el",b:"⪙"},{a:"Element",b:"∈"},{a:"elinters",b:"⏧"},{a:"ell",b:"ℓ"},{a:"els",b:"⪕"},{a:"elsdot",b:"⪗"},{a:"Emacr",b:"Ē"},{a:"emacr",b:"ē"},{a:"empty",b:"∅"},{a:"emptyset",b:"∅"},{a:"EmptySmallSquare",b:"◻"},{a:"emptyv",b:"∅"},{a:"EmptyVerySmallSquare",b:"▫"},{a:"emsp13",b:" "},{a:"emsp14",b:" "},{a:"emsp",b:" "},{a:"ENG",b:"Ŋ"},{a:"eng",b:"ŋ"},{a:"ensp",b:" "},{a:"Eogon",b:"Ę"},{a:"eogon",b:"ę"},{a:"Eopf",b:"𝔼"},{a:"eopf",b:"𝕖"},{a:"epar",b:"⋕"},{a:"eparsl",b:"⧣"},{a:"eplus",b:"⩱"},{a:"epsi",b:"ε"},{a:"Epsilon",b:"Ε"},{a:"epsilon",b:"ε"},{a:"epsiv",b:"ϵ"},{a:"eqcirc",b:"≖"},{a:"eqcolon",b:"≕"},{a:"eqsim",b:"≂"},{a:"eqslantgtr",b:"⪖"},{a:"eqslantless",b:"⪕"},{a:"Equal",b:"⩵"},{a:"equals",b:"="},{a:"EqualTilde",b:"≂"},{a:"equest",b:"≟"},{a:"Equilibrium",b:"⇌"},{a:"equiv",b:"≡"},{a:"equivDD",b:"⩸"},{a:"eqvparsl",b:"⧥"},{a:"erarr",b:"⥱"},{a:"erDot",b:"≓"},{a:"escr",b:"ℯ"},{a:"Escr",b:"ℰ"},{a:"esdot",b:"≐"},{a:"Esim",b:"⩳"},{a:"esim",b:"≂"},{a:"Eta",b:"Η"},{a:"eta",b:"η"},{a:"ETH",b:"Ð"},{a:"eth",b:"ð"},{a:"Euml",b:"Ë"},{a:"euml",b:"ë"},{a:"euro",b:"€"},{a:"excl",b:"!"},{a:"exist",b:"∃"},{a:"Exists",b:"∃"},{a:"expectation",b:"ℰ"},{a:"exponentiale",b:"ⅇ"},{a:"ExponentialE",b:"ⅇ"},{a:"fallingdotseq",b:"≒"},{a:"Fcy",b:"Ф"},{a:"fcy",b:"ф"},{a:"female",b:"♀"},{a:"ffilig",b:"ffi"},{a:"fflig",b:"ff"},{a:"ffllig",b:"ffl"},{a:"Ffr",b:"𝔉"},{a:"ffr",b:"𝔣"},{a:"filig",b:"fi"},{a:"FilledSmallSquare",b:"◼"},{a:"FilledVerySmallSquare",b:"▪"},{a:"fjlig",b:"fj"},{a:"flat",b:"♭"},{a:"fllig",b:"fl"},{a:"fltns",b:"▱"},{a:"fnof",b:"ƒ"},{a:"Fopf",b:"𝔽"},{a:"fopf",b:"𝕗"},{a:"forall",b:"∀"},{a:"ForAll",b:"∀"},{a:"fork",b:"⋔"},{a:"forkv",b:"⫙"},{a:"Fouriertrf",b:"ℱ"},{a:"fpartint",b:"⨍"},{a:"frac12",b:"½"},{a:"frac13",b:"⅓"},{a:"frac14",b:"¼"},{a:"frac15",b:"⅕"},{a:"frac16",b:"⅙"},{a:"frac18",b:"⅛"},{a:"frac23",b:"⅔"},{a:"frac25",b:"⅖"},{a:"frac34",b:"¾"},{a:"frac35",b:"⅗"},{a:"frac38",b:"⅜"},{a:"frac45",b:"⅘"},{a:"frac56",b:"⅚"},{a:"frac58",b:"⅝"},{a:"frac78",b:"⅞"},{a:"frasl",b:"⁄"},{a:"frown",b:"⌢"},{a:"fscr",b:"𝒻"},{a:"Fscr",b:"ℱ"},{a:"gacute",b:"ǵ"},{a:"Gamma",b:"Γ"},{a:"gamma",b:"γ"},{a:"Gammad",b:"Ϝ"},{a:"gammad",b:"ϝ"},{a:"gap",b:"⪆"},{a:"Gbreve",b:"Ğ"},{a:"gbreve",b:"ğ"},{a:"Gcedil",b:"Ģ"},{a:"Gcirc",b:"Ĝ"},{a:"gcirc",b:"ĝ"},{a:"Gcy",b:"Г"},{a:"gcy",b:"г"},{a:"Gdot",b:"Ġ"},{a:"gdot",b:"ġ"},{a:"ge",b:"≥"},{a:"gE",b:"≧"},{a:"gEl",b:"⪌"},{a:"gel",b:"⋛"},{a:"geq",b:"≥"},{a:"geqq",b:"≧"},{a:"geqslant",b:"⩾"},{a:"gescc",b:"⪩"},{a:"ges",b:"⩾"},{a:"gesdot",b:"⪀"},{a:"gesdoto",b:"⪂"},{a:"gesdotol",b:"⪄"},{a:"gesl",b:"⋛︀"},{a:"gesles",b:"⪔"},{a:"Gfr",b:"𝔊"},{a:"gfr",b:"𝔤"},{a:"gg",b:"≫"},{a:"Gg",b:"⋙"},{a:"ggg",b:"⋙"},{a:"gimel",b:"ℷ"},{a:"GJcy",b:"Ѓ"},{a:"gjcy",b:"ѓ"},{a:"gla",b:"⪥"},{a:"gl",b:"≷"},{a:"glE",b:"⪒"},{a:"glj",b:"⪤"},{a:"gnap",b:"⪊"},{a:"gnapprox",b:"⪊"},{a:"gne",b:"⪈"},{a:"gnE",b:"≩"},{a:"gneq",b:"⪈"},{a:"gneqq",b:"≩"},{a:"gnsim",b:"⋧"},{a:"Gopf",b:"𝔾"},{a:"gopf",b:"𝕘"},{a:"grave",b:"`"},{a:"GreaterEqual",b:"≥"},{a:"GreaterEqualLess",b:"⋛"},{a:"GreaterFullEqual",b:"≧"},{a:"GreaterGreater",b:"⪢"},{a:"GreaterLess",b:"≷"},{a:"GreaterSlantEqual",b:"⩾"},{a:"GreaterTilde",b:"≳"},{a:"Gscr",b:"𝒢"},{a:"gscr",b:"ℊ"},{a:"gsim",b:"≳"},{a:"gsime",b:"⪎"},{a:"gsiml",b:"⪐"},{a:"gtcc",b:"⪧"},{a:"gtcir",b:"⩺"},{a:"gt",b:">"},{a:"GT",b:">"},{a:"Gt",b:"≫"},{a:"gtdot",b:"⋗"},{a:"gtlPar",b:"⦕"},{a:"gtquest",b:"⩼"},{a:"gtrapprox",b:"⪆"},{a:"gtrarr",b:"⥸"},{a:"gtrdot",b:"⋗"},{a:"gtreqless",b:"⋛"},{a:"gtreqqless",b:"⪌"},{a:"gtrless",b:"≷"},{a:"gtrsim",b:"≳"},{a:"gvertneqq",b:"≩︀"},{a:"gvnE",b:"≩︀"},{a:"Hacek",b:"ˇ"},{a:"hairsp",b:" "},{a:"half",b:"½"},{a:"hamilt",b:"ℋ"},{a:"HARDcy",b:"Ъ"},{a:"hardcy",b:"ъ"},{a:"harrcir",b:"⥈"},{a:"harr",b:"↔"},{a:"hArr",b:"⇔"},{a:"harrw",b:"↭"},{a:"Hat",b:"^"},{a:"hbar",b:"ℏ"},{a:"Hcirc",b:"Ĥ"},{a:"hcirc",b:"ĥ"},{a:"hearts",b:"♥"},{a:"heartsuit",b:"♥"},{a:"hellip",b:"…"},{a:"hercon",b:"⊹"},{a:"hfr",b:"𝔥"},{a:"Hfr",b:"ℌ"},{a:"HilbertSpace",b:"ℋ"},{a:"hksearow",b:"⤥"},{a:"hkswarow",b:"⤦"},{a:"hoarr",b:"⇿"},{a:"homtht",b:"∻"},{a:"hookleftarrow",b:"↩"},{a:"hookrightarrow",b:"↪"},{a:"hopf",b:"𝕙"},{a:"Hopf",b:"ℍ"},{a:"horbar",b:"―"},{a:"HorizontalLine",b:"─"},{a:"hscr",b:"𝒽"},{a:"Hscr",b:"ℋ"},{a:"hslash",b:"ℏ"},{a:"Hstrok",b:"Ħ"},{a:"hstrok",b:"ħ"},{a:"HumpDownHump",b:"≎"},{a:"HumpEqual",b:"≏"},{a:"hybull",b:"⁃"},{a:"hyphen",b:"‐"},{a:"Iacute",b:"Í"},{a:"iacute",b:"í"},{a:"ic",b:"⁣"},{a:"Icirc",b:"Î"},{a:"icirc",b:"î"},{a:"Icy",b:"И"},{a:"icy",b:"и"},{a:"Idot",b:"İ"},{a:"IEcy",b:"Е"},{a:"iecy",b:"е"},{a:"iexcl",b:"¡"},{a:"iff",b:"⇔"},{a:"ifr",b:"𝔦"},{a:"Ifr",b:"ℑ"},{a:"Igrave",b:"Ì"},{a:"igrave",b:"ì"},{a:"ii",b:"ⅈ"},{a:"iiiint",b:"⨌"},{a:"iiint",b:"∭"},{a:"iinfin",b:"⧜"},{a:"iiota",b:"℩"},{a:"IJlig",b:"IJ"},{a:"ijlig",b:"ij"},{a:"Imacr",b:"Ī"},{a:"imacr",b:"ī"},{a:"image",b:"ℑ"},{a:"ImaginaryI",b:"ⅈ"},{a:"imagline",b:"ℐ"},{a:"imagpart",b:"ℑ"},{a:"imath",b:"ı"},{a:"Im",b:"ℑ"},{a:"imof",b:"⊷"},{a:"imped",b:"Ƶ"},{a:"Implies",b:"⇒"},{a:"incare",b:"℅"},{a:"in",b:"∈"},{a:"infin",b:"∞"},{a:"infintie",b:"⧝"},{a:"inodot",b:"ı"},{a:"intcal",b:"⊺"},{a:"int",b:"∫"},{a:"Int",b:"∬"},{a:"integers",b:"ℤ"},{a:"Integral",b:"∫"},{a:"intercal",b:"⊺"},{a:"Intersection",b:"⋂"},{a:"intlarhk",b:"⨗"},{a:"intprod",b:"⨼"},{a:"InvisibleComma",b:"⁣"},{a:"InvisibleTimes",b:"⁢"},{a:"IOcy",b:"Ё"},{a:"iocy",b:"ё"},{a:"Iogon",b:"Į"},{a:"iogon",b:"į"},{a:"Iopf",b:"𝕀"},{a:"iopf",b:"𝕚"},{a:"Iota",b:"Ι"},{a:"iota",b:"ι"},{a:"iprod",b:"⨼"},{a:"iquest",b:"¿"},{a:"iscr",b:"𝒾"},{a:"Iscr",b:"ℐ"},{a:"isin",b:"∈"},{a:"isindot",b:"⋵"},{a:"isinE",b:"⋹"},{a:"isins",b:"⋴"},{a:"isinsv",b:"⋳"},{a:"isinv",b:"∈"},{a:"it",b:"⁢"},{a:"Itilde",b:"Ĩ"},{a:"itilde",b:"ĩ"},{a:"Iukcy",b:"І"},{a:"iukcy",b:"і"},{a:"Iuml",b:"Ï"},{a:"iuml",b:"ï"},{a:"Jcirc",b:"Ĵ"},{a:"jcirc",b:"ĵ"},{a:"Jcy",b:"Й"},{a:"jcy",b:"й"},{a:"Jfr",b:"𝔍"},{a:"jfr",b:"𝔧"},{a:"jmath",b:"ȷ"},{a:"Jopf",b:"𝕁"},{a:"jopf",b:"𝕛"},{a:"Jscr",b:"𝒥"},{a:"jscr",b:"𝒿"},{a:"Jsercy",b:"Ј"},{a:"jsercy",b:"ј"},{a:"Jukcy",b:"Є"},{a:"jukcy",b:"є"},{a:"Kappa",b:"Κ"},{a:"kappa",b:"κ"},{a:"kappav",b:"ϰ"},{a:"Kcedil",b:"Ķ"},{a:"kcedil",b:"ķ"},{a:"Kcy",b:"К"},{a:"kcy",b:"к"},{a:"Kfr",b:"𝔎"},{a:"kfr",b:"𝔨"},{a:"kgreen",b:"ĸ"},{a:"KHcy",b:"Х"},{a:"khcy",b:"х"},{a:"KJcy",b:"Ќ"},{a:"kjcy",b:"ќ"},{a:"Kopf",b:"𝕂"},{a:"kopf",b:"𝕜"},{a:"Kscr",b:"𝒦"},{a:"kscr",b:"𝓀"},{a:"lAarr",b:"⇚"},{a:"Lacute",b:"Ĺ"},{a:"lacute",b:"ĺ"},{a:"laemptyv",b:"⦴"},{a:"lagran",b:"ℒ"},{a:"Lambda",b:"Λ"},{a:"lambda",b:"λ"},{a:"lang",b:"⟨"},{a:"Lang",b:"⟪"},{a:"langd",b:"⦑"},{a:"langle",b:"⟨"},{a:"lap",b:"⪅"},{a:"Laplacetrf",b:"ℒ"},{a:"laquo",b:"«"},{a:"larrb",b:"⇤"},{a:"larrbfs",b:"⤟"},{a:"larr",b:"←"},{a:"Larr",b:"↞"},{a:"lArr",b:"⇐"},{a:"larrfs",b:"⤝"},{a:"larrhk",b:"↩"},{a:"larrlp",b:"↫"},{a:"larrpl",b:"⤹"},{a:"larrsim",b:"⥳"},{a:"larrtl",b:"↢"},{a:"latail",b:"⤙"},{a:"lAtail",b:"⤛"},{a:"lat",b:"⪫"},{a:"late",b:"⪭"},{a:"lates",b:"⪭︀"},{a:"lbarr",b:"⤌"},{a:"lBarr",b:"⤎"},{a:"lbbrk",b:"❲"},{a:"lbrace",b:"{"},{a:"lbrack",b:"["},{a:"lbrke",b:"⦋"},{a:"lbrksld",b:"⦏"},{a:"lbrkslu",b:"⦍"},{a:"Lcaron",b:"Ľ"},{a:"lcaron",b:"ľ"},{a:"Lcedil",b:"Ļ"},{a:"lcedil",b:"ļ"},{a:"lceil",b:"⌈"},{a:"lcub",b:"{"},{a:"Lcy",b:"Л"},{a:"lcy",b:"л"},{a:"ldca",b:"⤶"},{a:"ldquo",b:"“"},{a:"ldquor",b:"„"},{a:"ldrdhar",b:"⥧"},{a:"ldrushar",b:"⥋"},{a:"ldsh",b:"↲"},{a:"le",b:"≤"},{a:"lE",b:"≦"},{a:"LeftAngleBracket",b:"⟨"},{a:"LeftArrowBar",b:"⇤"},{a:"leftarrow",b:"←"},{a:"LeftArrow",b:"←"},{a:"Leftarrow",b:"⇐"},{a:"LeftArrowRightArrow",b:"⇆"},{a:"leftarrowtail",b:"↢"},{a:"LeftCeiling",b:"⌈"},{a:"LeftDoubleBracket",b:"⟦"},{a:"LeftDownTeeVector",b:"⥡"},{a:"LeftDownVectorBar",b:"⥙"},{a:"LeftDownVector",b:"⇃"},{a:"LeftFloor",b:"⌊"},{a:"leftharpoondown",b:"↽"},{a:"leftharpoonup",b:"↼"},{a:"leftleftarrows",b:"⇇"},{a:"leftrightarrow",b:"↔"},{a:"LeftRightArrow",b:"↔"},{a:"Leftrightarrow",b:"⇔"},{a:"leftrightarrows",b:"⇆"},{a:"leftrightharpoons",b:"⇋"},{a:"leftrightsquigarrow",b:"↭"},{a:"LeftRightVector",b:"⥎"},{a:"LeftTeeArrow",b:"↤"},{a:"LeftTee",b:"⊣"},{a:"LeftTeeVector",b:"⥚"},{a:"leftthreetimes",b:"⋋"},{a:"LeftTriangleBar",b:"⧏"},{a:"LeftTriangle",b:"⊲"},{a:"LeftTriangleEqual",b:"⊴"},{a:"LeftUpDownVector",b:"⥑"},{a:"LeftUpTeeVector",b:"⥠"},{a:"LeftUpVectorBar",b:"⥘"},{a:"LeftUpVector",b:"↿"},{a:"LeftVectorBar",b:"⥒"},{a:"LeftVector",b:"↼"},{a:"lEg",b:"⪋"},{a:"leg",b:"⋚"},{a:"leq",b:"≤"},{a:"leqq",b:"≦"},{a:"leqslant",b:"⩽"},{a:"lescc",b:"⪨"},{a:"les",b:"⩽"},{a:"lesdot",b:"⩿"},{a:"lesdoto",b:"⪁"},{a:"lesdotor",b:"⪃"},{a:"lesg",b:"⋚︀"},{a:"lesges",b:"⪓"},{a:"lessapprox",b:"⪅"},{a:"lessdot",b:"⋖"},{a:"lesseqgtr",b:"⋚"},{a:"lesseqqgtr",b:"⪋"},{a:"LessEqualGreater",b:"⋚"},{a:"LessFullEqual",b:"≦"},{a:"LessGreater",b:"≶"},{a:"lessgtr",b:"≶"},{a:"LessLess",b:"⪡"},{a:"lesssim",b:"≲"},{a:"LessSlantEqual",b:"⩽"},{a:"LessTilde",b:"≲"},{a:"lfisht",b:"⥼"},{a:"lfloor",b:"⌊"},{a:"Lfr",b:"𝔏"},{a:"lfr",b:"𝔩"},{a:"lg",b:"≶"},{a:"lgE",b:"⪑"},{a:"lHar",b:"⥢"},{a:"lhard",b:"↽"},{a:"lharu",b:"↼"},{a:"lharul",b:"⥪"},{a:"lhblk",b:"▄"},{a:"LJcy",b:"Љ"},{a:"ljcy",b:"љ"},{a:"llarr",b:"⇇"},{a:"ll",b:"≪"},{a:"Ll",b:"⋘"},{a:"llcorner",b:"⌞"},{a:"Lleftarrow",b:"⇚"},{a:"llhard",b:"⥫"},{a:"lltri",b:"◺"},{a:"Lmidot",b:"Ŀ"},{a:"lmidot",b:"ŀ"},{a:"lmoustache",b:"⎰"},{a:"lmoust",b:"⎰"},{a:"lnap",b:"⪉"},{a:"lnapprox",b:"⪉"},{a:"lne",b:"⪇"},{a:"lnE",b:"≨"},{a:"lneq",b:"⪇"},{a:"lneqq",b:"≨"},{a:"lnsim",b:"⋦"},{a:"loang",b:"⟬"},{a:"loarr",b:"⇽"},{a:"lobrk",b:"⟦"},{a:"longleftarrow",b:"⟵"},{a:"LongLeftArrow",b:"⟵"},{a:"Longleftarrow",b:"⟸"},{a:"longleftrightarrow",b:"⟷"},{a:"LongLeftRightArrow",b:"⟷"},{a:"Longleftrightarrow",b:"⟺"},{a:"longmapsto",b:"⟼"},{a:"longrightarrow",b:"⟶"},{a:"LongRightArrow",b:"⟶"},{a:"Longrightarrow",b:"⟹"},{a:"looparrowleft",b:"↫"},{a:"looparrowright",b:"↬"},{a:"lopar",b:"⦅"},{a:"Lopf",b:"𝕃"},{a:"lopf",b:"𝕝"},{a:"loplus",b:"⨭"},{a:"lotimes",b:"⨴"},{a:"lowast",b:"∗"},{a:"lowbar",b:"_"},{a:"LowerLeftArrow",b:"↙"},{a:"LowerRightArrow",b:"↘"},{a:"loz",b:"◊"},{a:"lozenge",b:"◊"},{a:"lozf",b:"⧫"},{a:"lpar",b:"("},{a:"lparlt",b:"⦓"},{a:"lrarr",b:"⇆"},{a:"lrcorner",b:"⌟"},{a:"lrhar",b:"⇋"},{a:"lrhard",b:"⥭"},{a:"lrm",b:"‎"},{a:"lrtri",b:"⊿"},{a:"lsaquo",b:"‹"},{a:"lscr",b:"𝓁"},{a:"Lscr",b:"ℒ"},{a:"lsh",b:"↰"},{a:"Lsh",b:"↰"},{a:"lsim",b:"≲"},{a:"lsime",b:"⪍"},{a:"lsimg",b:"⪏"},{a:"lsqb",b:"["},{a:"lsquo",b:"‘"},{a:"lsquor",b:"‚"},{a:"Lstrok",b:"Ł"},{a:"lstrok",b:"ł"},{a:"ltcc",b:"⪦"},{a:"ltcir",b:"⩹"},{a:"lt",b:"<"},{a:"LT",b:"<"},{a:"Lt",b:"≪"},{a:"ltdot",b:"⋖"},{a:"lthree",b:"⋋"},{a:"ltimes",b:"⋉"},{a:"ltlarr",b:"⥶"},{a:"ltquest",b:"⩻"},{a:"ltri",b:"◃"},{a:"ltrie",b:"⊴"},{a:"ltrif",b:"◂"},{a:"ltrPar",b:"⦖"},{a:"lurdshar",b:"⥊"},{a:"luruhar",b:"⥦"},{a:"lvertneqq",b:"≨︀"},{a:"lvnE",b:"≨︀"},{a:"macr",b:"¯"},{a:"male",b:"♂"},{a:"malt",b:"✠"},{a:"maltese",b:"✠"},{a:"Map",b:"⤅"},{a:"map",b:"↦"},{a:"mapsto",b:"↦"},{a:"mapstodown",b:"↧"},{a:"mapstoleft",b:"↤"},{a:"mapstoup",b:"↥"},{a:"marker",b:"▮"},{a:"mcomma",b:"⨩"},{a:"Mcy",b:"М"},{a:"mcy",b:"м"},{a:"mdash",b:"—"},{a:"mDDot",b:"∺"},{a:"measuredangle",b:"∡"},{a:"MediumSpace",b:" "},{a:"Mellintrf",b:"ℳ"},{a:"Mfr",b:"𝔐"},{a:"mfr",b:"𝔪"},{a:"mho",b:"℧"},{a:"micro",b:"µ"},{a:"midast",b:"*"},{a:"midcir",b:"⫰"},{a:"mid",b:"∣"},{a:"middot",b:"·"},{a:"minusb",b:"⊟"},{a:"minus",b:"−"},{a:"minusd",b:"∸"},{a:"minusdu",b:"⨪"},{a:"MinusPlus",b:"∓"},{a:"mlcp",b:"⫛"},{a:"mldr",b:"…"},{a:"mnplus",b:"∓"},{a:"models",b:"⊧"},{a:"Mopf",b:"𝕄"},{a:"mopf",b:"𝕞"},{a:"mp",b:"∓"},{a:"mscr",b:"𝓂"},{a:"Mscr",b:"ℳ"},{a:"mstpos",b:"∾"},{a:"Mu",b:"Μ"},{a:"mu",b:"μ"},{a:"multimap",b:"⊸"},{a:"mumap",b:"⊸"},{a:"nabla",b:"∇"},{a:"Nacute",b:"Ń"},{a:"nacute",b:"ń"},{a:"nang",b:"∠⃒"},{a:"nap",b:"≉"},{a:"napE",b:"⩰̸"},{a:"napid",b:"≋̸"},{a:"napos",b:"ʼn"},{a:"napprox",b:"≉"},{a:"natural",b:"♮"},{a:"naturals",b:"ℕ"},{a:"natur",b:"♮"},{a:"nbsp",b:" "},{a:"nbump",b:"≎̸"},{a:"nbumpe",b:"≏̸"},{a:"ncap",b:"⩃"},{a:"Ncaron",b:"Ň"},{a:"ncaron",b:"ň"},{a:"Ncedil",b:"Ņ"},{a:"ncedil",b:"ņ"},{a:"ncong",b:"≇"},{a:"ncongdot",b:"⩭̸"},{a:"ncup",b:"⩂"},{a:"Ncy",b:"Н"},{a:"ncy",b:"н"},{a:"ndash",b:"–"},{a:"nearhk",b:"⤤"},{a:"nearr",b:"↗"},{a:"neArr",b:"⇗"},{a:"nearrow",b:"↗"},{a:"ne",b:"≠"},{a:"nedot",b:"≐̸"},{a:"NegativeMediumSpace",b:"​"},{a:"NegativeThickSpace",b:"​"},{a:"NegativeThinSpace",b:"​"},{a:"NegativeVeryThinSpace",b:"​"},{a:"nequiv",b:"≢"},{a:"nesear",b:"⤨"},{a:"nesim",b:"≂̸"},{a:"NestedGreaterGreater",b:"≫"},{a:"NestedLessLess",b:"≪"},{a:"NewLine",b:"\n"},{a:"nexist",b:"∄"},{a:"nexists",b:"∄"},{a:"Nfr",b:"𝔑"},{a:"nfr",b:"𝔫"},{a:"ngE",b:"≧̸"},{a:"nge",b:"≱"},{a:"ngeq",b:"≱"},{a:"ngeqq",b:"≧̸"},{a:"ngeqslant",b:"⩾̸"},{a:"nges",b:"⩾̸"},{a:"nGg",b:"⋙̸"},{a:"ngsim",b:"≵"},{a:"nGt",b:"≫⃒"},{a:"ngt",b:"≯"},{a:"ngtr",b:"≯"},{a:"nGtv",b:"≫̸"},{a:"nharr",b:"↮"},{a:"nhArr",b:"⇎"},{a:"nhpar",b:"⫲"},{a:"ni",b:"∋"},{a:"nis",b:"⋼"},{a:"nisd",b:"⋺"},{a:"niv",b:"∋"},{a:"NJcy",b:"Њ"},{a:"njcy",b:"њ"},{a:"nlarr",b:"↚"},{a:"nlArr",b:"⇍"},{a:"nldr",b:"‥"},{a:"nlE",b:"≦̸"},{a:"nle",b:"≰"},{a:"nleftarrow",b:"↚"},{a:"nLeftarrow",b:"⇍"},{a:"nleftrightarrow",b:"↮"},{a:"nLeftrightarrow",b:"⇎"},{a:"nleq",b:"≰"},{a:"nleqq",b:"≦̸"},{a:"nleqslant",b:"⩽̸"},{a:"nles",b:"⩽̸"},{a:"nless",b:"≮"},{a:"nLl",b:"⋘̸"},{a:"nlsim",b:"≴"},{a:"nLt",b:"≪⃒"},{a:"nlt",b:"≮"},{a:"nltri",b:"⋪"},{a:"nltrie",b:"⋬"},{a:"nLtv",b:"≪̸"},{a:"nmid",b:"∤"},{a:"NoBreak",b:"⁠"},{a:"NonBreakingSpace",b:" "},{a:"nopf",b:"𝕟"},{a:"Nopf",b:"ℕ"},{a:"Not",b:"⫬"},{a:"not",b:"¬"},{a:"NotCongruent",b:"≢"},{a:"NotCupCap",b:"≭"},{a:"NotDoubleVerticalBar",b:"∦"},{a:"NotElement",b:"∉"},{a:"NotEqual",b:"≠"},{a:"NotEqualTilde",b:"≂̸"},{a:"NotExists",b:"∄"},{a:"NotGreater",b:"≯"},{a:"NotGreaterEqual",b:"≱"},{a:"NotGreaterFullEqual",b:"≧̸"},{a:"NotGreaterGreater",b:"≫̸"},{a:"NotGreaterLess",b:"≹"},{a:"NotGreaterSlantEqual",b:"⩾̸"},{a:"NotGreaterTilde",b:"≵"},{a:"NotHumpDownHump",b:"≎̸"},{a:"NotHumpEqual",b:"≏̸"},{a:"notin",b:"∉"},{a:"notindot",b:"⋵̸"},{a:"notinE",b:"⋹̸"},{a:"notinva",b:"∉"},{a:"notinvb",b:"⋷"},{a:"notinvc",b:"⋶"},{a:"NotLeftTriangleBar",b:"⧏̸"},{a:"NotLeftTriangle",b:"⋪"},{a:"NotLeftTriangleEqual",b:"⋬"},{a:"NotLess",b:"≮"},{a:"NotLessEqual",b:"≰"},{a:"NotLessGreater",b:"≸"},{a:"NotLessLess",b:"≪̸"},{a:"NotLessSlantEqual",b:"⩽̸"},{a:"NotLessTilde",b:"≴"},{a:"NotNestedGreaterGreater",b:"⪢̸"},{a:"NotNestedLessLess",b:"⪡̸"},{a:"notni",b:"∌"},{a:"notniva",b:"∌"},{a:"notnivb",b:"⋾"},{a:"notnivc",b:"⋽"},{a:"NotPrecedes",b:"⊀"},{a:"NotPrecedesEqual",b:"⪯̸"},{a:"NotPrecedesSlantEqual",b:"⋠"},{a:"NotReverseElement",b:"∌"},{a:"NotRightTriangleBar",b:"⧐̸"},{a:"NotRightTriangle",b:"⋫"},{a:"NotRightTriangleEqual",b:"⋭"},{a:"NotSquareSubset",b:"⊏̸"},{a:"NotSquareSubsetEqual",b:"⋢"},{a:"NotSquareSuperset",b:"⊐̸"},{a:"NotSquareSupersetEqual",b:"⋣"},{a:"NotSubset",b:"⊂⃒"},{a:"NotSubsetEqual",b:"⊈"},{a:"NotSucceeds",b:"⊁"},{a:"NotSucceedsEqual",b:"⪰̸"},{a:"NotSucceedsSlantEqual",b:"⋡"},{a:"NotSucceedsTilde",b:"≿̸"},{a:"NotSuperset",b:"⊃⃒"},{a:"NotSupersetEqual",b:"⊉"},{a:"NotTilde",b:"≁"},{a:"NotTildeEqual",b:"≄"},{a:"NotTildeFullEqual",b:"≇"},{a:"NotTildeTilde",b:"≉"},{a:"NotVerticalBar",b:"∤"},{a:"nparallel",b:"∦"},{a:"npar",b:"∦"},{a:"nparsl",b:"⫽⃥"},{a:"npart",b:"∂̸"},{a:"npolint",b:"⨔"},{a:"npr",b:"⊀"},{a:"nprcue",b:"⋠"},{a:"nprec",b:"⊀"},{a:"npreceq",b:"⪯̸"},{a:"npre",b:"⪯̸"},{a:"nrarrc",b:"⤳̸"},{a:"nrarr",b:"↛"},{a:"nrArr",b:"⇏"},{a:"nrarrw",b:"↝̸"},{a:"nrightarrow",b:"↛"},{a:"nRightarrow",b:"⇏"},{a:"nrtri",b:"⋫"},{a:"nrtrie",b:"⋭"},{a:"nsc",b:"⊁"},{a:"nsccue",b:"⋡"},{a:"nsce",b:"⪰̸"},{a:"Nscr",b:"𝒩"},{a:"nscr",b:"𝓃"},{a:"nshortmid",b:"∤"},{a:"nshortparallel",b:"∦"},{a:"nsim",b:"≁"},{a:"nsime",b:"≄"},{a:"nsimeq",b:"≄"},{a:"nsmid",b:"∤"},{a:"nspar",b:"∦"},{a:"nsqsube",b:"⋢"},{a:"nsqsupe",b:"⋣"},{a:"nsub",b:"⊄"},{a:"nsubE",b:"⫅̸"},{a:"nsube",b:"⊈"},{a:"nsubset",b:"⊂⃒"},{a:"nsubseteq",b:"⊈"},{a:"nsubseteqq",b:"⫅̸"},{a:"nsucc",b:"⊁"},{a:"nsucceq",b:"⪰̸"},{a:"nsup",b:"⊅"},{a:"nsupE",b:"⫆̸"},{a:"nsupe",b:"⊉"},{a:"nsupset",b:"⊃⃒"},{a:"nsupseteq",b:"⊉"},{a:"nsupseteqq",b:"⫆̸"},{a:"ntgl",b:"≹"},{a:"Ntilde",b:"Ñ"},{a:"ntilde",b:"ñ"},{a:"ntlg",b:"≸"},{a:"ntriangleleft",b:"⋪"},{a:"ntrianglelefteq",b:"⋬"},{a:"ntriangleright",b:"⋫"},{a:"ntrianglerighteq",b:"⋭"},{a:"Nu",b:"Ν"},{a:"nu",b:"ν"},{a:"num",b:"#"},{a:"numero",b:"№"},{a:"numsp",b:" "},{a:"nvap",b:"≍⃒"},{a:"nvdash",b:"⊬"},{a:"nvDash",b:"⊭"},{a:"nVdash",b:"⊮"},{a:"nVDash",b:"⊯"},{a:"nvge",b:"≥⃒"},{a:"nvgt",b:">⃒"},{a:"nvHarr",b:"⤄"},{a:"nvinfin",b:"⧞"},{a:"nvlArr",b:"⤂"},{a:"nvle",b:"≤⃒"},{a:"nvlt",b:"<⃒"},{a:"nvltrie",b:"⊴⃒"},{a:"nvrArr",b:"⤃"},{a:"nvrtrie",b:"⊵⃒"},{a:"nvsim",b:"∼⃒"},{a:"nwarhk",b:"⤣"},{a:"nwarr",b:"↖"},{a:"nwArr",b:"⇖"},{a:"nwarrow",b:"↖"},{a:"nwnear",b:"⤧"},{a:"Oacute",b:"Ó"},{a:"oacute",b:"ó"},{a:"oast",b:"⊛"},{a:"Ocirc",b:"Ô"},{a:"ocirc",b:"ô"},{a:"ocir",b:"⊚"},{a:"Ocy",b:"О"},{a:"ocy",b:"о"},{a:"odash",b:"⊝"},{a:"Odblac",b:"Ő"},{a:"odblac",b:"ő"},{a:"odiv",b:"⨸"},{a:"odot",b:"⊙"},{a:"odsold",b:"⦼"},{a:"OElig",b:"Œ"},{a:"oelig",b:"œ"},{a:"ofcir",b:"⦿"},{a:"Ofr",b:"𝔒"},{a:"ofr",b:"𝔬"},{a:"ogon",b:"˛"},{a:"Ograve",b:"Ò"},{a:"ograve",b:"ò"},{a:"ogt",b:"⧁"},{a:"ohbar",b:"⦵"},{a:"ohm",b:"Ω"},{a:"oint",b:"∮"},{a:"olarr",b:"↺"},{a:"olcir",b:"⦾"},{a:"olcross",b:"⦻"},{a:"oline",b:"‾"},{a:"olt",b:"⧀"},{a:"Omacr",b:"Ō"},{a:"omacr",b:"ō"},{a:"Omega",b:"Ω"},{a:"omega",b:"ω"},{a:"Omicron",b:"Ο"},{a:"omicron",b:"ο"},{a:"omid",b:"⦶"},{a:"ominus",b:"⊖"},{a:"Oopf",b:"𝕆"},{a:"oopf",b:"𝕠"},{a:"opar",b:"⦷"},{a:"OpenCurlyDoubleQuote",b:"“"},{a:"OpenCurlyQuote",b:"‘"},{a:"operp",b:"⦹"},{a:"oplus",b:"⊕"},{a:"orarr",b:"↻"},{a:"Or",b:"⩔"},{a:"or",b:"∨"},{a:"ord",b:"⩝"},{a:"order",b:"ℴ"},{a:"orderof",b:"ℴ"},{a:"ordf",b:"ª"},{a:"ordm",b:"º"},{a:"origof",b:"⊶"},{a:"oror",b:"⩖"},{a:"orslope",b:"⩗"},{a:"orv",b:"⩛"},{a:"oS",b:"Ⓢ"},{a:"Oscr",b:"𝒪"},{a:"oscr",b:"ℴ"},{a:"Oslash",b:"Ø"},{a:"oslash",b:"ø"},{a:"osol",b:"⊘"},{a:"Otilde",b:"Õ"},{a:"otilde",b:"õ"},{a:"otimesas",b:"⨶"},{a:"Otimes",b:"⨷"},{a:"otimes",b:"⊗"},{a:"Ouml",b:"Ö"},{a:"ouml",b:"ö"},{a:"ovbar",b:"⌽"},{a:"OverBar",b:"‾"},{a:"OverBrace",b:"⏞"},{a:"OverBracket",b:"⎴"},{a:"OverParenthesis",b:"⏜"},{a:"para",b:"¶"},{a:"parallel",b:"∥"},{a:"par",b:"∥"},{a:"parsim",b:"⫳"},{a:"parsl",b:"⫽"},{a:"part",b:"∂"},{a:"PartialD",b:"∂"},{a:"Pcy",b:"П"},{a:"pcy",b:"п"},{a:"percnt",b:"%"},{a:"period",b:"."},{a:"permil",b:"‰"},{a:"perp",b:"⊥"},{a:"pertenk",b:"‱"},{a:"Pfr",b:"𝔓"},{a:"pfr",b:"𝔭"},{a:"Phi",b:"Φ"},{a:"phi",b:"φ"},{a:"phiv",b:"ϕ"},{a:"phmmat",b:"ℳ"},{a:"phone",b:"☎"},{a:"Pi",b:"Π"},{a:"pi",b:"π"},{a:"pitchfork",b:"⋔"},{a:"piv",b:"ϖ"},{a:"planck",b:"ℏ"},{a:"planckh",b:"ℎ"},{a:"plankv",b:"ℏ"},{a:"plusacir",b:"⨣"},{a:"plusb",b:"⊞"},{a:"pluscir",b:"⨢"},{a:"plus",b:"+"},{a:"plusdo",b:"∔"},{a:"plusdu",b:"⨥"},{a:"pluse",b:"⩲"},{a:"PlusMinus",b:"±"},{a:"plusmn",b:"±"},{a:"plussim",b:"⨦"},{a:"plustwo",b:"⨧"},{a:"pm",b:"±"},{a:"Poincareplane",b:"ℌ"},{a:"pointint",b:"⨕"},{a:"popf",b:"𝕡"},{a:"Popf",b:"ℙ"},{a:"pound",b:"£"},{a:"prap",b:"⪷"},{a:"Pr",b:"⪻"},{a:"pr",b:"≺"},{a:"prcue",b:"≼"},{a:"precapprox",b:"⪷"},{a:"prec",b:"≺"},{a:"preccurlyeq",b:"≼"},{a:"Precedes",b:"≺"},{a:"PrecedesEqual",b:"⪯"},{a:"PrecedesSlantEqual",b:"≼"},{a:"PrecedesTilde",b:"≾"},{a:"preceq",b:"⪯"},{a:"precnapprox",b:"⪹"},{a:"precneqq",b:"⪵"},{a:"precnsim",b:"⋨"},{a:"pre",b:"⪯"},{a:"prE",b:"⪳"},{a:"precsim",b:"≾"},{a:"prime",b:"′"},{a:"Prime",b:"″"},{a:"primes",b:"ℙ"},{a:"prnap",b:"⪹"},{a:"prnE",b:"⪵"},{a:"prnsim",b:"⋨"},{a:"prod",b:"∏"},{a:"Product",b:"∏"},{a:"profalar",b:"⌮"},{a:"profline",b:"⌒"},{a:"profsurf",b:"⌓"},{a:"prop",b:"∝"},{a:"Proportional",b:"∝"},{a:"Proportion",b:"∷"},{a:"propto",b:"∝"},{a:"prsim",b:"≾"},{a:"prurel",b:"⊰"},{a:"Pscr",b:"𝒫"},{a:"pscr",b:"𝓅"},{a:"Psi",b:"Ψ"},{a:"psi",b:"ψ"},{a:"puncsp",b:" "},{a:"Qfr",b:"𝔔"},{a:"qfr",b:"𝔮"},{a:"qint",b:"⨌"},{a:"qopf",b:"𝕢"},{a:"Qopf",b:"ℚ"},{a:"qprime",b:"⁗"},{a:"Qscr",b:"𝒬"},{a:"qscr",b:"𝓆"},{a:"quaternions",b:"ℍ"},{a:"quatint",b:"⨖"},{a:"quest",b:"?"},{a:"questeq",b:"≟"},{a:"quot",b:'"'},{a:"QUOT",b:'"'},{a:"rAarr",b:"⇛"},{a:"race",b:"∽̱"},{a:"Racute",b:"Ŕ"},{a:"racute",b:"ŕ"},{a:"radic",b:"√"},{a:"raemptyv",b:"⦳"},{a:"rang",b:"⟩"},{a:"Rang",b:"⟫"},{a:"rangd",b:"⦒"},{a:"range",b:"⦥"},{a:"rangle",b:"⟩"},{a:"raquo",b:"»"},{a:"rarrap",b:"⥵"},{a:"rarrb",b:"⇥"},{a:"rarrbfs",b:"⤠"},{a:"rarrc",b:"⤳"},{a:"rarr",b:"→"},{a:"Rarr",b:"↠"},{a:"rArr",b:"⇒"},{a:"rarrfs",b:"⤞"},{a:"rarrhk",b:"↪"},{a:"rarrlp",b:"↬"},{a:"rarrpl",b:"⥅"},{a:"rarrsim",b:"⥴"},{a:"Rarrtl",b:"⤖"},{a:"rarrtl",b:"↣"},{a:"rarrw",b:"↝"},{a:"ratail",b:"⤚"},{a:"rAtail",b:"⤜"},{a:"ratio",b:"∶"},{a:"rationals",b:"ℚ"},{a:"rbarr",b:"⤍"},{a:"rBarr",b:"⤏"},{a:"RBarr",b:"⤐"},{a:"rbbrk",b:"❳"},{a:"rbrace",b:"}"},{a:"rbrack",b:"]"},{a:"rbrke",b:"⦌"},{a:"rbrksld",b:"⦎"},{a:"rbrkslu",b:"⦐"},{a:"Rcaron",b:"Ř"},{a:"rcaron",b:"ř"},{a:"Rcedil",b:"Ŗ"},{a:"rcedil",b:"ŗ"},{a:"rceil",b:"⌉"},{a:"rcub",b:"}"},{a:"Rcy",b:"Р"},{a:"rcy",b:"р"},{a:"rdca",b:"⤷"},{a:"rdldhar",b:"⥩"},{a:"rdquo",b:"”"},{a:"rdquor",b:"”"},{a:"rdsh",b:"↳"},{a:"real",b:"ℜ"},{a:"realine",b:"ℛ"},{a:"realpart",b:"ℜ"},{a:"reals",b:"ℝ"},{a:"Re",b:"ℜ"},{a:"rect",b:"▭"},{a:"reg",b:"®"},{a:"REG",b:"®"},{a:"ReverseElement",b:"∋"},{a:"ReverseEquilibrium",b:"⇋"},{a:"ReverseUpEquilibrium",b:"⥯"},{a:"rfisht",b:"⥽"},{a:"rfloor",b:"⌋"},{a:"rfr",b:"𝔯"},{a:"Rfr",b:"ℜ"},{a:"rHar",b:"⥤"},{a:"rhard",b:"⇁"},{a:"rharu",b:"⇀"},{a:"rharul",b:"⥬"},{a:"Rho",b:"Ρ"},{a:"rho",b:"ρ"},{a:"rhov",b:"ϱ"},{a:"RightAngleBracket",b:"⟩"},{a:"RightArrowBar",b:"⇥"},{a:"rightarrow",b:"→"},{a:"RightArrow",b:"→"},{a:"Rightarrow",b:"⇒"},{a:"RightArrowLeftArrow",b:"⇄"},{a:"rightarrowtail",b:"↣"},{a:"RightCeiling",b:"⌉"},{a:"RightDoubleBracket",b:"⟧"},{a:"RightDownTeeVector",b:"⥝"},{a:"RightDownVectorBar",b:"⥕"},{a:"RightDownVector",b:"⇂"},{a:"RightFloor",b:"⌋"},{a:"rightharpoondown",b:"⇁"},{a:"rightharpoonup",b:"⇀"},{a:"rightleftarrows",b:"⇄"},{a:"rightleftharpoons",b:"⇌"},{a:"rightrightarrows",b:"⇉"},{a:"rightsquigarrow",b:"↝"},{a:"RightTeeArrow",b:"↦"},{a:"RightTee",b:"⊢"},{a:"RightTeeVector",b:"⥛"},{a:"rightthreetimes",b:"⋌"},{a:"RightTriangleBar",b:"⧐"},{a:"RightTriangle",b:"⊳"},{a:"RightTriangleEqual",b:"⊵"},{a:"RightUpDownVector",b:"⥏"},{a:"RightUpTeeVector",b:"⥜"},{a:"RightUpVectorBar",b:"⥔"},{a:"RightUpVector",b:"↾"},{a:"RightVectorBar",b:"⥓"},{a:"RightVector",b:"⇀"},{a:"ring",b:"˚"},{a:"risingdotseq",b:"≓"},{a:"rlarr",b:"⇄"},{a:"rlhar",b:"⇌"},{a:"rlm",b:"‏"},{a:"rmoustache",b:"⎱"},{a:"rmoust",b:"⎱"},{a:"rnmid",b:"⫮"},{a:"roang",b:"⟭"},{a:"roarr",b:"⇾"},{a:"robrk",b:"⟧"},{a:"ropar",b:"⦆"},{a:"ropf",b:"𝕣"},{a:"Ropf",b:"ℝ"},{a:"roplus",b:"⨮"},{a:"rotimes",b:"⨵"},{a:"RoundImplies",b:"⥰"},{a:"rpar",b:")"},{a:"rpargt",b:"⦔"},{a:"rppolint",b:"⨒"},{a:"rrarr",b:"⇉"},{a:"Rrightarrow",b:"⇛"},{a:"rsaquo",b:"›"},{a:"rscr",b:"𝓇"},{a:"Rscr",b:"ℛ"},{a:"rsh",b:"↱"},{a:"Rsh",b:"↱"},{a:"rsqb",b:"]"},{a:"rsquo",b:"’"},{a:"rsquor",b:"’"},{a:"rthree",b:"⋌"},{a:"rtimes",b:"⋊"},{a:"rtri",b:"▹"},{a:"rtrie",b:"⊵"},{a:"rtrif",b:"▸"},{a:"rtriltri",b:"⧎"},{a:"RuleDelayed",b:"⧴"},{a:"ruluhar",b:"⥨"},{a:"rx",b:"℞"},{a:"Sacute",b:"Ś"},{a:"sacute",b:"ś"},{a:"sbquo",b:"‚"},{a:"scap",b:"⪸"},{a:"Scaron",b:"Š"},{a:"scaron",b:"š"},{a:"Sc",b:"⪼"},{a:"sc",b:"≻"},{a:"sccue",b:"≽"},{a:"sce",b:"⪰"},{a:"scE",b:"⪴"},{a:"Scedil",b:"Ş"},{a:"scedil",b:"ş"},{a:"Scirc",b:"Ŝ"},{a:"scirc",b:"ŝ"},{a:"scnap",b:"⪺"},{a:"scnE",b:"⪶"},{a:"scnsim",b:"⋩"},{a:"scpolint",b:"⨓"},{a:"scsim",b:"≿"},{a:"Scy",b:"С"},{a:"scy",b:"с"},{a:"sdotb",b:"⊡"},{a:"sdot",b:"⋅"},{a:"sdote",b:"⩦"},{a:"searhk",b:"⤥"},{a:"searr",b:"↘"},{a:"seArr",b:"⇘"},{a:"searrow",b:"↘"},{a:"sect",b:"§"},{a:"semi",b:";"},{a:"seswar",b:"⤩"},{a:"setminus",b:"∖"},{a:"setmn",b:"∖"},{a:"sext",b:"✶"},{a:"Sfr",b:"𝔖"},{a:"sfr",b:"𝔰"},{a:"sfrown",b:"⌢"},{a:"sharp",b:"♯"},{a:"SHCHcy",b:"Щ"},{a:"shchcy",b:"щ"},{a:"SHcy",b:"Ш"},{a:"shcy",b:"ш"},{a:"ShortDownArrow",b:"↓"},{a:"ShortLeftArrow",b:"←"},{a:"shortmid",b:"∣"},{a:"shortparallel",b:"∥"},{a:"ShortRightArrow",b:"→"},{a:"ShortUpArrow",b:"↑"},{a:"shy",b:"­"},{a:"Sigma",b:"Σ"},{a:"sigma",b:"σ"},{a:"sigmaf",b:"ς"},{a:"sigmav",b:"ς"},{a:"sim",b:"∼"},{a:"simdot",b:"⩪"},{a:"sime",b:"≃"},{a:"simeq",b:"≃"},{a:"simg",b:"⪞"},{a:"simgE",b:"⪠"},{a:"siml",b:"⪝"},{a:"simlE",b:"⪟"},{a:"simne",b:"≆"},{a:"simplus",b:"⨤"},{a:"simrarr",b:"⥲"},{a:"slarr",b:"←"},{a:"SmallCircle",b:"∘"},{a:"smallsetminus",b:"∖"},{a:"smashp",b:"⨳"},{a:"smeparsl",b:"⧤"},{a:"smid",b:"∣"},{a:"smile",b:"⌣"},{a:"smt",b:"⪪"},{a:"smte",b:"⪬"},{a:"smtes",b:"⪬︀"},{a:"SOFTcy",b:"Ь"},{a:"softcy",b:"ь"},{a:"solbar",b:"⌿"},{a:"solb",b:"⧄"},{a:"sol",b:"/"},{a:"Sopf",b:"𝕊"},{a:"sopf",b:"𝕤"},{a:"spades",b:"♠"},{a:"spadesuit",b:"♠"},{a:"spar",b:"∥"},{a:"sqcap",b:"⊓"},{a:"sqcaps",b:"⊓︀"},{a:"sqcup",b:"⊔"},{a:"sqcups",b:"⊔︀"},{a:"Sqrt",b:"√"},{a:"sqsub",b:"⊏"},{a:"sqsube",b:"⊑"},{a:"sqsubset",b:"⊏"},{a:"sqsubseteq",b:"⊑"},{a:"sqsup",b:"⊐"},{a:"sqsupe",b:"⊒"},{a:"sqsupset",b:"⊐"},{a:"sqsupseteq",b:"⊒"},{a:"square",b:"□"},{a:"Square",b:"□"},{a:"SquareIntersection",b:"⊓"},{a:"SquareSubset",b:"⊏"},{a:"SquareSubsetEqual",b:"⊑"},{a:"SquareSuperset",b:"⊐"},{a:"SquareSupersetEqual",b:"⊒"},{a:"SquareUnion",b:"⊔"},{a:"squarf",b:"▪"},{a:"squ",b:"□"},{a:"squf",b:"▪"},{a:"srarr",b:"→"},{a:"Sscr",b:"𝒮"},{a:"sscr",b:"𝓈"},{a:"ssetmn",b:"∖"},{a:"ssmile",b:"⌣"},{a:"sstarf",b:"⋆"},{a:"Star",b:"⋆"},{a:"star",b:"☆"},{a:"starf",b:"★"},{a:"straightepsilon",b:"ϵ"},{a:"straightphi",b:"ϕ"},{a:"strns",b:"¯"},{a:"sub",b:"⊂"},{a:"Sub",b:"⋐"},{a:"subdot",b:"⪽"},{a:"subE",b:"⫅"},{a:"sube",b:"⊆"},{a:"subedot",b:"⫃"},{a:"submult",b:"⫁"},{a:"subnE",b:"⫋"},{a:"subne",b:"⊊"},{a:"subplus",b:"⪿"},{a:"subrarr",b:"⥹"},{a:"subset",b:"⊂"},{a:"Subset",b:"⋐"},{a:"subseteq",b:"⊆"},{a:"subseteqq",b:"⫅"},{a:"SubsetEqual",b:"⊆"},{a:"subsetneq",b:"⊊"},{a:"subsetneqq",b:"⫋"},{a:"subsim",b:"⫇"},{a:"subsub",b:"⫕"},{a:"subsup",b:"⫓"},{a:"succapprox",b:"⪸"},{a:"succ",b:"≻"},{a:"succcurlyeq",b:"≽"},{a:"Succeeds",b:"≻"},{a:"SucceedsEqual",b:"⪰"},{a:"SucceedsSlantEqual",b:"≽"},{a:"SucceedsTilde",b:"≿"},{a:"succeq",b:"⪰"},{a:"succnapprox",b:"⪺"},{a:"succneqq",b:"⪶"},{a:"succnsim",b:"⋩"},{a:"succsim",b:"≿"},{a:"SuchThat",b:"∋"},{a:"sum",b:"∑"},{a:"Sum",b:"∑"},{a:"sung",b:"♪"},{a:"sup1",b:"¹"},{a:"sup2",b:"²"},{a:"sup3",b:"³"},{a:"sup",b:"⊃"},{a:"Sup",b:"⋑"},{a:"supdot",b:"⪾"},{a:"supdsub",b:"⫘"},{a:"supE",b:"⫆"},{a:"supe",b:"⊇"},{a:"supedot",b:"⫄"},{a:"Superset",b:"⊃"},{a:"SupersetEqual",b:"⊇"},{a:"suphsol",b:"⟉"},{a:"suphsub",b:"⫗"},{a:"suplarr",b:"⥻"},{a:"supmult",b:"⫂"},{a:"supnE",b:"⫌"},{a:"supne",b:"⊋"},{a:"supplus",b:"⫀"},{a:"supset",b:"⊃"},{a:"Supset",b:"⋑"},{a:"supseteq",b:"⊇"},{a:"supseteqq",b:"⫆"},{a:"supsetneq",b:"⊋"},{a:"supsetneqq",b:"⫌"},{a:"supsim",b:"⫈"},{a:"supsub",b:"⫔"},{a:"supsup",b:"⫖"},{a:"swarhk",b:"⤦"},{a:"swarr",b:"↙"},{a:"swArr",b:"⇙"},{a:"swarrow",b:"↙"},{a:"swnwar",b:"⤪"},{a:"szlig",b:"ß"},{a:"Tab",b:"\t"},{a:"target",b:"⌖"},{a:"Tau",b:"Τ"},{a:"tau",b:"τ"},{a:"tbrk",b:"⎴"},{a:"Tcaron",b:"Ť"},{a:"tcaron",b:"ť"},{a:"Tcedil",b:"Ţ"},{a:"tcedil",b:"ţ"},{a:"Tcy",b:"Т"},{a:"tcy",b:"т"},{a:"tdot",b:"⃛"},{a:"telrec",b:"⌕"},{a:"Tfr",b:"𝔗"},{a:"tfr",b:"𝔱"},{a:"there4",b:"∴"},{a:"therefore",b:"∴"},{a:"Therefore",b:"∴"},{a:"Theta",b:"Θ"},{a:"theta",b:"θ"},{a:"thetasym",b:"ϑ"},{a:"thetav",b:"ϑ"},{a:"thickapprox",b:"≈"},{a:"thicksim",b:"∼"},{a:"ThickSpace",b:"  "},{a:"ThinSpace",b:" "},{a:"thinsp",b:" "},{a:"thkap",b:"≈"},{a:"thksim",b:"∼"},{a:"THORN",b:"Þ"},{a:"thorn",b:"þ"},{a:"tilde",b:"˜"},{a:"Tilde",b:"∼"},{a:"TildeEqual",b:"≃"},{a:"TildeFullEqual",b:"≅"},{a:"TildeTilde",b:"≈"},{a:"timesbar",b:"⨱"},{a:"timesb",b:"⊠"},{a:"times",b:"×"},{a:"timesd",b:"⨰"},{a:"tint",b:"∭"},{a:"toea",b:"⤨"},{a:"topbot",b:"⌶"},{a:"topcir",b:"⫱"},{a:"top",b:"⊤"},{a:"Topf",b:"𝕋"},{a:"topf",b:"𝕥"},{a:"topfork",b:"⫚"},{a:"tosa",b:"⤩"},{a:"tprime",b:"‴"},{a:"trade",b:"™"},{a:"TRADE",b:"™"},{a:"triangle",b:"▵"},{a:"triangledown",b:"▿"},{a:"triangleleft",b:"◃"},{a:"trianglelefteq",b:"⊴"},{a:"triangleq",b:"≜"},{a:"triangleright",b:"▹"},{a:"trianglerighteq",b:"⊵"},{a:"tridot",b:"◬"},{a:"trie",b:"≜"},{a:"triminus",b:"⨺"},{a:"TripleDot",b:"⃛"},{a:"triplus",b:"⨹"},{a:"trisb",b:"⧍"},{a:"tritime",b:"⨻"},{a:"trpezium",b:"⏢"},{a:"Tscr",b:"𝒯"},{a:"tscr",b:"𝓉"},{a:"TScy",b:"Ц"},{a:"tscy",b:"ц"},{a:"TSHcy",b:"Ћ"},{a:"tshcy",b:"ћ"},{a:"Tstrok",b:"Ŧ"},{a:"tstrok",b:"ŧ"},{a:"twixt",b:"≬"},{a:"twoheadleftarrow",b:"↞"},{a:"twoheadrightarrow",b:"↠"},{a:"Uacute",b:"Ú"},{a:"uacute",b:"ú"},{a:"uarr",b:"↑"},{a:"Uarr",b:"↟"},{a:"uArr",b:"⇑"},{a:"Uarrocir",b:"⥉"},{a:"Ubrcy",b:"Ў"},{a:"ubrcy",b:"ў"},{a:"Ubreve",b:"Ŭ"},{a:"ubreve",b:"ŭ"},{a:"Ucirc",b:"Û"},{a:"ucirc",b:"û"},{a:"Ucy",b:"У"},{a:"ucy",b:"у"},{a:"udarr",b:"⇅"},{a:"Udblac",b:"Ű"},{a:"udblac",b:"ű"},{a:"udhar",b:"⥮"},{a:"ufisht",b:"⥾"},{a:"Ufr",b:"𝔘"},{a:"ufr",b:"𝔲"},{a:"Ugrave",b:"Ù"},{a:"ugrave",b:"ù"},{a:"uHar",b:"⥣"},{a:"uharl",b:"↿"},{a:"uharr",b:"↾"},{a:"uhblk",b:"▀"},{a:"ulcorn",b:"⌜"},{a:"ulcorner",b:"⌜"},{a:"ulcrop",b:"⌏"},{a:"ultri",b:"◸"},{a:"Umacr",b:"Ū"},{a:"umacr",b:"ū"},{a:"uml",b:"¨"},{a:"UnderBar",b:"_"},{a:"UnderBrace",b:"⏟"},{a:"UnderBracket",b:"⎵"},{a:"UnderParenthesis",b:"⏝"},{a:"Union",b:"⋃"},{a:"UnionPlus",b:"⊎"},{a:"Uogon",b:"Ų"},{a:"uogon",b:"ų"},{a:"Uopf",b:"𝕌"},{a:"uopf",b:"𝕦"},{a:"UpArrowBar",b:"⤒"},{a:"uparrow",b:"↑"},{a:"UpArrow",b:"↑"},{a:"Uparrow",b:"⇑"},{a:"UpArrowDownArrow",b:"⇅"},{a:"updownarrow",b:"↕"},{a:"UpDownArrow",b:"↕"},{a:"Updownarrow",b:"⇕"},{a:"UpEquilibrium",b:"⥮"},{a:"upharpoonleft",b:"↿"},{a:"upharpoonright",b:"↾"},{a:"uplus",b:"⊎"},{a:"UpperLeftArrow",b:"↖"},{a:"UpperRightArrow",b:"↗"},{a:"upsi",b:"υ"},{a:"Upsi",b:"ϒ"},{a:"upsih",b:"ϒ"},{a:"Upsilon",b:"Υ"},{a:"upsilon",b:"υ"},{a:"UpTeeArrow",b:"↥"},{a:"UpTee",b:"⊥"},{a:"upuparrows",b:"⇈"},{a:"urcorn",b:"⌝"},{a:"urcorner",b:"⌝"},{a:"urcrop",b:"⌎"},{a:"Uring",b:"Ů"},{a:"uring",b:"ů"},{a:"urtri",b:"◹"},{a:"Uscr",b:"𝒰"},{a:"uscr",b:"𝓊"},{a:"utdot",b:"⋰"},{a:"Utilde",b:"Ũ"},{a:"utilde",b:"ũ"},{a:"utri",b:"▵"},{a:"utrif",b:"▴"},{a:"uuarr",b:"⇈"},{a:"Uuml",b:"Ü"},{a:"uuml",b:"ü"},{a:"uwangle",b:"⦧"},{a:"vangrt",b:"⦜"},{a:"varepsilon",b:"ϵ"},{a:"varkappa",b:"ϰ"},{a:"varnothing",b:"∅"},{a:"varphi",b:"ϕ"},{a:"varpi",b:"ϖ"},{a:"varpropto",b:"∝"},{a:"varr",b:"↕"},{a:"vArr",b:"⇕"},{a:"varrho",b:"ϱ"},{a:"varsigma",b:"ς"},{a:"varsubsetneq",b:"⊊︀"},{a:"varsubsetneqq",b:"⫋︀"},{a:"varsupsetneq",b:"⊋︀"},{a:"varsupsetneqq",b:"⫌︀"},{a:"vartheta",b:"ϑ"},{a:"vartriangleleft",b:"⊲"},{a:"vartriangleright",b:"⊳"},{a:"vBar",b:"⫨"},{a:"Vbar",b:"⫫"},{a:"vBarv",b:"⫩"},{a:"Vcy",b:"В"},{a:"vcy",b:"в"},{a:"vdash",b:"⊢"},{a:"vDash",b:"⊨"},{a:"Vdash",b:"⊩"},{a:"VDash",b:"⊫"},{a:"Vdashl",b:"⫦"},{a:"veebar",b:"⊻"},{a:"vee",b:"∨"},{a:"Vee",b:"⋁"},{a:"veeeq",b:"≚"},{a:"vellip",b:"⋮"},{a:"verbar",b:"|"},{a:"Verbar",b:"‖"},{a:"vert",b:"|"},{a:"Vert",b:"‖"},{a:"VerticalBar",b:"∣"},{a:"VerticalLine",b:"|"},{a:"VerticalSeparator",b:"❘"},{a:"VerticalTilde",b:"≀"},{a:"VeryThinSpace",b:" "},{a:"Vfr",b:"𝔙"},{a:"vfr",b:"𝔳"},{a:"vltri",b:"⊲"},{a:"vnsub",b:"⊂⃒"},{a:"vnsup",b:"⊃⃒"},{a:"Vopf",b:"𝕍"},{a:"vopf",b:"𝕧"},{a:"vprop",b:"∝"},{a:"vrtri",b:"⊳"},{a:"Vscr",b:"𝒱"},{a:"vscr",b:"𝓋"},{a:"vsubnE",b:"⫋︀"},{a:"vsubne",b:"⊊︀"},{a:"vsupnE",b:"⫌︀"},{a:"vsupne",b:"⊋︀"},{a:"Vvdash",b:"⊪"},{a:"vzigzag",b:"⦚"},{a:"Wcirc",b:"Ŵ"},{a:"wcirc",b:"ŵ"},{a:"wedbar",b:"⩟"},{a:"wedge",b:"∧"},{a:"Wedge",b:"⋀"},{a:"wedgeq",b:"≙"},{a:"weierp",b:"℘"},{a:"Wfr",b:"𝔚"},{a:"wfr",b:"𝔴"},{a:"Wopf",b:"𝕎"},{a:"wopf",b:"𝕨"},{a:"wp",b:"℘"},{a:"wr",b:"≀"},{a:"wreath",b:"≀"},{a:"Wscr",b:"𝒲"},{a:"wscr",b:"𝓌"},{a:"xcap",b:"⋂"},{a:"xcirc",b:"◯"},{a:"xcup",b:"⋃"},{a:"xdtri",b:"▽"},{a:"Xfr",b:"𝔛"},{a:"xfr",b:"𝔵"},{a:"xharr",b:"⟷"},{a:"xhArr",b:"⟺"},{a:"Xi",b:"Ξ"},{a:"xi",b:"ξ"},{a:"xlarr",b:"⟵"},{a:"xlArr",b:"⟸"},{a:"xmap",b:"⟼"},{a:"xnis",b:"⋻"},{a:"xodot",b:"⨀"},{a:"Xopf",b:"𝕏"},{a:"xopf",b:"𝕩"},{a:"xoplus",b:"⨁"},{a:"xotime",b:"⨂"},{a:"xrarr",b:"⟶"},{a:"xrArr",b:"⟹"},{a:"Xscr",b:"𝒳"},{a:"xscr",b:"𝓍"},{a:"xsqcup",b:"⨆"},{a:"xuplus",b:"⨄"},{a:"xutri",b:"△"},{a:"xvee",b:"⋁"},{a:"xwedge",b:"⋀"},{a:"Yacute",b:"Ý"},{a:"yacute",b:"ý"},{a:"YAcy",b:"Я"},{a:"yacy",b:"я"},{a:"Ycirc",b:"Ŷ"},{a:"ycirc",b:"ŷ"},{a:"Ycy",b:"Ы"},{a:"ycy",b:"ы"},{a:"yen",b:"¥"},{a:"Yfr",b:"𝔜"},{a:"yfr",b:"𝔶"},{a:"YIcy",b:"Ї"},{a:"yicy",b:"ї"},{a:"Yopf",b:"𝕐"},{a:"yopf",b:"𝕪"},{a:"Yscr",b:"𝒴"},{a:"yscr",b:"𝓎"},{a:"YUcy",b:"Ю"},{a:"yucy",b:"ю"},{a:"yuml",b:"ÿ"},{a:"Yuml",b:"Ÿ"},{a:"Zacute",b:"Ź"},{a:"zacute",b:"ź"},{a:"Zcaron",b:"Ž"},{a:"zcaron",b:"ž"},{a:"Zcy",b:"З"},{a:"zcy",b:"з"},{a:"Zdot",b:"Ż"},{a:"zdot",b:"ż"},{a:"zeetrf",b:"ℨ"},{a:"ZeroWidthSpace",b:"​"},{a:"Zeta",b:"Ζ"},{a:"zeta",b:"ζ"},{a:"zfr",b:"𝔷"},{a:"Zfr",b:"ℨ"},{a:"ZHcy",b:"Ж"},{a:"zhcy",b:"ж"},{a:"zigrarr",b:"⇝"},{a:"zopf",b:"𝕫"},{a:"Zopf",b:"ℤ"},{a:"Zscr",b:"𝒵"},{a:"zscr",b:"𝓏"},{a:"zwj",b:"‍"},{a:"zwnj",b:"‌"}]),u(re,l(function(a,n){return u(Ye,a.a,a.b,n)}),Fe,Jr)),cn=i(no,function(a){return i(Jn,"&"+a+";",i(ze,a,so))},Ot(Hr(Tn))),E=function(a){return a<0||1114111"!==a&&"`"!==a&&"&"!==a})),$o])))),yn=It(N([i(Bt,i(Wt,i(Wt,Ft(Rn),St(se("="))),Rt(jt)),It(N([yn,_('"'),_("'")]))),Ft("")])),_=i(Bt,i(Bt,Ft(ht),i(Wt,O,Rt(jt))),i(Wt,yn,Rt(jt))),To=oo(_),Ao=i(no,Pt,Ot(i(Wt,St(An),Rt(function(a){return An(a)||"-"===a})))),So=i(no,i(je,De(""),function(a){return{$:0,a:a}}),i(ko,"text element",It(N([Ot(Hr(function(a){return"<"!==a&&"&"!==a})),$o]))));function Do(){return It(N([So,Yt,Ro()]))}function Ro(){return i(vt,function(a){var n=a.a,a=a.b;return ao(n)?i(Wt,i(Wt,Ft(u(_t,n,a,D)),It(N([St(se("/")),Ft(0)]))),St(se(">"))):i(Bt,i(Wt,Ft(i(_t,n,a)),St(se(">"))),i(Wt,oo(wt(Do())),Vt(n)))},i(Bt,i(Bt,i(Wt,Ft(ht),St(se("<"))),i(Wt,Ao,Rt(jt))),To))}var Lo=Do();Do=function(){return Lo};var No=Ro();Ro=function(){return No};function Wo(a){return Ba(Pa(a))}function Bo(a){return i(ts,a.a,a.b)}function Co(a){return i(at,os,a)}function Uo(a){return(a=""===(a=a)?ge(D):i(yt,i(ko,"node",Lo),a)).$?D:Co(a.a)}function Io(a){return i(bs,"click",rt(a))}function Fo(a){return i(fs,N([{$:4,a:N([Io({$:2,a:a})])},ds]),N([rs(a.s),i(ss,D,D),i(ls,D,Uo(a.W))]))}function _o(a){return i(fs,N([{$:4,a:N([Io({$:1,a:a})])},ds]),N([rs(a.s),i(ss,D,D),i(ls,D,Uo(a.W))]))}function Po(a){return i(st,"href",/^javascript:/i.test((a=a).replace(/\s/g,""))?"":a)}function Go(a){return a.b}function Ho(a){return lt(i(De," ",i(at,En,i(ws,Go,a))))}function Oo(a){switch(a){case 0:return"primary";case 1:return"secondary";case 2:return"success";case 3:return"info";case 4:return"warning";case 5:return"danger";case 6:return"dark";case 7:return"light";default:return"link"}}function jo(a){return Ca(Pa(a))}function Vo(a){var n=a.cG;return lt("col"+(i(Jn,"",i(ct,function(a){return"-"+a},Cr(a.dk)))+i(Jn,"",i(ct,function(a){return"-"+a},function(a){switch(a){case 0:return Mn;case 1:return zn("1");case 2:return zn("2");case 3:return zn("3");case 4:return zn("4");case 5:return zn("5");case 6:return zn("6");case 7:return zn("7");case 8:return zn("8");case 9:return zn("9");case 10:return zn("10");case 11:return zn("11");case 12:return zn("12");default:return zn("auto")}}(n)))))}function zo(a){return(a=Cr(a)).$?"-":"-"+a.a+"-"}function Mo(a){var n=a.c2;return lt("offset"+(zo(a.dk)+function(a){switch(a){case 0:return"0";case 1:return"1";case 2:return"2";case 3:return"3";case 4:return"4";case 5:return"5";case 6:return"6";case 7:return"7";case 8:return"8";case 9:return"9";case 10:return"10";default:return"11"}}(n)))}function Jo(a){switch(a){case 0:return"0";case 1:return"1";case 2:return"2";case 3:return"3";case 4:return"4";case 5:return"5";case 6:return"6";case 7:return"7";case 8:return"8";case 9:return"9";case 10:return"10";case 11:return"11";default:return"12"}}function Ko(a){var n=u(re,Ms,Js,a),a=!Sn(i(Sr,Rn,N([n.aK,n.aI,n.aH,n.aG,n.aJ])));return S((a=N([a?zn(i(Ps,0,0)):n.aK,n.aI,n.aH,n.aG,n.aJ]),i(Sr,Rn,i(at,function(a){return i(ct,Vo,a)},a))),S((a=N([n.br,n.bo,n.bn,n.bm,n.bq]),i(Sr,Rn,i(at,function(a){return i(ct,Mo,a)},a))),S((a=N([n.bH,n.bF,n.bE,n.bD,n.bG]),i(Sr,Rn,i(at,function(a){if(a.$)return Mn;var n=a.a.ad;return zn(lt("pull"+(zo(a.a.dk)+Jo(n))))},a))),S((a=N([n.bM,n.bK,n.bJ,n.bI,n.bL]),i(Sr,Rn,i(at,function(a){if(a.$)return Mn;var n=a.a.ad;return zn(lt("push"+(zo(a.a.dk)+Jo(n))))},a))),S((a=N([n.bB,n.bz,n.by,n.bx,n.bA]),i(Sr,Rn,i(at,function(a){if(a.$)return Mn;var n=a.a.ad;return zn(lt("order"+(zo(a.a.dk)+function(a){switch(a){case 0:return"first";case 1:return"1";case 2:return"2";case 3:return"3";case 4:return"4";case 5:return"5";case 6:return"6";case 7:return"7";case 8:return"8";case 9:return"9";case 10:return"10";case 11:return"11";case 12:return"12";default:return"last"}}(n))))},a))),S(i(Ys,"align-self-",N([n.aT,n.aR,n.aQ,n.aP,n.aS])),S((a=n.bV).$?D:N([Ur(a.a)]),n.aW)))))))}function Yo(a){switch(a.$){case 0:var n=a.a.cE;return i(ot,Ko(a.a.ck),n);case 1:return a.a;default:n=a.a.cE;return u(jo,"div",Ko(a.a.ck),n)}}function Xo(a){var n=a.cA;return lt("justify-content-"+(i(Jn,"",i(ct,function(a){return a+"-"},Cr(a.dk)))+function(a){switch(a){case 0:return"start";case 1:return"center";case 2:return"end";case 3:return"around";default:return"between"}}(n)))}function Zo(a){var n=u(re,Qs,al,a);return S(N([lt("row")]),S(i(Ys,"align-items-",N([n.b1,n.b$,n.b_,n.bZ,n.b0])),S((a=N([n.bd,n.bb,n.ba,n.a9,n.bc]),i(Sr,Rn,i(at,function(a){return i(ct,Xo,a)},a))),n.aW)))}function Qo(a){var n=a.cE;return i(a.cW,function(a){return S(N([Ho(N([{a:"list-group-item",b:!0},{a:"disabled",b:a.a7},{a:"active",b:a.aN},{a:"list-group-item-action",b:a.aM}]))]),S(N([xs(a.a7)]),S(i(Jn,D,i(ct,function(a){return N([i(it,"list-group-item",a)])},a.bO)),a.aW)))}(u(re,el,rl,a.ck)),n)}function as(a){return i(ot,N([lt("list-group")]),i(at,Qo,a))}function ns(a){return{cc:a.cc,cg:a.cg,cl:a.cl}}function es(n){return S(N([Ho(N([{a:"nav",b:!0},{a:"nav-tabs",b:!n.ab},{a:"nav-pills",b:n.ab}]))]),S(function(){var a=n.ac;if(a.$)return D;switch(a.a){case 3:return N([lt("nav-justified")]);case 2:return N([lt("nav-fill")]);case 0:return N([lt("justify-content-center")]);default:return N([lt("justify-content-end")])}}(),n.aW))}var rs=Wa,ts=l(function(a,n){return i(_a,function(a){return/^(on|formAction$)/i.test(a)?"data-"+a:a}(a),function(a){return/^\s*(javascript:|data:text\/html)/i.test(a)?"":a}(n))}),os=function(a){switch(a.$){case 1:var n=a.c;return u(Wo,a.a,i(at,Bo,a.b),Co(n));case 0:return rs(a.a);default:return rs("")}},ss=Ba("p"),ls=Ba("span"),cs=Ua,bs=l(function(a,n){return i(cs,a,{$:0,a:n})}),ps=Ba("button"),is=st("type"),us={$:3},fs=l(function(a,n){return{cE:n,cW:ps,ck:i(Pn,us,S(a,N([{$:4,a:N([is("button")])}])))}}),ds={$:0,a:3},ys=l(function(a,n){return!i(ze,a,n).$}),hs=l(function(a,n){return i(ys,a,n)}),gs=Ba("a"),ms=Ia,vs=l(function(a,n){return n.$?Mn:a(n.a)}),ws=l(function(e,a){return u(Tr,l(function(a,n){return e(a)?i(Pn,a,n):n}),D,a)}),qs=sa,xs=l(function(a,n){return i(Fa,a,qs(n))})("disabled"),Es=l(function(a,n){switch(a.$){case 0:return A(n,{cr:zn(a.a)});case 1:return A(n,{A:zn(a.a)});case 2:return A(n,{aZ:!0});case 3:return A(n,{a7:a.a});default:return A(n,{aW:S(n.aW,a.a)})}}),$s={aW:D,aZ:!1,A:Mn,a7:!1,cr:Mn},ks=l(function(a,n){return i(ps,function(a){var n=u(re,Es,$s,a);return S(N([Ho(N([{a:"btn",b:!0},{a:"btn-block",b:n.aZ},{a:"disabled",b:n.a7}])),xs(n.a7)]),S((a=i(vs,Cr,n.cr)).$?D:N([lt("btn-"+a.a)]),S((a=n.A).$?D:N(a.a.$?[lt("btn-outline-"+Oo(a.a.a))]:[lt("btn-"+Oo(a.a.a))]),n.aW)))}(a),n)}),Ts={$:1,a:{$:0,a:5}},As={$:1,a:{$:0,a:2}},Ss=l(function(a,n){switch(a.$){case 0:return A(n,{y:zn(a.a)});case 1:return A(n,{A:zn(a.a)});case 2:return A(n,{E:zn(a.a)});default:return A(n,{aW:S(n.aW,a.a)})}}),Ds={y:Mn,aW:D,A:Mn,E:Mn},Rs=l(function(a,n){return{$:0,a:i(ot,function(a){var n=u(re,Ss,Ds,a);return S(N([lt("card-body")]),S((a=n.y).$?D:N([Ur(a.a)]),S((a=n.A).$?D:N([i(it,"bg",a.a)]),S((a=n.E).$?D:N([Ir(a.a)]),n.aW))))}(a),i(at,function(a){return a},n))}}),Ls=e(function(a,n,e){return A(e,{b6:S(e.b6,N([i(Rs,a,n)]))})}),Ns=e(function(a,n,e){return A(e,{ca:zn(i(ot,i(Pn,lt("card-footer"),a),n))})}),O=Ba("h3"),Ws=r(function(a,n,e,r){return A(r,{bf:zn(i(a,i(Pn,lt("card-header"),n),e))})})(O),Bs=l(function(a,n){return i(ss,S(N([lt("card-text")]),a),n)}),Cs=l(function(a,n){return u(Ns,D,N([i(hs,n.s,a.G)?i(ks,N([Ts,{$:4,a:N([Io({$:4,a:n})])}]),N([rs("Remove")])):i(ks,N([As,{$:4,a:N([Io({$:3,a:n})])}]),N([rs("Add")]))]),u(Ls,D,N([i(Bs,D,Uo(n.dI))]),u(Ws,D,N([i(gs,N([Po(n.dy)]),N([rs(n.s)]))]),(n=N([{$:3,a:N([i(ms,"min-width","20em"),i(ms,"max-width","20em"),i(ms,"padding","0.25em"),i(ms,"margin","0.5em")])}]),{b6:D,ca:Mn,bf:Mn,cd:Mn,ce:Mn,ck:n}))))}),Us=N([{dI:"

Adds abbrevs dictionary to hold user-defined command abbreviations. The dictionary is searched as you type and the matching words are replaced at the command line by the corresponding dictionary contents once you hit 'Space' or 'Return' key. For instance a frequently used command such as git status can be abbreviated to gst as follows:

\n
$ xontrib load abbrevs\n$ abbrevs['gst'] = 'git status'\n$ gst # Once you hit <space> or <return>, 'gst' gets expanded to 'git status'.
",c:"BSD 3-clause",s:"abbrevs",dy:"http://xon.sh"},{dI:"

Adds tabcomplete functionality to apt-get/apt-cache inside of xonsh.

",c:"BSD 2-clause",s:"apt_tabcomplete",dy:"https://github.com/DangerOnTheRanger/xonsh-apt-tabcomplete"},{dI:"

Argcomplete support to tab completion of python and xonsh scripts in xonsh.

",c:"",s:"argcomplete",dy:"https://github.com/anki-code/xontrib-argcomplete"},{dI:"

autojump support for xonsh

",c:"",s:"autojump",dy:"https://github.com/sagartewari01/autojump-xonsh"},{dI:"

Manages automatic activation of virtual environments.

",c:"BSD 3-clause",s:"autovox",dy:"http://xon.sh"},{dI:'

Adds automatic execution of xonsh script files called .autoxsh when enterting a directory with cd function

',c:"GPLv3",s:"autoxsh",dy:"https://github.com/Granitas/xonsh-autoxsh"},{dI:"

Automatic (de)activation of virtual environments as you cd around

",c:"GPLv3",s:"avox",dy:"https://github.com/AstraLuma/xontrib-avox"},{dI:'

Enables additional Bash-like syntax while at the command prompt. For example, the !! syntax for running the previous command is now usable. Note that these features are implemented as precommand events and these additions do not affect the xonsh language when run as script. That said, you might find them useful if you have strong muscle memory.

\n

Warning: This xontrib may modify user command line input to implement its behavior. To see the modifications as they are applied (in unified diffformat), please set $XONSH_DEBUG to 2 or higher.

\n

The xontrib also adds commands: alias, export, unset, set, shopt, complete.

',c:"BSD 3-clause",s:"bashisms",dy:"http://xon.sh"},{dI:"

Change base16 shell themes

",c:"",s:"base16_shell",dy:"https://github.com/ErickTucto/xontrib-base16-shell"},{dI:'

Additional core utilities that are implemented in xonsh. The current list includes:

\n
    \n
  • cat

  • \n
  • echo

  • \n
  • pwd

  • \n
  • tee

  • \n
  • tty

  • \n
  • yes

  • \n
\n

In many cases, these may have a lower performance overhead than the posix command line utility with the same name. This is because these tools avoid the need for a full subprocess call. Additionally, these tools are cross-platform.

',c:"BSD 3-clause",s:"coreutils",dy:"http://xon.sh"},{dI:"

Supports direnv.

",c:"MIT",s:"direnv",dy:"https://github.com/74th/xonsh-direnv"},{dI:"

Move through directory history with nextd and prevd also with keybindings.

",c:"",s:"hist_navigator",dy:"https://github.com/jnoortheen/xontrib-hist-navigator"},{dI:"

The distributed parallel computing library hooks for xonsh. Importantly this provides a substitute 'dworker' command which enables distributed workers to have access to xonsh builtins.

\n

Furthermore, this xontrib adds a 'DSubmitter' context manager for executing a block remotely. Moreover, this also adds a convenience function 'dsubmit()' for creating DSubmitter and Executor instances at the same time. Thus users may submit distributed jobs with:

\n
with dsubmit('127.0.0.1:8786', rtn='x') as dsub:\n    x = $(echo I am elsewhere)\n\nres = dsub.future.result()\nprint(res)
\n

This is useful for long running or non-blocking jobs.

",c:"BSD 3-clause",s:"distributed",dy:"http://xon.sh"},{dI:"

Adds tabcomplete functionality to docker inside of xonsh.

",c:"MIT",s:"docker_tabcomplete",dy:"https://github.com/xsteadfastx/xonsh-docker-tabcomplete"},{dI:'

Windows only xontrib, to release the lock on the current directory whenever the prompt is shown. Enabling this will allow the other programs or Windows Explorer to delete or rename the current or parent directories. Internally, it is accomplished by temporarily resetting CWD to the root drive folder while waiting at the prompt. This only works with the prompt_toolkit backend and can cause cause issues if any extensions are enabled that hook the prompt and relies on os.getcwd()

',c:"BSD 3-clause",s:"free_cwd",dy:"http://xon.sh"},{dI:"

Adds some fzf widgets to your xonsh shell.

",c:"GPLv3",s:"fzf-widgets",dy:"https://github.com/laloch/xontrib-fzf-widgets"},{dI:"

Useful aliases and shortcuts for extracting links and textfrom command output history and putting them into the clipboard.

",c:"GPLv3",s:"histcpy",dy:"https://github.com/con-f-use/xontrib-histcpy"},{dI:"

Use Jedi as xonsh's python completer.

",c:"BSD 3-clause",s:"jedi",dy:"http://xon.sh"},{dI:"

Xonsh hooks for the Kitty terminal emulator.

",c:"BSD-3-Clause",s:"kitty",dy:"https://github.com/scopatz/xontrib-kitty"},{dI:"

Matplotlib hooks for xonsh, including the new 'mpl' alias that displays the current figure on the screen.

",c:"BSD 3-clause",s:"mpl",dy:"http://xon.sh"},{dI:"

Get identifiers, names, paths, URLs and words from the previous command output and use them for the next command.

",c:"",s:"output_search",dy:"https://github.com/anki-code/xontrib-output-search"},{dI:"

When you click to a file or folder in graphical OS they will be opened in associated app.The xontrib-onepath brings the same logic for the xonsh shell. Type the filename or pathwithout preceding command and an associated action will be executed. The actions are customizable.

",c:"",s:"onepath",dy:"https://github.com/anki-code/xontrib-onepath"},{dI:"

Simple built-in debugger. Runs pdb on reception of SIGUSR1 signal.

",c:"BSD 3-clause",s:"pdb",dy:"http://xon.sh"},{dI:"

Let your pipe lines flow thru the Python code in xonsh.

",c:"",s:"pipeliner",dy:"https://github.com/anki-code/xontrib-pipeliner"},{dI:"

Powerline for Xonsh shell

",c:"MIT",s:"powerline",dy:"https://github.com/santagada/xontrib-powerline"},{dI:"

An elegance bar style for prompt.

",c:"",s:"prompt_bar",dy:"https://github.com/anki-code/xontrib-prompt-bar"},{dI:"

Uses powerline to render the xonsh prompt

",c:"MIT",s:"powerline-binding",dy:"https://github.com/dyuri/xontrib-powerline-binding"},{dI:"

Adds return code info to the prompt

",c:"BSD 3-clause",s:"prompt_ret_code",dy:"http://xon.sh"},{dI:"

vi-mode status formatter for xonsh prompt

",c:"MIT",s:"prompt_vi_mode",dy:"https://github.com/t184256/xontrib-prompt-vi-mode"},{dI:"

pyenv integration for xonsh.

",c:"MIT",s:"pyenv",dy:"https://github.com/dyuri/xontrib-pyenv"},{dI:"

Make traceback easier to see for xonsh.

",c:"MIT",s:"readable-traceback",dy:"https://github.com/6syun9/xontrib-readable-traceback"},{dI:"

Xonsh Task Scheduling

",c:"MIT",s:"schedule",dy:"https://github.com/AstraLuma/xontrib-schedule"},{dI:"

Adds tabcomplete functionality to scrapy inside of xonsh.

",c:"GPLv3",s:"scrapy_tabcomplete",dy:"https://github.com/Granitas/xonsh-scrapy-tabcomplete"},{dI:"

ssh-agent integration

",c:"MIT",s:"ssh_agent",dy:"https://github.com/dyuri/xontrib-ssh-agent"},{dI:"

Python virtual environment manager for xonsh.

",c:"BSD 3-clause",s:"vox",dy:"http://xon.sh"},{dI:"

Adds tabcomplete functionality to vox inside of xonsh.

",c:"GPLv3",s:"vox_tabcomplete",dy:"https://github.com/Granitosaurus/xonsh-vox-tabcomplete"},{dI:"

Jumping across whole words (non-whitespace) with Ctrl+Left/Right. Alt+Left/Right remains unmodified to jump over smaller word segments. Shift+Delete removes the whole word.

",c:"BSD 3-clause",s:"whole_word_jumping",dy:"http://xon.sh"},{dI:"

Adds an 'xo' alias to run the exofrills text editor in the current Python interpreter session. This shaves off a bit of the startup time when running your favorite, minimal text editor.

",c:"WTFPL",s:"xo",dy:"https://github.com/scopatz/xo"},{dI:"

Run/plot/explain sql query for PostgreSQL.

",c:"Apache",s:"xpg",dy:"https://github.com/fengttt/xsh/tree/master/py"},{dI:"

Tracks your most used directories, based on 'frecency'.

",c:"GPLv3",s:"z",dy:"https://github.com/AstraLuma/xontrib-z"}]),Is=Ba("h2"),Fs=l(function(a,n){return{$:0,a:{cE:n,ck:a}}}),_s=l(function(a,n){return i(ot,S(N([lt("container")]),a),n)}),Ps=l(function(a,n){return{cG:n,dk:a}}),Gs=l(function(a,n){switch(a.dk){case 0:return A(n,{aT:zn(a)});case 1:return A(n,{aR:zn(a)});case 2:return A(n,{aQ:zn(a)});case 3:return A(n,{aP:zn(a)});default:return A(n,{aS:zn(a)})}}),Hs=l(function(a,n){switch(a.dk){case 0:return A(n,{br:zn(a)});case 1:return A(n,{bo:zn(a)});case 2:return A(n,{bn:zn(a)});case 3:return A(n,{bm:zn(a)});default:return A(n,{bq:zn(a)})}}),Os=l(function(a,n){switch(a.dk){case 0:return A(n,{bB:zn(a)});case 1:return A(n,{bz:zn(a)});case 2:return A(n,{by:zn(a)});case 3:return A(n,{bx:zn(a)});default:return A(n,{bA:zn(a)})}}),js=l(function(a,n){switch(a.dk){case 0:return A(n,{bH:zn(a)});case 1:return A(n,{bF:zn(a)});case 2:return A(n,{bE:zn(a)});case 3:return A(n,{bD:zn(a)});default:return A(n,{bG:zn(a)})}}),Vs=l(function(a,n){switch(a.dk){case 0:return A(n,{bM:zn(a)});case 1:return A(n,{bK:zn(a)});case 2:return A(n,{bJ:zn(a)});case 3:return A(n,{bI:zn(a)});default:return A(n,{bL:zn(a)})}}),zs=l(function(a,n){switch(a.dk){case 0:return A(n,{aK:zn(a)});case 1:return A(n,{aI:zn(a)});case 2:return A(n,{aH:zn(a)});case 3:return A(n,{aG:zn(a)});default:return A(n,{aJ:zn(a)})}}),Ms=l(function(a,n){switch(a.$){case 6:return A(n,{aW:S(n.aW,a.a)});case 0:return i(zs,a.a,n);case 1:return i(Hs,a.a,n);case 2:return i(js,a.a,n);case 3:return i(Vs,a.a,n);case 4:return i(Os,a.a,n);case 5:return i(Gs,a.a,n);default:return A(n,{bV:zn(a.a)})}}),Js={aP:Mn,aQ:Mn,aR:Mn,aS:Mn,aT:Mn,aW:D,bm:Mn,bn:Mn,bo:Mn,bq:Mn,br:Mn,bx:Mn,by:Mn,bz:Mn,bA:Mn,bB:Mn,bD:Mn,bE:Mn,bF:Mn,bG:Mn,bH:Mn,bI:Mn,bJ:Mn,bK:Mn,bL:Mn,bM:Mn,bV:Mn,aG:Mn,aH:Mn,aI:Mn,aJ:Mn,aK:Mn},Ks=l(function(a,n){var e=n.cA;return lt(S(a,S(i(Jn,"",i(ct,function(a){return a+"-"},Cr(n.dk))),function(a){switch(a){case 0:return"start";case 1:return"center";default:return"end"}}(e))))}),Ys=l(function(n,a){return i(Sr,Rn,i(at,function(a){return i(ct,Ks(n),a)},a))}),Xs=l(function(a,n){switch(a.dk){case 0:return A(n,{bd:zn(a)});case 1:return A(n,{bb:zn(a)});case 2:return A(n,{ba:zn(a)});case 3:return A(n,{a9:zn(a)});default:return A(n,{bc:zn(a)})}}),Zs=l(function(a,n){switch(a.dk){case 0:return A(n,{b1:zn(a)});case 1:return A(n,{b$:zn(a)});case 2:return A(n,{b_:zn(a)});case 3:return A(n,{bZ:zn(a)});default:return A(n,{b0:zn(a)})}}),Qs=l(function(a,n){switch(a.$){case 2:return A(n,{aW:S(n.aW,a.a)});case 0:return i(Zs,a.a,n);default:return i(Xs,a.a,n)}}),al={aW:D,a9:Mn,ba:Mn,bb:Mn,bc:Mn,bd:Mn,bZ:Mn,b_:Mn,b$:Mn,b0:Mn,b1:Mn},nl=l(function(a,n){return i(ot,Zo(a),i(at,Yo,n))}),el=l(function(a,n){switch(a.$){case 0:return A(n,{bO:zn(a.a)});case 3:return A(n,{aM:!0});case 2:return A(n,{a7:!0});case 1:return A(n,{aN:!0});default:return A(n,{aW:S(n.aW,a.a)})}}),rl={aM:!1,aN:!1,aW:D,a7:!1,bO:Mn},tl=l(function(a,n){return A(n,{ac:zn(a)})})(0),ol=l(function(a,n){return A(n,{cX:a})}),sl=l(function(a,n){return{aW:a,cE:n}}),ll=l(function(a,n){return{aW:a,cE:n}}),cl=Ba("ul"),bl=l(function(a,n){a=a.aO;if(1===a.$)return xn(n.cX);var e=a.a;return(a=xn(i(ws,function(a){return w(a.cc,e)},n.cX))).$?xn(n.cX):zn(a.a)}),pl=Ba("li"),il=l(function(a,n){var e={a:a,b:n};a:for(;e.a;)switch(e.b){case 0:return 1;case 1:return 2;default:break a}return 2}),ul=r(function(a,n,e,r){var t=e.aW,o=e.cE,e=N([{a:"nav-link",b:!0},{a:"active",b:n}]),n=Io(r.cx({aO:zn(a),j:i(il,r.Q&&!n,0)})),o=r.bY?i(gs,S(N([Ho(e),n,Po("#"+a)]),t),o):i(ps,S(N([Ho(S(e,N([{a:"btn",b:!0},{a:"btn-link",b:!0}]))),n]),t),o);return i(pl,N([lt("nav-item")]),N([o]))}),fl=st("id"),dl=l(function(a){switch(a.j){case 0:return N([i(ms,"display","none")]);case 1:return N([i(ms,"display","block"),i(ms,"opacity","0")]);default:return S(N([i(ms,"display","block")]),N([i(ms,"opacity",Se(1)),i(ms,"-webkit-transition","opacity 0.15s linear"),i(ms,"-o-transition","opacity 0.15s linear"),i(ms,"transition","opacity 0.15s linear")]))}}),yl=t(function(a,n,e,r,t){var o=e.aW,e=e.cE,t=n?i(dl,r,t):N([i(ms,"display","none")]);return i(ot,S(N([fl(a),lt("tab-pane")]),S(t,o)),e)}),hl=l(function(n,a){var e=a,a=i(bl,n,e);if(1===a.$)return i(ot,D,N([i(cl,es(e),D),i(ot,N([lt("tab-content")]),D)]));var r=a.a;return i(ot,D,N([i(cl,es(e),i(at,function(a){return f(ul,a.cc,w(a.cc,r.cc),a.cg,e)},e.cX)),i(ot,N([lt("tab-content")]),i(at,function(a){return p(yl,a.cc,w(a.cc,r.cc),a.cl,n,e)},e.cX))]))}),gl=Rn,ml=function(){for(;;){0}},yn=vr(0),vl=l(function(n,a){return i(qr,function(a){return vr(n(a))},a)}),wl=l(function(a,n){return ua(i(qr,tr(a),n))}),_=e(function(a,n){return i(vl,function(){return 0},Un(i(at,wl(a),n)))}),Ia=e(function(){return vr(0)}),O=l(function(a,n){return i(vl,a,n)});va.Task={b:yn,c:_,d:Ia,e:O,f:void 0};var ql,xl=xa("Task"),El=l(function(a,n){return xl(i(vl,a,n))}),$l=l(function(a,n){return a<1?n:u(Gt,a,vo(n),n)}),kl=M,Tl=l(function(a,n){return a<1?"":u(Gt,0,a,n)}),Al=V,Sl=function(a){for(var n=0,e=a.charCodeAt(0),r=43==e||45==e?1:0,t=r;t + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xonsh/webconfig/main.py b/xonsh/webconfig/main.py index 15c80cd663..552498666a 100644 --- a/xonsh/webconfig/main.py +++ b/xonsh/webconfig/main.py @@ -1,103 +1,129 @@ #!/usr/bin/env python3 -import os -import sys +import cgi +import contextlib import json +import os import socketserver -from http import server -from pprint import pprint -from argparse import ArgumentParser +import string +import sys import typing as tp +from argparse import ArgumentParser +from http import server, HTTPStatus +from pathlib import Path +from pprint import pprint +from urllib import parse -RENDERERS: tp.List[tp.Callable] = [] - - -def renderer(f): - """Adds decorated function to renderers list.""" - RENDERERS.append(f) - - -@renderer -def prompt(config): - return ["$PROMPT = {!r}".format(config["prompt"])] - - -@renderer -def colors(config): - style = config["colors"] - if style == "default": - return [] - return [f"$XONSH_COLOR_STYLE = {style!r}"] - - -@renderer -def xontribs(config): - xtribs = config["xontribs"] - if not xtribs: - return [] - return ["xontrib load " + " ".join(xtribs)] - +from xonsh.built_ins import XSH +from xonsh.webconfig import tags as t +from xonsh.webconfig.file_writes import insert_into_xonshrc +from xonsh.webconfig.routes import Routes -def config_to_xonsh( - config, prefix="# XONSH WEBCONFIG START", suffix="# XONSH WEBCONFIG END" -): - """Turns config dict into xonsh code (str).""" - lines = [prefix] - for func in RENDERERS: - lines.extend(func(config)) - lines.append(suffix) - return "\n".join(lines) - - -def insert_into_xonshrc( - config, - xonshrc="~/.xonshrc", - prefix="# XONSH WEBCONFIG START", - suffix="# XONSH WEBCONFIG END", -): - """Places a config dict into the xonshrc.""" - # get current contents - fname = os.path.expanduser(xonshrc) - if os.path.isfile(fname): - with open(fname) as f: - s = f.read() - before, _, s = s.partition(prefix) - _, _, after = s.partition(suffix) - else: - before = after = "" - dname = os.path.dirname(fname) - if dname: - os.makedirs(dname, exist_ok=True) - # compute new values - new = config_to_xonsh(config, prefix=prefix, suffix=suffix) - # write out the file - with open(fname, "w", encoding="utf-8") as f: - f.write(before + new + after) - return fname +RENDERERS: tp.List[tp.Callable] = [] class XonshConfigHTTPRequestHandler(server.SimpleHTTPRequestHandler): - def _set_headers(self): - self.send_response(200) - self.send_header("Content-type", "text/html") + def _write_headers(self, *headers: "tuple[str, str]"): + for name, val in headers: + self.send_header(name, val) self.end_headers() + def _write_data(self, data: "bytes|dict|str"): + if isinstance(data, bytes): + content_type = "text/html" + elif isinstance(data, dict): + content_type = "application/json" + data = json.dumps(data).encode() + else: + content_type = "text/html" + data = str(data).encode() + self._write_headers( + ("Content-type", content_type), + ("Content-Length", str(len(data))), + ) + self.wfile.write(data) + self.wfile.flush() + + def _send( + self, + data: "bytes|dict|str|None" = None, + status: "None|int" = None, + redirect: "str|None" = None, + ): + status = status or (HTTPStatus.FOUND if redirect else HTTPStatus.OK) + self.send_response(status) + if data: + self._write_data(data) + elif redirect: + self._write_headers( + ("Location", redirect), + ) + + def _read(self): + content_len = int(self.headers.get("content-length", 0)) + return self.rfile.read(content_len) + + def render_get(self, route): + try: + webconfig = Path(__file__).parent + except Exception: + # in case of thread missing __file__ definition + webconfig = Path.cwd() + path = webconfig / "index.html" + tmpl = string.Template(path.read_text()) + navlinks = t.to_str(route.get_nav_links()) + msgs = t.to_str(route.get_err_msgs()) + body = t.to_str(route.get()) # type: ignore + data = tmpl.substitute(navlinks=navlinks, body=msgs + body) + return self._send(data) + + def _get_route(self, method: str): + url = parse.urlparse(self.path) + route_cls = Routes.registry.get(url.path) + if route_cls and hasattr(route_cls, method): + params = parse.parse_qs(url.query) + return route_cls(url=url, params=params, xsh=XSH) + + def do_GET(self) -> None: + route = self._get_route("get") + if route is not None: + return self.render_get(route) + return super().do_GET() + + def _read_form(self): + ctype, pdict = cgi.parse_header(self.headers.get("content-type")) + # if ctype == "multipart/form-data": + # postvars = cgi.parse_multipart(self.rfile, pdict) + if ctype == "application/x-www-form-urlencoded": + return parse.parse_qs(self._read(), keep_blank_values=True) + return {} + def do_POST(self): """Reads post request body""" - self._set_headers() - content_len = int(self.headers.get("content-length", 0)) - post_body = self.rfile.read(content_len) + route = self._get_route("post") + if route is not None: + # redirect after form submission + data = cgi.FieldStorage( + self.rfile, + headers=self.headers, + environ={"REQUEST_METHOD": "POST"}, + keep_blank_values=True, + ) + new_route = route.post(data) or route + return self._send(redirect=new_route.path) + post_body = self._read() config = json.loads(post_body) print("Web Config Values:") pprint(config) fname = insert_into_xonshrc(config) print("Wrote out to " + fname) - self.wfile.write(b"received post request:
" + post_body) + self._send(b"received post request:
" + post_body) def make_parser(): p = ArgumentParser("xonfig web") p.add_argument( "--no-browser", + "-n", action="store_false", dest="browser", default=True, @@ -106,27 +132,26 @@ def make_parser(): return p -def main(args=None): - p = make_parser() - ns = p.parse_args(args=args) +def bind_server_to( + port: int = 8421, handler_cls=XonshConfigHTTPRequestHandler, browser=False +): + cls = socketserver.TCPServer + # cls = socketserver.ThreadingTCPServer # required ctrl+c twice ? - webconfig_dir = os.path.dirname(__file__) - if webconfig_dir: - os.chdir(webconfig_dir) + cls.allow_reuse_address = True - port = 8421 - Handler = XonshConfigHTTPRequestHandler while port <= 9310: try: - with socketserver.TCPServer(("", port), Handler) as httpd: - url = f"http://localhost:{port}" - print(f"Web config started at '{url}'. Hit Crtl+C to stop.") - if ns.browser: - import webbrowser - - webbrowser.open(url) - httpd.serve_forever() - break + cls.allow_reuse_address = True + + httpd = cls(("", port), handler_cls) + url = f"http://localhost:{port}" + print(f"Web config started at '{url}'. Hit Crtl+C to stop.") + if browser: + import webbrowser + + webbrowser.open(url) + return httpd except OSError: type, value = sys.exc_info()[:2] if "Address already in use" not in str(value): @@ -136,5 +161,28 @@ def main(args=None): port += 1 +def serve(browser=False): + httpd = bind_server_to(browser=browser) + + with contextlib.suppress(KeyboardInterrupt): + with httpd: + httpd.serve_forever() + + +def main(args=None): + from xonsh.main import setup + + setup() + + p = make_parser() + ns = p.parse_args(args=args) + + webconfig_dir = os.path.dirname(__file__) + if webconfig_dir: + os.chdir(webconfig_dir) + serve(ns.browser) + + if __name__ == "__main__": + # watchexec -r -e py -- python -m xonsh.webconfig --no-browser main() diff --git a/xonsh/webconfig/routes.py b/xonsh/webconfig/routes.py new file mode 100644 index 0000000000..5d3aea5a1b --- /dev/null +++ b/xonsh/webconfig/routes.py @@ -0,0 +1,432 @@ +import cgi +import inspect +import sys +from typing import TYPE_CHECKING + +from xonsh.environ import Env +from .file_writes import insert_into_xonshrc +from ..built_ins import XonshSession + +if TYPE_CHECKING: + from typing import Type + +import logging + +from . import xonsh_data, tags as t +from urllib import parse + + +class Routes: + path: str + registry: "dict[str, Type[Routes]]" = {} + navbar = False + nav_title: "str|None" = None + err_msgs: "list" = [] + """session storage for error messages""" + + def __init__( + self, + url: "parse.ParseResult", + params: "dict[str, list[str]]", + xsh: "XonshSession", + ): + self.url = url + self.params = params + self.env: "Env" = xsh.env + self.xsh = xsh + + def __init_subclass__(cls, **kwargs): + cls.registry[cls.path] = cls + + def err(self, msg: str): + html = xonsh_data.rst_to_html(msg) + tree = t.etree.fromstring(html) + self.err_msgs.append(tree) + + def get_nav_links(self): + for page in self.registry.values(): + klass = [] + if page.path == self.url.path: + klass.append("active") + + title = page.nav_title() if callable(page.nav_title) else page.nav_title + if title: + yield t.nav_item(*klass)[ + t.nav_link(href=page.path)[title], + ] + + def get_err_msgs(self): + if not self.err_msgs: + return + for msg in self.err_msgs: + yield t.alert("alert-danger")[msg] + self.err_msgs.clear() + + def get_sel_url(self, name): + params = parse.urlencode({"selected": name}) + return self.path + "?" + params + + @staticmethod + def get_display(display): + try: + display = t.etree.fromstring(display) + except Exception as ex: + logging.error(f"Failed to parse color-display {ex!r}. {display!r}") + display = t.pre()[display] + return display + + def update_rc(self, **kwargs): + # todo: handle updates and deletion as well + insert_into_xonshrc(kwargs) + + +class ColorsPage(Routes): + path = "/" + nav_title = "Colors" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.colors = dict(xonsh_data.render_colors()) + self.var_name = "XONSH_COLOR_STYLE" + + def to_card(self, name: str, display: str): + return t.card()[ + t.card_body()[ + t.card_title()[ + t.a("stretched-link", href=self.get_sel_url(name))[name] + ], + self.get_display(display), + ] + ] + + def get_cols(self): + for name, display in self.colors.items(): + yield t.col_sm()[ + self.to_card(name, display), + ] + + def _get_selected_header(self): + selected = self.params.get("selected") + current = self.env.get(self.var_name) + if selected and selected != current: + name = selected[0] + # update env-variable + form = t.inline_form(method="post")[ + t.btn_primary("ml-2", "p-1", type="submit", name=self.var_name)[ + f"Update ${self.var_name}", + ], + ] + return (f"Selected: {name}", form), name + return (f"Current: {current}",), current + + def get_selected(self): + header, name = self._get_selected_header() + name = name if name in self.colors else "default" + display = self.colors[name] + + card = t.card()[ + t.card_header()[header], + t.card_body()[self.get_display(display)], + ] + + return t.row()[ + t.col()[card], + ] + + def get(self): + # banner + yield self.get_selected() + + yield t.br() + yield t.br() + # rest + cols = list(self.get_cols()) + yield t.row()[cols] + + def post(self, _): + selected = self.params.get("selected") + if selected: + self.env[self.var_name] = selected[0] + self.update_rc(color=selected[0]) + + +class PromptsPage(Routes): + path = "/prompts" + nav_title = "Prompts" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + prompts = xonsh_data.render_prompts(self.env) + self.current = next(prompts) + self.prompts = dict(prompts) + self.var_name = "PROMPT" + # todo: support updating RIGHT_PROMPT, BOTTOM_TOOLBAR + + def to_card(self, name: str, display: str): + return t.card()[ + t.card_body()[ + t.card_title()[ + t.a("stretched-link", href=self.get_sel_url(name))[name] + ], + self.get_display(display), + ] + ] + + def _get_selected_header(self): + ps_names = self.params.get("selected") + if ps_names: + name = ps_names[0] + if name in self.prompts: + return f"Selected: {name}", name + return "Current: ", None + + def get_selected(self): + header, cur_sel = self._get_selected_header() + if cur_sel is None: + prompt = self.current["value"] + display = self.current["display"] + else: + cont = self.prompts[cur_sel] + prompt: str = cont["value"] + display = cont["display"] + # update env-variable + txt_area = t.textarea( + "form-control", + name=self.var_name, + rows=str(len(prompt.splitlines())), + )[prompt] + + card = t.card()[ + t.card_header()[header], + t.card_body()[ + t.card_title()["Edit"], + txt_area, + t.br(), + t.card_title()["Preview"], + t.p("text-muted")[ + "It is not a live preview. `Set` to get the updated view." + ], + self.get_display(display), + ], + t.card_footer("py-1")[ + t.btn_primary("py-1", type="submit")[ + "Set", + ], + ], + ] + return t.row()[ + t.col()[t.form(method="post")[card]], + ] + + def get_cols(self): + for name, prompt in self.prompts.items(): + yield t.row()[ + t.col()[ + self.to_card(name, prompt["display"]), + ] + ] + + def get(self): + # banner + yield self.get_selected() + + yield t.br() + yield t.br() + # rest + yield from self.get_cols() + + def post(self, data: "cgi.FieldStorage"): + if data: + prompt = data.getvalue(self.var_name) + self.env[self.var_name] = prompt + self.update_rc(prompt=prompt) + + +class XontribsPage(Routes): + path = "/xontribs" + nav_title = "Xontribs" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.xontribs = dict(xonsh_data.render_xontribs()) + + @staticmethod + def mod_name(name): + return f"xontrib.{name}" + + @staticmethod + def is_loaded(name): + return XontribsPage.mod_name(name) in sys.modules + + def xontrib_card(self, name, data): + from xonsh.xontribs import find_xontrib + + title = t.a(href=data["url"])[name] + if find_xontrib(name): + act_label = "Add" + if self.is_loaded(name): + act_label = "Remove" + action = t.inline_form(method="post")[ + t.btn_primary("ml-2", "p-1", type="submit", name=name)[ + act_label, + ], + ] + else: + title = title("stretched-link") # add class + action = "" + return t.card()[ + t.card_header()[title, action], + t.card_body()[ + self.get_display(data["display"]), + ], + ] + + def get(self): + for name, data in self.xontribs.items(): + yield t.row()[ + t.col()[ + self.xontrib_card(name, data), + ] + ] + yield t.br() + + def post(self, data: "cgi.FieldStorage"): + if not data.keys(): + return + name = data.keys()[0] + if self.is_loaded(name): + # todo: update rc file + del sys.modules[self.mod_name(name)] + else: + from xonsh.xontribs import xontribs_load + + _, err, _ = xontribs_load([name]) + if err: + self.err(err) + else: + self.update_rc(xontribs=[name]) + + +class EnvVariablesPage(Routes): + path = "/vars" + nav_title = "Variables" + + def get_header(self): + yield t.tr()[ + t.th("text-right")["Name"], + t.th()["Value"], + ] + + def get_rows(self): + for name in sorted(self.env.keys()): + if not self.env.is_configurable(name): + continue + value = self.env[name] + envvar = self.env.get_docs(name) + html = xonsh_data.rst_to_html(envvar.doc) + yield t.tr()[ + t.td("text-right")[str(name)], + t.td()[ + t.p()[repr(value)], + t.small()[self.get_display(html)], + ], + ] + + def get_table(self): + rows = list(self.get_rows()) + yield t.tbl("table-striped")[ + self.get_header(), + rows, + ] + + def get(self): + yield t.div("table-responsive")[self.get_table()] + + +class AliasesPage(Routes): + path = "/alias" + nav_title = "Aliases" + + def get_header(self): + yield t.tr()[ + t.th("text-right")["Name"], + t.th()["Value"], + ] + + def get_rows(self): + if not self.xsh.aliases: + return + for name in sorted(self.xsh.aliases.keys()): + alias = self.xsh.aliases[name] + if callable(alias): + continue + # todo: + # 1. do not edit default aliases as well + # 2. way to update string aliases + + yield t.tr()[ + t.td("text-right")[str(name)], + t.td()[ + t.p()[repr(alias)], + ], + ] + + def get_table(self): + rows = list(self.get_rows()) + yield t.tbl("table-sm", "table-striped")[ + self.get_header(), + rows, + ] + + def get(self): + yield t.div("table-responsive")[self.get_table()] + + +class AbbrevsPage(Routes): + path = "/abbrevs" + mod_name = XontribsPage.mod_name("abbrevs") + + def __init__(self, **kwargs): + super().__init__(**kwargs) + # lazy import as to not load by accident + from xontrib.abbrevs import abbrevs # type: ignore + + self.abbrevs: "dict[str, str]" = abbrevs + + @classmethod + def nav_title(cls): + if cls.mod_name in sys.modules: + return "Abbrevs" + + def get_header(self): + yield t.tr()[ + t.th("text-right")["Name"], + t.th()["Value"], + ] + + def get_rows(self): + for name in sorted(self.abbrevs.keys()): + alias = self.abbrevs[name] + if callable(alias): + display = inspect.getsource(alias) + else: + display = str(alias) + # todo: + # 2. way to update + + yield t.tr()[ + t.td("text-right")[str(name)], + t.td()[ + t.p()[repr(display)], + ], + ] + + def get_table(self): + rows = list(self.get_rows()) + yield t.tbl("table-sm", "table-striped")[ + self.get_header(), + rows, + ] + + def get(self): + yield t.div("table-responsive")[self.get_table()] diff --git a/xonsh/webconfig/tags.py b/xonsh/webconfig/tags.py new file mode 100644 index 0000000000..b0b97c7b28 --- /dev/null +++ b/xonsh/webconfig/tags.py @@ -0,0 +1,141 @@ +import logging +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Iterable +import xml.etree.ElementTree as etree +from functools import partial + + +class Elem(etree.Element): + def __init__( + self, + tag: "str", + *cls: str, + **kwargs: "str", + ): + super().__init__(tag) + self.set_attrib(*cls, **kwargs) + + def __getitem__(self, item: "int|str|Elem|Iterable[Elem]"): # type: ignore + """nice sub-tree""" + if isinstance(item, int): + return super().__getitem__(item) + if isinstance(item, str): + self.text = (self.text or "") + item + elif isinstance(item, etree.Element): + self.append(item) + else: + for ele in item: + try: + _ = self[ele] # recursive call + except Exception as ex: + logging.error( + f"Failed to append to node list. {ex!r} : {item!r}>{ele!r} : {self.to_str()!r}" + ) + break + return self + + def set_attrib(self, *cls: str, **kwargs: str): + klass = " ".join(cls) + classes = [klass, self.attrib.pop("class", "")] + cls_str = " ".join(filter(None, classes)) + if cls_str: + self.attrib["class"] = cls_str + self.attrib.update(kwargs) + + def __call__(self, *cls: str, **kwargs: str): + self.set_attrib(*cls, **kwargs) + return self + + def to_str(self) -> bytes: + return etree.tostring(self) + + +div = partial(Elem, "div") +row = partial(div, "row") +col = partial(div, "col") +col_sm = partial(div, "col-sm") +col_md = partial(div, "col-md") + +alert = partial(div, "alert", role="alert") + +br = partial(Elem, "br") + +h3 = partial(Elem, "h3") +h4 = partial(Elem, "h4") +h5 = partial(Elem, "h5") +card_title = partial(h5, "card-title") + +li = partial(Elem, "li") +nav_item = partial(li, "nav-item") + +p = partial(Elem, "p") +small = partial(Elem, "small") +pre = partial(Elem, "pre") +code = partial(Elem, "code") + +a = partial(Elem, "a") +nav_link = partial(a, "nav-link") + +form = partial(Elem, "form") +inline_form = partial(form, "d-inline") + +card = partial(div, "card") +card_header = partial(div, "card-header") +card_body = partial(div, "card-body") +card_text = partial(div, "card-text") +card_footer = partial(div, "card-footer") + +textarea = partial(Elem, "textarea") + +table = partial(Elem, "table") +tbl = partial(table, "table") # bootstrap table +tr = partial(Elem, "tr") +th = partial(Elem, "th") +td = partial(Elem, "td") + +btn = partial(Elem, "button", "btn", type="button") +btn_primary = partial(btn, "btn-primary") +btn_primary_sm = partial(btn_primary, "btn-sm") + + +def to_pretty(txt: str): + import xml.dom.minidom + + dom = xml.dom.minidom.parseString(txt) + txt = dom.toprettyxml() + return "".join(txt.splitlines(keepends=True)[1:]) + + +def to_str(elems: "Iterable[Elem]|Elem", debug=False) -> str: + def _to_str(): + if isinstance(elems, etree.Element): + yield etree.tostring(elems) + else: + for idx, el in enumerate(elems): + try: + yield etree.tostring(el) + except Exception: + logging.error( + f"Failed to serialize {el!r}. ({elems!r}.{idx!r})", + exc_info=True, + ) + + txt = b"".join(_to_str()).decode() + if debug: + txt = to_pretty(txt) + return txt + + +if __name__ == "__main__": + nav = nav_item()[ + nav_link(href="/")["Colors"], + ] + gen = to_str(nav, debug=True) + print(gen) + assert gen.splitlines() == [ + '", + ] diff --git a/xonsh/webconfig/xonsh_data.py b/xonsh/webconfig/xonsh_data.py new file mode 100644 index 0000000000..7025c40389 --- /dev/null +++ b/xonsh/webconfig/xonsh_data.py @@ -0,0 +1,215 @@ +"""script for compiling elm source and dumping it to the js folder.""" +import functools +import io +import logging + +import pygments + +from xonsh.color_tools import rgb_to_ints +from xonsh.prompt.base import PromptFormatter, default_prompt +from xonsh.pyghooks import ( + XonshStyle, + xonsh_style_proxy, + XonshHtmlFormatter, + Token, + XonshLexer, +) +from xonsh.pygments_cache import get_all_styles +from xonsh.style_tools import partial_color_tokenize +from xonsh.xontribs_meta import get_xontribs, Xontrib + + +# $RAISE_SUBPROC_ERROR = True +# $XONSH_SHOW_TRACEBACK = False + +# +# helper funcs +# + + +@functools.lru_cache(maxsize=4) +def get_rst_formatter(**kwargs): + from pygments.formatters.html import HtmlFormatter + from pygments.lexers.markup import RstLexer + + return RstLexer(), HtmlFormatter(**kwargs) + + +def escape(s): + return s.replace(r"\n", "
") + + +def invert_color(orig): + r, g, b = rgb_to_ints(orig) + inverted = [255 - r, 255 - g, 255 - b] + new = [hex(n)[2:] for n in inverted] + new = [n if len(n) == 2 else "0" + n for n in new] + return "".join(new) + + +def html_format(s, style="default"): + buf = io.StringIO() + proxy_style = xonsh_style_proxy(XonshStyle(style)) + # make sure we have a foreground color + fgcolor = proxy_style._styles[Token.Text][0] + if not fgcolor: + fgcolor = invert_color(proxy_style.background_color[1:].strip("#")) + # need to generate stream before creating formatter so that all tokens actually exist + if isinstance(s, str): + token_stream = partial_color_tokenize(s) + else: + token_stream = s + formatter = XonshHtmlFormatter( + wrapcode=True, + noclasses=True, + style=proxy_style, + prestyles="margin: 0em; padding: 0.5em 0.1em; color: #" + fgcolor, + cssstyles="border-style: solid; border-radius: 5px", + ) + formatter.format(token_stream, buf) + return buf.getvalue() + + +def rst_to_html(text): + try: + from pygments import highlight + + lexer, formatter = get_rst_formatter( + noclasses=True, + cssstyles="background: transparent", + style="monokai", # a dark bg style + ) + return highlight(text, lexer, formatter) + except ImportError: + return text + + +# render prompts +def get_named_prompts(): + return [ + ( + "default", + default_prompt(), + ), + ("debian chroot", "{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}{RESET}> "), + ("minimalist", "{BOLD_GREEN}{cwd_base}{RESET} ) "), + ( + "terlar", + "{env_name}{BOLD_GREEN}{user}{RESET}@{hostname}:" + "{BOLD_GREEN}{cwd}{RESET}|{gitstatus}\\n{BOLD_INTENSE_RED}➤{RESET} ", + ), + ( + "default with git status", + "{env_name}{BOLD_GREEN}{user}@{hostname}{BOLD_BLUE} {cwd}" + "{branch_color}{gitstatus: {}}{RESET} {BOLD_BLUE}" + "{prompt_end}{RESET} ", + ), + ("robbyrussell", "{BOLD_INTENSE_RED}➜ {CYAN}{cwd_base} {gitstatus}{RESET} "), + ("just a dollar", "$ "), + ( + "simple pythonista", + "{INTENSE_RED}{user}{RESET} at {INTENSE_PURPLE}{hostname}{RESET} " + "in {BOLD_GREEN}{cwd}{RESET}\\n↪ ", + ), + ( + "informative", + "[{localtime}] {YELLOW}{env_name} {BOLD_BLUE}{user}@{hostname} " + "{BOLD_GREEN}{cwd} {gitstatus}{RESET}\\n> ", + ), + ( + "informative Version Control", + "{YELLOW}{env_name} " "{BOLD_GREEN}{cwd} {gitstatus}{RESET} {prompt_end} ", + ), + ("classic", "{user}@{hostname} {BOLD_GREEN}{cwd}{RESET}> "), + ( + "classic with git status", + "{gitstatus} {RESET}{user}@{hostname} {BOLD_GREEN}{cwd}{RESET}> ", + ), + ("screen savvy", "{YELLOW}{user}@{PURPLE}{hostname}{BOLD_GREEN}{cwd}{RESET}> "), + ( + "sorin", + "{CYAN}{cwd} {INTENSE_RED}❯{INTENSE_YELLOW}❯{INTENSE_GREEN}❯{RESET} ", + ), + ( + "acidhub", + "❰{INTENSE_GREEN}{user}{RESET}❙{YELLOW}{cwd}{RESET}{env_name}❱{gitstatus}≻ ", + ), + ( + "nim", + "{INTENSE_GREEN}┬─[{YELLOW}{user}{RESET}@{BLUE}{hostname}{RESET}:{cwd}" + "{INTENSE_GREEN}]─[{localtime}]─[{RESET}G:{INTENSE_GREEN}{curr_branch}=]" + "\\n{INTENSE_GREEN}╰─>{INTENSE_RED}{prompt_end}{RESET} ", + ), + ] + + +def get_initial(env, prompt_format, fields): + template = env.get_stringified("PROMPT") + return { + "value": template, + "display": escape(html_format(prompt_format(template, fields=fields))), + } + + +def render_prompts(env): + prompt_format = PromptFormatter() + fields = dict(env.get("PROMPT_FIELDS") or {}) + fields.update( + cwd="~/snail/stuff", + cwd_base="stuff", + user="lou", + hostname="carcolh", + env_name=fields["env_prefix"] + "env" + fields["env_postfix"], + curr_branch="branch", + gitstatus="{CYAN}branch|{BOLD_BLUE}+2{RESET}⚑7", + branch_color="{BOLD_INTENSE_RED}", + localtime="15:56:07", + ) + yield get_initial(env, prompt_format, fields) + for name, template in get_named_prompts(): + display = html_format(prompt_format(template, fields=fields)) + yield name, { + "value": template, + "display": escape(display), + } + + +def render_colors(): + source = ( + "import sys\n" + 'echo "Welcome $USER on" @(sys.platform)\n\n' + "def func(x=42):\n" + ' d = {"xonsh": True}\n' + ' return d.get("xonsh") and you\n\n' + "# This is a comment\n" + "![env | uniq | sort | grep PATH]\n" + ) + lexer = XonshLexer() + lexer.add_filter("tokenmerge") + token_stream = list(pygments.lex(source, lexer=lexer)) + token_stream = [(t, s.replace("\n", "\\n")) for t, s in token_stream] + styles = sorted(get_all_styles()) + styles.insert(0, styles.pop(styles.index("default"))) + for style in styles: + try: + display = html_format(token_stream, style=style) + except Exception as ex: + logging.error( + f"Failed to format Xonsh code {ex!r}. {style!r}", exc_info=True + ) + display = source + yield style, escape(display) + + +def format_xontrib(xontrib: Xontrib): + return { + "url": xontrib.url, + "license": xontrib.package.license if xontrib.package else "", + "display": escape(rst_to_html(xontrib.description)), + } + + +def render_xontribs(): + md = get_xontribs() + for xontrib_name, xontrib in md.items(): + yield xontrib_name, format_xontrib(xontrib) diff --git a/xonsh/xonfig.py b/xonsh/xonfig.py index 68f71dcbb4..e4a81d3a86 100644 --- a/xonsh/xonfig.py +++ b/xonsh/xonfig.py @@ -698,13 +698,13 @@ def _web( Parameters ---------- - browser : --nb, --no-browser + browser : --nb, --no-browser, -n don't open browser """ - import subprocess + from xonsh.webconfig import main - subprocess.run([sys.executable, "-m", "xonsh.webconfig"] + _args[1:]) + main.main(_args[1:]) def _jupyter_kernel( @@ -802,7 +802,7 @@ def build(self): return parser -xonfig_main = XonfigAlias() +xonfig_main = XonfigAlias(threadable=False) @lazyobject diff --git a/xonsh/xontribs.py b/xonsh/xontribs.py index 9522d90622..5970d33375 100644 --- a/xonsh/xontribs.py +++ b/xonsh/xontribs.py @@ -54,7 +54,7 @@ def prompt_xontrib_install(names: tp.List[str]): if xontrib.package: packages.append(xontrib.package.name) - print( + return ( "The following xontribs are enabled but not installed: \n" " {xontribs}\n" "To install them run \n" @@ -110,6 +110,8 @@ def xontribs_load( """ ctx = XSH.ctx res = ExitCode.OK + stdout = None + stderr = None for name in names: if verbose: print(f"loading xontrib {name!r}") @@ -120,9 +122,9 @@ def xontribs_load( print_exception(f"Failed to load xontrib {name}.") if hasattr(update_context, "bad_imports"): res = ExitCode.NOT_FOUND - prompt_xontrib_install(update_context.bad_imports) # type: ignore + stderr = prompt_xontrib_install(update_context.bad_imports) # type: ignore del update_context.bad_imports # type: ignore - return res + return stdout, stderr, res def xontrib_installed(names: tp.Set[str]):