From f708364d784d4a71e41b13eef357c5983f9fb520 Mon Sep 17 00:00:00 2001 From: jigsaw Date: Sun, 11 Aug 2024 04:15:58 +0800 Subject: [PATCH 1/2] :sparkles: better list_{plugin,adapter,driver} --- nb_cli/config/model.py | 6 +++- nb_cli/handlers/adapter.py | 7 +++- nb_cli/handlers/driver.py | 7 +++- nb_cli/handlers/plugin.py | 7 +++- nb_cli/handlers/store.py | 68 +++++++++++++++++++++++++++----------- 5 files changed, 71 insertions(+), 24 deletions(-) diff --git a/nb_cli/config/model.py b/nb_cli/config/model.py index 4b31c41f..cd037d98 100644 --- a/nb_cli/config/model.py +++ b/nb_cli/config/model.py @@ -1,5 +1,6 @@ from pydantic import BaseModel +from typing import Optional from nb_cli.compat import PYDANTIC_V2, ConfigDict @@ -13,6 +14,7 @@ class Adapter(SimpleInfo): project_link: str desc: str + is_official: Optional[bool] = None class Plugin(SimpleInfo): @@ -20,6 +22,8 @@ class Plugin(SimpleInfo): project_link: str desc: str + is_official: Optional[bool] = None + valid: Optional[bool] = None class Driver(SimpleInfo): @@ -27,10 +31,10 @@ class Driver(SimpleInfo): project_link: str desc: str + is_official: Optional[bool] = None class NoneBotConfig(BaseModel): - if PYDANTIC_V2: # pragma: pydantic-v2 model_config = ConfigDict(extra="allow") else: # pragma: pydantic-v1 diff --git a/nb_cli/handlers/adapter.py b/nb_cli/handlers/adapter.py index 005d623d..a81771d2 100644 --- a/nb_cli/handlers/adapter.py +++ b/nb_cli/handlers/adapter.py @@ -32,5 +32,10 @@ async def list_adapters(query: Optional[str] = None) -> list[Adapter]: return [ adapter for adapter in adapters - if any(query in value for value in model_dump(adapter).values()) + if any( + query in value + for value in model_dump( + adapter, include={"name", "module_name", "project_link", "desc"} + ).values() + ) ] diff --git a/nb_cli/handlers/driver.py b/nb_cli/handlers/driver.py index 6daae72c..69b2a19f 100644 --- a/nb_cli/handlers/driver.py +++ b/nb_cli/handlers/driver.py @@ -13,5 +13,10 @@ async def list_drivers(query: Optional[str] = None) -> list[Driver]: return [ driver for driver in drivers - if any(query in value for value in model_dump(driver).values()) + if any( + query in value + for value in model_dump( + driver, include={"name", "module_name", "project_link", "desc"} + ).values() + ) ] diff --git a/nb_cli/handlers/plugin.py b/nb_cli/handlers/plugin.py index f899ca56..ced3a9f0 100644 --- a/nb_cli/handlers/plugin.py +++ b/nb_cli/handlers/plugin.py @@ -57,5 +57,10 @@ async def list_plugins(query: Optional[str] = None) -> list[Plugin]: return [ plugin for plugin in plugins - if any(query in value for value in model_dump(plugin).values()) + if any( + query in value + for value in model_dump( + plugin, include={"name", "module_name", "project_link", "desc"} + ).values() + ) ] diff --git a/nb_cli/handlers/store.py b/nb_cli/handlers/store.py index 3223996a..7047b5d0 100644 --- a/nb_cli/handlers/store.py +++ b/nb_cli/handlers/store.py @@ -1,4 +1,5 @@ import shutil +from statistics import median_high from asyncio import create_task, as_completed from typing import TYPE_CHECKING, Union, Literal, TypeVar, Optional, overload @@ -25,14 +26,14 @@ async def load_module_data(module_type: Literal["plugin"]) -> list[Plugin]: ... async def load_module_data(module_type: Literal["driver"]) -> list[Driver]: ... async def load_module_data( - module_type: Literal["adapter", "plugin", "driver"] + module_type: Literal["adapter", "plugin", "driver"], ) -> Union[list[Adapter], list[Plugin], list[Driver]]: ... else: @cache(ttl=None) async def load_module_data( - module_type: Literal["adapter", "plugin", "driver"] + module_type: Literal["adapter", "plugin", "driver"], ) -> Union[list[Adapter], list[Plugin], list[Driver]]: if module_type == "adapter": module_class = Adapter @@ -79,6 +80,13 @@ async def _request(url: str) -> httpx.Response: ) +def split_text_by_wcswidth(text: str, width: int): + _width = width + while wcswidth(text[:_width]) > width: + _width = _width - 1 + return text[:_width], text[_width:] + + def format_package_results( hits: list[T], name_column_width: Optional[int] = None, @@ -88,29 +96,49 @@ def format_package_results( return "" if name_column_width is None: - name_column_width = ( - max(wcswidth(f"{hit.name} ({hit.project_link})") for hit in hits) + 4 + name_column_width = median_high( + wcswidth(f"{hit.name} ({hit.project_link})") for hit in hits ) if terminal_width is None: terminal_width = shutil.get_terminal_size()[0] + desc_width = terminal_width - name_column_width - 8 + lines: list[str] = [] for hit in hits: - name = f"{hit.name} ({hit.project_link})" - summary = hit.desc - target_width = terminal_width - name_column_width - 5 - if target_width > 10: - # wrap and indent summary to fit terminal - summary_lines = [] - while wcswidth(summary) > target_width: - tmp_length = target_width - while wcswidth(summary[:tmp_length]) > target_width: - tmp_length = tmp_length - 1 - summary_lines.append(summary[:tmp_length]) - summary = summary[tmp_length:] - summary_lines.append(summary) - summary = ("\n" + " " * (name_column_width + 3)).join(summary_lines) - - lines.append(f"{name + ' ' * (name_column_width - wcswidth(name))} - {summary}") + is_official = "👍" if hit.is_official else " " + valid = " " + if isinstance(hit, Plugin): + valid = "✅" if hit.valid else "❌" + name = hit.name.replace("\n", "") + link = f"({hit.project_link})" + desc = hit.desc.replace("\n", "") + # wrap and indent summary to fit terminal + is_first_line = True + while ( + wcswidth(f"{name} {link}") > name_column_width + or wcswidth(desc) > desc_width + ): + name_column, name = split_text_by_wcswidth(name, name_column_width) + if name_column == "": + name_column, link = split_text_by_wcswidth(link, name_column_width) + desc_column, desc = split_text_by_wcswidth(desc, desc_width) + lines.append( + name_column + + " " * (name_column_width - wcswidth(name_column)) + + (f" {valid} {is_official} " if is_first_line else " " * 7) + + desc_column + + " " * (desc_width - wcswidth(desc_column)) + ) + is_first_line = False + + name_column = f"{name} {link}".strip() + lines.append( + name_column + + " " * (name_column_width - wcswidth(name_column)) + + (f" {valid} {is_official} " if is_first_line else " " * 7) + + desc + + " " * (desc_width - wcswidth(desc)) + ) return "\n".join(lines) From 87d76c47f62f068f79539f3c09286e11ecfd1fac Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 10 Aug 2024 20:17:28 +0000 Subject: [PATCH 2/2] :rotating_light: auto fix by pre-commit hooks --- nb_cli/config/model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nb_cli/config/model.py b/nb_cli/config/model.py index cd037d98..c09d24c1 100644 --- a/nb_cli/config/model.py +++ b/nb_cli/config/model.py @@ -1,6 +1,7 @@ +from typing import Optional + from pydantic import BaseModel -from typing import Optional from nb_cli.compat import PYDANTIC_V2, ConfigDict