1extern crate proc_macro;
2use proc_macro2::{Delimiter, Group, Literal, Punct, Spacing, TokenStream, TokenTree};
3use quote::quote;
4
5#[proc_macro]
6pub fn layout(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7 let input: TokenStream = input.into();
8
9 let mut out = TokenStream::new();
10
11 let mut inside = TokenStream::new();
12
13 for t in input {
14 match t {
15 TokenTree::Group(g) if g.delimiter() == Delimiter::Brace => {
16 let layer = parse_layer(g.stream());
17 inside.extend(quote! {
18 [#layer],
19 });
20 }
21 _ => panic!("{}", "Invalid token, expected layer: {{ ... }}"),
22 }
23 }
24
25 let all: TokenStream = quote! { [#inside] };
26 out.extend(all);
27
28 out.into()
29}
30
31fn parse_layer(input: TokenStream) -> TokenStream {
32 let mut out = TokenStream::new();
33 for t in input {
34 match t {
35 TokenTree::Group(g) if g.delimiter() == Delimiter::Bracket => {
36 let row = parse_row(g.stream());
37 out.extend(quote! {
38 [#row],
39 });
40 }
41 TokenTree::Punct(p) if p.as_char() == ',' => (),
42 _ => panic!("Invalid token, expected row: [ ... ]"),
43 }
44 }
45 out
46}
47
48fn parse_row(input: TokenStream) -> TokenStream {
49 let mut out = TokenStream::new();
50 for t in input {
51 match t {
52 TokenTree::Ident(i) => match i.to_string().as_str() {
53 "n" => out.extend(quote! { keyberon::action::Action::NoOp, }),
54 "t" => out.extend(quote! { keyberon::action::Action::Trans, }),
55 _ => out.extend(quote! {
56 keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::#i),
57 }),
58 },
59 TokenTree::Punct(p) => punctuation_to_keycode(&p, &mut out),
60 TokenTree::Literal(l) => literal_to_keycode(&l, &mut out),
61 TokenTree::Group(g) => parse_group(&g, &mut out),
62 }
63 }
64 out
65}
66
67fn parse_group(g: &Group, out: &mut TokenStream) {
68 match g.delimiter() {
69 Delimiter::Parenthesis if g.stream().is_empty() => {
71 eprintln!("Expected a layer number in layer switch");
72 }
73 Delimiter::Brace if g.stream().is_empty() => {
74 eprintln!("Expected an action - group cannot be empty");
75 }
76 Delimiter::Bracket if g.stream().is_empty() => {
77 eprintln!("Expected keycodes - keycode group cannot be empty");
78 }
79
80 Delimiter::Parenthesis => {
82 let tokens = g.stream();
83 out.extend(quote! { keyberon::action::Action::Layer(#tokens), });
84 }
85 Delimiter::Brace => out.extend(g.stream().into_iter().chain(TokenStream::from(
87 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
88 ))),
89 Delimiter::Bracket => parse_keycode_group(g.stream(), out),
91
92 Delimiter::None => eprintln!("Unexpected group"),
94 }
95}
96
97fn parse_keycode_group(input: TokenStream, out: &mut TokenStream) {
98 let mut inner = TokenStream::new();
99 for t in input {
100 match t {
101 TokenTree::Ident(i) => inner.extend(quote! {
102 keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::#i),
103 }),
104 TokenTree::Punct(p) => punctuation_to_keycode(&p, &mut inner),
105 TokenTree::Literal(l) => literal_to_keycode(&l, &mut inner),
106 TokenTree::Group(g) => parse_group(&g, &mut inner),
107 }
108 }
109 out.extend(quote! { keyberon::action::Action::MultipleActions(&[#inner].as_slice()), });
110}
111
112fn punctuation_to_keycode(p: &Punct, out: &mut TokenStream) {
113 match p.as_char() {
114 '-' => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Minus), }),
116 '=' => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Equal), }),
117 ';' => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::SColon), }),
118 ',' => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Comma), }),
119 '.' => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Dot), }),
120 '/' => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Slash), }),
121
122 '!' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb1].as_slice()), }),
124 '@' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb2].as_slice()), }),
125 '#' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb3].as_slice()), }),
126 '$' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb4].as_slice()), }),
127 '%' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb5].as_slice()), }),
128 '^' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb6].as_slice()), }),
129 '&' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb7].as_slice()), }),
130 '*' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb8].as_slice()), }),
131 '_' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Minus].as_slice()), }),
132 '+' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Equal].as_slice()), }),
133 '|' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Bslash].as_slice()), }),
134 '~' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Grave].as_slice()), }),
135 '<' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Comma].as_slice()), }),
136 '>' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Dot].as_slice()), }),
137 '?' => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Slash].as_slice()), }),
138 _ => eprintln!("Punctuation could not be parsed as a keycode")
140 }
141}
142
143fn literal_to_keycode(l: &Literal, out: &mut TokenStream) {
144 match l.to_string().as_str() {
146 "1" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb1), }),
147 "2" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb2), }),
148 "3" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb3), }),
149 "4" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb4), }),
150 "5" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb5), }),
151 "6" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb6), }),
152 "7" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb7), }),
153 "8" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb8), }),
154 "9" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb9), }),
155 "0" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Kb0), }),
156
157 r#"'\''"# => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Quote), }),
159 r#"'\\'"# => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Bslash), }),
160 "'['" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::LBracket), }),
162 "']'" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::RBracket), }),
163 "'`'" => out.extend(quote! { keyberon::action::Action::KeyCode(keyberon::key_code::KeyCode::Grave), }),
164 "'\"'" => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Quote].as_slice()), }),
165 "'('" => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb9].as_slice()), }),
166 "')'" => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Kb0].as_slice()), }),
167 "'{'" => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::LBracket].as_slice()), }),
168 "'}'" => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::RBracket].as_slice()), }),
169 "'_'" => out.extend(quote! { keyberon::action::Action::MultipleKeyCodes(&[keyberon::key_code::KeyCode::LShift, keyberon::key_code::KeyCode::Minus].as_slice()), }),
170
171 s if s.starts_with('\'') => eprintln!("Literal could not be parsed as a keycode"),
172
173 s if s.starts_with('\"') => {
174 eprintln!("Typing strings on key press is not yet supported")
175 }
176 _ => eprintln!("Literal could not be parsed as a keycode")
177 }
178}