ultrabug / py3status

py3status is an extensible i3status wrapper written in python
https://ultrabug.github.io/py3status/
BSD 3-Clause "New" or "Revised" License
892 stars 261 forks source link

Formatter new features #752

Closed tobes closed 5 years ago

tobes commented 7 years ago

I'm planning to add some new formatter features.

Current features for blocks.

The new options I'm thinking of adding are below, please vote on any you would like to see or are opposed to. I'll make each as a new reply.

Also feel free to add suggestions

tobes commented 7 years ago

ellipsis for shortened strings

\?max_length=12&ellipsis=…

tobes commented 7 years ago

symmetric shortening etc. allow trimming left, right, both

\?max_length=12&trim=both

tobes commented 7 years ago

soft spaces.

Often we want a separator but only if needed eg {artist} - {title} but maybe we have just one or both

{artist}[\?soft - ]{title} it would only show if it has non-whitespace before and after.

lasers commented 7 years ago

and condition: \?if pacman&aur=... (I vote +1 on this ; but might be easier for users if we fix arch_updates to have three possible results instead.)

Could be good to have &trim=both strip_whitespace as default for all modules so we don't waste time giving users left,right,both option or adding .strip ourselves (in some modules) to trim some unwanted spaces on certain modules when there is no input and/or delayed input... such as clementine, bluetooth, and xsel.

|BT: | (default 'BT: {format_bt}')     <-- Space
|♫ | (default '♫ {current}')           <-- Space
|       ayayaya xsel content lmao    | <-- Spaces

────────── DIVIDER ──────────

always show block [\?show ...] is somewhat vague. I think \?always_show is better. Nitpicking?

────────── DIVIDER ──────────

Soft spaces could be perfect for colons too. BT[\?soft : ]{bluetooth} Sometimes none due to delayed/slow/off response.

There should be some other modules just like that too. No song playing, etc.

────────── DIVIDER ──────────

Unrelated: An ability to override STRING_ERROR / STRING_UNAVAILABLE.

tobes commented 7 years ago

BT[\?soft : ]{bluetooth} actually BT[: {bluetooth}] probably works just as well

lasers commented 7 years ago

I think I need to wait for new formatters before I make uptime PR.

Something to make hours|hour possible.

    # available configuration parameters
    format = 'up {decades}{years}{weeks}{days}{hours}{minutes}'
    decades = '[\?if=D {D} decades, ]'
    years = '[\?if=Y {Y} years, ]'
    weeks = '[\?if=w {w} weeks, ]'
    days = '[\?if=d {d} days, ]'
    hours = '[\?if=h {h} hours, ]'
    minutes = '[\?if=m {m} minutes, ]'
    seconds = '[\?if=s {s} seconds, ]'

[\?soft ,] would be nice for comma. I have .rstrip(' ').rstrip(',') before it goes out.

tobes commented 7 years ago

Something to make hours|hour possible.

it might be possible to do something like {hour} [\?plural=hours hour|hours] but this feels a bit messy. The reason I'd do it like this would be to allow it to be used across multiple languages see https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html. So to do it properly we would have to learn about language plurals.

We could just do it for English and most other European languages (interestingly not French) but I'd prefer a full implementation.

A problem is that it might be over complicated for users, also getting the plural types for all different languages would be a pain (I can't seem to find them easily)

lasers commented 7 years ago

What about just not_one similar to not_zero?

[?\not_one {h} hours|hour]

tobes commented 7 years ago

What about just not_one

I care about the french :fr:

lasers commented 7 years ago
# keyboard_locks
format = '{caps} {num} {scr}'
icon_scr_off= ""
icon_num_off = ""
icon_caps_off = ""

... does not work as expected due to spaces. Soft spaces may be perfect for this.

# New format
format = '[{caps}][\?soft  ][{num}][\?soft  ][{scr}]'

Maybe we could make[\?space] or just keep using [\?soft ] (Two spaces).

# New format
format = '[{caps}][\?space][{num}][\?space][{scr}]'

EDIT: Maybe make it smart like this if no character(s) detected.

# New format
format = '[{caps}][\?soft][{num}][\?soft][{scr}]'
tobes commented 7 years ago

how about format = '{caps}[\?soft]{num}[\?soft]{scr}'?

I like the [\?soft] will be a space by default idea.

It is a shame that '{caps} {num} {scr}' doesn't just do this by default (eating whitespace) I might see how that works out. Not sure how many modules it'd break

lasers commented 7 years ago

Two modules off my head... xsel and clementine.

For clementine, format = u'♫ {current}'. If empty, would be '♫ ' (no quote). Maybe we change default format for clementine? Make it format = u'♫[ {current}'] ? That was pretty annoying.

We could do same for any other similar module (if we find them).

lasers commented 7 years ago

