Custom keywords polish
diff --git a/src/export.rs b/src/export.rs
index 9608a01..148839e 100644
--- a/src/export.rs
+++ b/src/export.rs
@@ -1,7 +1,29 @@
+pub use std::clone::Clone;
+pub use std::cmp::{Eq, PartialEq};
+pub use std::convert::From;
+pub use std::default::Default;
+pub use std::fmt::{self, Debug, Formatter};
+pub use std::hash::{Hash, Hasher};
+pub use std::marker::Copy;
+pub use std::option::Option::{None, Some};
 pub use std::result::Result::{Err, Ok};
 
-#[cfg(feature = "parsing")]
-pub use std::convert::From;
+pub use proc_macro2::{Span, TokenStream as TokenStream2};
+
+pub use span::IntoSpans;
 
 #[cfg(feature = "proc-macro")]
 pub use proc_macro::TokenStream;
+
+#[cfg(feature = "printing")]
+pub use quote::{ToTokens, TokenStreamExt};
+
+#[allow(non_camel_case_types)]
+pub type bool = help::Bool;
+#[allow(non_camel_case_types)]
+pub type str = help::Str;
+
+mod help {
+    pub type Bool = bool;
+    pub type Str = str;
+}
diff --git a/src/keyword.rs b/src/keyword.rs
index 9ecf07b..8fcb711 100644
--- a/src/keyword.rs
+++ b/src/keyword.rs
@@ -1,62 +1,142 @@
-use buffer::Cursor;
-use token::Token;
-
-pub trait Keyword {
-    fn ident() -> &'static str;
-
-    fn display() -> &'static str;
-}
-
-impl<K: Keyword> Token for K {
-    fn peek(cursor: Cursor) -> bool {
-        if let Some((ident, _rest)) = cursor.ident() {
-            ident == K::ident()
-        } else {
-            false
-        }
-    }
-
-    fn display() -> &'static str {
-        K::display()
-    }
-}
-
-#[macro_export]
+/// Define a type that supports parsing and printing a given identifier as if it
+/// were a keyword.
+///
+/// # Usage
+///
+/// As a convention, it is recommended that this macro be invoked within a
+/// module called `kw` and that the resulting parser be invoked with a `kw::`
+/// prefix.
+///
+/// ```
+/// # extern crate syn;
+/// #
+/// mod kw {
+///     # use syn;
+///     syn::custom_keyword!(whatever);
+/// }
+/// ```
+///
+/// The generated syntax tree node supports the following operations just like
+/// any built-in keyword token.
+///
+/// - [Peeking] — `input.peek(kw::whatever)`
+///
+/// - [Parsing] — `input.parse::<kw::whatever>()?`
+///
+/// - [Printing] — `quote!( ... #whatever_token ... )`
+///
+/// - Construction from a [`Span`] — `let whatever_token = kw::whatever(sp)`
+///
+/// - Field access to its span — `let sp = whatever_token.span`
+///
+/// [Peeking]: parse/struct.ParseBuffer.html#method.peek
+/// [Parsing]: parse/struct.ParseBuffer.html#method.parse
+/// [Printing]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
+/// [`Span`]: struct.Span.html
+///
+/// # Example
+///
+/// This example parses input that looks like `bool = true` or `str = "value"`.
+/// The key must be either the identifier `bool` or the identifier `str`. If
+/// `bool`, the value may be either `true` or `false`. If `str`, the value may
+/// be any string literal.
+///
+/// The symbols `bool` and `str` are not reserved keywords in Rust so these are
+/// not considered keywords in the `syn::token` module. Like any other
+/// identifier that is not a keyword, these can be declared as custom keywords
+/// by crates that need to use them as such.
+///
+/// ```
+/// # extern crate syn;
+/// #
+/// use syn::{LitBool, LitStr, Token};
+/// use syn::parse::{Parse, ParseStream, Result};
+///
+/// mod kw {
+///     # use syn;
+///     syn::custom_keyword!(bool);
+///     syn::custom_keyword!(str);
+/// }
+///
+/// enum Argument {
+///     Bool {
+///         bool_token: kw::bool,
+///         eq_token: Token![=],
+///         value: LitBool,
+///     },
+///     Str {
+///         str_token: kw::str,
+///         eq_token: Token![=],
+///         value: LitStr,
+///     },
+/// }
+///
+/// impl Parse for Argument {
+///     fn parse(input: ParseStream) -> Result<Self> {
+///         let lookahead = input.lookahead1();
+///         if lookahead.peek(kw::bool) {
+///             Ok(Argument::Bool {
+///                 bool_token: input.parse::<kw::bool>()?,
+///                 eq_token: input.parse()?,
+///                 value: input.parse()?,
+///             })
+///         } else if lookahead.peek(kw::str) {
+///             Ok(Argument::Str {
+///                 str_token: input.parse::<kw::str>()?,
+///                 eq_token: input.parse()?,
+///                 value: input.parse()?,
+///             })
+///         } else {
+///             Err(lookahead.error())
+///         }
+///     }
+/// }
+/// #
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
 macro_rules! custom_keyword {
     ($ident:ident) => {
-        custom_keyword_internal!({ pub(in self) } $ident);
-    };
-    (pub $ident:ident) => {
-        custom_keyword_internal!({ pub } $ident);
-    };
-    (pub(crate) $ident:ident) => {
-        custom_keyword_internal!({ pub(crate) } $ident);
-    };
-    (pub(super) $ident:ident) => {
-        custom_keyword_internal!({ pub(super) } $ident);
-    };
-    (pub(self) $ident:ident) => {
-        custom_keyword_internal!({ pub(self) } $ident);
-    };
-    (pub(in $path:path) $ident:ident) => {
-        custom_keyword_internal!({ pub(in $path) } $ident);
-    };
-}
-
-#[macro_export]
-#[doc(hidden)]
-macro_rules! custom_keyword_internal {
-    ({ $($vis:tt)* } $ident:ident) => {
-        $($vis)* struct $ident {
-            inner: $crate::Ident
+        pub struct $ident {
+            pub span: $crate::export::Span,
         }
 
-        impl $crate::parse::Keyword for $ident {
-            fn ident() -> &'static str {
+        #[doc(hidden)]
+        #[allow(non_snake_case)]
+        pub fn $ident<__S: $crate::export::IntoSpans<[$crate::export::Span; 1]>>(span: __S) -> $ident {
+            $ident {
+                span: $crate::export::IntoSpans::into_spans(span)[0],
+            }
+        }
+
+        impl $crate::export::Default for $ident {
+            fn default() -> Self {
+                $ident {
+                    span: $crate::export::Span::call_site(),
+                }
+            }
+        }
+
+        impl_parse_for_custom_keyword!($ident);
+        impl_to_tokens_for_custom_keyword!($ident);
+        impl_clone_for_custom_keyword!($ident);
+        impl_extra_traits_for_custom_keyword!($ident);
+    }
+}
+
+// Not public API.
+#[cfg(feature = "parsing")]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! impl_parse_for_custom_keyword {
+    ($ident:ident) => {
+        // For peek.
+        impl $crate::token::CustomKeyword for $ident {
+            fn ident() -> &'static $crate::export::str {
                 stringify!($ident)
             }
 
-            fn display() -> &'static str {
+            fn display() -> &'static $crate::export::str {
                 concat!("`", stringify!($ident), "`")
             }
         }
@@ -64,18 +144,103 @@
         impl $crate::parse::Parse for $ident {
             fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {
                 input.step(|cursor| {
-                    if let Some((ident, rest)) = cursor.ident() {
+                    if let $crate::export::Some((ident, rest)) = cursor.ident() {
                         if ident == stringify!($ident) {
-                            return Ok(($ident { inner: ident }, rest));
+                            return $crate::export::Ok(($ident { span: ident.span() }, rest));
                         }
                     }
-                    Err(cursor.error(concat!("expected `", stringify!($ident), "`")))
+                    $crate::export::Err(cursor.error(concat!("expected `", stringify!($ident), "`")))
                 })
             }
         }
+    };
+}
 
