Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store custom ASCII-art in theme's toml file #255

Open
KingPEPSALT opened this issue Apr 2, 2022 · 16 comments
Open

Store custom ASCII-art in theme's toml file #255

KingPEPSALT opened this issue Apr 2, 2022 · 16 comments
Labels
area/ascii Has something to do with ASCII art. type/feature Request a new feature.

Comments

@KingPEPSALT
Copy link

KingPEPSALT commented Apr 2, 2022

Currently, the only way I can see (in the docs) to use custom ascii art is through a path but could a multiline toml variable not be added that holds the ascii? Sort of like:

[custom_ascii]
ascii='''
       _                        
       \`*-.                    
        )  _`-.                 
       .  : `. .                
       : _   '  \               
       ; *` _.   `*-._          
       `-.-'          `-.       
         ;       `       `.     
         :.       .        \    
         . \  .   :   .-'   .   
         '  `+.;  ;  '      :   
         :  '  |    ;       ;-. 
         ; '   : :`-:     _.`* ;
[bug] .*' /  .*' ; .*`- +'  `*' 
      `*-*   `*-*  `*-*'
'''

This way, the whole theme can be stored within the toml file.
It's also worth asking: could custom themes have multiple ASCII arts for different operating systems? I'd love to see a lot more flexibility with themes as long as it doesn't downgrade performance.

@grtcdr
Copy link
Member

grtcdr commented Apr 2, 2022

Good idea :)

I'm trying to think of a safe way to implement this. Can we assume that in the presence of a newline, that the "path" contains embedded art, and we may proceed with passing that string for rendering directly?

EDIT: As long as serde parses multi-line strings, this should be trivial to add.

@KingPEPSALT
Copy link
Author

Well an issue that could potentially arise, if escape characters are ever considered when parsing new lines and stuff, is that the ascii art string could potentially contain \n or a similar character and that get treated as a new line?

@grtcdr
Copy link
Member

grtcdr commented Apr 2, 2022

The way we store builtin ASCII art is through a vector of strings, see this as an example.

I'm wondering if it is sufficient to split (the input ASCII art) by "\n", and create a vector out of the resulting split.

Is this what you mean?

Note:

A downside of this is that colors will not work, unless we add support for a user-friendly color templating engine, as I don't see myself getting behind the idea of allowing the use of the long and horrendous ANSI escape codes.

Consider the following ASCII art for an example of such a templating engine:

{b}<<>>{-}
  • {b} maps to "color the following with blue"
  • {-} maps to "stop coloring"

I don't know of such an engine, but if you do, please let me know.

If one doesn't exist, then I suggest the use of regular expressions.

@KingPEPSALT
Copy link
Author

I don't know of any templaying function but I like the idea of

{{b}}<<>>{{-}}

Using double brackets to avoid the possibility of it occuring within the ASCII art

Regular expressions would definitely work with this.

I'm wondering if it is sufficient to split (the input ASCII art) by "\n", and create a vector out of the resulting split.

This is what I'd be concerned about as \n might be within the ascii so maybe a sequence like {{n}} could be used (following your templating example).

@grtcdr
Copy link
Member

grtcdr commented Apr 2, 2022

I wrote a regular expression that handles this pretty well I think, at least for the couple test runs I made (with pen and paper). I'll post it tomorrow.

This is what I'd be concerned about as \n might be within the ascii so maybe a sequence like {{n}} could be used (following your templating example).

Agreed.

Using double brackets to avoid the possibility of it occuring within the ASCII art

I agree to a certain extent, but I feel like this clutters up the templating syntax. I'd say that this becomes a question of probability, which I think is somewhat low.

By the way, I truly appreciate your feedback. You've been very helpful. ❤️

@grtcdr grtcdr added type/feature Request a new feature. area/ascii Has something to do with ASCII art. labels Apr 2, 2022
@KingPEPSALT
Copy link
Author