Something inverse of [?\soft]......... [?\omit ,] or [\strip ,]?

format = 'up [\?if=W {W} weeks, ][\?if=D {D} days, ][\?if=H {H} hours, ][\?if=M {M} minutes, ][?\omit ,]'

Can be crazy if we toss in [?\soft ,] for all those things. Keep modules clean. Then we can remove uptime = uptime.rstrip(' ').rstrip(',').

EDIT: Smart suggestion.

[\?strip] removes whitespace. [\?strip=,] removes whitespace+comma+whitespace. [\?strip=,:] removes whitespace+comma+colon+whitespace.

I suppose we could be cleaner by not assuming we want to strip whitespace in second, third lines, but I can't think of scenario where we want to keep whitespace at end for the modules since I assume we will be mostly using [\?strip] something always at end of the format.

lasers commented 7 years ago

Place an extra 0 in front of single digit numbers. 5 becomes 05... foruptime.

tobes commented 7 years ago

Place an extra 0 in front of single digit numbers. 5 becomes 05... for uptime.

{minutes:02d} sounds like what you want

lasers commented 7 years ago

I think we ought to have something like this [\?color=color {status}] to pass color so we don't paint format with unwanted color_good, color_bad, or color_degraded. We might have to update format for all modules with this if we want full color customization. See example.

Current format = 'Dropbox: {status}' --> (color) Dropbox: {status} // Always red, green or yellow.

New format... Same as Current format = '[\?color=color Dropbox: {status}]' --> (color) Dropbox: {status} // Always red, green, or yellow

Customizing... format = 'Dropbox: [\?color=color {status}]' --> (white) Dropbox: (color) {status}. // Always white // Always red, green, or yellow

Note: (white) is not necessarily white, but the statusline color in our i3 config.

 *Example (default colors)*:
 --------------------------------------
 bar {
     colors {
         background #000000
         statusline #ffffff    <---------- This.
         separator #666666
     }
 }

This way, I think, would allow us to change statusline color for all modules and would make it possible for people to keep all modules consistent with just 6 existing color options after they finished setting up new format for all modules.

EDIT: Maybe just [\?color] if it's not set. 👍 😀

lasers commented 7 years ago

I'm experimenting with something to see if I can keep everything in one format for whatismyip... and I'm struggling with this part. Can we use multiple ifs? I looked at test formatter and don't see any. I tried many things.

format = '[\?if=use_icon \?if=connected YES|NO]|{ip}' format = '[\?if=icon&show \?connected YES|NO]|{ip}'

{icon}:True/False {connected}:True/False {ip}:127.0.0.1

EDIT: I got it... format = '\?if=!connected ■|[\?if=icon&show ●|[{ip}]]'. 💯 💯 💯 THIS IS FUUUUUU HARD!!!!!!!!!!!!! 👍

lasers commented 7 years ago

@tobes What should I do with this? https://github.com/lasers/py3status/blob/whatismyip-formatter/py3status/modules/whatismyip.py

I cleaned up everything. No depreciation stuffs. One format in whatismyip now. I added missing placeholders too. Just a fun experiment that I'm done with. Putting together a working format was tricky.

@maximbaz I know you like this module. What do you think of it now? Everything should be same as old whatismyip except that we don't scroll anymore so it doesn't retrieve URLs more than button_toggle clicks and/or cache_timeout. Let me know if something is off.

maximbaz commented 7 years ago

So my feedback:

lasers commented 7 years ago

I renamed mouse to toggled. Better naming and to stay in consistent with button_toggle so we can name more mouse booleans if necessary.

If we add other options... like stop, mute, etc... we could use if\?=stoppped or if\?=muted. I think it may be a problem with next, prev, up, down, etc... if\?nexted, if\?=preved, if\?=upped, if\?=downed (??). I don't know.

I also don't know if that's the direction we're going with the formatter. This is a good experiment to get some ideas of whenever we want things to be like this.

I think this is nice and easy for on/off modules. This is a little crazy because I went a little crazy by omitting icons stuffs. If we get that back in, then we could go with something a little simple like \?if=toggled {icon}|[{ip}] instead.

Thanks for the feedback. 👍

tobes commented 7 years ago

My take on the whatsmyip is that is is quite crazy, I can't think of any other modules that have a mode, maybe I missed some. Anyhow I think we should look for a common approach that we can use in many places.

I like the idea of supplying multiple formats to modules eg format = ['format 1', 'format 2', ..] and cycling through them on click like we do in some other places eg clock because I think it is useful eg for battery_level I could do format = ['{icon}', '{icon} {percent} {time_remaining}'] so I can switch between summary and detailed output.

Now the multi format approach is a challenge because really if we do it we want to allow it without having to implement it for every module (because that would be a pain) So anyhow that's where I'd like to go but I don't think it is easy. But that does not really solve the whatsmyip issues.