-        $($vis)* fn $ident(marker: $crate::parse::TokenMarker) -> $ident {
-            match marker {}
+// Not public API.
+#[cfg(not(feature = "parsing"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! impl_parse_for_custom_keyword {
+    ($ident:ident) => {};
+}
+
+// Not public API.
+#[cfg(feature = "printing")]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! impl_to_tokens_for_custom_keyword {
+    ($ident:ident) => {
+        impl $crate::export::ToTokens for $ident {
+            fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) {
+                let ident = $crate::Ident::new(stringify!($ident), self.span);
+                $crate::export::TokenStreamExt::append(tokens, ident);
+            }
         }
-    }
+    };
+}
+
+// Not public API.
+#[cfg(not(feature = "printing"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! impl_to_tokens_for_custom_keyword {
+    ($ident:ident) => {};
+}
+
+// Not public API.
+#[cfg(feature = "clone-impls")]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! impl_clone_for_custom_keyword {
+    ($ident:ident) => {
+        impl $crate::export::Copy for $ident {}
+
+        impl $crate::export::Clone for $ident {
+            fn clone(&self) -> Self {
+                *self
+            }
+        }
+    };
+}
+
+// Not public API.
+#[cfg(not(feature = "clone-impls"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! impl_clone_for_custom_keyword {
+    ($ident:ident) => {};
+}
+
+// Not public API.
+#[cfg(feature = "extra-traits")]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! impl_extra_traits_for_custom_keyword {
+    ($ident:ident) => {
+        impl $crate::export::Debug for $ident {
+            fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result {
+                $crate::export::Formatter::write_str(f, stringify!($ident))
+            }
+        }
+
+        impl $crate::export::Eq for $ident {}
+
+        impl $crate::export::PartialEq for $ident {
+            fn eq(&self, _other: &Self) -> $crate::export::bool {
+                true
+            }
+        }
+
+        impl $crate::export::Hash for $ident {
+            fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {}
+        }
+    };
+}
+
+// Not public API.
+#[cfg(not(feature = "extra-traits"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! impl_extra_traits_for_custom_keyword {
+    ($ident:ident) => {};
 }
