crossterm-rs / crossterm

Cross platform terminal library rust
MIT License
3.18k stars 274 forks source link

Macros to reduce the syntactic burden of parsing events #874

Open emmalexandria opened 5 months ago

emmalexandria commented 5 months ago

Problem: Parsing crossterm events can be incredibly wordy. Of course, this can be improved with splitting out the parsing of different groups of events into functions, but the depth of nesting and length of the declarations to, e.g, match all KeyCode events and then match on specific KeyCode::Char(_) events can get deeply messy.

Potential solution Macros could be defined that return match conditions scoped to work on Event. I implemented a basic proof of concept macro for generating the match conditions and it's definitely feasible:

macro_rules! match_keycode {
        ($(code=$($ch:literal,)? $code:path,)? $(kind=$kind:path,)? $(modifier=$modifier:path,)? $(state=$state:path)? $(,)?) => {
            ::crossterm::event::Event::Key(::crossterm::event::KeyEvent {
                code $(: $code$(($ch))?)?,
                kind $(: $kind)?,
                modifiers $(: $modifier)?,
                state $(: $state)?
            })
        };
    }

I ran it through some basic tests and it worked fine, although it has many flaws in usability which I can elaborate on if wanted (I'm pretty bad at macros).

I understand that this might be out of scope for this project, but I thought it could make an interesting improvement to code readability.

babybabycloud commented 5 months ago

Maybe I have the same question, I didn't find a better solution to replace the below code

match key_event.code {
KeyCode::Char('A') => some_funtion('A'),
KeyCode::Char('B') => some_funtion('B'),
KeyCode::Char('C') => some_funtion('C'),
KeyCode::Char('D') => some_funtion('D'),
KeyCode::Char('E') => some_funtion('E'),
_ => (),
}

Although they all have the same logic, but I need to do the same thing for they all

TimonPost commented 5 months ago

you can match on variable no?

enum KeyCode{
    Char(char)
}
fn some_funtion(a: char) {

}

fn main() {
   match KeyCode::Char('b') {
    KeyCode::Char(c) => some_funtion(c),
    _ => (),
    }
}
TimonPost commented 5 months ago

For the macros i am not sure. Surely it removes redundancy, but the thing is that they are quite hard to understand on how to use them.

babybabycloud commented 4 months ago

you can match on variable no?

enum KeyCode{
    Char(char)
}
fn some_funtion(a: char) {

}

fn main() {
   match KeyCode::Char('b') {
    KeyCode::Char(c) => some_funtion(c),
    _ => (),
    }
}

Yes, I think this is correct. After comment on this issue, I found such usage in other project. LOL