Anyhow with the example format format = '\?if=!connected ■|[\?if=toggled&show ●|[{ip}]]' which is basically trying to do everything in one format. We should be able to simplify it by removing the &show because the if should be all that is needed. The square brackets can probably die too. The way you are passing connected, and toggled to the format is horrible but that can be cleaned up easily. My main concern is about keeping the formats, or at least the default ones easily understandable for users who are less technical.

lasers commented 7 years ago

format = '\?if=!connected ■|[\?if=toggled&show ●|[{ip}]]' OLD. format = '\?if=!connected ■|[\?if=toggled ●|[{ip}]]'. NEW. Confirmed. No &show. Thx... Still need brackets. Thx.

How should I pass connected and toggled? They seems somewhat straightforward.

I tried True/False approach first and it doesn't work. Need dicts w/o 0,1 cause if I input numbers, it shows up on the status too... so I opted for '',' ' instead... Kinda knew there would be a better solution in the future. This is neat that we trimmed, I think 30 lines, from master branch when I wc. Not sure cause of docstrings and extra placeholders. 👍

I think the screenshot have the mode too... I wanted a mode for bluetooth too. You saw that long Microsoft mouse name. Also, we probably could put mpd_status or dropbox in one format using format+control placeholders... [\?if=play... ]|[\?if=pause... ]|[\?if=stop..... ][if\?if=not_authenticated...]|[\?if=not_connected... ]. Crazy?

tobes commented 7 years ago

How should I pass connected and toggled?

Well it seems the nice way to do it is broken in master, but it works correctly in #887 and I've now added some tests to that branch. The nice way is to just set self.toggle in the module and the formatter will find it. In master you can do format = '{toggle}', but not format = '[\?if=toggle ...]'.

A fine demonstration of why #887 is amazing 🔥

lasers commented 7 years ago

Can confirm the nice way using DPMS's cache_timeout as a test. Ran into an issue with this... frame -> format_button_closed = '{urgent} URGENT +|+'

Composite issue? If this works, then I could drop frame: add urgent button PR, but I think it'd be nice for others if we get that merged... or make it a new default.

lasers commented 7 years ago

@tobes I'm making changes in gcal... and I just realized something.

We may be missing the formatter features for time stuffs too.

In gcal... I wanted to know if I should bother exposing starttime, endtime.

1) OMITTED [{starttime}], [{endtime}] with 00:00... to make it disappear ? 2) Come up with a way to turn 7:00 PM into 7 PM or 7PM ?

EDIT: Hi. There won't be any timedate placeholders. We can forget about 1).

In cmus, moc, and probably others too.

Real data       |  {duration} length time in seconds, eg 171
Synthetic data  |  {durationtime} length time in [HH:]MM:SS, eg 02:51
Real data       |  {position} elapsed time in seconds, eg 17
Synthetic data  |  {positiontime} elapsed time in [HH:]MM:SS, eg 00:17

to...

Real data       |  {duration} length time in seconds, eg 171
Real data       |  {duration:s} length time in [HH:]MM:SS, eg 02:51
Real data       |  {position} elapsed time in seconds, eg 17
Real data       |  {position:s} elapsed time in [HH:]MM:SS, eg 00:17

Replace ? with time symbol.

I'll keep working on gcal to get rid of that year bug. I plan to support urgent too. 🔔

lasers commented 7 years ago

Something for the sizes?

{installed} package installed size, eg 2128558 <-- raw bytes
{filename} package filename size, eg 583304 <-- raw bytes
{installed} package installed size, eg 2.03MB  <-- hardcoded / manipulated
{filename} package filename size, eg 570KB <-- hardcoded / manipulated
{installed:bytes} package installed size, eg 2.03MB  <-- formatter ?
{filename:bytes} package filename size, eg 570KB <-- formatter ?

Not sure how we can configure the format... Maybe universal format_size?

lasers commented 7 years ago

Something for the urgency hints? 🔔

format = '[\?if=percent<5&urgent Critical|[\?if=percent<15&urgent Low] {percent}'
format = '[\?if=count>1&urgent][\?not_zero Mail {count}]
format = '[\?not_zero&urgent Mail {count}] ^ Same.
lasers commented 6 years ago

Something for the notifications? :information_source:

format = '[\?if=percent=5&notify Critical|[\?if=percent=15&notify Low] {percent}'
format = '[\?if=count>1&notify 'You got a mail'][\?not_zero Mail {count}]

Something for the exec? :exclamation:

format = '[\?if=percent=5&exec ~/suspend.sh shutdown|[\?if=percent=15&exec ~/suspend.sh autosave] {percent}'
format = '[\?if=count>1&exec ~/pgrep-thunderbird.sh][\?not_zero Mail {count}]
lasers commented 6 years ago

