wrapped.vim/python3/wrapped/util.py
Ari Archer 23aa5ea650
Add a screenshot
Signed-off-by: Ari Archer <ari@ari.lt>
2024-12-23 19:25:02 +02:00

186 lines
4.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Utilities"""
import datetime
import difflib
import typing as t
from dataclasses import dataclass
import vim
TIME_UNITS: t.Final[t.Dict[str, int]] = {
"year": 31536000, # 365 days
"month": 2592000, # 30 days
"day": 86400, # 1 day
"hour": 3600, # 1 hour
"minute": 60, # 1 minute
"second": 1, # 1 second
}
@dataclass
class VimID:
session_id: str
window_id: int
buffer_id: int
def to_json(self) -> t.Dict[str, t.Any]:
"""Return json replof the VimID"""
return {
"id": self.session_id,
"win": self.window_id,
"buf": self.buffer_id,
}
class VimLoadingBar:
"""Implements a loading bar for Vim"""
def __init__(self, justify: int = 0) -> None:
self._total: int = 0
self._justify: int = justify
self._msg: str = "Progress...".ljust(justify)
self._drawn: bool = False
self._progress: int = 0
# Updates every roughly 1%
self._step: int = 0
def render_status(self) -> "VimLoadingBar":
"""Renders a status"""
t: str = "?" * len(str(self._total))
vim.current.buffer[0] = f"{self._msg} ({t}/{t}) [working...{' ' * 40}] ???.??%"
return self
def update(self) -> "VimLoadingBar":
"""Update bar"""
self._progress += 1
if self._step == 0 or self._progress % self._step != 0:
return self
self._progress = min(self._progress, self._total)
empty: int = int(50 - (self._progress / self._total * 50))
full: int = 50 - empty
t: str = str(self._progress).rjust(len(str(self._total)))
self.clear(False)
vim.current.buffer[0] = (
f"{self._msg} ({t}/{self._total}) [{ '*' * full}{' ' * empty}] {self._progress / self._total * 100:>6.2f}%"
)
vim.command("redraw")
return self
def clear(self, redraw: bool = True) -> "VimLoadingBar":
"""Clear bar"""
vim.current.buffer[0] = ""
if redraw:
vim.command("redraw")
return self
def reset(self) -> "VimLoadingBar":
"""Reset progress"""
self._progress = 0
return self
def set_msg(self, msg: str) -> "VimLoadingBar":
"""Reset progress"""
self._msg = msg.ljust(self._justify)
return self
def set_total(self, total: int) -> "VimLoadingBar":
"""Set a new goal"""
self._total = total
self._step = total // 100
self._progress = 0
return self
def get_vid() -> VimID:
"""Get Vim ID"""
return VimID(
vim.eval("g:wrapped_stats_id"),
int(vim.eval("win_getid()")),
int(vim.eval("bufnr('%')")),
)
def ts() -> str:
"""Get UTC timestamp"""
return datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
def diff_file(filename: str, current_buf: t.List[str]) -> t.Tuple[int, int]:
"""Diff a file"""
with open(filename, "r") as fp:
file_lines: t.List[str] = fp.readlines()
current_lines: t.Tuple[str, ...] = tuple(line.strip() for line in current_buf)
new_lines: t.Tuple[str, ...] = tuple(line.strip() for line in file_lines)
diff: t.Iterator[str] = difflib.ndiff(new_lines, current_lines)
added_lines: int = 0
removed_lines: int = 0
for line in diff:
if line.startswith("+ "):
added_lines += 1
elif line.startswith("- "):
removed_lines += 1
return added_lines, removed_lines
def seconds_to_human_readable(seconds: int) -> str:
"""Converts seconds to human-readable format"""
components: t.List[str] = []
for unit, unit_seconds in TIME_UNITS.items():
if seconds >= unit_seconds:
count: int = seconds // unit_seconds
if count > 0:
components.append(f"{count} {unit}{'s' if count > 1 else ''}")
seconds %= unit_seconds
if len(components) > 1:
if len(components) > 3:
return ", ".join(components[:-1]) + ", and " + components[-1]
else:
return components[0] + " and " + components[1]
elif components:
return components[0]
else:
return "0 seconds"
def get_median(numbers: t.Iterable[int]) -> t.Union[int, float]:
"""Median"""
numbers = sorted(numbers)
n: int = len(numbers)
median: t.Union[int, float]
if n % 2 == 1:
median = numbers[n // 2]
else:
mid1: int = numbers[n // 2]
mid2: int = numbers[n // 2 - 1]
median = (mid1 + mid2) / 2
return median