I'm happy to see this program get developed :) I really like it and am using it as a replacement for neofetch for Powershell due to speed so I'm always happy to help

@grtcdr
Copy link
Member

grtcdr commented Apr 3, 2022

In formal language theory, the regular expression would be:

((\{[rgbcmyBWn-]\})*.*)*

(\{[rgbcmyBWn-]\})* captures the tokens which are defined later on in this comment. The remaining .* captures any other possible characters. e.g.:

let set = RegexSet::new(&[
    r"(\{[rgbcmyBWn-]\})*",
    r".*",
]).unwrap();

So for a given input of:

{b}[><]{n}{c}<||>{-}

Our regular expression should have no problem validating it, but the regular
expression should be rewritten to be compatible with the syntax of the regex
crate.

The crate that implements this templating scheme should save the
results as an array of structs(text: String, color: ColorEnum).

macchina would send the custom_ascii.ascii option to
the crate for handling, and then color the text accordingly.

If you have any suggestions, I'd love to hear them.

Colors:

  • r: Red
  • g: Green
  • b: Blue
  • c: Cyan
  • m: Magenta
  • y: Yellow
  • B: Black
  • W: White

Special characters:

  • n: Newline
  • -: Reset (stop coloring)

@KingPEPSALT
Copy link
Author

KingPEPSALT commented Apr 3, 2022