Reverse ellipsis is required if we want to keep window_title fully intact.

lasers commented 6 years ago

Something for the case conversions? 💌

\?upper and \?lower

{city}, eg Chicago
format = '\?upper City: {city}'  ----> 'CITY: CHICAGO'
format = '\?lower City: {city}'  ----> 'city: chicago'

{city:upper} and {city:lower}

{country}, eg Russia
format = 'Welcome to {city:upper}'  ----> 'Welcome to RUSSIA'
format = 'Welcome to {city:lower}'  ----> 'Welcome to russia'
lasers commented 6 years ago

Something for the timeouts? ⏲

Thought:

Policy:

Reasons.

Examples using cmus:

    cache_timeout = 5
    format = '[\?if=is_started [\?if=is_playing > ][\?if=is_paused \|\| ]' +\
        '[\?if=is_stopped .. ][[{artist}][\?soft  - ][{title}]' +\
        '|\?show cmus: waiting for user input]]'
    sleep_timeout = 20

New.

    format = '[\?if=is_started [\?if=is_playing > ][\?if=is_paused \|\| ]' +\
        '[\?if=is_stopped .. ][[{artist}][\?soft  - ][{title}]' +\
        '|\?show cmus: waiting for user input]]'
    format += '[\?if=is_started&cache=5 |\?cache=20 ]'

Pro -- More choices.

    format = '[\?if=is_started [\?if=is_playing > ][\?if=is_paused \|\| ]' +\
        '[\?if=is_stopped .. ][[{artist}][\?soft  - ][{title}]' +\
        '|\?show cmus: waiting for user input]]'
    format += '[\?if=is_started&cache=15 |[\?if=is_playing&cache=1 |[\if=is_paused&cache=5 |\?cache=60 ]]'

Con -- Not user friendly.

lasers commented 6 years ago

Something to update the other modules? :arrow_up:

Random module updating other module.

format += '[\?if=is_connected&update github]'

Random module updating all modules.

format += '[\?if=is_connected&update all]'
    OR
format += '[\?if=is_connected&update ]'
tobes commented 6 years ago

@lasers I think things like the :lower are nice ideas and fit in with the formatter as a concept. However I think things like updating modules or the timeout do not really belong as part of the format. It is an interesting idea and I will have a think.

Really the main work I need to do now is provide some formatter documentation and then look at implementing some of the more straight forward ideas. Also hopefully I will get more settled and have some time for py3status. Maybe even today if I meet my targets.

lasers commented 6 years ago

updating modules

The module input can use this. For now, I use a config. I'm just listing them as I see them. Cheers.

the timeout

interval can be changed based on behaviors too, not just the format. Best part? Less configs.

Format placeholder:
    {format_coin} format for cryptocurrency coins

Time placeholders:
    cache:  refresh interval for this module (default 5)
    request:  time to wait for a response, in seconds (default 5)
    sleep:  sleep interval for this module (default 20)

do not really belong as part of the format

One format to rule them all. :ring:

Also hopefully I will get more settled and have some time for py3status.

No problem. I hope the new situation is working out wonderful for you. :package:

lasers commented 6 years ago

@tobes Anything to truncate 3 chars from right side? I can with left. EDIT: Placeholder.

lasers commented 6 years ago

Something to report errors? This comes from https://github.com/ultrabug/py3status/issues/1217

Assuming (self.py3.error) error will switch to CACHE_FOREVER one day.

# stop the module permanently
format = '[\?if=status~running!&error Dropbox disabled]'
# keep running the module
format = '[\?if=status~running!&color=bad Dropbox disabled]
L0ric0 commented 6 years ago

I think there is an inconsitency in the parsing of the format strings. Consider the following format string for the mpd_status module: MPD: {state} [\?if=!state=[stop] [[[{artist}] - {title}]|[{file}]]] The brackets around the stop are literal brackets which are used in the comparison, but in the README it is stated that literal brackets need to be escaped. I don't know if it is worth fixing this or if it is enough to mention it in the README and the documentation of the affected modules.

Oh and is it possible to add a format or condition debug mode which will print the exact comparisons done to the journal? Like: <module_name>: if=!state=stop evaluated to [stop]=stop -> False

lasers commented 5 years ago

For archiving purposes... The answer to comment above is that in mpd_status, the user-configurable default states are already wrapped in brackets making it difficult to write a format string. See https://github.com/ultrabug/py3status/pull/1394 and/or the default states in question... (code below).

https://github.com/ultrabug/py3status/blob/d065a995745822ab02e9671c23d135de4ab6e300/py3status/modules/mpd_status.py#L113-L115

I think this issue has outlived its usefulness long time ago. And we have a RFC 4.0 for dropping the formatter so we probably should not worry about this issue (i.e. new formatter features) right now. We could always reopen this issue later too.