299 lines
10 KiB
Python
299 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
# languages -*- coding: utf-8 -*-
|
|
"""Wrapped.vim plugin"""
|
|
|
|
import datetime
|
|
import os
|
|
import typing as t
|
|
import uuid
|
|
|
|
import vim
|
|
|
|
from . import const, processing, util
|
|
|
|
YEAR: t.Final[int] = datetime.datetime.now(datetime.timezone.utc).year
|
|
|
|
|
|
def setup() -> None:
|
|
"""Setup stats"""
|
|
vim.command(f"const g:wrapped_stats_id = '{uuid.uuid4()}'")
|
|
|
|
|
|
def wrap_gen_seasonal_nick(hour: int, month: int) -> str:
|
|
"""Generate seasonal nickname"""
|
|
|
|
nick: str
|
|
|
|
if month in [12, 1, 2]:
|
|
nick = "Winter"
|
|
elif month in [3, 4, 5]:
|
|
nick = "Spring"
|
|
elif month in [6, 7, 8]:
|
|
nick = "Summer"
|
|
elif month in [9, 10, 11]:
|
|
nick = "Autumn"
|
|
else:
|
|
nick = "Intergalactic"
|
|
|
|
if hour < 6:
|
|
nick += " night owl"
|
|
elif hour < 12:
|
|
nick += " morning sun"
|
|
elif hour < 18:
|
|
nick += " afternoon delight"
|
|
else:
|
|
nick += " evening breeze"
|
|
|
|
return nick
|
|
|
|
|
|
def wrap_logo(g: processing.GeneralStats, total: int) -> None:
|
|
"""Print logo"""
|
|
|
|
c: t.Any = vim.current.buffer # type: ignore
|
|
|
|
logo: str = f"""
|
|
________ ++ ________
|
|
/VVVVVVVV\\++++ /VVVVVVVV\\
|
|
\\VVVVVVVV/++++++\\VVVVVVVV/ {os.getlogin().title()}'s Vim wrapped of {YEAR}.
|
|
|VVVVVV|++++++++/VVVVV/'
|
|
|VVVVVV|++++++/VVVVV/' - {wrap_gen_seasonal_nick(g.hour.most_common(1)[0][0], g.month.most_common(1)[0][0])}
|
|
+|VVVVVV|++++/VVVVV/'+ - {total} events
|
|
+++|VVVVVV|++/VVVVV/'+++++ - {g.adds + g.dels} changes in {g.opens} sessions
|
|
+++++|VVVVVV|/VVV___++++++++++ - {len(g.langs)} languages
|
|
+++|VVVVVVVVVV/##/ +_+_+_+_ - {(g.adds - g.dels) / (g.adds + g.dels) * 100:.2f}% code growth ratio
|
|
+|VVVVVVVVV___ +/#_#,#_#,\\ - {(g.pastes - g.copies) / (g.adds + g.dels) * 100:.2f}% copy-paste ratio
|
|
|VVVVVVV//##/+/#/+/#/'/#/
|
|
|VVVVV/'+/#/+/#/+/#/ /#/ #VimWrapped{YEAR} #VimInAPod{YEAR}
|
|
|VVV/'++/#/+/#/ /#/ /#/ Your year was eventful. Let's see how you did!
|
|
'V/' /##//##//##//###/
|
|
++
|
|
|
|
Credits to Erling Westenvik for the ASCII logo
|
|
Presented to you with <3 by wrapped.vim"""
|
|
|
|
for line in logo.splitlines()[1:]:
|
|
c.append(line)
|
|
|
|
|
|
def wrap_general(g: processing.GeneralStats, total: int) -> None:
|
|
"""Wrap general stats up"""
|
|
|
|
c: t.Any = vim.current.buffer # type: ignore
|
|
|
|
tlines: int = g.adds - g.dels
|
|
c.append(
|
|
f"This year, you have written {g.adds} ({g.invalid_writes} of which were invalid) new lines of code and removed {g.dels} lines in {len(g.langs)} languages, resulting in a {'neutral' if tlines == 0 else ('positive' if tlines > 0 else 'negative')} total of {tlines} lines."
|
|
)
|
|
c.append(
|
|
f"These statistics make your code growth ratio {(g.adds - g.dels) / (g.adds + g.dels) * 100:.2f}%."
|
|
)
|
|
|
|
c.append("")
|
|
c.append("Some of your most prominent languages used included:")
|
|
lang_total: int = g.langs.total()
|
|
for lang, frequency in g.langs.most_common(10):
|
|
c.append(f" * {lang.title()} at {frequency / lang_total * 100:.2f}%")
|
|
|
|
c.append(
|
|
f"Also, when editing, you copied code {g.copies} times and pasted code {g.pastes} times, making your copy-paste ratio {(g.pastes - g.copies) / (g.adds + g.dels) * 100:.2f}%."
|
|
)
|
|
|
|
c.append("")
|
|
c.append(
|
|
f"Your most beloved filenames included {', '.join(f[0] for f in g.files.most_common(5))}. Got a naming convension, huh?"
|
|
)
|
|
|
|
c.append("")
|
|
|
|
c.append("Timewise it's simple.")
|
|
month: t.Tuple[int, int] = g.month.most_common(1)[0]
|
|
c.append(
|
|
f" 1. You did the most in {const.MONTHS[month[0] - 1]} with {month[1]} events recorded then."
|
|
)
|
|
try:
|
|
h1, h2 = g.hour.most_common(2)
|
|
except Exception:
|
|
h1 = h2 = g.hour.most_common(1)[0]
|
|
|
|
c.append(
|
|
f" 2. Your most active times were {h1[0]:02}:00 and {h2[0]:02}:00 with {h1[1] + h2[1]} events recorded in total during these times."
|
|
)
|
|
days: t.List[t.Tuple[int, int]] = g.day.most_common(7)
|
|
c.append(
|
|
f" 3. Your most active weekday was {const.DAYS[days[0][0]]} at {days[0][1]} events recorded{', with the follow-uppers being...' if len(days) > 1 else '.'}"
|
|
)
|
|
for day, frequency in days[1:]:
|
|
c.append(
|
|
f" {const.DAYS[day]:<10} | {frequency} events ({frequency / total * 100:.2f}%)"
|
|
)
|
|
c.append("Great time management!")
|
|
|
|
c.append("")
|
|
cmds_total: int = g.cmds.total()
|
|
c.append(
|
|
f"You opened Vim in total of {g.opens} times, then closed it {g.closes} times."
|
|
+ (
|
|
" (Forgot how to exit vim? :p)"
|
|
if g.opens > g.closes
|
|
else (" (Lots of tabs? Productive!)" if g.opens < g.closes else "")
|
|
)
|
|
)
|
|
|
|
c.append("")
|
|
|
|
c.append(f"In the mean time you ran {cmds_total} commands, such as:")
|
|
for cmd, frequency in g.cmds.most_common(10):
|
|
c.append(f" :{cmd} ({frequency / cmds_total * 100:.2f}%)")
|
|
c.append(f"Out of which {g.invalid_commands} were invalid.")
|
|
|
|
|
|
def wrap_behaviour(
|
|
g: processing.GeneralStats, b: processing.BehaviourStats, total: int
|
|
) -> None:
|
|
"""Wrap behavioural statistics"""
|
|
|
|
c: t.Any = vim.current.buffer # type: ignore
|
|
|
|
c.append(
|
|
f"After collecting and analysing {total} events spanning {util.seconds_to_human_readable(int(b.data_range))}, we have concluded your habits."
|
|
)
|
|
c.append(
|
|
f"Amongst those events we found {b.invalid_writes} invalid write events and {b.invalid_commands} invalid command events."
|
|
)
|
|
c.append("")
|
|
|
|
days: float = b.data_range / 86400
|
|
|
|
c.append(
|
|
f"You have created {len(b.sessions)} sessions, out of which {b.invalid_session_closes} produced invalid (stray) close events, and/or had {b.invalid_events} invalid (stray) events."
|
|
)
|
|
c.append("")
|
|
|
|
c.append(
|
|
f"You also spent a whole {util.seconds_to_human_readable(int(b.editing_time))} editing code and other files!"
|
|
)
|
|
|
|
c.append(
|
|
f"Moreover, your average session lasts {util.seconds_to_human_readable(b.avg_duration)}, though usually lasting {util.seconds_to_human_readable(b.med_duration)}."
|
|
)
|
|
|
|
c.append("")
|
|
c.append("During your coding sessions you tend to do the following the most:")
|
|
|
|
for evt, freq in sorted(b.evts.items(), key=lambda p: p[1], reverse=True):
|
|
c.append(f" * {evt.name} ({freq / total * 100:.2f}%)")
|
|
|
|
c.append("")
|
|
c.append("Lastly, on average, you...")
|
|
c.append(f" * Work with {b.avg_sessions} sessions on the daily basis.")
|
|
c.append(
|
|
f" * Write {g.adds // days:.0f} new lines of code while simultaneously deleting {g.dels // days:.0f} a day."
|
|
)
|
|
c.append(
|
|
f" * Copy {g.copies // days:.0f} lines of code and paste {g.pastes // days:.0f} a day."
|
|
)
|
|
c.append(f" * Work with {b.avg_langs} languages on average a day.")
|
|
|
|
c.append("")
|
|
c.append("Your journey was impressive. Keep it up!")
|
|
|
|
|
|
def wrap() -> None:
|
|
"""Wrap your year in Vim up"""
|
|
|
|
vim.command("tabnew")
|
|
vim.command("setlocal ft=wrapped syntax=wrapped")
|
|
vim.command("setlocal noconfirm buftype=nofile")
|
|
|
|
c: t.Any = vim.current.buffer # type: ignore
|
|
|
|
for lang, clr in const.LANG_COLOURS.items():
|
|
vim.command(
|
|
f"call matchadd('Wrapped{clr.capitalize()}', '\\<{lang.capitalize()}\\>')"
|
|
)
|
|
|
|
# Syntax
|
|
vim.command(r"call matchadd('WrappedGreen', '\d*.\d*%')")
|
|
vim.command(r"call matchadd('WrappedRed', '-\d*.\d*%')")
|
|
vim.command(
|
|
r"call matchadd('WrappedYellow', '\(, \)\?\(and \)\?\d\+ \(second\|minute\|hour\|day\|month\|year\)s\?')"
|
|
)
|
|
|
|
# Vim logo
|
|
vim.command(r"call matchadd('WrappedGreen', 'VVVV*')")
|
|
vim.command(r"call matchadd('WrappedGreen', 'V/')")
|
|
vim.command(r"call matchadd('WrappedGreen', '##*')")
|
|
vim.command(r"call matchadd('WrappedBlue', '#VimWrapped\d\+')")
|
|
vim.command(r"call matchadd('WrappedBlue', '#VimInAPod\d\+')")
|
|
vim.command(r"call matchadd('WrappedGreen', 'Vimming')")
|
|
|
|
# Misc
|
|
vim.command(r"call matchadd('WrappedGreen', 'written \d* new lines of code')")
|
|
vim.command(r"call matchadd('WrappedGreen', 'positive total of \d* lines')")
|
|
vim.command(r"call matchadd('WrappedRed', 'removed \d* lines')")
|
|
vim.command(r"call matchadd('WrappedRed', 'negative total of -\d* lines')")
|
|
vim.command(r"call matchadd('WrappedBlue', 'neutral total of \d* lines')")
|
|
vim.command(r"call matchadd('WrappedGray', '=============================*')")
|
|
|
|
# Credits
|
|
vim.command(
|
|
r"call matchadd('WrappedGray', 'Credits to Erling Westenvik for the ASCII logo')"
|
|
)
|
|
vim.command(
|
|
r"call matchadd('WrappedGray', 'Presented to you with <3 by wrapped.vim')"
|
|
)
|
|
|
|
bar: util.VimLoadingBar = util.VimLoadingBar(36)
|
|
|
|
def wrap_end() -> None:
|
|
"""Finishing message"""
|
|
bar.clear()
|
|
c.append("")
|
|
c.append(
|
|
f"That's all we're able to tell you thus far \u00AF\\\u005F(\u30C4)\u005F/\u00AF Keep Vimming and see you in {YEAR + 1}!"
|
|
)
|
|
vim.command("set readonly nomodifiable")
|
|
vim.command("normal gg")
|
|
|
|
bar.set_msg("Counting data...").render_status()
|
|
total: int = processing.count_of_records_yr()
|
|
|
|
if total == 0:
|
|
bar.clear()
|
|
wrap_end()
|
|
return
|
|
|
|
bar.set_total(total)
|
|
g: processing.GeneralStats = processing.process_general_statistics(
|
|
bar.set_msg("Processing general statistics...")
|
|
)
|
|
bar.reset()
|
|
|
|
bar.set_msg("Rendering general info...").render_status()
|
|
wrap_logo(g, total)
|
|
c.append("")
|
|
wrap_general(g, total)
|
|
c.append("")
|
|
c.append("=" * 128)
|
|
c.append("")
|
|
|
|
bar.reset()
|
|
bar.set_total(total)
|
|
b: processing.BehaviourStats = processing.process_behaviour_statistics(
|
|
bar.set_msg("Processing behavioural statistics...")
|
|
)
|
|
bar.reset()
|
|
bar.set_total(len(b.sessions))
|
|
b = processing.process_avg_behaviour_statistics(
|
|
bar.set_msg("Processing average statistics..."),
|
|
b,
|
|
)
|
|
bar.reset()
|
|
|
|
bar.set_msg("Rendering behaviour info...").render_status()
|
|
wrap_behaviour(g, b, total)
|
|
|
|
c.append("")
|
|
c.append("=" * 128)
|
|
wrap_end()
|