diff --git a/src/lib.rs b/src/lib.rs
index ccf70b7..d3b7359 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -567,11 +567,10 @@
 #[doc(hidden)]
 pub mod export;
 
-#[cfg(feature = "parsing")]
-mod lookahead;
+mod keyword;
 
 #[cfg(feature = "parsing")]
-mod keyword;
+mod lookahead;
 
 #[cfg(feature = "parsing")]
 pub mod parse;
diff --git a/src/parse.rs b/src/parse.rs
index 4dd735d..0445cef 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -214,8 +214,7 @@
 use token::Token;
 
 pub use error::{Error, Result};
-pub use lookahead::{Lookahead1, Peek, TokenMarker};
-pub use keyword::Keyword;
+pub use lookahead::{Lookahead1, Peek};
 
 /// Parsing interface implemented by all types that can be parsed in a default
 /// way from a token stream.
diff --git a/src/token.rs b/src/token.rs
index 637d901..8892c33 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -118,7 +118,7 @@
 #[cfg(feature = "parsing")]
 use lookahead;
 #[cfg(feature = "parsing")]
-use parse::{Keyword, Parse, ParseStream};
+use parse::{Parse, ParseStream};
 use span::IntoSpans;
 
 /// Marker trait for types that represent single tokens.
@@ -144,9 +144,6 @@
 impl private::Sealed for Ident {}
 
 #[cfg(feature = "parsing")]
-impl<K: Keyword> private::Sealed for K {}
-
-#[cfg(feature = "parsing")]
 fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool {
     let scope = Span::call_site();
     let unexpected = Rc::new(Cell::new(None));
@@ -194,6 +191,28 @@
 #[cfg(any(feature = "full", feature = "derive"))]
 impl_token!(LitBool "boolean literal");
 
+// Not public API.
+#[cfg(feature = "parsing")]
+#[doc(hidden)]
+pub trait CustomKeyword {
+    fn ident() -> &'static str;
+    fn display() -> &'static str;
+}
+
+#[cfg(feature = "parsing")]
+impl<K: CustomKeyword> private::Sealed for K {}
+
+#[cfg(feature = "parsing")]
+impl<K: CustomKeyword> Token for K {
+    fn peek(cursor: Cursor) -> bool {
+        parsing::peek_keyword(cursor, K::ident())
+    }
+
+    fn display() -> &'static str {
+        K::display()
+    }
+}
+
 macro_rules! define_keywords {
     ($($token:tt pub struct $name:ident #[$doc:meta])*) => {
         $(