I really like this however this does lose some functionality such as hexcode colours, is there a way to implement this?
The regex would be this I believe: ((\{(#[0-9A-Fa-f]{6}|[rgbcmyBWn-])\})*.*)*
And using Rust's RegexSet I think you may do something like this, though I'm not too sure:

let set = RegexSet::new(&[
    r"(\{[rgbcmyBWn-]\})*",
    r"(\{#[0-9A-Fa-f]{6}\})*",
    r".*",
]).unwrap();

This would catch all hexadecimal numbers, new-line tokens, reset tokens or predefined colour tokens. Could this possibly work?

Edit: this would catch all of the characters stated above as well as:
{#FA1234}, {#ec23c1} and {#EeEeEe} which are all valid hexadecimal colours if you are ignoring case
It would not catch:
{FA1234} (though you may prefer this), {#FFEECCD}, {#ECECECn} or {#FE}

@grtcdr
Copy link
Member

grtcdr commented Apr 3, 2022

I don't see why it wouldn't work, we can definitely have this. I suppose the crate should expose as much functionality as possible.

@grtcdr
Copy link
Member

grtcdr commented Apr 3, 2022

I'm struggling to make it work, but I'll keep you posted if I make any progress on this. If I take too long, I'm happy to accept a PR if you're up for the task. 👍

@KingPEPSALT
Copy link
Author

I'd give it a try for sure

@KingPEPSALT
Copy link
Author

I'd expand the RegexSet to match for {colour}*{-} and use that to create spans of colour, denoted with * between and trim to the first } and after the last { in the match. I don't know if that helps but I thought I'd just drop it here!

@grtcdr
Copy link
Member

grtcdr commented Apr 3, 2022

I discovered that RegexSet doesn't do what I needed it to do, so I went with Regex which offers more functionality. But I feel as though I'm wrong.

{colour}*{-}

Note that both special characters and colors are optional, it's also more efficient if we combine the color and special character set, e.g. ({color|special})*.

trim to the first } and after the last { in the match.

That's one way to do it, I tried a similar but slightly different approach, i.e. extract the token, store as key, extract text, store as value.

While documenting my approach, I've realized I've made a huge mistake in my code.

I don't know if that helps but I thought I'd just drop it here!

I appreciate you sharing your thought patterns, it will definitely help us document the steps we take to make this happen.

@KingPEPSALT
Copy link
Author

KingPEPSALT commented Apr 3, 2022

Note that both special characters and colors are optional, it's also more efficient if we combine the color and special character set, e.g. ({color|special})*.

I didn't realise this!

Whilst parsing it with regex, we are essentially tokenizing it into tui spans right? So we just need to split the ASCII by {n} and in those lines we then split by colour tags {r} or {#abcdef}, we can use the colour tag to determine the colour of the span and use {n} to act as a reset token as well so we don't need to roll colour information between lines. Is this how you are handling it or a way we should consider?

   {b}_.-{r}=-._{w}     .-,{n}
 {g}.'      {y} "-.,'{#abcdef} / 
let ascii: Vec<&str> = vec![
    "   {b}_.-{r}=-._{w}     .-,{n}"
    " {g}.'      {y} \"-.,'{#abcdef} / "
]
// then through some parsing this becomes
let coloured_ascii: Vec<Span> = vec![
    Spans::from(vec![
        Span::styled("_.-", *BLUE),
        Span::styled("=-._", *RED),
        Span::styled("     .-,", *WHITE),
    ]), 
    Spans::from(vec![
        Span::styled(".'      ", *GREEN),
        Span::styled(" \"-..'", *YELLOW),
        Span::styled(" /", Style::default().fg(Color::Rgb(0xab, 0xcd, 0xef))
    ])
]

I'm not sure on how efficient this would be but it would work. In theory it would be O(n) where n is the length of the ASCII in characters as we will end up looping over each character twice at a minimum

Edit: I'm not a rust expert so forgive me if my 'code' is slightly off x)

@grtcdr
Copy link
Member

grtcdr commented Apr 3, 2022

Note that both special characters and colors are optional, it's also more efficient if we combine the color and special character set, e.g. ({color|special})*.

I didn't realise this!

Whilst parsing it with regex, we are essentially tokenizing it into tui spans right?

Exactly.

So we just need to split the ASCII by {n} and in those lines we then split by colour tags > {r} or {#abcdef}, we can use the colour tag to determine the colour of the span and use >{n} to act as a reset token as well so we don't need to roll colour information between lines. Is this how you are handling it or a way we should consider?

This is the idea I was hoping to achieve.

   {b}_.-{r}=-._{w}     .-,{n}
 {g}.'      {y} "-.,'{#abcdef} / 

Looking much better than ANSI already.

let ascii: Vec<&str> = vec![
    "   {b}_.-{r}=-._{w}     .-,{n}"
    " {g}.'      {y} \"-.,'{#abcdef} / "
]
// then through some parsing this becomes
let coloured_ascii: Vec<Span> = vec![
    Spans::from(vec![
        Span::styled("_.-", *BLUE),
        Span::styled("=-._", *RED),
        Span::styled("     .-,", *WHITE),
    ]), 
    Spans::from(vec![
        Span::styled(".'      ", *GREEN),
        Span::styled(" \"-..'", *YELLOW),
        Span::styled(" /", Style::default().fg(Color::Rgb(0xab, 0xcd, 0xef))
    ])
]

Yeah, the example you linked translates to the above snippet very well.

I'm not sure on how efficient this would be but it would work. In theory it would be O(n) where n is the length of the ASCII in characters as we will end up looping

over each character twice at a minimum

So O(2n)?

I'm not a rust expert so forgive me if my 'code' is slightly off x)

Code is perfect :)

@KingPEPSALT
Copy link
Author

KingPEPSALT commented Apr 3, 2022

Looking much better than ANSI already.

Yep and it allows for hexadecimal colours trivially which I like!

So O(2n)?

Yep it should be - I'm going to say around O(n) is the minimum possible for this problem, O(2n) should be more than fast enough since we are only realistically considering small numbers of characters, I'm guessing n < 64x64 as an absolute maximum and 16x16 < n < 24x24 as an approximate average.

Code is perfect :)

Good to know!

@grtcdr grtcdr changed the title [FEATURE] Store custom ASCII-art in theme's toml file Store custom ASCII-art in theme's toml file May 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/ascii Has something to do with ASCII art. type/feature Request a new feature.
Development

No branches or pull requests

2 participants