Files
Graphite/core/proc-macro/src/helper_structs.rs
TrueDoctor 4b19a459b7 Major overhaul of input and communication systems
* Add input manager

* WIP lifetime hell

* Hell yeah, dark lifetime magic

* Replace events with actions in tools

* Fix borrow in GlobalEventHandler

* Fix typo in response-handler

* Distribute dispatch structure

* Add translation from events to actions

* Port default key actions to input_mapper

* Split actions macro

* Add handling for Ambiguous Mouse events

* Fix warnings and clippy lints

* Add actions macro

* WIP rework

* Add AsMessage macro

* Add implementation for derived enums

* Add macro implementation for top level message

* Add #[child] attribute to indicate derivation

* Replace some mentions of Actions and Responses with Message

* It compiles !!!

* Add functionality to some message handlers

* Add document rendering

* ICE

* Rework the previous code while keeping basic functionality

* Reduce parent-top-level macro args to only two

* Add workaround for ICE

* Fix cyclic reference in document.rs

* Make derive_transitive_child a bit more powerful

This addresses the todo that was left,
enabling arbitrary expressions to be passed as the last
parameter of a #[parent] attribute

* Adapt frontend message format

* Make responses use VecDeque

Our responses are a queue so we should use a queue type for them

* Move traits to sensible location

* Are we rectangle yet?

* Simplify, improve & document `derive_discriminant`

* Change `child` to `sub_discriminant`

This only applies to `ToDiscriminant`.
Code using `#[impl_message]` continues to work.

* Add docs for `derive_transitive_child`

* Finish docs and improve macros

The improvements are that impl_message now uses trait
resolution to obtain the parent's discriminant
and that derive_as_message now allows for non-unit
variants (which we don't use but it's nice to have,
just in case)

* Remove logging call

* Move files around and cleanup structure

* Fix proc macro doc tests

* Improve actions_fn!() macro

* Add ellipse tool

* Pass populated actions list to the input mapper

* Add KeyState bitvector

* Merge mouse buttons into "keyboard"

* Add macro for initialization of key mapper table

* Add syntactic sugar for the macro

* Implement mapping function

* Translate the remaining tools

* Fix shape tool

* Add keybindings for line and pen tool

* Fix modifiers

* Cleanup

* Add doc comments for the actions macro

* Fix formatting

* Rename MouseMove to PointerMove

* Add keybinds for tools

* Apply review suggestions

* Rename KeyMappings -> KeyMappingEntries

* Apply review changes

Co-authored-by: T0mstone <realt0mstone@gmail.com>
Co-authored-by: Paul Kupper <kupper.pa@gmail.com>
2021-05-23 01:26:24 +02:00

208 lines
4.7 KiB
Rust

use proc_macro2::{Ident, TokenStream};
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::token::Paren;
use syn::{parenthesized, LitStr, Token};
pub struct IdentList {
pub parts: Punctuated<Ident, Token![,]>,
}
impl Parse for IdentList {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
let _paren_token = parenthesized!(content in input);
Ok(Self {
parts: Punctuated::parse_terminated(&content)?,
})
}
}
/// Parses `("some text")`
pub struct AttrInnerSingleString {
_paren_token: Paren,
pub content: LitStr,
}
impl Parse for AttrInnerSingleString {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
let _paren_token = parenthesized!(content in input);
Ok(Self {
_paren_token,
content: content.parse()?,
})
}
}
/// Parses `key="value"`
pub struct KeyEqString {
key: Ident,
_eq_token: Token![=],
lit: LitStr,
}
impl Parse for KeyEqString {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
key: input.parse()?,
_eq_token: input.parse()?,
lit: input.parse()?,
})
}
}
/// Parses `(key="value", key="value", …)`
pub struct AttrInnerKeyStringMap {
_paren_token: Paren,
parts: Punctuated<KeyEqString, Token![,]>,
}
impl Parse for AttrInnerKeyStringMap {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
let _paren_token = parenthesized!(content in input);
Ok(Self {
_paren_token,
parts: Punctuated::parse_terminated(&content)?,
})
}
}
impl AttrInnerKeyStringMap {
pub fn multi_into_iter(iter: impl IntoIterator<Item = Self>) -> impl Iterator<Item = (Ident, Vec<LitStr>)> {
use std::collections::hash_map::Entry;
let mut res = Vec::<(Ident, Vec<LitStr>)>::new();
let mut idx = HashMap::<Ident, usize>::new();
for part in iter.into_iter().flat_map(|x: Self| x.parts) {
match idx.entry(part.key) {
Entry::Occupied(occ) => {
res[*occ.get()].1.push(part.lit);
}
Entry::Vacant(vac) => {
let ident = vac.key().clone();
vac.insert(res.len());
res.push((ident, vec![part.lit]));
}
}
}
res.into_iter()
}
}
/// Parses `(left, right)`
pub struct Pair<F, S> {
pub paren_token: Paren,
pub first: F,
pub sep: Token![,],
pub second: S,
}
impl<F, S> Parse for Pair<F, S>
where
F: Parse,
S: Parse,
{
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
let paren_token = parenthesized!(content in input);
Ok(Self {
paren_token,
first: content.parse()?,
sep: content.parse()?,
second: content.parse()?,
})
}
}
/// parses `(...)`
pub struct ParenthesizedTokens {
pub paren: Paren,
pub tokens: TokenStream,
}
impl Parse for ParenthesizedTokens {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
let paren = parenthesized!(content in input);
Ok(Self { paren, tokens: content.parse()? })
}
}
/// parses a comma-delimeted list of `T`s with optional trailing comma
pub struct SimpleCommaDelimeted<T>(pub Vec<T>);
impl<T: Parse> Parse for SimpleCommaDelimeted<T> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let punct = Punctuated::<T, Token![,]>::parse_terminated(input)?;
Ok(Self(punct.into_iter().collect()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn attr_inner_single_string() {
let res = syn::parse2::<AttrInnerSingleString>(quote::quote! {
("a string literal")
});
assert!(res.is_ok());
assert_eq!(res.ok().unwrap().content.value(), "a string literal");
let res = syn::parse2::<AttrInnerSingleString>(quote::quote! {
wrong, "stuff"
});
assert!(res.is_err());
}
#[test]
fn key_eq_string() {
let res = syn::parse2::<KeyEqString>(quote::quote! {
key="value"
});
assert!(res.is_ok());
let res = res.ok().unwrap();
assert_eq!(res.key, "key");
assert_eq!(res.lit.value(), "value");
let res = syn::parse2::<KeyEqString>(quote::quote! {
wrong, "stuff"
});
assert!(res.is_err());
}
#[test]
fn attr_inner_key_string_map() {
let res = syn::parse2::<AttrInnerKeyStringMap>(quote::quote! {
(key="value", key2="value2")
});
assert!(res.is_ok());
let res = res.ok().unwrap();
for (item, (k, v)) in res.parts.into_iter().zip(vec![("key", "value"), ("key2", "value2")]) {
assert_eq!(item.key, k);
assert_eq!(item.lit.value(), v);
}
let res = syn::parse2::<AttrInnerKeyStringMap>(quote::quote! {
(key="value", key2="value2",)
});
assert!(res.is_ok());
let res = res.ok().unwrap();
for (item, (k, v)) in res.parts.into_iter().zip(vec![("key", "value"), ("key2", "value2")]) {
assert_eq!(item.key, k);
assert_eq!(item.lit.value(), v);
}
let res = syn::parse2::<AttrInnerKeyStringMap>(quote::quote! {
wrong, "stuff"
});
assert!(res.is_err());
}
}