├── .github └── workflows │ └── python-publish.yml ├── .gitignore ├── .idea ├── .gitignore ├── GitLink.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── mehditor.iml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── mehditor ├── __init__.py ├── app_commands.py ├── config.py ├── main.py ├── screens │ ├── __init__.py │ ├── about.py │ ├── app_menu.py │ ├── chooser.py │ ├── confirm_dialog.py │ ├── file_chooser.py │ ├── file_open.py │ ├── file_save.py │ ├── file_settings.py │ ├── hotkeys.py │ ├── input_prompt.py │ ├── mehditor_app.py │ ├── quit_screen.py │ └── shortcuts.py ├── validators.py └── widgets │ ├── __init__.py │ └── better_text_area.py ├── poetry.lock ├── pyproject.toml ├── screenshots ├── meh-1.png ├── meh-2.png └── meh-3.png └── tests ├── __init__.py └── test_validators.py /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: [ created ] 4 | 5 | jobs: 6 | publish: 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | python-version: [ 3.9 ] 11 | poetry-version: [ 1.6.1 ] 12 | os: [ ubuntu-latest ] 13 | runs-on: ${{ matrix.os }} 14 | permissions: 15 | contents: read 16 | id-token: write 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: actions/setup-python@v2 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Build 23 | uses: abatilo/actions-poetry@v2 24 | with: 25 | poetry-version: ${{ matrix.poetry-version }} 26 | - name: Run image 27 | uses: abatilo/actions-poetry@v2 28 | with: 29 | poetry-version: ${{ matrix.poetry-version }} 30 | - name: build 31 | run: | 32 | poetry build 33 | - name: Publish package distributions to PyPI 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### JetBrains template 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea/**/workspace.xml 7 | .idea/**/tasks.xml 8 | .idea/**/usage.statistics.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | # AWS User-specific 13 | .idea/**/aws.xml 14 | 15 | # Generated files 16 | .idea/**/contentModel.xml 17 | 18 | # Sensitive or high-churn files 19 | .idea/**/dataSources/ 20 | .idea/**/dataSources.ids 21 | .idea/**/dataSources.local.xml 22 | .idea/**/sqlDataSources.xml 23 | .idea/**/dynamic.xml 24 | .idea/**/uiDesigner.xml 25 | .idea/**/dbnavigator.xml 26 | 27 | # Gradle 28 | .idea/**/gradle.xml 29 | .idea/**/libraries 30 | 31 | # Gradle and Maven with auto-import 32 | # When using Gradle or Maven with auto-import, you should exclude module files, 33 | # since they will be recreated, and may cause churn. Uncomment if using 34 | # auto-import. 35 | # .idea/artifacts 36 | # .idea/compiler.xml 37 | # .idea/jarRepositories.xml 38 | # .idea/modules.xml 39 | # .idea/*.iml 40 | # .idea/modules 41 | # *.iml 42 | # *.ipr 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # SonarLint plugin 66 | .idea/sonarlint/ 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | # Android studio 3.1+ serialized cache file 78 | .idea/caches/build_file_checksums.ser 79 | 80 | ### Python template 81 | # Byte-compiled / optimized / DLL files 82 | __pycache__/ 83 | *.py[cod] 84 | *$py.class 85 | 86 | # C extensions 87 | *.so 88 | 89 | # Distribution / packaging 90 | .Python 91 | build/ 92 | develop-eggs/ 93 | dist/ 94 | downloads/ 95 | eggs/ 96 | .eggs/ 97 | lib/ 98 | lib64/ 99 | parts/ 100 | sdist/ 101 | var/ 102 | wheels/ 103 | share/python-wheels/ 104 | *.egg-info/ 105 | .installed.cfg 106 | *.egg 107 | MANIFEST 108 | 109 | # PyInstaller 110 | # Usually these files are written by a python script from a template 111 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 112 | *.manifest 113 | *.spec 114 | 115 | # Installer logs 116 | pip-log.txt 117 | pip-delete-this-directory.txt 118 | 119 | # Unit test / coverage reports 120 | htmlcov/ 121 | .tox/ 122 | .nox/ 123 | .coverage 124 | .coverage.* 125 | .cache 126 | nosetests.xml 127 | coverage.xml 128 | *.cover 129 | *.py,cover 130 | .hypothesis/ 131 | .pytest_cache/ 132 | cover/ 133 | 134 | # Translations 135 | *.mo 136 | *.pot 137 | 138 | # Django stuff: 139 | *.log 140 | local_settings.py 141 | db.sqlite3 142 | db.sqlite3-journal 143 | 144 | # Flask stuff: 145 | instance/ 146 | .webassets-cache 147 | 148 | # Scrapy stuff: 149 | .scrapy 150 | 151 | # Sphinx documentation 152 | docs/_build/ 153 | 154 | # PyBuilder 155 | .pybuilder/ 156 | target/ 157 | 158 | # Jupyter Notebook 159 | .ipynb_checkpoints 160 | 161 | # IPython 162 | profile_default/ 163 | ipython_config.py 164 | 165 | # pyenv 166 | # For a library or package, you might want to ignore these files since the code is 167 | # intended to run in multiple environments; otherwise, check them in: 168 | # .python-version 169 | 170 | # pipenv 171 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 172 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 173 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 174 | # install all needed dependencies. 175 | #Pipfile.lock 176 | 177 | # poetry 178 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 179 | # This is especially recommended for binary packages to ensure reproducibility, and is more 180 | # commonly ignored for libraries. 181 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 182 | #poetry.lock 183 | 184 | # pdm 185 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 186 | #pdm.lock 187 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 188 | # in version control. 189 | # https://pdm.fming.dev/#use-with-ide 190 | .pdm.toml 191 | 192 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 193 | __pypackages__/ 194 | 195 | # Celery stuff 196 | celerybeat-schedule 197 | celerybeat.pid 198 | 199 | # SageMath parsed files 200 | *.sage.py 201 | 202 | # Environments 203 | .env 204 | .venv 205 | env/ 206 | venv/ 207 | ENV/ 208 | env.bak/ 209 | venv.bak/ 210 | 211 | # Spyder project settings 212 | .spyderproject 213 | .spyproject 214 | 215 | # Rope project settings 216 | .ropeproject 217 | 218 | # mkdocs documentation 219 | /site 220 | 221 | # mypy 222 | .mypy_cache/ 223 | .dmypy.json 224 | dmypy.json 225 | 226 | # Pyre type checker 227 | .pyre/ 228 | 229 | # pytype static type analyzer 230 | .pytype/ 231 | 232 | # Cython debug symbols 233 | cython_debug/ 234 | 235 | # PyCharm 236 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 237 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 238 | # and can be added to the global gitignore or merged into this file. For a more nuclear 239 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 240 | #.idea/ 241 | 242 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/GitLink.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/mehditor.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Modern but simple text editor 2 | 3 | Mehditor is a modern but simple text editor. I made it because I needed a quick editor (like Pico or Nano) on servers, 4 | but wanted a more modern and friendly interface. Meditor is written in Python and uses the excellent 5 | [Textual](https://textual.textualize.io) for its user interface. I wrote it in a weekend. 6 | 7 | ![Mehditor screenshot showing application in terminal window](https://github.com/kkinder/mehditor/blob/main/screenshots/meh-1.png) 8 | 9 | ![Mehditor screenshot showing application in terminal window](https://github.com/kkinder/mehditor/blob/main/screenshots/meh-2.png) 10 | 11 | ![Mehditor screenshot showing application in terminal window](https://github.com/kkinder/mehditor/blob/main/screenshots/meh-3.png) 12 | 13 | ## Features 14 | 15 | - Put that VGA graphics card to use with astonishing _color_ (light and dark modes!) 16 | - Mouse support 17 | - Modern-ish user interface with command palette and menus 18 | - Undo/redo support (now updated with Textual's new undo/redo) 19 | - Sensible keyboard shortcuts that work well over ssh, tmux, etc 20 | - Syntax highlighting for Python, SQL, and more 21 | - Easy installation via pip -- no root needed 22 | 23 | ## Drawbacks (what's missing) 24 | 25 | - Made in a weekend, probably not ideal for heavy lifting 26 | - Fairly minimal without features like multiple files or support for a lot of languages 27 | - Written in Python, so probably not the most lightweight choice 28 | 29 | ## Installation 30 | 31 | ``` 32 | pip install -U mehditor 33 | ``` 34 | 35 | ## Usage 36 | 37 | ``` 38 | meh 39 | ``` 40 | 41 | -------------------------------------------------------------------------------- /mehditor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkinder/mehditor/fa37154b3b421cb425322054946de956097b2891/mehditor/__init__.py -------------------------------------------------------------------------------- /mehditor/app_commands.py: -------------------------------------------------------------------------------- 1 | from textual.command import Hit, Hits, Provider 2 | from mehditor.screens.app_menu import AppMenu 3 | 4 | command_help = { 5 | "new_file": "Create new empty file, replacing current one", 6 | "open_file": "Open new or existing file by name", 7 | "save_file": "Save current file", 8 | "save_file_as": "Save current file with new name", 9 | "quit": "Exit application", 10 | "suspend_process": "Suspend Process", 11 | "cut": "Cut selected text to clipboard", 12 | "copy": "Copy selected text to clipboard", 13 | "paste": "Paste text from clipboard", 14 | "undo_change": "Undo last modification", 15 | "redo_change": "Redo last undo operation", 16 | "change_theme": "Change editor theme (separate from application light/dark mode)", 17 | "toggle_dark_mode": "Toggle application light/dark mode (separate from editor theme)", 18 | "toggle_line_numbers": "Toggle line number visibility", 19 | "toggle_soft_wrap": "Toggle soft wrap", 20 | "change_file_type": "Change current file type (language and syntax highlighting)", 21 | "set_indent_type": "Switch between using tabs and spaces", 22 | "set_indent_width": "Choose width of tabs or number of spaces to align when pressing tab key", 23 | "show_shortcuts": "Show keyboard shortcuts", 24 | "show_about": "About this application" 25 | } 26 | 27 | 28 | class AppCommands(Provider): 29 | def __init__(self, *args, **kwargs): 30 | super().__init__(*args, **kwargs) 31 | 32 | self.commands = [] 33 | 34 | for menu_id, menu_data in AppMenu.menus.items(): 35 | hotkey, label, items = menu_data 36 | for item in items: 37 | if item == "-": 38 | continue 39 | elif len(item) == 3: 40 | command, item_label, item_hotkey = item 41 | elif len(item) == 4: 42 | command, item_label, item_hotkey, item_binding = item 43 | else: 44 | raise ValueError(f"Invalid menu item: {item}") 45 | self.commands.append(( 46 | item_label, 47 | getattr(self.app, f"action_{command}"), 48 | command_help.get(command, None) 49 | )) 50 | 51 | async def search(self, query: str) -> Hits: 52 | matcher = self.matcher(query) 53 | 54 | for name, runnable, help_text in self.commands: 55 | match = matcher.match(f"{name} {help_text}" if help_text else name) 56 | if match > 0: 57 | yield Hit( 58 | match, 59 | matcher.highlight(name), 60 | runnable, 61 | help=help_text, 62 | ) 63 | -------------------------------------------------------------------------------- /mehditor/config.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import os 3 | import pathlib 4 | 5 | DEFAULT_SETTINGS = { 6 | "shortcuts": { 7 | "menu": "escape", 8 | "quit": "ctrl+c", 9 | "new_file": "ctrl+n", 10 | "open_file": "ctrl+o", 11 | "save_file": "ctrl+s", 12 | "goto_line": "ctrl+g", 13 | "show_shortcuts": "f1", 14 | "cut": "f2", 15 | "copy": "f3", 16 | "paste": "f4", 17 | "undo_change": "ctrl+z", 18 | "redo_change": "ctrl+y", 19 | "suspend_process": "f9" 20 | }, 21 | "filetypes": { 22 | "css": "css", 23 | "html": "html", 24 | "htm": "html", 25 | "xml": "html", 26 | "py": "python", 27 | "yaml": "yaml", 28 | "yml": "yaml", 29 | "json": "json", 30 | "markdown": "markdown", 31 | "md": "markdown", 32 | "sql": "sql", 33 | "toml": "toml", 34 | "regex": "regex", 35 | }, 36 | "editing": { 37 | "indent_type": "spaces", 38 | "indent_width": 4, 39 | "theme": "dracula", 40 | "dark_mode": True, 41 | "show_line_numbers": True, 42 | "soft_wrap": False 43 | } 44 | } 45 | 46 | PRIORITY_SHORTCUTS = ["menu", "quit"] 47 | SHOW_SHORTCUTS = ["menu", "show_shortcuts", "cut", "copy", "paste"] 48 | SHORTCUT_ALTS = { 49 | "escape": "Esc", 50 | } 51 | SHORTCUT_ITEM_NAME_OVERRIDES = { 52 | "undo_change": "Undo", 53 | "redo_change": "Redo" 54 | } 55 | settings = configparser.ConfigParser() 56 | settings.read_dict(DEFAULT_SETTINGS) 57 | 58 | 59 | def generate_binding(item, show=None): 60 | from textual.binding import Binding 61 | name = SHORTCUT_ITEM_NAME_OVERRIDES.get(item, item.replace('_', ' ').title()) 62 | 63 | if show is None: 64 | show = item in SHOW_SHORTCUTS 65 | 66 | return Binding(settings["shortcuts"][item], item, name, show=show, priority=item in PRIORITY_SHORTCUTS, 67 | key_display=shortcut_alts(item)) 68 | 69 | 70 | def shortcut_alts(item): 71 | shortcut = settings["shortcuts"][item] 72 | return SHORTCUT_ALTS.get(shortcut, shortcut) 73 | 74 | 75 | def load(): 76 | config_paths = [] 77 | 78 | f = get_default_config_file() 79 | if f: 80 | config_paths.append(f) 81 | 82 | if os.getenv('MEHEDITOR_CONFIG'): 83 | if pathlib.Path(os.getenv('MEHEDITOR_CONFIG')).exists(): 84 | config_paths.append(pathlib.Path(os.getenv('MEHEDITOR_CONFIG'))) 85 | else: 86 | raise FileNotFoundError( 87 | f"MEHEDITOR_CONFIG environment variable set to {os.getenv('MEHEDITOR_CONFIG')} but file does not exist") 88 | 89 | for path in config_paths: 90 | settings.read(path) 91 | 92 | 93 | def get_default_config_file(): 94 | if os.name == 'nt': 95 | return pathlib.Path(os.getenv('APPDATA')) / 'mehditor' / 'mehditor.cfg' 96 | elif os.name == 'posix': 97 | return pathlib.Path(os.getenv('XDG_CONFIG_HOME', 98 | pathlib.Path(os.path.expanduser("~")) / '.config')) / 'mehditor' / 'mehditor.cfg' 99 | 100 | 101 | def save(): 102 | f = get_default_config_file() 103 | if not f: 104 | raise FileNotFoundError("Could not find default config file") 105 | f.parent.mkdir(parents=True, exist_ok=True) 106 | with open(f, 'w') as fp: 107 | settings.write(fp) 108 | return f 109 | 110 | 111 | load() 112 | 113 | __ALL__ = ['settings', 'shortcut_alts', 'load', 'save'] 114 | -------------------------------------------------------------------------------- /mehditor/main.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | import argparse 3 | 4 | parser = argparse.ArgumentParser(description="Mehditor") 5 | parser.add_argument("filename", nargs="*", help="File to open") 6 | args = parser.parse_args() 7 | 8 | from mehditor.screens.mehditor_app import MeheditorApp 9 | 10 | if args.filename: 11 | app = MeheditorApp(file=args.filename[0]) 12 | app.run() 13 | else: 14 | app = MeheditorApp(file=None) 15 | app.run() 16 | 17 | 18 | if __name__ == "__main__": 19 | main() 20 | -------------------------------------------------------------------------------- /mehditor/screens/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkinder/mehditor/fa37154b3b421cb425322054946de956097b2891/mehditor/screens/__init__.py -------------------------------------------------------------------------------- /mehditor/screens/about.py: -------------------------------------------------------------------------------- 1 | import webbrowser 2 | from pathlib import Path 3 | 4 | from textual import on 5 | from textual.app import ComposeResult 6 | from textual.containers import Vertical 7 | from textual.screen import ModalScreen 8 | from textual.widgets import Button, MarkdownViewer, Markdown 9 | from importlib.metadata import version 10 | 11 | ABOUT = """ 12 | # Mehditor 13 | Version {version} 14 | 15 | A text editor for twentieth century computing. Built with Python and Textual. 16 | 17 | https://github.com/kkinder/mehditor 18 | 19 | © Copyright 2023-2024 Ken Kinder {email} {site} 20 | """.format( 21 | version=version("mehditor"), 22 | author="Ken Kinder", 23 | email="ken@kkinder.com", 24 | site="kkinder.com" 25 | ) 26 | 27 | 28 | class MarkdownViewerWithLinks(MarkdownViewer): 29 | @on(Markdown.LinkClicked) 30 | def handle_link(self, event: Markdown.LinkClicked) -> None: 31 | if not Path(event.href).exists(): 32 | event.prevent_default() 33 | webbrowser.open(event.href) 34 | # self.notify(f'WTF: {event.href}') 35 | 36 | 37 | class About(ModalScreen): 38 | CSS = """ 39 | About { 40 | align: center middle; 41 | } 42 | 43 | #dialog { 44 | max-width: 70; 45 | max-height: 20; 46 | border: thick $background 80%; 47 | background: $surface; 48 | } 49 | 50 | Button { 51 | width: 100%; 52 | } 53 | 54 | MarkdownViewer { 55 | height: 1fr; 56 | } 57 | """ 58 | 59 | def compose(self) -> ComposeResult: 60 | with Vertical(id="dialog"): 61 | yield MarkdownViewerWithLinks(ABOUT, show_table_of_contents=False) 62 | yield Button("Dismiss", variant="primary") 63 | 64 | def on_mount(self): 65 | self.query_one("Button").focus() 66 | 67 | def on_button_pressed(self, event: Button.Pressed) -> None: 68 | self.dismiss() 69 | -------------------------------------------------------------------------------- /mehditor/screens/app_menu.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | from rich.table import Table 4 | from textual import events, on 5 | from textual.binding import Binding 6 | from textual.containers import Horizontal, Vertical, Container 7 | from textual.screen import ModalScreen 8 | from textual.widgets import Footer, TabbedContent, TabPane, OptionList, Tabs, Label, Button, Static, Markdown, Header 9 | from textual.widgets._tabbed_content import ContentTabs 10 | from textual.widgets.option_list import Separator, Option 11 | from mehditor import config 12 | 13 | class AppMenu(ModalScreen): 14 | CSS = """ 15 | AppMenu { 16 | align: center middle; 17 | } 18 | 19 | #dialog { 20 | width: 80%; 21 | height: 70%; 22 | border: thick $background 80%; 23 | background: $panel-darken-1; 24 | padding: 0 25 | } 26 | 27 | TabbedContent { 28 | padding: 0; 29 | height: 100%; 30 | } 31 | 32 | TabPane { 33 | padding: 0; 34 | height: 100%; 35 | background: $panel; 36 | } 37 | 38 | ContentSwitcher { 39 | padding: 0; 40 | height: 100%; 41 | background: $panel; 42 | } 43 | 44 | OptionList { 45 | border: none; 46 | background: $panel; 47 | } 48 | 49 | Button { 50 | border: none; 51 | height: 1 52 | } 53 | 54 | #toolstrip { 55 | align: center bottom; 56 | height: 1fr; 57 | 58 | } 59 | 60 | """ 61 | 62 | BINDINGS = [ 63 | Binding("f", "show_tab('file')", "File"), 64 | Binding("e", "show_tab('edit')", "Edit"), 65 | Binding("w", "show_tab('view')", "View"), 66 | Binding("h", "show_tab('help')", "Help"), 67 | ] 68 | 69 | menus = OrderedDict() 70 | menus["file"] = [ 71 | "f", "File", [ 72 | ["new_file", "New", "n"], 73 | ["open_file", "Open...", "o"], 74 | ["save_file", "Save", "s"], 75 | ["save_file_as", "Save As...", "a"], 76 | "-", 77 | ["quit", "Quit", "q"], 78 | ["suspend_process", "Suspend", "u"] 79 | ] 80 | ] 81 | menus["edit"] = [ 82 | "e", "Edit", [ 83 | ["cut", "Cut", "x"], 84 | ["copy", "Copy", "c"], 85 | ["paste", "Paste", "v"], 86 | ["undo_change", "Undo", "u"], 87 | ["redo_change", "Redo", "r"], 88 | ] 89 | ] 90 | menus["view"] = [ 91 | "w", "View", [ 92 | ["change_theme", "Theme...", "t"], 93 | ["toggle_dark_mode", "Toggle Light/Dark Mode", "d"], 94 | ["toggle_line_numbers", "Toggle Line Numbers", "l"], 95 | "-", 96 | ["change_file_type", "File type...", "y"], 97 | ["toggle_soft_wrap", "Toggle soft wrap...", "s"], 98 | 99 | # This seems to be broken up stream 100 | ["set_indent_type", "Set Indent Type...", "n"], 101 | ["set_indent_width", "Set Indent Width...", "i"], 102 | ] 103 | ] 104 | menus["help"] = [ 105 | "h", "Help", [ 106 | ["show_shortcuts", "Show Showcuts", "s"], 107 | ["show_about", "About...", "a"], 108 | ] 109 | ] 110 | 111 | def compose(self): 112 | self.menu_hotkeys = [] 113 | self.menu_hotkey_items = {} 114 | for menu_id, menu_data in self.menus.items(): 115 | hotkey, label, items = menu_data 116 | self.menu_hotkeys.append(hotkey) 117 | self.menu_hotkey_items[menu_id] = {} 118 | for item in items: 119 | if item != "-": 120 | self.menu_hotkey_items[menu_id][item[2]] = item[0] 121 | 122 | with Container(id="dialog"): 123 | with TabbedContent(id="tabber", initial=f"{next(iter(self.menus.keys()))}"): 124 | for menu_id, menu_data in self.menus.items(): 125 | hotkey, label, items = menu_data 126 | 127 | with TabPane(f"{hotkey}: {label}", id=f"{menu_id}"): 128 | options = [] 129 | for item in items: 130 | if item == "-": 131 | options.append(Separator()) 132 | continue 133 | elif len(item) == 3: 134 | command, item_label, item_hotkey = item 135 | else: 136 | raise ValueError(f"Invalid menu item: {item}") 137 | 138 | if config.settings.has_option('shortcuts', command): 139 | item_binding = config.settings.get('shortcuts', command) 140 | else: 141 | item_binding = None 142 | 143 | if item_hotkey and item_hotkey in self.menu_hotkeys: 144 | raise Exception(f"Hotkey {item_hotkey} already in use by menu") 145 | 146 | table = Table.grid(padding=0, pad_edge=False, expand=True) 147 | table.add_column() 148 | table.add_column(justify="right") 149 | 150 | if item_binding: 151 | table.add_row(f"[bold]{item_hotkey}:[/] {item_label}", f'[yellow]{item_binding}[/]') 152 | else: 153 | table.add_row(f"[bold]{item_hotkey}:[/] {item_label}", "") 154 | 155 | options.append(Option(table, id=f"option-{command}")) 156 | 157 | yield OptionList( 158 | *options, 159 | id=f"{menu_id}-list") 160 | 161 | yield Footer() 162 | 163 | def _on_mount(self, event: events.Mount) -> None: 164 | super()._on_mount(event) 165 | self.query_one(f"#{self.query_one(TabbedContent).active}-list").focus() 166 | 167 | def action_show_tab(self, tab: str) -> None: 168 | self.tabs.active = tab 169 | self.query_one(f"#{tab}-list").focus() 170 | 171 | @on(TabbedContent.TabActivated) 172 | def on_tab_pane_tab_activated(self, event) -> None: 173 | self.query_one(f"#{event.pane.id}-list").focus() 174 | 175 | def on_key(self, event: events.Key) -> None: 176 | if event.key == "left": 177 | self.query_one("#tabber").get_child_by_type(ContentTabs).action_previous_tab() 178 | elif event.key == "right": 179 | self.query_one("#tabber").get_child_by_type(ContentTabs).action_next_tab() 180 | elif event.key in self.menu_hotkeys: 181 | pass 182 | else: 183 | current_menu_keys = self.menu_hotkey_items[self.tabs.active] 184 | if event.key in current_menu_keys: 185 | self.dismiss(current_menu_keys[event.key]) 186 | 187 | def on_option_list_option_selected(self, event): 188 | self.dismiss(event.option.id.split('-', 1)[1]) 189 | 190 | @property 191 | def tabs(self): 192 | return self.query_one("#tabber") 193 | -------------------------------------------------------------------------------- /mehditor/screens/chooser.py: -------------------------------------------------------------------------------- 1 | from textual import on 2 | from textual.app import ComposeResult 3 | from textual.containers import Vertical, Container 4 | from textual.screen import ModalScreen 5 | from textual.widgets import Label, ListView, ListItem 6 | 7 | 8 | class Chooser(ModalScreen): 9 | CSS = """ 10 | Chooser { 11 | align: center middle; 12 | } 13 | 14 | #dialog { 15 | max-width: 60; 16 | max-height: 20; 17 | border: thick $background 80%; 18 | background: $surface; 19 | } 20 | 21 | #question { 22 | height: 3; 23 | } 24 | 25 | ListView { 26 | } 27 | 28 | Label { 29 | padding: 1 2; 30 | } 31 | """ 32 | 33 | def __init__(self, question, choices, default=None): 34 | super().__init__() 35 | self.question = question 36 | self.choices = choices 37 | 38 | self.initial_index = 0 39 | if default: 40 | for i, item in enumerate(self.choices): 41 | if item[0] == default: 42 | self.initial_index = i 43 | 44 | 45 | def compose(self) -> ComposeResult: 46 | with Vertical(id="dialog"): 47 | yield Container(Label(self.question), id="question") 48 | yield ListView(*[ListItem(Label(label), id=i, classes="choices") for i, label in self.choices], 49 | id="choices", classes="choices", initial_index=self.initial_index) 50 | 51 | @on(ListView.Selected) 52 | def selected(self, event): 53 | self.dismiss(event.item.id) 54 | -------------------------------------------------------------------------------- /mehditor/screens/confirm_dialog.py: -------------------------------------------------------------------------------- 1 | from textual.app import ComposeResult 2 | from textual.containers import Grid 3 | from textual.screen import ModalScreen 4 | from textual.widgets import Button, Label, Footer 5 | 6 | 7 | class ConfirmDialog(ModalScreen): 8 | CSS = """ 9 | ConfirmDialog { 10 | align: center middle; 11 | } 12 | 13 | #dialog { 14 | grid-size: 2; 15 | grid-gutter: 1 2; 16 | grid-rows: 1fr 3; 17 | padding: 0 1; 18 | width: 60; 19 | height: 11; 20 | border: thick $background 80%; 21 | background: $surface; 22 | } 23 | 24 | #question { 25 | column-span: 2; 26 | height: 1fr; 27 | width: 1fr; 28 | content-align: center middle; 29 | } 30 | 31 | Button { 32 | width: 100%; 33 | } 34 | """ 35 | 36 | def __init__(self, question, confirm_text="Yes", cancel_text="No"): 37 | super().__init__() 38 | self.question = question 39 | self.confirm_text = confirm_text 40 | self.cancel_text = cancel_text 41 | 42 | def compose(self) -> ComposeResult: 43 | with Grid(id="dialog"): 44 | yield Label(self.question, id="question") 45 | yield Button(self.confirm_text, variant="primary", id="confirm") 46 | yield Button(self.cancel_text, variant="error", id="cancel") 47 | 48 | def on_button_pressed(self, event: Button.Pressed) -> None: 49 | if event.button.id == "confirm": 50 | self.dismiss(True) 51 | else: 52 | self.dismiss(False) 53 | -------------------------------------------------------------------------------- /mehditor/screens/file_chooser.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from textual import on 4 | from textual.containers import Vertical, Horizontal 5 | from textual.reactive import reactive 6 | from textual.screen import ModalScreen 7 | from textual.widgets import Button, DirectoryTree, Label 8 | from textual.widgets import Input 9 | 10 | from mehditor.screens.confirm_dialog import ConfirmDialog 11 | 12 | 13 | class FileChooser(ModalScreen): 14 | CSS = """ 15 | FileChooser { 16 | align: center middle; 17 | } 18 | 19 | #dialog { 20 | padding: 0; 21 | width: 85%; 22 | height: 80%; 23 | border: thick $background 80%; 24 | background: $surface; 25 | } 26 | 27 | #title { 28 | content-align: center middle; 29 | width: 100%; 30 | display: block; 31 | } 32 | 33 | #nav-area { 34 | height: 3; 35 | # background: $primary-background; 36 | } 37 | 38 | #file-input { 39 | width: 1fr 40 | } 41 | 42 | Button { 43 | margin: 0 1 0 0; 44 | padding: 0 1; 45 | min-width: 2; 46 | width: auto; 47 | } 48 | """ 49 | 50 | cwd = reactive(str) 51 | 52 | def __init__(self, current_file, confirm_overwrite=False): 53 | super().__init__() 54 | 55 | if current_file: 56 | self.current_file = current_file.resolve() 57 | else: 58 | self.current_file = None 59 | self.confirm_overwrite = confirm_overwrite 60 | 61 | def on_mount(self): 62 | if self.current_file: 63 | self.cwd = self.current_file.parent 64 | self.set_input_value(self.current_file.parent) 65 | self.query_one("#dir-tree").path = self.current_file.parent 66 | else: 67 | self.cwd = Path.cwd().resolve() 68 | self.set_input_value(self.cwd) 69 | self.query_one("#dir-tree").path = self.cwd 70 | self.query_one("#file-input").focus() 71 | 72 | def watch_cwd(self): 73 | self.query_one("#dir-tree").path = self.cwd 74 | self.set_input_value(self.cwd) 75 | 76 | def set_input_value(self, value): 77 | value = Path(value) 78 | if value.is_dir(): 79 | value = f'{value}/' 80 | else: 81 | value = str(value) 82 | self.query_one("#file-input").value = value 83 | self.query_one("#file-input").action_end() 84 | self.query_one("#file-input").focus() 85 | 86 | def compose(self): 87 | with Vertical(id="dialog"): 88 | yield Label("Choose File", id="title") 89 | with Horizontal(id="nav-area"): 90 | yield Button("../", id="up-dir") 91 | yield Button("/", id="root-dir") 92 | yield Button("~", id="home-dir") 93 | yield Input(id="file-input") 94 | yield DirectoryTree(id="dir-tree", path=Path.cwd().resolve()) 95 | 96 | def on_button_pressed(self, event: Button.Pressed) -> None: 97 | if event.button.id == "up-dir": 98 | self.cwd = self.cwd.parent 99 | elif event.button.id == "root-dir": 100 | self.cwd = Path("/") 101 | elif event.button.id == "home-dir": 102 | self.cwd = Path.home() 103 | else: 104 | self.dismiss(False) 105 | 106 | @on(Input.Submitted, "#file-input") 107 | def on_file_input_submitted(self, event): 108 | loc = Path(event.value).resolve() 109 | if loc.is_file(): 110 | self.choose_file(loc) 111 | elif loc.is_dir(): 112 | self.cwd = Path(event.value).resolve() 113 | elif loc.parent.exists(): 114 | # Parent file exists; this is the return value 115 | self.dismiss(loc) 116 | else: 117 | self.notify(f"Invalid path: {loc}", severity="error") 118 | 119 | @on(DirectoryTree.FileSelected) 120 | def on_file_selected(self, event): 121 | self.choose_file(event.path) 122 | 123 | def choose_file(self, loc): 124 | if self.confirm_overwrite and loc.resolve() != self.current_file: 125 | def confirmed(confirm: bool) -> None: 126 | if confirm: 127 | self.dismiss(loc) 128 | 129 | self.app.push_screen(ConfirmDialog("File already exists. Overwrite?"), confirmed) 130 | else: 131 | self.dismiss(loc) 132 | -------------------------------------------------------------------------------- /mehditor/screens/file_open.py: -------------------------------------------------------------------------------- 1 | from mehditor.screens.file_chooser import FileChooser 2 | 3 | 4 | class FileOpen(FileChooser): 5 | CSS = """ 6 | FileOpen { 7 | align: center middle; 8 | } 9 | """ + FileChooser.CSS -------------------------------------------------------------------------------- /mehditor/screens/file_save.py: -------------------------------------------------------------------------------- 1 | from mehditor.screens.file_chooser import FileChooser 2 | 3 | 4 | class FileSave(FileChooser): 5 | CSS = """ 6 | FileSave { 7 | align: center middle; 8 | } 9 | """ + FileChooser.CSS 10 | 11 | def __init__(self, current_file, confirm_overwrite=True): 12 | super().__init__(current_file, confirm_overwrite) 13 | 14 | -------------------------------------------------------------------------------- /mehditor/screens/file_settings.py: -------------------------------------------------------------------------------- 1 | # from textual.containers import Vertical, Horizontal 2 | # from textual.screen import ModalScreen 3 | # from textual.widgets import Button, Label, Checkbox, ListView, ListItem 4 | # from textual.widgets import Input 5 | # 6 | # 7 | # class FileSettings(ModalScreen): 8 | # CSS = """ 9 | # FileSettings { 10 | # align: center middle; 11 | # } 12 | # 13 | # #dialog { 14 | # padding: 0; 15 | # width: 50; 16 | # height: 10; 17 | # border: thick $background 80%; 18 | # background: $surface; 19 | # } 20 | # """ 21 | # 22 | # def __init__(self, settings, language_choices): 23 | # super().__init__() 24 | # 25 | # self.settings = settings 26 | # self.language_choices = language_choices 27 | # 28 | # if self.settings["file_type"] in self.language_choices: 29 | # self.language_index = self.language_choices.index(self.settings["file_type"]) 30 | # else: 31 | # self.language_index = 0 32 | # 33 | # def compose(self): 34 | # with Vertical(id="dialog"): 35 | # yield Label("File Settings", id="title") 36 | # yield Checkbox("Use spaces instead of tabs", self.settings["ident_type"] == "spaces", id="use-spaces") 37 | # with Horizontal(): 38 | # yield Label("Indent width: ", id="tab-width-label") 39 | # yield Input(str(self.settings["tab_width"]), id="tab-width") 40 | # with Horizontal(): 41 | # yield Label("File Type: ", id="indent-width-label") 42 | # yield ListView(*[ListItem(Label(label), id=i, classes="choices") for i, label in self.language_choices], 43 | # id="choices", classes="choices", initial_index=self.language_index) 44 | # 45 | # def on_button_pressed(self, event: Button.Pressed) -> None: 46 | # self.dismiss() 47 | -------------------------------------------------------------------------------- /mehditor/screens/hotkeys.py: -------------------------------------------------------------------------------- 1 | from textual.app import ComposeResult 2 | from textual.containers import Vertical 3 | from textual.screen import ModalScreen 4 | from textual.widgets import Button, MarkdownViewer 5 | 6 | 7 | class About(ModalScreen): 8 | CSS = """ 9 | About { 10 | align: center middle; 11 | } 12 | 13 | #dialog { 14 | max-width: 70; 15 | max-height: 15; 16 | border: thick $background 80%; 17 | background: $surface; 18 | } 19 | 20 | Button { 21 | width: 100%; 22 | } 23 | 24 | MarkdownViewer { 25 | height: 1fr; 26 | } 27 | """ 28 | 29 | def compose(self) -> ComposeResult: 30 | with Vertical(id="dialog"): 31 | yield MarkdownViewer(ABOUT, show_table_of_contents=False) 32 | yield Button("Dismiss", variant="primary") 33 | 34 | def on_button_pressed(self, event: Button.Pressed) -> None: 35 | self.dismiss() 36 | -------------------------------------------------------------------------------- /mehditor/screens/input_prompt.py: -------------------------------------------------------------------------------- 1 | from textual import on 2 | from textual.app import ComposeResult 3 | from textual.containers import Vertical, Container 4 | from textual.screen import ModalScreen 5 | from textual.widgets import Label, Input, Button 6 | 7 | 8 | class InputPrompt(ModalScreen): 9 | CSS = """ 10 | InputPrompt { 11 | align: center middle; 12 | } 13 | 14 | #dialog { 15 | max-width: 60; 16 | height: 12; 17 | border: thick $background 80%; 18 | background: $surface; 19 | } 20 | 21 | #question { 22 | height: 3; 23 | padding: 0 2 24 | } 25 | 26 | Button { 27 | margin: 1 0 0 0; 28 | width: 100% 29 | } 30 | """ 31 | 32 | def __init__(self, question, value): 33 | super().__init__() 34 | self.question = question 35 | self.value = value 36 | 37 | def compose(self) -> ComposeResult: 38 | with Vertical(id="dialog"): 39 | yield Container(Label(self.question), id="question") 40 | yield Input(value=str(self.value), id="input") 41 | yield Button("OK", id="ok", variant="primary") 42 | 43 | @on(Input.Submitted) 44 | @on(Button.Pressed) 45 | def selected(self, event): 46 | self.dismiss(self.query_one("#input").value) 47 | -------------------------------------------------------------------------------- /mehditor/screens/mehditor_app.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from pathlib import Path 3 | 4 | from textual import on 5 | from textual.app import App, ComposeResult 6 | from textual.notifications import Notification 7 | from textual.reactive import reactive 8 | from textual.screen import ModalScreen 9 | from textual.widgets import Header, Input, Footer 10 | from textual.widgets._text_area import ThemeDoesNotExist 11 | 12 | from mehditor import config 13 | from mehditor.app_commands import AppCommands 14 | from mehditor.screens.about import About 15 | from mehditor.screens.app_menu import AppMenu 16 | from mehditor.screens.chooser import Chooser 17 | from mehditor.screens.confirm_dialog import ConfirmDialog 18 | from mehditor.screens.file_open import FileOpen 19 | from mehditor.screens.file_save import FileSave 20 | from mehditor.screens.input_prompt import InputPrompt 21 | from mehditor.screens.quit_screen import QuitScreen 22 | from mehditor.screens.shortcuts import Shortcuts 23 | from mehditor.validators import LineNumber 24 | from mehditor.widgets.better_text_area import BetterTextArea 25 | 26 | known_dark_themes = { 27 | "monokai", 28 | "dracula", 29 | "vscode_dark", 30 | } 31 | 32 | known_light_themes = { 33 | "github_light" 34 | } 35 | 36 | 37 | def handle_os_error_decorator(error_message, severity="error", timeout=Notification.timeout): 38 | def decorator(func): 39 | @functools.wraps(func) 40 | def wrapper(self, *args, **kwargs): 41 | try: 42 | return func(self, *args, **kwargs) 43 | except OSError as e: 44 | self.notify(title=error_message, message=str(e), severity=severity, timeout=timeout) 45 | 46 | return wrapper 47 | 48 | return decorator 49 | 50 | 51 | class MeheditorApp(App): 52 | BINDINGS = [ 53 | config.generate_binding("menu"), 54 | config.generate_binding("quit"), 55 | config.generate_binding("save_file"), 56 | config.generate_binding("new_file"), 57 | config.generate_binding("goto_line"), 58 | config.generate_binding("suspend_process"), 59 | ] 60 | 61 | COMMANDS = {AppCommands} 62 | 63 | DEFAULT_CSS = """ 64 | .hidden { 65 | display: none; 66 | } 67 | #text-buffer { 68 | border: none; 69 | } 70 | """ 71 | 72 | show_line_numbers = reactive(True) 73 | soft_wrap = reactive(False) 74 | indent_type = reactive("spaces") 75 | indent_width = reactive(4) 76 | theme = reactive("default") 77 | file_type = reactive("text") 78 | show_line_finder = reactive(False) 79 | file = reactive(Path) 80 | file_unsaved = reactive(False) 81 | clipboard = reactive('') 82 | 83 | def __init__(self, file): 84 | super().__init__() 85 | self.initial_file = file 86 | 87 | def on_mount(self): 88 | self.indent_type = config.settings['editing']['indent_type'] 89 | self.indent_width = config.settings.getint('editing', 'indent_width') 90 | 91 | try: 92 | self.theme = config.settings['editing']['theme'] 93 | except ThemeDoesNotExist: 94 | self.notify(f"Configured theme not available: {config.settings['editing']['theme']}", severity="error") 95 | self.dark = config.settings.getboolean('editing', 'dark_mode') 96 | self.show_line_numbers = config.settings.getboolean('editing', 'show_line_numbers') 97 | self.soft_wrap = config.settings.getboolean('editing', 'soft_wrap') 98 | 99 | if self.initial_file: 100 | self.open_file(self.initial_file) 101 | else: 102 | self.new_file() 103 | self.query_one("#text-buffer").focus() 104 | 105 | ################################################################# 106 | ## Internal actions to be used elsewhere ## 107 | ################################################################# 108 | def new_file(self): 109 | self.query_one("#text-buffer").load_text("") 110 | self.query_one("#text-buffer").history.clear() 111 | self.file = None 112 | self.title = '(Untitled)' 113 | self.file_unsaved = False 114 | 115 | @handle_os_error_decorator("Error Opening File") 116 | def open_file(self, file): 117 | file = Path(file) 118 | if file.exists(): 119 | try: 120 | text = file.read_text() 121 | except UnicodeDecodeError as e: 122 | self.notify(f'Error decoding file (is it a text file?): {e}', severity="error", timeout=20) 123 | return 124 | else: 125 | text = '' 126 | 127 | self.query_one("#text-buffer").load_text(text) 128 | self.query_one("#text-buffer").history.clear() 129 | self.file = Path(file).resolve() if file else None 130 | self.title = self.file.name 131 | self.file_unsaved = False 132 | 133 | @handle_os_error_decorator("Error Saving Settings") 134 | def save_settings(self): 135 | config.settings['editing']['indent_type'] = self.indent_type 136 | config.settings['editing']['indent_width'] = str(self.indent_width) 137 | config.settings['editing']['theme'] = self.theme 138 | config.settings['editing']['dark_mode'] = 'Yes' if self.dark else 'No' 139 | config.settings['editing']['show_line_numbers'] = 'Yes' if self.show_line_numbers else 'No' 140 | config.settings['editing']['soft_wrap'] = 'Yes' if self.soft_wrap else 'No' 141 | config.save() 142 | 143 | ################################################################# 144 | ## Watchers ## 145 | ################################################################# 146 | def watch_theme(self, theme): 147 | self.query_one("#text-buffer").theme = theme if theme not in (None, "default") else "css" 148 | 149 | def watch_dark(self, *args): 150 | super().watch_dark(*args) 151 | if self.dark and self.query_one("#text-buffer").theme in known_light_themes: 152 | self.notify("Dark mode enabled, but using light theme (try changing editor theme too)") 153 | elif not self.dark and self.query_one("#text-buffer").theme in known_dark_themes: 154 | self.notify("Light mode enabled, but using dark theme (try changing editor theme too)") 155 | 156 | def watch_indent_type(self): 157 | self.query_one("#text-buffer").indent_type = self.indent_type 158 | 159 | def watch_indent_width(self): 160 | self.query_one("#text-buffer").indent_width = self.indent_width 161 | 162 | def watch_show_line_numbers(self): 163 | self.query_one("#text-buffer").show_line_numbers = self.show_line_numbers 164 | 165 | def watch_soft_wrap(self): 166 | self.query_one("#text-buffer").soft_wrap = self.soft_wrap 167 | 168 | def watch_file_type(self): 169 | if self.file_type == "text": 170 | self.query_one("#text-buffer").language = None 171 | self.sub_title = '' 172 | else: 173 | self.query_one("#text-buffer").language = self.file_type 174 | self.sub_title = self.file_type 175 | 176 | def watch_file(self): 177 | if self.file and '.' in self.file.name: 178 | extension = self.file.name.split('.')[-1].lower().strip() 179 | if extension in config.settings['filetypes']: 180 | self.file_type = config.settings['filetypes'][extension] 181 | 182 | def watch_show_line_finder(self): 183 | w = self.query_one("#line_number") 184 | 185 | if self.show_line_finder: 186 | w.remove_class("hidden") 187 | line, col = self.query_one("#text-buffer").cursor_location 188 | if col > 0: 189 | w.value = f"{line + 1}:{col + 1}" 190 | else: 191 | w.value = f"{line + 1}" 192 | w.focus() 193 | else: 194 | w.add_class("hidden") 195 | 196 | ################################################################# 197 | ## Main methods and actions ## 198 | ################################################################# 199 | def compose(self) -> ComposeResult: 200 | yield Header() 201 | 202 | yield BetterTextArea("", language=None, id="text-buffer", tab_behavior="indent") 203 | yield Input( 204 | id="line_number", 205 | placeholder="Go to line number (eg 10:3 for line 10, col 3)", 206 | validate_on=["changed"], 207 | validators=[LineNumber()]) 208 | yield Footer() 209 | 210 | def action_cut(self): 211 | tb: BetterTextArea = self.query_one("#text-buffer") 212 | self.clipboard = tb.selected_text 213 | tb.replace("", tb.selection.start, tb.selection.end) 214 | 215 | def action_copy(self): 216 | tb: BetterTextArea = self.query_one("#text-buffer") 217 | if tb.selected_text: 218 | self.clipboard = tb.selected_text 219 | 220 | def action_paste(self): 221 | if self.clipboard: 222 | tb: BetterTextArea = self.query_one("#text-buffer") 223 | tb.replace(self.clipboard, tb.selection.start, tb.selection.end) 224 | else: 225 | self.notify("Nothing to paste", severity="error") 226 | 227 | @handle_os_error_decorator("Error Saving File") 228 | def action_save_file(self) -> None: 229 | 230 | if self.file: 231 | self.file.write_text(self.query_one("#text-buffer").text) 232 | self.file_unsaved = False 233 | self.notify("File saved") 234 | else: 235 | self.action_save_file_as() 236 | 237 | def action_goto_line(self): 238 | self.show_line_finder = True 239 | 240 | def action_undo_change(self): 241 | self.query_one("#text-buffer").undo() 242 | 243 | def action_redo_change(self): 244 | self.query_one("#text-buffer").redo() 245 | 246 | def action_menu(self): 247 | if self.show_line_finder: 248 | self.show_line_finder = False 249 | return 250 | 251 | # TODO: Figure out how to do this without checking a private attribute 252 | if len(self._screen_stack) == 1: 253 | def menu_action(action): 254 | if hasattr(self, f"action_{action}"): 255 | getattr(self, f"action_{action}")() 256 | else: 257 | self.notify(f"Unknown action: {action}", severity="error") 258 | 259 | self.push_screen(AppMenu(), menu_action) 260 | elif len(self._screen_stack) > 1 and isinstance(self._screen_stack[-1], (ModalScreen)): 261 | self.pop_screen() 262 | 263 | def action_new_file(self): 264 | if self.file_unsaved: 265 | def confirmed(confirm: bool) -> None: 266 | if confirm: 267 | self.new_file() 268 | 269 | self.push_screen(ConfirmDialog("File not saved. Erase and start new file?"), confirmed) 270 | else: 271 | self.new_file() 272 | 273 | def action_open_file(self): 274 | def check_result(file): 275 | self.open_file(file) 276 | 277 | self.push_screen(FileOpen(self.file), check_result) 278 | 279 | def action_save_file_as(self): 280 | def check_result(file): 281 | self.file = file 282 | self.title = self.file.name 283 | self.action_save_file() 284 | 285 | self.push_screen(FileSave(self.file), check_result) 286 | 287 | def action_show_about(self): 288 | self.push_screen(About()) 289 | 290 | def action_show_shortcuts(self): 291 | self.push_screen(Shortcuts()) 292 | 293 | def action_change_file_type(self): 294 | def check_result(result): 295 | self.file_type = result 296 | 297 | self.push_screen( 298 | Chooser( 299 | "Choose File Type", 300 | [("text", "text")] + [(l, l) for l in self.query_one("#text-buffer").available_languages], 301 | default=self.file_type 302 | ), 303 | check_result) 304 | 305 | def action_set_indent_type(self): 306 | def check_result(result): 307 | self.indent_type = result 308 | self.save_settings() 309 | 310 | self.push_screen( 311 | Chooser( 312 | "Choose indentation type", 313 | [("tabs", "Tabs"), ("spaces", "Spaces")], 314 | default=self.indent_type 315 | ), 316 | check_result) 317 | 318 | def action_toggle_dark_mode(self): 319 | self.dark = not self.dark 320 | self.save_settings() 321 | 322 | def action_toggle_line_numbers(self): 323 | self.show_line_numbers = not self.show_line_numbers 324 | self.save_settings() 325 | 326 | def action_toggle_soft_wrap(self): 327 | self.soft_wrap = not self.soft_wrap 328 | self.save_settings() 329 | 330 | def action_set_indent_width(self): 331 | def check_result(result): 332 | try: 333 | self.indent_width = int(result) 334 | except ValueError: 335 | self.notify(f"Invalid number: {result}", severity="error") 336 | self.save_settings() 337 | 338 | self.push_screen( 339 | InputPrompt( 340 | "Choose indentation level", 341 | str(self.indent_width) 342 | ), 343 | check_result) 344 | 345 | def action_change_theme(self): 346 | def check_result(result): 347 | self.theme = result 348 | self.save_settings() 349 | 350 | self.push_screen( 351 | Chooser( 352 | "Choose Theme", 353 | [(l, l) for l in self.query_one("#text-buffer").available_themes], 354 | default=self.theme 355 | ), 356 | check_result) 357 | 358 | def action_quit(self) -> None: 359 | if not self.file_unsaved: 360 | return self.exit() 361 | 362 | if len(self._screen_stack) > 1 and isinstance(self._screen_stack[-1], QuitScreen): 363 | self.exit() 364 | 365 | def check_quit(result): 366 | if result == "save-and-quit": 367 | if self.file: 368 | self.file.write_text(self.query_one("#text-buffer").text) 369 | self.exit() 370 | else: 371 | def check_result(file): 372 | self.file = file 373 | self.title = self.file.name 374 | self.action_save_file() 375 | self.exit() 376 | 377 | self.push_screen(FileSave(self.file), check_result) 378 | elif result == "quit-without-save": 379 | self.exit() 380 | elif result == "cancel": 381 | pass 382 | elif result == "suspend": 383 | self.action_suspend_process() 384 | else: 385 | self.notify(f"Unknown result: {result}", severity="error") 386 | 387 | self.push_screen(QuitScreen(), check_quit) 388 | 389 | ################################################################# 390 | ## Events ## 391 | ################################################################# 392 | @on(Input.Submitted, "#line_number") 393 | def on_line_number_submitted(self, event): 394 | linenumber = event.value 395 | if LineNumber().validate(linenumber).is_valid: 396 | if ':' in linenumber: 397 | line, col = linenumber.split(':', 1) 398 | line = int(line) - 1 399 | col = int(col) - 1 400 | else: 401 | line = int(linenumber) - 1 402 | col = 0 403 | self.query_one("#text-buffer").move_cursor((line, col)) 404 | self.show_line_finder = False 405 | self.query_one("#text-buffer").focus() 406 | else: 407 | self.notify(f'Not a valid line number', severity="error") 408 | 409 | @on(BetterTextArea.Changed, "#text-buffer") 410 | def on_text_buffer_changed(self, event): 411 | self.file_unsaved = True 412 | -------------------------------------------------------------------------------- /mehditor/screens/quit_screen.py: -------------------------------------------------------------------------------- 1 | from textual.app import ComposeResult 2 | from textual.binding import Binding 3 | from textual.containers import Grid, Vertical, Container 4 | from textual.screen import ModalScreen 5 | from textual.widgets import Label, Button 6 | 7 | 8 | class QuitScreen(ModalScreen): 9 | BINDINGS = [ 10 | Binding("s", "save_and_quit", "Save and quit", show=False), 11 | Binding("q", "quit_without_save", "Quit without saving", show=False), 12 | Binding("c", "cancel", "Cancel", show=False), 13 | Binding("u", "suspend", "Suspend", show=False), 14 | ] 15 | 16 | CSS = """ 17 | QuitScreen { 18 | align: center middle; 19 | } 20 | 21 | #dialog { 22 | width: 50; 23 | height: 22; 24 | border: thick $background 80%; 25 | background: $surface; 26 | } 27 | 28 | #question { 29 | align: center middle; 30 | height: 4; 31 | width: 100%; 32 | } 33 | 34 | Button { 35 | width: 100%; 36 | margin: 1 37 | } 38 | """ 39 | 40 | def compose(self) -> ComposeResult: 41 | with Vertical(id="dialog"): 42 | yield Container(Label("Save file before quitting?"), id="question") 43 | yield Button("S: Save and quit", id="save-and-quit") 44 | yield Button("Q: Quit without saving", variant="error", id="quit-without-save") 45 | yield Button("C: Cancel", id="cancel") 46 | yield Button("U: Suspend", id="suspend") 47 | 48 | def action_save_and_quit(self) -> None: 49 | self.dismiss("save-and-quit") 50 | 51 | def action_quit_without_save(self) -> None: 52 | self.dismiss("quit-without-save") 53 | 54 | def action_cancel(self) -> None: 55 | self.dismiss("cancel") 56 | 57 | def action_suspend(self) -> None: 58 | self.dismiss("suspend") 59 | 60 | def on_button_pressed(self, event: Button.Pressed) -> None: 61 | self.dismiss(event.button.id) 62 | -------------------------------------------------------------------------------- /mehditor/screens/shortcuts.py: -------------------------------------------------------------------------------- 1 | from textual.app import ComposeResult 2 | from textual.containers import Horizontal 3 | from textual.screen import ModalScreen 4 | from textual.scroll_view import ScrollView 5 | from textual.widgets import MarkdownViewer 6 | 7 | from mehditor import config 8 | 9 | SHORTCUTS = """ 10 | | Action | Shortcut | 11 | | ------ | -------- | 12 | | Menu | {menu} | 13 | | Command Palette | ctrl+\ | 14 | | Open File | {open_file} | 15 | | Show Shortcuts | {show_shortcuts} | 16 | | Quit | {quit} | 17 | | Suspend | {suspend_process} | 18 | | | 19 | | New File | {new_file} | 20 | | Open File | {open_file} | 21 | | Save File | {save_file} | 22 | | | 23 | | Go to Line | {goto_line} | 24 | | Cut | {cut} | 25 | | Copy | {copy} | 26 | | Paste | {paste} | 27 | | Undo | {undo_change} | 28 | | Redo | {redo_change} | 29 | | | 30 | | Move | ⬆️ ⬇️ ⬅️ ➡️ | 31 | | Move Word | ctrl + ⬅️ ➡️ | 32 | | Home | home, ctrl+a | 33 | | End | end, ctrl+e | 34 | | Page Up | pageup | 35 | | Page Down | pagedown | 36 | | | 37 | | Select | Shift + ⬆️ ⬇️ ⬅️ ➡️ | 38 | | Select Word | ctrl + shift + ⬅️ ➡️ | 39 | | Select to Line Start | shift+home | 40 | | Select to Line End | shift+end | 41 | | Select Line | f6 | 42 | | Select All | F7 | 43 | | | 44 | | Delete Left | backspace | 45 | | Delete Right | DEL, ctrl+d | 46 | | Delete Word Left | ctrl+w | 47 | | Delete Word Right | ctrl+f | 48 | | Delete Line | ctrl+x | 49 | | Delete Line Start | ctrl+u | 50 | | Delete Line End | ctrl+k | 51 | 52 | > Note that you can quickly execute menu items by typing escape then keybindings. (Eg, "escape, w, t" to change theme) 53 | 54 | ## Exit any dialog (including this one): Esc 55 | """ 56 | 57 | class Shortcuts(ModalScreen): 58 | CSS = """ 59 | Shortcuts { 60 | align: center middle; 61 | } 62 | 63 | #dialog { 64 | height: 90%; 65 | width: 90%; 66 | border: solid; 67 | padding: 0; 68 | margin: 0; 69 | } 70 | 71 | MarkdownViewer { 72 | padding: 0; 73 | margin: 0; 74 | } 75 | """ 76 | 77 | def compose(self) -> ComposeResult: 78 | shortcuts = SHORTCUTS.format(**config.settings['shortcuts']) 79 | with ScrollView(id="dialog"): 80 | with Horizontal(): 81 | yield MarkdownViewer(shortcuts, show_table_of_contents=False) 82 | -------------------------------------------------------------------------------- /mehditor/validators.py: -------------------------------------------------------------------------------- 1 | from textual.validation import Integer, Validator, Failure, ValidationResult 2 | 3 | 4 | class LineNumber(Validator): 5 | class NotALineNumber(Failure): 6 | pass 7 | 8 | def validate(self, value: str): 9 | if value.count(":") == 1: 10 | line, col = value.split(':', 1) 11 | return Integer(minimum=1).validate(line) and Integer(minimum=1).validate(col) 12 | elif value.count(":") == 0: 13 | return Integer(minimum=1).validate(value) 14 | else: 15 | return ValidationResult.failure([LineNumber.NotALineNumber(self, value)]) 16 | 17 | def describe_failure(self, failure: Failure): 18 | return "Must be a line number (with optional column); eg 10:3 for line 10, col 3" 19 | -------------------------------------------------------------------------------- /mehditor/widgets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkinder/mehditor/fa37154b3b421cb425322054946de956097b2891/mehditor/widgets/__init__.py -------------------------------------------------------------------------------- /mehditor/widgets/better_text_area.py: -------------------------------------------------------------------------------- 1 | from textual.binding import Binding 2 | from textual.widgets import TextArea 3 | 4 | from mehditor.config import generate_binding 5 | 6 | 7 | class BetterTextArea(TextArea): 8 | BINDINGS = [ 9 | generate_binding("menu"), 10 | generate_binding("open_file"), 11 | generate_binding("show_shortcuts"), 12 | 13 | generate_binding("cut"), 14 | generate_binding("copy"), 15 | generate_binding("paste"), 16 | generate_binding("undo_change"), 17 | generate_binding("redo_change"), 18 | 19 | # Cursor movement 20 | Binding("up", "cursor_up", "cursor up", show=False), 21 | Binding("down", "cursor_down", "cursor down", show=False), 22 | Binding("left", "cursor_left", "cursor left", show=False), 23 | Binding("right", "cursor_right", "cursor right", show=False), 24 | Binding("ctrl+left", "cursor_word_left", "cursor word left", show=False), 25 | Binding("ctrl+right", "cursor_word_right", "cursor word right", show=False), 26 | Binding("home,ctrl+a", "cursor_line_start", "cursor line start", show=False), 27 | Binding("end,ctrl+e", "cursor_line_end", "cursor line end", show=False), 28 | Binding("pageup", "cursor_page_up", "cursor page up", show=False), 29 | Binding("pagedown", "cursor_page_down", "cursor page down", show=False), 30 | # Making selections (generally holding the shift key and moving cursor) 31 | Binding( 32 | "ctrl+shift+left", 33 | "cursor_word_left(True)", 34 | "cursor left word select", 35 | show=False, 36 | ), 37 | Binding( 38 | "ctrl+shift+right", 39 | "cursor_word_right(True)", 40 | "cursor right word select", 41 | show=False, 42 | ), 43 | Binding( 44 | "shift+home", 45 | "cursor_line_start(True)", 46 | "cursor line start select", 47 | show=False, 48 | ), 49 | Binding( 50 | "shift+end", "cursor_line_end(True)", "cursor line end select", show=False 51 | ), 52 | Binding("shift+up", "cursor_up(True)", "cursor up select", show=False), 53 | Binding("shift+down", "cursor_down(True)", "cursor down select", show=False), 54 | Binding("shift+left", "cursor_left(True)", "cursor left select", show=False), 55 | Binding("shift+right", "cursor_right(True)", "cursor right select", show=False), 56 | # Shortcut ways of making selections 57 | Binding("f6", "select_line", "select line", show=False), 58 | Binding("f7", "select_all", "select all", show=False), 59 | 60 | # Deletion 61 | Binding("backspace", "delete_left", "delete left", show=False), 62 | Binding( 63 | "ctrl+w", "delete_word_left", "delete left to start of word", show=False 64 | ), 65 | Binding("delete,ctrl+d", "delete_right", "delete right", show=False), 66 | Binding( 67 | "ctrl+f", "delete_word_right", "delete right to start of word", show=False 68 | ), 69 | Binding("ctrl+x", "delete_line", "delete line", show=False), 70 | Binding( 71 | "ctrl+u", "delete_to_start_of_line", "delete to line start", show=False 72 | ), 73 | Binding("ctrl+k", "delete_to_end_of_line", "delete to line end", show=False), 74 | 75 | ] 76 | # pass 77 | # BINDINGS = [ 78 | # Binding("escape", "menu", "Menu", priority=True, show=True, key_display="ESC") 79 | # ] + TextArea.BINDINGS[1:] 80 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "aiohttp" 5 | version = "3.9.3" 6 | description = "Async http client/server framework (asyncio)" 7 | optional = false 8 | python-versions = ">=3.8" 9 | files = [ 10 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, 11 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, 12 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, 13 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, 14 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, 15 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, 16 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, 17 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, 18 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, 19 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, 20 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, 21 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, 22 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, 23 | {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, 24 | {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, 25 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, 26 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, 27 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, 28 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, 29 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, 30 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, 31 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, 32 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, 33 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, 34 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, 35 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, 36 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, 37 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, 38 | {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, 39 | {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, 40 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, 41 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, 42 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, 43 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, 44 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, 45 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, 46 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, 47 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, 48 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, 49 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, 50 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, 51 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, 52 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, 53 | {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, 54 | {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, 55 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, 56 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, 57 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, 58 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, 59 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, 60 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, 61 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, 62 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, 63 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, 64 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, 65 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, 66 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, 67 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, 68 | {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, 69 | {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, 70 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, 71 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, 72 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, 73 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, 74 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, 75 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, 76 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, 77 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, 78 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, 79 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, 80 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, 81 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, 82 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, 83 | {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, 84 | {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, 85 | {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, 86 | ] 87 | 88 | [package.dependencies] 89 | aiosignal = ">=1.1.2" 90 | async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} 91 | attrs = ">=17.3.0" 92 | frozenlist = ">=1.1.1" 93 | multidict = ">=4.5,<7.0" 94 | yarl = ">=1.0,<2.0" 95 | 96 | [package.extras] 97 | speedups = ["Brotli", "aiodns", "brotlicffi"] 98 | 99 | [[package]] 100 | name = "aiosignal" 101 | version = "1.3.1" 102 | description = "aiosignal: a list of registered asynchronous callbacks" 103 | optional = false 104 | python-versions = ">=3.7" 105 | files = [ 106 | {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, 107 | {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, 108 | ] 109 | 110 | [package.dependencies] 111 | frozenlist = ">=1.1.0" 112 | 113 | [[package]] 114 | name = "async-timeout" 115 | version = "4.0.3" 116 | description = "Timeout context manager for asyncio programs" 117 | optional = false 118 | python-versions = ">=3.7" 119 | files = [ 120 | {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, 121 | {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, 122 | ] 123 | 124 | [[package]] 125 | name = "attrs" 126 | version = "23.2.0" 127 | description = "Classes Without Boilerplate" 128 | optional = false 129 | python-versions = ">=3.7" 130 | files = [ 131 | {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, 132 | {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, 133 | ] 134 | 135 | [package.extras] 136 | cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] 137 | dev = ["attrs[tests]", "pre-commit"] 138 | docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] 139 | tests = ["attrs[tests-no-zope]", "zope-interface"] 140 | tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] 141 | tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] 142 | 143 | [[package]] 144 | name = "click" 145 | version = "8.1.7" 146 | description = "Composable command line interface toolkit" 147 | optional = false 148 | python-versions = ">=3.7" 149 | files = [ 150 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 151 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 152 | ] 153 | 154 | [package.dependencies] 155 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 156 | 157 | [[package]] 158 | name = "colorama" 159 | version = "0.4.6" 160 | description = "Cross-platform colored terminal text." 161 | optional = false 162 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 163 | files = [ 164 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 165 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 166 | ] 167 | 168 | [[package]] 169 | name = "frozenlist" 170 | version = "1.4.1" 171 | description = "A list-like structure which implements collections.abc.MutableSequence" 172 | optional = false 173 | python-versions = ">=3.8" 174 | files = [ 175 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, 176 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, 177 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, 178 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, 179 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, 180 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, 181 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, 182 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, 183 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, 184 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, 185 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, 186 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, 187 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, 188 | {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, 189 | {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, 190 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, 191 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, 192 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, 193 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, 194 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, 195 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, 196 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, 197 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, 198 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, 199 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, 200 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, 201 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, 202 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, 203 | {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, 204 | {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, 205 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, 206 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, 207 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, 208 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, 209 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, 210 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, 211 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, 212 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, 213 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, 214 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, 215 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, 216 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, 217 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, 218 | {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, 219 | {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, 220 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, 221 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, 222 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, 223 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, 224 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, 225 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, 226 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, 227 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, 228 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, 229 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, 230 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, 231 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, 232 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, 233 | {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, 234 | {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, 235 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, 236 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, 237 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, 238 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, 239 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, 240 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, 241 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, 242 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, 243 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, 244 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, 245 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, 246 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, 247 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, 248 | {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, 249 | {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, 250 | {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, 251 | {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, 252 | ] 253 | 254 | [[package]] 255 | name = "idna" 256 | version = "3.6" 257 | description = "Internationalized Domain Names in Applications (IDNA)" 258 | optional = false 259 | python-versions = ">=3.5" 260 | files = [ 261 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 262 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 263 | ] 264 | 265 | [[package]] 266 | name = "linkify-it-py" 267 | version = "2.0.3" 268 | description = "Links recognition library with FULL unicode support." 269 | optional = false 270 | python-versions = ">=3.7" 271 | files = [ 272 | {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}, 273 | {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}, 274 | ] 275 | 276 | [package.dependencies] 277 | uc-micro-py = "*" 278 | 279 | [package.extras] 280 | benchmark = ["pytest", "pytest-benchmark"] 281 | dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] 282 | doc = ["myst-parser", "sphinx", "sphinx-book-theme"] 283 | test = ["coverage", "pytest", "pytest-cov"] 284 | 285 | [[package]] 286 | name = "markdown-it-py" 287 | version = "3.0.0" 288 | description = "Python port of markdown-it. Markdown parsing, done right!" 289 | optional = false 290 | python-versions = ">=3.8" 291 | files = [ 292 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 293 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 294 | ] 295 | 296 | [package.dependencies] 297 | linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""} 298 | mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""} 299 | mdurl = ">=0.1,<1.0" 300 | 301 | [package.extras] 302 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 303 | code-style = ["pre-commit (>=3.0,<4.0)"] 304 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 305 | linkify = ["linkify-it-py (>=1,<3)"] 306 | plugins = ["mdit-py-plugins"] 307 | profiling = ["gprof2dot"] 308 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 309 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 310 | 311 | [[package]] 312 | name = "mdit-py-plugins" 313 | version = "0.4.0" 314 | description = "Collection of plugins for markdown-it-py" 315 | optional = false 316 | python-versions = ">=3.8" 317 | files = [ 318 | {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, 319 | {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, 320 | ] 321 | 322 | [package.dependencies] 323 | markdown-it-py = ">=1.0.0,<4.0.0" 324 | 325 | [package.extras] 326 | code-style = ["pre-commit"] 327 | rtd = ["myst-parser", "sphinx-book-theme"] 328 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 329 | 330 | [[package]] 331 | name = "mdurl" 332 | version = "0.1.2" 333 | description = "Markdown URL utilities" 334 | optional = false 335 | python-versions = ">=3.7" 336 | files = [ 337 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 338 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 339 | ] 340 | 341 | [[package]] 342 | name = "msgpack" 343 | version = "1.0.7" 344 | description = "MessagePack serializer" 345 | optional = false 346 | python-versions = ">=3.8" 347 | files = [ 348 | {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"}, 349 | {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"}, 350 | {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"}, 351 | {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"}, 352 | {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"}, 353 | {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"}, 354 | {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"}, 355 | {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"}, 356 | {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"}, 357 | {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"}, 358 | {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"}, 359 | {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"}, 360 | {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"}, 361 | {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"}, 362 | {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"}, 363 | {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"}, 364 | {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"}, 365 | {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"}, 366 | {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"}, 367 | {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"}, 368 | {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"}, 369 | {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"}, 370 | {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"}, 371 | {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"}, 372 | {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"}, 373 | {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"}, 374 | {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"}, 375 | {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"}, 376 | {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"}, 377 | {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"}, 378 | {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"}, 379 | {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"}, 380 | {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"}, 381 | {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"}, 382 | {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"}, 383 | {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"}, 384 | {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"}, 385 | {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"}, 386 | {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"}, 387 | {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"}, 388 | {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"}, 389 | {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"}, 390 | {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"}, 391 | {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"}, 392 | {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"}, 393 | {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"}, 394 | {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"}, 395 | {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"}, 396 | {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"}, 397 | {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"}, 398 | {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"}, 399 | {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"}, 400 | {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"}, 401 | {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"}, 402 | {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"}, 403 | {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, 404 | ] 405 | 406 | [[package]] 407 | name = "multidict" 408 | version = "6.0.5" 409 | description = "multidict implementation" 410 | optional = false 411 | python-versions = ">=3.7" 412 | files = [ 413 | {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, 414 | {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, 415 | {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, 416 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, 417 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, 418 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, 419 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, 420 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, 421 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, 422 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, 423 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, 424 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, 425 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, 426 | {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, 427 | {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, 428 | {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, 429 | {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, 430 | {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, 431 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, 432 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, 433 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, 434 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, 435 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, 436 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, 437 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, 438 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, 439 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, 440 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, 441 | {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, 442 | {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, 443 | {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, 444 | {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, 445 | {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, 446 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, 447 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, 448 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, 449 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, 450 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, 451 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, 452 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, 453 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, 454 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, 455 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, 456 | {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, 457 | {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, 458 | {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, 459 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, 460 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, 461 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, 462 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, 463 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, 464 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, 465 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, 466 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, 467 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, 468 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, 469 | {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, 470 | {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, 471 | {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, 472 | {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, 473 | {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, 474 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, 475 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, 476 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, 477 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, 478 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, 479 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, 480 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, 481 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, 482 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, 483 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, 484 | {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, 485 | {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, 486 | {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, 487 | {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, 488 | {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, 489 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, 490 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, 491 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, 492 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, 493 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, 494 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, 495 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, 496 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, 497 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, 498 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, 499 | {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, 500 | {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, 501 | {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, 502 | {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, 503 | ] 504 | 505 | [[package]] 506 | name = "pygments" 507 | version = "2.17.2" 508 | description = "Pygments is a syntax highlighting package written in Python." 509 | optional = false 510 | python-versions = ">=3.7" 511 | files = [ 512 | {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, 513 | {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, 514 | ] 515 | 516 | [package.extras] 517 | plugins = ["importlib-metadata"] 518 | windows-terminal = ["colorama (>=0.4.6)"] 519 | 520 | [[package]] 521 | name = "rich" 522 | version = "13.7.0" 523 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 524 | optional = false 525 | python-versions = ">=3.7.0" 526 | files = [ 527 | {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, 528 | {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, 529 | ] 530 | 531 | [package.dependencies] 532 | markdown-it-py = ">=2.2.0" 533 | pygments = ">=2.13.0,<3.0.0" 534 | 535 | [package.extras] 536 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 537 | 538 | [[package]] 539 | name = "setuptools" 540 | version = "69.1.0" 541 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 542 | optional = false 543 | python-versions = ">=3.8" 544 | files = [ 545 | {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, 546 | {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, 547 | ] 548 | 549 | [package.extras] 550 | docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] 551 | testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] 552 | testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] 553 | 554 | [[package]] 555 | name = "textual" 556 | version = "0.58.0" 557 | description = "Modern Text User Interface framework" 558 | optional = false 559 | python-versions = "<4.0,>=3.8" 560 | files = [ 561 | {file = "textual-0.58.0-py3-none-any.whl", hash = "sha256:9daddf713cb64d186fa1ae647fea482dc84b643c9284132cd87adb99cd81d638"}, 562 | {file = "textual-0.58.0.tar.gz", hash = "sha256:5c8c3322308e2b932c4550b0ae9f70daebc39716de3f920831cda96d1640b383"}, 563 | ] 564 | 565 | [package.dependencies] 566 | markdown-it-py = {version = ">=2.1.0", extras = ["linkify", "plugins"]} 567 | rich = ">=13.3.3" 568 | tree-sitter = {version = ">=0.20.1,<0.21.0", optional = true, markers = "extra == \"syntax\""} 569 | tree-sitter-languages = {version = "1.10.2", optional = true, markers = "extra == \"syntax\""} 570 | typing-extensions = ">=4.4.0,<5.0.0" 571 | 572 | [package.extras] 573 | syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree-sitter-languages (==1.10.2)"] 574 | 575 | [[package]] 576 | name = "textual-dev" 577 | version = "1.5.1" 578 | description = "Development tools for working with Textual" 579 | optional = false 580 | python-versions = ">=3.8,<4.0" 581 | files = [ 582 | {file = "textual_dev-1.5.1-py3-none-any.whl", hash = "sha256:bb37dd769ae6b67e1422aa97f6d6ef952e0a6d2aafe08327449e8bdd70474776"}, 583 | {file = "textual_dev-1.5.1.tar.gz", hash = "sha256:e0366ab6f42c128d7daa37a7c418e61fe7aa83731983da990808e4bf2de922a1"}, 584 | ] 585 | 586 | [package.dependencies] 587 | aiohttp = ">=3.8.1" 588 | click = ">=8.1.2" 589 | msgpack = ">=1.0.3" 590 | textual = ">=0.36.0" 591 | typing-extensions = ">=4.4.0,<5.0.0" 592 | 593 | [[package]] 594 | name = "tree-sitter" 595 | version = "0.20.4" 596 | description = "Python bindings for the Tree-Sitter parsing library" 597 | optional = false 598 | python-versions = ">=3.3" 599 | files = [ 600 | {file = "tree_sitter-0.20.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c259b9bcb596e54f54713eb3951226fc834d65289940f4bfdcdf519f08e8e876"}, 601 | {file = "tree_sitter-0.20.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88da7e2e4c69881cd63916cc24ae0b809f96aae331da45b418ae6b2d1ed2ca19"}, 602 | {file = "tree_sitter-0.20.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66a68b156ba131e9d8dff4a1f72037f4b368cc50c58f18905a91743ae1d1c795"}, 603 | {file = "tree_sitter-0.20.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae28e25d551f406807011487bdfb9728041e656b30b554fa7f3391ab64ed69f9"}, 604 | {file = "tree_sitter-0.20.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36b10c9c69e825ba65cf9b0f77668bf33e70d2a5764b64ad6f133f8cc9220f09"}, 605 | {file = "tree_sitter-0.20.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7c18c64ddd44b75b7e1660b9793753eda427e4b145b6216d4b2d2e9b200c74f2"}, 606 | {file = "tree_sitter-0.20.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e9e9e594bbefb76ad9ea256f5c87eba7591b4758854d3df83ce4df415933a006"}, 607 | {file = "tree_sitter-0.20.4-cp310-cp310-win32.whl", hash = "sha256:b4755229dc18644fe48bcab974bde09b171fcb6ef625d3cb5ece5c6198f4223e"}, 608 | {file = "tree_sitter-0.20.4-cp310-cp310-win_amd64.whl", hash = "sha256:f792684cee8a46d9194d9f4223810e54ccc704470c5777538d59fbde0a4c91bf"}, 609 | {file = "tree_sitter-0.20.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d22ee75f45836554ee6a11e50dd8f9827941e67c49fce9a0790245b899811a9"}, 610 | {file = "tree_sitter-0.20.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a0ffd76dd991ba745bb5d0ba1d583bec85726d3ddef8c9685dc8636a619adde"}, 611 | {file = "tree_sitter-0.20.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:060d4e5b803be0975f1ac46e54a292eab0701296ccd912f6cdac3f7331e29143"}, 612 | {file = "tree_sitter-0.20.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822e02366dbf223697b2b56b8f91aa5b60571f9fe7c998988a381db1c69604e9"}, 613 | {file = "tree_sitter-0.20.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:527ca72c6a8f60fa719af37fa86f58b7ad0e07b8f74d1c1c7e926c5c888a7e6b"}, 614 | {file = "tree_sitter-0.20.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a418ca71309ea7052e076f08d623f33f58eae01a8e8cdc1e6d3a01b5b8ddebfe"}, 615 | {file = "tree_sitter-0.20.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:08c3ba2561b61a83c28ca06a0bce2a5ffcfb6b39f9d27a45e5ebd9cad2bedb7f"}, 616 | {file = "tree_sitter-0.20.4-cp311-cp311-win32.whl", hash = "sha256:8d04c75a389b2de94952d602264852acff8cd3ed1ccf8a2492a080973d5ddd58"}, 617 | {file = "tree_sitter-0.20.4-cp311-cp311-win_amd64.whl", hash = "sha256:ba9215c0e7529d9eb370528e5d99b7389d14a7eae94f07d14fa9dab18f267c62"}, 618 | {file = "tree_sitter-0.20.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c4c1af5ed4306071d30970c83ec882520a7bf5d8053996dbc4aa5c59238d4990"}, 619 | {file = "tree_sitter-0.20.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9d70bfa550cf22c9cea9b3c0d18b889fc4f2a7e9dcf1d6cc93f49fa9d4a94954"}, 620 | {file = "tree_sitter-0.20.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6de537bca0641775d8d175d37303d54998980fc0d997dd9aa89e16b415bf0cc3"}, 621 | {file = "tree_sitter-0.20.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b1c0f8c0e3e50267566f5116cdceedf4e23e8c08b55ef3becbe954a11b16e84"}, 622 | {file = "tree_sitter-0.20.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef2ee6d9bb8e21713949e5ff769ed670fe1217f95b7eeb6c675788438c1e6e"}, 623 | {file = "tree_sitter-0.20.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b6fd1c881ab0de5faa67168db2d001eee32be5482cb4e0b21b217689a05b6fe4"}, 624 | {file = "tree_sitter-0.20.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf47047420021d50aec529cb66387c90562350b499ddf56ecef1fc8255439e30"}, 625 | {file = "tree_sitter-0.20.4-cp312-cp312-win32.whl", hash = "sha256:c16b48378041fc9702b6aa3480f2ffa49ca8ea58141a862acd569e5a0679655f"}, 626 | {file = "tree_sitter-0.20.4-cp312-cp312-win_amd64.whl", hash = "sha256:973e871167079a1b1d7304d361449253efbe2a6974728ad563cf407bd02ddccb"}, 627 | {file = "tree_sitter-0.20.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9d33a55598dd18a4d8b869a3417de82a4812c3a7dc7e61cb025ece3e9c3e4e96"}, 628 | {file = "tree_sitter-0.20.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cee6955c2c97fc5927a41c7a8b06647c4b4d9b99b8a1581bf1183435c8cec3e"}, 629 | {file = "tree_sitter-0.20.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5022bea67e479ad212be7c05b983a72e297a013efb4e8ea5b5b4d7da79a9fdef"}, 630 | {file = "tree_sitter-0.20.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:640f60a5b966f0990338f1bf559455c3dcb822bc4329d82b3d42f32a48374dfe"}, 631 | {file = "tree_sitter-0.20.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0e83f641fe6f27d91bd4d259fff5d35de1567d3f581b9efe9bbd5be50fe4ddc7"}, 632 | {file = "tree_sitter-0.20.4-cp36-cp36m-win32.whl", hash = "sha256:ce6a85027c66fa3f09d482cc6d41927ea40955f7f33b86aedd26dd932709a2c9"}, 633 | {file = "tree_sitter-0.20.4-cp36-cp36m-win_amd64.whl", hash = "sha256:fe10779347a6c067af29cb37fd4b75fa96c5cb68f587cc9530b70fe3f2a51a55"}, 634 | {file = "tree_sitter-0.20.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28d5f84e34e276887e3a240b60906ca7e2b51e975f3145c3149ceed977a69508"}, 635 | {file = "tree_sitter-0.20.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c913b65cbe10996116988ac436748f24883b5097e58274223e89bb2c5d1bb1a"}, 636 | {file = "tree_sitter-0.20.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecaed46241e071752195a628bb97d2b740f2fde9e34f8a74456a4ea8bb26df88"}, 637 | {file = "tree_sitter-0.20.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b641e88a97eab002a1736d93ef5a4beac90ea4fd6e25affd1831319b99f456c9"}, 638 | {file = "tree_sitter-0.20.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:327c40f439c6155e4eee54c4657e4701a04f5f4816d9defdcb836bf65bf83d21"}, 639 | {file = "tree_sitter-0.20.4-cp37-cp37m-win32.whl", hash = "sha256:1b7c1d95f006b3de42fbf4045bd00c273d113e372fcb6a5378e74ed120c12032"}, 640 | {file = "tree_sitter-0.20.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6140d037239a41046f5d34fba5e0374ee697adb4b48b90579c618b5402781c11"}, 641 | {file = "tree_sitter-0.20.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f42fd1104efaad8151370f1936e2a488b7337a5d24544a9ab59ba4c4010b1272"}, 642 | {file = "tree_sitter-0.20.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7859717c5d62ee386b3d036cab8ed0f88f8c027b6b4ae476a55a8c5fb8aab713"}, 643 | {file = "tree_sitter-0.20.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdd361fe1cc68db68b4d85165641275e34b86cc26b2bab932790204fa14824dc"}, 644 | {file = "tree_sitter-0.20.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b8d7539075606027b67764543463ff2bc4e52f4158ef6dc419c9f5625aa5383"}, 645 | {file = "tree_sitter-0.20.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78e76307f05aca6cde72f3307b4d53701f34ae45f2248ceb83d1626051e201fd"}, 646 | {file = "tree_sitter-0.20.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd8c352f4577f61098d06cf3feb7fd214259f41b5036b81003860ed54d16b448"}, 647 | {file = "tree_sitter-0.20.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:281f3e5382d1bd7fccc88d1afe68c915565bc24f8b8dd4844079d46c7815b8a7"}, 648 | {file = "tree_sitter-0.20.4-cp38-cp38-win32.whl", hash = "sha256:6a77ac3cdcddd80cdd1fd394318bff99f94f37e08d235aaefccb87e1224946e5"}, 649 | {file = "tree_sitter-0.20.4-cp38-cp38-win_amd64.whl", hash = "sha256:8eee8adf54033dc48eab84b040f4d7b32355a964c4ae0aae5dfbdc4dbc3364ca"}, 650 | {file = "tree_sitter-0.20.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e89f6508e30fce05e2c724725d022db30d877817b9d64f933506ffb3a3f4a2c2"}, 651 | {file = "tree_sitter-0.20.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fb6286bb1fae663c45ff0700ec88fb9b50a81eed2bae8a291f95fcf8cc19547"}, 652 | {file = "tree_sitter-0.20.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11e93f8b4bbae04070416a82257a7ab2eb0afb76e093ae3ea73bd63b792f6846"}, 653 | {file = "tree_sitter-0.20.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8250725c5f78929aeb2c71db5dca76f1ef448389ca16f9439161f90978bb8478"}, 654 | {file = "tree_sitter-0.20.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d404a8ca9de9b0843844f0cd4d423f46bc46375ab8afb63b1d8ec01201457ac8"}, 655 | {file = "tree_sitter-0.20.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0f2422c9ee70ba972dfc3943746e6cf7fc03725a866908950245bda9ccfc7301"}, 656 | {file = "tree_sitter-0.20.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:21a937942e4729abbe778a609d2c218574436cb351c36fba89ef3c8c6066ec78"}, 657 | {file = "tree_sitter-0.20.4-cp39-cp39-win32.whl", hash = "sha256:427a9a39360cc1816e28f8182550e478e4ba983595a2565ab9dfe32ea1b03fd7"}, 658 | {file = "tree_sitter-0.20.4-cp39-cp39-win_amd64.whl", hash = "sha256:7095bb9aff297fa9c6026bf8914fd295997d714d1a6ee9a1edf7282c772f9f64"}, 659 | {file = "tree_sitter-0.20.4-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:859260b90f0e3867ae840e39f54e830f607b3bc531bc21deeeeaa8a30cbb89ad"}, 660 | {file = "tree_sitter-0.20.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dfc14be73cf46126660a3aecdd0396e69562ad1a902245225ca7bd29649594e"}, 661 | {file = "tree_sitter-0.20.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec46355bf3ff23f54d5e365871ffd3e05cfbc65d1b36a8be7c0bcbda30a1d43"}, 662 | {file = "tree_sitter-0.20.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d933a942fde39876b99c36f12aa3764e4a555ae9366c10ce6cca8c16341c1bbf"}, 663 | {file = "tree_sitter-0.20.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7eec3b55135fe851a38fa248c9fd75fc3d58ceb6e1865b795e416e4d598c2a1"}, 664 | {file = "tree_sitter-0.20.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc76225529ee14a53e84413480ce81ec3c44eaa0455c140e961c90ac3118ead"}, 665 | {file = "tree_sitter-0.20.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccf0396e47efffc0b528959a8f2e2346a98297579f867e9e1834c2aad4be829c"}, 666 | {file = "tree_sitter-0.20.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a15fbabd3bc8e29c48289c156d743e69f5ec72bb125cf44f7adbdaa1937c3da6"}, 667 | {file = "tree_sitter-0.20.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:36f8adf2126f496cf376b6e4b707cba061c25beb17841727eef6f0e083e53e1f"}, 668 | {file = "tree_sitter-0.20.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841efb40c116ab0a066924925409a8a4dcffeb39a151c0b2a1c2abe56ad4fb42"}, 669 | {file = "tree_sitter-0.20.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2051e8a70fd8426f27a43dad71d11929a62ce30a9b1eb65bba0ed79e82481592"}, 670 | {file = "tree_sitter-0.20.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:99a3c2824d4cfcffd9f961176891426bde2cb36ece5280c61480be93319c23c4"}, 671 | {file = "tree_sitter-0.20.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:72830dc85a10430eca3d56739b7efcd7a05459c8d425f08c1aee6179ab7f13a9"}, 672 | {file = "tree_sitter-0.20.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4992dd226055b6cd0a4f5661c66b799a73d3eff716302e0f7ab06594ee12d49f"}, 673 | {file = "tree_sitter-0.20.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a66d95bbf92175cdc295d6d77f330942811f02e3aaf3fc64431cb749683b2f7d"}, 674 | {file = "tree_sitter-0.20.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a25b1087e4f7825b2458dacf5f4b0be2938f78e850e822edca1ff4994b56081a"}, 675 | {file = "tree_sitter-0.20.4.tar.gz", hash = "sha256:6adb123e2f3e56399bbf2359924633c882cc40ee8344885200bca0922f713be5"}, 676 | ] 677 | 678 | [package.dependencies] 679 | setuptools = {version = ">=60.0.0", markers = "python_version >= \"3.12\""} 680 | 681 | [[package]] 682 | name = "tree-sitter-languages" 683 | version = "1.10.2" 684 | description = "Binary Python wheels for all tree sitter languages." 685 | optional = false 686 | python-versions = "*" 687 | files = [ 688 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5580348f0b20233b1d5431fa178ccd3d07423ca4a3275df02a44608fd72344b9"}, 689 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:103c7466644486b1e9e03850df46fc6aa12f13ca636c74f173270276220ac80b"}, 690 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d13db84511c6f1a7dc40383b66deafa74dabd8b877e3d65ab253f3719eccafd6"}, 691 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57adfa32be7e465b54aa72f915f6c78a2b66b227df4f656b5d4fbd1ca7a92b3f"}, 692 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6385e033e460ceb8f33f3f940335f422ef2b763700a04f0089391a68b56153"}, 693 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dfa3f38cc5381c5aba01dd7494f59b8a9050e82ff6e06e1233e3a0cbae297e3c"}, 694 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9f195155acf47f8bc5de7cee46ecd07b2f5697f007ba89435b51ef4c0b953ea5"}, 695 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2de330e2ac6d7426ca025a3ec0f10d5640c3682c1d0c7702e812dcfb44b58120"}, 696 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-win32.whl", hash = "sha256:c9731cf745f135d9770eeba9bb4e2ff4dabc107b5ae9b8211e919f6b9100ea6d"}, 697 | {file = "tree_sitter_languages-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:6dd75851c41d0c3c4987a9b7692d90fa8848706c23115669d8224ffd6571e357"}, 698 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7eb7d7542b2091c875fe52719209631fca36f8c10fa66970d2c576ae6a1b8289"}, 699 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b41bcb00974b1c8a1800c7f1bb476a1d15a0463e760ee24872f2d53b08ee424"}, 700 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f370cd7845c6c81df05680d5bd96db8a99d32b56f4728c5d05978911130a853"}, 701 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1dc195c88ef4c72607e112a809a69190e096a2e5ebc6201548b3e05fdd169ad"}, 702 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae34ac314a7170be24998a0f994c1ac80761d8d4bd126af27ee53a023d3b849"}, 703 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:01b5742d5f5bd675489486b582bd482215880b26dde042c067f8265a6e925d9c"}, 704 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ab1cbc46244d34fd16f21edaa20231b2a57f09f092a06ee3d469f3117e6eb954"}, 705 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0b1149e7467a4e92b8a70e6005fe762f880f493cf811fc003554b29f04f5e7c8"}, 706 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-win32.whl", hash = "sha256:049276343962f4696390ee555acc2c1a65873270c66a6cbe5cb0bca83bcdf3c6"}, 707 | {file = "tree_sitter_languages-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:7f3fdd468a577f04db3b63454d939e26e360229b53c80361920aa1ebf2cd7491"}, 708 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c0f4c8b2734c45859edc7fcaaeaab97a074114111b5ba51ab4ec7ed52104763c"}, 709 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eecd3c1244ac3425b7a82ba9125b4ddb45d953bbe61de114c0334fd89b7fe782"}, 710 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15db3c8510bc39a80147ee7421bf4782c15c09581c1dc2237ea89cefbd95b846"}, 711 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92c6487a6feea683154d3e06e6db68c30e0ae749a7ce4ce90b9e4e46b78c85c7"}, 712 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2f1cd1d1bdd65332f9c2b67d49dcf148cf1ded752851d159ac3e5ee4f4d260"}, 713 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:976c8039165b8e12f17a01ddee9f4e23ec6e352b165ad29b44d2bf04e2fbe77e"}, 714 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:dafbbdf16bf668a580902e1620f4baa1913e79438abcce721a50647564c687b9"}, 715 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1aeabd3d60d6d276b73cd8f3739d595b1299d123cc079a317f1a5b3c5461e2ca"}, 716 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-win32.whl", hash = "sha256:fab8ee641914098e8933b87ea3d657bea4dd00723c1ee7038b847b12eeeef4f5"}, 717 | {file = "tree_sitter_languages-1.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:5e606430d736367e5787fa5a7a0c5a1ec9b85eded0b3596bbc0d83532a40810b"}, 718 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:838d5b48a7ed7a17658721952c77fda4570d2a069f933502653b17e15a9c39c9"}, 719 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b3c71b1d278c2889e018ee77b8ee05c384e2e3334dec798f8b611c4ab2d1e"}, 720 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faa00abcb2c819027df58472da055d22fa7dfcb77c77413d8500c32ebe24d38b"}, 721 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e102fbbf02322d9201a86a814e79a9734ac80679fdb9682144479044f401a73"}, 722 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8f0b87cf1a7b03174ba18dfd81582be82bfed26803aebfe222bd20e444aba003"}, 723 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c0f1b9af9cb67f0b942b020da9fdd000aad5e92f2383ae0ba7a330b318d31912"}, 724 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5a4076c921f7a4d31e643843de7dfe040b65b63a238a5aa8d31d93aabe6572aa"}, 725 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-win32.whl", hash = "sha256:fa6391a3a5d83d32db80815161237b67d70576f090ce5f38339206e917a6f8bd"}, 726 | {file = "tree_sitter_languages-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:55649d3f254585a064121513627cf9788c1cfdadbc5f097f33d5ba750685a4c0"}, 727 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6f85d1edaa2d22d80d4ea5b6d12b95cf3644017b6c227d0d42854439e02e8893"}, 728 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d78feed4a764ef3141cb54bf00fe94d514d8b6e26e09423e23b4c616fcb7938c"}, 729 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1aca27531f9dd5308637d76643372856f0f65d0d28677d1bcf4211e8ed1ad0"}, 730 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1031ea440dafb72237437d754eff8940153a3b051e3d18932ac25e75ce060a15"}, 731 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99d3249beaef2c9fe558ecc9a97853c260433a849dcc68266d9770d196c2e102"}, 732 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:59a4450f262a55148fb7e68681522f0c2a2f6b7d89666312a2b32708d8f416e1"}, 733 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ce74eab0e430370d5e15a96b6c6205f93405c177a8b2e71e1526643b2fb9bab1"}, 734 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9b4dd2b6b3d24c85dffe33d6c343448869eaf4f41c19ddba662eb5d65d8808f4"}, 735 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-win32.whl", hash = "sha256:92d734fb968fe3927a7596d9f0459f81a8fa7b07e16569476b28e27d0d753348"}, 736 | {file = "tree_sitter_languages-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a13f7d38f2eeb75f7cf127d1201346093748c270d686131f0cbc50e42870a1"}, 737 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f8c6a936ae99fdd8857e91f86c11c2f5e507ff30631d141d98132bb7ab2c8638"}, 738 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c283a61423f49cdfa7b5a5dfbb39221e3bd126fca33479cd80749d4d7a6b7349"}, 739 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e60be6bdcff923386a54a5edcb6ff33fc38ab0118636a762024fa2bc98de55"}, 740 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c00069f9575bd831eabcce2cdfab158dde1ed151e7e5614c2d985ff7d78a7de1"}, 741 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:475ff53203d8a43ccb19bb322fa2fb200d764001cc037793f1fadd714bb343da"}, 742 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26fe7c9c412e4141dea87ea4b3592fd12e385465b5bdab106b0d5125754d4f60"}, 743 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8fed27319957458340f24fe14daad467cd45021da034eef583519f83113a8c5e"}, 744 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3657a491a7f96cc75a3568ddd062d25f3be82b6a942c68801a7b226ff7130181"}, 745 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-win32.whl", hash = "sha256:33f7d584d01a7a3c893072f34cfc64ec031f3cfe57eebc32da2f8ac046e101a7"}, 746 | {file = "tree_sitter_languages-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:1b944af3ee729fa70fc8ae82224a9ff597cdb63addea084e0ea2fa2b0ec39bb7"}, 747 | ] 748 | 749 | [package.dependencies] 750 | tree-sitter = "*" 751 | 752 | [[package]] 753 | name = "typing-extensions" 754 | version = "4.9.0" 755 | description = "Backported and Experimental Type Hints for Python 3.8+" 756 | optional = false 757 | python-versions = ">=3.8" 758 | files = [ 759 | {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, 760 | {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, 761 | ] 762 | 763 | [[package]] 764 | name = "uc-micro-py" 765 | version = "1.0.3" 766 | description = "Micro subset of unicode data files for linkify-it-py projects." 767 | optional = false 768 | python-versions = ">=3.7" 769 | files = [ 770 | {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}, 771 | {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}, 772 | ] 773 | 774 | [package.extras] 775 | test = ["coverage", "pytest", "pytest-cov"] 776 | 777 | [[package]] 778 | name = "yarl" 779 | version = "1.9.4" 780 | description = "Yet another URL library" 781 | optional = false 782 | python-versions = ">=3.7" 783 | files = [ 784 | {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, 785 | {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, 786 | {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, 787 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, 788 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, 789 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, 790 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, 791 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, 792 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, 793 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, 794 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, 795 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, 796 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, 797 | {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, 798 | {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, 799 | {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, 800 | {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, 801 | {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, 802 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, 803 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, 804 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, 805 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, 806 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, 807 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, 808 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, 809 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, 810 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, 811 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, 812 | {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, 813 | {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, 814 | {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, 815 | {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, 816 | {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, 817 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, 818 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, 819 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, 820 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, 821 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, 822 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, 823 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, 824 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, 825 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, 826 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, 827 | {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, 828 | {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, 829 | {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, 830 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, 831 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, 832 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, 833 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, 834 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, 835 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, 836 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, 837 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, 838 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, 839 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, 840 | {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, 841 | {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, 842 | {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, 843 | {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, 844 | {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, 845 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, 846 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, 847 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, 848 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, 849 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, 850 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, 851 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, 852 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, 853 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, 854 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, 855 | {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, 856 | {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, 857 | {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, 858 | {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, 859 | {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, 860 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, 861 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, 862 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, 863 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, 864 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, 865 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, 866 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, 867 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, 868 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, 869 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, 870 | {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, 871 | {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, 872 | {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, 873 | {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, 874 | ] 875 | 876 | [package.dependencies] 877 | idna = ">=2.0" 878 | multidict = ">=4.0" 879 | 880 | [metadata] 881 | lock-version = "2.0" 882 | python-versions = "^3.9" 883 | content-hash = "752c2a893b23124ec759edab54ab38658a5086472eea783c78713f2bdf1e2135" 884 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "mehditor" 3 | version = "0.2.0" 4 | description = "A text editor for twentieth century computing." 5 | authors = ["Ken Kinder "] 6 | license = "MIT" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.9" 11 | textual = {extras = ["syntax"], version = "^0.58.0"} 12 | 13 | [tool.poetry.group.dev.dependencies] 14 | textual-dev = "^1.5.1" 15 | 16 | [build-system] 17 | requires = ["poetry-core"] 18 | build-backend = "poetry.core.masonry.api" 19 | 20 | [tool.poetry.scripts] 21 | meh = "mehditor.main:main" 22 | mehditor = "mehditor.main:main" 23 | -------------------------------------------------------------------------------- /screenshots/meh-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkinder/mehditor/fa37154b3b421cb425322054946de956097b2891/screenshots/meh-1.png -------------------------------------------------------------------------------- /screenshots/meh-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkinder/mehditor/fa37154b3b421cb425322054946de956097b2891/screenshots/meh-2.png -------------------------------------------------------------------------------- /screenshots/meh-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkinder/mehditor/fa37154b3b421cb425322054946de956097b2891/screenshots/meh-3.png -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkinder/mehditor/fa37154b3b421cb425322054946de956097b2891/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_validators.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from mehditor.validators import LineNumber 3 | 4 | 5 | class TestLineNumber(unittest.TestCase): 6 | 7 | def setUp(self): 8 | self.lineNumber = LineNumber() 9 | 10 | def test_validate_success_separated(self): 11 | self.assertTrue(self.lineNumber.validate("10:3").is_valid) 12 | 13 | def test_validate_success_simple(self): 14 | self.assertTrue(self.lineNumber.validate("10").is_valid) 15 | 16 | def test_validate_failure_wrong_format(self): 17 | self.assertFalse(self.lineNumber.validate("10:3:5").is_valid) 18 | 19 | def test_validate_failure_non_number(self): 20 | self.assertFalse(self.lineNumber.validate("a:b").is_valid) 21 | 22 | if __name__ == '__main__': 23 | unittest.main() 24 | 25 | --------------------------------------------------------------------------------